Skip to Content
WebhooksWebhooks Overview

Webhooks

Asiri delivers events via signed HTTPS POSTs to URLs you register per workspace. Use them for asynchronous workflows: a DSR transitioning state, a policy being published, or a subscription test from the dashboard.

Event catalog

EventFires when
webhook.testA tenant admin tests a webhook subscription.
dsr.createdA data subject request is opened.
dsr.identity_verifiedRequester identity verification is completed.
dsr.assignedA request receives an owner.
dsr.transitionedA request moves between states.
dsr.extension_grantedA statutory extension is recorded.
dsr.respondedA response package is delivered to the requester.
dsr.completedThe request is closed.
dsr.sla_warningA response deadline is approaching.
dsr.sla_breachedA response deadline has passed.
policy.createdA policy record is created.
policy.version_draftedA new policy version is drafted.
policy.submitted_for_reviewA policy is submitted for review.
policy.publishedA policy version is published.
policy.retiredA policy is retired.
policy.assignedA policy acknowledgement task is assigned.
policy.acknowledgedA user acknowledges a policy.

Every current event is delivered as event_version: 1.

Use the dedicated references when implementing a production subscriber:

  • Webhook versioning explains event_version, pinnedVersions, deprecation headers, and migration rules.
  • Events v1 lists the current v1 event data shapes.

Signature verification

Every delivery includes an X-Asiri-Signature header:

X-Asiri-Signature: t=1715260800,v1=4f3c...

t is the Unix timestamp; v1 is HMAC_SHA256(secret, "v1.{t}.{rawBody}") in lowercase hex. You must verify the signature before trusting the body.

Pseudocode:

import { createHmac, timingSafeEqual } from 'node:crypto'; function verify(rawBody: string, header: string, secret: string): boolean { const parts = Object.fromEntries(header.split(',').map((p) => p.split('='))); const expected = createHmac('sha256', secret).update(`v1.${parts.t}.${rawBody}`).digest('hex'); return timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1)); }

Reject deliveries where the timestamp is older than 5 minutes — that’s the replay window.

Retry policy

Asiri retries non-2xx responses with exponential backoff:

AttemptDelay
10
230s
32min
410min
51h
66h
724h

After the seventh failure we mark the endpoint disabled and email the workspace owner. Your endpoint must respond within 10 seconds — slower responses count as failures.

Versioning

Webhook payloads are versioned independently of the REST API. Every request includes:

  • X-Asiri-Event: event type, for example dsr.created.
  • X-Asiri-Event-Id: unique delivery event id.
  • X-Asiri-Event-Version: integer event schema version.

The body uses the same versioned envelope:

{ "event_version": 1, "event_type": "dsr.created", "event_id": "evt_01HXYZ", "occurred_at": "2026-05-15T12:00:00.000Z", "tenant_id": "00000000-0000-4000-8000-000000000001", "data": { "requestId": "00000000-0000-4000-8000-000000000002", "ref": "DSR-2026-0001" } }

Breaking schema changes ship as a new integer version for the affected event type. Subscriptions receive the latest version unless they pin a tested version in the dashboard. Deprecated pinned versions surface warnings in admin before their retirement date.