Skip to content
emailkit
Esc
navigateopen⌘Jpreview
On this page

Event reference

Every hook emailkit can call — when it fires, what it carries, and what to do with it.

Everything emailkit tells your app arrives through one hooks object on the client. Four groups, all optional — handle what you care about, skip the rest:

EmailKit({
  emailDrivers: [...],
  hooks: {
    email: { ... }, // messages: received, sent, delivered, opened, clicked…
    mailbox: { ... }, // inboxes users connect — save what emailkit hands you
    domain: { ... }, // sending domains as they're created and verified
    webhook: { ... }, // the provider subscriptions emailkit manages
  },
});

Whichever provider produced an event, your hook sees the same shape. This page walks through when each hook fires; every hook links to its own page with the exact payload and a working handler.

The life of an email

Ten email hooks sounds like a lot, but they tell one story. Sending:

emailkit.sendEmail()

Your app sends a message.

onOutbound

The provider accepted it. Record the send under its messageId — every later event carries the same one.

onDelivered

The recipient’s server took the message, with responseTime when the provider measures it.

onOpened

The message was opened — client, device, and location when available, plus a botDetection verdict so scanners don’t count.

onClicked

A tracked link was followed. url says which one; the same client and bot data comes along.

Receiving is one hook:

a message arrives at your address
onInbound

The full message: addresses, subject, text and HTML bodies, reply context for threading, headers, and attachments.

When sending goes wrong

Three hooks cover the unhappy paths — in a good week, none of them fire:

onBounced

The message couldn’t be delivered. severity separates hard from soft bounces — suppress recipients that bounce permanent.

onComplained

The recipient marked it as spam. Stop emailing them before it hurts your sender reputation.

onRejected

The provider refused the send — blocked address, policy, suppression list. reason and category say why.

Catch-alls

onAll

Every email event, before the specific hook runs, tagged with its type and the emailDriver that produced it. Built for logging, metrics, and one-table event stores.

onUnknown

A verified provider event emailkit can’t normalize. Rare — but nothing gets silently dropped.

Bots don’t count

Raw open and click numbers lie: security scanners open every message and follow every link before a human ever sees it. emailkit classifies each open and click and attaches the verdict to the event — no setup, it’s on every onOpened and onClicked call:

onOpened: async (event) => {
  if (event.botDetection?.isBot) return; // e.g. reason: "known-bot-agent"
  await markOpened(event.messageId);
},

The classifier checks known scanner user agents (Barracuda, Outlook safe-link crawlers, HTTP libraries), opens arriving impossibly fast after send, HEAD requests on tracked links, and malformed browser signatures. It also knows the Gmail, Yahoo, and Outlook image proxies are real humans opening mail, so those still count. reason tells you which rule decided.

Need the same logic elsewhere? checkOpenBot and checkClickBot are exported from emailkit.

Resource hooks

Email hooks tell you what happened; resource hooks tell you what to save. emailkit keeps no database, so whenever something worth remembering changes — a connected inbox, a domain, a webhook subscription — it hands the new state to your app. Write it down and you’re done. (How emailkit works explains this stateless design.)

hooks.mailboxInboxes your users connect
onConnected

A mailbox was connected — OAuth flow or mailboxes.connect(). Comes with the mailbox and, when the driver returns one, its auth — encrypt and store both.

onAuthUpdated

emailkit refreshed the tokens. Overwrite the stored auth — the old one is dead.

onCreated

A mailbox was created through the API, on providers that support it.

onDeleted

The mailbox is gone — clean up your records.

hooks.domainSending domains
onCreated

A domain was registered with the provider. The event carries the DNS records to show your user.

onVerified

A domains.verify() check passed — the domain can send.

onDeleted

The domain was removed.

hooks.webhookSubscriptions emailkit manages for you
onCreated

emailkit registered a webhook with a provider. Persist it — mailbox subscriptions expire and get renewed.

onUpdated

A subscription was renewed or its status changed. Store the new state; previousWebhook shows what changed.

onDeleted

The subscription no longer exists.

onActionRequired

emailkit can’t fix this alone — auth revoked, endpoint unreachable. reason plus recommendedActions tell you the next move.

onSyncRequired

Notifications were missed or history gapped. Run a sync to replay the gap.

onAll

Every webhook lifecycle event in one stream.

Every resource event names its emailDriver, includes the affected item, and keeps the provider’s original payload in raw.

Good to know

  • Live webhooks and recovered events both use the same hooks.
  • The same event can arrive more than once, so hooks should safely ignore duplicates.
  • emailkit verifies provider webhooks before running your hooks.
  • A field stays optional when the provider does not supply it; emailkit does not invent a value.
  • The original event stays available in raw for debugging and provider-specific work.

Last updated on July 24, 2026

Was this page helpful?