Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.senderkit.com/llms.txt

Use this file to discover all available pages before exploring further.

By the end of this guide, a welcome email sends automatically the moment a new user signs up — triggered by your auth provider, rendered from a dashboard template, with no copy living in your codebase. This is the email your auth provider doesn’t send for you. Clerk and Auth.js handle the auth-related mail — verification codes, magic links, password resets — but neither sends a product welcome. That gap is yours to fill, and it’s a one-line send() from a webhook or event handler.
You’ll need: a SenderKit account with an API key (create one), a Next.js app, and either a Clerk or Auth.js (NextAuth) setup. Start with an sk_test_ key so nothing reaches a real inbox while you wire this up.
1

Author the welcome template in the dashboard

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

Hi {{name}},

Thanks for signing up. Here's your dashboard to get started:
{{dashboard_url}}
Don’t want to start from a blank editor? Use AI authoring to draft the subject, layout, and variables from a one-line brief, then refine and publish.
In test mode SenderKit renders your latest draft, so you can iterate on copy without publishing. Live sends only ever render the published version — publish when you’re happy.
2

Install the SDK and configure the client

npm install @senderkit/sdk
Set your key in the environment, and create a single shared client:
.env.local
SENDERKIT_API_KEY=sk_test_...
lib/senderkit.ts
import { SenderKit } from "@senderkit/sdk";

export const senderkit = new SenderKit({
  apiKey: process.env.SENDERKIT_API_KEY!,
});
Then a small helper both auth integrations will call:
lib/email.ts
import { senderkit } from "./senderkit";

export async function sendWelcomeEmail(opts: {
  userId: string;
  email: string;
  name?: string;
}) {
  await senderkit.send({
    template: "welcome",
    to: opts.email,
    vars: {
      name: opts.name ?? "there",
      dashboard_url: "https://app.example.com/dashboard",
    },
    // A stable key makes a duplicate webhook or a retry a no-op.
    idempotencyKey: `welcome:${opts.userId}`,
    // Indexed — lets you later filter messages down to this user.
    metadata: { userId: opts.userId },
  });
}
Keep the API key server-side only. The SDK runs in your Next.js route handlers and server actions — never in a client component or the browser bundle.
3

Trigger the send on signup

The SenderKit side is identical either way — the only difference is how your auth provider tells you a user was created.
Clerk delivers signup events as webhooks (over Svix). Add a route handler that verifies the request and calls your helper on user.created:
app/api/webhooks/clerk/route.ts
import { verifyWebhook } from "@clerk/nextjs/webhooks";
import { NextRequest } from "next/server";
import { sendWelcomeEmail } from "@/lib/email";

export async function POST(req: NextRequest) {
  try {
    const evt = await verifyWebhook(req);

    if (evt.type === "user.created") {
      const { id, email_addresses, primary_email_address_id, first_name } =
        evt.data;
      const email = email_addresses.find(
        (e) => e.id === primary_email_address_id,
      )?.email_address;

      if (email) {
        await sendWelcomeEmail({ userId: id, email, name: first_name ?? undefined });
      }
    }

    return new Response("ok", { status: 200 });
  } catch (err) {
    console.error("Clerk webhook verification failed", err);
    return new Response("Bad signature", { status: 400 });
  }
}
Then register the endpoint: Clerk Dashboard → Webhooks → Add Endpoint, point it at https://your-app.com/api/webhooks/clerk, subscribe to user.created, and copy the Signing Secret into your environment as CLERK_WEBHOOK_SIGNING_SECRET (verifyWebhook reads it automatically).
To test locally, expose your dev server (ngrok http 3000), use that URL as the endpoint, and fire a sample user.created from the endpoint’s Testing tab.
4

Verify it works

With your sk_test_ key in place, sign up a test user (Clerk’s test tab, or a real signup flow against your dev database for Auth.js).The send is accepted immediately and delivered asynchronously. In test mode SenderKit synthesizes the full lifecycle — queued → rendered → sent → delivered — without touching a provider. Confirm it in the dashboard, or from code:
const { data } = await senderkit.messages.list({ template: "welcome" });
console.log(data[0]?.status); // "delivered" in test mode
Because the send is keyed on welcome:${userId}, a duplicate webhook delivery (they happen — delivery is at-least-once) collapses to the same message instead of emailing the user twice.
5

Go live

Swap the environment key to sk_live_ and register a production webhook endpoint in Clerk (or deploy your Auth.js app). No code changes — SenderKit derives live vs. test from the key prefix, and the welcome template you published renders for real.

What’s next

Recover failed payments

The Stripe lifecycle emails Stripe doesn’t send for you.

Send team invites

Invite teammates with a tokenized accept link.

Sending

Idempotency, scheduling, and the accept-now / deliver-later model.

Messages

Track a send from queued to delivered.