← website
Start here

Examples

Five example projects ship in the repository, each a single file you can read in a couple of minutes. Each answers a different question — how do I call this without an SDK, how do I plug in my own provider, what does a workflow look like end to end, and how fast is it. Every section below links straight to the source on GitHub.

All five talk to a running NotifKit server, so you will want one before any of them do anything — the quickstart gets you there in a few minutes, and custom-server below is a working server in a single file.

basic-usage

The one to read first. Three HTTP calls — sync a template, register a user, send — using nothing but fetch. It exists to make the point that NotifKit has no required SDK: anything that can POST JSON is a client, so the same three calls work from Go, Python, PHP, or a shell script.

The third call comes back 202 Accepted, which is NotifKit saying queued, not delivered — the distinction the rest of the docs keep coming back to. The task id it returns is what GET /v1/notifications/{taskId} reports against.

examples/basic-usage on GitHub →
Shows youRead next
Template sync, user creation, a first sendQuickstart
Bearer auth on every /v1 routeReference

custom-server

Bootstraps NotifkitServer in your own process with services: ["all"], registers a hand-written email transport, sets worker concurrency, and installs graceful shutdown handlers. The transport is the interesting part: a class with a channel and a send() that returns { success, providerMessageId }. That is the entire contract — Resend and FCM implement the same one.

examples/custom-server on GitHub →
Shows youRead next
Writing a TransportChannels & fallback
Running every service in one processArchitecture
Graceful shutdownOperations

workflows

Defines a two-step onboarding drip — welcome, wait an hour, follow up — as a JSON workflow, registers the recipient, then triggers one instance for them. Worth reading closely for one detail: no step names a recipient. Each inherits the user the instance was triggered with, which is why the same definition serves every subscriber.

The JSON form needs no deploy — the sequence is data, editable by someone who is not shipping a release. Code-defined workflows can do the same thing with conditionals and arbitrary side effects.

examples/workflows on GitHub →
Shows youRead next
Declarative workflows, and who a step sends toWorkflows
Waiting between stepsThe step API

scheduled-notifications

Sends with sendAt set ten minutes out, for a user in America/New_York with a 22:00–08:00 quiet window. Two mechanisms overlap here, and the example is the shortest way to see the difference: sendAt is absolute UTC and is not shifted into the user's timezone, while quiet hours are timezone-aware — so a scheduled send that lands inside a quiet window gets deferred again to the far edge of it.

The message is parked in Postgres with a pointer in a Redis sorted set, where GET /v1/notifications/scheduled can see it and where it stays cancellable right up until the scheduler releases it.

examples/scheduled-notifications on GitHub →
Shows youRead next
sendAt, listing and cancelling scheduled sendsScheduling
Quiet-hours deferralPreferences & quiet hours

load-test

Fires a configurable number of notifications through a pool of concurrent workers and reports throughput and elapsed time. Defaults to 500 notifications at concurrency 50, across 100 distinct users.

consequence

This measures ingestion, not delivery. Every call returns as soon as the request is queued, so a high number here says the API accepted the work quickly — it says nothing about how fast messages reached anyone. For that, watch stream depth in GET /v1/system/metrics, which shows whether the queue drains afterwards or grows without bound.

examples/load-test on GitHub →
Shows youRead next
Ingestion throughput, batching behaviourArchitecture
What to watch while it runsOperations