← website
Packages

Packages

Four packages ship alongside the core. Three are transports — the thing that actually hands a message to a provider — and one is an MCP server. A channel with no transport registered delivers nothing, so this is the list of what you get for free and what you still have to write.

PackageChannelWrapsUse it for
@notifkit/provider-resendemailResend + svixProduction email, with engagement webhooks.
@notifkit/provider-fcmpushFirebase AdminProduction push to iOS and Android.
@notifkit/provider-consoleconfigurablenothingDevelopment and tests. Prints and reports success.
@notifkit/mcpMCP SDKDriving NotifKit from an AI agent. Not a transport.

SMS has no package. The channel works everywhere else in the system — contacts, templates, targeting, quiet hours, fallback, suppression — but nothing ships that talks to Twilio or Vonage, so a send fails with no_transport until you register your own.

all of them are optional

Nothing here is required to run NotifKit — the core has no provider dependency, which is why firebase-admin and resend are not in its dependency tree. Each package declares notifkit as a peer dependency and is registered by you at boot, so an install that only sends email never pulls in the Firebase SDK.

By channel

Writing your own

Everything above implements the same Transport interface, and nothing about the first-party packages is privileged — a transport you write registers the same way and is picked up by the same registry. Register more than one for a channel and the highest priority is tried first, with the rest as failover.

import { registerTransport } from "notifkit";

registerTransport(new ResendTransport({ /* … */ }), 10);  // tried first
registerTransport(new PostmarkTransport({ /* … */ }), 5); // only if Resend fails
priority is failover, not routing

Two transports on one channel give you provider failover. They do not let you choose between them per send — the registry keys on channel alone, so the second is reached only when the first fails. Anything that varies per message, a sender address included, belongs in the template and is read by a single transport.

The full interface, including the webhook hooks and what DeliveryResult.invalidToken does, is in the reference, and Channels & fallback walks through a worked example.