Samva is in early access — self-serve signup is limited. Have a team invite? Sign up with that email. Contact us for access.

Prisma

Send transactional email after Prisma writes with Samva.

Prisma

Use Prisma for persistence and the samva SDK for transactional email. The primary pattern is query-then-send: create or update the row, then send email from the persisted data.

Samva sends from the verified sender configured on your account, so there is no from field in the send payload.

Install

bun add samva @prisma/client

Keep SAMVA_API_KEY server-side only:

import "server-only";
import { createClient } from "samva";

const apiKey = process.env.SAMVA_API_KEY;
if (!apiKey) {
  throw new Error("SAMVA_API_KEY is required");
}

export const samva = createClient({ apiKey });

Query, then send

Use the committed row as the source of truth for the email:

import { prisma } from "@/lib/prisma";
import { samva } from "@/lib/samva";

const order = await prisma.order.create({
  data: {
    email: "ada@example.com",
    total: 4900,
    status: "confirmed",
  },
});

await samva.messages.send({
  to: [{ email: order.email }],
  channel: "email",
  email: {
    subject: `Order #${order.id} confirmed`,
    html: `<p>Thanks. Your order total is ${order.total}.</p>`,
  },
});

This keeps the boundary clear: Prisma owns database state, Samva owns delivery. If you need durable retries, write an outbox row in the same database transaction and let a worker send from that outbox after commit.

Data-layer hook

For advanced cases, Prisma Client Extensions can centralize the send around a specific write:

import { PrismaClient } from "@prisma/client";
import { samva } from "@/lib/samva";

const base = new PrismaClient();

const prisma = base.$extends({
  name: "samva-order-emails",
  query: {
    order: {
      async create({ args, query }) {
        const order = await query(args);
        await samva.messages.send({
          to: [{ email: order.email }],
          channel: "email",
          email: {
            subject: `Order #${order.id} confirmed`,
            html: `<p>Thanks. Your order total is ${order.total}.</p>`,
          },
        });
        return order;
      },
    },
  },
});

Use this only when the data layer really owns the side effect. For transaction correctness, send after an interactive transaction resolves or use an outbox; email delivery cannot be rolled back with the database write.

Runtime notes

The Samva SDK is fetch-based, so the send works in Node, Workers, and edge runtimes. Prisma can query at the edge when you use the Rust-free client with an edge-compatible driver adapter.

React Email works well for templates: render the component to html, derive a plain-text fallback, and pass both strings to Samva. See the React Email integration for the template workflow.

Signup, reset, magic link, and OTP emails should live in your auth callbacks instead of a generic Prisma write hook. See the Better Auth integration for that callback shape.

Full cookbook

The cookbook link resolves after the companion samva-integrations PR lands.

On this page