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/
Have feedback?
Help us make this page even more useful.
Tell us what you'd like to see expanded, which examples would help, or what workflow you want covered next. Every message gets read.