Skip to main content
AiVault uses Resend to send automated email notifications to tool submitters when their submissions are reviewed.

Configuration

Email notifications require two environment variables:
.env.local
RESEND_API_KEY=re_your_api_key_here
FROM_EMAIL=Ai Vault <[email protected]m>
1

Get Resend API Key

Sign up at resend.com and create an API key from your dashboard.
2

Configure Sender Email

Set FROM_EMAIL to use your verified domain. If not set, it defaults to Ai Vault <[email protected]>.
3

Verify Domain (Production)

For production use, verify your domain in Resend to avoid emails being marked as spam.
If RESEND_API_KEY is not set, email sending will fail gracefully with a console warning, but the approval/rejection process will still complete.

Email Service Implementation

The core email service is in lib/email.ts:11-36:
export async function sendEmail(data: EmailData) {
  if (!process.env.RESEND_API_KEY) {
    console.warn('RESEND_API_KEY not set, email not sent');
    return { success: false, error: 'RESEND_API_KEY not configured' };
  }

  try {
    const result = await resend.emails.send({
      from: process.env.FROM_EMAIL || 'Ai Vault <[email protected]>',
      to: data.to,
      subject: data.subject,
      html: data.html,
    });

    if (result.error) {
      console.error('Resend API Error:', result.error);
      return { success: false, error: result.error };
    }

    console.log('Email sent successfully:', result.data?.id);
    return { success: true, data: result.data };
  } catch (error) {
    console.error('Failed to send email:', error);
    return { success: false, error };
  }
}

Approval Emails

When a tool is approved, the submitter receives a celebratory email.

Email Template

Defined in lib/email.ts:69-100:
export function approvalEmail(toolName: string, toolUrl: string) {
  return {
    subject: `🎉 Your Tool is Now Live - ${toolName}`,
    html: `
      <div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px;">
        <h2 style="color: #22c55e; border-bottom: 2px solid #22c55e; padding-bottom: 10px;">
          Congratulations! Your tool has been approved!
        </h2>
        <p style="color: #666; line-height: 1.6;">
          Great news! <strong>${toolName}</strong> has been approved and is now live on Ai Vault.
        </p>
        <div style="text-align: center; margin: 30px 0;">
          <a href="${toolUrl}" 
             style="background: #6366f1; color: white; padding: 12px 30px; text-decoration: none; border-radius: 6px; display: inline-block;">
            View Your Tool
          </a>
        </div>
        <p style="color: #666; line-height: 1.6;">
          Share your tool with the community and start collecting upvotes. The more engagement your tool receives, the higher it will rank in our directory.
        </p>
        <div style="margin-top: 30px; padding: 15px; background: #f0fdf4; border-radius: 8px; border-left: 4px solid #22c55e;">
          <p style="color: #166534; margin: 0; font-size: 14px;">
            <strong>Tip:</strong> Share your tool on social media to get more visibility and upvotes!
          </p>
        </div>
      </div>
    `
  };
}

Approval Email Contents

  • Subject: 🎉 Your Tool is Now Live - {Tool Name}
  • Green success header with congratulations message
  • Prominent CTA button linking to the live tool page
  • Engagement tips to encourage sharing
  • Tip callout with green styling suggesting social media promotion

Sending Approval Emails

Emails are sent from app/admin/tools/[id]/page.tsx:38-54 after successful approval:
await approveTool({ toolId });

// Send approval email
try {
  await fetch('/api/send-email', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      type: 'approval',
      recipientUserId: tool.submittedBy,
      preferenceKey: 'reviewStatusUpdates',
      data: {
        toolName: tool.name,
        toolUrl: `${window.location.origin}/tools/${tool.slug}`
      }
    }),
  });
} catch (emailError) {
  console.error('Failed to send approval email:', emailError);
}

Rejection Emails

When a tool is rejected, the submitter receives feedback on why.

Email Template

Defined in lib/email.ts:102-128:
export function rejectionEmail(toolName: string, reason: string) {
  return {
    subject: `Tool Submission Update - ${toolName}`,
    html: `
      <div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px;">
        <h2 style="color: #ef4444; border-bottom: 2px solid #ef4444; padding-bottom: 10px;">
          Tool Submission Update
        </h2>
        <p style="color: #666; line-height: 1.6;">
          Thank you for submitting <strong>${toolName}</strong> to Ai Vault. After review, we were unable to approve your submission at this time.
        </p>
        <div style="margin: 20px 0; padding: 15px; background: #fef2f2; border-radius: 8px; border-left: 4px solid #ef4444;">
          <p style="color: #991b1b; margin: 0; font-size: 14px;">
            <strong>Reason:</strong><br>
            ${reason}
          </p>
        </div>
        <p style="color: #666; line-height: 1.6;">
          You're welcome to resubmit after addressing the feedback above. If you have any questions, feel free to reach out to our support team.
        </p>
      </div>
    `
  };
}

Rejection Email Contents

  • Subject: Tool Submission Update - {Tool Name}
  • Red header indicating rejection
  • Polite rejection message thanking them for submitting
  • Prominent reason callout with red styling showing admin feedback
  • Invitation to resubmit after addressing issues
  • Support contact offer for questions

Sending Rejection Emails

Emails are sent from app/admin/tools/[id]/page.tsx:73-90 after rejection:
await rejectTool({ toolId });

// Send rejection email
try {
  await fetch('/api/send-email', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      type: 'rejection',
      recipientUserId: tool.submittedBy,
      preferenceKey: 'reviewStatusUpdates',
      data: {
        toolName: tool.name,
        reason: rejectReason.trim()
      }
    }),
  });
} catch (emailError) {
  console.error('Failed to send rejection email:', emailError);
}

Other Email Templates

The email library also includes templates for:

Submission Confirmation

lib/email.ts:39-67 - Sent when a user submits a new tool:
export function submissionConfirmationEmail(toolName: string)
  • Confirms receipt of submission
  • Explains the review process (1-3 business days)
  • Sets expectations for what happens next

Admin Notifications

lib/email.ts:130-157 - Notifies admins of new submissions:
export function adminNotificationEmail(toolName: string, submittedBy: string, adminUrl: string)
  • Alerts admins to pending reviews
  • Includes tool name and submitter
  • Contains direct link to admin review page
All email templates use inline CSS styling for maximum email client compatibility.

Error Handling

Email failures are logged but don’t block the approval/rejection process:
try {
  await fetch('/api/send-email', { /* ... */ });
} catch (emailError) {
  console.error('Failed to send approval email:', emailError);
  // Process continues even if email fails
}
This ensures that:
  • Tool status updates always complete
  • Email issues don’t break the admin workflow
  • Errors are logged for debugging
  • Users aren’t left in limbo if emails fail

Build docs developers (and LLMs) love