SvelteKit
Complete guide to using Unsent in SvelteKit applications with form actions and server endpoints
The Unsent SDK integrates seamlessly with SvelteKit, leveraging form actions and server-side functions. This guide covers everything from basic email sending to advanced features like campaigns, webhooks, and analytics.
Prerequisites
Node.js 16+: SvelteKit requires Node.js 16 or higher
Unsent API Key: Generate one in your Unsent dashboard
Verified Domain: Set up a domain in the Domains section
Installation
Install the SDK using your preferred package manager:
npm install @unsent/sdkpnpm add @unsent/sdkyarn add @unsent/sdkbun add @unsent/sdkConfiguration
Add your Unsent API key to .env:
UNSENT_API_KEY=un_...Add .env to your .gitignore if it's not already there to keep your API key secure.
Quick Start
Create a form action to send emails:
import { Unsent } from '@unsent/sdk';
import { UNSENT_API_KEY } from '$env/static/private';
import { fail } from '@sveltejs/kit';
const unsent = new Unsent(UNSENT_API_KEY);
export const actions = {
default: async ({ request }) => {
const formData = await request.formData();
const email = formData.get('email') as string;
const { data, error } = await unsent.emails.send({
from: 'Acme <onboarding@unsent.dev>',
to: email,
subject: 'Welcome!',
html: '<h1>Hello!</h1><p>Thanks for signing up.</p>',
});
if (error) {
return fail(400, { error: error.message });
}
return { success: true, emailId: data.emailId };
}
};Create the form in your page:
<script>
export let form;
</script>
<form method="POST">
<input type="email" name="email" required placeholder="Enter your email" />
<button type="submit">Send Email</button>
</form>
{#if form?.success}
<p>Email sent successfully!</p>
{/if}
{#if form?.error}
<p>Error: {form.error}</p>
{/if}Email Operations
Send with Attachments
import { Unsent } from '@unsent/sdk';
import { UNSENT_API_KEY } from '$env/static/private';
import { json } from '@sveltejs/kit';
const unsent = new Unsent(UNSENT_API_KEY);
export async function POST({ request }) {
const { data, error } = await unsent.emails.send({
from: 'Billing <billing@yourdomain.com>',
to: 'customer@example.com',
subject: 'Your Invoice',
html: '<p>Please find your invoice attached.</p>',
attachments: [{
filename: 'invoice.pdf',
content: 'base64-encoded-content',
contentType: 'application/pdf'
}]
});
if (error) {
return json({ error }, { status: 400 });
}
return json(data);
}Schedule Emails
import { Unsent } from '@unsent/sdk';
import { UNSENT_API_KEY } from '$env/static/private';
import { json } from '@sveltejs/kit';
const unsent = new Unsent(UNSENT_API_KEY);
export async function POST({ request }) {
const scheduledTime = new Date();
scheduledTime.setHours(scheduledTime.getHours() + 24);
const { data, error } = await unsent.emails.send({
from: 'Reminders <reminders@yourdomain.com>',
to: 'user@example.com',
subject: 'Meeting Reminder',
html: '<p>Your meeting is tomorrow at 10 AM</p>',
scheduledAt: scheduledTime.toISOString()
});
return json({ data, error });
}Batch Sending
Send up to 100 emails in one request:
import { Unsent } from '@unsent/sdk';
import { UNSENT_API_KEY } from '$env/static/private';
import { json } from '@sveltejs/kit';
const unsent = new Unsent(UNSENT_API_KEY);
export async function POST({ request }) {
const { users } = await request.json();
const emails = users.map(user => ({
from: 'Newsletter <news@yourdomain.com>',
to: user.email,
subject: 'Monthly Update',
html: `<h1>Hi ${user.name}!</h1><p>Here's what's new...</p>`
}));
const { data, error } = await unsent.emails.batch(emails);
return json({ data, error });
}List Emails
import { Unsent } from '@unsent/sdk';
import { UNSENT_API_KEY } from '$env/static/private';
import { json } from '@sveltejs/kit';
const unsent = new Unsent(UNSENT_API_KEY);
export async function GET({ url }) {
const page = parseInt(url.searchParams.get('page') || '1');
const { data, count, error } = await unsent.emails.list({
page,
limit: 50
});
return json({ data, count, error });
}Get Email Status
import { Unsent } from '@unsent/sdk';
import { UNSENT_API_KEY } from '$env/static/private';
import { json } from '@sveltejs/kit';
const unsent = new Unsent(UNSENT_API_KEY);
export async function GET({ params }) {
const { data, error } = await unsent.emails.get(params.id);
return json({ data, error });
}Contact Management
Create Contact Book
import { Unsent } from '@unsent/sdk';
import { UNSENT_API_KEY } from '$env/static/private';
import { json } from '@sveltejs/kit';
const unsent = new Unsent(UNSENT_API_KEY);
export async function POST({ request }) {
const { name, emoji } = await request.json();
const { data, error } = await unsent.contactBooks.create({
name,
emoji
});
return json({ data, error });
}
export async function GET() {
const { data, error } = await unsent.contactBooks.list();
return json({ data, error });
}Manage Contacts
import { Unsent } from '@unsent/sdk';
import { UNSENT_API_KEY } from '$env/static/private';
import { json } from '@sveltejs/kit';
const unsent = new Unsent(UNSENT_API_KEY);
export async function POST({ request }) {
const { bookId, contact } = await request.json();
const { data, error } = await unsent.contacts.create(bookId, {
email: contact.email,
firstName: contact.firstName,
lastName: contact.lastName,
subscribed: true,
metadata: contact.metadata
});
return json({ data, error });
}Upsert Contact
import { Unsent } from '@unsent/sdk';
import { UNSENT_API_KEY } from '$env/static/private';
import { json } from '@sveltejs/kit';
const unsent = new Unsent(UNSENT_API_KEY);
export async function POST({ request }) {
const { bookId, contactId, contact } = await request.json();
const { data, error } = await unsent.contacts.upsert(
bookId,
contactId,
contact
);
return json({ data, error });
}Campaign Management
Create Campaign
import { Unsent } from '@unsent/sdk';
import { UNSENT_API_KEY } from '$env/static/private';
import { json } from '@sveltejs/kit';
const unsent = new Unsent(UNSENT_API_KEY);
export async function POST({ request }) {
const { campaign } = await request.json();
const { data, error } = await unsent.campaigns.create({
name: campaign.name,
subject: campaign.subject,
html: campaign.html,
from: 'Newsletter <news@yourdomain.com>',
contactBookId: campaign.bookId,
replyTo: 'support@yourdomain.com'
});
return json({ data, error });
}
export async function GET() {
const { data, error } = await unsent.campaigns.list();
return json({ data, error });
}Schedule Campaign
import { Unsent } from '@unsent/sdk';
import { UNSENT_API_KEY } from '$env/static/private';
import { json } from '@sveltejs/kit';
const unsent = new Unsent(UNSENT_API_KEY);
export async function POST({ params, request }) {
const { scheduledAt } = await request.json();
const { data, error } = await unsent.campaigns.schedule(params.id, {
scheduledAt,
batchSize: 1000,
batchInterval: 60
});
return json({ data, error });
}Domain Management
List and Create Domains
import { Unsent } from '@unsent/sdk';
import { UNSENT_API_KEY } from '$env/static/private';
import { json } from '@sveltejs/kit';
const unsent = new Unsent(UNSENT_API_KEY);
export async function GET() {
const { data, error } = await unsent.domains.list();
return json({ data, error });
}
export async function POST({ request }) {
const { name, region } = await request.json();
const { data, error } = await unsent.domains.create({
name,
region
});
return json({ data, error });
}Verify Domain
import { Unsent } from '@unsent/sdk';
import { UNSENT_API_KEY } from '$env/static/private';
import { json } from '@sveltejs/kit';
const unsent = new Unsent(UNSENT_API_KEY);
export async function POST({ params }) {
const { data, error } = await unsent.domains.verify(params.id);
return json({ data, error });
}Templates
Create Template
import { Unsent } from '@unsent/sdk';
import { UNSENT_API_KEY } from '$env/static/private';
import { json } from '@sveltejs/kit';
const unsent = new Unsent(UNSENT_API_KEY);
export async function POST({ request }) {
const { template } = await request.json();
const { data, error } = await unsent.templates.create({
name: template.name,
subject: template.subject,
html: template.html,
content: template.content
});
return json({ data, error });
}
export async function GET() {
const { data, error } = await unsent.templates.list();
return json({ data, error });
}Send with Template
import { Unsent } from '@unsent/sdk';
import { UNSENT_API_KEY } from '$env/static/private';
import { json } from '@sveltejs/kit';
const unsent = new Unsent(UNSENT_API_KEY);
export async function POST({ request }) {
const { to, templateId, variables } = await request.json();
const { data, error } = await unsent.emails.send({
from: 'Hello <hello@yourdomain.com>',
to,
templateId,
variables
});
return json({ data, error });
}Analytics
Get Account Analytics
import { Unsent } from '@unsent/sdk';
import { UNSENT_API_KEY } from '$env/static/private';
import { json } from '@sveltejs/kit';
const unsent = new Unsent(UNSENT_API_KEY);
export async function GET() {
const { data, error } = await unsent.analytics.get();
return json({ data, error });
}Get Time Series Data
import { Unsent } from '@unsent/sdk';
import { UNSENT_API_KEY } from '$env/static/private';
import { json } from '@sveltejs/kit';
const unsent = new Unsent(UNSENT_API_KEY);
export async function GET({ url }) {
const days = parseInt(url.searchParams.get('days') || '30');
const { data, error } = await unsent.analytics.getTimeSeries({ days });
return json({ data, error });
}Get Reputation Score
import { Unsent } from '@unsent/sdk';
import { UNSENT_API_KEY } from '$env/static/private';
import { json } from '@sveltejs/kit';
const unsent = new Unsent(UNSENT_API_KEY);
export async function GET() {
const { data, error } = await unsent.analytics.getReputation();
return json({ data, error });
}Webhooks
Manage Webhooks
import { Unsent } from '@unsent/sdk';
import { UNSENT_API_KEY } from '$env/static/private';
import { json } from '@sveltejs/kit';
const unsent = new Unsent(UNSENT_API_KEY);
export async function GET() {
const { data, error } = await unsent.webhooks.list();
return json({ data, error });
}
export async function POST({ request }) {
const { url, events } = await request.json();
const { data, error } = await unsent.webhooks.create({
url,
events
});
return json({ data, error });
}Receive Webhook Events
import { json } from '@sveltejs/kit';
export async function POST({ request }) {
const event = await request.json();
// Handle different event types
switch (event.type) {
case 'email.delivered':
console.log('Email delivered:', event.data.emailId);
break;
case 'email.bounced':
console.log('Email bounced:', event.data.emailId);
break;
case 'email.opened':
console.log('Email opened:', event.data.emailId);
break;
case 'email.clicked':
console.log('Email clicked:', event.data.emailId);
break;
}
return json({ received: true });
}Suppression Management
Manage Suppressions
import { Unsent } from '@unsent/sdk';
import { UNSENT_API_KEY } from '$env/static/private';
import { json } from '@sveltejs/kit';
const unsent = new Unsent(UNSENT_API_KEY);
export async function GET() {
const { data, error } = await unsent.suppressions.list({
page: 1,
limit: 50
});
return json({ data, error });
}
export async function POST({ request }) {
const { email, reason } = await request.json();
const { data, error } = await unsent.suppressions.add({
email,
reason
});
return json({ data, error });
}
export async function DELETE({ request }) {
const { email } = await request.json();
const { data, error } = await unsent.suppressions.delete(email);
return json({ data, error });
}API Key Management
Manage API Keys
import { Unsent } from '@unsent/sdk';
import { UNSENT_API_KEY } from '$env/static/private';
import { json } from '@sveltejs/kit';
const unsent = new Unsent(UNSENT_API_KEY);
export async function GET() {
const { data, error } = await unsent.apiKeys.list();
return json({ data, error });
}
export async function POST({ request }) {
const { name, permission } = await request.json();
const { data, error } = await unsent.apiKeys.create({
name,
permission // 'FULL', 'SENDING', or 'READONLY'
});
return json({ data, error });
}Form Actions
Enhanced Form Action
Use enhanced form actions for progressive enhancement:
import { Unsent } from '@unsent/sdk';
import { UNSENT_API_KEY } from '$env/static/private';
import { fail, redirect } from '@sveltejs/kit';
const unsent = new Unsent(UNSENT_API_KEY);
export const actions = {
subscribe: async ({ request }) => {
const formData = await request.formData();
const email = formData.get('email') as string;
if (!email || !email.includes('@')) {
return fail(400, { email, missing: true });
}
const { data, error } = await unsent.emails.send({
from: 'Newsletter <news@yourdomain.com>',
to: email,
subject: 'Thanks for subscribing!',
html: '<h1>Welcome!</h1><p>You are now subscribed to our newsletter.</p>'
});
if (error) {
return fail(500, { email, error: error.message });
}
throw redirect(303, '/thanks');
}
};<script>
import { enhance } from '$app/forms';
export let form;
</script>
<form method="POST" action="?/subscribe" use:enhance>
<input
type="email"
name="email"
value={form?.email ?? ''}
required
placeholder="your@email.com"
/>
{#if form?.missing}
<p class="error">Email is required</p>
{/if}
{#if form?.error}
<p class="error">{form.error}</p>
{/if}
<button type="submit">Subscribe</button>
</form>Error Handling
Comprehensive Error Handling
import { Unsent } from '@unsent/sdk';
import { UNSENT_API_KEY } from '$env/static/private';
import { json } from '@sveltejs/kit';
const unsent = new Unsent(UNSENT_API_KEY);
export async function POST({ request }) {
try {
const { email } = await request.json();
const { data, error } = await unsent.emails.send({
from: 'Hello <hello@yourdomain.com>',
to: email,
subject: 'Test',
html: '<p>Test</p>'
});
if (error) {
switch (error.code) {
case 'VALIDATION_ERROR':
return json({ error: 'Invalid email data' }, { status: 400 });
case 'RATE_LIMIT_EXCEEDED':
return json(
{ error: 'Too many requests', retryAfter: error.retryAfter },
{ status: 429 }
);
case 'DOMAIN_NOT_VERIFIED':
return json({ error: 'Domain not verified' }, { status: 403 });
default:
return json({ error: error.message }, { status: 500 });
}
}
return json(data);
} catch (err) {
return json({ error: 'Internal server error' }, { status: 500 });
}
}Best Practices
Singleton Pattern
Create a singleton to reuse the Unsent instance:
import { Unsent } from '@unsent/sdk';
import { UNSENT_API_KEY } from '$env/static/private';
let unsentInstance: Unsent | null = null;
export function getUnsent() {
if (!unsentInstance) {
if (!UNSENT_API_KEY) {
throw new Error('UNSENT_API_KEY is not defined');
}
unsentInstance = new Unsent(UNSENT_API_KEY);
}
return unsentInstance;
}Use in your endpoints:
import { getUnsent } from '$lib/server/unsent';
import { json } from '@sveltejs/kit';
export async function POST({ request }) {
const unsent = getUnsent();
// Use unsent...
}Idempotency Keys
Use idempotency keys to prevent duplicate sends:
import { getUnsent } from '$lib/server/unsent';
import { json } from '@sveltejs/kit';
export async function POST({ request }) {
const unsent = getUnsent();
const { userId, email } = await request.json();
const { data, error } = await unsent.emails.send(
{
from: 'Welcome <hello@yourdomain.com>',
to: email,
subject: 'Welcome!',
html: '<p>Thanks for signing up!</p>'
},
{
idempotencyKey: `signup-${userId}`
}
);
return json({ data, error });
}Type Safety
Leverage TypeScript for type safety:
import { getUnsent } from '$lib/server/unsent';
import { json } from '@sveltejs/kit';
import type { SendEmailPayload } from '@unsent/sdk';
export async function POST({ request }) {
const unsent = getUnsent();
const body = await request.json();
const emailPayload: SendEmailPayload = {
from: 'Hello <hello@yourdomain.com>',
to: body.to,
subject: body.subject,
html: body.html
};
const { data, error } = await unsent.emails.send(emailPayload);
return json({ data, error });
}Load Functions
Use load functions to fetch data server-side:
import { getUnsent } from '$lib/server/unsent';
export async function load() {
const unsent = getUnsent();
const { data, count, error } = await unsent.emails.list({
page: 1,
limit: 20
});
return {
emails: data,
totalCount: count,
error
};
}<script>
export let data;
</script>
<h1>Emails ({data.totalCount})</h1>
{#if data.error}
<p>Error loading emails: {data.error.message}</p>
{:else}
<ul>
{#each data.emails as email}
<li>{email.subject} - {email.status}</li>
{/each}
</ul>
{/if}