notifkit 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,8 +2,11 @@
2
2
 
3
3
  # notifkit
4
4
 
5
- **Self-hosted notification infrastructure.**
6
- One call delivers to email, SMS, push, and webhook — routed by preference, quiet hours, and consent.
5
+ **You shouldn't have to build a notification system.**
6
+
7
+ Self-hosted notification infrastructure for product notifications. One API call handles email, SMS, push, and webhooks — with preferences, quiet hours, retries, fallback, scheduling, workflows, and delivery logs built in.
8
+
9
+ [![npm version](https://img.shields.io/npm/v/notifkit.svg?style=flat-square&color=6366f1)](https://www.npmjs.com/package/notifkit) [![npm downloads](https://img.shields.io/npm/dm/notifkit.svg?style=flat-square&color=6366f1)](https://www.npmjs.com/package/notifkit) [![Coverage](https://img.shields.io/badge/coverage-85%25-brightgreen.svg?style=flat-square)](https://github.com/devkitshq/notifkit) [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-3178c6.svg?style=flat-square)](https://www.typescriptlang.org/) [![Node.js](https://img.shields.io/badge/node-%3E%3D22.0.0-339933.svg?style=flat-square)](https://nodejs.org) [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](./LICENSE)
7
10
 
8
11
  [Documentation](https://notifkit.dev/docs/) · [Quickstart](https://notifkit.dev/docs/quickstart.html) · [Examples](https://notifkit.dev/docs/examples.html) · [notifkit.dev](https://notifkit.dev)
9
12
 
@@ -11,83 +14,381 @@ One call delivers to email, SMS, push, and webhook — routed by preference, qui
11
14
 
12
15
  ---
13
16
 
14
- ## Install
17
+ ### The first notification is easy
15
18
 
16
- ```bash
17
- npm install notifkit
19
+ ```ts
20
+ await sendEmail({
21
+ to: user.email,
22
+ subject: "Order Shipped",
23
+ ...
24
+ });
18
25
  ```
19
26
 
20
- Node 22+, PostgreSQL, and Redis. Migrations ship in the package — point drizzle-kit at
21
- `node_modules/notifkit/drizzle` — and in development notifkit starts throwaway Postgres
22
- and Redis containers for you, so Docker is the only prerequisite to try it.
27
+ ### Then reality hits
23
28
 
24
- ## What it does
29
+ Users opt out. People are asleep. Push tokens die. Providers throw 503s. Some channels fail and need fallback. You need timezone-aware quiet hours, future scheduling, deduplication, multi-channel templates, delivery logs, unsubscribe handling, multi-step workflows, and a dead-letter queue nobody wants to maintain.
25
30
 
26
- Shipping one notification is easy. Shipping a notification _system_ is not: you end up with queues, retry logic, a preference store, quiet-hours maths, provider adapters, and a dead-letter queue nobody wants to own.
31
+ **notifkit is that machinery, already built.**
27
32
 
28
- notifkit is that machinery, running on your servers. You describe **who** needs to know **what**; it works out **how** the right channel, at a decent hour, only to people who said yes.
33
+ Your app makes one typed call. notifkit handles the rest **who gets it, which channel to use, when to send it, whether they're allowed to receive it, and what happens when delivery fails.**
29
34
 
30
35
  ```ts
36
+ import { notifkit } from "notifkit";
37
+
31
38
  await notifkit.notify({
32
39
  user: "usr_123",
33
40
  template: "order-shipped",
34
41
  channels: ["push", "email"],
35
- fallback: true, // push first; email only if push fails
42
+ fallback: true,
36
43
  });
37
44
  ```
38
45
 
39
- That call returns immediately. Behind it: preference and consent filtering, timezone-aware quiet hours, per-user throttling, template rendering, provider dispatch with retries and circuit breakers, and a durable log of what happened.
46
+ > **Push first. If it fails, email.**
47
+ >
48
+ > Preferences, consent, quiet hours, retries, deduplication, throttling, template rendering, and delivery tracking happen behind that single call.
49
+
50
+ ---
51
+
52
+ ## What actually runs
53
+
54
+ notifkit is both an **orchestration engine** and a **typed SDK**.
55
+
56
+ ```text
57
+ ┌──────────────────────────────────────────────────────────┐
58
+ │ Your Application / AI Agent │
59
+ │ (Typed SDK / REST API / MCP Server) │
60
+ └────────────────────────────┬─────────────────────────────┘
61
+ │ HTTP POST /v1/notify
62
+
63
+ ┌──────────────────────────────────────────────────────────┐
64
+ │ Notifkit API Server │
65
+ │ • Schema Validation • Auth & Multi-Tenancy │
66
+ │ • Idempotency Gate • Priority Queue Ingestion │
67
+ └──────────────┬────────────────────────────┬──────────────┘
68
+ │ │
69
+ ▼ ▼
70
+ ┌─────────────────────────────┐ ┌─────────────────────────┐
71
+ │ PostgreSQL (Storage) │ │ Redis (Streams & ZSET) │
72
+ │ • Users & Preferences │ │ • Priority Queues │
73
+ │ • Templates & Workflows │ │ • Scheduled Sends │
74
+ │ • Delivery Logs & DLQ │ │ • Sliding Rate Limits │
75
+ └──────────────▲──────────────┘ └──────────┬──────────────┘
76
+ │ │
77
+ │ ┌────────────────────────┘
78
+ │ ▼
79
+ ┌──────────────────────────────────────────────────────────┐
80
+ │ Background Workers Pipeline │
81
+ │ │
82
+ │ ┌───────────┐ ┌─────────────┐ ┌────────────────┐ │
83
+ │ │ Enricher │───►│ Engine │───►│ Delivery │ │
84
+ │ │ (Resolve) │ │(Quiet Hours)│ │(Rate Limits/CB)│ │
85
+ │ └───────────┘ └──────┬──────┘ └───────┬────────┘ │
86
+ │ │ │ │
87
+ │ ┌──────▼──────┐ │ │
88
+ │ │ Scheduler │────────────┘ │
89
+ │ │ (sendAt/QH) │ │
90
+ │ └─────────────┘ │
91
+ └──────────────────────────────────┬───────────────────────┘
92
+ │ Dispatch
93
+
94
+ ┌──────────────────────────────────────────────────────────┐
95
+ │ Provider Transports │
96
+ │ │
97
+ │ Email: Resend, SES, Postmark Push: Firebase (FCM) │
98
+ │ SMS: Twilio, MessageBird Webhooks: Custom HTTP │
99
+ └──────────────────────────────────────────────────────────┘
100
+ ```
101
+
102
+ - **`NotifkitServer`**: Runs the HTTP REST API router (`/v1/notify`, `/health`, `/metrics`) and the background worker pipelines (enricher, decision engine, scheduler, delivery).
103
+ - **`NotifkitClient`**: The lightweight client your application uses to trigger notifications, sync templates, and manage users over HTTP.
104
+
105
+ ### Topologies
106
+
107
+ - **Single Process (Monolith)**: Run the API and all workers in the same Node.js process (`services: ["all"]`). Perfect for small-to-medium apps, side projects, and staging.
108
+ - **Distributed Services**: Run stateless API servers (`services: ["api"]`) behind a load balancer and scale worker pools (`services: ["enricher", "engine", "delivery", "scheduler"]`) horizontally across Redis Streams consumer groups.
109
+
110
+ ---
111
+
112
+ ## Battle-tested for production
113
+
114
+ > **Battle-tested in production:** notifkit powers production notification pipelines handling **thousands of emails, push notifications, and OTPs every day.**
115
+ >
116
+ > It is the infrastructure we built because we needed it ourselves — rather than spending months reinventing distributed notification plumbing or paying SaaS tolls per alert.
117
+
118
+ **Your servers. Your providers. Your data. Zero notification SaaS markups.**
119
+
120
+ ### Reliability & Chaos Engineering
121
+
122
+ Because notification delivery is mission-critical, every pipeline component is tested against extreme failure conditions:
123
+
124
+ ```text
125
+ ┌────────────────┐ Kill Worker ┌────────────────────────┐
126
+ │ Redis Streams │ ──( SIGKILL )────► │ Auto-Claim & Replay │ ──► Zero Lost Messages
127
+ └────────────────┘ └────────────────────────┘
128
+ ┌────────────────┐ Drop DB/Redis ┌────────────────────────┐
129
+ │ Connection Loss│ ──( Disconnect )──► │ Auto-Reconnect / Retry │ ──► In-Flight State Intact
130
+ └────────────────┘ └────────────────────────┘
131
+ ┌────────────────┐ High Load ┌────────────────────────┐
132
+ │ 10k+ Messages │ ──( Burst )───────► │ Concurrency & Limits │ ──► Flat Memory, No Leaks
133
+ └────────────────┘ └────────────────────────┘
134
+ ```
135
+
136
+ - **Chaos Monkey Testing (`tests/chaos/crash.test.ts`)**: Background worker processes are randomly terminated with `SIGKILL` during active, high-throughput message streaming. Consumer group Pending Entries List (PEL) re-claims guarantee **zero lost messages** and seamless failover.
137
+ - **Infrastructure Recovery Testing (`tests/chaos/recovery.test.ts`)**: PostgreSQL and Redis connections are forcefully severed and restored under live traffic. Verifies automatic client reconnection, worker backpressure, and durable state resumption.
138
+ - **High-Throughput Load Testing (`tests/chaos/load.test.ts`)**: Stressed with bursts of **10,000+ notifications** across parallel worker pools, verifying queue drain velocity, sliding-window rate limiters, and flat memory profiles without leaks.
139
+ - **Race Conditions & Concurrency (`tests/race-conditions.test.ts`, `tests/idempotency.test.ts`)**: Hardened against concurrent duplicate dispatches, overlapping quiet-hour boundary evaluations, atomic user updates, and 24-hour idempotency key deduplication.
140
+ - **100% Real Ephemeral Containers**: Unit, integration, and chaos test suites execute against real PostgreSQL and Redis containers via [Testcontainers](https://testcontainers.com), eliminating mocks for core storage and streaming primitives.
141
+
142
+ ---
40
143
 
41
- | | |
42
- | ----------------- | ----------------------------------------------------------------------------------- |
43
- | **Channels** | `email`, `sms`, `push`, `webhook` |
44
- | **Targeting** | A user, a list of users, a segment, or a topic |
45
- | **Priorities** | `low`, `normal`, `high`, `critical` — separate stream lanes |
46
- | **Scheduling** | Future sends with `sendAt`, quiet-hours deferral, cancel before dispatch |
47
- | **Preferences** | Per-user channel and topic opt-outs, quiet hours, contact-level overrides |
48
- | **Workflows** | Multi-step sequences with `wait`, `waitForEvent`, and `notify` steps |
49
- | **Reliability** | Redis Streams, 24h idempotency, backoff retries, DLQ, provider circuit breakers |
50
- | **Templates** | `{{var}}` interpolation with escaping decided by the destination field |
51
- | **AI** | Optional LLM augmentation before render, via the Vercel AI SDK |
52
- | **Multi-tenancy** | Projects with isolated keys, data, and rate limits |
53
- | **Consent** | RFC 8058 one-click unsubscribe; complaints and hard bounces suppress automatically |
54
- | **Reporting** | Tag a send with a `campaign` label, then read delivery and engagement totals back |
55
- | **Agents** | An MCP server ([`@notifkit/mcp`](./packages/mcp)) driving all of it from a terminal |
56
- | **Observability** | Prometheus `/metrics`, `/health`, `/live`, `/ready`, and a queryable delivery log |
144
+ ## What you get
57
145
 
58
- Bring your own provider accounts first-party packages ship for Resend and Firebase Cloud Messaging, and anything else is one `Transport` class with a `send()` method. Your keys, your billing, your deliverability.
146
+ | The problem you don't want to build | How notifkit solves it |
147
+ | :------------------------------------------------------- | :------------------------------------------------------------------- |
148
+ | **“Should this user receive it?”** | User preferences, topic opt-outs, and consent gates |
149
+ | **“Is this a bad time to send?”** | Timezone-aware quiet hours that defer non-urgent sends |
150
+ | **“What if push fails?”** | Automatic ordered multi-channel fallback (`push` → `email` → `sms`) |
151
+ | **“What if my worker crashes?”** | Redis Streams consumer groups, retries, and durable idempotency |
152
+ | **“What if an event fires twice?”** | 24-hour deduplication via idempotency keys |
153
+ | **“Can I send this later?”** | Priority scheduling with `sendAt` and cancellation before dispatch |
154
+ | **“Can I send this 3 days after signup?”** | Stateful multi-step workflows with `wait` and `waitForEvent` |
155
+ | **“How do I know what happened?”** | Queryable delivery logs, Prometheus metrics, and campaign reporting |
156
+ | **“What happens when a provider goes down?”** | Circuit breakers, exponential backoff, and DLQ replay |
157
+ | **“What about bounces and spam complaints?”** | RFC 8058 one-click unsubscribe and automatic hard-bounce suppression |
158
+ | **“What if I don't want another SaaS holding my data?”** | 100% self-hosted on your PostgreSQL and Redis |
159
+
160
+ > **The idea is simple:** You decide what to say. **notifkit handles getting it there reliably.**
161
+
162
+ ---
59
163
 
60
- **Requirements:** Node 22+, PostgreSQL, Redis. In development notifkit starts throwaway Postgres and Redis containers for you, so the only prerequisite to try it is Docker.
164
+ ## What notifkit is and what it isn't
61
165
 
62
- **What it is not:** a marketing automation suite. It is infrastructure for product notifications.
166
+ **What it is:** the durable notification infrastructure layer running directly inside your own stack.
167
+
168
+ **What it isn't:** a marketing automation suite.
169
+
170
+ notifkit is not Customer.io, OneSignal, or SendGrid. You bring your own provider accounts — your keys, your billing, your deliverability.
171
+
172
+ First-party providers ship for Resend and Firebase Cloud Messaging. Anything else is a simple `Transport` class with a `send()` method.
173
+
174
+ ---
175
+
176
+ ## Quickstart
177
+
178
+ ### 1. Install
179
+
180
+ ```bash
181
+ npm install notifkit @notifkit/provider-resend
182
+ ```
183
+
184
+ ### 2. Run the engine and dispatch your first notification
185
+
186
+ ```ts
187
+ import { NotifkitServer, NotifkitClient } from "notifkit";
188
+ import { ResendTransport } from "@notifkit/provider-resend";
189
+
190
+ // 1. Start the server (runs API + workers; auto-starts Postgres & Redis in dev)
191
+ const server = new NotifkitServer({
192
+ services: ["all"],
193
+ port: 3000,
194
+ providers: [new ResendTransport({ apiKey: process.env.RESEND_API_KEY! })],
195
+ });
196
+ await server.start();
197
+
198
+ // 2. Instantiate client and register a template
199
+ const notifkit = new NotifkitClient({ baseUrl: "http://localhost:3000" });
200
+
201
+ await notifkit.syncTemplates({
202
+ templates: [
203
+ {
204
+ id: "order-shipped",
205
+ channel: "email",
206
+ content: { subject: "Order #{{orderId}} Shipped", text: "Your order is on the way!" },
207
+ },
208
+ ],
209
+ });
210
+
211
+ // 3. Register user and dispatch
212
+ await notifkit.addUser({ id: "usr_123", email: "alex@acme.com" });
213
+
214
+ await notifkit.notify({
215
+ user: "usr_123",
216
+ template: "order-shipped",
217
+ channels: ["email"],
218
+ data: { orderId: "9481" },
219
+ });
220
+ ```
221
+
222
+ ### 3. Or call directly via REST API
223
+
224
+ You don't need the Node.js SDK — notifkit exposes a standard HTTP REST API, so you can dispatch notifications and manage resources from any language (cURL, Python, Go, etc.):
225
+
226
+ ```bash
227
+ curl -X POST http://localhost:3000/v1/notify \
228
+ -H "Content-Type: application/json" \
229
+ -d '{
230
+ "user": "usr_123",
231
+ "template": "order-shipped",
232
+ "channels": ["email"],
233
+ "data": { "orderId": "9481" }
234
+ }'
235
+ ```
236
+
237
+ ### Development vs. Production
238
+
239
+ - **Local Development**: Docker is the only prerequisite. In development, notifkit starts throwaway PostgreSQL and Redis containers automatically.
240
+ - **Production**: Node 22+, PostgreSQL, Redis. Run migrations by pointing `drizzle-kit` at `node_modules/notifkit/drizzle`.
241
+
242
+ ---
243
+
244
+ ## Agent-operable
245
+
246
+ **notifkit isn't just an API your application can call — your AI agent can operate it directly.**
247
+
248
+ Connect the notifkit MCP server ([`@notifkit/mcp`](./packages/mcp)) to Claude Code, Cursor, Claude Desktop, Gemini, or any MCP-compatible agent:
249
+
250
+ ```bash
251
+ npx -y @notifkit/mcp
252
+ ```
253
+
254
+ ### Ask your agent
255
+
256
+ ```text
257
+ You: Why didn't usr_9182 receive their password reset?
258
+
259
+ Agent: The notification was suppressed because usr_9182's email
260
+ address has a hard-bounce suppression from yesterday.
261
+ ```
262
+
263
+ Your application and your AI agents use the **same notification infrastructure**:
264
+
265
+ - **Send & dispatch** — Send one-off notifications or campaigns to users, lists, and segments (`send_notification`, `send_campaign`)
266
+ - **Investigate & triage** — Diagnose delivery issues by inspecting message histories, provider responses, and quiet hours (`get_delivery_logs`, `get_notification`)
267
+ - **Schedule & cancel** — Schedule future sends and cancel pending notifications (`list_scheduled`, `cancel_notification`)
268
+ - **Campaign analytics** — Check delivery, open, click, bounce, and complaint metrics (`list_campaigns`, `get_campaign_stats`)
269
+ - **Template management** — List, preview, and update templates with sample data (`list_templates`, `preview_template`, `upsert_template`)
270
+ - **Users & preferences** — Look up users, contacts, preferences, and segment membership (`list_users`, `get_user_preferences`, `update_user_preferences`)
271
+ - **Workflow operations** — Trigger workflows and inspect workflow runs (`create_workflow`, `trigger_workflow`, `get_workflow_run`)
272
+ - **Suppressions & health** — Manage bounce suppressions, check system queues, and replay dead-letter messages (`list_suppressions`, `get_dead_letters`, `replay_dead_letter`)
273
+
274
+ ### From “write a script” to “just ask”
275
+
276
+ | Without an agent | With NotifKit MCP |
277
+ | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
278
+ | Jump into the DB to find contact info → open Twilio/Resend or write a throwaway script → format the payload → check their timezone manually → fire it off → hope it delivered | **You:** _“Send an urgent update to alex@acme.com that his package was lost in transit and support is rushing a replacement — text him if push doesn't deliver.”_<br><br>**Agent:** Looks up `alex@acme.com` → renders template → dispatches push with SMS fallback → bypasses quiet hours for urgent delivery → tracks delivery status → confirms it hit his phone |
279
+
280
+ [Set up MCP](https://notifkit.dev/docs/mcp.html) · [MCP documentation](https://notifkit.dev/docs/mcp.html)
281
+
282
+ ---
283
+
284
+ ## AI-assisted migration
285
+
286
+ Already have notification code scattered across your application?
287
+
288
+ Point your coding agent at:
289
+
290
+ ```text
291
+ https://notifkit.dev/llms-full.txt
292
+ ```
293
+
294
+ It can understand notifkit's API and help identify ad-hoc notification code in your repository and refactor it into durable notifkit calls.
295
+
296
+ ---
297
+
298
+ ## Feature matrix
299
+
300
+ | | |
301
+ | :------------------ | :------------------------------------------------------------------------------------- |
302
+ | **Channels** | `email`, `sms`, `push`, `webhook` |
303
+ | **Targeting** | A user, a list of users, a segment, or a topic |
304
+ | **Priorities** | `low`, `normal`, `high`, `critical` — separate stream lanes |
305
+ | **Scheduling** | Future sends with `sendAt`, quiet-hours deferral, cancellation |
306
+ | **Preferences** | Per-user channel and topic opt-outs, quiet hours, contact-level overrides |
307
+ | **Workflows** | Multi-step sequences with `wait`, `waitForEvent`, and `notify` steps |
308
+ | **Reliability** | Redis Streams, 24h idempotency, retries, DLQ, provider circuit breakers |
309
+ | **Templates** | `{{var}}` interpolation with destination-aware escaping |
310
+ | **AI** | Optional LLM augmentation before render via the Vercel AI SDK |
311
+ | **Multi-tenancy** | Projects with isolated keys, data, and rate limits |
312
+ | **Consent** | RFC 8058 one-click unsubscribe; complaints and hard bounces suppress automatically |
313
+ | **Reporting** | Campaign labels with delivery and engagement totals |
314
+ | **Agent operation** | MCP server for sending, triage, campaigns, templates, workflows, and system operations |
315
+ | **Observability** | Prometheus `/metrics`, `/health`, `/live`, `/ready`, and queryable delivery logs |
316
+
317
+ ---
318
+
319
+ ## Providers
320
+
321
+ Bring your own provider accounts.
322
+
323
+ First-party packages:
324
+
325
+ - [`@notifkit/provider-resend`](./packages/provider-resend) — transactional email via Resend
326
+ - [`@notifkit/provider-fcm`](./packages/provider-fcm) — push notifications via Firebase Cloud Messaging
327
+
328
+ For anything else, implement a simple `Transport`:
329
+
330
+ ```ts
331
+ class MyTransport implements Transport {
332
+ async send(message) {
333
+ // Send through Twilio, SES, Postmark, APNs,
334
+ // SendGrid, a custom webhook, or anything else.
335
+ }
336
+ }
337
+ ```
338
+
339
+ **Your keys. Your billing. Your deliverability.**
340
+
341
+ ---
63
342
 
64
343
  ## Documentation
65
344
 
66
- Everything lives at **[notifkit.dev/docs](https://notifkit.dev/docs/)**.
345
+ Everything lives at [**notifkit.dev/docs**](https://notifkit.dev/docs/).
346
+
347
+ | | |
348
+ | :----------------------------------------------------------------------------- | :------------------------------------------------------- |
349
+ | [Quickstart](https://notifkit.dev/docs/quickstart.html) | Install to first delivered notification |
350
+ | [How it works](https://notifkit.dev/docs/concepts.html) | Core concepts and the notification pipeline |
351
+ | [Channels & fallback](https://notifkit.dev/docs/guides/routing.html) | Multicast, ordered fallback, and custom transports |
352
+ | [Preferences & quiet hours](https://notifkit.dev/docs/guides/preferences.html) | Preference, consent, and timing rules |
353
+ | [Templates & AI](https://notifkit.dev/docs/guides/templates.html) | Interpolation, escaping, and per-channel content |
354
+ | [Segments & scheduling](https://notifkit.dev/docs/guides/segments.html) | Fan-out, priority lanes, `sendAt`, and idempotency |
355
+ | [Workflows](https://notifkit.dev/docs/guides/workflows.html) | Multi-step sequences, recurring sends, and digests |
356
+ | [Examples](https://notifkit.dev/docs/examples.html) | Runnable projects |
357
+ | [Architecture](https://notifkit.dev/docs/architecture.html) | Streams, delivery guarantees, topologies, and data model |
358
+ | [Deployment](https://notifkit.dev/docs/deployment.html) | Docker, Compose, and production topologies |
359
+ | [Operations](https://notifkit.dev/docs/operations.html) | Health, metrics, DLQ, key rotation, and shutdown |
360
+ | [Reference](https://notifkit.dev/docs/reference.html) | API, payloads, and SDK methods |
361
+ | [MCP server](https://notifkit.dev/docs/mcp.html) | Operate notifkit from an AI agent |
362
+
363
+ ---
364
+
365
+ ## Why build this?
366
+
367
+ Because notification infrastructure looks simple until you're responsible for it.
368
+
369
+ You can spend months building queues, retries, provider adapters, preference systems, quiet-hour logic, workflows, suppression handling, and operational tooling.
67
370
 
68
- | | |
69
- | ------------------------------------------------------------------------------ | ---------------------------------------------------------- |
70
- | [Quickstart](https://notifkit.dev/docs/quickstart.html) | Install to first delivered notification, about ten minutes |
71
- | [How it works](https://notifkit.dev/docs/concepts.html) | The five nouns and the pipeline they move through |
72
- | [Channels & fallback](https://notifkit.dev/docs/guides/routing.html) | Multicast, ordered fallback, writing a transport |
73
- | [Preferences & quiet hours](https://notifkit.dev/docs/guides/preferences.html) | The four gates every notification passes |
74
- | [Templates & AI](https://notifkit.dev/docs/guides/templates.html) | Interpolation, escaping, per-channel content |
75
- | [Segments & scheduling](https://notifkit.dev/docs/guides/segments.html) | Fan-out, priority lanes, `sendAt`, idempotency |
76
- | [Workflows](https://notifkit.dev/docs/guides/workflows.html) | Multi-step sequences, recurring sends, digests |
77
- | [Examples](https://notifkit.dev/docs/examples.html) | The five runnable projects in [`examples/`](./examples) |
78
- | [Architecture](https://notifkit.dev/docs/architecture.html) | Streams, delivery guarantees, topologies, data model |
79
- | [Deployment](https://notifkit.dev/docs/deployment.html) | Dockerfile, Compose, splitting API from workers |
80
- | [Operations](https://notifkit.dev/docs/operations.html) | Health, metrics, the DLQ, key rotation, shutdown |
81
- | [Reference](https://notifkit.dev/docs/reference.html) | Every endpoint, payload shape, and SDK method |
82
- | [MCP server](https://notifkit.dev/docs/mcp.html) | Send and report on campaigns from a terminal agent |
371
+ Or you can use the infrastructure we built for ourselves.
83
372
 
84
- ## Star the repo
373
+ **notifkit exists so your team can spend its time building the product — not another notification platform.**
85
374
 
86
- If notifkit saves you a month or two you were about to spend building this yourself, **[give it a star](https://github.com/devkitshq/notifkit)** — it is the cheapest way to help other people find it.
375
+ ---
376
+
377
+ ## Star the repo ⭐
378
+
379
+ If notifkit saves you a month or two you were about to spend building this yourself, **[give it a star on GitHub](https://github.com/devkitshq/notifkit)** — it's the cheapest way to help other people find it.
87
380
 
88
381
  ## Contributing
89
382
 
90
- Issues and pull requests are welcome. `npm install && npm run build`, then `npm test` — the suite starts its own Postgres and Redis containers, so Docker is the only thing you need running.
383
+ Issues and pull requests are welcome.
384
+
385
+ ```bash
386
+ npm install
387
+ npm run build
388
+ npm test
389
+ ```
390
+
391
+ The test suite starts its own PostgreSQL and Redis containers, so Docker is the only thing you need running.
91
392
 
92
393
  ## License
93
394
 
package/dist/index.d.mts CHANGED
@@ -444,6 +444,7 @@ declare const NotificationEnrichedPayloadSchema: z.ZodObject<{
444
444
  priority: "low" | "normal" | "high" | "critical";
445
445
  projectId: string;
446
446
  recipientId: string;
447
+ rawEventId: string;
447
448
  templateVariables: Record<string, unknown>;
448
449
  recipient: {
449
450
  id: string;
@@ -463,7 +464,6 @@ declare const NotificationEnrichedPayloadSchema: z.ZodObject<{
463
464
  pushToken?: string | undefined;
464
465
  pushTokens?: string[] | undefined;
465
466
  };
466
- rawEventId: string;
467
467
  aiPrompts?: Record<string, string> | undefined;
468
468
  templateId?: string | undefined;
469
469
  campaignId?: string | undefined;
@@ -474,6 +474,7 @@ declare const NotificationEnrichedPayloadSchema: z.ZodObject<{
474
474
  priority: "low" | "normal" | "high" | "critical";
475
475
  projectId: string;
476
476
  recipientId: string;
477
+ rawEventId: string;
477
478
  templateVariables: Record<string, unknown>;
478
479
  recipient: {
479
480
  id: string;
@@ -493,7 +494,6 @@ declare const NotificationEnrichedPayloadSchema: z.ZodObject<{
493
494
  pushTokens?: string[] | undefined;
494
495
  locale?: string | undefined;
495
496
  };
496
- rawEventId: string;
497
497
  aiPrompts?: Record<string, string> | undefined;
498
498
  templateId?: string | undefined;
499
499
  campaignId?: string | undefined;
@@ -702,9 +702,9 @@ declare const NotificationDispatchedPayloadSchema: z.ZodObject<{
702
702
  priority: "low" | "normal" | "high" | "critical";
703
703
  projectId: string;
704
704
  taskId: string;
705
- enrichedEventId: string;
706
705
  recipientId: string;
707
706
  templateVariables: Record<string, unknown>;
707
+ enrichedEventId: string;
708
708
  renderedContent: {
709
709
  content: Record<string, unknown>;
710
710
  attachments?: {
@@ -747,8 +747,8 @@ declare const NotificationDispatchedPayloadSchema: z.ZodObject<{
747
747
  priority: "low" | "normal" | "high" | "critical";
748
748
  projectId: string;
749
749
  taskId: string;
750
- enrichedEventId: string;
751
750
  recipientId: string;
751
+ enrichedEventId: string;
752
752
  renderedContent: {
753
753
  content: Record<string, unknown>;
754
754
  attachments?: {
@@ -993,7 +993,6 @@ declare const NotificationAiPendingPayloadSchema: z.ZodObject<{
993
993
  priority: "low" | "normal" | "high" | "critical";
994
994
  aiPrompts: Record<string, string>;
995
995
  projectId: string;
996
- enrichedEventId: string;
997
996
  recipientId: string;
998
997
  templateVariables: Record<string, unknown>;
999
998
  recipient: {
@@ -1014,6 +1013,7 @@ declare const NotificationAiPendingPayloadSchema: z.ZodObject<{
1014
1013
  pushToken?: string | undefined;
1015
1014
  pushTokens?: string[] | undefined;
1016
1015
  };
1016
+ enrichedEventId: string;
1017
1017
  templateId?: string | undefined;
1018
1018
  scheduledAt?: string | undefined;
1019
1019
  fallbackChain?: ("push" | "email" | "sms" | "webhook" | "in-app")[] | undefined;
@@ -1022,7 +1022,6 @@ declare const NotificationAiPendingPayloadSchema: z.ZodObject<{
1022
1022
  priority: "low" | "normal" | "high" | "critical";
1023
1023
  aiPrompts: Record<string, string>;
1024
1024
  projectId: string;
1025
- enrichedEventId: string;
1026
1025
  recipientId: string;
1027
1026
  templateVariables: Record<string, unknown>;
1028
1027
  recipient: {
@@ -1043,6 +1042,7 @@ declare const NotificationAiPendingPayloadSchema: z.ZodObject<{
1043
1042
  pushTokens?: string[] | undefined;
1044
1043
  locale?: string | undefined;
1045
1044
  };
1045
+ enrichedEventId: string;
1046
1046
  templateId?: string | undefined;
1047
1047
  scheduledAt?: string | undefined;
1048
1048
  fallbackChain?: ("push" | "email" | "sms" | "webhook" | "in-app")[] | undefined;
@@ -9266,7 +9266,13 @@ declare class UserRepository {
9266
9266
  preferences?: Preferences;
9267
9267
  }): Promise<boolean>;
9268
9268
  delete(projectId: string, userId: string): Promise<boolean>;
9269
- list(projectId: string, limit: number, cursor?: string): Promise<{
9269
+ list(projectId: string, limit: number, cursor?: string, filters?: {
9270
+ search?: string;
9271
+ segment?: string;
9272
+ language?: string;
9273
+ timezone?: string;
9274
+ channel?: string;
9275
+ }): Promise<{
9270
9276
  users: UserProfile[];
9271
9277
  nextCursor: string | null;
9272
9278
  }>;
@@ -9789,7 +9795,10 @@ declare class NotifkitClient {
9789
9795
  synced: number;
9790
9796
  }>;
9791
9797
  /** List registered workflow definitions. */
9792
- listWorkflows(): Promise<{
9798
+ listWorkflows(options?: {
9799
+ limit?: number;
9800
+ search?: string;
9801
+ }): Promise<{
9793
9802
  workflows: any[];
9794
9803
  }>;
9795
9804
  /** Get a workflow instance by ID. */
@@ -9804,6 +9813,9 @@ declare class NotifkitClient {
9804
9813
  workflowInstanceId?: string;
9805
9814
  channel?: string;
9806
9815
  status?: string;
9816
+ taskId?: string;
9817
+ campaign?: string;
9818
+ search?: string;
9807
9819
  }): Promise<{
9808
9820
  logs: any[];
9809
9821
  nextCursor: string | null;
@@ -9812,6 +9824,11 @@ declare class NotifkitClient {
9812
9824
  listUsers(options?: {
9813
9825
  limit?: number;
9814
9826
  cursor?: string;
9827
+ search?: string;
9828
+ segment?: string;
9829
+ language?: string;
9830
+ timezone?: string;
9831
+ channel?: string;
9815
9832
  }): Promise<{
9816
9833
  users: any[];
9817
9834
  nextCursor: string | null;
@@ -9853,6 +9870,11 @@ declare class NotifkitClient {
9853
9870
  /** List campaign labels seen in the delivery log, most recent activity first. */
9854
9871
  listCampaigns(options?: {
9855
9872
  limit?: number;
9873
+ search?: string;
9874
+ channel?: string;
9875
+ since?: string | Date;
9876
+ until?: string | Date;
9877
+ minMessages?: number;
9856
9878
  }): Promise<{
9857
9879
  campaigns: {
9858
9880
  campaign: string;
@@ -9874,6 +9896,7 @@ declare class NotifkitClient {
9874
9896
  limit?: number;
9875
9897
  channel?: string;
9876
9898
  reason?: string;
9899
+ target?: string;
9877
9900
  }): Promise<{
9878
9901
  suppressions: any[];
9879
9902
  }>;