Schedules
Start runs of a workflow on a recurrence — daily, weekly, monthly or yearly at a set time — with a run-name pattern, preset field values and per-task assignees; pause, resume, run now and preview.
Operations: Schedules endpoints · scope schedules:manage.
A schedule starts runs of one published workflow on a recurrence. Each run gets a name from the schedule's pattern, the schedule's preset field values, and — for the tasks you name — the schedule's assignees instead of the task's own assignment rule. Schedules are managed in the app (Automations → Schedules, Builders and Admins) and through the API with the schedules:manage scope.
| Operation | HTTP | SDK method |
|---|---|---|
| List schedules | GET /v1 |
list |
| Create a schedule | POST /v1 |
create |
| Get a schedule | GET /v1 |
get |
| Update a schedule | PATCH /v1 |
update |
| Delete a schedule | DELETE /v1 |
delete |
| Pause a schedule | POST /v1 |
pause |
| Resume a schedule | POST /v1 |
resume |
| Start a run from a schedule now | POST /v1 |
run |
| Preview a schedule's next occurrences | GET /v1 |
preview |
Create a schedule
curl -X POST https://app.processcart.com/api/v1/schedules \
-H "Authorization: Bearer pc_live_…" \
-H "Content-Type: application/json" \
-d '{ "workflowId": "3c1d…", "name": "Weekly order review",
"recurrence": { "freq": "weekly", "interval": 1, "time": "09:00",
"startDate": "2026-10-12", "weekly": { "days": ["mon"] } },
"namePattern": "{{workflow}} — {{date:YYYY-MM-DD}}",
"presetValues": { "customer": "Acme Corp", "amount": 250 },
"assignmentOverrides": { "review": [{ "kind": "group", "groupId": "7f0e…" }] } }'const schedule = await client.createSchedule({
workflowId: "3c1d…",
name: "Weekly order review",
recurrence: {
freq: "weekly",
interval: 1,
time: "09:00",
startDate: "2026-10-12",
weekly: { days: ["mon"] },
},
presetValues: { customer: "Acme Corp", amount: 250 },
});
console.log(schedule.description, schedule.nextOccurrences);The answer is the schedule (201, with an absolute Location): its recurrence in words (description), nextRunAt and the next three occurrences. A recurrence with no future occurrence is refused (422).
timezoneis an IANA zone such asAmerica/New_York; leave it out to use the workspace's zone.presetValuesare canonical values keyed by field key, checked against the published version likePOST /v1/runsfields (Field values & typing). A preset its field's format rules refuse — outside a minimum or maximum, or failing a pattern — is refused when the schedule is saved (422,errors[].codeinvalid_valueatpresetValues.<key>), so a schedule never starts runs with it. Every required kickoff field needs a preset (422,errors[].coderequired; a required checkbox needstrue) — except a File field, a field the API cannot write (a formula or other computed field), and a field whose default value fills it.assignmentOverridesmaps a task key to the members and groups that get that task in every run —{ "kind": "user", "userId" }or{ "kind": "group", "groupId" }(1–20 per task), the same spelling the Run resource uses for those two kinds. Only these two kinds are accepted: a Run'sroleorguestassignee cannot be an override, and an entry carrying any other member is refused (422). An override replaces the task's own assignment rule for those runs; an override whose members have all left the workspace, or whose group was deleted, falls back to that rule.- A schedule stays on its workflow; create another schedule for a different one. The workflow must be published, not archived and not in Trash.
Recurrence
| Member | Meaning |
|---|---|
freq |
daily, weekly, monthly or yearly. |
interval |
Every N days (1–365), weeks (1–52), months (1–24) or years (1–10), counted from start. |
time |
HH:mm, 24-hour, minutes on 5-minute steps (00, 05, …, 55), in the schedule's zone. |
start / end |
The first and last local dates an occurrence may fall on (end inclusive; omit it or send null for no end). |
weekly |
{ "days": ["mon", "wed", "fri"] } — weeks start on Monday; the week holding start is the first. |
monthly |
{ "kind": "day", "day": 31 } (a day past the end of a shorter month runs on its last day) or { "kind": "ordinal", "ordinal": "last", "target": "weekday" } — ordinal is first, second, third, fourth or last; target is a weekday (mon … sun), day, weekday or weekend_day (“the last weekday”, “the first Monday”). |
yearly |
{ "month": 3, "day": 15 }; February 29 runs on February 28 in other years. |
Only the member that matches freq may be present. GET /v1/schedules/{id}/preview?count=5 answers the next occurrences (1–10, default 3) as UTC instants; for a paused schedule, the ones a resume would produce.
When runs are created
- Runs are created at the scheduled time, usually within a minute: the scheduler checks for due schedules every 5 minutes, and schedule times sit on 5-minute steps.
- Daylight saving time. A time that does not exist on a spring-forward day (02:30 when clocks jump from 02:00 to 03:00) runs at the same offset into the new hour (03:30). A time that happens twice on a fall-back day runs once, at the first.
- Catch-up. If runs could not be created while the schedule was on (an outage), the next check creates ONE run, for the latest missed occurrence, and records how many it skipped in
lastError. It never creates one run per missed occurrence. - Resuming never catches up. Occurrences that fall while a schedule is paused are skipped; after a resume, the next run is the first occurrence after now.
- Time zones. The schedule's zone decides when a run is created and what
{{date}}and{{time}}read. Inside the run,TODAY(), date-field defaults and due rules use the workspace's zone, as for any run. - Outages. A start that fails for a passing reason — the database did not answer, or the start failed unexpectedly — is not a refusal:
lastErrorrecords it, the next check retries it, and the schedule is never paused for it. Run now answers 503unavailablewithRetry-Afterinstead (500internal_errorfor an unexpected failure). Any other error while creating a run counts toward a pause: the third in a row pauses the schedule (failed). - Which server fires it. A schedule fires only on the kind of server (production, preview or local) that created it or last edited or resumed it. When an edit or a resume moves it to another kind, its next run is recomputed from now, so the new server never creates a catch-up run for occurrences the old one missed.
Run names
namePattern (default {{workflow}} — {{date}}) takes these tokens; anything else is kept literally, and the name is cut to 200 characters.
| Token | Renders |
|---|---|
{{workflow}} |
The workflow's name. |
{{date}} |
The occurrence's local date, MMM D, YYYY. |
{{date:<pattern>}} |
The date in a Date-field pattern: YYYY-MM-DD, MM/, DD/, DD-MMM-YYYY, MMM D, YYYY or MMMM D, YYYY. |
{{time}} |
The occurrence's local time, HH:mm. |
Runs a schedule creates
Every such run has startedBy: "schedule" and schedule: { id, name } (Runs). scheduledFor is the occurrence it was created for; it is null for a run started with Run now. Each occurrence creates at most one run. After a schedule is deleted its runs stay, and their schedule reads null. Subscribe to schedule.run_created to hear about them (Webhooks).
curl -X POST https://app.processcart.com/api/v1/schedules/5b2a…/run \
-H "Authorization: Bearer pc_live_…"Run now (POST /v1/schedules/{id}/run) starts one run right away — paused or not — with the schedule's pattern (rendered at the start instant), presets and overrides. It answers the new Run (201, its revision in the body) and an absolute Location. Presets the published version no longer accepts are skipped (the schedule's presetMismatches names them); a required kickoff field without a preset is refused with 422 required_fields_unsatisfied.
Pause, resume, update, delete
POST /v1/schedules/{id}/pauseand/resumeanswer the schedule (200), unchanged when it already was. Resuming a schedule whose end date has passed is 409schedule_ended— change its recurrence first. Creating, resuming or running a schedule now — or aPATCHthat changes itspresetValuesorassignmentOverrides— on an unpublished workflow is 409workflow_not_published, on one in Trash 409workflow_trashed, on an archived one 403workflow_not_runnable.PATCH /v1/schedules/{id}changes any ofname,recurrence,timezone,namePattern,presetValuesandassignmentOverrides; a new recurrence or zone recomputes the next run from now.enabledis refused (422,errors[].codeunknown_keyonenabled) — pause and resume own it.DELETE /v1/schedules/{id}answers 204.GET /v1/scheduleslists schedules newest first; filter byworkflowIdorenabled, and useupdatedSincefor a sync (Incremental sync).
Schedules carry no revision: no schedule operation reads Expected-Revision (the SDK's expectedRevision option is ignored here), and an update applies to the schedule as it is stored, so the last write wins. Read the schedule again before an update that depends on what another client may have changed.
Every change is recorded in the workspace's audit log under the key's name.
Why a schedule is paused
disabledReason says why enabled is false:
| Reason | Meaning |
|---|---|
manual |
Paused by a person or a key. |
ended |
The recurrence's end date passed, or it has no occurrence left to schedule (last then reads “This schedule has no future runs.”). |
unpublished |
The workflow has no published version. |
failed |
A start was refused (a preset or required field problem), three attempts in a row to create a run failed for a reason other than an outage, or the recurrence can no longer be read; last says why. |
workflow_trashed |
The workflow moved to Trash. Restoring it does not resume the schedule. |
workflow_archived |
The workflow was archived. Unarchiving it does not resume the schedule. |
When the scheduler itself pauses a schedule as failed or unpublished, the workspace's Builders and Admins are notified. Moving the workflow to Trash or archiving it pauses its schedules at once, without a notification.
Allowlists
A key restricted to workflows or folders lists only the schedules of workflows inside its allowlist; any other schedule answers 403 not_allowed_for_key, and so does creating one for a workflow outside it.