API
Start a saved Start-compatible ActionFlow with POST /api/runs, including authentication, request options, validation timing, and queue handoff.
API
Use the public run endpoint to start a saved graph from your backend. The endpoint is POST /api/runs; there is no per-flow /flows/{actionFlowId}/run URL and no API start-node type.
Use a saved graph with a Start node for this path. API and webhook calls run the saved Start-compatible graph; they do not add or change a start node.
Authentication
External integrations should use an API key:
Authorization: Bearer YOUR_API_KEY
Content-Type: application/jsonThe same REST route also accepts an authenticated application session, such as a valid signed-in app session cookie. A session is appropriate for an authenticated application request; an external integration should use an API key rather than a session cookie.
API key scope is enforced separately:
- An organization-scoped key is fixed to its organization. The request cannot switch to another organization, and the
organizationIdin the payload must match that key's organization. - A user-scoped key has no
activeOrganizationId; it must supply an authorizedorganizationIdin the request, and the route checks the caller's organization permission. - An authenticated application session may use its active organization or an authorized requested organization, and the route checks the session user's organization permission.
The gateway assigns run attribution from the authenticated context. The current REST route ignores a client-supplied payload userId when assigning attribution; do not send it to change who owns or is associated with the run.
Request envelope
{
"payload": {
"organizationId": "org_123",
"actionFlowId": "flow_123",
"inputs": {
"node_abc_prompt": "Hello"
},
"trigger": {
"body": {
"event": "order.created"
},
"headers": {
"content-type": "application/json"
},
"method": "POST",
"query": {
"source": "backend"
}
},
"triggerSource": "api"
},
"idempotencyKey": "order-created-ORD-12345",
"delay": "PT5M",
"tags": ["production", "api"],
"maxAttempts": 3,
"maxDuration": 3600
}payload identifies the saved flow. The REST schema validates the top-level options (delay, idempotencyKey, tags, maxAttempts, and maxDuration); it does not deeply schema-validate the nested payload. The route separately checks that payload.actionFlowId and payload.organizationId are present before it continues. payload.inputs contains per-run overrides keyed as {nodeId}_{inputName}. payload.trigger is a separate envelope whose body, headers, query, and method fields are merged onto the saved Start node. The webhook path supplies its own envelope; do not put API run options inside payload.
| Field | Location | Meaning |
|---|---|---|
organizationId | payload | Organization that owns the flow |
actionFlowId | payload | Saved ActionFlow to run |
inputs | payload | Per-run input overrides using {nodeId}_{inputName} keys |
trigger | payload | Optional body, headers, query, and method envelope merged onto Start |
triggerSource | payload | Canonical source metadata; the route does not enforce an enum, and API-key calls default to api |
idempotencyKey | top level | Stable key used by local queue/direct admission, scoped by organization and ActionFlow; request payloads are not compared |
delay | top level | Delay string passed to the execution service; PT5M is the schema example |
tags | top level | Caller tags; the platform adds and protects its organization tag |
maxAttempts | top level | Positive retry-budget override passed to the execution service |
maxDuration | top level | Positive run-duration override in seconds |
The current Flow task definition defaults to one attempt because a retry can repeat non-idempotent side effects. A supplied retry value is forwarded as an execution-service override; it is not a promise of checkpoint-safe replay.
Validation timing and inputs
The REST route first validates the top-level run options, then treats payload as an unvalidated nested value and separately checks that payload.actionFlowId and payload.organizationId are present. Nested inputs, trigger, and triggerSource are not schema-validated at this boundary. A payload object missing either required ID is rejected with 400; do not rely on a stable 400 contract for a missing or null payload because those cases are not handled as a documented malformed-envelope response.
The canonical triggerSource values are manual, api, webhook, schedule, subflow, and unknown. The route does not reject a non-canonical string at this boundary, but only those values are supported by the product contract; an arbitrary value is not a supported trigger or authorization mechanism.
After the ID checks, the gateway loads the saved graph and calls validateFlow before enqueueing. API validation examines the saved graph before payload.inputs and payload.trigger are merged into the execution copy. Consequently, a blank required field in the saved graph can still produce a validation error even when the request contains a run input for it. A request rejected by these pre-enqueue checks does not create a run-history row.
Responses
201 Created: dispatched immediately
When the run is admitted directly, the endpoint returns 201 and a data object containing the runtime run ID and its history ID:
{
"success": true,
"data": {
"id": "run_123",
"runHistoryId": "runhist_123",
"status": "dispatched",
"triggerRunId": "run_123"
}
}201 means the execution service accepted the run; it does not mean the graph completed.
202 Accepted: placed in the queue
When the organization run queue is enabled, the endpoint admits the run to the queue and returns 202 (the queue consumer handles concurrency):
{
"success": true,
"data": {
"position": 2,
"queueId": "queue_123",
"runHistoryId": "runhist_123",
"status": "waiting",
"triggerRunId": null
}
}Save queueId and use it with Get Queued Run, which is the detail endpoint for observing the item after admission. The List Queued Runs endpoint is an active-queue view: it returns only waiting and dispatching items, so it will not show a dispatched, failed, or cancelled item. Poll the detail endpoint until it reports one of those states and retrieve triggerRunId when present from that response; then use the runtime ID with Get Run. 202 is an admission response, not a completion response.
Status behavior
| Status | Current POST /api/runs behavior |
|---|---|
201 | The run was dispatched immediately. |
202 | The run was accepted into the organization queue. |
400 | A top-level run option failed schema validation, a required payload ID was missing, or the saved graph failed validateFlow. |
401 | Authentication is missing or invalid. |
403 | The authenticated caller cannot access the requested organization or flow. |
404 | The flow does not exist in the authorized organization. |
409 | Not emitted by the current REST route. A passive-flow 409 belongs to the webhook path; the API/Actionflow Studio run path has no equivalent active-state check. |
422 | Not emitted by the current REST route. Saved-flow validation is returned as 400; 422 is used by the webhook path. |
429 | The organization/user run-create rate limit was exceeded. |
402 | Organization credits were insufficient during admission. |
500 | The run could not be admitted or an unexpected admission error occurred. |
For a 400 caused by a top-level option, the response can contain issues. A payload object missing either required ID returns the simpler invalid-request response. For graph validation, the response contains validationErrors and validationWarnings; warnings do not block admission. Do not treat any of these responses as a queued run.
Idempotency, delay, and tags
Use a stable idempotencyKey for a logical request and reuse it when retrying after a network timeout. The local admission record is 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. For direct admission, 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. These direct-admission failures return a generic REST 500. A dispatched direct duplicate with a triggerRunId is successful and returns 201. In queue mode, a waiting duplicate can be republished, and a dispatched duplicate with a triggerRunId returns HTTP 202 with status: dispatched. A dispatched duplicate without a triggerRunId and failed or cancelled terminal duplicates are unavailable and return a generic REST 500; none is admitted as a new run. Retain the queue ID and original key while diagnosing a retry.
delay is passed through to the execution service. The gateway validates it as a string; use a supported duration such as PT5M (the documented schema example). Existing execution-service examples also use short forms such as 30s, 5m, 1h, and 2d.
Caller tags are sanitized so an organization ownership tag cannot be replaced. The organization tag is added automatically and omitted from public run metadata.
Example
curl -X POST https://api.actionflows.ai/api/runs \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"payload": {
"organizationId": "org_123",
"actionFlowId": "flow_123",
"inputs": { "node_abc_prompt": "Hello" },
"trigger": {
"body": { "event": "order.created" },
"method": "POST"
}
},
"idempotencyKey": "order-created-ORD-12345",
"tags": ["production", "api"]
}'const response = await fetch("https://api.actionflows.ai/api/runs", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ACTIONFLOWS_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
payload: {
organizationId: "org_123",
actionFlowId: "flow_123",
inputs: { node_abc_prompt: "Hello" },
},
idempotencyKey: "order-created-ORD-12345",
}),
});
const result = await response.json();
if (response.status === 201) {
console.log(result.data.id);
} else if (response.status === 202) {
console.log(result.data.queueId);
} else {
console.error(result.error, result.validationErrors, result.issues);
}Related
- Trigger Run API reference
- Webhook for external HTTP delivery
- Manual
- Scheduled
- Queue API
- TypeScript SDK runs
Scheduled
Configure recurring, weekly, and one-time ActionFlow schedules with IANA timezones, DST-aware wall-clock calculations, missed-run behavior, and independent scheduled runs.
Subflow
The catalog exposes Subflow-shaped nodes, but the current runtime does not dispatch a child flow, create a nested run, or return child output.