AiVault uses Resend to send automated email notifications to tool submitters when their submissions are reviewed.
Configuration
Email notifications require two environment variables:
Get Resend API Key
Sign up at resend.com and create an API key from your dashboard. Configure Sender Email
Set FROM_EMAIL to use your verified domain. If not set, it defaults to Ai Vault <[email protected]>. 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