Skip to content

Webhooks

Webhooks let your backend react to things that happen inside your IntelliAuth tenant — without polling. The tenant emits an event (a user signed in, an MFA factor was enrolled, an application was created); the platform sends an HTTP POST to your registered receiver URL with the event payload; your backend processes it.

The shape is standard:

POST https://api.banking.cymmetri.com/webhooks/intelliauth HTTP/1.1
Content-Type: application/json
X-IntelliAuth-Event-Id: evt_01HZX...
X-IntelliAuth-Event-Type: user.signed_in
X-IntelliAuth-Signature: <HMAC-SHA256>
X-IntelliAuth-Timestamp: 2026-05-17T12:34:56Z
X-IntelliAuth-Delivery-Attempt: 1
{
"id": "evt_01HZX...",
"type": "user.signed_in",
"created": "2026-05-17T12:34:56Z",
"tenant_id": "tnt_01HZX...",
"data": {
"user_id": "usr_01HZX...",
"session_id": "ses_01HZX...",
"auth_level": "AAL2",
"connection": "google-oauth2"
}
}

What the platform gives you:

  • At-least-once delivery. The platform retries failed deliveries with exponential backoff up to a budget (typically 24 hours).
  • HMAC signature. Every delivery is signed with a shared secret; your receiver verifies the signature before trusting the payload. See Verify webhook signatures.
  • Anti-replay window. Each delivery carries a timestamp; replays older than 5 minutes are flagged. Combined with idempotency keys, replay attacks are defeated.
  • Ordered per resource (best effort). The platform tries to deliver events about the same resource in order; cross-resource ordering isn't guaranteed.
  • SSRF protection. The platform won't deliver to internal-network or loopback addresses. Your receiver must be on the public internet.

What you're expected to guarantee:

  • Idempotent receivers. Same event id might arrive twice (network blip, your 200 not received). Track event ids; ignore duplicates.
  • Fast 2xx responses. Return 200 within 30 seconds. Do any heavy work asynchronously (push to a queue, return 200 immediately).
  • Signature verification. Reject deliveries with invalid signatures. The signature is your only proof that the event is real.
  • Stable receiver URL. If you change it, register the new URL in the tenant admin console; the platform doesn't auto-discover.

The platform emits events for every meaningful state change. A non-exhaustive list:

  • user.created, user.updated, user.deleted
  • user.signed_in, user.signed_out
  • user.mfa_enrolled, user.mfa_removed, user.mfa_challenged
  • user.password_changed, user.password_reset_requested
  • session.created, session.revoked, session.expired
  • application.created, application.updated, application.deleted
  • application.secret_rotated
  • application.scope_added, application.scope_removed
  • risk.threshold_triggered, risk.feed_hit
  • audit.compliance_export_requested

See Event reference for the full catalogue with payload shapes.

  • Pipelines run during an event and can change its outcome (deny a sign-in, add claims to the token).
  • Webhooks fire after an event has happened and can react but not change it.

If you want to deny a sign-in based on your business logic, that's an Action inside a pipeline. If you want to record the sign-in in your CRM, that's a webhook.

The two compose nicely: a pipeline emits the event, the webhook fans it out to your downstream systems.

Two anti-patterns:

  • As an audit log replacement. The IntelliAuth audit log is the source of truth for who-did-what. Webhooks are a delivery channel, not a record. If your webhook receiver loses an event, the audit log still has it.
  • For real-time, low-latency reactions. Webhooks are eventually-consistent (best-effort within seconds). If you need synchronous "react during the sign-in", that's a pipeline Action, not a webhook.

For testing webhooks against a localhost receiver, use a tunnel:

Terminal window
# Using ngrok
ngrok http 3000
# → register the ngrok URL as your webhook receiver in the tenant admin

Or use the platform's webhook-replay feature (when available) — the admin console can re-deliver a recent event to a different URL for testing.