The full upstream README, mirrored here for reference. Install config, tool schemas, adoption signals, and an original overview live on the Notifyd listing page.
The notification service your agent can send through and run.
Email, SMS, WhatsApp, push, in-app inbox. One Rust binary. Postgres only. No dashboard: a digest endpoint and an MCP server instead.
Quick Start • Agent operations • API Reference • Architecture • Benchmarks • llms.txt • Contributing
Every product needs to send email, texts and in-app notifications. The usual options are a hosted SaaS billed per notification, or a self-hosted stack with MongoDB, Redis and four containers behind a React dashboard.
notifyd is the third option: a single 10 MB binary with PostgreSQL as
its only dependency, that sends through the providers you already have
(Resend, Cloudflare Email Service, any SMTP, AgentMail, Telnyx, Twilio,
Web Push, FCM), with a real delivery engine (priorities, pacing, retries with
Retry-After, provider failover, send windows, one-click unsubscribe) and
no admin UI at all. Operating it is an API call or an MCP tool, so the
person on call can be an AI agent.
Three instances run in production today, one per company, operated this way.

60 s explainer, invented data. MP4 · Remotion source
Flat REST, curl is the SDK. Retries are safe (idempotency_key), scheduling
is a field (scheduled_at), a marketing campaign is POST /v1/batch with
thousands of subscribers per call and it lands in the bulk lane so it never delays a
password reset. Follow any send with GET /v1/jobs/:id.
Most notification tools were designed for a human clicking through a dashboard. notifyd exposes the operator's job as tools, with the detail a human operator would need:
1. One call says what needs attention. GET /v1/admin/digest ranks
findings and tells you the action for each one, in JSON or Markdown:
2. The same operations as MCP tools. POST /mcp is a Streamable HTTP
MCP server (current spec revision, legacy initialize kept). Add it to Claude
Code, Claude Desktop, Cursor or your own agent:
| Tool | What the agent can do |
|---|---|
digest | Ranked findings with actions, queue, outcomes, latency, deliverability, per project |
list_jobs, get_job | Filter by project, channel, status, recipient, time; see provider, attempts, last error |
retry_job, cancel_job | Act on a stuck or wrong send |
list_projects, update_project | Sender identity (from_email, from_name), channels, inbound rate limit, bulk send_window in the recipients' timezone |
list_suppressions, add_suppression, release_suppression | Suppression list with all or marketing scope |
template_metrics | Sent, failed, bounced, opened per template |
send_test | Prove the pipeline end to end on any channel |
Every tool carries readOnlyHint / destructiveHint annotations and an
outputSchema. A read-only operator key (READONLY_API_KEY) exposes only
the read tools, for an agent that reports but must not act. Every MCP call is
audited.
3. Everything an agent needs to integrate is in the repo. docs/llms.txt
is the whole API in plain text for a context window; three Agent Skills
ship in skills/ (notifyd-operate, notifyd-integrate,
notifyd-deploy):
Published on the official MCP registry as mcp-name: io.github.rmzlb/notifyd
(server.json). Full operator guide: docs/AGENT.md.
Channels
lettre), attachments, per-project sender identityEventSource), read / archive / star, unread badge, multi-replica through Postgres NOTIFYDelivery engine
critical, normal, bulk lanes; /v1/batch and campaign tags land in bulkEMAIL_RATE_PER_SEC); a provider 429 pauses that channel for Retry-After without consuming an attempt, other channels keep flowing, and when it resumes the claim order puts critical firstEMAIL_FALLBACK_PROVIDER)Governance
List-Unsubscribe one-click on every marketing email, suppression scopes all / marketingOperations
llms.txt/v1/metrics, /v1/metrics/prometheus, per-template metrics{{variable}} substitution, stored per projectThe image reads its configuration from environment variables; no config file to mount.
Create a project and get its API key:
Prebuilt image, linux/amd64 and linux/arm64: ghcr.io/rmzlb/notifyd.
Release binaries: Linux x86_64 and aarch64, macOS Intel and Apple Silicon.
NixOS: services.notifyd.enable = true; with an environmentFile (see
flake.nix).
No provider yet? EMAIL_PROVIDER=log prints emails instead of sending them.
→ Full setup, TOML alternative, production notes: docs/SETUP.md
Project endpoints take X-Api-Key: sk_<project>_…; operator endpoints take
the admin (or read-only) key, as X-Api-Key or Authorization: Bearer.
Inbox endpoints also accept a subscriber JWT.
| Method | Endpoint | What it does |
|---|---|---|
POST | /v1/send | Send on one or several channels |
POST | /v1/batch | Send to many subscribers (bulk lane, idempotent) |
GET | /v1/jobs/:id | Status, provider, attempts, last error |
GET | /v1/inbox/:id · /stream | In-app inbox, SSE realtime stream |
POST | /v1/workflows/trigger | Trigger an event-based workflow |
GET | /v1/admin/digest | What needs attention, with actions |
GET | /v1/admin/jobs · POST …/:id/retry | Operator view and actions |
PATCH | /v1/admin/projects/:id | Sender, channels, rate limit, send window |
POST | /mcp | MCP server (Streamable HTTP) |
GET | /v1/metrics/prometheus | Prometheus exposition |
GET | /u/:token | One-click unsubscribe landing |
→ Every endpoint with examples: docs/API.md, or feed docs/llms.txt to your agent.
| Novu | Knock / Courier / SuprSend | notifyd | |
|---|---|---|---|
| Infra | MongoDB + Redis + 4 containers | Hosted SaaS | Postgres only, one 42 MB image |
| Setup | 30+ min | Signup + dashboard | docker compose up (2 min) |
| Language | Node.js (multiple services) | N/A (hosted) | Rust (single binary) |
| Memory | not measured by us | N/A | 13 MB idle, 23 MB draining 100k jobs (method) |
| Throughput | — | quota-bound | 44k jobs/s enqueued, 3.5k jobs/s drained (benchmarks) |
| Provider 429 | job fails | managed | channel paused for Retry-After, attempt not consumed, failover provider tried first |
| Priorities / send windows | ❌ | ✅ | ✅ critical → bulk lanes, per-subscriber timezone windows |
| Ops surface | React dashboard | dashboard + API | digest endpoint, MCP server, Agent Skills, Prometheus |
| Realtime | WebSocket | WebSocket | SSE, native EventSource, multi-replica |
| Self-hosted | ✅ (heavy) | ❌ | ✅ one container per company |
| Cost | Free tier / paid | per notification | Free forever, MIT |
We only publish numbers we measured on notifyd itself; the rest of the table describes shape, not performance. Method, hardware and bias disclaimer in docs/BENCHMARKS.md.
State lives in Postgres and survives restarts.
Environment variables are the primary interface (that is what the image and
the compose file use); a notifyd.toml is accepted for local development.
Required: DATABASE_URL, JWT_SECRET, ADMIN_API_KEY. Then one provider:
| Variable | Purpose |
|---|---|
EMAIL_PROVIDER | resend (default when RESEND_API_KEY is set), cloudflare, smtp, agentmail, log |
EMAIL_FROM, EMAIL_FROM_NAME | Instance default sender; projects can override |
EMAIL_FALLBACK_PROVIDER | Second provider on 429 / 5xx |
EMAIL_RATE_PER_SEC | Outbound pacing per replica |
SMS_PROVIDER, SMS_FROM | telnyx or twilio with their credentials |
PUBLIC_URL | Base URL for one-click unsubscribe links |
READONLY_API_KEY | Optional read-only operator key |
→ Every variable, per provider: docs/CONNECTORS.md and docker-compose.yml. TOML reference: notifyd.toml.example.
~12 000 lines of Rust, no unsafe. 10.8 MB binary, 42 MB image, 13 MB RSS
idle, 23 MB while draining 100 000 jobs. → docs/ARCHITECTURE.md
notifyd is a 0.x used in production by its authors. What it does not do yet: no dashboard (by design), no A/B testing, no APNs, no inbound email parsing, no multi-tenant billing. Breaking changes are announced in release notes; the queue schema is migrated automatically.
| 📦 Setup | Local dev, Docker, production |
| 🔌 API reference | Every endpoint with curl / TypeScript / Rust examples |
| 🤝 Agent operations | Digest, MCP tools, read-only key, how an agent runs an instance |
| 🔌 Connectors | Providers, environment variables, adding one |
| 🏗️ Architecture | Queue, priorities, pacing, SSE, connectors |
| 📈 Benchmarks | Footprint, throughput, how to reproduce |
| 🚀 Deployments | One instance per company, runbook |
| 📝 Writing | A notification queue on PostgreSQL alone: what SKIP LOCKED does not give you |
| 📣 Visibility | Registries and launch channels |
| 🤖 llms.txt | The API in plain text for agents |
Issues and pull requests are welcome; good first issue is the place to
start, and big features begin with an issue. Read CONTRIBUTING.md.
MIT.
Built with 🦀 in Grenoble, France 🏔️