Browse documentation

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

EventTrigger
key.created / key.revokedVirtual key creation, revocation, or bulk operations
key.rotatedVirtual 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 check last_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

Was this page helpful?