Changelog

This page lists the changes to the Reassign API and to its documentation.

Project and priority on a plan request (main c86570e3)

2026-10-16

The API specification is generated from main commit c86570e3c15fcc4a557f5be02dfd07ca3fe2fe74, tag v0.2.35. This entry covers only the 10 main commits after develop 520e84f8. On 2026-10-18, the live API specification is the same as this specification.

BeforeNow
A POST /schedule/plan request takes no project and no priority. A request with projectId or priority fails with validation. The booked event has projectId: null and priority: 0.Exact and window requests take projectId and priority, as an event create does. The project must be live, else the row fails with not_found and the message No live project with id "<id>". With no areaId, the event takes the area of the project. priority is 0 to 4, and an omitted value is 0. priority: null fails with validation.
A POST /schedule/plan retry with the same requestId that sends areaId: null or activityTypeId: null in place of an omitted key is a new request, and it books a second event.areaId: null and activityTypeId: null match an omitted key. projectId and priority are content. projectId: null and priority: 0 match an omitted key. A retry that adds a real id, or a priority from 1 to 4, in place of an omitted key is a new request.
(Beta) On a day with a recurring event that crosses midnight, a POST /command change can go to the wrong one of the two occurrences. A new end, such as "wake me at 06:00", for the part from the day before fails with the notice A change to "<name>" was skipped because its new end is not after its start.The server checks a new end or a new length against the start of the occurrence of the day. A new end with no new start that fits only the part from the day before changes that part. With scope: "occurrence", only the occurrence of the day before changes. A change with no new end, such as a rename, still changes the occurrence of the day. For the occurrence of the day, the command drops a new end that is not after its start, with the notice A new end was skipped because it is not after the start. A new length for the occurrence of the day uses the start of that occurrence.

The live MCP schemas of 2026-10-18 confirm the projectId and priority fields of write_events and the loadByProject field of get_schedule. The docs did not verify that the live MCP connector runs this commit. No MCP tool books a plan, so the MCP tools did not change. Reassign still has 10 MCP tools, and the public OpenAPI specification still has 26 operations. Refer to Scheduling and Quick command.

Projects, priority, and command scope (develop 520e84f8)

2026-10-14

The API specification is generated from develop commit 520e84f82edccc35a52c8223a1128190686f246e. Tag v0.2.29 (2026-10-14) holds this commit.

BeforeNow
An event has no project and no priority.Each event has projectId (an id or null) and priority (0 none, 1 low, 2 medium, 3 high, 4 urgent). This applies to GET /events/{id}, GET /events/search, GET /schedule, the POST /events and POST /schedule/plan results, and the updated rows of POST /reviews/{date}.
An Inbox item has no project and no priority.Each item has projectId and priority, in GET /backlog, in GET /schedule backlog, and in the POST /backlog results.
POST /events create and update take no project.They take projectId and priority. A project must be live, else the op fails with not_found. A new project with no areaId in the op gives the event the area of the project. On update, projectId: null clears the project and keeps the area. On an event from a task app, a new project that needs a move of the task fails with read_only. An omitted priority is 0 on create and keeps the value on update.
POST /backlog capture, capture_text, and update take no project.They take projectId and priority, with the same rules. The new project also replaces an area that the user set by hand. On an item that a task app owns, a new project can move the task to a list of that project. Else the op fails with read_only, and the message gives the reason.
On capture and capture_text, an omitted calendarId uses the account default list.It uses the default list of the project of the item first. Then it uses the account default list.
GET /categories gives areas and activityTypes.It also gives projects, and the list includes the archived projects. Each project has id, name, areaId, order, archived, and publishCalendarId.
POST /categories takes areas and activityTypes.It also takes projects ops: create, update, delete, split, and relink. Two live projects of a caller cannot have the same name, in any area (conflict). A delete of a project that a task list or a Linear project feeds fails with conflict. The response has projects rows only when the request sent project ops.
A delete of an area counts events, Inbox items, trackers, and calendar defaults.It also counts the live projects of the area. A delete without reassignTo of an area with projects fails with validation, and usage.projects gives the count.
GET /schedule gives the load by area and by activity type.It also gives projects (an archived project has archived: true) and loadByProject for each day. A read of more than one day also gives a top-level loadByProject. includeLookups=false also leaves out projects.
The Inbox filters of GET /schedule do not use the project or the priority.backlogProjectId (a project id, or none) and backlogMinPriority (0 to 4) filter the Inbox items. Both need includeBacklog=true.
POST /command gives intents, questions, notices, and the undo receipt.It also gives created (each new event and Inbox block), written (the indexes of the written intents), and writtenLines. The request takes sourceUrl and a written line on each history turn. A create or park intent can have projectId and priority (1 to 4).
Breaking. (Beta) A delete intent of POST /command has no scope. A delete on a recurring event removes one day only.A delete intent takes scope: occurrence, series, or future. With no clear scope in the line, the command asks one choice question and does not delete. A changed occurrence, or an event from a calendar, gets no question.
A series or future command change from a changed occurrence does not change the series.It applies to the series of that occurrence, and to the occurrence. When the new end is not after the start of the occurrence, the occurrence keeps its end, and a notice says so.
An undo checks the area and the activity type of an event or an Inbox item.It also checks the project and the priority. A hand change of them makes the token stale. conflictingRows[].entity can also be projects or source_projects. A project undo has four new stale messages. A token from before projects stays valid while the row has no project and the priority 0.
An update with calendarId: null and a kind change on an all-day calendar event fails with read_only.The op applies. The event is local after the op, so the kind lock of the calendar does not apply.
AI classification of a new event can leave the area and the activity type empty. It starts about 5 seconds after the write.For an event on the dial, AI always picks an area and an activity type for each empty field. A manual choice stays. Classification starts about 1 second after the write.
GET /schedule integrations.defaultCalendarId can name a writable calendar of a source that is not connected. A write refuses that calendar.It is present only when the calendar is writable and its source is connected. GET /calendars uses the same rule.

The MCP tools get_schedule, write_events, and manage_inbox take the same new fields, and manage_categories takes the same projects ops. The MCP server text now names projects and priority. Reassign still has 10 MCP tools, and the public OpenAPI specification still has 26 operations. The MCP claims come from a code comparison only. Refresh tools/list, and update code that parses events, Inbox items, or category lists with a strict schema. Refer to Events, Scheduling, Categories, Backlog, Quick command, and MCP server.

Plan copy styles and series copies (main 5b5115d9)

2026-10-09

The API specification is generated from main commit 5b5115d93af979d0cc9a747c9b43ca12444b01c2, tag v0.2.18.

BeforeNow
Of the calendar fields, a POST /schedule/plan request takes calendarId and mirrorCalendarIds only. A request with mirrorStyles fails with validation.Exact and window requests take mirrorStyles, as an event create does. Each key must be in the mirrorCalendarIds of the request, else the row fails with validation. The row event gives the styles, and the styles are part of the requestId check.
The requestId check uses a hash of the request content. The hash sorts only the top-level keys.The hash sorts the keys at each level, and an array keeps its order. On a plan request and on a create, mirrorStyles: {} is the same as no map. A retry with {} in place of no map, or with the style keys in another order, matches. A retry with mirrorCalendarIds in another order is a new request.
An update that adds mirrorCalendarIds to a dial-only series fails with validation.A dial-only series with a valid calendar identity can have copies. A series that came from a connected calendar keeps this identity after calendarId: null. No read shows the identity. Other series still fail with the same message.
A scope: "future" split of a dial-only series with copies fails with validation.The split applies. The new series starts with no copies and no mirrorStyles. The old series keeps its copies.
The Error schema lists 11 codes.It also lists checkout_unlinked (409) and an optional unlinkedCheckout field. Only the billing routes of the Reassign web app send them. A token or OAuth caller does not get them.

The docs now use the new field labels of the Reassign web app: Server address, New key, and Base URL. Reassign still has 10 MCP tools, and the public OpenAPI specification still has 26 operations. Refer to Events, Scheduling, Errors, Authentication, and MCP server.

Error details, category deletes, and copy styles (main 10a32db0)

2026-10-07

The API specification is generated from main commit 10a32db05027a14c67700b66a5417745b2764792, tag v0.2.14.

BeforeNow
Breaking. A create or a rename to the name of a category that exists fails with validation (422).It fails with conflict (409). The message gives the id of that category. This applies to POST /categories and MCP manage_categories.
Breaking. A change of plannedDate or plannedUntil on an Inbox block that a task app owns fails with permission.It fails with read_only. The message is the same.
Breaking. A create id in POST /events and MCP write_events can be any string.It must be a UUID. Else the op fails with validation: "id must be a UUID; omit it to let the server generate one."
The error object has only code and message.It can also have issues, feature, conflictingRows, conflictingRowsTruncated, usage, and reassignTargets. In a rejected batch, these fields are only in the rows. MCP tool errors have the same fields.
A 422 shows only the first bad field.error.issues lists up to 20 bad fields, each with path, message, and an optional expected format. The message still names the first bad field. Bad op rows also have issues.
The plan refusal is { code, message }.REST adds feature: "api". The MCP JSON-RPC 403 adds data: { "code": "permission", "feature": "mcp" }.
You cannot delete a shared default category.The delete hides it, and your fork of it, for your account only. deletedIds can have two ids.
A delete of a category that events use needs reassignTo.A delete of a category that events, Inbox items, trackers, or calendar defaults use needs reassignTo. null clears the links. A refused delete gives usage and reassignTargets.
An event write has no copy style.mirrorStyles sets each copy to full, private, or busy. GET /schedule gives the default of each calendar as copyStyle and busyCopyTitle.
A series can have copies with no home calendar.A series with copies needs a home calendar, else the op fails with validation. A one-off event on the dial only can have copies.
classification_pending says that AI can change the area, the activity type, or the kind.A create keeps its kind, and the areaId and activityTypeId that it sends. On update, null is a manual choice of no link, also in the Inbox.
Each write that changes data returns an undoToken. A stale undo row has only a message.The receipt is optional, so a response with no undoToken does not prove a no-op. A stale row lists up to 50 conflictingRows. Undo stamps now cover the Inbox area and activity type.
Event search includes the events that start in the range.GET /events/search and MCP find_event include each event that overlaps a day of the range.
An empty discard of a day review returns empty lists.It also returns noop: true.
nearestSlots start on a 30-minute mark.They start on a 15-minute mark, at a gap edge, or at the free minute nearest to the request. Window slots keep the 30-minute grid.
A reversed POST /schedule/plan window repeats the sent values in its message.The message is `latest` must be a local datetime after `earliest`.
A fixed-start POST /schedule/plan request checks for conflicts as a blocking event.A fixed-start non_blocking or reference request does not fail with conflict.
GET /schedule has no AI rule warnings.integrations.aiRuleWarnings lists the areas and activity types in the AI rules that do not exist or are not unique.
POST /command shows the full span of an event from the day before.The model sees only the part after midnight. More dropped items now get a notice.
The docs do not say what an update on the id of a deleted occurrence does.It puts the occurrence back with the sent fields.
The MCP server version is 0.0.1.It is 2026.10.3.1, plus the build commit.

Inbox undo tokens from before this release can fail with stale until they expire, after 30 minutes or less. Reassign still has 10 MCP tools, and the public OpenAPI specification still has 26 operations. Refresh tools/list, and update code that reads the codes of these errors. Refer to Errors, Events, Categories, Backlog, Scheduling, and MCP server.

Agent feedback and write checks (main fa63b006)

2026-10-02

The API specification is generated from main commit fa63b0068586c666680901eceea23fedd99f8493, which includes PR #1330.

BeforeNow
MCP send_feedback takes one top-level kind and message.Send reports, an array of 1 to 10 { kind, message } items, in one call per session. An optional top-level submissionId covers the whole submission. REST POST /feedback keeps its single-report body and HTTP 204 response.
Each feedback submission can send an acknowledgment email.The user receives at most one acknowledgment per UTC day. Grouped MCP reports reach the team in one email.
MCP write_events always attempts the write.Optional dryRun: true checks the ops without applying them: valid ops are skipped, failures keep their usual errors, and there is no undoToken. REST POST /events does not accept this flag.
A successful event-write row gives no notice of queued AI classification.It can include warnings: ["classification_pending"]: the saved event's area, activity type, or kind may change after the reply.
Schedule reads return all free slots.Optional minDuration (1 to 1440 minutes) filters freeSlots in MCP get_schedule and REST GET /schedule. Events and the full spans of qualifying gaps stay unchanged.
Event search sorts matches without returning their score.MCP find_event and REST GET /events/search include score from 0 to 1 on each event. Inbox matches have no score.
A changed recurring occurrence omits recurrence and can omit inherited calendar fields.It carries the series' recurrence and inherits calendar fields unless it chose its own calendar. Its stable id still uses the original date. nextOccurrence uses a moved occurrence's actual date.
A scope: "future" update can leave an override on the split date unchanged.The edit applies to that occurrence too. A time change replaces the override with the new series occurrence; other edits keep its own time. Later overrides keep their individual fields.
reflect can mark an event on today or a future day.The occurrence must be on a past day in the account's time zone, as with review_day confirmation.
Analytics can add a required llm_model argument to MCP tool schemas.Model capture is off. The server strips stale llm_model and conversation_id arguments until 2026-12-01.

Refresh tools/list and update saved feedback calls. Reassign still has 10 MCP tools, and the public OpenAPI specification still has 26 operations. Refer to MCP server, Events, and Scheduling.

Scheduling changes (main 27130c49)

2026-10-02

These changes are live on reassign.app. The API specification is generated from commit 27130c4985518827e3180511a76945b789988f76, which includes PR #1329 and Chrome extension 1.3.2.

BeforeNow
MCP has schedule_events and confirm_schedule.Both tools are removed. Read freeSlots with get_schedule, choose a span, and create with write_events. For an existing Inbox item, keep using manage_inbox with op: "schedule".
POST /schedule/plan can return proposed options and a commitToken.Every successful request books immediately and returns event. An exact start books there when free; a window books its top-ranked free slot. A window with no slot fails with conflict.
POST /schedule/confirm books a proposal.The endpoint is removed. Read GET /schedule to choose a time before writing, then send the chosen span to POST /events or an exact-start request to POST /schedule/plan.
A window can use autoCommitBest: false to request options.autoCommitBest is deprecated. A window accepts only true or an omitted field; both book immediately. false fails with validation, as does the field with an exact start.
Window retry matching includes autoCommitBest.Omitting it or sending true matches the same booked request. Keep the same requestId when retrying within 60 seconds. Older open proposals no longer replay.
Chrome extension capture sends autoCommitBest: true.Extension 1.3.2 omits the field. Its capture retry keys remain compatible with 1.3.1.

Reassign now has 10 MCP tools. Refresh the tool list in your AI app and update saved calls. The optional analytics tool get_more_tools can appear as an eleventh tool. The public OpenAPI specification has 26 operations. Refer to MCP server and Scheduling for the current flows.

MCP changes (main 59d36f40)

2026-10-02

PRs #1327 and #1328 reduce the Reassign MCP tool set from 14 to 12 tools. Refresh the tool list in your AI app and update any saved tool calls. The old tool names are no longer registered.

BeforeNow
scheduleschedule_events, with the same arguments and results.
undoundo_changes, with the same arguments and results.
manage_backlogmanage_inbox. Its operations, including schedule, keep their names.
get_weather and get_energyUse get_schedule with includeWeather: true or includeEnergy: true. Set from and to to the same date. weatherLocation replaces the weather tool's location argument and requires includeWeather: true. The reports are strings in weather and energy.
find_event searches scheduled events only.It also returns inbox, inboxMatchedCount, and nextInboxOffset. Send inboxOffset to page through up to 50 Inbox matches at a time. Date and time-of-day filters apply to events only; ambiguous also counts events only.
MCP get_schedule takes backlogQuery.Use find_event to find an Inbox item by name. includeBacklog, backlogPlannedOn, and backlogOffset still work in get_schedule.
A weather report for another place does not label its time zone.The report names the IANA time zone used for its date and times. This also applies to REST GET /weather with location.
A failed scheduling confirmation says Re-run schedule.It says Re-run schedule_events, also in REST POST /schedule/confirm error messages.

REST keeps its existing route names, GET /schedule backlogQuery, and separate weather and energy endpoints. GET /events/search continues to return events only. The optional analytics tool get_more_tools can appear as a thirteenth tool. See MCP server for the current tools and examples.

API changes (main 7d09cfa2, v0.1.80)

2026-10-02

These changes are in the app release v0.1.80.

BeforeNow
POST /schedule/plan and POST /schedule/confirm (MCP schedule and confirm_schedule) are not atomic. Each item succeeds or fails alone.They are atomic by default. When one row fails, the server writes nothing, and the other rows are skipped. Send partial: true for the old behavior. A replayed row stays ok. Refer to Scheduling.
A taken exact start in a plan request returns options and a commitToken.It fails with conflict, conflicts, and up to 5 nearestSlots. To take a slot, send its start as a new request.
A plan request has no calendar fields.It takes calendarId and mirrorCalendarIds, as an event create does.
A confirm of a token that you already confirmed gives This token is not awaiting commit (status: active).It gives This token is already committed. Use its undoToken to reverse it. A token two times in one call gives Duplicate token in this call.
An undo row result has a shape for each kind of write. A command, category, or Inbox undo gives reversed.Every undo row is { reverted, restored, removed, voided, occurrenceRestored? }.
An undo reverts a row also when the row changed after the write.The undo fails with the new code stale (409) and changes nothing. A token from before this release is also stale. Refer to Stale tokens.
An occurrence gives its series only in the part of its id before @.Each occurrence has seriesId and originalDate in all reads and write echoes.
GET /schedule always gives areas, activityTypes, and integrations.includeLookups=false leaves them out. A series row also has kind, source, calendarId, mirrorCalendarIds, and readOnly. It has no notes now.
GET /events/search (MCP find_event) gives the occurrence of a series that is nearest to today.It gives the occurrence that matches best. On a tie, it gives the one nearest to today.
POST /backlog capture and capture_text make a block in Reassign only.They take calendarId, the Source of the new block. A task list Source also creates the task in that list. With no calendarId, the block takes the default Source. Refer to Backlog.
A park loses the planned day and the ticked steps.The Inbox block keeps the day of the event as plannedDate, and it keeps the ticked steps.
POST /reviews/{date} confirm gives no warning on a day with no marks. discard on a day with nothing recorded returns 404.confirm adds a warning. discard returns 200 with empty lists and no undoToken.
adherence.event and adherence.layer are 1 on a day with no planned time.They are null. Refer to Day reviews.
The energy and weather reports, and the MCP prose, use 12-hour times (3pm).They use 24-hour times (15:00).
An MCP tool refusal is a text message. A tool result can have a second {"conversation_id"} block.A refusal is the JSON error envelope, also for a prose tool. A result has no conversation_id block. Refer to MCP server.

API changes (code-cleanup 7b08651c)

2026-09-29

These changes reach reassign.app with the next app release.

BeforeNow
POST /command gives the answer to a question in notices.It gives the answer in reply, a maximum of 400 characters. A request that is not about the plan gets I can only help with your plan. Refer to Quick command.
POST /command intents are create, update, shift, delete, clear, park, log_moment, and checklist.New intents: checklist_edit, split, remember, reflect, and day_note. update can also change end, name, kind, areaId, activityTypeId, and the notes. update, checklist, and checklist_edit have scope for a recurring event. create can have checklist.
POST /command always plans changes.A line command about a past day, or about how today went, runs in review mode. It records marks and day notes, and it does not change the plan.
POST /command: a clear skips calendar-synced events with no notice.It adds the calendar-synced notice for each event that it skips.
The limit of 600 requests per minute counts each Bearer token. An OAuth token that is not valid can get 429.It counts each OAuth grant (all its access tokens together) or each personal access token. An OAuth token that is not valid gets 401 first. Refer to Errors.
A refresh token from before rotation works one time.It fails with 400 invalid_grant. A failed grant read also gives 503 temporarily_unavailable.
get_schedule and GET /schedule give integrations.sources[].account only to first-party callers.Every caller gets account. GET /calendars still gives account only to first-party callers.
GET /events/search with only a to more than 7 days in the past returns 422.The default start moves, so that the window is valid.
A reflect op accepts an actual span of any length.The span must be from 5 minutes to 168 hours.
A batch can have more than one scope: "future" op for a series.A batch can have one scope: "future" op for each series, and no op on the bare series id with it.
The clear result has no count of the read-only events that it kept.It gives skippedReadOnly.
POST /backlog: only Google Tasks, Microsoft To Do, and Linear accept a date change, and only for one day.Todoist and TickTick also accept it, and a plannedUntil is allowed. A task that recurs in the task app still fails with permission.
An areaId or activityTypeId of a shared default that you forked links to the shared default.It links to your fork.

API changes (develop 9d9856b6)

2026-09-24

These changes reach reassign.app with the next app release.

BeforeNow
A change to one occurrence gives it a new id, and the event has derivedFromId (the series id).The occurrence keeps the id seriesId@<original date>, also after a move to another day. derivedFromId is removed. The series id is the part before @. An id from an old write result still works. Refer to Core concepts.
GET /events/{id} with the occurrence id of a moved occurrence can return 404.It returns the moved occurrence.
POST /schedule/plan (MCP schedule) books a window request when only one slot fits.A window request books only with autoCommitBest: true. Otherwise it returns options and a commitToken, also for one slot. A free exact start still books.
A plan request with the same content in 60 seconds replays the first result, also with no requestId.Only a requestId replays (60 seconds, same content). A request without requestId is always new.
calendarId: null on update removes only the home link, and it cannot carry other fields.It also deletes all mirror copies. It can carry start, end, name, notes, areaId, activityTypeId, and kind. It works on a read-only event. It still refuses mirrorCalendarIds, recurrence, recurrenceEnd, sourceUrl, and focusIntervals.
An update with mirrorCalendarIds checks every id. Thus, a kept copy on a read-only or disconnected calendar fails.The API checks only the added ids. A kept id can stay.
The API has no limit for each token.600 requests per minute for each Bearer token: 429 rate_limited, "Too many requests.", Retry-After: 60. The MCP server has no such limit. Refer to Errors.
GET /schedule gives integrations.sources[].account to every caller.Only a personal access token and the Reassign apps get account. A third-party OAuth app and the MCP tool get_schedule do not.
A bad-op row has the message Invalid "create" op. name: …. An op that is not an object gets Unknown op undefined. ….The message is Invalid "create" op. `name`: …. An op that is not an object gets "Each op must be an object." This applies to POST /events, POST /backlog, POST /categories, and the MCP write tools.
POST /command: an unknown area or activity type on a new event or Inbox item skips the item.The item stays without that label, with a notice. A name in an id field that matches one row resolves to its id. An unknown calendar still skips the event.
POST /command: a new park item keeps any positive durationMinutes.A value that is not from 5 to 1440 drops the item, with a notice.
The MCP tools/list has 14 tools.It can also have get_more_tools. This tool records a missing-tool report and changes no data. Refer to MCP server.
An OAuth refresh replay in the 60-second window gets the scope of the token that you sent. Thus, a replay of an old token can widen a family that a refresh made narrower.It gets the scope of the live token, cut to the scope of the token that you sent. It cannot widen the scope.

API fixes (PR #1071)

2026-09-24

These fixes reach reassign.app with the next app release.

BeforeNow
POST /events/batch, PATCH /events/{id}, DELETE /events/{id}, and GET /reviews/{date} get HTTP status 405401 unauthorized without a valid token, else 404 not_found. The Reassign Chrome extension 1.1.x gets 422 validation. Refer to Errors.
A token with only events:read and a bad body on a write route gets 422 validation403 scope. A write route checks the scope before the body.
A bad op in POST /backlog or POST /categories fails the full call with 422The bad op is an error row with the code validation.
An atomic POST /categories call that fails gives an error row for each op, with the message "Batch rolled back: …"Only the failed op is an error row. The valid ops are skipped rows with a reason.
The backlog schedule op does not check the span or conflictsThe span is 5 minutes to 168 hours. An overlap with a blocking event fails with conflict, conflicts, and nearestSlots.
Backlog durationMinutes is 1 to 1440Backlog durationMinutes is 5 to 1440
A POST /events or POST /backlog call can get 503 after its writes changed dataHTTP status 200 with the undoToken. A row that the server cannot read again has the code internal.
A tracker has isGlobal and overridesId only when setisGlobal is always present. overridesId is always present, null when not set.
The order of an area or an activity type can be nullorder is always an integer
A create or park intent of POST /command has areaId and activityTypeId only when setBoth are always present, null when not set
A log_moment intent of POST /command changes a rating that is out of bounds to the nearest boundThe command drops the log with a notice, for example "A log was skipped because its value 7 is not from 1 to 5." A rating that is not a whole number is also dropped.
PATCH /me works with a token that has events:writePATCH /me serves the Reassign web app only. A token gets 403 permission.
The error code ambiguous is in the list of codes. No route sends it.Removed

API v1 contract change

2026-09-23

The REST API and the MCP server now use one contract: one name and one format for each value. The docs show the new contract. Refer to Core concepts.

General rules

  • A request with an unknown key or an unknown query parameter now gets HTTP status 422. Before, the API ignored it.
  • A body that is not valid JSON now gets HTTP status 422. Before, the API read it as {}.
  • Enum values are lowercase snake_case, for example non_blocking, hatch_r, google_tasks, and log_moment.
  • A time stamp is an ISO 8601 instant in UTC, for example "2026-09-23T07:00:00.000Z". Before, some fields used milliseconds since the epoch.
  • A response with a span gives timezone one time, at the top level.
  • A batch write that changes data returns undoToken and expiresAt at the top level. A write to /moments or /trackers returns no undoToken.
  • A create on /moments and /trackers now answers with HTTP status 201.

What changed

BeforeNow
A span as date and "HH:MM" timesstart and end as local datetimes "YYYY-MM-DDTHH:MM"
endNextDay, crossesMidnight, endsAtDayBoundary, continuesFromPrevDayRemoved. A span past midnight has its end on the next date. Midnight is "<next date>T00:00".
startHour, endHour, durationMinutes, label, and isRecurringInstance on an event, and startIso and endIso on search and scheduling resultsRemoved. Use start and end.
An event is 5 minutes to 24 hours long, on a 5-minute gridAn API write is 5 minutes to 168 hours long. The server keeps the exact minute.
syncTocalendarId
mirrorTomirrorCalendarIds
areaName, activityTypeName in a writeRemoved. Send areaId and activityTypeId only.
area and activityType objects on an eventareaId and activityTypeId, always present, null when not set. GET /schedule gives the areas and activityTypes lists.
occurrenceDateThe occurrence id seriesId@YYYY-MM-DD
scope: "this", "future", or "all"scope: "future" only, with an occurrence id. An occurrence id alone changes that occurrence. A series id alone changes the full series.
recurrence presets (daily, weekly, …) and "none"An RRULE only. null stops the series.
kind: "non-blocking"kind: "non_blocking"
POST /events with one event, POST /events/batch, PATCH /events/{id}, and DELETE /events/{id}POST /events with { ops, partial? }, the one event write endpoint
The move opThe update op with a new start or end
The clear op with date and toThe clear op with from and to (both required)
GET /events?date= (the events of one day)GET /schedule?from=&to=
GET /events?updatedSince= and a numeric cursorGET /events?cursor=, with an opaque cursor string
GET /reviews and GET /reviews/{date}Removed. GET /schedule gives a review block on each confirmed day.
applied, failed, and skipped counts in a batch resultRemoved. Read the status of each row in results.
A row error string and errorCodeA row error object: { code, message, conflicts?, nearestSlots? }
A rejected batch body without the error object{ error, timezone?, results }. When the failed ops have different codes, the status is 400 and the code is batch_rejected.
batchUndoToken, and an undoToken in each rowOne undoToken and expiresAt at the top level
expiresInMs, and expiresAt in millisecondsexpiresAt (an instant)
suggestedFollowup text in an MCP result, and the concise argument of write_eventsRemoved
request_id in a scheduling requestrequestId
duration (for example "90m") and date in a scheduling requestdurationMinutes (a whole number), and start or earliest and latest as local datetimes
GET /schedule with date, dates, or compactGET /schedule with from and to (both required)
now as an object in GET /schedulenow as a local datetime, and timezone at the top level
userId in GET /meid
trialEndsAt in milliseconds, only on a trialtrialEndsAt as an instant, or null
email in GET /me only when presentemail, always present, null when there is no address
focusIntervals with plannedIntervals and completedIntervalsfocusIntervals: { focusMin, breakMin } and completedFocusIntervals
checklist: { items, done, total } on an eventchecklist: { items }
reflect: { state, actualStartHour, actualEndHour, … }reflect: { status, actualStart?, actualEnd? }
durationHours and steps in the InboxdurationMinutes and checklist: { items }
eventId in the park opid
date, hour, and trackerName on a momenttime (a local datetime)
GET /moments?date= and DELETE /moments?date=GET /moments and DELETE /moments with from and to (both required)
deleted, reassignedTo in a category delete resultdeletedIds
isDefault on a calendarRemoved. Compare the id with defaultCalendarId.
notes: "" on an event with no notesnotes is not there

API v1 documentation published

2026-09-23