Prerequisites
To get the most out of this guide, you’ll need to:- Create a SendPost Account
- Nuxt.js 3+
- 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 file:
SENDPOST_API_KEY=your_sub_account_api_key
SENDPOST_FROM_EMAIL=hello@playwithsendpost.io
SENDPOST_FROM_NAME=SendPost
3. Create Server Utility
Createserver/utils/sendpost.ts:
TypeScript
import sendpost from 'sendpost-js-sdk';
const emailApi = new sendpost.EmailApi();
export async function sendEmail({
to,
subject,
htmlBody,
textBody,
from,
groups,
trackOpens = true,
trackClicks = true,
customFields,
}: {
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;
customFields?: Record<string, any>;
}) {
try {
const emailMessage = new sendpost.EmailMessage();
emailMessage.from = from || {
email: process.env.SENDPOST_FROM_EMAIL || 'hello@playwithsendpost.io',
name: process.env.SENDPOST_FROM_NAME || 'SendPost',
};
// Handle single email or array of recipients
if (typeof to === 'string') {
emailMessage.to = [{ email: to }];
} else {
emailMessage.to = to.map(recipient => {
if (typeof recipient === 'string') {
return { email: recipient };
}
return {
email: recipient.email,
name: recipient.name,
customFields: recipient.customFields || customFields,
};
});
}
emailMessage.subject = subject;
emailMessage.htmlBody = htmlBody;
emailMessage.textBody = textBody || htmlBody.replace(/<[^>]*>/g, '');
emailMessage.trackOpens = trackOpens;
emailMessage.trackClicks = trackClicks;
if (groups) {
emailMessage.groups = 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,
};
}
}
4. Send Your First Email
Using Server API Route
Createserver/api/send-email.post.ts:
TypeScript
import { sendEmail } from '~/server/utils/sendpost';
export default defineEventHandler(async (event) => {
try {
const body = await readBody(event);
const { to, subject, htmlBody, textBody } = body;
if (!to || !subject || !htmlBody) {
throw createError({
statusCode: 400,
statusMessage: 'Missing required fields: to, subject, htmlBody',
});
}
const result = await sendEmail({
to,
subject,
htmlBody,
textBody,
});
if (result.success) {
return {
success: true,
messageId: result.messageId,
};
} else {
throw createError({
statusCode: 500,
statusMessage: result.error,
});
}
} catch (error: any) {
throw createError({
statusCode: error.statusCode || 500,
statusMessage: error.message || 'Failed to send email',
});
}
});
Using Server Action
Createserver/actions/email.ts:
TypeScript
import { sendEmail } from '~/server/utils/sendpost';
export async function sendWelcomeEmail(email: string, firstName: string) {
return await sendEmail({
to: {
email,
name: firstName,
customFields: { firstName },
},
subject: 'Welcome {{firstName}}!',
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>
`,
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}}
`,
groups: ['welcome'],
});
}
5. Common Use Cases
Welcome Email API Route
Createserver/api/welcome-email.post.ts:
TypeScript
import { sendEmail } from '~/server/utils/sendpost';
export default defineEventHandler(async (event) => {
try {
const { email, firstName } = await readBody(event);
if (!email || !firstName) {
throw createError({
statusCode: 400,
statusMessage: 'Missing required fields: email, firstName',
});
}
const result = await sendEmail({
to: {
email,
name: firstName,
customFields: { firstName },
},
subject: 'Welcome {{firstName}}!',
htmlBody: `
<h1>Welcome, {{firstName}}!</h1>
<p>Thank you for joining us. We're excited to have you on board.</p>
<p><a href="{{unsubscribe}}">Unsubscribe</a></p>
`,
groups: ['welcome'],
});
return {
success: result.success,
messageId: result.messageId,
};
} catch (error: any) {
throw createError({
statusCode: error.statusCode || 500,
statusMessage: error.message,
});
}
});
Password Reset Email
Createserver/api/password-reset.post.ts:
TypeScript
import { sendEmail } from '~/server/utils/sendpost';
export default defineEventHandler(async (event) => {
try {
const { email, resetToken, firstName } = await readBody(event);
const config = useRuntimeConfig();
const resetUrl = `${config.public.appUrl}/reset-password?token=${resetToken}`;
const result = await sendEmail({
to: {
email,
name: firstName || 'User',
customFields: {
firstName: firstName || 'User',
resetUrl,
},
},
subject: 'Reset Your Password',
htmlBody: `
<h2>Hello {{firstName}},</h2>
<p>We received a request to reset your password. Click the button below:</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: {{resetUrl}}</p>
<p>This link will expire in 1 hour.</p>
`,
groups: ['password-reset'],
trackClicks: true,
});
return {
success: result.success,
messageId: result.messageId,
};
} catch (error: any) {
throw createError({
statusCode: error.statusCode || 500,
statusMessage: error.message,
});
}
});
6. Using in Components
Client-Side Form
Createpages/contact.vue:
Vue
<template>
<form @submit.prevent="handleSubmit">
<input v-model="form.email" type="email" placeholder="Email" required />
<input v-model="form.name" type="text" placeholder="Name" required />
<textarea v-model="form.message" placeholder="Message" required></textarea>
<button type="submit" :disabled="loading">
{{ loading ? 'Sending...' : 'Send' }}
</button>
<div v-if="message" :class="messageType">
{{ message }}
</div>
</form>
</template>
<script setup lang="ts">
const form = reactive({
email: '',
name: '',
message: '',
});
const loading = ref(false);
const message = ref('');
const messageType = ref('');
async function handleSubmit() {
loading.value = true;
message.value = '';
try {
const { data } = await $fetch('/api/send-email', {
method: 'POST',
body: {
to: 'contact@yourdomain.com',
subject: `Contact from ${form.name}`,
htmlBody: `
<h2>New Contact Form Submission</h2>
<p><strong>Name:</strong> ${form.name}</p>
<p><strong>Email:</strong> ${form.email}</p>
<p><strong>Message:</strong></p>
<p>${form.message.replace(/\n/g, '<br>')}</p>
`,
},
});
if (data.success) {
message.value = 'Message sent successfully!';
messageType.value = 'success';
form.email = '';
form.name = '';
form.message = '';
}
} catch (error: any) {
message.value = error.data?.message || 'Failed to send message';
messageType.value = 'error';
} finally {
loading.value = false;
}
}
</script>
7. Runtime Config
Add tonuxt.config.ts:
TypeScript
export default defineNuxtConfig({
runtimeConfig: {
sendpostApiKey: process.env.SENDPOST_API_KEY,
sendpostFromEmail: process.env.SENDPOST_FROM_EMAIL || 'hello@playwithsendpost.io',
sendpostFromName: process.env.SENDPOST_FROM_NAME || 'SendPost',
public: {
appUrl: process.env.APP_URL || 'https://yourapp.com',
},
},
});
Make sure your sender email domain is verified in your SendPost account before sending emails.
8. Next Steps
Explore more examples and use cases:Nuxt.js Documentation
Learn more about Nuxt.js framework
SendPost JavaScript SDK
View the official SDK package on npm
Personalization Guide
Learn how to personalize emails with Handlebars templates