Skip to main content
In about five minutes you’ll do the one thing that makes SenderKit different: create a message template in the dashboard, wire up a single send call, and then change the wording from the dashboard — no code change, no redeploy. The subject, layout, and copy live outside your repo; your code just names the template and fills in the variables.
1

Create your first template

In the dashboard, create an email template with the slug welcome. Write a subject and body, and reference dynamic values as variables with Mustache braces:
Template copy (in the dashboard editor)
Subject: Welcome to Acme, {{name}} 👋

Hi {{name}},

Thanks for signing up — we're glad you're here.
The slug welcome is the stable name your code will send to. Staring at a blank editor? AI authoring drafts the subject, layout, and variables from a one-line brief.
You don’t need to publish yet. In test mode SenderKit renders your latest draft, so you can keep editing copy as you go. Live sends only ever render the published version.
2

Get a test API key

Create a key in the dashboard. Use an sk_test_ key while you’re wiring things up — test keys never call your providers, so nothing reaches a real inbox — and switch to sk_live_ to send for real. The plaintext is shown once at creation, so copy it then.
3

Send it

Reference the template by slug, pass the recipient and vars, and send. Pick your surface:
Install the SDK:
npm install @senderkit/sdk
pnpm add @senderkit/sdk
yarn add @senderkit/sdk
bun add @senderkit/sdk
Keep your key in the environment (out of source), then create a client and send:
export SENDERKIT_API_KEY="sk_test_..."
import { SenderKit } from "@senderkit/sdk";

const senderkit = new SenderKit({ apiKey: process.env.SENDERKIT_API_KEY! });

const result = await senderkit.send({
  template: "welcome",
  to: "user@example.com",
  vars: { name: "Ada" },
});

console.log(result); // { id: "msg_…", status: "queued", livemode: false }
The core SDK has zero runtime dependencies and uses native fetch (Node.js 18+). Keep the key server-side — never ship it in a client bundle.
vars fills the {{name}} holes in your template. Sends are asynchronous: the call returns status: "queued" once SenderKit accepts the message (or "scheduled" if you passed a future scheduledAt), and delivery happens out of band.
4

Confirm it worked

The send is accepted immediately as queued and delivered asynchronously — in test mode without ever touching a real provider, so nothing reaches an inbox. Look the message up in the dashboard, or fetch it by the id you got back:
const message = await senderkit.messages.get(result.id);
console.log(message.status, message.timeline);
You can also list recent sends with senderkit.messages.list({ template: "welcome" }) (CLI: senderkit messages list; HTTP: GET /v1/messages). See Messages for how a send moves from queued to delivered.
5

Change the copy — no redeploy

Here’s the payoff. Go back to the welcome template in the dashboard, change the subject or body, and run the exact same send again. The new wording renders — you didn’t touch a line of code or ship a deploy.That’s the whole idea: send({ template: "welcome" }) is a stable contract, and everything that changes without engineering — the words, the layout, the subject — lives on the dashboard side of it. Anyone on the team can fix a typo without opening your editor.
6

Go live

Publish the welcome template, then swap your key to sk_live_. No code changes — SenderKit derives live vs. test from the key prefix, and your published template renders for real.

What’s next

Send a welcome email on signup

Trigger this send automatically from a Clerk or Auth.js event.

Authentication

Key types, scopes, and the test vs. live model.

Templates

Slugs, channels, and why copy edits never touch your code.

Messages

Track a send from queued to delivered.
Working with an AI assistant? The MCP server exposes these same operations to agents in Claude, Cursor, and other MCP clients.