Core concepts
This page explains the model behind the Reassign API. Read it before you write to a day plan. Each section links to its reference page.
The day plan
Reassign is a 24-hour circular day planner. A day plan is the list of events on one date.
- Each event has a span: a
startand anend. Refer to Spans. - An event that you write is 5 minutes to 168 hours (7 days) long. This rule applies to
POST /events,POST /schedule/plan, and the backlogscheduleop. - The server keeps the exact minute that you send. It does not move times to a grid.
GET /schedulegives the events of each day, the free time of each day (freeSlots), and the planned minutes for each area, activity type, and project.
kind | Conflicts |
|---|---|
blocking (the default) | Two blocking events cannot overlap. Events that only touch do not overlap. |
non_blocking | Never conflicts, and does not block other events |
reference | Never conflicts, and does not block other events |
An event write that overlaps a blocking event fails with the error code conflict. The backlog schedule op also checks for conflicts. For the HTTP status, refer to Errors. Refer to Events and Scheduling.
Dates, times, and time zones
| Value | Format | Example |
|---|---|---|
| Date | "YYYY-MM-DD". The API refuses a date that does not exist, for example "2027-02-29". | "2026-09-23" |
| Local datetime | "YYYY-MM-DDTHH:MM", in the time zone of the account. No seconds, no offset, no Z. Hours go from 00 to 23. | "2026-09-23T18:30" |
| Instant (a point in real time) | ISO 8601 in UTC, with milliseconds | "2026-09-23T16:30:00.000Z" |
| Duration | Whole minutes, in a field that ends with Minutes | 90 |
- A span, a moment, and
nowuse local datetimes. A time stamp (a field that ends withAt, for exampleupdatedAtorexpiresAt) uses an instant. - All local datetimes are in the time zone of the account.
- The API does not take a time zone for each request.
- If the account has no valid time zone, the server uses
UTC. - A response with a local datetime gives the zone one time, at the top level, as an IANA name. For example,
"timezone": "Europe/Ljubljana". - On a daylight saving day, a start time can fall into the hour that the clock skips. The event then has a
warningtext.
Refer to Events.
Spans past midnight and multi-day spans
A span is two local datetimes: start and end. The end must be after the start. An event that you write is also 5 minutes to 168 hours long.
| To write | Send |
|---|---|
| 09:00 to 10:30 | "start": "2026-09-23T09:00", "end": "2026-09-23T10:30" |
| 23:00 to 01:00 | "start": "2026-09-23T23:00", "end": "2026-09-24T01:00" |
| 22:00 to midnight | "start": "2026-09-23T22:00", "end": "2026-09-24T00:00" |
| Friday 18:00 to Sunday 18:00 | "start": "2026-09-25T18:00", "end": "2026-09-27T18:00" |
- The day of an event is the date part of
start. An event from 23:00 to 01:00 belongs to the first date. - An event past midnight, or an event of more than one day, needs no extra field. Its
endis on a later date. - A span to midnight ends at
T00:00of the next date. The API refusesT24:00. - An
endthat is not afterstartgets HTTP status 422 with the error codevalidation. - A span has no
datefield and no field for the next day. - The same rule applies to all spans: events, free slots, conflicts, and the actual times of a review (
actualStartandactualEnd).
Refer to Events.
Names and formats
| Item | Style | Example |
|---|---|---|
| Keys in a body, a query, and a nested object | camelCase | activityTypeId, requestId |
| Enum values and error codes | lowercase snake_case | non_blocking, not_found |
| Path segments | lowercase kebab-case | /api/v1/schedule/plan |
| Color | Lowercase #rrggbb | "#3b82f6" |
| Boolean query value | true or false only | ?includeBacklog=true |
- Each concept has one name. The same key is in the request and in the response. Thus, you can copy a field from a read into a write.
- A key has no alias. The API does not change a value to a different format.
- The object's own id is
id. A reference to a different object is<entity>Id, for exampleareaId. - OAuth 2.0 fields (
access_token), HTTP headers, scope strings (events:read), and RRULE text keep their standard names.
Validation
- A request with a bad value gets HTTP status 422 with the error code
validation. - Each route checks its query and its body against a schema. The message names the first bad field by its path, for example
`from`.error.issueslists up to 20 bad fields. - A query parameter that the route does not know, or a query parameter that you send two times, gets HTTP status 422.
- A body that is not valid JSON gets HTTP status 422. An empty body reads as
{}. - A write that sends an output-only field fails. The output-only fields are
source,meeting,location,warning,readOnly,completedFocusIntervals, andreflect. - In
POST /events, a bad op does not fail the full request. It becomes an error row inresults. In an atomic batch, the bad op still rejects the batch, and the response has HTTP status 422 with the rows.
Refer to Errors.
Unknown fields
- Responses can contain more fields than these docs list. Ignore fields that you do not know.
- Requests are different. The API refuses a key that it does not know with HTTP status 422, for example
Unknown body key `color`. - A key that has no effect in its context is also an error. For example,
recurrenceEndwithoutrecurrencefails.
Areas and activity types
| Category | What it is | Shown as |
|---|---|---|
| Area | A group of events, for example "Work" | A color (color) |
| Activity type | A tag, for example "Focus" | A fill pattern (pattern) |
- An event has zero or one area and zero or one activity type.
- An event refers to them by id only:
areaIdandactivityTypeId. There are no name fields. GET /schedulegives theareasandactivityTypeslists. Use them to find the name and the color of an id.- Some areas and activity types are shared defaults (
isGlobal: true). An edit of a shared default makes your own copy with a new id. A delete of a shared default hides it for your account only. - An event has no color field. Its color comes from its area.
A read always has areaId and activityTypeId. The value is null when the event has no area or no activity type.
| You send | On create | On update |
|---|---|---|
An id, for example "areaId": "3c9d…" | Sets the link | Sets the link |
null | No link (the same as no key) | Removes the link. AI classification does not fill it again. |
| No key | No link | Keeps the current link |
On create, an id that you send is a manual choice. AI classification can fill only a field that has no id. Refer to Rules of the fields.
Refer to Categories.
Projects and priority
A project is a named goal, for example "Launch". It can be in one area.
- A project belongs to your account only. It has no shared default and no copy.
- An event and an Inbox item have zero or one project:
projectId. A read always hasprojectId. The value isnullwhen the item has no project. GET /schedulegives theprojectslist:{ id, name, areaId }. An archived project also hasarchived: true.GET /categorieslists all projects, also the archived projects.- A project has no color. Its color comes from its area.
projectIdmust be a live project of your account. An unknown or archived id fails withnot_found. An archived project that is already on the item can stay. APOST /categoriesprojectupdatewitharchived: falserestores an archived project.- When a write sets a new project and no
areaId, the item gets the area of the project, if the project has an area. This also replaces an area that the user set. - Two live projects cannot have the same name, also in different areas. A duplicate name fails with
conflict, for example "A project named Launch exists in Work." POST /categorieschanges projects with theprojectslist. Its ops arecreate,update,delete,split, andrelink.- Each day of
GET /schedulehasloadByProject: the blocking minutes for each project.projectId: nullis the time with no project. A read of more than one day also has a top-levelloadByProjectwith the total.
You send projectId | On create | On update |
|---|---|---|
| An id | Sets the project | Sets the project |
null | No project (the same as no key) | Removes the project. The area stays. |
| No key | No project | Keeps the current project |
priority is a whole number from 0 to 4: 0 none, 1 low, 2 medium, 3 high, 4 urgent.
- A read always has
priority. nullis not a valid value. Send0to remove the priority.- On
create, no key gives0. Onupdate, no key keeps the current value.
Refer to Categories and Backlog.
Recurring events and series
A recurring event is a series. Each date of the series is an occurrence.
- Set
recurrenceto an iCalendar RRULE, for example"FREQ=WEEKLY;BYDAY=MO,WE,FR". The API does not accept preset names. - The API refuses a rule with
FREQ=HOURLY,FREQ=MINUTELY, orFREQ=SECONDLY, and a rule withBYHOUR,BYMINUTE, orBYSECOND. - In an update, send
"recurrence": null. The series then does not repeat. recurrenceEndsets the last date of the series. The date is included. Send it withrecurrencein the same op.- The change poll shows a series as one row.
GET /scheduleshows each occurrence.
The id selects what a change applies to:
| You send | The change applies to |
|---|---|
The series id, for example "c4d5…" | The full series |
An occurrence id seriesId@YYYY-MM-DD, for example "c4d5…@2026-09-24" | That occurrence only |
An occurrence id and "scope": "future" | That occurrence and all later occurrences |
scopehas one value:"future". It is valid only with an occurrence id.- Use the occurrence id that a read gives. Do not make the date part yourself.
- After you move an occurrence to another day, its id keeps the original date. Thus, the date part of the id can differ from the date of
start. - An occurrence id on an event that does not repeat fails. A date that is not an occurrence of the series also fails.
- A changed occurrence keeps its id
seriesId@YYYY-MM-DDafter the change. - Each occurrence of a series has the fields
seriesIdandoriginalDate. They are the two parts of its id, also on a moved occurrence. - A changed occurrence carries the series'
recurrence. It inherits the series'calendarId,mirrorCalendarIds, andmirrorStylesunless it chose its own calendar. recurrence,calendarId,mirrorCalendarIds,mirrorStyles, andsourceUrlbelong to the full series. On an occurrence id, the current value is not a change. A new value fails.
Refer to Events.
Checklists
An event can have a checklist of small tasks: checklist: { items: [{ id, text, done }] }.
- A
createop can include a first checklist. - After that, use the
checklistop ofPOST /events.itemsreplaces the full list.checkandunchecktake item ids. - On an occurrence id,
checkanduncheckchange that occurrence only. On a series id,itemschanges the list of the full series. - The
checklistop has noscope.
Refer to Events.
Focus intervals
A blocking event can have a focus rhythm: focusIntervals: { focusMin, breakMin }, for example { "focusMin": 25, "breakMin": 5 }.
focusMinis 5 to 180.breakMinis 1 to 60.- The breaks are between the focus intervals. The block always ends on a focus interval.
- A
non_blockingorreferenceevent refusesfocusIntervals. - In an update, send
"focusIntervals": nullto remove the rhythm. - A read gives
completedFocusIntervalswhen one or more intervals are done.
Refer to Events.
Poll for changes (Beta)
GET /events is a change poll. It returns the stored events that changed after your last poll.
- On the first poll, do not send
cursor. - The response has
events,cursor,hasMore, andtimezone. Send thecursorback as?cursor=on the next poll. - The cursor is opaque. Do not parse it or make it yourself. A value that the API did not write gets HTTP status 422.
- When
hasMoreistrue, poll again at once with the newcursor. limitsets the page size: 1 to 500. The default is 100.- The poll does not skip or repeat a row.
- A series is one row. The poll does not expand its occurrences.
- A changed occurrence is a separate row. Its
idis the stored id of that row, notseriesId@YYYY-MM-DD. No other read gives this id. GET /events/{id}with that stored id returns the occurrence with the idseriesId@YYYY-MM-DD.- The poll does not show deleted events.
- To read a day, use
GET /schedule?from=YYYY-MM-DD&to=YYYY-MM-DD.
Refer to Events.
The Inbox (backlog)
The Inbox holds blocks that do not have a time yet, for example "Wash the car". The route is /backlog.
captureadds a block to the Inbox.scheduleputs a block on the day plan at astart(a local datetime). The block gives the length.scheduleobeys the rules of an event write. The span is 5 minutes to 168 hours. A blocking block that overlaps a blocking event fails withconflict.durationMinutesof a block is 5 to 1440.- A bad op becomes an error row with the code
validation. It does not fail the full request. parkmoves an event from the day plan back to the Inbox.capture,capture_text, andupdatetakeprojectIdandpriority. Refer to Projects and priority.- On
GET /schedulewithincludeBacklog=true,backlogProjectIdkeeps the items of one project. Sendnonefor the items with no project.backlogMinPrioritykeeps the items with that priority or a higher priority.
Refer to Backlog.
Moments and trackers
| Item | What it is |
|---|---|
| Tracker | A thing that you log, for example "Coffee" or "Mood". |
| Moment | One logged entry of a tracker, at a point in time. |
Tracker scale | Each log |
|---|---|
tally | Adds the stored step, or 1 when no step is stored. The API refuses step on a tally tracker. |
rating | A value on a scale, 1 to 5 by default |
quantity | An amount in unit |
- The point of a moment is
time, a local datetime. The day of a moment is the date part oftime.
Refer to Moments and trackers.
Day reviews
A day review records how a past day went.
- Mark each event:
kept,skipped,changed, oradded. Use thereflectop ofPOST /events. Forchangedandadded, you can also sendactualStartandactualEnd. - Confirm the day with
POST /reviews/{date}and"action": "confirm". This keeps a fixed copy of the results.
- You can confirm only a date before today. You can discard the review of any day.
"action": "discard"removes the review and the marks of that day. It also deletes the events markedadded.- The review gives two adherence values from 0 to 1.
eventis the score of each event, weighted by its length.layercompares the planned areas with the actual areas. - There is no separate route to read a review.
GET /schedulegives areviewblock on each confirmed day. - Each event of that day also has a
reflectblock with its mark.
Refer to Day reviews.
Calendars and read-only events
A user can connect calendars (Google or Microsoft) and task lists. GET /calendars lists them.
- The events that you create, edit, or delete also sync to the connected calendars.
- REST writes, MCP writes, and changes in the app use the same sync path.
- The sync runs after the server sends the response. Thus, the calendar can show the change a short time later.
calendarIdis the home calendar of an event. Only a calendar withwritable: truecan take events.
calendarId on create | Result |
|---|---|
| A calendar id | The event goes to that calendar. |
null | The event stays on the dial only. |
| No key | The event goes to the default calendar of the account, if one is set. The default calendar must be writable, and its source must be connected. Otherwise the event stays on the dial only. |
mirrorCalendarIdsadds one-way copies to more calendars. It must not contain the homecalendarId. A task list cannot take a copy.- On
update,mirrorCalendarIdsreplaces the set.[]removes all copies. - On
update, the API checks only the ids that the op adds tomirrorCalendarIds. An id that is already on the event can stay, also when its calendar is read-only or disconnected now. mirrorStylessets how much each copy shows:full,private, orbusy. Refer to Copy styles.- A one-off event on the dial only can have copies. A series with copies needs a home calendar. A series that came from a connected calendar is an exception. Refer to Rules of the fields.
- On
update, an omittedcalendarIdkeeps the home calendar.
calendarId: null on update | Result |
|---|---|
| On a calendar event | The event stays on the dial only. The API removes the home link and deletes all mirror copies. |
| On a dial-only event | No change. The copies stay. |
With an occurrence id or scope | Fails with validation. Use the series id. |
With start, end, name, notes, areaId, activityTypeId, projectId, priority, or kind | The API applies these edits first, then removes the links, in one step. |
With mirrorCalendarIds, mirrorStyles, recurrence, recurrenceEnd, sourceUrl, or focusIntervals | Fails with validation. Send that change in a separate op. |
- An event from a calendar that the user does not own has
readOnly: true. - The API refuses an edit or a delete of a read-only event with the error code
read_only(HTTP 403). Change it in its calendar. - One exception: an
updatewithcalendarId: nullworks on a read-only event. The edits that it carries also apply, because the event is local after the op.
Refer to Calendars.
Undo tokens
| Token | Comes from | Lifetime | Use |
|---|---|---|---|
undoToken | A write that records an undo receipt, for example POST /events, POST /schedule/plan, POST /backlog, POST /categories, and POST /reviews/{date} | 30 minutes, until expiresAt | Send it to POST /actions/undo. One use only. |
- When present,
undoTokenandexpiresAtcome together, at the top level of the response. One token covers all the changes of the call. - A write that changed nothing gives no
undoToken. A rejected batch also gives noundoToken. A write also gives noundoTokenwhen the server cannot record the undo. The change then stays. Thus, a response with noundoTokendoes not prove that nothing changed. - Tracker and moment writes give no
undoToken. To reverse one, send another write. POST /actions/undotakes 1 to 20 tokens. An undo gives no new token. You cannot undo an undo.- After 30 minutes, the change stays. The undo row then has the error "The 30-minute undo window has passed; the change stays."
- An undo can fail with
conflictwhile a calendar sync sends the same change. The undo then changes nothing. Send it again after a short time. - An undo fails with
stalewhen a row that the write changed has a newer change. The undo then changes nothing. The row can list the changed rows inconflictingRows. Refer to Stale tokens.
Refer to Scheduling.
Scopes
| Scope | Permits |
|---|---|
events:read | All read calls, except GET /me |
events:write | Read, create, change and delete events and other plan data. It includes events:read. |
- A personal access token with Read only access has
events:read. Read and write access has both scopes. GET /meneeds no scope.events:readcovers the other read calls.- A call without the necessary scope fails with the error code
scope(HTTP 403). - A write route checks the scope before the body. Thus, a token with only
events:readgetsscope, also when the body is not correct. - Most calls also need a trial or Pro plan. The 7-day free trial includes Reassign Pro.
GET /meworks on all plans.
Refer to Authentication.