Skip to main content
Every event you subscribe to is delivered as a POST to your endpoint with the same top-level shape. The two fields most integrations care about are event_type (what happened) and metadata (what you tagged the verification with at create time).

Headers

The signature is HMAC-SHA256 of "<timestamp>.<raw_body>" using your endpoint’s secret. Recompute it on your side and reject the request if it doesn’t match.

Body

Top-level fields

user block

metadata block

Everything you passed in metadata when you created the challenge, echoed back verbatim. Use it as the primary correlation key — ticket_id, order_id, whatever your domain needs. For MSP challenges (created via POST /v1/workspaces/:workspace_id/challenges), Scute auto-attaches an msp sub-object with routing identifiers:
Cosmetic MSP fields (name, logo_url, primary_color) are used by the tenant verifier page but not included in webhook payloads — your handler gets only the routing IDs. The _plain_code field (used internally for OTP delivery) is always stripped from the payload — it is never sent to your endpoint.

Event types

Subscribe via the dashboard or POST /v1/apps/:app_id/webhook_endpoints. The slugs we currently emit:

Verification

User

Auth / sessions

App / system

Wildcard

Delivery semantics

  • At-least-once. If your endpoint returns non-2xx, we retry with backoff up to the endpoint’s retry_limit (default 3). Use the id field for idempotency.
  • Exactly one POST per event per matching subscriber. No duplicates.
  • No ordering guarantee. Don’t assume events arrive in causal order.
  • Async delivery. Webhook delivery happens shortly after the underlying event — typically within milliseconds, but can be delayed under load.
  • No automatic IP allowlisting today. Validate via the X-Webhook-Signature header.

Verifying the signature (example, Node.js)

Pass the raw request body (not the parsed JSON object) when computing the signature.

Per-challenge callback_url (alternative to webhook endpoints)

If you set callback_url when creating a challenge, Scute also POSTs a single request to that URL when the challenge reaches a terminal state. The payload shape differs from the webhook endpoint payload — see Manage Users for the challenge creation API. Use webhook endpoints unless you specifically need per-challenge routing.