Email for SaaS products.
Password resets, receipts, and plan-change notifications through one typed API. Versioned templates, signed delivery events, and a conversation thread when a customer replies.
One call out, a signed event back.
A typed send call returns a message id right away. When the recipient's provider confirms delivery, Samva posts a signed event to your webhook, and your product database updates from that signal.
POST https://api.samva.dev/v1/messages
{ to: [{ email: "ada@example.com" }],
channel: "email",
email: { templateSlug: "plan-changed" } }
← { id: "msg_7q2xk9mvt4znw8rh", status: "pending" }POST /webhooks/samva
webhook-signature: v1,SGVsbG8...
{ type: "message.delivered",
timestamp: "2026-07-08T09:42:14Z",
data: { messageId: "msg_7q2xk9mvt4znw8rh",
channel: "email", status: "delivered" } }Product email is a stack nobody signed up to own.
A send call, a template engine, and a thread tracker, glued together.
Password resets go through one library, receipts through another, and you're the one reconciling what "delivered" means across both.
Product email is scattered across services.
Every service that sends mail keeps its own copy of the same HTML and its own retry logic, so a subject line change means hunting through several repos.
Deliverability is a chore nobody scheduled.
SPF, DKIM, and DMARC records need attention as domains change, and a stale record shows up as a support ticket instead of a warning.
One call, a version, a signed event.
One call replaces the stack.
Call one typed endpoint with a recipient and either inline content or a templateId. Samva returns a message id and status before the request finishes.
const message = await samva.email.send({
to: "ada@example.com",
templateSlug: "plan-changed",
});
← msg_7q2xk9mvt4znw8rh · pendingContent lives in one place, versioned.
Move the HTML into a template once. Publish a version, and every send that references the templateId renders whatever version is pinned, with rollback if a bad one ships.
template plan-changed
v4 draft edited 2h ago
v3 published pinned
v2 archived
v1 archived
send { templateSlug: "plan-changed", templateData: { plan: "Growth" } }
# the pinned version is the version that sendsDelivery becomes an event in your system.
Register a webhook for message.delivered and message.failed. Every request is signed with HMAC-SHA256, so you verify it before your product database updates.
POST /webhooks/samva
webhook-signature: v1,SGVsbG8...
{ type: "message.delivered",
timestamp: "2026-07-08T09:42:14Z",
data: { messageId: "msg_7q2xk9mvt4znw8rh" } }
verify: timingSafeEqual(signature, hmacSha256(secret, body))See the pieces up close.
Questions from engineering leads.
Migrating from Resend or SendGrid?
Migration is scoped to email. Point your integration at a Samva API key, and the send call takes a recipient plus either inline subject and html or text, or a templateId with no inline body. Versioned templates and signed webhooks replace what you built around your previous provider.
How does threading change the data model?
A reply doesn't need a table you maintain. Every inbound email carries a conversationId that ties it to the outbound send it answers, and you fetch the full thread from the conversations API instead of stitching a thread_id together yourself.
What about EU GDPR?
Transactional email like a password reset runs on contract performance, a different legal basis than marketing, so it needs no consent checkbox. When you send marketing from the same account, unsubscribes and complaints feed the same automatic suppression list Samva already runs for bounces, so consent stays enforced without a separate tool.
How does volume pricing work?
Each plan ships a fixed monthly allowance, and upgrading adds volume, seats, and domains while the send surface stays the same. Send past your allowance and the extra emails settle on your invoice as itemized overage, up to a monthly spend limit you control.
Send the next product email in minutes.
Create a key and send a typed call.