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, an AI agent built with the Vercel AI SDK can send transactional emails on its own: you expose a sendEmail tool backed by SenderKit, and the model decides when to call it and dispatches a real templated send. This is the inverse of the MCP server — there, an assistant operates SenderKit for you in your editor. Here, your own app’s agent sends through SenderKit as part of whatever it’s doing: an onboarding bot that emails a setup guide, a support agent that confirms a resolution, a workflow that notifies on completion.
You’ll need: a SenderKit account with an API key, a model provider key (the example uses OpenAI), and a Node.js or Next.js app. Use an sk_test_ key — an agent deciding to email people is exactly when you want sends going nowhere real until you trust it.
1

Author a template for the agent to send

Give the agent a template to target rather than letting it compose raw HTML — the copy stays controlled and editable while the agent only chooses who and what variables. For this example, reuse a welcome template with a name variable (see Send a welcome email).
2

Install dependencies and configure the client

npm install ai @ai-sdk/openai zod @senderkit/sdk
lib/senderkit.ts
import { SenderKit } from "@senderkit/sdk";

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

Define the sendEmail tool

A tool is a typed function the model can call. Constrain its inputs tightly — a zod enum of known template slugs, a validated email — so the model can’t send anything you didn’t intend.
lib/tools/send-email.ts
import { tool } from "ai";
import { z } from "zod";
import { senderkit } from "@/lib/senderkit";

export const sendEmail = tool({
  description:
    "Send a transactional email to a user via SenderKit. " +
    "Only use one of the known template slugs.",
  inputSchema: z.object({
    to: z.string().email().describe("Recipient email address"),
    template: z
      .enum(["welcome", "trial-ending", "export-ready"])
      .describe("Which template to send"),
    vars: z
      .record(z.string(), z.string())
      .describe("Template variables as key/value pairs"),
  }),
  execute: async ({ to, template, vars }) => {
    const result = await senderkit.send({
      template,
      to,
      vars,
      metadata: { source: "ai-agent" },
    });
    return { id: result.id, status: result.status };
  },
});
The value execute returns is fed back to the model, so it can confirm what it did (“queued message msg_… to ada@example.com”).
4

Wire the tool into the agent

Pass the tool to generateText (or streamText) and allow multiple steps so the model can call the tool, read the result, and reply.
app/agent.ts
import { generateText, stepCountIs } from "ai";
import { openai } from "@ai-sdk/openai";
import { sendEmail } from "@/lib/tools/send-email";

const { text, steps } = await generateText({
  model: openai("gpt-4o"),
  tools: { sendEmail },
  stopWhen: stepCountIs(5), // allow tool call → result → final answer
  prompt: "Send the welcome email to ada@example.com — her name is Ada.",
});

// Inspect what the agent actually did:
const sends = steps.flatMap((s) => s.toolResults);
console.log(text, sends);
5

Verify it works

Run it with your sk_test_ key. The agent calls sendEmail, SenderKit accepts the send, and in test mode runs the full lifecycle without touching a provider. Confirm it landed:
const { data } = await senderkit.messages.list({ metadata: { source: "ai-agent" } });
console.log(data[0]?.status);
An agent that can email anyone is a real risk surface. Before going live with an sk_live_ key:
  • Constrain recipients. Validate to against your own user records — don’t let the model email arbitrary addresses it invented.
  • Constrain templates. Keep the z.enum of slugs tight; never let the model pass free-form HTML to a raw send in production.
  • Add idempotency. If the agent can be re-run, pass a stable idempotencyKey so a retry doesn’t double-send.
  • Consider a human in the loop. For anything irreversible, require confirmation before the tool actually sends.

What’s next

MCP Server

The other direction — operate SenderKit from your AI editor.

Sending

Idempotency, raw sends, and the async model.

Welcome on signup

The template this agent sends, authored end to end.

TypeScript SDK

The full send() surface the tool wraps.