Scheduling
Run a chatbot flow or automation at a set time with runAt or delaySeconds, then cancel or reschedule it before it starts.
The async trigger endpoints can hold a run until a time you choose: a reminder the day before an appointment, a follow-up 30 minutes after a cart is abandoned. You get an invocationId back immediately and can cancel or move the run until it starts.
Where scheduling works
runAt and delaySeconds work on POST /flows/{projectId}/trigger-async and POST /automations/{projectId}/trigger-async. The sync endpoints and POST /bots/{projectId}/invoke ignore them and run straight away.
For a repeating schedule (every day at 08:00), use a Schedule trigger in the automation instead. See Automation triggers.
Schedule a run
Send one of these in the request body:
| Field | Type | Rule |
|---|---|---|
runAt | ISO 8601 date-time | Must be in the future. Include Z or an offset, for example 2026-10-01T08:00:00+00:00. |
delaySeconds | number | Must be greater than 0. The run starts that many seconds from now. |
curl -X POST "https://www.buni.ai/api/v1/orgs/$ORG_ID/automations/$PROJECT_ID/trigger-async" \
-H "Authorization: Bearer $BUNI_API_TOKEN" \
-H "Content-Type: application/json" \
-H "x-idempotency-key: appt-7781-reminder" \
-d '{
"event": "appointment.reminder",
"payload": { "appointmentId": "7781", "phone": "+233201234567" },
"runAt": "2026-10-01T08:00:00Z"
}'The response is 202 with status SCHEDULED:
{
"invocationId": "clx9km1a40031ab12cd34ef41",
"status": "SCHEDULED",
"scheduledFor": "2026-10-01T08:00:00.000Z"
}Store the invocationId: you need it, and the same token, to change the run.
Error (400) | Cause |
|---|---|
Provide either runAt or delaySeconds, not both | Both fields sent. |
runAt must be a valid ISO date-time | runAt could not be parsed. |
runAt must be in the future | runAt is now or in the past. |
delaySeconds must be a positive number | Zero, negative or not a number. |
What happens at run time
At scheduledFor the run moves to RUNNING and finishes like any async run. Your monthly quota is checked again at that moment. If you passed webhookUrl, the callback follows. See Async runs and callbacks.
A scheduled run is not checked against the Trigger node's trigger key and expected payload when it fires. Check the payload with a "dryRun": true call when you schedule it.
If Enable External API is off when the run comes up, the run fails.
Reschedule a run
Send PATCH with "action": "reschedule" and a new runAt or delaySeconds. The same rules apply as when scheduling.
curl -X PATCH "https://www.buni.ai/api/v1/orgs/$ORG_ID/external-invocations/$INVOCATION_ID" \
-H "Authorization: Bearer $BUNI_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"action": "reschedule", "runAt": "2026-10-02T08:00:00Z"}'The response is the full invocation with the new scheduledFor.
Cancel a run
curl -X PATCH "https://www.buni.ai/api/v1/orgs/$ORG_ID/external-invocations/$INVOCATION_ID" \
-H "Authorization: Bearer $BUNI_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"action": "cancel"}'The run's status becomes CANCELLED and it never runs. The response is the full invocation.
When a change is refused
Cancel and reschedule only work on async runs that have not started. Otherwise the call returns 409:
| Error | Cause |
|---|---|
Only scheduled invocations can be modified | The run is already queued, running or finished, or was cancelled. |
Only async invocations can be modified | The run came from a sync endpoint. |
Invocation is no longer scheduled | The run started while your request was in flight. |
approve and reject actions on the same endpoint are for runs paused at an Approval node. They need a signed-in user with edit access to the project, so an API token cannot use them. See Approvals.