Worked examples
Real requests and payloads for webhooks out, request maps and the failure handler, ready to copy.
Every example on this page is what the API takes and sends, with made-up ids. Addresses such as company.example.invalid stand for your own.
Webhooks out
Tell your own system when a workflow finishes or fails. Add a webhook to the workflow with the address to call and the events it wants. In the API a workflow is a pipeline:
POST /api/v0/pipelines/pipeline_webhook_fixture/webhooks
Idempotency-Key: example-request-343
{
"name": "Notify our system",
"destination": "webhook",
"address": "https://company.example.invalid/pipeline-events",
"events": [
"falcon.pipeline.run.finished"
],
"include_results": false,
"enabled": false
}
When a run finishes, the address receives one signed request. The body is a CloudEvent; data says how the run went, step by step:
POST https://company.example.invalid/pipeline-events
content-type: application/cloudevents+json
webhook-id: evt_2b7d9e41
webhook-timestamp: 1790690539
webhook-signature: v1,tADkis+ZUs0deVI6jKf4G56u24Ohuz1R4mQQe3Yv6ag=
{
"specversion": "1.0",
"type": "falcon.pipeline.run.finished",
"source": "/pipelines/pipeline_new_leads",
"id": "evt_2b7d9e41",
"subject": "run_4f9c2a",
"time": "2026-09-29T14:02:19.000Z",
"datacontenttype": "application/json",
"data": {
"run_id": "run_4f9c2a",
"request_id": "req_7Hc2xQ",
"pipeline": "pipeline_new_leads",
"pipeline_version": "version_12",
"outcome": "finished",
"started_at": "2026-09-29T14:02:11.000Z",
"finished_at": "2026-09-29T14:02:19.000Z",
"steps": [
{
"step": "qualify",
"outcome": "finished"
},
{
"step": "reply",
"outcome": "finished"
}
]
}
}
Check the signature
Webhooks are signed the Standard Webhooks way. Join webhook-id, webhook-timestamp and the exact body with dots, sign that with HMAC-SHA256 using the webhook's secret (the part after whsec_, base64-decoded), and compare it with the value after v1, in webhook-signature. Refuse a timestamp more than five minutes old.
import { createHmac, timingSafeEqual } from "node:crypto";
function verify(secret, headers, body) {
const key = Buffer.from(secret.replace(/^whsec_/, ""), "base64");
const signed = `${headers["webhook-id"]}.${headers["webhook-timestamp"]}.${body}`;
const expected = createHmac("sha256", key).update(signed).digest();
return headers["webhook-signature"]
.split(" ")
.map((part) => Buffer.from(part.replace(/^v1,/, ""), "base64"))
.some((given) => given.length === expected.length && timingSafeEqual(given, expected));
}
A failed run sends falcon.pipeline.run.failed, and its data adds the step that failed and why. Answer with any 2xx status within ten seconds. Anything else is tried again after 30 seconds, 2 minutes, 10 minutes, then 15 minutes twice, and then marked as not delivered in the webhook's delivery log.
Request maps
Give another system its own address to call, and say which parts of what it sends go into the workflow. This map checks a shared key and takes the order number from the body:
POST /api/v0/connections/incoming-requests
Idempotency-Key: incoming-map-example
{
"name": "order_paid",
"target": {
"kind": "workflow",
"workflow_id": "workflow_fixture"
},
"acting_as": {
"kind": "agent_rule"
},
"topic": null,
"keywords": [],
"person_fields": [],
"verification": {
"kind": "shared_key",
"header": "X-Falcon-Key",
"key": "fixture-key"
},
"conditions": [],
"mapping": [
{
"path": "order.id",
"input": "order_id",
"transform": "text"
}
],
"idempotency_field": "id"
}
The answer gives the map its address. The key itself is never shown again: secret_set only says there is one.
{
"id": "request_map_fixture",
"name": "order_paid",
"target": {
"kind": "workflow",
"workflow_id": "workflow_fixture"
},
"acting_as": {
"kind": "agent_rule"
},
"topic": null,
"keywords": [],
"person_fields": [],
"verification": {
"kind": "shared_key",
"header": "X-Falcon-Key",
"secret_set": true
},
"conditions": [],
"mapping": [
{
"path": "order.id",
"input": "order_id",
"transform": "text"
}
],
"idempotency_field": "id",
"address": "/v1/request-maps/example",
"last_request": null,
"recent_results": [],
"revision": "1",
"needs_look": false,
"target_version": null
}
Your system then calls the address with the key in the header the map names. When the workflow is still running, the answer says it was accepted:
POST /v1/request-maps/example
X-Falcon-Key: ••••••••
{
"id": "event_fixture"
}
HTTP/1.1 202
{
"request_id": "request_fixture",
"state": "accepted",
"run_id": "run_fixture",
"received_at": "2026-09-28T00:00:00.000Z"
}
When the workflow answers quickly, the answer carries its result and where to read the run:
HTTP/1.1 200
{
"request_id": "request_fixture",
"state": "finished",
"run_id": "run_fixture",
"status_url": "/api/v0/pipelines/pipeline_fixture/runs/run_fixture",
"result": {
"text": "Your order is on its way."
},
"received_at": "2026-09-28T00:00:00.000Z"
}
The failure handler
Choose one workflow to run whenever another workflow fails or stops for someone to look at it. Give it the start A workflow failed or needs attention, and narrow it by workflow, category or outcome if you like. Then make it the company's handler in Company settings › General › When a workflow fails, or with the API:
PUT /api/v0/settings/workflow-failures
If-Match: "1"
{
"handler_pipeline_id": "pipeline_tell_the_team"
}
A workflow can name its own handler instead, in its settings. The handler starts with exactly what a webhook out sends for the failed run:
{
"run_id": "run_8d31e0",
"request_id": "req_Qm41Lp",
"pipeline": "pipeline_new_leads",
"pipeline_version": "version_12",
"outcome": "not_done",
"started_at": "2026-09-29T15:40:02.000Z",
"finished_at": "2026-09-29T15:40:31.000Z",
"steps": [
{
"step": "qualify",
"outcome": "finished"
},
{
"step": "reply",
"outcome": "failed"
}
],
"failed_step": "reply",
"error": {
"code": "dispatch_failed"
}
}
outcomeisnot_done.failed_stepis left out when the run stopped as a whole rather than at one step.- A handler's own runs never start a handler, and test runs never do.
- The handler starts once for each failed run.