Scheduling
Use the scheduling routes to read one or more days, to find conflict-free time for new work, and to undo a recent change. The server calculates the free slots for you.
The machine-readable spec of all public endpoints: openapi.json.
How scheduling works
- Read the days with
GET /schedule. The response gives the events, the free slots, and the load by area, activity type, and project. - Book work with
POST /schedule/plan. Each request gives a name, a duration, and an exact start or a time window. - A successful request books at once. An exact
startbooks there when it is free; a window books its top-ranked free slot. - Undo a change with
POST /actions/undoand theundoTokenof that change.
| Item | Lifetime | Use |
|---|---|---|
undoToken | 30 minutes. The response gives the exact time in expiresAt. | Send it to POST /actions/undo. You can use a token one time only. |
requestId | 60 seconds | A retry key for one request of POST /schedule/plan. |
POST /schedule/plan writes events immediately. To choose a time before booking, read GET /schedule and select a span from freeSlots. Keep the undoToken of each call that books an event.
MCP clients use get_schedule to read free slots, then write_events to create an event. Refer to Schedule new work.
Scopes
| Route | Scope |
|---|---|
GET /schedule | events:read or events:write |
POST /schedule/plan | events:write |
POST /actions/undo | events:write |
All routes need a trial or Pro plan. Refer to Authentication.
Rows and status codes of the write routes
The two write routes return results, with one row for each item. Each row has index (0-based), status (ok, error, or skipped), and result or error. An error is { code, message }, with optional detail fields. Refer to Error format. A skipped row has a reason.
- Plan is atomic by default. When one row fails, the server writes nothing. Each other row is
skipped, and the call has noundoToken. - Send
partial: trueto plan to write the rows that succeed. The failed rows are errors. - Undo has no
partialflag. Each token succeeds or fails alone. - When one or more rows are
ok, the status is200. The body hasresults. For plan, it also hastimezone, andundoTokenwithexpiresAtwhen the server recorded an undo receipt. For undo, it hasresultsonly. - When no row is
ok, Reassign rejects the call. The body is the error envelope andresults. The status followserror.code, as the Responses table of each route shows. - A body that is not valid gets
422with the codevalidationand noresults. - A plan row that replays an earlier call (refer to
requestId) staysokwithreplayed: true, also when another row fails. Thus the call is not rejected. - A missing or empty array (
requestsortokens) returns 422. More than 20 items also returns 422.
For the error codes, refer to Errors.
Read a schedule
Read one day or a range of days. The response gives the events, the free slots, and the load of each day. It also gives your areas, activity types, projects, and preferences. Scope: events:read.
fromandtoare required. For one day, send the same date in both.tomust not be beforefrom. The range is 366 days or less. Else the server returns 422.- Send each boolean parameter as
trueorfalse. Other values return 422. includeBacklog=trueadds the Inbox items inbacklog.backlogQuery,backlogPlannedOn,backlogProjectId,backlogMinPriority, andbacklogOffsetwork only withincludeBacklog=true. Without it, they return 422. Refer to Backlog.backlogProjectIdkeeps the Inbox items of one project. Send a project id, ornonefor the items with no project.backlogMinPrioritykeeps the Inbox items with a priority of this value or more, from 0 to 4.- The Inbox filters work together. With one or more filters, the response also has
backlogMatchedCount. includeSeries=trueadds the recurring series inseries. Refer to Series rows.includeLookups=falseremovesareas,activityTypes,projects, andintegrationsfrom the response. Use it when you have them from an earlier read. The default istrue.userPreferencesandtimezonestay.minDurationis an optional whole number from 1 to 1440, in minutes. For example,minDuration=90returns onlyfreeSlotsof at least 90 minutes. It keeps the full span of each qualifying gap and does not filter events.
The MCP tool get_schedule has separate weather and energy options. For REST, read those reports through GET /weather and GET /energy. The optional energy field in the shared response schema is used by MCP only.
What the response contains
timezoneis the IANA time zone of the account. Allstartandendvalues are local datetimes in this time zone.nowis the current local datetime, for example"2026-09-23T14:05".dayshas one item for each date. Each day hasevents(the event model) andfreeSlots(each astartandend). On today, the first free slot starts on the first 5-minute mark that is more than 5 minutes afternow. A write accepts that start.- Each occurrence of a series has
seriesIdandoriginalDate. Refer to Ids of recurring events. loadByAreaandloadByActivityTypegive the time of the blocking events of the day. Each bucket isareaId(oractivityTypeId),minutes, andeventCount.areaIdisnullfor the events with no area.loadByProjectgives the same buckets byprojectId.projectIdisnullfor the events with no project. The buckets start with the most minutes.- A read of more than one day also has a top-level
loadByProject: the sum of theloadByProjectbuckets of all days. An event on two days (across midnight) counts one time ineventCount. projectslists your projects:id,name, andareaId(ornull). An archived project also hasarchived: true. It keeps its links, but it refuses new links. Refer to Categories.nonBlockingLoadByAreaandnonBlockingLoadByActivityTypegive the same buckets for the events of the kindnon_blocking. Thereferenceevents are not in them. A day with nonon_blockingevent does not have these fields.- A day that has a confirmed review also has
review:reviewedAt(an ISO 8601 instant) and theadherencescores. A score can benull. Refer to Day reviews. userPreferences.conflictPolicyis alwayspropose. A write that overlaps a blocking event fails withconflictand givesnearestSlots. No setting changes this.integrationsis in the response when a calendar is connected. Every caller getsintegrations.sources[].account, the account name at the provider.integrations.sources[].calendars[].timezoneis the time zone of the calendar at the provider. It is for information only: all spans use the top-leveltimezone.integrations.defaultCalendarIdis the default calendar of a new event, and the default Source of a new Inbox item. It is absent when the default calendar is not writable or its source is not connected.backlogCountis in the response when the Inbox is not empty. WithincludeBacklog=true, it is always there.- A writable event calendar in
integrations.sources[].calendars[]also hascopyStyle:full,private, orbusy. It is the style of a copy on that calendar when themirrorStylesof the event does not name the calendar. WithcopyStyle: "busy",busyCopyTitleis the title of the copy.nullmeans "Busy". Refer to Copy styles. integrations.aiRuleWarningslists the problems in the AI rules of the user. Each item is an area or an activity type that the rules name. It does not exist, or its name matches more than one category. It has up to 20 items{ code, field, name }.codeismissing_taxonomy_referenceorambiguous_taxonomy_reference.fieldisareaoractivityType. The field is absent when there is no warning.
Series rows
With includeSeries=true, series has one row for each recurring series that obeys one of these conditions:
- The series has an occurrence in the range from
fromtoto. - The series has a
nextOccurrence. - The series has no end yet. A series with an open rule never ends, also when its next date is more than 366 days ahead.
A series ends when its UNTIL or COUNT leaves no date on or after today. The response does not include a series that ended outside the range.
| Field | Value |
|---|---|
id, name, recurrence | The series id, its name, and its RRULE |
start, end | The anchor span of the series |
nextOccurrence | The first date from today to 366 days ahead with an occurrence that has not ended at now. A moved occurrence counts on its actual date, not the original date in its id. Else null. |
overrides | { edited, deleted }: the occurrences that you changed or deleted |
areaId, activityTypeId | The id, or null |
kind, source | As on an event |
calendarId, mirrorCalendarIds, mirrorStyles, readOnly | As on an event, when they apply |
A series row has no notes. The events of the series have them.
Query parameters
- Name
from- Type
- string, required
- Description
First day of the range, inclusive.
- Name
to- Type
- string, required
- Description
Last day of the range, inclusive.
- Name
includeSeries- Type
- "true" | "false"
- Description
Also return the recurring masters as
series.
- Name
includeLookups- Type
- "true" | "false"
- Description
Default true. Pass false to leave out
areas,activityTypes,projectsandintegrationswhen you already have them from an earlier read.
- Name
minDuration- Type
- string
- Description
Return only the
freeSlotsof at least this many minutes.
- Name
includeBacklog- Type
- "true" | "false"
- Description
Include the Inbox items (the
backlogarray), not justbacklogCount. Pass true when the user wants to see, schedule, or manage their Inbox.
- Name
backlogQuery- Type
- string
- Description
Filter the Inbox blocks by name (fuzzy, case/accent-insensitive), e.g. "dentist". Needs includeBacklog: true.
- Name
backlogPlannedOn- Type
- string
- Description
Filter the Inbox blocks to those planned for this day (a planned day, or a planned window that covers it). Needs includeBacklog: true; composes with backlogQuery.
- Name
backlogProjectId- Type
- string
- Description
Filter the Inbox blocks to one project id, or "none" for the blocks with no project. Needs includeBacklog: true; composes with the other filters.
- Name
backlogMinPriority- Type
- string
- Description
Filter the Inbox blocks to a priority of at least this value (0 none, 1 low, 2 medium, 3 high, 4 urgent). Needs includeBacklog: true; composes with the other filters.
- Name
backlogOffset- Type
- string
- Description
Inbox page offset from nextBacklogOffset; keep the same filters. Needs includeBacklog: true.
Response fields
- Name
timezone- Type
- string, required
- Description
- Name
now- Type
- string, required
- Description
- Name
weather- Type
- string
- Description
- Name
energy- Type
- string
- Description
- Name
days- Type
- object[], required
- Description
9 fields
- Name
date- Type
- string, required
- Description
- Name
events- Type
- object[], required
- Description
26 fields
- Name
start- Type
- string, required
- Description
- Name
end- Type
- string, required
- Description
- Name
id- Type
- string, required
- Description
- Name
seriesId- Type
- string
- Description
- Name
originalDate- Type
- string
- Description
- Name
kind- Type
- "blocking" | "non_blocking" | "reference", required
- Description
- Name
name- Type
- string, required
- Description
- Name
notes- Type
- string
- Description
- Name
source- Type
- "google" | "google_tasks" | "linear" | "microsoft" | "microsoft_todo" | "ticktick" | "todoist" | "reassign", required
- Description
- Name
sourceUrl- Type
- string
- Description
- Name
meeting- Type
- object
- Description
2 fields
- Name
url- Type
- string, required
- Description
- Name
label- Type
- string, required
- Description
- Name
location- Type
- object
- Description
2 fields
- Name
text- Type
- string, required
- Description
- Name
url- Type
- string
- Description
- Name
recurrence- Type
- string
- Description
- Name
warning- Type
- string
- Description
- Name
areaId- Type
- string | null, required
- Description
- Name
activityTypeId- Type
- string | null, required
- Description
- Name
projectId- Type
- string | null, required
- Description
- Name
priority- Type
- integer, required
- Description
- Name
calendarId- Type
- string | null
- Description
- Name
mirrorCalendarIds- Type
- string[]
- Description
- Name
mirrorStyles- Type
- map of "full" | "private" | "busy"
- Description
- Name
readOnly- Type
- true
- Description
- Name
focusIntervals- Type
- object
- Description
2 fields
- Name
focusMin- Type
- integer, required
- Description
- Name
breakMin- Type
- integer, required
- Description
- Name
completedFocusIntervals- Type
- integer
- Description
- Name
checklist- Type
- object
- Description
1 fields
- Name
items- Type
- object[], required
- Description
3 fields
- Name
id- Type
- string, required
- Description
- Name
text- Type
- string, required
- Description
- Name
done- Type
- boolean, required
- Description
- Name
reflect- Type
- object
- Description
2 forms
One form: Without
actualStart,actualEnd- Name
status- Type
- "kept" | "skipped" | "changed" | "added", required
- Description
One form: With
actualStart,actualEnd- Name
status- Type
- "changed", required
- Description
- Name
actualStart- Type
- string, required
- Description
- Name
actualEnd- Type
- string, required
- Description
- Name
freeSlots- Type
- object[], required
- Description
2 fields
- Name
start- Type
- string, required
- Description
- Name
end- Type
- string, required
- Description
- Name
loadByArea- Type
- object[], required
- Description
3 fields
- Name
areaId- Type
- string | null, required
- Description
- Name
minutes- Type
- integer, required
- Description
- Name
eventCount- Type
- integer, required
- Description
- Name
loadByActivityType- Type
- object[], required
- Description
3 fields
- Name
activityTypeId- Type
- string | null, required
- Description
- Name
minutes- Type
- integer, required
- Description
- Name
eventCount- Type
- integer, required
- Description
- Name
loadByProject- Type
- object[], required
- Description
3 fields
- Name
projectId- Type
- string | null, required
- Description
- Name
minutes- Type
- integer, required
- Description
- Name
eventCount- Type
- integer, required
- Description
- Name
nonBlockingLoadByArea- Type
- object[]
- Description
3 fields
- Name
areaId- Type
- string | null, required
- Description
- Name
minutes- Type
- integer, required
- Description
- Name
eventCount- Type
- integer, required
- Description
- Name
nonBlockingLoadByActivityType- Type
- object[]
- Description
3 fields
- Name
activityTypeId- Type
- string | null, required
- Description
- Name
minutes- Type
- integer, required
- Description
- Name
eventCount- Type
- integer, required
- Description
- Name
review- Type
- object
- Description
2 fields
- Name
reviewedAt- Type
- string, required
- Description
- Name
adherence- Type
- object
- Description
6 fields
- Name
event- Type
- number | null, required
- Description
- Name
layer- Type
- number | null, required
- Description
- Name
plannedMinutes- Type
- integer, required
- Description
- Name
unplannedMinutes- Type
- integer, required
- Description
- Name
byArea- Type
- object[], required
- Description
3 fields
- Name
areaId- Type
- string | null, required
- Description
- Name
adherence- Type
- number, required
- Description
- Name
plannedMinutes- Type
- integer, required
- Description
- Name
byActivityType- Type
- object[], required
- Description
3 fields
- Name
activityTypeId- Type
- string | null, required
- Description
- Name
adherence- Type
- number, required
- Description
- Name
plannedMinutes- Type
- integer, required
- Description
- Name
loadByProject- Type
- object[]
- Description
3 fields
- Name
projectId- Type
- string | null, required
- Description
- Name
minutes- Type
- integer, required
- Description
- Name
eventCount- Type
- integer, required
- Description
- Name
areas- Type
- object[]
- Description
3 fields
- Name
id- Type
- string, required
- Description
- Name
name- Type
- string, required
- Description
- Name
color- Type
- string, required
- Description
- Name
activityTypes- Type
- object[]
- Description
3 fields
- Name
id- Type
- string, required
- Description
- Name
name- Type
- string, required
- Description
- Name
pattern- Type
- "solid" | "hatch_r" | "hatch_l" | "cross" | "horizontal" | "vertical" | "grid" | "dots" | "waves" | "chevron", required
- Description
- Name
projects- Type
- object[]
- Description
4 fields
- Name
id- Type
- string, required
- Description
- Name
name- Type
- string, required
- Description
- Name
areaId- Type
- string | null, required
- Description
- Name
archived- Type
- true
- Description
Present on an archived project: it refuses new links, and an update with archived: false restores it.
- Name
userPreferences- Type
- object, required
- Description
1 fields
- Name
conflictPolicy- Type
- "propose", required
- Description
Always
propose: a write that collides is refused with free slots.
- Name
integrations- Type
- object
- Description
5 fields
- Name
aiClassify- Type
- boolean, required
- Description
- Name
aiRules- Type
- string
- Description
- Name
aiRuleWarnings- Type
- object[]
- Description
3 fields
- Name
code- Type
- "missing_taxonomy_reference" | "ambiguous_taxonomy_reference", required
- Description
- Name
field- Type
- "area" | "activityType", required
- Description
- Name
name- Type
- string, required
- Description
- Name
defaultCalendarId- Type
- string
- Description
- Name
sources- Type
- object[], required
- Description
4 fields
- Name
provider- Type
- "google" | "google_tasks" | "linear" | "microsoft" | "microsoft_todo" | "ticktick" | "todoist", required
- Description
- Name
account- Type
- string
- Description
- Name
status- Type
- "connected" | "disconnected" | "error" | "pending" | "revoked", required
- Description
- Name
calendars- Type
- object[], required
- Description
9 fields
- Name
id- Type
- string, required
- Description
- Name
name- Type
- string, required
- Description
- Name
writable- Type
- boolean, required
- Description
- Name
area- Type
- object, required
- Description
2 forms
One form:
mode: "automatic"One form:
mode: "fixed"- Name
areaId- Type
- string, required
- Description
- Name
activityType- Type
- object, required
- Description
3 forms
One form:
mode: "automatic"One form:
mode: "fixed"- Name
activityTypeId- Type
- string, required
- Description
One form:
mode: "none"
- Name
kind- Type
- object, required
- Description
2 forms
One form:
mode: "automatic"One form:
mode: "fixed"- Name
kind- Type
- "blocking" | "non_blocking" | "reference", required
- Description
- Name
timezone- Type
- string
- Description
- Name
copyStyle- Type
- "full" | "private" | "busy"
- Description
- Name
busyCopyTitle- Type
- string | null
- Description
- Name
series- Type
- object[]
- Description
15 fields
- Name
start- Type
- string, required
- Description
- Name
end- Type
- string, required
- Description
- Name
id- Type
- string, required
- Description
- Name
name- Type
- string, required
- Description
- Name
recurrence- Type
- string, required
- Description
- Name
nextOccurrence- Type
- string | null, required
- Description
- Name
overrides- Type
- object, required
- Description
2 fields
- Name
edited- Type
- integer, required
- Description
- Name
deleted- Type
- integer, required
- Description
- Name
areaId- Type
- string | null, required
- Description
- Name
activityTypeId- Type
- string | null, required
- Description
- Name
kind- Type
- "blocking" | "non_blocking" | "reference", required
- Description
- Name
source- Type
- "google" | "google_tasks" | "linear" | "microsoft" | "microsoft_todo" | "ticktick" | "todoist" | "reassign", required
- Description
- Name
calendarId- Type
- string | null
- Description
- Name
mirrorCalendarIds- Type
- string[]
- Description
- Name
mirrorStyles- Type
- map of "full" | "private" | "busy"
- Description
- Name
readOnly- Type
- true
- Description
- Name
backlogCount- Type
- integer
- Description
- Name
backlogMatchedCount- Type
- integer
- Description
- Name
nextBacklogOffset- Type
- integer | null
- Description
- Name
backlog- Type
- object[]
- Description
13 fields
- Name
id- Type
- string, required
- Description
- Name
name- Type
- string, required
- Description
- Name
notes- Type
- string
- Description
- Name
sourceUrl- Type
- string
- Description
- Name
kind- Type
- "blocking" | "non_blocking" | "reference", required
- Description
- Name
durationMinutes- Type
- integer
- Description
- Name
areaId- Type
- string | null, required
- Description
- Name
activityTypeId- Type
- string | null, required
- Description
- Name
projectId- Type
- string | null, required
- Description
- Name
priority- Type
- integer, required
- Description
- Name
plannedDate- Type
- string
- Description
- Name
plannedUntil- Type
- string
- Description
- Name
checklist- Type
- object
- Description
1 fields
- Name
items- Type
- object[], required
- Description
2 fields
- Name
id- Type
- string, required
- Description
- Name
text- Type
- string, required
- Description
Responses
| Status | Codes | Meaning |
|---|---|---|
200 | — | Success. |
401 | unauthorized | No Bearer token and no session cookie, or the token is invalid, expired, or revoked. The response carries a WWW-Authenticate challenge that points at the protected-resource metadata. |
403 | permissionscope | A token without events:read or events:write gets scope "This token is not authorized to read events (requires events:read or events:write scope)." An account without Reassign Pro access gets permission "Reassign needs an active subscription." The 7-day free trial includes Reassign Pro. A cookie session from another site gets permission "Cross-origin request." |
422 | validation | A query parameter is not valid. The message names the first bad field by its path. A repeated or unknown query parameter is also a 422. |
429 | rate_limited | Too many requests. The edge firewall limits each IP to 2000 requests per minute across /api/v1; that 429 comes from the edge and can have a body that is not this JSON and no Retry-After header. When an edge 429 has no Retry-After, retry after 60 seconds. The app also limits each Bearer credential to 600 requests per minute, and some routes limit each user. These limits return this body. This app 429 always has Retry-After: retry after that number of seconds. |
503 | internal | A fault on our side. The request is safe to retry. |
Request
curl "https://reassign.app/api/v1/schedule?from=2026-08-14&to=2026-08-14" \
-H "Authorization: Bearer rsk_live_YOUR_TOKEN"Response
{
"timezone": "Europe/Ljubljana",
"now": "2026-08-14T10:42",
"days": [
{
"date": "2026-08-14",
"events": [
{
"id": "b7f1c2a0-1d2e-4f3a-9b8c-5d6e7f801234",
"start": "2026-08-14T09:00",
"end": "2026-08-14T10:30",
"kind": "blocking",
"name": "Team sync",
"source": "google",
"meeting": {
"url": "https://us02web.zoom.us/j/1234567890",
"label": "Zoom"
},
"location": {
"text": "Room 4, 2nd floor",
"url": "https://www.google.com/maps/search/?api=1&query=Room%204%2C%202nd%20floor"
},
"areaId": "3c9d2a41-6b7e-4f80-9a1b-2c3d4e5f6071",
"activityTypeId": "5e6f7081-92a3-4b4c-8d5e-6f708192a3b4",
"projectId": null,
"priority": 0,
"calendarId": "8a1f0c2e-3b4d-4e5f-9a0b-1c2d3e4f5061",
"mirrorCalendarIds": [
"a1b2c3d4-5e6f-4071-8293-a4b5c6d7e8f9"
]
}
],
"freeSlots": [
{
"start": "2026-08-14T10:45",
"end": "2026-08-15T00:00"
}
],
"loadByArea": [
{
"areaId": "3c9d2a41-6b7e-4f80-9a1b-2c3d4e5f6071",
"minutes": 90,
"eventCount": 1
}
],
"loadByActivityType": [
{
"activityTypeId": "5e6f7081-92a3-4b4c-8d5e-6f708192a3b4",
"minutes": 90,
"eventCount": 1
}
],
"loadByProject": [
{
"projectId": null,
"minutes": 90,
"eventCount": 1
}
]
}
],
"areas": [
{
"id": "3c9d2a41-6b7e-4f80-9a1b-2c3d4e5f6071",
"name": "Work",
"color": "#3b82f6"
}
],
"activityTypes": [
{
"id": "5e6f7081-92a3-4b4c-8d5e-6f708192a3b4",
"name": "Focus",
"pattern": "solid"
}
],
"projects": [],
"userPreferences": {
"conflictPolicy": "propose"
},
"integrations": {
"aiClassify": true,
"defaultCalendarId": "8a1f0c2e-3b4d-4e5f-9a0b-1c2d3e4f5061",
"sources": [
{
"provider": "google",
"account": "user@example.com",
"status": "connected",
"calendars": [
{
"id": "8a1f0c2e-3b4d-4e5f-9a0b-1c2d3e4f5061",
"name": "Work",
"writable": true,
"area": {
"mode": "automatic"
},
"activityType": {
"mode": "automatic"
},
"kind": {
"mode": "automatic"
}
},
{
"id": "a1b2c3d4-5e6f-4071-8293-a4b5c6d7e8f9",
"name": "Team",
"writable": true,
"area": {
"mode": "automatic"
},
"activityType": {
"mode": "automatic"
},
"kind": {
"mode": "automatic"
}
}
]
}
]
},
"backlogCount": 1
}Plan work into free time
Place one or more events into free time. You do not calculate the slots. Send 1 to 20 requests. Scope: events:write.
The requests run in their order. A later request does not overlap an event that an earlier request booked.
The call is atomic by default. When one request fails, the server books nothing. Send partial: true to book the requests that succeed.
Two forms of a request
Each request has name (1 to 200 characters) and durationMinutes (a whole number from 5 to 1440). It also has one of these two forms:
| Form | Fields | What the server does |
|---|---|---|
| Fixed start | start | When the time is free, the server books the event. When the time overlaps a blocking event, or a request before it in the same call, the row fails with conflict. The row gives conflicts and up to 5 nearestSlots in the next 7 days. The server does not move an exact start to another time. |
| Window | earliest and latest | The server searches the window. The window is at most 24 hours, and it can go past midnight. The server books the top-ranked free slot immediately. When no slot fits, the row fails with conflict. |
- All times are local datetimes
"YYYY-MM-DDTHH:MM". autoCommitBestis deprecated. A window acceptstrueor an omitted field, with the same behavior.falsefails withvalidation.- A request with
startand alsoearliest,latest, orautoCommitBestis an error. - A request can also have
kindandrecurrence, as on an eventcreate. A fixed-start request withkind: "non_blocking"orkind: "reference"does not fail withconflict. A blocking fixed-start request withrecurrencefails withconflictwhen an occurrence overlaps a blocking event. - A start or a window in the past is an error. On today, the start must be more than 5 minutes after now. A window with no room for a 5-minute slot after that point fails with
validation, notconflict. - A window slot starts on a 30-minute mark. When no 30-minute mark fits, it starts at the start of a free gap (a 5-minute mark).
- A nearest slot can start at one of these times: a 15-minute mark, the start of a free gap, or the latest start that fits in a free gap. It can also start at the free minute nearest to the requested start. It keeps the exact
durationMinutes. This rule applies to eachnearestSlotslist, also inPOST /eventsandPOST /backlog. - An unknown key fails the row with
validation:Unrecognized key(s): <keys>.When the key belongs to the other form, the message names that form. calendarId,mirrorCalendarIds, andmirrorStyleswork as on an event create.nullkeeps the event on the dial only. When you omitcalendarId, the event goes to the default calendar if there is one. Else it stays on the dial only. A request withrecurrenceandmirrorCalendarIdsneeds a home calendar. Refer to Rules of the fields.mirrorStylessets the style of each copy. Each key must be in themirrorCalendarIdsof the request.{}is the same as no map. Refer to Copy styles.areaIdandactivityTypeIdtake an id.nulland an omitted key are the same. An id that does not exist, or that you cannot see, fails the row withnot_found.projectIdtakes the id of a live project.nulland an omitted key are the same. An empty string fails the row withvalidation. An archived or unknown id fails the row withnot_found. Inresult.event,projectIdis the stored form of the id, also when you send it braced or in upper case.- With no
areaId, the event takes the area of the project. AnareaIdin the request wins. When the area of the project is hidden, the event gets no area. Refer to Rules of the fields. priorityis a whole number:0none,1low,2medium,3high,4urgent. An omitted value is0.null, a number that is not whole, or a value out of the range fails the row withvalidation.- The server checks
areaIdandactivityTypeIdbeforeprojectId. When a request has an unknown area and an unknown project, the row error names the area.
Results
Each successful row has event, the booked event, in result.
- An exact-start
conflictrow givesnearestSlots. To take one, send itsstartas a new request. - When the call books one or more events and records an undo receipt, the body has one
undoTokenand itsexpiresAt. This token undoes all bookings of the call. If the server cannot record the undo, the bookings stay and the response has noundoToken.
Row errors
A request that is not valid has status: "error" and the code validation. In the default atomic mode, the other rows are skipped, and the server rejects the call. With partial: true, the other requests still run, and the call returns 200 when one or more rows are ok.
A validation row from a schema check also has error.issues. Later checks, such as a start in the past or a series with copies and no home calendar, can return validation without issues. Refer to Validation errors.
A body that is not valid returns 422 with no results, for example when requests is empty or has more than 20 items.
Some row errors:
| Cause | error.message | error.code |
|---|---|---|
| A name of spaces only | Event name cannot be blank. | validation |
| A window longer than 24 hours | The window from `earliest` to `latest` can be at most 24 hours. | validation |
A window where latest is not after earliest | `latest` must be a local datetime after `earliest`. | validation |
A request with recurrence and mirrorCalendarIds, and no home calendar | Copies of a series need a home calendar. Give the series a calendarId, or remove its mirrorCalendarIds. | validation |
A mirrorStyles key that is not in mirrorCalendarIds | mirrorStyles has calendar "<id>", which is not in the event's mirrorCalendarIds. Add it to mirrorCalendarIds, or remove it from mirrorStyles. | validation |
| A fixed start in the past | <start> is in the past. Pick a later time. | validation |
| A fixed start today, 5 minutes or less after now | <start> is too soon: a start today must be more than 5 minutes after now. Pick a later time. | validation |
| A window in the past, or with no room after now | The window <earliest> to <latest> is in the past or starts too soon: a start today must be more than 5 minutes after now. Pick a later window. | validation |
| A taken fixed start | <start> is taken. Pick another time or one of the nearest free slots. | conflict |
| A taken fixed start with no free slot in the next 7 days | <start> is taken and no free <n>-minute slot fits through <date> (the next 7 days). | conflict |
An unknown areaId | Area "<id>" not found or not accessible. | not_found |
An unknown activityTypeId | Activity type "<id>" not found or not accessible. | not_found |
An unknown or archived projectId | No live project with id "<id>". | not_found |
When all rows fail with not_found, the status is 404.
A window schema error does not repeat the values that you sent. When earliest or latest has a bad format, its issue has expected: "YYYY-MM-DDTHH:MM"; the schema skips the window-order and length checks until both values have valid formats. Later errors, such as a window in the past or a window with no free slot, include the window's times.
Retries with requestId
Give each request a requestId. When you send the same requestId again in 60 seconds, the server does not book a second event. It returns the first result with replayed: true, and it writes nothing new.
- The retry must send the same content. A request with different content is a new request.
mirrorStylesis content. The order of the keys inmirrorStylesdoes not change the match.{}and no map match. The order ofmirrorCalendarIdschanges the match.projectIdandpriorityare content. A retry with another priority is a new request.priority: 0,projectId: null,areaId: null, andactivityTypeId: nullmatch an omitted key.priority: nullis not valid.- The server compares the ids as you sent them. Send each id in the same form in a retry.
- Only a booked result can replay. The server does not replay a result that you undid.
- Omitting
autoCommitBestor sendingtruedoes not change retry matching. Clients can drop the deprecated field while keeping the samerequestId. - Only a
requestIdreplays a result. A request with norequestIdis always a new request. - When one call sends the same
requestIdin more than one request, the server replays an earlier result for one request only. The other requests run as new requests.
Request body
- Name
requests- Type
- object[], required
- Description
2 forms
One form: With
startAn exact request: book at
start.- Name
name- Type
- string, required
- Description
Event title, e.g. "Deep work", "Gym".
- Name
durationMinutes- Type
- integer, required
- Description
How long, in whole minutes, e.g. 90.
- Name
areaId- Type
- string | null
- Description
Area id to attach (from the schedule read's
areas); null or omitted leaves it unset.
- Name
activityTypeId- Type
- string | null
- Description
Activity-type id to attach (from the schedule read's
activityTypes); null or omitted leaves it unset.
- Name
projectId- Type
- string | null
- Description
A live project id from get_schedule
projects. When the project changes and the op sets no areaId, the event takes the area of the project. null or omitted leaves it unset.
- Name
priority- Type
- integer
- Description
0 none, 1 low, 2 medium, 3 high, 4 urgent. Omit for 0.
- Name
recurrence- Type
- string
- Description
Repeat rule as an RRULE, e.g. "FREQ=WEEKLY;BYDAY=MO"; omit for a one-off.
- Name
kind- Type
- "blocking" | "non_blocking" | "reference"
- Description
"blocking" (default) cannot overlap others; "non_blocking" is an overlay (e.g. a fasting band or an away/OOO marker) that may overlap and never conflicts; "reference" is something the user wants to SEE but is not doing — non-blocking, and don't move, delete, or schedule work into it unless asked. Note a non-blocking request still SEARCHES for a free slot like any other; it simply does not occupy one once placed.
- Name
notes- Type
- string
- Description
- Name
sourceUrl- Type
- string
- Description
Where this was captured from — the http(s) page address. Shown as a source chip; never parsed. Set it only for something grabbed off a real page, not for an intention the user typed.
- Name
calendarId- Type
- string | null
- Description
The home calendar: a calendar id (from the calendars list or the schedule's
integrations). Omit it to use the account's default calendar if one is set, else the event stays on the dial only. null keeps the event on the dial only. A calendar id requires Reassign Pro; whole-series only.
- Name
mirrorCalendarIds- Type
- string[]
- Description
The one-way copy calendars, as calendar ids. It must not contain the home
calendarId. On update it REPLACES the set; [] clears it. An id already on the event may stay even if its calendar is read-only now; only an added id is checked. Adding an id requires Reassign Pro; whole-series only.
- Name
mirrorStyles- Type
- map of "full" | "private" | "busy"
- Description
How much each copy shows, per copy calendar id: 'full' (default) copies the title and notes; 'private' keeps them but marks the copy private; 'busy' is a shield: a private copy with a neutral title (the calendar's busy title, else "Busy"), no notes, and no reminders. Each key must be in the event's mirrorCalendarIds. A calendar left out uses that calendar's default copy style. On update it REPLACES the map; {} clears it. Needs Reassign Pro; whole-series only.
- Name
requestId- Type
- string
- Description
Idempotency key; reuse on retry to avoid double-booking. Only this key replays: a request without it is always booked as new.
- Name
start- Type
- string, required
- Description
Exact local start "YYYY-MM-DDTHH:MM"; place there. A taken start is a
conflicterror withnearestSlots.
One form: With
earliest,latestA window request: search from
earliesttolatest, at most 24 hours.- Name
name- Type
- string, required
- Description
Event title, e.g. "Deep work", "Gym".
- Name
durationMinutes- Type
- integer, required
- Description
How long, in whole minutes, e.g. 90.
- Name
areaId- Type
- string | null
- Description
Area id to attach (from the schedule read's
areas); null or omitted leaves it unset.
- Name
activityTypeId- Type
- string | null
- Description
Activity-type id to attach (from the schedule read's
activityTypes); null or omitted leaves it unset.
- Name
projectId- Type
- string | null
- Description
A live project id from get_schedule
projects. When the project changes and the op sets no areaId, the event takes the area of the project. null or omitted leaves it unset.
- Name
priority- Type
- integer
- Description
0 none, 1 low, 2 medium, 3 high, 4 urgent. Omit for 0.
- Name
recurrence- Type
- string
- Description
Repeat rule as an RRULE, e.g. "FREQ=WEEKLY;BYDAY=MO"; omit for a one-off.
- Name
kind- Type
- "blocking" | "non_blocking" | "reference"
- Description
"blocking" (default) cannot overlap others; "non_blocking" is an overlay (e.g. a fasting band or an away/OOO marker) that may overlap and never conflicts; "reference" is something the user wants to SEE but is not doing — non-blocking, and don't move, delete, or schedule work into it unless asked. Note a non-blocking request still SEARCHES for a free slot like any other; it simply does not occupy one once placed.
- Name
notes- Type
- string
- Description
- Name
sourceUrl- Type
- string
- Description
Where this was captured from — the http(s) page address. Shown as a source chip; never parsed. Set it only for something grabbed off a real page, not for an intention the user typed.
- Name
calendarId- Type
- string | null
- Description
The home calendar: a calendar id (from the calendars list or the schedule's
integrations). Omit it to use the account's default calendar if one is set, else the event stays on the dial only. null keeps the event on the dial only. A calendar id requires Reassign Pro; whole-series only.
- Name
mirrorCalendarIds- Type
- string[]
- Description
The one-way copy calendars, as calendar ids. It must not contain the home
calendarId. On update it REPLACES the set; [] clears it. An id already on the event may stay even if its calendar is read-only now; only an added id is checked. Adding an id requires Reassign Pro; whole-series only.
- Name
mirrorStyles- Type
- map of "full" | "private" | "busy"
- Description
How much each copy shows, per copy calendar id: 'full' (default) copies the title and notes; 'private' keeps them but marks the copy private; 'busy' is a shield: a private copy with a neutral title (the calendar's busy title, else "Busy"), no notes, and no reminders. Each key must be in the event's mirrorCalendarIds. A calendar left out uses that calendar's default copy style. On update it REPLACES the map; {} clears it. Needs Reassign Pro; whole-series only.
- Name
requestId- Type
- string
- Description
Idempotency key; reuse on retry to avoid double-booking. Only this key replays: a request without it is always booked as new.
- Name
earliest- Type
- string, required
- Description
Start of the local "YYYY-MM-DDTHH:MM" window to search.
- Name
latest- Type
- string, required
- Description
End of the search window: after
earliestand at most 24 hours later; it may be on the next day.
- Name
autoCommitBest- Type
- true
- Description
Deprecated: a window always books its top-ranked free slot. Accepted so that older clients still work.
- Name
partial- Type
- boolean
- Description
Best-effort mode: apply the rows that succeed and report the rest as errors, instead of the default all-or-nothing.
Response fields
- Name
timezone- Type
- string
- Description
- Name
undoToken- Type
- string
- Description
- Name
expiresAt- Type
- string
- Description
- Name
results- Type
- object[], required
- Description
3 forms
One form:
status: "ok"- Name
index- Type
- integer, required
- Description
- Name
result- Type
- object, required
- Description
2 fields
- Name
event- Type
- object, required
- Description
26 fields
- Name
start- Type
- string, required
- Description
- Name
end- Type
- string, required
- Description
- Name
id- Type
- string, required
- Description
- Name
seriesId- Type
- string
- Description
- Name
originalDate- Type
- string
- Description
- Name
kind- Type
- "blocking" | "non_blocking" | "reference", required
- Description
- Name
name- Type
- string, required
- Description
- Name
notes- Type
- string
- Description
- Name
source- Type
- "google" | "google_tasks" | "linear" | "microsoft" | "microsoft_todo" | "ticktick" | "todoist" | "reassign", required
- Description
- Name
sourceUrl- Type
- string
- Description
- Name
meeting- Type
- object
- Description
2 fields
- Name
url- Type
- string, required
- Description
- Name
label- Type
- string, required
- Description
- Name
location- Type
- object
- Description
2 fields
- Name
text- Type
- string, required
- Description
- Name
url- Type
- string
- Description
- Name
recurrence- Type
- string
- Description
- Name
warning- Type
- string
- Description
- Name
areaId- Type
- string | null, required
- Description
- Name
activityTypeId- Type
- string | null, required
- Description
- Name
projectId- Type
- string | null, required
- Description
- Name
priority- Type
- integer, required
- Description
- Name
calendarId- Type
- string | null
- Description
- Name
mirrorCalendarIds- Type
- string[]
- Description
- Name
mirrorStyles- Type
- map of "full" | "private" | "busy"
- Description
- Name
readOnly- Type
- true
- Description
- Name
focusIntervals- Type
- object
- Description
2 fields
- Name
focusMin- Type
- integer, required
- Description
- Name
breakMin- Type
- integer, required
- Description
- Name
completedFocusIntervals- Type
- integer
- Description
- Name
checklist- Type
- object
- Description
1 fields
- Name
items- Type
- object[], required
- Description
3 fields
- Name
id- Type
- string, required
- Description
- Name
text- Type
- string, required
- Description
- Name
done- Type
- boolean, required
- Description
- Name
reflect- Type
- object
- Description
2 forms
One form: Without
actualStart,actualEnd- Name
status- Type
- "kept" | "skipped" | "changed" | "added", required
- Description
One form: With
actualStart,actualEnd- Name
status- Type
- "changed", required
- Description
- Name
actualStart- Type
- string, required
- Description
- Name
actualEnd- Type
- string, required
- Description
- Name
replayed- Type
- true
- Description
- Name
warnings- Type
- "classification_pending"[]
- Description
One form:
status: "error"- Name
index- Type
- integer, required
- Description
- Name
error- Type
- object, required
- Description
11 fields
- Name
code- Type
- "conflict" | "not_found" | "validation" | "permission" | "scope" | "stale" | "read_only" | "rate_limited" | "internal", required
- Description
- Name
message- Type
- string, required
- Description
- Name
issues- Type
- object[]
- Description
3 fields
- Name
path- Type
- string | integer[], required
- Description
- Name
message- Type
- string, required
- Description
- Name
expected- Type
- string
- Description
- Name
feature- Type
- string
- Description
- Name
conflictingRows- Type
- object[]
- Description
3 fields
- Name
entity- Type
- string, required
- Description
- Name
id- Type
- string, required
- Description
- Name
reason- Type
- "changed" | "deleted" | "newly_present", required
- Description
- Name
conflictingRowsTruncated- Type
- true
- Description
- Name
usage- Type
- object
- Description
5 fields
- Name
events- Type
- integer, required
- Description
- Name
inboxItems- Type
- integer, required
- Description
- Name
trackers- Type
- integer, required
- Description
- Name
calendars- Type
- integer, required
- Description
- Name
projects- Type
- integer
- Description
- Name
reassignTargets- Type
- object[]
- Description
2 fields
- Name
id- Type
- string, required
- Description
- Name
name- Type
- string, required
- Description
- Name
unlinkedCheckout- Type
- object
- Description
3 fields
- Name
sessionId- Type
- string, required
- Description
- Name
paymentReceived- Type
- boolean, required
- Description
- Name
supportUrl- Type
- string, required
- Description
- Name
conflicts- Type
- object[]
- Description
2 forms
One form: With
id- Name
start- Type
- string, required
- Description
- Name
end- Type
- string, required
- Description
- Name
id- Type
- string, required
- Description
- Name
name- Type
- string, required
- Description
One form: With
batchIndex- Name
start- Type
- string, required
- Description
- Name
end- Type
- string, required
- Description
- Name
batchIndex- Type
- integer, required
- Description
- Name
name- Type
- string, required
- Description
- Name
nearestSlots- Type
- object[]
- Description
4 fields
- Name
start- Type
- string, required
- Description
- Name
end- Type
- string, required
- Description
- Name
score- Type
- number
- Description
- Name
reason- Type
- string
- Description
One form:
status: "skipped"- Name
index- Type
- integer, required
- Description
- Name
reason- Type
- string, required
- Description
Responses
| Status | Codes | Meaning |
|---|---|---|
200 | — | Success. |
400 | batch_rejected | Every op failed, and the failed ops have different codes. The code is batch_rejected. |
401 | unauthorized | No Bearer token and no session cookie, or the token is invalid, expired, or revoked. The response carries a WWW-Authenticate challenge that points at the protected-resource metadata. |
403 | permissionscope | A token without events:write gets scope "This token is not authorized to modify events (missing events:write scope)." An account without Reassign Pro access gets permission "Reassign needs an active subscription." The 7-day free trial includes Reassign Pro. A cookie session from another site gets permission "Cross-origin request." |
404 | not_found | Every request failed with the code not_found. The areaId or activityTypeId of each request names an area or an activity type that does not exist or that the caller cannot see, or its projectId names no live project of the caller. |
409 | conflict | An op conflicts with the current state. |
422 | validation | The request body is not valid. The message names the first bad field by its path. A repeated or unknown query parameter is also a 422. |
429 | rate_limited | Too many requests. The edge firewall limits each IP to 2000 requests per minute across /api/v1; that 429 comes from the edge and can have a body that is not this JSON and no Retry-After header. When an edge 429 has no Retry-After, retry after 60 seconds. The app also limits each Bearer credential to 600 requests per minute, and some routes limit each user. These limits return this body. This app 429 always has Retry-After: retry after that number of seconds. |
503 | internal | A fault on our side. The request is safe to retry. |
Most error responses: When no op of the batch applied, the body also has the results rows, so the caller sees which op failed.
Request
curl -X POST "https://reassign.app/api/v1/schedule/plan" \
-H "Authorization: Bearer rsk_live_YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"requests": [
{
"name": "Write the launch post",
"durationMinutes": 90,
"earliest": "2026-08-14T09:00",
"latest": "2026-08-14T18:00",
"areaId": "3c9d2a41-6b7e-4f80-9a1b-2c3d4e5f6071",
"projectId": "f3a4b5c6-d7e8-4f90-a1b2-c3d4e5f60718",
"priority": 3,
"requestId": "launch-post-0814"
}
]
}'Response
{
"timezone": "Europe/Ljubljana",
"undoToken": "00000000-0000-4000-8000-000000000001",
"expiresAt": "2026-08-13T09:12:00.000Z",
"results": [
{
"index": 0,
"status": "ok",
"result": {
"event": {
"id": "b7f1c2a0-1d2e-4f3a-9b8c-5d6e7f801234",
"start": "2026-08-14T11:00",
"end": "2026-08-14T12:30",
"kind": "blocking",
"name": "Write the launch post",
"source": "reassign",
"areaId": "3c9d2a41-6b7e-4f80-9a1b-2c3d4e5f6071",
"activityTypeId": null,
"projectId": "f3a4b5c6-d7e8-4f90-a1b2-c3d4e5f60718",
"priority": 3
}
}
}
]
}Undo a change
Reverse one or more recent writes. Send 1 to 20 tokens. Each token is the undoToken of a write that recorded an undo receipt. Scope: events:write.
- A token is valid until its
expiresAt, 30 minutes after the write. - You can use a token one time only.
- The
resultsrows are in the order oftokens. The server undoes the newest write first. Thus two tokens that change one row work in any order. - The undo stops when a row that the write changed has a newer change. Refer to Stale tokens.
- The response has only
results. An undo does not return a newundoToken. You cannot undo an undo.
These routes can return an undoToken when the server records an undo receipt: POST /events, POST /schedule/plan, POST /reviews/{date}, POST /backlog, POST /categories, and POST /command. The tracker and moment routes return no undoToken. Refer to Moments & trackers.
Row results
Each ok row has one shape: { reverted, restored, removed, voided, occurrenceRestored? }. The counts count the rows that the undo changed.
| Field | Value |
|---|---|
reverted | The total: restored + removed + voided |
restored | The rows that the undo put back |
removed | The rows that the undo deleted or marked as deleted |
voided | The open proposals that the undo cancelled; current plan calls create none, so their undo returns 0 |
occurrenceRestored | The occurrences of a series that the undo put back. It is a part of restored. The field is there only when it is more than 0. |
The four counts reverted, restored, removed, and voided are always there. A count is 0 when nothing of that kind changed. A calendar link or a task link is not a counted row.
| Write that you undo | Counts that can be more than 0 |
|---|---|
| A plan | removed (the booked events) |
| An event write, a day review, a command, a category write, or an Inbox write | restored and removed |
The undo of an Inbox capture to a task list cancels the task create when the server did not send it yet. When the task exists, the undo deletes it in its app. Linear cannot delete an issue, so the undo closes it.
Stale tokens
The server stores a stamp of each row that a write changed. The undo compares each row with its stamp. A sync that changes only the bookkeeping fields of a row does not change the stamp. The stamp of an event or an Inbox item includes its project and its priority. A token from before projects stays valid while the row has no project and the priority 0.
The undo fails the token row with stale in these cases:
- A row that the write changed has a newer change.
- A row is gone, or a row that the write did not delete is now deleted.
- The token is from before the stamps.
- A category undo would delete a category that the write made, and an event, an Inbox item, a tracker, or a calendar default now uses it.
- A user changed the area, the activity type, the project, or the priority of an event or an Inbox item by hand. An AI classification after the write does not make the token stale.
- A category undo would show a shared default again, and a category of the same type now has its name.
A stale row changes nothing. A token that an undo already used is validation, not stale.
A stale row from a stamp check has error.conflictingRows: up to 50 items { entity, id, reason }. error.conflictingRowsTruncated: true shows that more rows block the undo. A token from before the stamps, and a category refusal, have no conflictingRows.
| Field | Value |
|---|---|
entity | The stored type of the row: sections (events), backlog_blocks (Inbox items), source_links (calendar and task links), day_reviews, moment_entries (moments), moments (trackers), source_calendars (calendars), source_projects (Linear projects), areas, activity_types, or projects |
id | The id of the row |
reason | changed: the row changed after the write. deleted: the row is gone. newly_present: a row that the write removed is there again. |
Item errors
| Cause | error.message | error.code |
|---|---|---|
| The token is not a UUID | Unknown token "<token>". | validation |
| An unknown token | Unknown token "<token>". | not_found |
| A token that you already used | Nothing to undo for this token (status: <status>). | validation |
| The token expired | The 30-minute undo window has passed; the change stays. | validation |
| A calendar sync sends the change now | A calendar sync is sending this change now. Try the undo again in a moment. | conflict |
A row has a newer change. The row also has conflictingRows. | A row that this write changed has a newer change. The undo stops, so that it does not overwrite it. Nothing changed. | stale |
| The token has no stamps | This undo token is from an older version and cannot be checked. Undo is not available for it. | stale |
| A category that the write created now has a link | An event, an Inbox item, a tracker or a calendar now uses a category that this write created. The undo stops, so that it does not clear that link. Nothing changed. | stale |
| A category of the same type now has the name of a hidden shared default | A category named "<name>" now exists. The undo stops, so that the list does not show two of them. Nothing changed. | stale |
| A project that the write created now has a link | An event, an Inbox item, a calendar or a Linear project now uses a project that this write created. The undo stops, so that it does not clear that link. Nothing changed. | stale |
| The undo gives a project the name of a different live project | A project named <name> now exists in <area>. The undo stops, so that the list does not show two of them. Nothing changed. | stale |
The Linear project of a relink now feeds a different project, or the old project is gone or archived | The Linear project that this write linked now feeds another project, or the old project is gone or archived. The undo stops. Nothing changed. | stale |
The list or the Linear project of a split now feeds a different project, or the old project is gone or archived | The list or Linear project that this write split now feeds another project, or the old project is gone or archived. The undo stops. Nothing changed. | stale |
| The token has a damaged category entry | This undo token has a malformed taxonomy relink entry. Undo is not available for it. | validation |
A sync race changes nothing. Send the undo again after a short time. Do not send a stale token again: it fails again. When all the rows of the call fail with conflict, or all fail with stale, the status is 409. A mix of codes gives 400 with batch_rejected.
Request body
- Name
tokens- Type
- string[], required
- Description
The
undoTokenfrom an earlier write’s successfully recorded undo receipt.
Response fields
- Name
results- Type
- object[], required
- Description
3 forms
One form:
status: "ok"- Name
index- Type
- integer, required
- Description
- Name
result- Type
- object, required
- Description
5 fields
- Name
reverted- Type
- integer, required
- Description
- Name
restored- Type
- integer, required
- Description
- Name
removed- Type
- integer, required
- Description
- Name
voided- Type
- integer, required
- Description
- Name
occurrenceRestored- Type
- integer
- Description
- Name
warnings- Type
- "classification_pending"[]
- Description
One form:
status: "error"- Name
index- Type
- integer, required
- Description
- Name
error- Type
- object, required
- Description
11 fields
- Name
code- Type
- "conflict" | "not_found" | "validation" | "permission" | "scope" | "stale" | "read_only" | "rate_limited" | "internal", required
- Description
- Name
message- Type
- string, required
- Description
- Name
issues- Type
- object[]
- Description
3 fields
- Name
path- Type
- string | integer[], required
- Description
- Name
message- Type
- string, required
- Description
- Name
expected- Type
- string
- Description
- Name
feature- Type
- string
- Description
- Name
conflictingRows- Type
- object[]
- Description
3 fields
- Name
entity- Type
- string, required
- Description
- Name
id- Type
- string, required
- Description
- Name
reason- Type
- "changed" | "deleted" | "newly_present", required
- Description
- Name
conflictingRowsTruncated- Type
- true
- Description
- Name
usage- Type
- object
- Description
5 fields
- Name
events- Type
- integer, required
- Description
- Name
inboxItems- Type
- integer, required
- Description
- Name
trackers- Type
- integer, required
- Description
- Name
calendars- Type
- integer, required
- Description
- Name
projects- Type
- integer
- Description
- Name
reassignTargets- Type
- object[]
- Description
2 fields
- Name
id- Type
- string, required
- Description
- Name
name- Type
- string, required
- Description
- Name
unlinkedCheckout- Type
- object
- Description
3 fields
- Name
sessionId- Type
- string, required
- Description
- Name
paymentReceived- Type
- boolean, required
- Description
- Name
supportUrl- Type
- string, required
- Description
- Name
conflicts- Type
- object[]
- Description
2 forms
One form: With
id- Name
start- Type
- string, required
- Description
- Name
end- Type
- string, required
- Description
- Name
id- Type
- string, required
- Description
- Name
name- Type
- string, required
- Description
One form: With
batchIndex- Name
start- Type
- string, required
- Description
- Name
end- Type
- string, required
- Description
- Name
batchIndex- Type
- integer, required
- Description
- Name
name- Type
- string, required
- Description
- Name
nearestSlots- Type
- object[]
- Description
4 fields
- Name
start- Type
- string, required
- Description
- Name
end- Type
- string, required
- Description
- Name
score- Type
- number
- Description
- Name
reason- Type
- string
- Description
One form:
status: "skipped"- Name
index- Type
- integer, required
- Description
- Name
reason- Type
- string, required
- Description
Responses
| Status | Codes | Meaning |
|---|---|---|
200 | — | Success. |
400 | batch_rejected | Every op failed, and the failed ops have different codes. The code is batch_rejected. |
401 | unauthorized | No Bearer token and no session cookie, or the token is invalid, expired, or revoked. The response carries a WWW-Authenticate challenge that points at the protected-resource metadata. |
403 | permissionscope | A token without events:write gets scope "This token is not authorized to modify events (missing events:write scope)." An account without Reassign Pro access gets permission "Reassign needs an active subscription." The 7-day free trial includes Reassign Pro. A cookie session from another site gets permission "Cross-origin request." |
404 | not_found | An op names an item that does not exist. |
409 | conflict | The request conflicts with the current state: conflict "A calendar sync is sending this change now. Try the undo again in a moment." or stale "A row that this write changed has a newer change. The undo stops, so that it does not overwrite it. Nothing changed." or stale "This undo token is from an older version and cannot be checked. Undo is not available for it." |
422 | validation | The request body is not valid. The message names the first bad field by its path. A repeated or unknown query parameter is also a 422. |
429 | rate_limited | Too many requests. The edge firewall limits each IP to 2000 requests per minute across /api/v1; that 429 comes from the edge and can have a body that is not this JSON and no Retry-After header. When an edge 429 has no Retry-After, retry after 60 seconds. The app also limits each Bearer credential to 600 requests per minute, and some routes limit each user. These limits return this body. This app 429 always has Retry-After: retry after that number of seconds. |
503 | internal | A fault on our side. The request is safe to retry. |
Most error responses: When no op of the batch applied, the body also has the results rows, so the caller sees which op failed.
Request
curl -X POST "https://reassign.app/api/v1/actions/undo" \
-H "Authorization: Bearer rsk_live_YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"tokens": [
"00000000-0000-4000-8000-000000000001"
]
}'Response
{
"results": [
{
"index": 0,
"status": "ok",
"result": {
"reverted": 1,
"restored": 1,
"removed": 0,
"voided": 0
}
}
]
}