Runs
Start a workflow run with inputs, follow it to what it hands back, list past runs, stop a run in progress, and delete one.
A run is one pass through a workflow. Runs start in the background: you start one, get its ID straight away, and check it until it finishes. The API calls a run an execution, so its ID is execution_id and its endpoints are under /executions.
The run object
{
"execution_id": "683b2c3d4e5f6a7b8c9d0e1f",
"workflow_id": "665f1a2b3c4d5e6f7a8b9c0d",
"version_id": "6683e1c4b2a9d7f0e5c3a1b2",
"workflow_name": "Monthly Client Report",
"status": "success",
"is_trigger_listener": false,
"created_at": "2026-01-15T10:30:00+00:00",
"started_at": "2026-01-15T10:30:00+00:00",
"completed_at": "2026-01-15T10:30:03+00:00",
"error": null,
"outputs": { "total": 1250, "summary": "A good month for Acme" }
}Fields
execution_idstring- The run's ID.
workflow_idstring- The ID of the workflow that ran. It stays the same when the workflow is edited.
version_idstring- The ID of the version of the workflow that ran. It changes as the workflow is edited.
workflow_namestring- The workflow's name.
statusstring- Where the run is, such as running or success. See Run statuses.
is_trigger_listenerboolean- Whether this run is a workflow that starts from its own trigger or schedule, listening. See Workflows that start themselves.
created_atstring- When the run was started, in ISO 8601.
started_atstring or null- When it began working, in ISO 8601, or null while it's queued.
completed_atstring or null- When it finished, in ISO 8601, or null while it's still going.
errorstring or null- Why the run failed, or null.
outputsobject- What the run handed back: one field per output its workflow declares, by name. Empty until the run succeeds, and for a workflow that declares none. An output the run never set is null. Only Get a run includes it.
Run statuses
| Status | Meaning | Final? |
|---|---|---|
queued | Started, and about to begin | No |
running | Working through its steps | No |
waiting | Set aside by a Wait step until its time comes | No |
awaiting_approval | Waiting for someone to answer an approval request | No |
success | Reached the end of the workflow | Yes |
failed | A step failed and the run stopped there | Yes |
stopped | Stopped before it finished | Yes |
When you check a run until it's done, wait for one of the final statuses rather than for it to leave running: a run waiting on a Wait step or an approval isn't finished.
Workflows that start themselves
A workflow that starts from its own trigger or schedule doesn't run to an end. Running it turns it on, and the run you get back is the workflow listening: is_trigger_listener is set, and its status reads running for as long as it listens. Each event or scheduled time it catches starts a run of its own, which List runs lists beside it. Don't wait for the listening run to finish; follow the runs it starts instead, and stop it to turn the workflow off.
Run a workflow
Starts a run in the background and returns its ID straight away; follow it with Get a run. Requires the workflows:execute scope, and the member who created the key must be allowed to run workflows (the Run workflows role permission).
:id can be the workflow's id or the ID of any of its versions. Either way, the workflow runs as it is now. Each run counts as one of your plan's monthly runs; when they're used up, the request gets 429 with the code monthly_run_limit (see Error codes).
Headers
Idempotency-Keystring- Makes the request safe to retry: a repeat with the same key returns the first response instead of starting another run. Up to 256 characters, remembered for 24 hours. Reusing a key for a different request, such as the same workflow with different inputs, returns 422. See Idempotency.
Body
Optional. To give values for the inputs the workflow declares, send a JSON object of at most 500 KB whose only field is inputs.
inputsobject- Values for the workflow's inputs, by input name.
{
"inputs": {
"client": "Acme",
"month": "2026-05"
}
}How inputs are checked
Each value is converted to its input's declared type:
| Type | Accepts |
|---|---|
text | Any value, as text |
number | A number, or text that reads as one, such as "21" |
boolean | true or false, or text such as "yes", "no", "on" or "0" |
list | A list, or comma-separated text |
object | An object |
An optional input you leave out takes its default, so a workflow with no required inputs can be run without a body. The run is refused with 400 and the code invalid_inputs, before anything starts and with a message naming the input, when:
- a value names an input the workflow doesn't declare
- a required input has no value
- a value can't be converted to its input's type
- a value can't be stored with a run, such as
NaN,Infinity, a whole number wider than 64 bits, or lists and objects nested more than 32 levels deep
A workflow that starts from its own trigger or schedule takes no inputs, and sending it some returns 400. Running it without a body turns it on, so it starts listening for its trigger or schedule: the response has is_trigger_listener set. See Workflows that start themselves.
Example
curl -X POST https://api-prod.lodol.com/api/v1/workflows/665f1a2b3c4d5e6f7a8b9c0d/run-async \
-H "Authorization: Bearer $LODOL_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: acme-report-2026-05" \
-d '{"inputs": {"client": "Acme", "month": "2026-05"}}'Response 202:
{
"execution_id": "683b2c3d4e5f6a7b8c9d0e1f",
"workflow_id": "665f1a2b3c4d5e6f7a8b9c0d",
"version_id": "6683e1c4b2a9d7f0e5c3a1b2",
"status": "queued",
"is_trigger_listener": false,
"created_at": "2026-01-15T10:30:00+00:00"
}Response 400:
{
"error": "'Monthly Client Report' needs a value for its 'client' input.",
"error_code": "invalid_inputs"
}List runs
Returns the workspace's runs, newest first, a page at a time. Requires the executions:read scope. The runs a workflow that starts itself starts are listed too, beside its listening run. A workflow another run called isn't listed on its own: it's part of the run that called it.
Query parameters
workflow_idstring- Only runs of this workflow, from every version of it. Takes the workflow's id or the ID of any of its versions.
limitinteger- How many runs to return, from 1 to 100.Default:
20. afterstring- For the next page: the execution_id of the last run on the page you have.
curl "https://api-prod.lodol.com/api/v1/executions?workflow_id=665f1a2b3c4d5e6f7a8b9c0d&limit=10" \
-H "Authorization: Bearer $LODOL_API_KEY"Response 200: a page of run objects, each with its error but without outputs: Get a run has them.
{
"executions": [
{
"execution_id": "683b2c3d4e5f6a7b8c9d0e1f",
"workflow_id": "665f1a2b3c4d5e6f7a8b9c0d",
"version_id": "6683e1c4b2a9d7f0e5c3a1b2",
"workflow_name": "Monthly Client Report",
"status": "failed",
"is_trigger_listener": false,
"created_at": "2026-01-15T10:30:00+00:00",
"started_at": "2026-01-15T10:30:00+00:00",
"completed_at": "2026-01-15T10:30:03+00:00",
"error": "The sales sheet is empty this month."
}
]
}Get a run
Returns one run, with the outputs it handed back once it has succeeded. Requires the executions:read scope. Step results are left out unless you ask for them, which keeps checking a run's status quick; they're for looking into how a run reached its result, while outputs is the result.
Query parameters
include_step_resultsstring- Set to true to include what each step produced, in steps.
# Check the status
curl https://api-prod.lodol.com/api/v1/executions/683b2c3d4e5f6a7b8c9d0e1f \
-H "Authorization: Bearer $LODOL_API_KEY"
# Get every step's result once it has finished
curl "https://api-prod.lodol.com/api/v1/executions/683b2c3d4e5f6a7b8c9d0e1f?include_step_results=true" \
-H "Authorization: Bearer $LODOL_API_KEY"Response 200:
{
"execution_id": "683b2c3d4e5f6a7b8c9d0e1f",
"workflow_id": "665f1a2b3c4d5e6f7a8b9c0d",
"version_id": "6683e1c4b2a9d7f0e5c3a1b2",
"workflow_name": "Monthly Client Report",
"status": "running",
"is_trigger_listener": false,
"created_at": "2026-01-15T10:30:00+00:00",
"started_at": "2026-01-15T10:30:00+00:00",
"completed_at": null,
"error": null,
"outputs": {}
}Response 200, with include_step_results=true:
{
"execution_id": "683b2c3d4e5f6a7b8c9d0e1f",
"workflow_id": "665f1a2b3c4d5e6f7a8b9c0d",
"version_id": "6683e1c4b2a9d7f0e5c3a1b2",
"workflow_name": "Monthly Client Report",
"status": "success",
"is_trigger_listener": false,
"created_at": "2026-01-15T10:30:00+00:00",
"started_at": "2026-01-15T10:30:00+00:00",
"completed_at": "2026-01-15T10:30:03+00:00",
"error": null,
"outputs": { "total": 1250, "summary": "A good month for Acme" },
"steps": [
{ "index": 0, "status": "completed", "result": "..." },
{ "index": 1, "status": "completed", "result": "..." }
]
}Stop a run
Stops a run in progress. Steps that already finished aren't undone. Requires the workflows:execute scope, and the member who created the key must be allowed to run workflows. A run that has already finished (success, failed or stopped) returns 400 with the code run_already_finished: there's nothing to stop, and Get a run says how it ended.
Headers
Idempotency-Keystring- Makes the request safe to retry, as for Run a workflow.
curl -X POST https://api-prod.lodol.com/api/v1/executions/683b2c3d4e5f6a7b8c9d0e1f/stop \
-H "Authorization: Bearer $LODOL_API_KEY"Response 200:
{
"execution_id": "683b2c3d4e5f6a7b8c9d0e1f",
"status": "stopped"
}Delete a run
Deletes a finished run from the history, as deleting it in Lodol does. This can't be undone. Requires the executions:delete scope, and the member who created the key must be allowed to delete runs (the Delete runs role permission); otherwise the request gets 403.
The runs it started go with it: the runs its trigger started, and the runs of the workflows it called. A run that hasn't finished, or that started one that hasn't, returns 409 with the code run_not_finished; stop it first, or wait for it to finish. Deleting a run doesn't give back the run it counted as.
curl -X DELETE https://api-prod.lodol.com/api/v1/executions/683b2c3d4e5f6a7b8c9d0e1f \
-H "Authorization: Bearer $LODOL_API_KEY"Response 200:
{
"execution_id": "683b2c3d4e5f6a7b8c9d0e1f",
"deleted": true,
"started_runs_deleted": 0
}started_runs_deleted says how many of the runs it started went with it. A run that doesn't exist, or is already deleted, returns 404. Deleting takes no Idempotency-Key, so if a delete times out, check with Get a run before sending it again: it may have gone through.