Platform
ScaiWave ScaiGrid ScaiCore ScaiBot ScaiDrive ScaiKey Models Tools & Services
Solutions
Organisations Developers Internet Service Providers Managed Service Providers AI-in-a-Box
Resources
Support Documentation Blog Downloads
Company
About Research Careers Investment Opportunities Contact
Log in

REST API: Webhooks

ScaiKey-signed webhook receiver. Updates the local user/group mirror in near-real-time on join/leave events.

POST /v1/webhooks/scaikey#

ScaiKey posts signed events here. The handler verifies the HMAC-SHA256 signature using SCAIFLOW_SCAIKEY_WEBHOOK_SECRET, then dispatches to per-event-type handlers.

Headers (set by ScaiKey, verified server-side):

  • X-Scaikey-Signature: t=<timestamp>,v1=<hex_sha256> — HMAC over timestamp.body with the shared secret.
  • X-Scaikey-Timestamp — used both in the signature payload and for replay-attack protection.
  • Content-Type: application/json.

Body — a ScaiKey webhook envelope:

jsonc
{
  "type": "user.created",                  // event type
  "id": "evt_xxx",
  "created_at": "2026-04-29T...",
  "data": {                                // type-dependent payload
    "user_id": "usr_xxx",
    "tenant_id": "tnt_acme",
    "email": "alice@acme.example"
  }
}

Response:

jsonc
{ "handled": true }                        // or false for unknown event types

Unknown event types return 200 + handled: false (so ScaiKey doesn't retry them — they're just ignored).

Bad/missing signatures return 400; missing timestamp or wrong secret config return 400/503 respectively.

Supported event types#

Type Behavior
user.created Upsert the user row in the local mirror.
user.updated Upsert (email/name/status changes).
user.deleted Soft-delete (preserves admin_role + audit trail).
group.created Upsert the group row.
group.updated Upsert.
group.deleted Soft-delete.
group_membership.added Upsert (user_id, group_id) into the memberships table. Triggers super_admin re-reconciliation.
group_membership.removed Soft-delete the membership. Triggers super_admin re-reconciliation.
application.group_assigned Refresh effective_users + re-reconcile super_admin. Does NOT create a Group row (mirroring is via the regular sync).
application.group_unassigned Same as assigned.

Anything else returns 200 + handled: false.

Fallback: scheduled sync#

If webhooks aren't reaching the backend (network issue, ScaiKey misconfiguration), the hourly background sync (interval set by SCAIFLOW_SYNC_INTERVAL_MINUTES, default 60) catches up. The CLI bootstrap (scaiflow scaikey-register) does the initial sync.

Configuration#

Two env vars on the backend:

  • SCAIFLOW_SCAIKEY_WEBHOOK_SECRET — the shared secret used for signature verification. Get it from your ScaiKey app's webhook configuration UI.
  • (no second env var) — the webhook URL is whatever you configure in ScaiKey's app settings. Typically https://scaiflow.example/api/v1/webhooks/scaikey.

If SCAIFLOW_SCAIKEY_WEBHOOK_SECRET is unset, the endpoint returns 503 (fail-closed). Set it before pointing ScaiKey at the URL.

Testing locally#

ScaiKey doesn't reach localhost. For local development, either:

  • Tunnel through ngrok/cloudflared and configure ScaiKey to point at the tunnel URL.
  • Trigger sync manually with POST /v1/admin/sync (uses the API path, not webhooks).
Updated 2026-05-18 16:05:27 View source (.md) rev 3