Webhooks
Subscribe HTTPS endpoints to nRouter virtual-key lifecycle events, verify HMAC signatures, and inspect delivery status for every subscription.
nRouter webhooks deliver signed JSON payloads to your HTTPS endpoints for virtual-key lifecycle events (key.created, key.revoked, key.rotated), verified via HMAC-SHA256 (x-nr-signature). Subscriptions are managed via the dashboard API at https://app.nrouter.ai/api/webhooks.
Alert channels (email, Slack, Teams, Jira) are managed separately via https://app.nrouter.ai/api/notification-channels for threshold alerts. See Alert channels are a different surface.
Events & API
| Event | Trigger |
|---|---|
key.created / key.revoked | Virtual key creation, revocation, or bulk operations |
key.rotated | Virtual key rotation |
Manage subscriptions via /api/webhooks?orgId=$ORG_ID using dashboard session cookies (Owner/org_admin) and Origin: https://app.nrouter.ai:
# Register (HTTPS public hosts only; secret enables HMAC RFC 2104)
curl -X POST "https://app.nrouter.ai/api/webhooks?orgId=$ORG_ID" -b "$COOKIE" \
-H "Origin: https://app.nrouter.ai" -H "Content-Type: application/json" \
-d '{"url":"https://example.com/webhook","events":["key.created","key.rotated"],"secret":"$SECRET"}'
# List or Delete
curl "https://app.nrouter.ai/api/webhooks?orgId=$ORG_ID" -b "$COOKIE"
curl -X DELETE "https://app.nrouter.ai/api/webhooks?orgId=$ORG_ID&id=$ID" -b "$COOKIE" -H "Origin: https://app.nrouter.ai"last_status records latest HTTP responses (0 = failure/timeout). Rate limit: 5/minute per user.
Payload & signature verification
{
"event": "key.created",
"organization_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"timestamp": "2026-05-27T11:42:03.118Z",
"data": { "key_name": "sk-...a91c", "key_alias": "backend", "models": ["gpt-4.1-mini"], "actor_user_id": "9a2b6f04-31c5-4d19-b0e7-5c3a8e17d240" }
}Deliveries omit secret keys (key_name is masked). When secret is configured, headers include x-nr-signature: sha256=<digest>:
import crypto from "node:crypto";
export function verifySignature(rawBody: string, header: string | undefined, secret: string): boolean {
if (!header?.startsWith("sha256=")) return false;
const expected = Buffer.from(crypto.createHmac("sha256", secret).update(rawBody).digest("hex"), "hex");
const actual = Buffer.from(header.slice(7), "hex");
return expected.length === actual.length && crypto.timingSafeEqual(expected, actual);
}Requirements & semantics
- Requirements: HTTPS only, public IPs (no private/loopback/internal ranges), no redirects, and 5s timeout.
- Delivery semantics: Fire-and-forget without automatic retries. Key changes persist regardless of webhook outcomes.
- Testing: Subscribe to
key.created, mint a test key, and checklast_status.
Alert channels are a different surface
Notification channels in dashboard Alerts use https://app.nrouter.ai/api/notification-channels for threshold alerts, not key lifecycle events.
Frequently asked questions
Are failed deliveries retried?
No. Deliveries are fire-and-forget; inspect last_status on the subscription row.
Does a delivery ever contain the full API key?
No. Payloads contain only masked keys (sk-... plus 4 characters).
Next Steps
- API Key Management — Virtual-key lifecycle operations
- Budget Controls — Caps per organization, team, and key
- Chat Completions API — Inference API reference