Invoice Generator includes an email API endpoint for sending invoices to customers. The email functionality is currently a stub that requires implementation with an email service provider.
Current implementation
The application includes a basic email API route at /api/email that validates inputs but doesn’t send actual emails:
export async function POST(request: NextRequest) {
try {
const body = await request.json();
const { invoiceId, recipientEmail, subject, message } = body;
// Validate inputs
if (!invoiceId || !recipientEmail) {
return NextResponse.json(
{ error: "Invoice ID and recipient email are required" },
{ status: 400 },
);
}
// Basic email validation
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(recipientEmail)) {
return NextResponse.json(
{ error: "Invalid email address" },
{ status: 400 },
);
}
// TODO: Implement email sending logic here
return NextResponse.json(
{
message: "Email sending not yet implemented",
invoiceId,
recipientEmail,
subject: subject || "Your Invoice",
},
{ status: 501 }, // Not Implemented
);
} catch (error) {
return NextResponse.json(
{
error: error instanceof Error ? error.message : "Failed to send email",
},
{ status: 500 },
);
}
}
Reference: app/api/email/route.ts:14-68
Supported email services
The application supports integration with various email service providers:
Resend (recommended)
Resend is a modern email API with excellent developer experience.
Installation:
Environment variables:
RESEND_API_KEY=re_123456789
Implementation example:
import { Resend } from 'resend';
const resend = new Resend(process.env.RESEND_API_KEY);
await resend.emails.send({
from: '[email protected]',
to: recipientEmail,
subject: subject || 'Your Invoice',
html: '<p>Your invoice is attached</p>',
attachments: [
{
filename: `invoice-${invoiceNumber}.pdf`,
content: pdfBuffer,
},
],
});
SendGrid
SendGrid is a popular email delivery platform with robust features.
Installation:
npm install @sendgrid/mail
Environment variables:
SENDGRID_API_KEY=SG.123456789
Implementation example:
import sgMail from '@sendgrid/mail';
sgMail.setApiKey(process.env.SENDGRID_API_KEY!);
await sgMail.send({
from: '[email protected]',
to: recipientEmail,
subject: subject || 'Your Invoice',
html: '<p>Your invoice is attached</p>',
attachments: [
{
content: pdfBuffer.toString('base64'),
filename: `invoice-${invoiceNumber}.pdf`,
type: 'application/pdf',
disposition: 'attachment',
},
],
});
Mailgun
Mailgun offers powerful email APIs and analytics.
Installation:
npm install mailgun.js form-data
Environment variables:
MAILGUN_API_KEY=your-api-key
MAILGUN_DOMAIN=yourdomain.com
Implementation example:
import formData from 'form-data';
import Mailgun from 'mailgun.js';
const mailgun = new Mailgun(formData);
const mg = mailgun.client({
username: 'api',
key: process.env.MAILGUN_API_KEY!,
});
await mg.messages.create(process.env.MAILGUN_DOMAIN!, {
from: '[email protected]',
to: recipientEmail,
subject: subject || 'Your Invoice',
html: '<p>Your invoice is attached</p>',
attachment: pdfBuffer,
});
AWS SES
AWS SES is Amazon’s scalable email service.
Installation:
npm install @aws-sdk/client-ses
Environment variables:
AWS_REGION=us-east-1
AWS_ACCESS_KEY_ID=your-access-key
AWS_SECRET_ACCESS_KEY=your-secret-key
Implementation example:
import { SESClient, SendRawEmailCommand } from '@aws-sdk/client-ses';
const ses = new SESClient({ region: process.env.AWS_REGION });
const message = [
`From: [email protected]`,
`To: ${recipientEmail}`,
`Subject: ${subject || 'Your Invoice'}`,
`MIME-Version: 1.0`,
`Content-Type: multipart/mixed; boundary="boundary"`,
``,
`--boundary`,
`Content-Type: text/html; charset=UTF-8`,
``,
`<p>Your invoice is attached</p>`,
`--boundary`,
`Content-Type: application/pdf; name="invoice.pdf"`,
`Content-Disposition: attachment; filename="invoice.pdf"`,
`Content-Transfer-Encoding: base64`,
``,
pdfBuffer.toString('base64'),
`--boundary--`,
].join('\n');
await ses.send(new SendRawEmailCommand({
RawMessage: { Data: Buffer.from(message) },
}));
Reference: app/api/email/route.ts:5-9
Implementation steps
To implement email sending:
Choose an email service
Select one of the supported email services (Resend, SendGrid, Mailgun, or AWS SES) based on your needs and budget.
Install the library
Install the corresponding npm package:npm install resend
# or
npm install @sendgrid/mail
# or
npm install mailgun.js form-data
# or
npm install @aws-sdk/client-ses
Configure environment variables
Add the required API keys and configuration to your .env.local file.
Create email template
Design an HTML email template for invoice delivery. Include:
- Company branding
- Invoice summary (number, amount, due date)
- Payment instructions
- Professional formatting
Update the email route
Replace the TODO section in app/api/email/route.ts with your chosen service implementation.
Generate and attach PDF
Get the invoice data from the database, generate a PDF using the existing PDF API, and attach it to the email.
Update invoice status
After successfully sending the email, update the invoice status to “sent” in the database.
Test thoroughly
Test email sending with various scenarios:
- Valid and invalid email addresses
- Different invoice types
- PDF attachment generation
- Error handling
Email template example
Here’s a basic HTML email template for invoices:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
.container { max-width: 600px; margin: 0 auto; padding: 20px; }
.header { background-color: #f4f4f4; padding: 20px; text-align: center; }
.content { padding: 20px; }
.invoice-details { background-color: #f9f9f9; padding: 15px; margin: 20px 0; }
.footer { text-align: center; color: #666; font-size: 12px; margin-top: 30px; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>Invoice from {{companyName}}</h1>
</div>
<div class="content">
<p>Hello {{customerName}},</p>
<p>Thank you for your business. Please find your invoice attached to this email.</p>
<div class="invoice-details">
<p><strong>Invoice Number:</strong> {{invoiceNumber}}</p>
<p><strong>Invoice Date:</strong> {{invoiceDate}}</p>
<p><strong>Due Date:</strong> {{dueDate}}</p>
<p><strong>Amount Due:</strong> {{currency}} {{totalAmount}}</p>
</div>
<p><strong>Payment Instructions:</strong></p>
<p>{{paymentInstructions}}</p>
<p>If you have any questions about this invoice, please contact us.</p>
<p>Best regards,<br>{{companyName}}</p>
</div>
<div class="footer">
<p>This is an automated message. Please do not reply to this email.</p>
</div>
</div>
</body>
</html>
Complete implementation example
Here’s a complete implementation using Resend:
import { NextRequest, NextResponse } from "next/server";
import { Resend } from 'resend';
import db from "@/app/lib/turso";
const resend = new Resend(process.env.RESEND_API_KEY);
export async function POST(request: NextRequest) {
try {
const body = await request.json();
const { invoiceId, recipientEmail, subject, message } = body;
// Validate inputs
if (!invoiceId || !recipientEmail) {
return NextResponse.json(
{ error: "Invoice ID and recipient email are required" },
{ status: 400 },
);
}
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(recipientEmail)) {
return NextResponse.json(
{ error: "Invalid email address" },
{ status: 400 },
);
}
// Get invoice data
const result = await db.execute({
sql: "SELECT * FROM invoices WHERE id = ? LIMIT 1",
args: [invoiceId],
});
const invoice = result.rows[0];
if (!invoice) {
return NextResponse.json(
{ error: "Invoice not found" },
{ status: 404 },
);
}
// Generate PDF (call your PDF API)
const pdfResponse = await fetch(`${process.env.NEXT_PUBLIC_URL}/api/pdf`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ invoiceId }),
});
if (!pdfResponse.ok) {
throw new Error('Failed to generate PDF');
}
const pdfBuffer = await pdfResponse.arrayBuffer();
// Send email
await resend.emails.send({
from: '[email protected]',
to: recipientEmail,
subject: subject || `Invoice ${invoice.invoice_number}`,
html: `<p>${message || 'Please find your invoice attached.'}</p>`,
attachments: [
{
filename: `invoice-${invoice.invoice_number}.pdf`,
content: Buffer.from(pdfBuffer),
},
],
});
// Update invoice status
await db.execute({
sql: "UPDATE invoices SET status = 'sent', updated_at = datetime('now') WHERE id = ?",
args: [invoiceId],
});
return NextResponse.json(
{
message: "Invoice sent successfully",
invoiceId,
recipientEmail,
},
{ status: 200 },
);
} catch (error) {
console.error('[email]', error);
return NextResponse.json(
{
error: error instanceof Error ? error.message : "Failed to send email",
},
{ status: 500 },
);
}
}
Environment variables
Add the appropriate environment variables for your chosen service:
# Resend
RESEND_API_KEY=re_123456789
# SendGrid
SENDGRID_API_KEY=SG.123456789
# Mailgun
MAILGUN_API_KEY=your-api-key
MAILGUN_DOMAIN=yourdomain.com
# AWS SES
AWS_REGION=us-east-1
AWS_ACCESS_KEY_ID=your-access-key
AWS_SECRET_ACCESS_KEY=your-secret-key
# Application URL (for PDF generation)
NEXT_PUBLIC_URL=http://localhost:3000
Security considerations
Follow these security best practices when implementing email sending:
- Validate email addresses - Always validate recipient emails to prevent abuse
- Rate limiting - Implement rate limiting to prevent spam
- Authentication - Require authentication before sending emails
- SPF/DKIM/DMARC - Configure email authentication records for your domain
- Secure API keys - Never expose API keys in client-side code
- Input sanitization - Sanitize user input to prevent injection attacks
- Attachment limits - Limit attachment sizes to prevent abuse
Domain verification
Most email services require domain verification:
- Add DNS records - Add TXT, SPF, DKIM records to your domain DNS
- Verify ownership - Complete the verification process in your email service dashboard
- Set up SPF - Add SPF record to authorize your email service
- Enable DKIM - Configure DKIM signing for email authentication
- Configure DMARC - Set up DMARC policy for email security
Testing
Test email sending thoroughly:
curl -X POST http://localhost:3000/api/email \
-H "Content-Type: application/json" \
-d '{
"invoiceId": "test-invoice-id",
"recipientEmail": "[email protected]",
"subject": "Test Invoice",
"message": "This is a test invoice."
}'
Troubleshooting
Email not sending
- Check API keys are correct and active
- Verify domain is verified in your email service
- Check rate limits haven’t been exceeded
- Review error logs for specific error messages
Emails going to spam
- Configure SPF, DKIM, and DMARC records
- Use a verified domain for the sender address
- Avoid spam trigger words in subject and content
- Maintain a good sender reputation
PDF attachment issues
- Verify PDF is generating correctly
- Check file size limits for your email service
- Ensure PDF buffer encoding is correct
- Test PDF attachment separately
Next steps
After implementing email sending:
- Add email tracking (opens, clicks)
- Implement email templates for different invoice types
- Add scheduling for automatic invoice delivery
- Create email logs for sent invoices
- Add retry logic for failed sends
- Implement email preferences for customers