Signature verification
Every delivery sends X-EmbodiFlow-Timestamp (Unix seconds) and X-EmbodiFlow-Signature (v1=<hex>, comma-separated during secret rotation). The signed string is timestamp + '.' + raw body bytes. Do not re-serialize JSON. Compute HMAC-SHA256 with the signing secret and compare in constant time. Reject timestamps more than 300 seconds from your clock. Worked example: secret whsec_example_do_not_use, timestamp 1710000000, body {"id":"c0a1e014-8000-5111-8148-000000000001","type":"workflow.run.succeeded","specVersion":1,"occurredAt":"2024-03-09T16:00:00.000Z","tenantId":"01900000-0000-7000-8000-000000000001","projectId":"01900000-0000-7000-8000-000000000002","resource":{"type":"workflow_run","id":"01900000-0000-7000-8000-000000000003"},"data":{"workflowRunId":"01900000-0000-7000-8000-000000000003","status":"succeeded"}}, HMAC-SHA256 hex db77a488491b575ab3bc5d1f305065bba69ec10e9ac893a10c8c94adc58a14bf. Full recipe: docs/contracts/outbound-events.md.