Skip to content

Scheduled Outbound Calls

Scheduling an outbound call runs a published workflow at a chosen time, so a Dial node inside it places the call and the workflow handles whatever answers.

Use this when a call should happen later rather than now — a callback requested through a web form, a reminder before an appointment, or a follow-up after an event.

TermDefinition
ScheduleThe timer. Holds when to run, which workflow version to run, and the data to run it with.
IdentifierWhat the schedule does when it fires. Use llm-chain.workflow.execute to run a workflow.
Run atThe moment the call should be placed, read in the schedule’s timezone.
PayloadThe workflow version to run, plus the context the workflow reads as it executes.
ContextFree-form data available to the workflow as ${context....} — typically the person’s number.
ActionDescription
Schedule a callCreate a one-time schedule pointing at a workflow that contains a Dial node.
Check what happenedRead the schedule and its execution history to see whether the call was placed.
Cancel a pending callDelete the schedule before its run time.

The workflow must be published and must contain a Dial node. Note its workflow version ID — the schedule runs a specific version, so a call scheduled today runs the workflow as it exists today, not as it may be edited tomorrow.

The Dial node’s To field should read from context, for example ${context.customer.phone}, so one workflow serves every customer.

Terminal window
curl -X POST "$OCTO_BASE_URL/schedules" \
-H "Authorization: Bearer $OCTO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Callback for Alex Morgan",
"identifier": "llm-chain.workflow.execute",
"scheduleType": "one_time",
"runAt": "2026-08-20T14:30:00Z",
"payload": {
"workflowVersionId": "workflow_version_123",
"context": {
"customer": { "phone": "+14155550123", "name": "Alex Morgan" }
}
}
}'
FieldRequiredNotes
nameyesHuman-readable label.
identifieryesllm-chain.workflow.execute to run a workflow.
scheduleTypeyesone_time for a callback. recurring takes a cron instead of a runAt.
runAtyesWhen to call. See below.
timezoneconditionalRequired when runAt has no UTC offset.
payload.workflowVersionIdyesThe published workflow version to run.
payload.contextnoFree-form data the workflow reads.

runAt takes one of two forms, and they behave differently.

An exact moment2026-08-20T14:30:00Z, or with an offset such as +05:30. Stored as UTC. Any timezone sent alongside it is ignored.

A local wall-clock time2026-08-20T09:00:00 — must be paired with an explicit timezone:

{ "runAt": "2026-08-20T09:00:00", "timezone": "America/New_York" }

This is stored as written, so it stays at 9am local even if that zone’s daylight-saving rules change before the call is due. Prefer it for anything expressed as a local hour.

A local time sent without a timezone is rejected rather than assumed. Guessing wrong there means calling someone in the middle of the night.

Two further rules apply: runAt must be in the future, and no more than two years ahead.

  1. The schedule starts the workflow version named in the payload.
  2. Steps before the Dial node run first, so a CRM lookup or ticket can complete before the phone rings.
  3. The Dial node places the call, and the workflow pauses while it rings.
  4. The workflow resumes on the branch matching the outcome — Connected, Voicemail, No answer, or Failed.

A one-time schedule marks itself complete after it fires, so the same request cannot place a second call.

Terminal window
curl "$OCTO_BASE_URL/schedules/schedule_123" \
-H "Authorization: Bearer $OCTO_API_KEY"

The response carries the schedule’s statusactive while it waits, completed once it has fired — along with its run time and payload. Execution history for the schedule records whether each run succeeded and the error if it did not.

Terminal window
curl -X DELETE "$OCTO_BASE_URL/schedules/schedule_123" \
-H "Authorization: Bearer $OCTO_API_KEY"

Deleting a schedule before its run time disarms the timer, and no call is placed.

SituationResult
runAt is in the past, or more than two years aheadRejected when the schedule is created.
Local runAt with no timezoneRejected — send an offset or an explicit zone.
An identical schedule already existsRejected, so a double submission does not book two calls.
Caller ID is not a number you ownThe call is not placed; the workflow takes the Failed branch.
Nobody answersThe workflow takes the No answer branch. Nothing retries automatically.

Does the schedule call the customer, or does the workflow? The workflow does. The schedule only decides when the workflow runs; the Dial node inside it places the call.

Can one schedule call several people? No. One schedule runs one workflow once. Create a schedule per call, or design a workflow that loops.

What if the workflow is edited after I schedule the call? The schedule runs the workflow version it was given, so later edits do not change a call that is already booked. Point a new schedule at the new version to pick up changes.

Can I schedule a recurring call? scheduleType: "recurring" with a cron expression is supported, but recurring automated calls to the same person are heavily regulated. Prefer one-time schedules for customer contact.