Webhook
Receive external POST deliveries at a saved ActionFlow webhook URL, with token, idempotency, validation, passive-flow, and response-status behavior.
Webhook
An inbound flow webhook is a setting on a saved ActionFlow. It is not a fourth start-node type: use a graph with a Start node, create the webhook from the flow's Webhooks settings, and send requests to the generated URL.
There is at most one active inbound flow webhook per flow. Organization outgoing event webhooks are a different feature; see Webhooks overview.
URL shape
The URL is built from NEXT_PUBLIC_WEB_URL:
${NEXT_PUBLIC_WEB_URL}/app/webhooks/actionflows/{actionFlowId}/{token}For the production web host, the shape is:
https://actionflows.ai/app/webhooks/actionflows/{actionFlowId}/{token}The Endpoint card in the flow settings is the source of truth. The path contains the ActionFlow ID and the secret webhook token; it is not app.actionflows.ai/webhooks/....
POST-only request
The flow webhook route exports POST only. Other HTTP methods are not accepted by this endpoint and are rejected by the route framework. The token in the path is the request credential; a REST API key is not required for this URL.
Send a JSON object when you want its fields to become run inputs:
curl -X POST https://actionflows.ai/app/webhooks/actionflows/{actionFlowId}/{token} \
-H "Content-Type: application/json" \
-H "X-Idempotency-Key: order-created-ORD-12345" \
-d '{
"node_start_orderId": "ORD-12345",
"event": "order.created",
"data": {
"total": 299.99
}
}'Use {nodeId}_{inputName} when a field must override a node input. The complete request is also merged into the trigger envelope as body, headers, query, and method. Only a small allowlist of request headers is copied into the envelope, including content-type, user-agent, x-request-id, and x-idempotency-key.
The webhook run has triggerSource: "webhook". The platform adds source:webhook, flow:{actionFlowId}, and organization:{organizationId} tags; callers cannot replace those tags through the webhook body.
Idempotency
X-Idempotency-Key is read case-insensitively from the request and passed to the execution service with the webhook run. Use a stable key for a logical delivery and reuse it when retrying the same event. The key is separate from the path token and does not authenticate a different webhook.
X-Idempotency-Key is applied to the local admission record, scoped by organization and actionFlowId; the lookup does not compare request payloads. Reusing a key for a different payload in the same organization and ActionFlow refers to the existing admission. In direct admission mode, success requires the execution response to contain a runtime id; an otherwise successful response without that id is an admission failure and may leave a history row in executing. A direct duplicate whose existing admission is executing/not dispatched and lacks triggerRunId also fails. A dispatched direct duplicate with a triggerRunId is successful; this webhook returns HTTP 202 with runtime status: dispatched for that existing admission. In queue mode, a waiting duplicate can be republished, and a dispatched duplicate with a triggerRunId also returns HTTP 202 with runtime status: dispatched. A dispatched duplicate without a triggerRunId and failed or cancelled terminal duplicates are unavailable and return a generic 500; none is admitted as a new run. Retain the returned runId or queueId and the original key when investigating duplicates.
Response statuses
| Status | Meaning |
|---|---|
202 | The request was accepted and the run was dispatched or placed in the queue. |
403 | The webhook record exists but is disabled. |
404 | The token or ActionFlow path does not identify a live webhook, or the flow was deleted. |
409 | The flow is passive (active: false); the request is rejected before enqueue. |
422 | validateFlow rejected the saved graph before the webhook input envelope was merged. No run is enqueued. |
429 | The organization and client-IP rate limit was exceeded. |
401 | The handler's timing-safe token guard can reject a mismatched token; clients should treat it like invalid credentials. |
405 | A non-POST method was sent to this POST-only route. |
500 | The webhook was found but the run could not be admitted or an unexpected handler error occurred. |
The handler checks the webhook record, enabled flag, active state, and rate limit before it validates or enqueues the run. A passive flow therefore returns 409 rather than a run ID. Enable the webhook and activate the flow when external delivery should be accepted.
202 response shapes
An immediately dispatched run can return:
{
"success": true,
"runId": "run_123",
"status": "dispatched"
}A queued run can return:
{
"success": true,
"queueId": "queue_123",
"status": "waiting"
}runId and queueId are optional fields, but the successful response contains at least one of them. A 202 means accepted, not completed. The List Queued Runs endpoint is only an active waiting/dispatching view and may stop showing the item after dispatch. Use queueId with Get Queued Run to observe dispatched, failed, or cancelled and retrieve triggerRunId when present; then follow the runtime ID through Run history or Get Run.
Validation timing
The webhook parses and records the delivery, then calls validateFlow on the saved graph before calling the enqueue function. The JSON body and request-envelope fields are not merged into the graph for this pre-enqueue validation. If the saved graph has a missing required input, fix and save the graph rather than expecting a webhook body value to bypass the 422 check. The 422 body uses errors and warnings (the REST endpoint uses validationErrors and validationWarnings for its 400 response).
A request that returns 202 may still fail later during execution. Inspect run history and the queue when the graph, credentials, credits, or a node is the source of the failure.
Security and operations
- Treat the path token as a secret. Regenerate it from the webhook settings if it may have leaked; the old URL then stops resolving to the webhook.
- Deleted webhooks and deleted flows stop accepting deliveries.
- Disabled webhooks return
403; passive flows return409. - The handler rate-limits by organization and client IP. Do not retry a
429in a tight loop. - Do not put credentials or sensitive payloads into logs, tickets, or public examples.
Webhook versus API
| Flow webhook | REST POST /api/runs | |
|---|---|---|
| Caller | An external system using the generated URL | A backend or authenticated application request; external integrations should use an API key |
| Credential | Secret token in the path | API key for external integrations; the REST route also accepts an authenticated application session |
| Saved graph | Start-compatible graph with a configured webhook | Start-compatible graph selected by IDs |
| Source | webhook | api for an API key or manual for an authenticated session by default |
| Validation failure | 422 before enqueue | 400 before enqueue |
Related
- Webhooks overview for inbound flow webhooks and organization outgoing events
- API
- Trigger overview
- Settings for active versus passive state
- Flow troubleshooting
Artifacts
Distinguish generated ActionFlow artifacts, uploaded run files, Knowledge Base files, and Agent artifacts, including the limits the current runtime enforces.
HITL Approvals
Pause an ActionFlow at a human-in-the-loop node, deliver a review request, record a decision, and continue from the saved partial execution.