Projects and stages
Webhook reference: events, headers, signature and retries
The contract of every delivery: catalog events, x-aproova-* headers, how to verify the signature and when Aproova retries.
Reviewed on November 14, 2026
Before you start
- A webhook created in Settings > Integrations (workspace owner, Flow or higher) and the signing secret shown in the form.
Quick task overview
Start with these steps. If the task has more steps, they appear right below.
- Step 1
Catalog events: review.created (review created), review.version_uploaded (version uploaded), review.comment_created (comment created), review.verdict_submitted (decision submitted by a participant), review.approved (review approved), review.rejected (review rejected), review.changes_requested (changes requested), review.approval_reopened (approval reopened), review.unlocked (review unlocked). Approval reopened and review unlocked are only delivered to webhooks that tick them; existing subscriptions keep receiving only the events they already had. The Test button sends the webhook.test event.
- Step 2
Headers on every delivery: x-aproova-event (event type), x-aproova-delivery (delivery identifier), x-aproova-timestamp (seconds since 1970, UTC) and x-aproova-signature. User-agent Aproova-Webhooks/1.0 is also sent and, when there is a body, content-type application/json. Custom headers cannot replace these names.
- Step 3
Signature: x-aproova-signature = "sha256=" followed by the hexadecimal HMAC-SHA256, computed with the webhook secret over the text "${timestamp}.${body}", where timestamp is the x-aproova-timestamp value and body is the raw body received (empty text for GET, DELETE or no body). Compute it over the received bytes, before parsing the JSON, and compare in constant time.
Step by step
- 4Default body (JSON): event, event_id, created_at, workspace_id, locale and, when present, proof, version, folder, teams, comment, decision, actor and whatsapp. Empty fields are omitted. The text of internal comments is never sent.
- 5Optional decision fields: verification_method (authenticated_user, email_otp or declared) and actor_verified (true only for authenticated_user and email_otp). They only appear on decision events that carry this evidence; in a custom body, use {{data.verification_method}} and {{data.actor_verified}}.
- 6Response: any 2xx status counts as delivered. Aproova waits up to 6 seconds for a response.
- 7Retries: network errors, timeouts and statuses 408, 425, 429 and 5xx are retried; other 4xx statuses are not. There are at most 4 attempts: the 2nd about 5 minutes after the 1st failure, the 3rd about 30 minutes later and the 4th about 120 minutes later. After the 4th, the delivery is marked as a final failure in the history.
- 8Every retry repeats the same event_id and x-aproova-delivery. Use that value to ignore repeats at the destination. If the workspace no longer has a plan with webhooks, the delivery fails without a retry.
What should happen
Your service checks the signature, recognizes repeats by event_id and answers 2xx quickly.
Common problems
- Signature mismatch: compute it over the raw body without reformatting the JSON, and use the current secret (generating a new secret invalidates the previous one).
- Duplicate action at the destination: a slow response or a 5xx triggers a retry. Store the event_id you already processed.
- A new event does not arrive: tick approval reopened or review unlocked in the webhook's event list.