Packages
notifkit ships first-party transports for every major channel alongside the core engine: email, push, SMS, Slack, WhatsApp, Telegram, and Discord, plus a development transport and an AI agent MCP server.
| Package | Channel | Wraps | Use it for |
|---|---|---|---|
@notifkit/provider-resend | email | Resend + svix | Production email, with engagement webhooks & bounce suppression. |
@notifkit/provider-fcm | push | Firebase Admin | Production push to iOS and Android devices. |
@notifkit/provider-twilio | sms | Twilio Messages API | Production SMS with status callbacks & STOP reply suppression. |
@notifkit/provider-slack | slack | Slack Web API & Webhooks | Direct messages, public/private channels, Block Kit layouts. |
@notifkit/provider-whatsapp | whatsapp | Meta Cloud API | Direct messaging to recipient phone numbers with zero SaaS markup. |
@notifkit/provider-telegram | telegram | Telegram Bot API | Bot notifications to chat IDs with HTML/Markdown and silent alerts. |
@notifkit/provider-discord | discord | Discord Webhooks | Channel alerts with custom avatars, usernames, and embeds. |
@notifkit/provider-console | configurable | nothing | Development and tests. Prints and reports success. |
@notifkit/mcp | — | MCP SDK | Driving notifkit from an AI agent. Not a transport. |
SMS has first-party support via @notifkit/provider-twilio, or you can write your own custom SMS transport for providers like Vonage, AWS SNS, or Infobip using the same standard Transport interface.
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
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.