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 start and an end. 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 backlog schedule op.
  • The server keeps the exact minute that you send. It does not move times to a grid.
  • GET /schedule gives the events of each day, the free time of each day (freeSlots), and the planned minutes for each area, activity type, and project.
kindConflicts
blocking (the default)Two blocking events cannot overlap. Events that only touch do not overlap.
non_blockingNever conflicts, and does not block other events
referenceNever 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

ValueFormatExample
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"
DurationWhole minutes, in a field that ends with Minutes90
  • A span, a moment, and now use local datetimes. A time stamp (a field that ends with At, for example updatedAt or expiresAt) 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 warning text.

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 writeSend
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 end is on a later date.
  • A span to midnight ends at T00:00 of the next date. The API refuses T24:00.
  • An end that is not after start gets HTTP status 422 with the error code validation.
  • A span has no date field 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 (actualStart and actualEnd).

Refer to Events.

Names and formats

ItemStyleExample
Keys in a body, a query, and a nested objectcamelCaseactivityTypeId, requestId
Enum values and error codeslowercase snake_casenon_blocking, not_found
Path segmentslowercase kebab-case/api/v1/schedule/plan
ColorLowercase #rrggbb"#3b82f6"
Boolean query valuetrue 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 example areaId.
  • 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.issues lists 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, and reflect.
  • In POST /events, a bad op does not fail the full request. It becomes an error row in results. 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, recurrenceEnd without recurrence fails.

Areas and activity types

CategoryWhat it isShown as
AreaA group of events, for example "Work"A color (color)
Activity typeA 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: areaId and activityTypeId. There are no name fields.
  • GET /schedule gives the areas and activityTypes lists. 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 sendOn createOn update
An id, for example "areaId": "3c9d…"Sets the linkSets the link
nullNo link (the same as no key)Removes the link. AI classification does not fill it again.
No keyNo linkKeeps 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 has projectId. The value is null when the item has no project.
  • GET /schedule gives the projects list: { id, name, areaId }. An archived project also has archived: true. GET /categories lists all projects, also the archived projects.
  • A project has no color. Its color comes from its area.
  • projectId must be a live project of your account. An unknown or archived id fails with not_found. An archived project that is already on the item can stay. A POST /categories project update with archived: false restores 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 /categories changes projects with the projects list. Its ops are create, update, delete, split, and relink.
  • Each day of GET /schedule has loadByProject: the blocking minutes for each project. projectId: null is the time with no project. A read of more than one day also has a top-level loadByProject with the total.
You send projectIdOn createOn update
An idSets the projectSets the project
nullNo project (the same as no key)Removes the project. The area stays.
No keyNo projectKeeps 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.
  • null is not a valid value. Send 0 to remove the priority.
  • On create, no key gives 0. On update, 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 recurrence to 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, or FREQ=SECONDLY, and a rule with BYHOUR, BYMINUTE, or BYSECOND.
  • In an update, send "recurrence": null. The series then does not repeat.
  • recurrenceEnd sets the last date of the series. The date is included. Send it with recurrence in the same op.
  • The change poll shows a series as one row. GET /schedule shows each occurrence.

The id selects what a change applies to:

You sendThe 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
  • scope has 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-DD after the change.
  • Each occurrence of a series has the fields seriesId and originalDate. 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, and mirrorStyles unless it chose its own calendar.
  • recurrence, calendarId, mirrorCalendarIds, mirrorStyles, and sourceUrl belong 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 create op can include a first checklist.
  • After that, use the checklist op of POST /events. items replaces the full list. check and uncheck take item ids.
  • On an occurrence id, check and uncheck change that occurrence only. On a series id, items changes the list of the full series.
  • The checklist op has no scope.

Refer to Events.

Focus intervals

A blocking event can have a focus rhythm: focusIntervals: { focusMin, breakMin }, for example { "focusMin": 25, "breakMin": 5 }.

  • focusMin is 5 to 180. breakMin is 1 to 60.
  • The breaks are between the focus intervals. The block always ends on a focus interval.
  • A non_blocking or reference event refuses focusIntervals.
  • In an update, send "focusIntervals": null to remove the rhythm.
  • A read gives completedFocusIntervals when 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, and timezone. Send the cursor back 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 hasMore is true, poll again at once with the new cursor.
  • limit sets 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 id is the stored id of that row, not seriesId@YYYY-MM-DD. No other read gives this id.
  • GET /events/{id} with that stored id returns the occurrence with the id seriesId@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.

  • capture adds a block to the Inbox.
  • schedule puts a block on the day plan at a start (a local datetime). The block gives the length.
  • schedule obeys the rules of an event write. The span is 5 minutes to 168 hours. A blocking block that overlaps a blocking event fails with conflict.
  • durationMinutes of a block is 5 to 1440.
  • A bad op becomes an error row with the code validation. It does not fail the full request.
  • park moves an event from the day plan back to the Inbox.
  • capture, capture_text, and update take projectId and priority. Refer to Projects and priority.
  • On GET /schedule with includeBacklog=true, backlogProjectId keeps the items of one project. Send none for the items with no project. backlogMinPriority keeps the items with that priority or a higher priority.

Refer to Backlog.

Moments and trackers

ItemWhat it is
TrackerA thing that you log, for example "Coffee" or "Mood".
MomentOne logged entry of a tracker, at a point in time.
Tracker scaleEach log
tallyAdds the stored step, or 1 when no step is stored. The API refuses step on a tally tracker.
ratingA value on a scale, 1 to 5 by default
quantityAn amount in unit
  • The point of a moment is time, a local datetime. The day of a moment is the date part of time.

Refer to Moments and trackers.

Day reviews

A day review records how a past day went.

  1. Mark each event: kept, skipped, changed, or added. Use the reflect op of POST /events. For changed and added, you can also send actualStart and actualEnd.
  2. 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 marked added.
  • The review gives two adherence values from 0 to 1. event is the score of each event, weighted by its length. layer compares the planned areas with the actual areas.
  • There is no separate route to read a review. GET /schedule gives a review block on each confirmed day.
  • Each event of that day also has a reflect block 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.
  • calendarId is the home calendar of an event. Only a calendar with writable: true can take events.
calendarId on createResult
A calendar idThe event goes to that calendar.
nullThe event stays on the dial only.
No keyThe 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.
  • mirrorCalendarIds adds one-way copies to more calendars. It must not contain the home calendarId. A task list cannot take a copy.
  • On update, mirrorCalendarIds replaces the set. [] removes all copies.
  • On update, the API checks only the ids that the op adds to mirrorCalendarIds. An id that is already on the event can stay, also when its calendar is read-only or disconnected now.
  • mirrorStyles sets how much each copy shows: full, private, or busy. 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 omitted calendarId keeps the home calendar.
calendarId: null on updateResult
On a calendar eventThe event stays on the dial only. The API removes the home link and deletes all mirror copies.
On a dial-only eventNo change. The copies stay.
With an occurrence id or scopeFails with validation. Use the series id.
With start, end, name, notes, areaId, activityTypeId, projectId, priority, or kindThe API applies these edits first, then removes the links, in one step.
With mirrorCalendarIds, mirrorStyles, recurrence, recurrenceEnd, sourceUrl, or focusIntervalsFails 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 update with calendarId: null works 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

TokenComes fromLifetimeUse
undoTokenA write that records an undo receipt, for example POST /events, POST /schedule/plan, POST /backlog, POST /categories, and POST /reviews/{date}30 minutes, until expiresAtSend it to POST /actions/undo. One use only.
  • When present, undoToken and expiresAt come 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 no undoToken. A write also gives no undoToken when the server cannot record the undo. The change then stays. Thus, a response with no undoToken does not prove that nothing changed.
  • Tracker and moment writes give no undoToken. To reverse one, send another write.
  • POST /actions/undo takes 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 conflict while a calendar sync sends the same change. The undo then changes nothing. Send it again after a short time.
  • An undo fails with stale when a row that the write changed has a newer change. The undo then changes nothing. The row can list the changed rows in conflictingRows. Refer to Stale tokens.

Refer to Scheduling.

Scopes

ScopePermits
events:readAll read calls, except GET /me
events:writeRead, 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 /me needs no scope. events:read covers 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:read gets scope, 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 /me works on all plans.

Refer to Authentication.