Prerequisites
To get the most out of this guide, you’ll need to:- Create a SendPost Account
- Next.js 13+ with App Router or Pages Router
- A Sub-Account API Key from your SendPost dashboard
You can find your API keys in your SendPost Dashboard. Make sure to use your Sub-Account API Key for sending emails.
1. Install Dependencies
npm
npm install sendpost-js-sdk
2. Environment Setup
Create or update your.env.local file:
SENDPOST_API_KEY=your_sub_account_api_key
SENDPOST_FROM_EMAIL=hello@playwithsendpost.io
SENDPOST_FROM_NAME=SendPost
3. Send Your First Email
Using API Routes (App Router)
Createapp/api/send-email/route.ts:
TypeScript
import { NextRequest, NextResponse } from 'next/server';
import sendpost from 'sendpost-js-sdk';
const emailApi = new sendpost.EmailApi();
export async function POST(request: NextRequest) {
try {
const { to, subject, htmlBody, textBody } = await request.json();
const emailMessage = new sendpost.EmailMessage();
emailMessage.from = {
email: process.env.SENDPOST_FROM_EMAIL!,
name: process.env.SENDPOST_FROM_NAME
};
emailMessage.to = [{ email: to }];
emailMessage.subject = subject;
emailMessage.htmlBody = htmlBody;
emailMessage.textBody = textBody;
emailMessage.trackOpens = true;
emailMessage.trackClicks = true;
const opts = {
emailMessage: emailMessage,
};
const response = await emailApi.sendEmail(
process.env.SENDPOST_API_KEY!,
opts
);
return NextResponse.json({
success: true,
messageId: response.messageId
});
} catch (error: any) {
console.error('SendPost error:', error);
return NextResponse.json(
{ success: false, error: error.message },
{ status: 500 }
);
}
}
Using Server Actions (App Router)
Createapp/actions/sendEmail.ts:
TypeScript
'use server';
import sendpost from 'sendpost-js-sdk';
const emailApi = new sendpost.EmailApi();
export async function sendEmail({
to,
subject,
htmlBody,
textBody,
}: {
to: string;
subject: string;
htmlBody: string;
textBody?: string;
}) {
try {
const emailMessage = new sendpost.EmailMessage();
emailMessage.from = {
email: process.env.SENDPOST_FROM_EMAIL!,
name: process.env.SENDPOST_FROM_NAME,
};
emailMessage.to = [{ email: to }];
emailMessage.subject = subject;
emailMessage.htmlBody = htmlBody;
emailMessage.textBody = textBody || htmlBody.replace(/<[^>]*>/g, '');
emailMessage.trackOpens = true;
emailMessage.trackClicks = true;
const response = await emailApi.sendEmail(
process.env.SENDPOST_API_KEY!,
{ emailMessage }
);
return { success: true, messageId: response.messageId };
} catch (error: any) {
console.error('SendPost error:', error);
return { success: false, error: error.message };
}
}
4. Common Use Cases
Welcome Email
Createapp/actions/welcomeEmail.ts:
TypeScript
'use server';
import sendpost from 'sendpost-js-sdk';
const emailApi = new sendpost.EmailApi();
export async function sendWelcomeEmail({
email,
firstName,
}: {
email: string;
firstName: string;
}) {
const emailMessage = new sendpost.EmailMessage();
emailMessage.from = {
email: process.env.SENDPOST_FROM_EMAIL!,
name: process.env.SENDPOST_FROM_NAME,
};
emailMessage.to = [
{
email,
name: firstName,
customFields: {
firstName,
},
},
];
emailMessage.subject = 'Welcome to {{firstName}}!';
emailMessage.htmlBody = `
<h1>Welcome, {{firstName}}!</h1>
<p>Thank you for joining us. We're excited to have you on board.</p>
<p>Get started by exploring our features:</p>
<ul>
<li>Feature 1</li>
<li>Feature 2</li>
<li>Feature 3</li>
</ul>
<p>If you have any questions, just reply to this email.</p>
<p><a href="{{unsubscribe}}">Unsubscribe</a></p>
`;
emailMessage.textBody = `
Welcome, {{firstName}}!
Thank you for joining us. We're excited to have you on board.
Get started by exploring our features:
- Feature 1
- Feature 2
- Feature 3
If you have any questions, just reply to this email.
Unsubscribe: {{unsubscribe}}
`;
emailMessage.trackOpens = true;
emailMessage.trackClicks = true;
emailMessage.groups = ['welcome'];
try {
const response = await emailApi.sendEmail(
process.env.SENDPOST_API_KEY!,
{ emailMessage }
);
return { success: true, messageId: response.messageId };
} catch (error: any) {
return { success: false, error: error.message };
}
}
Password Reset Email
Createapp/actions/passwordReset.ts:
TypeScript
'use server';
import sendpost from 'sendpost-js-sdk';
const emailApi = new sendpost.EmailApi();
export async function sendPasswordResetEmail({
email,
resetToken,
firstName,
}: {
email: string;
resetToken: string;
firstName: string;
}) {
const resetUrl = `${process.env.NEXT_PUBLIC_APP_URL}/reset-password?token=${resetToken}`;
const emailMessage = new sendpost.EmailMessage();
emailMessage.from = {
email: process.env.SENDPOST_FROM_EMAIL!,
name: process.env.SENDPOST_FROM_NAME,
};
emailMessage.to = [
{
email,
name: firstName,
customFields: {
firstName,
resetUrl,
},
},
];
emailMessage.subject = 'Reset Your Password';
emailMessage.htmlBody = `
<h2>Hello {{firstName}},</h2>
<p>We received a request to reset your password. Click the button below to create a new password:</p>
<p>
<a href="{{resetUrl}}" style="background-color: #5750EC; color: white; padding: 12px 24px; text-decoration: none; border-radius: 4px; display: inline-block;">
Reset Password
</a>
</p>
<p>Or copy and paste this link into your browser:</p>
<p>{{resetUrl}}</p>
<p>This link will expire in 1 hour.</p>
<p>If you didn't request this, please ignore this email.</p>
`;
emailMessage.textBody = `
Hello {{firstName}},
We received a request to reset your password. Click the link below to create a new password:
{{resetUrl}}
This link will expire in 1 hour.
If you didn't request this, please ignore this email.
`;
emailMessage.trackClicks = true;
emailMessage.groups = ['password-reset'];
try {
const response = await emailApi.sendEmail(
process.env.SENDPOST_API_KEY!,
{ emailMessage }
);
return { success: true, messageId: response.messageId };
} catch (error: any) {
return { success: false, error: error.message };
}
}
Order Confirmation Email
Createapp/actions/orderConfirmation.ts:
TypeScript
'use server';
import sendpost from 'sendpost-js-sdk';
const emailApi = new sendpost.EmailApi();
interface OrderItem {
name: string;
price: number;
quantity: number;
}
export async function sendOrderConfirmation({
email,
firstName,
orderId,
items,
total,
}: {
email: string;
firstName: string;
orderId: string;
items: OrderItem[];
total: number;
}) {
const emailMessage = new sendpost.EmailMessage();
emailMessage.from = {
email: process.env.SENDPOST_FROM_EMAIL!,
name: process.env.SENDPOST_FROM_NAME,
};
emailMessage.to = [
{
email,
name: firstName,
customFields: {
firstName,
orderId,
items,
total: total.toFixed(2),
},
},
];
emailMessage.subject = 'Order {{orderId}} Confirmed';
emailMessage.htmlBody = `
<h1>Thank you for your order, {{firstName}}!</h1>
<p>Your order <strong>{{orderId}}</strong> has been confirmed.</p>
<h2>Order Details</h2>
<table style="width: 100%; border-collapse: collapse;">
<thead>
<tr style="background-color: #f5f5f5;">
<th style="padding: 12px; text-align: left; border: 1px solid #ddd;">Item</th>
<th style="padding: 12px; text-align: left; border: 1px solid #ddd;">Quantity</th>
<th style="padding: 12px; text-align: right; border: 1px solid #ddd;">Price</th>
</tr>
</thead>
<tbody>
{{#each items}}
<tr>
<td style="padding: 12px; border: 1px solid #ddd;">{{name}}</td>
<td style="padding: 12px; border: 1px solid #ddd;">{{quantity}}</td>
<td style="padding: 12px; text-align: right; border: 1px solid #ddd;">${{price}}</td>
</tr>
{{/each}}
</tbody>
<tfoot>
<tr>
<td colspan="2" style="padding: 12px; text-align: right; border: 1px solid #ddd;"><strong>Total:</strong></td>
<td style="padding: 12px; text-align: right; border: 1px solid #ddd;"><strong>${{total}}</strong></td>
</tr>
</tfoot>
</table>
<p>We'll send you a shipping confirmation once your order is on its way.</p>
<p><a href="{{unsubscribe}}">Unsubscribe from order updates</a></p>
`;
emailMessage.trackOpens = true;
emailMessage.trackClicks = true;
emailMessage.groups = ['order-confirmation'];
try {
const response = await emailApi.sendEmail(
process.env.SENDPOST_API_KEY!,
{ emailMessage }
);
return { success: true, messageId: response.messageId };
} catch (error: any) {
return { success: false, error: error.message };
}
}
5. Using in React Components
Contact Form Example
Createapp/components/ContactForm.tsx:
TypeScript
'use client';
import { useState } from 'react';
import { sendEmail } from '@/app/actions/sendEmail';
export default function ContactForm() {
const [status, setStatus] = useState<'idle' | 'sending' | 'success' | 'error'>('idle');
const [message, setMessage] = useState('');
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
setStatus('sending');
setMessage('');
const formData = new FormData(e.currentTarget);
const result = await sendEmail({
to: formData.get('email') as string,
subject: `Contact from ${formData.get('name')}`,
htmlBody: `
<h2>New Contact Form Submission</h2>
<p><strong>Name:</strong> ${formData.get('name')}</p>
<p><strong>Email:</strong> ${formData.get('email')}</p>
<p><strong>Message:</strong></p>
<p>${formData.get('message')}</p>
`,
});
if (result.success) {
setStatus('success');
setMessage('Email sent successfully!');
e.currentTarget.reset();
} else {
setStatus('error');
setMessage(result.error || 'Failed to send email');
}
}
return (
<form onSubmit={handleSubmit} className="space-y-4">
<div>
<label htmlFor="name">Name</label>
<input
type="text"
id="name"
name="name"
required
className="w-full px-4 py-2 border rounded"
/>
</div>
<div>
<label htmlFor="email">Email</label>
<input
type="email"
id="email"
name="email"
required
className="w-full px-4 py-2 border rounded"
/>
</div>
<div>
<label htmlFor="message">Message</label>
<textarea
id="message"
name="message"
required
rows={5}
className="w-full px-4 py-2 border rounded"
/>
</div>
<button
type="submit"
disabled={status === 'sending'}
className="px-6 py-2 bg-[#5750EC] text-white rounded disabled:opacity-50"
>
{status === 'sending' ? 'Sending...' : 'Send Message'}
</button>
{message && (
<p className={status === 'success' ? 'text-green-600' : 'text-red-600'}>
{message}
</p>
)}
</form>
);
}
6. Error Handling & Best Practices
Create a Reusable Email Service
Createlib/sendpost.ts:
TypeScript
import sendpost from 'sendpost-js-sdk';
const emailApi = new sendpost.EmailApi();
interface SendEmailOptions {
to: string | Array<{ email: string; name?: string; customFields?: Record<string, any> }>;
subject: string;
htmlBody: string;
textBody?: string;
from?: { email: string; name?: string };
groups?: string[];
trackOpens?: boolean;
trackClicks?: boolean;
}
export async function sendEmailWithSendPost(options: SendEmailOptions) {
try {
const emailMessage = new sendpost.EmailMessage();
emailMessage.from = options.from || {
email: process.env.SENDPOST_FROM_EMAIL!,
name: process.env.SENDPOST_FROM_NAME,
};
// Handle single email or array of recipients
emailMessage.to = Array.isArray(options.to)
? options.to
: [{ email: options.to }];
emailMessage.subject = options.subject;
emailMessage.htmlBody = options.htmlBody;
emailMessage.textBody = options.textBody || options.htmlBody.replace(/<[^>]*>/g, '');
emailMessage.trackOpens = options.trackOpens ?? true;
emailMessage.trackClicks = options.trackClicks ?? true;
if (options.groups) {
emailMessage.groups = options.groups;
}
const response = await emailApi.sendEmail(
process.env.SENDPOST_API_KEY!,
{ emailMessage }
);
return {
success: true,
messageId: response.messageId,
data: response,
};
} catch (error: any) {
console.error('SendPost error:', error);
return {
success: false,
error: error.message || 'Failed to send email',
details: error,
};
}
}
Make sure your sender email domain is verified in your SendPost account before sending emails.
7. Next Steps
Explore more examples and use cases:Next.js Documentation
Learn more about Next.js App Router and Server Actions
SendPost JavaScript SDK
View the official SDK package on npm
Quickstart Example
Complete working example from this quickstart guide