Risk event webhooks
Receive versioned archdev.risk_event deliveries, verify the Stripe-style signature, and deduplicate on the webhook id.
Public contract, versioned.
archdev.risk_eventv1 is defined byservices/go/archdev/configs/json_schemas/archdev-risk-webhook-v1.yaml. Breaking changes ship as a newversionwith a new schema file; v1 payloads keep validating. Signed examples (one per event family) live inservices/go/archdev/fixtures/risk-webhook/.
Body
Each delivery is a JSON object with this shape:
| Field | Meaning |
|---|---|
id |
Message id. Equals the ArchDev-Webhook-Id header; the idempotency key. |
type |
Always archdev.risk_event. |
version |
Always 1 for v1. |
created_at |
RFC 3339 UTC creation time. |
org.id |
Owning organization. |
event |
plan.created, plan.started, plan.updated, task.created, task.started, task.updated, task.closed, pr.created, pr.updated, pr.merged (deprecated alias for pr.closed), or pr.closed. New merges are delivered as pr.closed. |
subject |
Discriminated subject matching the event family: plan carries path; task carries id; pull_request carries number, title, and url. |
repository |
full_name as owner/repo. |
risk |
combined (low, medium, high, critical), uncertainty and consequence (low, medium, high). |
summary |
Message content: human-readable risk summary. |
room |
thread_id, message_id (equals the body id), and url. |
Headers
| Header | Value |
|---|---|
ArchDev-Webhook-Id |
The body id. Deduplicate on it. |
ArchDev-Signature |
t=<unix seconds>,v1=<hex>[,v1=<hex>...], where each v1 entry is hex(hmac_sha256(secret, t + "." + raw_body)) over the exact raw request bytes. Multiple v1 entries are allowed during secret rotation; a signature is valid when any listed entry matches any accepted secret. |
Verification
- Read the raw request body bytes. Never re-serialize parsed JSON: key order and whitespace change the HMAC.
- Split the header on
,. Require an integertand at least onev1hex entry. - Reject when
|now - t|exceeds 300 seconds (5-minute tolerance in both directions). - Compute the expected HMAC per accepted secret and compare with a constant-time compare (
crypto/subtle.ConstantTimeComparein the Go reference atservices/go/archdev/internal/riskwebhook/riskwebhook.go). - Treat redelivery as normal: delivery is at-least-once, up to 3 attempts with backoff within one delivery run, with the same id and same bytes on every attempt. Store handled webhook ids and ignore repeats.
Sender
The platform Script archdev-risk-webhook is the only sender. The Go
package at services/go/archdev/internal/riskwebhook/ is the reference
signer/verifier and contract test oracle; it never sends deliveries.
Reference
cd services/go/archdev && go test -count=1 ./internal/riskwebhook/
Need something clearer?
Tell us where this page still falls short.
If a step is confusing, a diagram is misleading, or a workflow needs a better example, send feedback directly and we will tighten it.