Webhook HMAC
Security reference for ArcFlow external callback verification. External integrators POST outcomes to POST /v1/runs/{run_id}/external/{binding_id}. Treat this endpoint like a payment webhook: authenticate with Bearer API key, verify HMAC on raw body bytes, reject invalid signatures before parsing sensitive fields.
Tutorial: Webhook security guide.
Implementation: runtime/arcflow-core/src/external/webhook.rs, server/arcflow-server/src/handlers/external.rs.
Required configuration
| Variable | Purpose |
|---|---|
ARCFLOW_SERVER_API_KEY | Bearer auth on POST |
ARCFLOW_WEBHOOK_SECRET | HMAC shared secret |
ARCFLOW_POSTGRESQL_URL | Persist run state for external bindings |
Without webhook secret, handler returns error requiring configuration.
Required headers
| Header | Purpose |
|---|---|
Authorization: Bearer <ARCFLOW_SERVER_API_KEY> | Runtime authentication |
X-ArcFlow-Signature | HMAC-SHA256 over exact request body bytes |
Content-Type: application/json | Outcome JSON |
X-Idempotency-Key | Optional duplicate suppression |
HMAC computation (ArcFlow signing outbound or integrator signing inbound)
- Read the exact POST body as bytes (no re-serialization after parse).
- Compute HMAC-SHA256 with
ARCFLOW_WEBHOOK_SECRET. - Encode digest as lowercase hex.
- Send header
X-ArcFlow-Signature: sha256=<hex>.
Header may also be raw hex without sha256= prefix; server accepts both.
Python (integrator)
TypeScript (integrator)
Verifying at a gateway (optional)
Constant-time comparison
Server uses constant-time digest comparison (subtle::ConstantTimeEq in Rust) to reduce timing side channels. Integrators should use hmac.compare_digest (Python) or timingSafeEqual (Node).
Reject signatures whose hex length does not match expected digest length before comparison.
Secret rotation with dual-verify window
ArcFlow R1 supports a single active ARCFLOW_WEBHOOK_SECRET. For rotation without dropping callbacks:
- Deploy server accepting both old and new secret (requires custom gateway dual-verify or brief maintenance window).
- Update integrators to sign with new secret.
- Switch server env to new secret only.
- Revoke old secret.
Standard single-secret rotation: pause integrator traffic, update secret, resume (expect brief 401/422 on mismatched signatures).
Failure behavior
| Condition | HTTP | Notes |
|---|---|---|
| Missing Bearer | 401 | |
| Bad HMAC | 401 or 422 | Signature rejected before outcome persist |
| Missing webhook secret on server | 503 | Configuration error |
| Duplicate idempotency key | 200 with already_processed | Safe retry |
Failed verification must not parse or persist outcome fields.
curl example
Network reachability
Integrators must reach arcflow-server over HTTPS in production. Do not embed webhook secrets in browser JavaScript; post from backend workers.