This changelog was started on 2026-06-08. Entries below this date are documentation changes; behavioural API changes will be recorded here going forward.
2026-08-28
Idempotent writes (docs)- Send
X-Idempotency-Keyon a write and the API runs it at most once: a retry carrying the same key returns the original result instead of creating a second record. Optional and additive — a partner that sends nothing gets exactly today’s behaviour. - The key is any string up to 128 characters, read case-insensitively. A longer one is refused with
400 VALIDATION_ERRORrather than ignored, because answering200to a caller who believes they are protected is the worse failure. - Corrected: Retries & idempotency previously stated that the API does not support idempotency keys, and its
POSTguidance was built on that. Both are rewritten. Generate the key before the first attempt and reuse it across retries — a key minted per attempt protects nothing. - Over MCP the same protection is an optional
idempotency_keyon every additive write tool (catalogue). Omitted, a key is derived from the payload, so an identical retry within about ten minutes is treated as the same action — which is what stops a retrying agent from silently creating two schedule items.
pagination object alongside data, additive and absent on unpaged reads.
totalCount,returnedCount,hasMore,truncatedandnextOffset, onGET …/schedule,…/venues,…/accommodation,…/guestlist,…/bulletinsand…/passTypes.returnedCountalways equalsdata.length.- Page with
nextOffset, not withreturnedCount.nextOffsetcounts the rows the server consumed, which can exceedoffset + returnedCountwhen rows were dropped from the page after being counted — advancing by the row count would re-read them on every iteration. A page the server consumed nothing from reportshasMore: falseandnextOffset: null, so awhile (hasMore)loop terminates — whilereturnedCount: 0withhasMore: trueis legitimate and means every row on that page was filtered out after being counted. truncated: truemeans the response ceiling shortened the page, not yourlimit— a smallerlimitwill not lengthen it; only paging will. This is why a short page is not a reliable end-of-list signal.- Deliberately conservative: a response that cannot report both counts omits the object entirely rather than returning a half-filled envelope that would read as a complete page.
- Over MCP the same six tools append a one-line
[pagination]note whenever a page is incomplete, naming theoffsetto call back with. A complete, untruncated page carries no note, so a healthy read is unchanged.
POST /v1/schedule/itemsandPUT /v1/schedule/items/{itemId}now return the item’s reminders with their server-generated ids when the request sent areminderlist. Previously there was no way to learn those ids from a write — and because areminderentry submitted without anidis created as a new row, a client that could not keep them duplicated its reminders on every save.- The array is the item’s reminders after the write, sorted by
id, read inside the same transaction. It carriesid,description,durationandis_sent— the same fields the list response already serves underitemReminder[].reminder. - Present only when the request sent
reminder; a write that did not touch reminders answers{ "id": … }exactly as before.POST /v1/schedule/items/batchdoes not echo them. - Over MCP, the same rows come back from
create_schedule_itemandupdate_schedule_item.
2026-08-27
95 → 102 endpoints, 97 → 106 MCP operations. Everything here is additive: no existing endpoint, field, or scope changed, and no scope was added. The one thing that changes for an existing integration is the MCP tool list — see the note below. New: a first-class file contract (docs) Files were upload-only:POST /v1/attachments fetched a public URL and handed back a key, and there was no way to list a tour’s files or turn a stored key into something fetchable. That endpoint is unchanged and still supported, and every upload source mints the same kind of key; five endpoints join it.
GET /v1/tours/{tourId}/files— every file on the tour, across schedule items, venues, stays and pass types, filtered to what you can see. Filter byentityTypes,dayIdor a namesearch; paged with an exacttotalCount.GET /v1/tours/{tourId}/files/{fileId}/download— a short-lived signed URL (about five minutes). Not a permanent link; request a fresh one rather than storing it.POST /v1/tours/{tourId}/files/content— upload the bytes inline as base64, max 256 KB, for when you have the file and no public URL to point at.POST /v1/tours/{tourId}/files/upload-ticket— a signedPUTfor a large local file.content-lengthandcontent-typeare part of the signature, so the upload is bounded by storage itself.POST /v1/tours/{tourId}/files/attach— attach an uploaded key to a schedule item, venue, stay or pass type. Additive (it never touches the entity’s existing attachments, unlike an update carryingattachments) and idempotent, so a retry is safe.- MCP equivalents:
list_files,get_file,upload_file(three sources) andattach_file, all advertised by default. - Corrected: attachments were documented as supported on guest list entries. They are not — an
attachmenton a guest-list body is not stored. Pass types are attachable and were undocumented. The four attachable kinds are schedule items, venues, accommodation and pass types.
POST /v1/attachments
- Uploads previously accepted whatever content type the source declared. There is now an allow-list: images, PDF, Office documents, text/CSV/Markdown/iCalendar/JSON, ZIP, audio and video.
image/svg+xml,text/html, scripts and executables are rejected with415, and the error lists what is accepted. Attachment files are served over HTTP, so an SVG or an HTML file stored among them is an active-content risk rather than a document. - If you were relying on uploading one of those types, this is a breaking change for that call. Everything else is unaffected.
GET /v1/tours/{tourId}/days/{dayId}/day-sheet/datareturns one day assembled as a print-ready model, andPOST /v1/tours/{tourId}/days/{dayId}/day-sheetrenders it as a PDF behind a short-lived signed URL. Non-destructive — the render creates nothing and changes no record.- Both ride
tours.readplus the other five read scopes, so no re-grant is needed for a partner that already holds them.
- The server had reached 97 advertised tools / about 33,000 tokens of definitions loaded before a client had read your question, which is more than a client can usefully reason about. It now advertises a curated 25 (~6,500 tokens) by default, and the other 81 operations are reached through
list_capabilities→describe_operations→invoke_operation.describe_operationsreturns the same JSON Schema the tool list used to carry, so nothing is harder to call correctly — it just arrives when you ask for it. invoke_operationis not a generic “call any endpoint” tool:operationmust name an operation in the catalogue that your connection is authorised for, and its input is validated against that operation’s real schema. An operation outside your consented scopes is refused withINSUFFICIENT_SCOPEnaming the scope needed.- Every tool name that ever existed is still callable by name. An integration that hardcodes
create_pass_typeneeds no change. A client that enumerates tools and wants the old list can pin it with?tools=allon the MCP URL, or thex-daysync-mcp-tools: allheader. - An existing MCP session must reconnect before any of this appears — tool lists are cached from session start.
- Pagination was documented as not existing. The page said list endpoints return all matching records and that there are no
limit/offsetparameters. Both halves were wrong:limit/offsetare accepted, and every day-scoped list read is capped server-side at 1,000 rows and shortened further if the response would be too large to serialize — whether or not you send a limit. A list response is not necessarily the whole list. The sync recipe has been corrected too, because a mirror built on the old text would delete rows that are still live. - The endpoint count (95) and the MCP tool count (97 in one place, 81 in another) were stale on the overview, Scopes and the MCP overview. The scope-per-endpoint table was missing the seven endpoints above.
- Endpoints that need more than one scope listed only the whole-tour read. There are now five.
- Pass types were described as something you configure in the Daysync app. They have had full CRUD over the API since 2026-08-21.
2026-08-21
79 → 95 endpoints, 81 → 97 MCP tools. Everything here is additive: no existing endpoint, field, or scope changed, and no scope was added — the new endpoints rideschedule.* and guestlist.*, so no partner re-grant is needed. An existing MCP session must reconnect before the new tools appear.
The whole-tour read now identifies its days
GET /v1/tours/{tourId}/datareturned days carrying only an ID, content counts, and child resources — so you could not tell which day you were holding, and had to make a secondGET /v1/tourscall and join on the day ID. Every day now also carriesname,date,time_zone,type_id({ id, type, description }),formattedAddress,placeFormattedAddress,place_idandviewport, using the same field names as thedayobject inGET /v1/tours. Day types were never unavailable — they were just absent from this endpoint.- The same response now also exposes
venueCount,passTypeCountandtruncated, so a day whose collections were capped is distinguishable from a day that is genuinely short. - A field the day does not have is an explicit
null, never an omitted key.
- Eight endpoints for saving a set of schedule items as a reusable template and applying it to another day: list, read items, save, rename, delete, restore, apply (
MERGEorREPLACE), and undo an apply. - Templates are per user — you only ever see your own — and a template can only be applied to a day of the tour its items came from.
POST /v1/tours/{tourId}/passTypes,PUT/DELETE /v1/passTypes/{passTypeId}, andPOST /v1/passTypes/{passTypeId}/restore. Pass types were readable but not manageable, which made the guest-list workflow impossible to set up over the API.DELETEwithoutforce=trueis a safe check: if any guest still holds the pass it deletes nothing and returnsstatus: falsewith the blocking entries indata.guestLists.
GET …/guestlist/approved-count— the approved-guest total for a day (a sum of party sizes, not a row count).PUT …/guestlist/config— day cap, lock, cut-off, and per-pass-type allocation caps, optionally scoped to one venue.PUT /v1/tours/{tourId}/guestlist/status— bulk approve/decline, all-or-nothing across the batch.POST /v1/guestlist/{guestListId}/restore— restore a deleted entry with its pass allocations.
5xx into the 4xx it should always have been, which matters because a 5xx reads as retryable and none of these are.
- A validation failure was a
500. Any rejection from body or query validation returned500with"error": "VALIDATION_ERROR"in the body — the status and the body disagreed. It is now400, and the body’sstatusfield agrees with the HTTP status. Affected?org_id=, guestemail, schedulestartTime, and every other validated field. - A malformed ID in the path was a
500or an opaque error.tourId,orgId,dayId,itemId,venueId,accommodationId,roomAssignmentId,bulletinId,tagId,templateId,passTypeIdandguestListIdare now validated up front and answer400 VALIDATION_ERRORnaming the parameter.GET /v1/tours/{tourId}/tags/{tagId}/usagepreviously returned a bare500 Internal Server Error. DELETE /v1/passTypes/{passTypeId}reported success when it had deleted nothing. Every2xxresponse hardcodedstatus: true, so the safe check above came back{ "status": true, "message": "Success" }. Responses now carry the operation’s real outcome. This is the one place where readingstatusrather than only the HTTP code matters.- A day-level guest-list cut-off on a day with no venue was a
500. It is now400with an explanation, because the cut-off is resolved against a venue’s time zone. dayIdson the whole-tour read is now scoped to the tour in the path. A day ID belonging to another tour is absent from the response instead of being served.
2026-08-10
Behavioural fix- A request body of literal
null(or a bare number, string, or top-level array) returned500on write endpoints —POST /v1/tours,POST /v1/venues,PUT /v1/venues/{venueId}andPOST /v1/schedule/items/batch. It now returns a4xxfrom the endpoint’s own validation, as it always should have. Worth noting because a5xxreads as retryable and a malformed body never is.
- New: explicitVisibility is now documented on schedule items, venues and stays. This field was always accepted but never described, and the default behaviour surprises people: naming a restricted audience merges the tour’s current admin list into it, so
visibilityList: ["alice-uuid"]on a tour with four admins is visible to five people. SendexplicitVisibility: trueto mean the list literally — on updates as well as creates. Do not send it withisAdminsOnly: true, which is implemented by that same merge. - Documented six endpoints that shipped on 2026-08-07 but had no reference section:
GET /v1/tours/{tourId}/data,POST /v1/schedule/items/batch,POST /v1/schedule/items/{itemId}/restore,POST /v1/tours/{tourId}/days/{dayId}/confirm-all,GET /v1/venues/{venueId}andGET /v1/accommodation/{accommodationId}. - Corrected:
GET /v1/reference/typeswas documented with alimitdefault of24— the number of tables that existed at the time. The default is uncapped and returns all of them; passing alimitequal to today’s count would silently drop any table added later. - Corrected: the
totalUnreadexample on Chat didn’t reconcile with its own conversation list.totalUnreadcounts only the conversations this API returns, so it is lower than the in-app unread badge — the app counts direct messages, which this API never exposes. That is not missing data. - The schedule response example now shows
schedule_item_tagsand the visibility flags, which the Tags page already told you to read from it.
2026-08-07
Major expansion — 29 → 79 endpoints, plus the MCP server The API now covers essentially the whole Daysync feature set. Everything here is additive: no existing endpoint, field, or scope changed, and no integration needs updating. New: MCP server- Daysync now runs a hosted MCP server at
https://mcp.daysync.com/mcp, exposing 81 tools over the endpoints below. Connect it to Claude or ChatGPT and work with tours in plain language. See the new MCP Server section — connect Claude, connect ChatGPT, tool catalogue, troubleshooting.
chat.read— the first addition to the scope catalogue since launch, taking it from 14 to 15. Read-only; there is nochat.write. Existing partners are unaffected until they are granted it.
- Full CRUD on the tags that label schedule items, plus usage counts and
PUT /v1/schedule/items/{itemId}/tagsto set an item’s tags. Rides the existingschedule.read/schedule.writescopes, so no re-grant is needed. GET /v1/tours/{tourId}/days/{dayId}/schedulenow returnsschedule_item_tagson each item. Tags were previously invisible over the API.
GET /v1/reference/typesreturns every ID lookup table in one call — status types, item types, time types, travel types, reminder types, avatar colours, day types, guest list statuses, pickup methods, pass material types, venue types and categories, and room types.- Plus
GET /v1/reference/age-restrictions,/v1/reference/venue-field-templates,/v1/reference/stay-field-templates, andGET /v1/tours/{tourId}/requesters. - These close a real gap: several write endpoints required IDs that had no read endpoint at all.
- Ten endpoints for reading channel and group-chat history — conversations, messages, threads, pinned messages, reactions, members, and search.
- 1:1 direct messages are never exposed. They are filtered from every list and search, and requesting one by ID returns
404.
GET /v1/tours/{tourId}/datareturns every day with its schedule items, venues, stays, guest lists, bulletins, and pass types in a single response — replacing six paged calls for the common “load me this tour” case.- This is the first endpoint requiring more than one scope: it needs the read scope for all six resources it returns. See Scopes.
- Custom fields on schedule items, venues, and stays — read plus per-field
ADD/UPDATE/REMOVE, and premade templates on venues and stays. - Contacts — the organization key-contact book (list, create, update) and the contacts linked to venues and stays. The book is what makes the previously-accepted
keyContactIdsfield usable. - Rooms — list, create, update, delete, and restore the rooms within a stay.
GET /v1/venues/{venueId}andGET /v1/accommodation/{accommodationId}return the full record, including contacts, custom fields, and visibility.POST /v1/schedule/items/batchcreates many items in one call;POST /v1/tours/{tourId}/days/{dayId}/confirm-allconfirms a whole day;POST /v1/schedule/items/{itemId}/restorerestores a soft-deleted item.
GET /v1/tours/{tourId}/days/{dayId}/schedulereturned500for any day containing a tagged item, while tag support was being added. Fixed before release.POST /v1/accommodation/{accommodationId}/roomsreturned an unhelpful400when creating a room with no occupants. It now works withguestListomitted.- Server-side (
5xx) failures are now logged with the route that produced them, so support can trace a failed call.
2026-07-09
New endpoint- Added
POST /v1/attachments(scopeschedule.write) — upload a file by URL for attaching to schedule items, venues, accommodation, and guest list entries. Daysync fetches the URL server-side (SSRF-guarded: public https/http only, no redirects, 25 MB, 15 s) and returns a hostedlinkto pass in theattachment/attachmentsfield. New Attachments page. - Clarified that
attachment.linkis a Daysync storage key (from the upload endpoint), not an external URL — corrected the Schedule → Attachments guidance and the Venues note. - Endpoint count is now 29. (Superseded — see 2026-08-07.)
2026-06-12
Documentation- Documented
GET /v1/day-types— live since launch but previously missing from the docs. It returns the validday_type_idvalues required when creating or editing tours. - New pages: Apply for API Access, Callback URLs, and Environments.
- Rewrote Authentication — clarified the two-credential model (shared OAuth Client vs per-partner API keys), added an OAuth error reference, token-type guide (
access_tokenvsid_tokenvsrefresh_token), refresh/revocation behaviour, and JWKS details for server-side validation. - Rewrote Quickstart as an end-to-end walkthrough — from received credentials to a refreshed token in five steps.
- Corrected the endpoint count (28) on the overview and added the missing scope row on Scopes & Permissions.
- Destructive-edit warning on Edit a tour —
day_listis declarative: days omitted from the list are permanently deleted along with all their content. Documented the safe single-day update pattern. - New Visibility and Attachments sections on the schedule page — where visibility IDs come from, the omit-everything footgun (
isEveryonedefaults tofalse), conflicting-flag400, link-based attachment model, and attachment upsert/delete semantics on update. - Every numeric type field (
type,startTimeTypeId,statusTypeId,itemColorId,venueTypeId,pickupMethodId, …) now links to its Field Reference value table. - Four previously undocumented ID catalogues added to Field Reference, verified against backend seed data: reminder types (
reminder[].typeId), travel types (traveltype, 11 modes), age restrictions (ageRestrictionId), and avatar colors (avatarColorId). Also noted that venuestatusTypeIdshares the schedule status-type table. - New Retries & idempotency section — per-method retry safety, the
POST-after-timeout duplicate trap, and which responses are worth retrying. - New Data model at a glance diagram — the org → tour → day hierarchy, which resources are day-scoped vs tour-scoped, and the ID type at each level.
- Documented the
/oauth2/userInfoendpoint for fetching the connected user’s profile without decoding JWTs. - Marked venue
venueCategoryIdas required-in-practice in the body table (was contradicting the note below it).
2026-06-08
Documentation- Added a Field Reference page covering schedule item types, accommodation types, and other enum/ID values.
- Documented the accommodation date/time format (epoch-milliseconds dates and minutes-from-midnight times).
- Added a Pass object reference and a full GET response field table on the guest list endpoint.
- Added a field-naming convention warning (snake_case vs camelCase).
- Added full error response body examples.
- Added Common Workflows recipes and operational notes (rate limits, versioning, webhooks, pagination).

