Pause a workflow for a person, a time, or another system, then continue.
Decision AI nodes tell you when a case needs a person (requires_human_review, review outcomes, uncertain). Route those cases to an Approval step: the run pauses, a person approves or rejects, and the run continues on that branch, on the same published version. A run can also wait for a Delay or for another system's Callback. Waits last up to 90 days. Add them in the Studio under Logic → Waits & approvals; a workflow can have up to 5.
wait:approval@1 has four branches:
requested runs as soon as the approval opens, with the approval link in nodes.<id>.output.url. Connect any app or chat action to send it (Gmail, Slack, Teams, a ticket comment).approved and rejected run once a person answers. The answers to the step's optional form (up to 5 fields: text, numbers, yes/no, or a choice) are in nodes.<id>.output.fields.expired runs when nobody answers before the step's expiry (7 days unless you set it).{
"nodes": [
{ "id": "input", "ref": "utility:input@1", "config": { "fields": [{ "key": "message", "type": "string", "required": true }] } },
{ "id": "triage", "ref": "builtin:support/triage@1", "inputs": [{ "target": "message", "source": { "ref": "input.message" } }] },
{ "id": "needs_person", "ref": "utility:condition@1",
"config": { "match": "all", "rules": [{ "value": "nodes.triage.output.requires_human_review", "operator": "is_true" }] } },
{ "id": "review", "ref": "wait:approval@1", "config": {
"title": "Review this ticket",
"message": "{{input.message}}\n\nSuggested category: {{nodes.triage.output.category}}",
"fields": [{ "key": "category", "type": "enum", "values": ["billing", "technical", "account", "product", "other"] }],
"expiresIn": { "amount": 2, "unit": "days" },
"notify": { "chat": { "platform": "slack", "connection": "conn_..." } } } },
{ "id": "route", "ref": "action:http@1", "config": { "...": "..." } }
],
"edges": [
{ "from": "input", "to": "triage" }, { "from": "triage", "to": "needs_person" },
{ "from": "needs_person", "to": "route", "branch": "false" },
{ "from": "needs_person", "to": "review", "branch": "true" },
{ "from": "review", "to": "route", "branch": "approved" }
]
}People answer in any of these places; the first answer wins:
curl https://kovca.io/api/v1/runs/run_.../waits/review/decision \
-H "Authorization: ******" \
-H "Content-Type: application/json" \
-d '{ "decision": "approved", "fields": { "category": "billing" } }'It answers 202; the run continues in the background. Approvals that don't allow API decisions answer 403 DECISIONS_NOT_ALLOWED; answered, expired, or ended ones answer 409 WAIT_CLOSED. Over MCP, use list_waits and decide_wait.
wait:delay@1 pauses for a duration (minutes, hours, or days) or until a date and time (ISO 8601, literal or from the run). A time in the past continues at once. Runs continue within about a minute of their time.
wait:callback@1 pauses until another system POSTs a JSON object to a one-time URL. The requested branch has it in nodes.<id>.output.url; hand it to that system (in an HTTP request step, for example). The body (up to 64 KB) continues on the received branch as nodes.<id>.output.body; without a callback before the expiry, the run continues on expired. Later callbacks answer 409 WAIT_CLOSED.
POST https://kovca.io/api/v1/resume/kvb_...
Content-Type: application/json
{ "status": "paid" }A run that reaches a wait answers 202 with "status": "waiting" and a Location header. Kovca doesn't keep outputs, so a run that waited doesn't return its output to the caller: end waiting workflows with the actions that should happen (a reply, a refund, a ticket update). Follow progress with GET /v1/runs/{id}, the Executions page, or the run.waiting, run.succeeded, and run.failed webhook events.
{
"request_id": "req_...",
"run_id": "run_...",
"status": "waiting",
"waits": 1,
"evaluations": 1,
"credits": 6
}Studio tests and Instant Workflow trials never wait: each approval or callback ends at once with the outcome you pick in the test panel (approved, or received, by default), and delays are skipped. Over MCP, test_workflow takes wait_outcomes.
RUN_STATE_TOO_LARGE.| Field | Type | Description |
|---|---|---|
| Waits per workflow | limit | 5 |
| Longest wait | limit | 90 days |
| Runs waiting at once | limit | Free 25; Pay As You Go 10,000 (TOO_MANY_WAITING_RUNS past it) |
| Approval emails | limit | Free 50; Pay As You Go 1,000 per 24 hours |
Waiting is free. A run pays its run fee once, and every step it runs is billed as usual, before and after the wait. See Billing.