Skip to content
emailkit
Esc
navigateopen⌘Jpreview
On this page

Your app's entire email layer, in one client

One central client for providers, webhooks, and mailboxes — and one clean TypeScript API for everything email.

emailkit is your app’s entire email layer in one client — like Prisma for your database or Better Auth for auth. Providers, webhooks, and mailboxes are all configured in one place, and sending, receiving, and every lifecycle event go through one clean API, no matter which providers you pick.

npm install emailkit
pnpm add emailkit
yarn add emailkit
bun add emailkit
import { EmailKit, ResendDriver } from "emailkit";

export const emailkit = EmailKit({
  emailDrivers: [ResendDriver({ apiKey: process.env.RESEND_API_KEY! })],
});

await emailkit.sendEmail({
  from: { email: "hello@example.com", name: "Acme" },
  to: { email: "ada@example.com" },
  subject: "Welcome",
  text: "Your account is ready.",
});

That’s the whole setup.

The cool parts

Everything email lives in the client. Webhook handling isn’t a pile of route files and provider dashboards — it’s config on the same client. emailkit verifies and normalizes every event, then calls your hooks; the route itself is one line:

export const emailkit = EmailKit({
  emailDrivers: [driver],
  hooks: {
    email: {
      onInbound: async (email) => saveToInbox(email),
      onDelivered: async (event) => markDelivered(event.messageId),
      onBounced: async (event) => suppress(event.recipient),
    },
  },
});

// app/api/email/route.ts — the whole route
export const { GET, POST } = createNextEmailKitHandler(emailkit);

Email APIs and mailboxes, same client. Transactional APIs like Resend, Mailgun, and AIInbx sit next to real inboxes like Gmail and Outlook. Mix them freely and route by sender — the rest of your app never knows the difference.

Users connect their inbox — or their whole domain. Gmail and Outlook OAuth, token refresh, and inbound notifications are handled for you; so are DNS records and domain verification on your email API. Each is a couple of calls:

// their inbox
const { redirectUrl } = await emailkit.mailboxes.connect({
  emailDriver: "gmail",
  email: "acme.support@gmail.com",
});

// or their domain
const { domain } = await emailkit.domains.ensure({
  emailDriver: "resend",
  domain: "acme.com",
});

Open and click stats you can trust. Security scanners open and click everything before your user does. emailkit ships bot detection on every open and click event — botDetection.isBot plus the reason — so your metrics count humans.

Replies stay threaded. Pass the reply details from an inbound email and emailkit produces the right thread format for whichever provider sends it.

Missed a webhook? Replay it. sync() asks the provider for the events you missed and runs them through your existing hooks.

TypeScript knows your providers. Configure Resend and sendAt appears. Configure Gmail and mailbox methods appear. Use a feature your provider doesn’t have, and your code doesn’t compile.

Nothing new to run. emailkit is a library, not a platform. No queue, no required database schema, no second copy of your data. It calls providers and tells your app what happened — your database stays the source of truth.

What are you building?

Or jump straight in: the Quickstart has you sending in a few minutes.

How it works

The few ideas behind emailkit, in five minutes.

Last updated on July 24, 2026

Was this page helpful?