Skip to main content

Schedules

A schedule is a standing instruction to start a checklist from a template on a clock — every Monday at 09:00 in London, the last weekday of each month, once on a given date. It is the API side of the Scheduling screen described in Create a Schedule. A schedule is not a checklist and never becomes one: it is a rule, and the checklists it produces are ordinary checklists from the moment they exist.

Behind every schedule sits a series of runs — one per future occurrence, written out in advance when the schedule is saved. A background job turns each run into a real checklist shortly before it is due. The /runs routes let you read that series, skip an occurrence and create an occurrence's checklist early. They are also the only way to find a run that was due but never produced a checklist. See How Schedules Run.

Schedules and runs are named by integer ids, not GUID keys. Neither table has a key column. A schedule id that belongs to another workspace is a 404, the same as one that does not exist.

What a key may do with schedules depends on who it acts as — see Permissions. A schedule's createdBy is the member the key acts as. See Who a Key Acts As.

Every write on this page accepts an Idempotency-Key header (see Idempotency), and every route is charged to the standard rate-limit budget (see Rate Limits).

Endpoints​

MethodPathDescriptionMCP tool
GET/v3/schedulesList scheduleslist_schedules
GET/v3/schedules/{id}Get a scheduleget_schedule
POST/v3/schedulesCreate a schedulecreate_schedule
PATCH/v3/schedules/{id}Update a scheduleupdate_schedule
DELETE/v3/schedules/{id}Delete a scheduledelete_schedule
GET/v3/schedules/{id}/runsList a schedule's runslist_schedule_runs
GET/v3/schedules/{id}/runs/{runId}Get a runget_schedule_run
DELETE/v3/schedules/{id}/runs/{runId}Skip a rundelete_schedule_run
POST/v3/schedules/{id}/runs/{runId}/checklistCreate a run's checklist nowcreate_schedule_run_checklist

Permissions​

The schedule routes apply the permissions the app's Scheduling screen applies. A key that acts as an Administrator can do everything on this page, and so can a key that acts as the workspace. A key that acts as a Member needs:

OperationWhat a Member needs
List and get schedules, list and get runsSchedule.Viewer or Schedule.Creator.
Create a scheduleSchedule.Creator, and permission to run checklists from the chosen template — Run and View or Run and View Assigned To in its Checklist Permissions.
Update or delete a scheduleSchedule.Creator. A schedule has no owner: a Member with Schedule.Creator can change or delete any schedule, as in the app.
Skip a run, or create a run's checklist nowSchedule.Creator, and permission to run checklists from the schedule's template.

A refusal is 403 FORBIDDEN, and its message names what is missing. It comes after the schedule is looked up, so a schedule in another workspace is still a 404.

How Schedules Run​

Creating a schedule writes out its run series immediately, from the first occurrence to a horizon that depends on how often it repeats. A background job runs every ten minutes and turns each run due in the next fifteen minutes into a checklist. So a checklist appears up to about fifteen minutes before its time. The checklist's start is set to the time the run was due, not the moment the job made it. Once a week another job tops up any series whose last run is less than three months away.

repeatsRuns generated aheadLargest every
neverOne run only—
daily400 days400
weekly400 days57
monthly25 months25
yearly10 years10
last_day_of_month, last_weekday_of_month, first_weekday_of_month25 months—
last_day_of_quarter, last_weekday_of_quarter60 months—

The job creates a run's checklist only when all of these hold:

  • the schedule is active
  • the template is not archived
  • the workspace's subscription or trial is current.

When any of them fails, the job skips the run and reports nothing — no error, no notification. The schedule still reads as healthy and still shows a nextRun. Two fields expose this:

  • templateIsArchived on the Schedule object. When it is true, the schedule produces nothing until the template is unarchived. Switching isActive off and on does not help.
  • status: "missed" on a run — a run whose time has passed with no checklist. List them with GET /v3/schedules/{id}/runs?status=missed.

Each generated checklist fires the new-checklist webhook, and the job notifies the whole team that the scheduler has created it.

Recurrence​

recurrence is one object with three fields: repeats, every and daysOfWeek. Only some combinations mean anything. The API refuses the others instead of storing a setting the generator never reads.

repeatsWhat it produceseverydaysOfWeek
neverOne run, at startDateTime, and nothing after.RefusedRefused
dailyEvery every days — or, with daysOfWeek, every one of those days.Only without daysOfWeekOptional
weeklyEvery every weeks on the start date's weekday — or, with daysOfWeek, on those days in every everyth week.OptionalOptional
monthlyEvery every months on the start date's day of the month.OptionalRefused
yearlyEvery every years on the start date.OptionalRefused
last_day_of_monthThe last calendar day of each month.RefusedRefused
last_weekday_of_monthThe last Monday-to-Friday of each month.RefusedRefused
first_weekday_of_monthThe first Monday-to-Friday of each month.RefusedRefused
last_day_of_quarterThe last calendar day of March, June, September and December.RefusedRefused
last_weekday_of_quarterThe last Monday-to-Friday of each quarter.RefusedRefused
  • repeats is matched without regard to case, and always reads back in lower case.
  • every defaults to 1 and must be 1 or more. 1 is accepted on every kind, so you can send a schedule's recurrence back exactly as you read it. A value above 1 is refused wherever the table says so, and above the largest value in How Schedules Run, because runs are only generated that far ahead.
  • daysOfWeek takes monday to sunday, in any case and any order, and reads back Monday first with duplicates removed. Leaving it out and sending an empty list mean the same thing: run on the weekday the start date falls on.
  • never is a one-off, not a way of switching a schedule off. To stop a schedule, set isActive to false.
  • There is no end date and no occurrence count. A repeating schedule runs until you deactivate or delete it.
  • "Weekday" in the fixed-position kinds means Monday to Friday. Public holidays are not taken into account.
  • The fixed-position kinds take their time of day from startDateTime. The first run is that position in the start date's month or quarter — or the next one, if that has already passed. nextRun shows where it landed.
warning

A monthly schedule that starts on the 29th, 30th or 31st moves to the last day of the first shorter month, and stays on that day afterwards. A schedule starting on 31 January runs on 28 February, then on 28 March. For month-end work, use last_day_of_month or last_weekday_of_month. A yearly schedule starting on 29 February behaves the same way.

For daily and weekly with daysOfWeek, choose a startDateTime that falls on one of the listed days.

Time Zones​

timeZone is the zone the schedule's clock runs in. It takes an IANA name such as Europe/London or a Windows name such as GMT Standard Time, and reads back exactly as you sent it. An unknown zone is a 400.

startDateTime is a wall-clock time in that zone, not an instant — the one exception, with a run's scheduledDateTime, to the UTC rule in Dates and Times. Send it with no Z and no offset:

"timeZone": "Europe/London",
"startDateTime": "2026-10-05T09:00:00"

That means 09:00 in London. Every run is generated at that wall-clock time, so a 09:00 schedule stays at 09:00 on both sides of a daylight-saving change and the UTC instant moves instead. Responses report both readings: startDateTime next to startDateTimeUtc on a schedule, and scheduledDateTime next to scheduledDateTimeUtc on a run.

  • A value with a Z or an offset is refused with 400, not converted. The two readings differ by hours, and a schedule saved an hour out would stay an hour out.
  • A time that does not exist — the hour skipped when the clocks go forward, such as 2027-03-28T01:30:00 in Europe/London — is refused.
  • A time that happens twice, when the clocks go back, is taken as the standard-time reading. That is the second of the two.
  • A start in the past is allowed for a repeating schedule. The first run moves forward to the next occurrence after now. It is refused for never, whose only run would already be due. To start a checklist now, use POST /v3/checklists.

startDateTime reads back as the value you set, not as the first run. When a past start has been moved forward, or a fixed-position kind has snapped to its day, nextRun holds the actual next run.

Checklist Names​

checklistNameFormat is the name each generated checklist gets. It is separate from the schedule's own name, and defaults to the template's name. It is text with tokens in double braces, and whitespace inside the braces is ignored. Token names are matched exactly, including case.

TokenExample for a run on Monday 5 October 2026 at 09:00
{{ template.name }}Invoice Review
{{ schedule.name }}Weekly Invoice Review
{{ checklist.date.dd/MM/yy }}05/10/26
{{ checklist.date.dd/MM/yyyy }}05/10/2026
{{ checklist.date.MM/dd/yy }}10/05/26
{{ checklist.date.MM/dd/yyyy }}10/05/2026
{{ checklist.date.yyyy-MM-dd }}2026-10-05
{{ checklist.date.MMMM d }}October 5th
{{ checklist.date.MMMM d, yyyy }}October 5th 2026
{{ checklist.date.dddd, MMMM d }}Monday, October 5th
{{ checklist.date.dddd, MMMM d, yyyy }}Monday, October 5th 2026
{{ checklist.date.MMMM }}October
{{ checklist.date.dddd }}Monday
{{ checklist.date.yyyy }}2026
{{ checklist.time.HH:mm }}09:00
{{ checklist.time.HH:mm:ss }}09:00:00
{{ checklist.week }}41 (the ISO week number)
{{ checklist.quarter }}Q4

Dates and times are the run's own wall-clock date and time in the schedule's zone. The same tokens are available on the Scheduling screen — see Auto Generated Values.

  • A token not in this list is refused. The expander does not leave an unknown token visible: it removes the braces and writes the text into every name.
  • Unbalanced {{ or }} is refused.
  • The pattern may be up to 1,000 characters, but a generated name is stored in 100 and is cut to fit without warning. A pattern whose fixed text — everything outside the braces — is already over 100 characters is refused.
  • Names are expanded when the run series is written, not when the checklist is made. A run keeps the name it was generated with. Changing the pattern regenerates the pending runs so they pick it up. Checklists that already exist keep their names.

Parameters​

A schedule can set values for the template's parameters, and every checklist it creates is started with them. Parameters the schedule does not mention take the template's defaults. GET /v3/templates/{key} lists a template's parameters.

Name each parameter by its key or its name (matched without regard to case), and give a value as a string. A parameter the template does not have is refused, not dropped. Naming the same parameter twice is refused. On create, an entry whose value is null is ignored.

If the template later removes a parameter, the schedule's stored value stays. GET /v3/schedules/{id} lists it with its key and value but no name or type. The job ignores it when it builds each checklist.

The Schedule Object​

FieldTypeDescription
idintegerThe schedule's id.
namestringThe schedule's own name. Not the name its checklists get — that is checklistNameFormat.
templateKeystring (GUID)The template each run is started from. It cannot be changed after creation.
templateNamestringThe template's current name, read at request time.
templateIsArchivedbooleantrue when the template is archived, which means the schedule produces nothing. See How Schedules Run.
checklistNameFormatstringThe name pattern for generated checklists. See Checklist Names.
isActivebooleanWhether the schedule is generating runs.
recurrenceRecurrenceHow often it repeats.
timeZonestringThe IANA or Windows zone the schedule's clock runs in, as it was sent.
startDateTimestringThe start as a wall-clock time in timeZone, with no Z — for example 2026-10-05T09:00:00. Absent only for an older schedule whose stored zone this server does not recognise.
startDateTimeUtcstringThe same moment as a UTC instant.
nextRunstringWhen the next run is due, as a UTC instant. Absent when nothing is queued — the schedule is inactive, or it was a one-off that has already run.
createdDateTimestringWhen the schedule was created, UTC.
createdByobjectname and email of the member who created it. Only on the single-schedule read and on write responses. Absent if that member has left the workspace.
parametersarray of ParameterThe parameter values the schedule sets. Only on the single-schedule read and on write responses.
runsRegeneratedbooleanOnly on the response to PATCH. Whether the change rebuilt the pending runs.
{
"id": 318,
"name": "Weekly Invoice Review",
"templateKey": "3f2b8c1e-7a4d-4e5b-9c61-2d8f0a7b6e14",
"templateName": "Invoice Review",
"templateIsArchived": false,
"checklistNameFormat": "{{template.name}} — {{checklist.date.yyyy-MM-dd}}",
"isActive": true,
"recurrence": {
"repeats": "weekly",
"every": 1,
"daysOfWeek": ["monday"]
},
"timeZone": "Europe/London",
"startDateTime": "2026-10-05T09:00:00",
"startDateTimeUtc": "2026-10-05T08:00:00Z",
"nextRun": "2026-10-05T08:00:00Z",
"createdDateTime": "2026-09-28T14:12:37Z",
"createdBy": {
"name": "Sarah Chen",
"email": "sarah.chen@acme.example"
},
"parameters": [
{
"key": "8d41c7a2-5e9b-4f13-b0c6-7a2e9d18f354",
"name": "Cost Centre",
"type": "string",
"value": "FIN-01"
}
]
}

The Recurrence Object​

FieldTypeDescription
repeatsstringOne of never, daily, weekly, monthly, yearly, last_day_of_month, last_weekday_of_month, first_weekday_of_month, last_day_of_quarter, last_weekday_of_quarter.
everyintegerThe interval. Present only for daily without days, weekly, monthly and yearly.
daysOfWeekarray of stringmonday to sunday, Monday first. Present only for daily and weekly, and an empty array when no days are chosen.

Fields a kind does not read are left out of responses. A recurrence you read can be sent back in a PATCH unchanged. See Recurrence for the rules.

The Parameter Object​

FieldTypeDescription
keystring (GUID)The template parameter's key.
namestringThe parameter's name. Absent when the template no longer has this parameter.
typestringstring, datetime or member_or_group. Absent when the template no longer has this parameter.
valuestringThe value the schedule sets.

The Run Object​

A run is one occurrence of a schedule: a checklist it is going to produce, or has produced. A run has an id long before it has a checklist.

status is not stored. It is worked out from whether the run has a checklist and whether its time has passed:

statusMeaning
pendingDue in the future, no checklist yet. This is the state of nearly every run.
createdHas a checklist, named in checklist.
missedDue in the past, no checklist.

A run moves from pending to missed because the clock passes it, not because anything happened. A run a few minutes overdue can read as missed while the job is still on its way to it. A run that stays missed means the template was archived, the subscription had lapsed when it was due, or the job failed. You can still create its checklist now.

FieldTypeDescription
idintegerThe run's id. Not the checklist's key.
scheduleIdintegerThe schedule this run belongs to.
statusstringpending, created or missed.
checklistNamestringThe fully expanded name the run was generated with.
scheduledDateTimestringWhen the run is due, as a wall-clock time in the schedule's zone, with no Z. Absent only when the schedule's stored zone is not recognised.
scheduledDateTimeUtcstringThe same moment as a UTC instant. Order and filter on this.
timeZonestringThe schedule's time zone.
checklistobjectThe checklist the run became: key, name and url. Absent until there is one. name is what the checklist is called now. If it differs from checklistName, somebody renamed it.
startedDateTimestringWhen the checklist started, UTC. Usually the same instant as scheduledDateTimeUtc. It is earlier when the checklist was created early. Absent until there is a checklist.
completedDateTimestringWhen the checklist was completed, UTC. Absent while it is outstanding or there is no checklist.
checklistCreatedbooleanOnly on the response to POST .../checklist. true when that request created the checklist.
{
"id": 52107,
"scheduleId": 318,
"status": "created",
"checklistName": "Invoice Review — 2026-10-05",
"scheduledDateTime": "2026-10-05T09:00:00",
"scheduledDateTimeUtc": "2026-10-05T08:00:00Z",
"timeZone": "Europe/London",
"checklist": {
"key": "c7e2a915-3b8d-4f06-9a4c-e15d2b7f8032",
"name": "Invoice Review — 2026-10-05 (late invoices)",
"url": "https://acme.checkflow.io/Checklist/Index?checklistKey=c7e2a915-3b8d-4f06-9a4c-e15d2b7f8032"
},
"startedDateTime": "2026-10-05T08:00:00Z",
"completedDateTime": "2026-10-05T15:42:10Z"
}

Worked Examples​

Each example is the body of a POST /v3/schedules. Replace templateKey with one of your own templates.

Every Monday at 09:00, London Time​

{
"name": "Weekly Invoice Review",
"templateKey": "3f2b8c1e-7a4d-4e5b-9c61-2d8f0a7b6e14",
"timeZone": "Europe/London",
"startDateTime": "2026-10-05T09:00:00",
"recurrence": { "repeats": "weekly" }
}

5 October 2026 is a Monday, so a plain weekly repeats on Mondays. "daysOfWeek": ["monday"] gives the same result. The first run is at 08:00 UTC (British Summer Time). From 26 October the runs are at 09:00 UTC, still 09:00 in London.

Every Weekday at 07:30, New York Time​

{
"name": "Daily Supplier Check",
"templateKey": "3f2b8c1e-7a4d-4e5b-9c61-2d8f0a7b6e14",
"timeZone": "America/New_York",
"startDateTime": "2026-10-05T07:30:00",
"recurrence": {
"repeats": "daily",
"daysOfWeek": ["monday", "tuesday", "wednesday", "thursday", "friday"]
}
}

Every Other Week, on Monday and Thursday​

{
"name": "Fortnightly Supplier Onboarding Review",
"templateKey": "3f2b8c1e-7a4d-4e5b-9c61-2d8f0a7b6e14",
"timeZone": "Europe/London",
"startDateTime": "2026-10-05T10:00:00",
"recurrence": {
"repeats": "weekly",
"every": 2,
"daysOfWeek": ["monday", "thursday"]
}
}

Weeks start on Monday. This runs on 5 and 8 October, then on 19 and 22 October, and so on.

The Last Working Day of Each Month at 17:00​

{
"name": "Month-End Close",
"templateKey": "3f2b8c1e-7a4d-4e5b-9c61-2d8f0a7b6e14",
"checklistNameFormat": "{{template.name}} — {{checklist.date.MMMM}} {{checklist.date.yyyy}}",
"timeZone": "Europe/London",
"startDateTime": "2026-10-01T17:00:00",
"recurrence": { "repeats": "last_weekday_of_month" }
}

The start date only supplies the month and the time of day. The first run snaps to Friday 30 October 2026 at 17:00, so the response shows "startDateTime": "2026-10-01T17:00:00" and "nextRun": "2026-10-30T17:00:00Z". "Working day" means Monday to Friday — public holidays are not skipped.

Once, on a Future Date​

{
"name": "Q4 Audit Kick-Off",
"templateKey": "3f2b8c1e-7a4d-4e5b-9c61-2d8f0a7b6e14",
"timeZone": "Europe/London",
"startDateTime": "2026-11-02T09:00:00",
"recurrence": { "repeats": "never" }
}

Not Supported​

The recurrence model cannot express these patterns:

  • an end date or a fixed number of occurrences
  • "the second Tuesday of the month" or any nth weekday other than first or last
  • skipping public holidays.

To skip a single occurrence, delete that run.

List Schedules​

Returns the workspace's schedules. Parameter values and createdBy are not on these rows — use Get a Schedule for those.

GET /v3/schedules

Parameters​

NameInTypeRequiredDescription
templateKeyquerystring (GUID)NoOnly the schedules that run this template.
isActivequerybooleanNotrue or false. Any other value is refused. Being active does not mean producing — check templateIsArchived.
sortquerystringNofield or field:asc / field:desc. One of name, templateName, startDateTime, nextRun, createdDateTime. Default name:asc. Ties are broken by id.
afterquerystringNoThe nextCursor from the previous page. Send the same filters, sort and pageSize with it.
pageSizequeryintegerNo1–100, default 50. A value that is not a number from 1 to 100 is replaced by 50, not refused.

See Pagination. total is always present.

Example​

GET https://api.checkflow.io/v3/schedules?isActive=true&sort=nextRun
X-API-KEY: your-api-key-here
HTTP/1.1 200 OK
{
"items": [
{
"id": 318,
"name": "Weekly Invoice Review",
"templateKey": "3f2b8c1e-7a4d-4e5b-9c61-2d8f0a7b6e14",
"templateName": "Invoice Review",
"templateIsArchived": false,
"checklistNameFormat": "{{template.name}} — {{checklist.date.yyyy-MM-dd}}",
"isActive": true,
"recurrence": {
"repeats": "weekly",
"every": 1,
"daysOfWeek": ["monday"]
},
"timeZone": "Europe/London",
"startDateTime": "2026-10-05T09:00:00",
"startDateTimeUtc": "2026-10-05T08:00:00Z",
"nextRun": "2026-10-05T08:00:00Z",
"createdDateTime": "2026-09-28T14:12:37Z"
},
{
"id": 324,
"name": "Month-End Close",
"templateKey": "a6d09e3b-41c7-4b2f-8e5a-f3c81d627b90",
"templateName": "Supplier Onboarding",
"templateIsArchived": false,
"checklistNameFormat": "{{template.name}} — {{checklist.date.MMMM}} {{checklist.date.yyyy}}",
"isActive": true,
"recurrence": {
"repeats": "last_weekday_of_month"
},
"timeZone": "Europe/London",
"startDateTime": "2026-10-01T17:00:00",
"startDateTimeUtc": "2026-10-01T16:00:00Z",
"nextRun": "2026-10-30T17:00:00Z",
"createdDateTime": "2026-09-28T14:20:05Z"
}
],
"hasMore": false,
"total": 2
}

Responses​

StatusCodeWhen
200—The page of schedules.
400VALIDATION_ERRORfield is templateKey (not a GUID), isActive (not true or false), sort (unknown field) or after (malformed, or issued for a different query).
403FORBIDDENThe key acts as a Member with neither Schedule.Viewer nor Schedule.Creator.

Get a Schedule​

Returns one schedule, with its parameter values and who created it.

GET /v3/schedules/{id}

Parameters​

NameInTypeRequiredDescription
idpathintegerYesThe schedule id.

Example​

GET https://api.checkflow.io/v3/schedules/318
X-API-KEY: your-api-key-here
HTTP/1.1 200 OK

Returns a Schedule object, as in the example there.

Responses​

StatusCodeWhen
200—The schedule.
400VALIDATION_ERRORfield is id: not a positive integer.
403FORBIDDENThe key acts as a Member with neither Schedule.Viewer nor Schedule.Creator.
404SCHEDULE_NOT_FOUNDNo schedule with that id in this workspace.

Create a Schedule​

Creates a schedule and writes out its run series straight away. Everything that decides when a run happens is checked before anything is stored.

POST /v3/schedules

Parameters​

This endpoint takes no parameters.

Request Body​

{
"name": "Weekly Invoice Review",
"templateKey": "3f2b8c1e-7a4d-4e5b-9c61-2d8f0a7b6e14",
"checklistNameFormat": "{{template.name}} — {{checklist.date.yyyy-MM-dd}}",
"timeZone": "Europe/London",
"startDateTime": "2026-10-05T09:00:00",
"recurrence": {
"repeats": "weekly",
"daysOfWeek": ["monday"]
},
"parameters": [
{ "name": "Cost Centre", "value": "FIN-01" }
]
}
FieldTypeRequiredDescription
namestringYesThe schedule's name. At most 100 characters.
templateKeystring (GUID)YesThe template to start each run from. Must be an unarchived template in this workspace. Cannot be changed later.
timeZonestringYesIANA or Windows zone name. See Time Zones.
startDateTimestringYesThe first run, as a wall-clock time in timeZone, with no Z and no offset.
recurrenceRecurrenceYesrepeats is required. Send { "repeats": "never" } for a one-off.
checklistNameFormatstringNoThe name pattern for generated checklists. Default: the template's name.
isActivebooleanNoDefault true. With false, no runs are generated until the schedule is activated.
parametersarrayNoEntries of { "key" or "name", "value" }. See Parameters.

There is no duplicate check. Two schedules on the same template at the same time are two schedules and create two checklists. That is useful with different parameter values on each.

Example​

POST https://api.checkflow.io/v3/schedules
X-API-KEY: your-api-key-here
Content-Type: application/json

{
"name": "Weekly Invoice Review",
"templateKey": "3f2b8c1e-7a4d-4e5b-9c61-2d8f0a7b6e14",
"checklistNameFormat": "{{template.name}} — {{checklist.date.yyyy-MM-dd}}",
"timeZone": "Europe/London",
"startDateTime": "2026-10-05T09:00:00",
"recurrence": {
"repeats": "weekly",
"daysOfWeek": ["monday"]
},
"parameters": [
{ "name": "Cost Centre", "value": "FIN-01" }
]
}
HTTP/1.1 201 Created

Returns the new Schedule object, exactly as in the example there, with createdBy, parameters and the nextRun the generator settled on.

A start time with a Z is refused:

HTTP/1.1 400 Bad Request
{
"error": {
"code": "VALIDATION_ERROR",
"message": "startDateTime is a wall-clock time in the schedule's own time zone. Send it without a 'Z' and without an offset, such as '2026-03-01T09:00:00', and name the zone in timeZone.",
"requestId": "0f7c2b9e-51a4-4d8e-b3a6-92e41c7d05f8",
"field": "startDateTime"
}
}

Responses​

StatusCodeWhen
201—The schedule was created.
400VALIDATION_ERRORNo body; field is name (missing or over 100 characters), templateKey (missing or not a GUID), timeZone (missing or unknown), startDateTime (missing, carries a Z or offset, falls in a clocks-forward gap, or in the past on never), recurrence (missing), recurrence.repeats (missing or unknown), recurrence.every (below 1, on a kind that does not read it, on daily with days, or over the limit), recurrence.daysOfWeek (unknown day, or on a kind that does not read it), checklistNameFormat (unknown token, unbalanced braces, over 1,000 characters or over 100 characters of fixed text) or parameters (a parameter the template does not have, one named twice, or an entry with neither key nor name).
403FORBIDDENThe key acts as a Member without Schedule.Creator, or without permission to run checklists from the template.
404TEMPLATE_NOT_FOUNDNo unarchived template with that key in this workspace.

Update a Schedule​

Changes a schedule. Only the fields you send are changed. The template cannot be changed — delete the schedule and create a new one.

PATCH /v3/schedules/{id}

Parameters​

NameInTypeRequiredDescription
idpathintegerYesThe schedule id.

Request Body​

This is a partial update, not a JSON merge patch. See Partial Updates with PATCH. The body must name at least one field. An empty body is refused because a misspelled field name is more likely than a request to change nothing.

FieldTypeSending nullDescription
namestringRefusedThe schedule's name. At most 100 characters.
checklistNameFormatstringResets it to the template's nameThe name pattern for future checklists.
timeZonestringRefusedChanging the zone alone keeps the wall-clock time and moves the instant. A 09:00 schedule moved from Europe/London to America/New_York still runs at 09:00, now five hours later in UTC. Send startDateTime too to set both.
startDateTimestringRefusedA new start, with the same rules as on create.
recurrenceRecurrenceRefusedReplaced as a whole, not merged. { "repeats": "weekly" } on a schedule that ran every two weeks on Monday and Thursday leaves it weekly, every week, on the start date's weekday. Read the schedule first and send the recurrence you want in full.
isActivebooleanRefusedfalse deletes the pending runs. true generates a fresh series from now. Missed occurrences are not made up.
parametersarrayChanges nothingMerged into the stored values one at a time. An entry with "value": null removes that value, so the template's default applies again. An empty list changes nothing.

Changing timeZone or recurrence re-checks the stored start. It is refused if the start now falls in a clocks-forward gap, or if the schedule becomes never with a start in the past.

Example​

Adding Thursdays to the weekly schedule, sent on Tuesday 27 October 2026:

PATCH https://api.checkflow.io/v3/schedules/318
X-API-KEY: your-api-key-here
Content-Type: application/json

{
"recurrence": {
"repeats": "weekly",
"daysOfWeek": ["monday", "thursday"]
}
}
HTTP/1.1 200 OK
{
"id": 318,
"name": "Weekly Invoice Review",
"templateKey": "3f2b8c1e-7a4d-4e5b-9c61-2d8f0a7b6e14",
"templateName": "Invoice Review",
"templateIsArchived": false,
"checklistNameFormat": "{{template.name}} — {{checklist.date.yyyy-MM-dd}}",
"isActive": true,
"recurrence": {
"repeats": "weekly",
"every": 1,
"daysOfWeek": ["monday", "thursday"]
},
"timeZone": "Europe/London",
"startDateTime": "2026-10-05T09:00:00",
"startDateTimeUtc": "2026-10-05T08:00:00Z",
"nextRun": "2026-10-29T09:00:00Z",
"createdDateTime": "2026-09-28T14:12:37Z",
"createdBy": {
"name": "Sarah Chen",
"email": "sarah.chen@acme.example"
},
"parameters": [
{
"key": "8d41c7a2-5e9b-4f13-b0c6-7a2e9d18f354",
"name": "Cost Centre",
"type": "string",
"value": "FIN-01"
}
],
"runsRegenerated": true
}

Responses​

StatusCodeWhen
200—The schedule as it now reads, with runsRegenerated.
400VALIDATION_ERRORThe body is not a JSON object or names no field; field is id (not a positive integer), isActive (null), or any field listed under Create a Schedule for the same reasons, including null where the table says it is refused. Also timeZone when the schedule's stored zone is not recognised — send timeZone and startDateTime together to repair it.
403FORBIDDENThe key acts as a Member without Schedule.Creator.
404SCHEDULE_NOT_FOUNDNo schedule with that id in this workspace.
404TEMPLATE_NOT_FOUNDparameters was sent and the schedule's template no longer exists.

Notes​

The pending runs are rebuilt when you change startDateTime, timeZone, recurrence, checklistNameFormat or isActive, or change name while the pattern uses {{ schedule.name }}. Sending isActive rebuilds even when the value is unchanged. runsRegenerated tells you whether it happened.

Rebuilding deletes every run due after now and generates the series again from the next occurrence. Runs already due are kept, so the schedule's history survives. The regenerated runs have new ids. A run you skipped comes back when the series is rebuilt.

warning

A rebuild can create or remove checklists near the current time:

  • The job creates checklists up to about fifteen minutes early, with a start at the run's due time. A checklist in that window counts as pending: the rebuild deletes it, and the job then creates it again from the regenerated run.
  • On a never schedule whose one run has already happened, a change that rebuilds generates that run again with its time already passed. The job then creates a second checklist.

Delete a Schedule​

Deletes a schedule, its pending runs and its parameter values. To stop a schedule without losing it, set isActive to false instead.

DELETE /v3/schedules/{id}

Parameters​

NameInTypeRequiredDescription
idpathintegerYesThe schedule id.

Example​

DELETE https://api.checkflow.io/v3/schedules/318
X-API-KEY: your-api-key-here

Responds 204 No Content with no body.

Responses​

StatusCodeWhen
204—The schedule was deleted.
400VALIDATION_ERRORfield is id: not a positive integer.
403FORBIDDENThe key acts as a Member without Schedule.Creator.
404SCHEDULE_NOT_FOUNDNo schedule with that id in this workspace.

Notes​

danger

Deleting a schedule cannot be undone. The recurrence, name pattern and parameter values are gone and must be rebuilt from scratch.

Checklists the schedule has already created are kept, including any created in the last few minutes for a run about to fall due. They are ordinary checklists and are not affected.

List a Schedule's Runs​

Returns a schedule's runs — the checklists it has produced and the ones it is going to produce — oldest first.

GET /v3/schedules/{id}/runs

Parameters​

NameInTypeRequiredDescription
idpathintegerYesThe schedule id.
statusquerystringNopending, created or missed, in any case. Any other value is refused.
fromquerystringNoOnly runs due at or after this time. A wall-clock time in the schedule's zone, such as 2026-10-01T00:00:00, with no Z and no offset.
toquerystringNoOnly runs due at or before this time, on the same terms as from.
sortquerystringNofield or field:asc / field:desc. One of scheduledDateTime, checklistName, startedDateTime, completedDateTime. Default scheduledDateTime:asc. There is no status sort. Ties are broken by id.
afterquerystringNoThe nextCursor from the previous page. Send the same filters, sort and pageSize with it.
pageSizequeryintegerNo1–100, default 50. A value that is not a number from 1 to 100 is replaced by 50, not refused.

from and to follow the same rules as startDateTime: a value with a Z or an offset is refused, and so is a time inside a clocks-forward gap. A time that happens twice takes the standard-time reading. To avoid the zone entirely, leave them out and filter on scheduledDateTimeUtc yourself. See Pagination. total is always present.

Example​

The runs either side of the October clock change, read on 20 October 2026:

GET https://api.checkflow.io/v3/schedules/318/runs?from=2026-10-19T00:00:00&to=2026-11-01T23:59:59
X-API-KEY: your-api-key-here
HTTP/1.1 200 OK
{
"items": [
{
"id": 52109,
"scheduleId": 318,
"status": "created",
"checklistName": "Invoice Review — 2026-10-19",
"scheduledDateTime": "2026-10-19T09:00:00",
"scheduledDateTimeUtc": "2026-10-19T08:00:00Z",
"timeZone": "Europe/London",
"checklist": {
"key": "5a90d3e7-c24b-4e81-9f36-0b7d1e4a2c59",
"name": "Invoice Review — 2026-10-19",
"url": "https://acme.checkflow.io/Checklist/Index?checklistKey=5a90d3e7-c24b-4e81-9f36-0b7d1e4a2c59"
},
"startedDateTime": "2026-10-19T08:00:00Z"
},
{
"id": 52110,
"scheduleId": 318,
"status": "pending",
"checklistName": "Invoice Review — 2026-10-26",
"scheduledDateTime": "2026-10-26T09:00:00",
"scheduledDateTimeUtc": "2026-10-26T09:00:00Z",
"timeZone": "Europe/London"
}
],
"hasMore": false,
"total": 2
}

Both runs are at 09:00 in London. The UTC instant moves by an hour because the clocks went back on 25 October.

Responses​

StatusCodeWhen
200—The page of runs.
400VALIDATION_ERRORfield is id (not a positive integer), status (unknown), from or to (not a date, carries a Z or offset, inside a clocks-forward gap, or the schedule's stored zone is not recognised), to (before from), sort (unknown field) or after (malformed, or issued for a different query).
403FORBIDDENThe key acts as a Member with neither Schedule.Viewer nor Schedule.Creator.
404SCHEDULE_NOT_FOUNDNo schedule with that id in this workspace.

Get a Run​

Returns one run, with its checklist if it has one.

GET /v3/schedules/{id}/runs/{runId}

Parameters​

NameInTypeRequiredDescription
idpathintegerYesThe schedule id.
runIdpathintegerYesThe run id, from List a Schedule's Runs.

Example​

GET https://api.checkflow.io/v3/schedules/318/runs/52107
X-API-KEY: your-api-key-here
HTTP/1.1 200 OK

Returns a Run object, as in the example there. In that example checklist.name differs from checklistName because somebody renamed the checklist after it was created.

Responses​

StatusCodeWhen
200—The run.
400VALIDATION_ERRORfield is id or runId: not a positive integer.
403FORBIDDENThe key acts as a Member with neither Schedule.Viewer nor Schedule.Creator.
404SCHEDULE_NOT_FOUNDNo schedule with that id in this workspace.
404SCHEDULE_RUN_NOT_FOUNDThe schedule exists but has no run with that id.

Notes​

A run is looked up within the schedule named in the path. A run id belonging to a different schedule is a 404. The two error codes tell you which id was wrong. Both ids are plain integers, so SCHEDULE_RUN_NOT_FOUND often means they were swapped.

Skip a Run​

Deletes one run that has not produced a checklist, so the schedule skips that occurrence. This is the only way to skip a single occurrence of a repeating schedule. It also removes a missed run.

DELETE /v3/schedules/{id}/runs/{runId}

Parameters​

NameInTypeRequiredDescription
idpathintegerYesThe schedule id.
runIdpathintegerYesThe run id.

Example​

Skipping the Monday of Christmas week:

DELETE https://api.checkflow.io/v3/schedules/318/runs/52118
X-API-KEY: your-api-key-here

Responds 204 No Content with no body.

Responses​

StatusCodeWhen
204—The run was deleted.
400VALIDATION_ERRORfield is id or runId: not a positive integer.
403FORBIDDENThe key acts as a Member without Schedule.Creator, or without permission to run checklists from the schedule's template.
404SCHEDULE_NOT_FOUNDNo schedule with that id in this workspace.
404SCHEDULE_RUN_NOT_FOUNDThe schedule has no run with that id.
409CONFLICTThe run has already produced a checklist. The message names the checklist's key.

Notes​

  • A skipped run stays skipped until the series is rebuilt. Any update with runsRegenerated: true brings it back.
  • A run that already has a checklist cannot be deleted this way. Deleting the run would leave the checklist with no record of where it came from. Delete the checklist with DELETE /v3/checklists/{key} instead, and the run is removed with it.

Create a Run's Checklist Now​

Creates the checklist for a run straight away, without waiting for the scheduled time. It is also the way to recover a missed run.

POST /v3/schedules/{id}/runs/{runId}/checklist

Parameters​

NameInTypeRequiredDescription
idpathintegerYesThe schedule id.
runIdpathintegerYesThe run id.

Request Body​

This endpoint takes no request body.

Example​

Creating the 26 October checklist on 22 October:

POST https://api.checkflow.io/v3/schedules/318/runs/52110/checklist
X-API-KEY: your-api-key-here
HTTP/1.1 201 Created
{
"id": 52110,
"scheduleId": 318,
"status": "created",
"checklistName": "Invoice Review — 2026-10-26",
"scheduledDateTime": "2026-10-26T09:00:00",
"scheduledDateTimeUtc": "2026-10-26T09:00:00Z",
"timeZone": "Europe/London",
"checklist": {
"key": "e3b6f18c-9d42-4a7e-b5c0-2f8a6d91c437",
"name": "Invoice Review — 2026-10-26",
"url": "https://acme.checkflow.io/Checklist/Index?checklistKey=e3b6f18c-9d42-4a7e-b5c0-2f8a6d91c437"
},
"startedDateTime": "2026-10-22T10:15:42Z",
"checklistCreated": true
}

Sending the same request again returns 200 OK with the same run and "checklistCreated": false.

Responses​

StatusCodeWhen
201—This request created the checklist. checklistCreated is true.
200—The run already had a checklist, and nothing was done. checklistCreated is false.
400VALIDATION_ERRORfield is id or runId: not a positive integer.
403FORBIDDENThe key acts as a Member without Schedule.Creator, or without permission to run checklists from the schedule's template.
404SCHEDULE_NOT_FOUNDNo schedule with that id in this workspace.
404SCHEDULE_RUN_NOT_FOUNDThe schedule has no run with that id.
404TEMPLATE_NOT_FOUNDThe schedule's template no longer exists.
409CONFLICTThe schedule's template is archived. This is also why the schedule's runs are being missed.

Notes​

  • The request is safe to repeat: it asks for the state "this run has a checklist", and nothing is ever created twice. If the scheduling job reaches the same run at the same moment, the request discards its own checklist and returns 200 with the job's.
  • The checklist gets the run's checklistName and the schedule's parameter values, just as the job would give it.
  • The checklist starts now, and its scheduled time stays at the run's due time. So a checklist created a week early is not dated into the future. startedDateTime on the run shows the difference.
  • This works on an inactive schedule too.
  • The new-checklist webhook fires. Unlike a checklist the job creates, no team notification is sent.

MCP Equivalents​

Each route has an MCP tool, listed in Endpoints. The differences:

  • create_schedule and update_schedule take startDateTime as a string, with the same wall-clock rules.
  • In update_schedule, an empty checklistNameFormat resets the pattern to the template's name. Leaving an argument out leaves that field unchanged.
  • create_schedule accepts an idempotencyKey argument.

See Schedule Tools.

  • Create a Schedule — the same schedules on the Scheduling screen.
  • Managing Schedules — pausing, deleting and what happens when a template changes.
  • Checklists — reading and deleting the checklists a schedule creates.
  • Templates — finding a template's key and parameters.
  • Webhooks — being told when a scheduled checklist is created.