Skip to content
Dashboard

Task schedules

A task schedule, called a task rule in the app, creates tasks on its own. A reservation rule creates them from each stay at its properties, for example a turnover clean 60 minutes after every check-out. A calendar rule creates them on a repeat, on empty days between stays, or on days inside a stay. This guide covers the six task schedule operations. Every field is listed on the operation’s page in the API Reference.

OperationRequestPermission
listTaskSchedulesGET /task-schedulesview_tasks
createReservationTaskSchedulePOST /task-schedules/reservationmodify_tasks
createRecurringTaskSchedulePOST /task-schedules/recurringmodify_tasks
updateTaskSchedulePATCH /task-schedules/{id}modify_tasks
previewTaskScheduleActivationGET /task-schedules/{id}/activation-previewview_tasks
toggleTaskSchedulePOST /task-schedules/{id}/togglemodify_tasks

Paths are relative to your region’s base URL, for example https://api-us.suiteop.com/api/v1. See Authentication for permissions. The API can’t delete a task rule. Pause it instead.

A rule has three layers:

  • scopes: where it applies, at least one entry. Each entry has a scopeType of property, property_group or tag and exactly one matching ID: propertyId, propertyGroupId or tagId (a property tag from listTags). A group or tag also covers properties added to it later.
  • workflows: when it fires, at least one. A reservation rule takes reservation workflows only. A calendar rule takes repeat, vacancy or occupancy workflows, and can mix them.
  • items inside each workflow: the tasks it creates, at least one. Each occurrence creates one task per item.

Each item needs a departmentId (from listDepartments) and either a templateId (from listTemplates) or a plain taskName, never both. priority is watch, low, medium (the default), high or urgent. assignmentMode decides who gets each task:

assignmentModeAssignee
unassigned (default)Nobody
specificassigneeUserId, the person’s userId from listTeamMembers, or assigneeOrgConnectionId
autoThe department’s auto-assignment picks someone
property_defaultThe property’s default person

assigneeUserId and assigneeOrgConnectionId are rejected with any mode other than specific. collaborators adds people to every task as userIds, and is not allowed with a company assignee.

POST /task-schedules/reservation creates a rule driven by stays. Each workflow names an eventAnchor (check_in, check_out or confirmation, when the booking is made) and an offsetMinutes from it, between -525600 and 525600. A negative offset is before the anchor: -1440 is one day before check-in.

Terminal window
curl -X POST https://api-us.suiteop.com/api/v1/task-schedules/reservation \
-H "Authorization: Bearer sk_live_your_key_here" \
-H "Idempotency-Key: 4b7e2c9a-1f3d-4e8b-a6c5-9d0f2e1b3a78" \
-H "Content-Type: application/json" \
-d '{
"name": "Turnover clean after every check-out",
"scopes": [{ "scopeType": "property_group", "propertyGroupId": "2e6d0f5b-8a1c-4d3e-9b7f-6c0a2e4d8f13" }],
"stayMinNights": 2,
"workflows": [
{
"eventAnchor": "check_out",
"offsetMinutes": 60,
"items": [
{
"templateId": "9a3c5e7f-1b2d-4f6a-8c0e-3d5b7f9a1c24",
"departmentId": "0b6f2c1e-4d3a-4f8b-9c2e-7a1d5e6f8b90",
"priority": "high",
"assignmentMode": "auto"
}
]
}
]
}'

It answers 201 with the rule, paused:

{
"data": {
"id": "6e1f3a9c-2d4b-4c8e-9f7a-0b5d3e1c8a26",
"name": "Turnover clean after every check-out",
"description": null,
"type": "reservation",
"isActive": false,
"autoAssign": false,
"stayMinNights": 2,
"stayMaxNights": null,
"sourceRuleType": null,
"bookingChannels": null,
"createdAt": "2026-10-11T14:02:18.000Z",
"updatedAt": "2026-10-11T14:02:18.000Z",
"scopes": [
{
"id": "c4a8e2f6-0b1d-4e3a-9c5f-7d2b6e0a4c18",
"scopeType": "property_group",
"propertyId": null,
"propertyGroupId": "2e6d0f5b-8a1c-4d3e-9b7f-6c0a2e4d8f13",
"tagId": null
}
],
"workflows": [
{
"id": "f2b6d0a4-8c1e-4f3b-a7d9-5e0c2a6b8d31",
"triggerType": "reservation_event",
"name": null,
"rank": 0,
"eventAnchor": "check_out",
"offsetMinutes": 60,
"variant": "every",
"linkNextStayMaxGapNights": null,
"items": [
{
"id": "8d0f2b4a-6c3e-4a1d-9b7f-1e5c3a9d7b42",
"templateId": "9a3c5e7f-1b2d-4f6a-8c0e-3d5b7f9a1c24",
"taskName": null,
"departmentId": "0b6f2c1e-4d3a-4f8b-9c2e-7a1d5e6f8b90",
"priority": "high",
"rank": 0,
"assignmentMode": "auto",
"assigneeUserId": null,
"assigneeOrgConnectionId": null,
"collaborators": null
}
]
}
]
},
"meta": { "requestId": "…" }
}

Every workflow object carries the fields of all four kinds. The example leaves out the calendar fields, which are null or their defaults on a reservation workflow.

  • variant (check-out only): every (the default) fires on every departure, turn only when a guest arrives the same day, and no_turn only when nobody does.
  • linkNextStayMaxGapNights (check-out only): link the task to the next stay only when it arrives within this many nights, 0 for a same-day turn. Omit it to link whatever arrives next.
  • Stay conditions: stayMinNights and stayMaxNights (1 to 365) limit which stays fire the rule. bookingChannels with sourceRuleType (only_selected or all_except_selected) limits it by channel, such as ["airbnb", "vrbo"]. Send both channel fields or neither.

POST /task-schedules/recurring creates a rule driven by dates rather than stays. Each workflow’s kind is set by the keys you send, and sending keys of two kinds in one workflow is refused:

KindMarked byFires
RepeatrecurrenceTypedaily, weekly (on daysOfWeek, 0 = Sunday) or monthly (on dayOfMonth), every recurrenceInterval periods
VacancyvacancyMinGapDaysOn day vacancyCadenceDays of each empty gap between stays that matches the gap condition, then every vacancyCadenceDays days through the gap. vacancyRepeats: false fires once per gap
OccupancyoccupancyCadenceDaysOn day occupancyCadenceDays of each stay that matches the stay-length condition, then every occupancyCadenceDays days through the stay, for example a mid-stay clean. occupancyRepeats: false fires once per stay

A repeat workflow needs timeOfDay (HH:MM, 24-hour) and a startDate (YYYY-MM-DD). timeOfDay is read on each property’s own clock, its timezone from getProperty, so a 09:00 rule over properties in New York and Denver fires at 09:00 local in each. The workflow’s own timezone field is deprecated and has no effect, but keep sending a valid IANA zone such as America/New_York (on update, the one the rule already stores): an older API revision that can still serve your request requires it.

Terminal window
curl -X POST https://api-us.suiteop.com/api/v1/task-schedules/recurring \
-H "Authorization: Bearer sk_live_your_key_here" \
-H "Idempotency-Key: 1c8e4a2f-7b3d-4d9e-8f6a-2b0c5e7d9a13" \
-H "Content-Type: application/json" \
-d '{
"name": "Monday pool check",
"scopes": [{ "scopeType": "property", "propertyId": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d" }],
"workflows": [
{
"recurrenceType": "weekly",
"daysOfWeek": [1],
"timeOfDay": "09:00",
"timezone": "America/New_York",
"startDate": "2026-10-12",
"rescheduleIfOccupied": true,
"rescheduleTarget": "fully_vacant",
"rescheduleWindowDays": 3,
"rescheduleFallback": "keep_original",
"items": [{ "taskName": "Check pool chemistry", "departmentId": "5c2e8a1f-9b3d-4a6e-8f7c-0d1b2e3f4a5b" }]
}
]
}'
  • Rescheduling around guests: with rescheduleIfOccupied: true, an occurrence that lands on an occupied day moves to a nearby fully_vacant, check_out_only, check_in_only or turn day within rescheduleWindowDays. rescheduleFallback decides what happens when there is none: skip or keep_original. Send all three with the flag.
  • Seasons: startDate and endDate bound when a workflow may fire. With repeatAnnually: true only the month and day count, so 2026-11-01 to 2027-03-31 means every winter.
  • No duplicates: two repeat workflows that create the same task at the same moment, such as “every day 09:30” and “Mondays 09:30”, are refused with 400 validation_error. On create the key validation.recurrence_overlap is in an issue’s message; on update it is in error.message. The same task at two different times of day is allowed.
  • Times come back with seconds. A timeOfDay you send as "09:00" reads back as "09:00:00".

Both create operations save the rule paused unless you send startPaused: false. A paused rule creates nothing. Check it before you start it:

Terminal window
curl "https://api-us.suiteop.com/api/v1/task-schedules/6e1f3a9c-2d4b-4c8e-9f7a-0b5d3e1c8a26/activation-preview" \
-H "Authorization: Bearer sk_live_your_key_here"
{
"data": { "reservationCount": 38, "countIsUpperBound": false, "blockers": [] },
"meta": { "requestId": "…" }
}
  • reservationCount is how many upcoming stays would get tasks. It is always 0 for a calendar rule. countIsUpperBound: true means a turn or no_turn workflow will fire on only some of them.
  • blockers lists what is wrong with the rule’s setup, as message keys. Not every blocker stops it creating tasks: an item whose template was deleted is skipped while the rest still run, and an item with no assignee still creates its task, unassigned.
Blocker keyMeaning
validation.at_least_one_workflowA calendar rule has no workflow
validation.at_least_one_itemNo workflow has an item
validation.schedule_template_deletedAn item’s template was deleted
validation.specific_assignee_requiredAn item is specific but has no assignee
validation.schedule_no_propertiesThe rule no longer covers any property
validation.recurrence_cannot_generateA repeat can never fire, such as weekly with no weekday

Then start it:

Terminal window
curl -X POST "https://api-us.suiteop.com/api/v1/task-schedules/6e1f3a9c-2d4b-4c8e-9f7a-0b5d3e1c8a26/toggle" \
-H "Authorization: Bearer sk_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{"isActive": true}'
{
"data": { "id": "6e1f3a9c-2d4b-4c8e-9f7a-0b5d3e1c8a26", "isActive": true, "type": "reservation" },
"meta": { "requestId": "…" }
}
  • Always send isActive. true starts the rule and false pauses it, and a rule already in that state is returned unchanged. Omitting it flips the current state, so a retried call would undo the first.
  • Starting a rule with a blocker returns 400 validation_error. The message is the first blocker key and details.blockers lists them all.
  • Pausing never fails on a blocker. It creates nothing new and keeps the tasks already made.

GET /task-schedules is paginated (default limit 50, maximum 100) and ordered by name. Each row is the rule in the shape create returns, plus activationBlockers.

ParameterEffect
typereservation or recurring
kindNarrower: reservation, recurring (plain repeats) or vacancy_occupancy. Combines with type
searchCase-insensitive part of the rule name
departmentIdsRules with an item in one of these departments. Repeat the key for each ID
isActivetrue for running rules, false for paused ones

activationBlockers is filled in only for running rules: a running rule with a blocker has a setup problem, and depending on the blocker it may be creating some, all or none of its tasks. On a paused rule it is always [], even when the rule has blockers; use the preview for those.

PATCH /task-schedules/{id} takes a data object. Send only what changes: name, description (null clears it), scopes, autoAssign, the stay conditions, or workflows. Changes apply to the rule’s upcoming tasks at once, even while it runs. To start or pause it, use toggleTaskSchedule.

Terminal window
curl -X PATCH "https://api-us.suiteop.com/api/v1/task-schedules/6e1f3a9c-2d4b-4c8e-9f7a-0b5d3e1c8a26" \
-H "Authorization: Bearer sk_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{"data": {"name": "Turnover clean", "stayMinNights": null}}'
  • Rebuild each workflow in its create shape, not the listed row: only its own kind’s keys, and no triggerType or rank. A key from another kind is refused even at 0 or false. Items take their id back, which keeps their upcoming tasks. Only a repeat workflow accepts its own id, and ignores it; an id on a reservation, vacancy or occupancy workflow returns 400 validation_error.
  • A reservation rule accepts reservation workflows only, and a calendar rule only calendar workflows. Stay conditions on a calendar rule return 400 validation_error.
  • To clear the channel condition, send sourceRuleType: null and bookingChannels: null together.
  • An update that sends workflows, changes the stay conditions, or adds or drops a property in scopes can return 409 conflict_error with the message task.scheduler.concurrent_update when the rule’s tasks are being processed at that moment. Read the rule again and resend. Two saves of the same rule run one after the other.
  • Raw message keys. Task schedule refusals carry message keys rather than sentences. Input checks put them in details.issues[].message, for example validation.template_or_name_required on an item with neither a template nor a name, or validation.exactly_one_scope_value on a scope with the wrong ID. Refusals from the rule’s state, such as a start blocked by a blocker, and checks run against the stored rule on update, such as validation.recurrence_overlap, put the key in error.message. Match on the keys (see Errors).
  • Property-limited callers. An OAuth token carries its member’s property access. The list shows only rules that reach one of the member’s properties. Toggling a rule needs access to every property it covers, and so does an update that changes the stay conditions or sends workflows. A scopes update only changes the scope rows the member can write; the others are kept.
  • Idempotency. Send an Idempotency-Key on both create operations so a retry doesn’t make a second rule (see Idempotency).
  • No delete. A rule you no longer need is paused with toggleTaskSchedule and isActive: false; delete it in the app if you must.