by devkitshq
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.
# Add to your Claude Code skills
git clone https://github.com/devkitshq/notifkitLast scanned: 8/29/2026
{
"issues": [
{
"type": "npm-audit",
"message": "@esbuild-kit/core-utils: Vulnerability found",
"severity": "medium"
},
{
"type": "npm-audit",
"message": "@esbuild-kit/esm-loader: Vulnerability found",
"severity": "medium"
},
{
"type": "npm-audit",
"message": "@google-cloud/firestore: Vulnerability found",
"severity": "medium"
},
{
"type": "npm-audit",
"message": "@google-cloud/storage: Vulnerability found",
"severity": "medium"
},
{
"type": "npm-audit",
"message": "@vitest/coverage-v8: Vulnerability found",
"severity": "critical"
},
{
"type": "npm-audit",
"message": "@vitest/mocker: Vulnerability found",
"severity": "medium"
},
{
"type": "npm-audit",
"message": "brace-expansion: brace-expansion: DoS via unbounded expansion length causing an out-of-memory process crash",
"severity": "high"
},
{
"type": "npm-audit",
"message": "drizzle-kit: Vulnerability found",
"severity": "medium"
},
{
"type": "npm-audit",
"message": "esbuild: esbuild enables any website to send any requests to the development server and read the response",
"severity": "medium"
},
{
"type": "npm-audit",
"message": "fast-uri: fast-uri vulnerable to host confusion via literal backslash authority delimiter",
"severity": "high"
},
{
"type": "npm-audit",
"message": "firebase-admin: Vulnerability found",
"severity": "medium"
},
{
"type": "npm-audit",
"message": "gaxios: Vulnerability found",
"severity": "medium"
},
{
"type": "npm-audit",
"message": "google-gax: Vulnerability found",
"severity": "medium"
},
{
"type": "npm-audit",
"message": "js-yaml: JS-YAML: Quadratic CPU consumption in !!omap resolution (3.x and 4.x) — CVE-2026-59870 fix not backported",
"severity": "high"
},
{
"type": "npm-audit",
"message": "nanoid: nanoid: custom generators can loop indefinitely when size is zero",
"severity": "high"
},
{
"type": "npm-audit",
"message": "postcss: PostCSS: incomplete fix of GHSA-6g55-p6wh-862q — attacker-controlled sourceMappingURL reads arbitrary .map files when `from` is unset",
"severity": "medium"
},
{
"type": "npm-audit",
"message": "retry-request: Vulnerability found",
"severity": "medium"
},
{
"type": "npm-audit",
"message": "teeny-request: Vulnerability found",
"severity": "medium"
},
{
"type": "npm-audit",
"message": "undici: undici vulnerable to downstream response desynchronization via retry interceptor",
"severity": "high"
},
{
"type": "npm-audit",
"message": "uuid: uuid: Missing buffer bounds check in v3/v5/v6 when buf is provided",
"severity": "medium"
},
{
"type": "npm-audit",
"message": "vite: Vite Vulnerable to Path Traversal in Optimized Deps `.map` Handling",
"severity": "high"
},
{
"type": "npm-audit",
"message": "vite-node: Vulnerability found",
"severity": "medium"
},
{
"type": "npm-audit",
"message": "vitest: Vulnerability found",
"severity": "critical"
}
],
"status": "FAILED",
"scannedAt": "2026-08-29T10:50:17.836Z",
"npmAuditRan": true,
"pipAuditRan": true,
"promptInjectionRan": true
}See how notifkit compares with popular alternatives.
notifkit is an open-source ai agents skill for AI coding assistants such as Claude Code, Codex CLI, and ChatGPT, built by devkitshq. 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. It has 113 GitHub stars.
notifkit failed SkillsLLM's automated security scan, which flagged one or more high-severity issues. Review the Security Report section carefully before using it.
Clone the repository with "git clone https://github.com/devkitshq/notifkit" and add it to your Claude Code skills directory (see the Installation section above).
notifkit is primarily written in TypeScript. It is open-source under devkitshq on GitHub, so you can review or fork the full source.
Yes. SkillsLLM lists many other AI Agents skills you can browse and compare side by side. Open the AI Agents category from the badge at the top of this page, or use the Related Skills and comparison links further down to weigh notifkit against similar tools.
No comments yet. Be the first to share your thoughts!
Requires a passing catalog security scan. Resolve the flagged issues and resubmit to enable featuring.
⚠️ Third-Party Software Notice
This skill is third-party open-source software developed and hosted independently on GitHub. SkillsLLM is an informational directory and does not control or maintain the underlying repository.
Any security checks, ratings, or warnings displayed by SkillsLLM are automated and limited in scope. They do not constitute a security certification or guarantee that the software is safe, error-free, or free from malicious code, vulnerabilities, compromised dependencies, or prompt-injection risks.
Review the source code, permissions, dependencies, and configuration before installing or running any third-party skill. Use is at your own risk. To the maximum extent permitted by applicable law, SkillsLLM is not liable for losses arising from third-party software.
You shouldn't have to build a notification system.
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.
Documentation · Quickstart · Examples · notifkit.dev
await sendEmail({
to: user.email,
subject: "Order Shipped",
...
});
Sending them reliably is the hard part. Users opt out. People are asleep. Push tokens die. Providers throw 503s. Channels fail and need fallback. Somewhere along the way 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.
notifkit is that machinery. Your app makes one typed call, and notifkit decides who gets the notification, which channel to use, when to send it, whether the user is allowed to receive it, and what to do when delivery fails.
import { notifkit } from "notifkit";
await notifkit.notify({
user: "usr_123",
template: "order-shipped",
channels: ["push", "email"],
fallback: true,
});
That call tries push, then email if push fails. Preferences, consent, quiet hours, retries, deduplication, throttling, template rendering, and delivery tracking all happen behind it.
notifkit is an orchestration engine and a typed SDK.
flowchart TD
App["Your Application / AI Agent<br/>Typed SDK · REST API · MCP Server"]
App -->|"HTTP POST /v1/notify"| API
API["Notifkit API Server<br/>Schema Validation · Auth · Multi-Tenancy<br/>Idempotency Gate · Priority Queue Ingestion"]
API --> PG
API --> REDIS
PG[("PostgreSQL — Storage<br/>Users · Preferences<br/>Templates · Workflows<br/>Delivery Logs · DLQ")]
REDIS[("Redis — Streams / ZSET<br/>Priority Queues<br/>Scheduled Sends<br/>Sliding Rate Limits")]
subgraph WORKERS["Background Workers Pipeline"]
direction LR
ENRICH["Enricher<br/>(Resolve)"] --> ENGINE["Engine<br/>(Quiet Hours)"] --> DELIVER["Delivery<br/>(Rate Limits / CB)"]
ENGINE --> SCHED["Scheduler<br/>(sendAt / QH)"]
SCHED --> DELIVER
end
REDIS -->|"consume"| ENRICH
ENRICH -.->|"read / write state"| PG
DELIVER -.->|"delivery logs"| PG
DELIVER -->|"Dispatch"| PROVIDERS
PROVIDERS["Provider Transports<br/>Email: Resend · Push: Firebase (FCM) · SMS: Twilio<br/>Chat: Slack, Telegram, Discord, WhatsApp<br/>Webhooks: Custom HTTP"]
classDef entry stroke:#6366f1,stroke-width:2px
classDef store stroke:#0ea5e9,stroke-width:2px
classDef work stroke:#22c55e,stroke-width:2px
class App,API,PROVIDERS entry
class PG,REDIS store
class ENRICH,ENGINE,DELIVER,SCHED work
NotifkitServer runs the HTTP REST API router (/v1/notify, /health, /metrics) and the background worker pipelines: enricher, decision engine, scheduler, and delivery. NotifkitClient is the lightweight client your application uses to trigger notifications, sync templates, and manage users over HTTP.
In a single process, the API and all workers run in the same Node.js process (services: ["all"]), which works for small and medium apps, side projects, and staging. Distributed, you 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.
npm install notifkit @notifkit/provider-resend
npm install -D tsx @testcontainers/postgresql @testcontainers/redis
The two @testcontainers/* packages are what notifkit uses to start throwaway PostgreSQL and Redis containers in development. They are imported lazily, only when the server is given neither a databaseUrl/redisUrl option nor a DATABASE_URL/REDIS_URL environment variable, so devDependencies is the right place for them.
[!WARNING] Those containers are for local development only. They are thrown away when the process exits, taking every user, template, delivery log, and queued notification with them. Before you deploy, point notifkit at a real PostgreSQL and Redis — set
DATABASE_URLandREDIS_URL(or passdatabaseUrlandredisUrl) and run withNODE_ENV=production, which refuses to start a container and fails loudly if either is missing.
server.ts starts the API and the worker pipelines. In development it auto-starts those containers, so Docker is the only prerequisite.
// server.ts
import { NotifkitServer } from "notifkit";
import { ResendTransport } from "@notifkit/provider-resend";
const server = new NotifkitServer({
services: ["all"], // API + enricher + engine + scheduler + delivery
port: 3000,
providers: [
new ResendTransport({
apiKey: process.env.RESEND_API_KEY!,
from: "notifications@yourdomain.com",
}),
],
});
await server.start();
console.log("notifkit listening on http://localhost:3000");
from is required on ResendTransport — it is the sender for any template that does not name its own, and it has to be an address on a domain you have verified in Resend. A template can override it with its own from, so one transport can serve both no-reply@ receipts and marketing@ campaigns.
ADMIN_API_KEY is the root credential. It is read from the environment, it is what mints project API keys in the next step, and without it the project-management routes answer 403. Any string works locally:
ADMIN_API_KEY=supersecretkey RESEND_API_KEY=re_xxx npx tsx server.ts
[!WARNING]
supersecretkeyis a local placeholder. In production this one value can mint keys for every project, so use a long random string kept in your secret store —openssl rand -hex 32is enough.
Every /v1/* route requires a project API key, and only the admin credential can mint one, so this is the single bootstrap step between a running server and your first notification:
ADMIN_API_KEY=supersecretkey npx notifkit-create-project "my-app"
Project "my-app" created. Save the API key now — it is not recoverable.
NOTIFKIT_PROJECT_ID=1ce67fa1-b4a9-4985-8046-ef6018912b2a
NOTIFKIT_API_KEY=nk_live_f57c57b76d795cef89e2dbf6b6f352a36…
The server stores only a SHA-256 hash of the key, so the nk_live_… value is printed once and never again — put it in your app's .env now. Point the script at another host with NOTIFKIT_URL, and mint further keys later with POST /v1/projects/:id/keys (role: "read_only" there gets you a key that can read but not send).
client.ts is your application code. It talks to the server over HTTP: register a template, register a user, and send.
// client.ts
import { NotifkitClient } from "notifkit";
const notifkit = new NotifkitClient({
baseUrl: "http://localhost:3000",
apiKey: process.env.NOTIFKIT_API_KEY!,
});
// 1. Register a template
await notifkit.syncTemplates({
templates: [
{
id: "order-shipped",
channel: "email",
content: { subject: "Order #{{orderId}} Shipped", text: "Your order is on the way!" },
},
],
});
// 2. Register a user (supports id + contacts array or object)
await notifkit.addUser("usr_123", [{ channel: "email", target: "alex@acme.com" }]);
// 3. Dispatch
await notifkit.notify({
user: "usr_123",
template: "order-shipped",
channels: ["email"],
data: { orderId: "9481" },
});
With the server still running in the first terminal, run the client in a second one:
NOTIFKIT_API_KEY=nk_live_xxx npx tsx client.ts
The Node.js SDK is optional. notifkit exposes a standard HTTP REST API, so you can dispatch notifications and manage resources from any language (cURL, Python, Go, and so on). The same project API key goes in the Authorization header (an x-api-key header works too):
curl -X POST http://localhost:3000/v1/notify \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $NOTIFKIT_API_KEY" \
-d '{
"user": "usr_123",
"template": "order-shipped",
"channels": ["email"],
"data": { "orderId": "9481" }
}'
Locally, Docker is the only prerequisite: in development notifkit starts throwaway PostgreSQL and Redis containers for you. In production you need Node 22+, PostgreSQL, and Redis, and you run migrations by pointing drizzle-kit at node_modules/notifkit/drizzle.
notifkit runs in production at my own company, delivering 100K+ notifications a day across email, push, and OTPs. I built it because I needed it and didn't want to spend months rebuilding distributed notification plumbing or pay a SaaS per alert. It runs on your servers, w