Orchestration · Webhooks
See Webhooks (global) → for signature, retry, idempotency.
Event catalog
| Event | Fires when | data keys |
|---|---|---|
workflow.execution.started | An execution transitions from pending to running | executionId · workflowId · workflowSlug · inputs · externalId |
workflow.execution.completed | Execution succeeded | executionId · workflowSlug · status · output · completedAt |
workflow.execution.failed | Execution failed | executionId · workflowSlug · failedStepId · errorMessage |
workflow.execution.cancelled | Execution was cancelled | executionId · workflowSlug · reason · actorUserId |
workflow.step.completed | A single step succeeded (high-volume; off by default) | executionId · stepId · nodeId · output |
workflow.step.failed | A single step failed (after retries) | executionId · stepId · nodeId · errorMessage · attempts |
workflow.human_approval_pending | Execution reached a human_approval node | executionId · stepId · actionLabel · assignTo · data |
webhook.test | You sent a test from the dashboard | source: "test" |
To observe an approval resolution from outside the workflow, subscribe to workflow.execution.completed / workflow.execution.failed — the run resumes from human_approval and emits the terminal event when it finishes.
Example: workflow.execution.completed
{
"id": "evt_01HXY...",
"event": "workflow.execution.completed",
"occurredAt": "2026-05-24T08:14:22.481Z",
"orgId": "org_01HXY...",
"data": {
"executionId": "exe_01HXY...",
"workflowId": "wkf_01HXY...",
"workflowSlug": "indonesian-kyc-v1",
"workflowVersion": 3,
"status": "succeeded",
"completedAt": "2026-05-24T08:14:22.481Z",
"output": {
"customerId": "cus_01HXY...",
"kycLevel": "premium",
"screeningResult": "no_match"
},
"externalId": "loan-app-2026-05-24-001"
}
}output is by convention the last step's output. Workflow authors can shape this with a final transform node if they want a cleaner public-facing payload.
Example: workflow.human_approval_pending
{
"id": "evt_01HXZ...",
"event": "workflow.human_approval_pending",
"occurredAt": "...",
"orgId": "...",
"data": {
"executionId": "exe_01HXY...",
"workflowSlug": "indonesian-kyc-v1",
"stepId": "stp_01HXY...",
"actionLabel": "AML hit on customer cus_01HXY... — MLRO review required",
"assignTo": "role:mlro",
"data": {
"customerName": "Budi Santoso",
"screeningResults": [...]
}
}
}Use this to fire a Slack notification, send an email to the MLRO, or page someone via PagerDuty.
Register a subscription
/api/webhooks{
"url": "https://your-app.example.com/webhooks/orchestration",
"events": [
"workflow.execution.completed",
"workflow.execution.failed",
"workflow.human_approval_pending"
]
}Response includes the signing secret once:
{
"data": {
"webhook": {
"id": "whk_01HXY...",
"url": "...",
"events": [...],
"secret": "qe_whsec_...",
"active": true
}
}
}Signature verification
Orchestration signs deliveries with HMAC-SHA256 of the raw JSON body using the per-subscription secret. The signature is sent as a hex string in X-QEOR-Signature (the QEOR- prefix is current; a suite-wide migration to X-Quantum-Signature is on the 2026 roadmap — accept both for forward compatibility).
POST /your-webhook HTTP/1.1
Content-Type: application/json
User-Agent: quantum-elixir-orchestration/webhook
X-QEOR-Event: workflow.execution.completed
X-QEOR-Delivery: whd_01HXY...
X-QEOR-Signature: sha256=<hex of HMAC-SHA256(secret, raw_body)>
X-QEOR-Timestamp: 1748151262
X-QEOR-Attempt: 1Verifying on the receiver side
import crypto from 'crypto';
function verify(rawBody: string, signatureHeader: string, secret: string): boolean {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(rawBody, 'utf8')
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signatureHeader),
Buffer.from(expected),
);
}Receivers MUST verify the signature against the raw request body before processing — the signature is computed over the bytes you receive, not the parsed object. Rotate the secret quarterly via POST /api/webhooks/{id}/rotate-secret.
Retry policy
Same as the rest of the suite — up to 5 attempts with exponential backoff:
| Attempt | Delay |
|---|---|
| 1 | immediate |
| 2 | 10s |
| 3 | 30s |
| 4 | 2m |
| 5 | 10m |