The Dual-Write Problem in Transactional Email
If you create a user in your database and then immediately call an email API, a network failure or server crash leaves your system in an inconsistent state: the user exists, but the verification email was never sent.
The Transactional Outbox pattern solves this by writing the email job into an outbox_messages database table inside the exact same atomic database transaction.
Prisma Implementation with Interactive Transactions
Write user records and outbound email jobs atomically using prisma.$transaction().
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
export async function registerUserWithOutbox(email: string, name: string) {
return await prisma.$transaction(async (tx) => {
// 1. Create user record
const user = await tx.user.create({
data: { email, name },
});
// 2. Atomically insert email job into outbox
await tx.outboxMessage.create({
data: {
to: email,
subject: 'Verify your email address',
body: `Hello ${name}, please click here to verify your account.`,
status: 'PENDING',
},
});
return user;
});
}
// Background poller picks up pending messages with SKIP LOCKED
export async function pollOutboxQueue() {
const pending = await prisma.outboxMessage.findMany({
where: { status: 'PENDING' },
take: 10,
});
for (const msg of pending) {
const res = await fetch('https://api.sadasend.com/v1/emails', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.SADASEND_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ to: msg.to, subject: msg.subject, text: msg.body }),
});
if (res.ok) {
await prisma.outboxMessage.update({
where: { id: msg.id },
data: { status: 'SENT', sentAt: new Date() },
});
}
}
}Key Benefits of the Outbox Pattern
- 100% Guaranteed Delivery: Even if the email API is down, messages are safely queued in Postgres.
- Zero Phantom Emails: If the database transaction rolls back, no email is ever sent.
- Idempotent Processing: Safe retries using Postgres FOR UPDATE SKIP LOCKED locks.
Building AI agents that send email?
Scoped API keys, per-key recipient allowlists, approval mode and a hosted MCP server with ten tools — on the free plan, without a card.