Every additive and breaking change to the /api/v1/ surface lands here with the date it shipped. Per the deprecation policy:

  • Additive changes do not require a version bump. Existing clients keep working.
  • Breaking changes carry a minimum 90-day window between announce and removal. Affected callers receive an email plus Deprecation and Sunset headers on the deprecated route.
  • Security exceptions may shrink the window; the call is documented inline.

2026-09-05 : Device-bound mobile tokens (additive, plus one scheduled breaking change)

  • POST /oauth/token/ with grant_type=password now creates a device session and returns device_id in the token JSON. Refresh grants rotate inside the same token family.
  • POST /oauth/revoke_token/ revokes the whole family: present the refresh token, and a pre-rotation refresh token still kills its live successor. A deleted access token returns the unknown-token 200.
  • Password logins carry a per-IP and per-identifier brake in addition to the existing per-IP token rate limit.
  • Every /api/v1/auth/* view rejects a JSON body that is not an object (list, string, number, null) with 400 instead of a 500.
  • Breaking, scheduled (security exception): refresh tokens issued before this deploy have no device session and are refused from 2026-09-19T00:00Z; the client must sign in again. Tokens issued after the deploy are unaffected.

2026-09-03 : 413 payload_too_large on both media upload routes (additive)

POST /books/{slug}/submissions/{id}/media/ and POST /me/books/{slug}/submissions/{id}/media/ now answer an oversized file with 413 payload_too_large, never 400 or 402. Two sources map to the one code: the 90 MiB whole-request ceiling, and the per-kind caps (20 MiB image, 50 MiB audio, the book plan's video limit). The code is contractual; the message text is not. Oversized uploads were previously reported as a storage-quota error.

2026-09-03 : Video poster thumbnail_url on media reads (additive)

MediaRead and the MediaSummary embedded in submission reads gain thumbnail_url: a signed URL for a video's poster frame, minted the same way as download_url (15-minute TTL, fail-closed null, only once processing_status == "complete"). null for images, audio and Live Photos. No version bump.

2026-09-02 : Live Photo pairing on the contributor media route (behaviour change)

POST /me/books/{slug}/submissions/{id}/media/ pairs a same-stem still and .MOV into one live_photo row (added 2026-08-30). Since 2026-09-02 the pairing runs on a worker after commit, so the 201 projects the row as created: a video that becomes a live_photo moments later, or an image whose motion_download_url appears moments later. Poll the timeline to observe the merge; do not read the merged shape from the POST body.

2026-08-30 : contributor_timezone on contributor create (additive)

POST /me/books/{slug}/submissions/ accepts an optional IANA contributor_timezone used to stamp the memory's local posting date. An invalid zone is dropped and the book's timezone is used; it is never a 422.

2026-08-09 : Account deletion requires step-up (BREAKING, security exception)

POST /me/delete/ no longer accepts the email-echo confirmation_token; a bearer credential alone can never erase an account. Send one of, in the body only:

  • reauth_token: a single-use, five-minute, delete-scoped grant from POST /me/delete/reauth/, which itself needs a fresh credential: the account password, or a delete-purpose OTP requested via POST /me/delete/reauth/otp/. Works on any auth transport.
  • password: a fresh password check, session auth only. A bearer presenting a password is rejected.

Shipped without the 90-day window because the removed proof proved nothing: GET /me/ returned the email to the same bearer.

2026-07-10 : cover_thumb_url on GET /me/books/ (additive)

Each item carries cover_thumb_url (string | null, always present): a signed absolute URL for the book's most recent approved, public, fully processed still or Live Photo. Videos are never used. null when there is none; fall back to a monogram.

2026-07-04 : Contributor timeline ordered by timeline basis (behaviour change)

GET /me/books/{slug}/submissions/ now orders by the book's timeline basis (memory date on a "happened" book, post date on a "shared" book) rather than raw created_at, matching the web and PDF. The cursor grew from two fields to three; a cursor minted before this change decodes as 400 cursor_invalid, and the client restarts from the first page. Pinning and curated display order are not applied; is_pinned is in the payload for clients that want to float pins.

2026-06-29 : Mobile engagement, Live Photo motion, edit affordance, push tokens (additive)

  • POST /me/books/{slug}/submissions/{id}/reactions/: toggle the caller's emoji reaction on an approved submission; returns the reaction state.
  • GET /me/books/{slug}/submissions/{id}/replies/ (approved replies, cursor-paginated) and POST to the same path to reply (201; 423 during a grace period).
  • Contributor submission reads gain can_edit: true only for the caller's own rows, in any moderation state.
  • MediaRead and MediaSummary gain motion_download_url + motion_status for a Live Photo's motion clip, signed and fail-closed like download_url.
  • POST /me/push/register/ ({token, platform="apns", environment, device_id?}; idempotent upsert on the APNs token, no Idempotency-Key) and DELETE /me/push/register/. Revoking a device session cascades to its push token.

2026-06-28 : Contributor write surface (additive)

Four bearer-only routes under /me/books/{slug}/submissions/: POST (create; always channel="mobile"; 403 not_a_member on a members-only book), PATCH …/{id}/ (edit own; any content change resets moderation to pending; If-Match/etag supported, stale → 412), POST …/{id}/media/ (upload; auto-dates memory_date from EXIF when unset), DELETE …/{id}/ (delete own; replays return 204 after the row is gone). All four require Idempotency-Key (428 if absent). Session and API-key callers get 401/403. Another user's submission and a missing one are both 404. Create and media upload are rate-limited 30 per hour per user (429 rate_limited); create, edit and delete return 423 during a billing grace period.

2026-06-27 : Contributor read surface, App Attest, device signup (additive)

  • GET /me/books/: cursor-paginated books the caller is a member of or has submitted to, accessible ones only. GET /me/books/{slug}/submissions/: the contributor timeline (everyone's approved public submissions plus all of the caller's own). Media on these reads is the transcript-excluded MediaSummary; transcripts stay on the owner surface.
  • POST /auth/attest/challenge/ (single-use nonce) and POST /auth/attest/register/ (Apple App Attest attestation; 400 attestation_invalid, reason not disclosed). Subsequent gated calls send an X-App-Attest assertion header; the counter must strictly increase.
  • POST /auth/signup/request/ (App-Attest-gated; 503 signup_unavailable when attestation is not configured, 403 attestation_required without a valid assertion, otherwise always 202) and POST /auth/signup/confirm/ (400 consent_required, 401 invalid_code, 409 account_exists, 200 token pair).

2026-06-25 : OAuth2 bearer tokens and native mobile login (additive)

  • Every /api/v1/ route accepts an OAuth2 bearer token alongside the API key and browser session. Tokens need the api scope; an inactive user is rejected on every transport.
  • POST /oauth/token/: grant_type=password and grant_type=refresh_token for the first-party public client.
  • POST /auth/otp/request/ and POST /auth/otp/login/: email or SMS one-time-code login that issues a token pair. Request always returns 202.
  • POST /auth/password/reset/request/ and POST /auth/password/reset/confirm/: password reset by email OTP. A login code cannot reset a password and a reset code cannot log in.
  • GET /me/devices/, DELETE /me/devices/{device_id}/, POST /me/sessions/revoke/ (revoke all).

2026-06-23 : Dating, engagement and transcript fields on the read schemas (additive)

  • SubmissionRead gains memory_date (nullable, owner-correctable), display_date (non-null ordering date resolved by the book's timeline basis), is_photo_album, reaction_summary (emoji to count) and reply_count (approved only). PATCH accepts memory_date; omitting it leaves the date, explicit null clears it.
  • BookRead gains timeline_basis (resolved, read-only) and timeline_basis_override (writable; "" or null inherits the use-case default).
  • MediaRead gains transcript, transcript_status, transcript_visibility (owner surface only).

2026-06-15 : Owner media upload auto-dates from EXIF (behaviour change)

POST /books/{slug}/submissions/{id}/media/ sets the submission's memory_date from the first image's EXIF capture date when no date is already set. An existing date is never overwritten.

2026-06-01 : MCP server (additive)

A read-only Model Context Protocol server exposes list_books, get_book and list_submissions under the same API-key auth and permission policy as /api/v1/. Forbidden and missing books both return not_found. See the MCP guide.

2026-05-17 : Members + invitations (additive)

Phase 2 Domain 4 of the API-first plan adds six endpoints under the /api/v1/books/{slug}/ prefix:

  • GET /api/v1/books/{slug}/members/ : cursor-paginated member roster, supports ?updated_since= for delta-sync.
  • POST /api/v1/books/{slug}/members/ : add a member by email or user_id (exactly one). 402 member_limit_exceeded when the plan cap is hit; 409 already_a_member on dupes; 404 user_not_found on unknown identifiers.
  • DELETE /api/v1/books/{slug}/members/{user_id}/ : remove a non-owner member. Owner-only. Cannot remove the owner of the book (400 cannot_remove_owner). Idempotent.
  • GET /api/v1/books/{slug}/invitations/ : cursor-paginated invitation list, optional ?status= filter (pending / accepted / expired / revoked).
  • POST /api/v1/books/{slug}/invitations/ : create an invitation by identifier_type (email / phone / telegram) + identifier. Eager-Membership when an existing user matches; status="accepted" on the returned row. Member-cap on eager branch surfaces as 402.
  • DELETE /api/v1/books/{slug}/invitations/{id}/ : soft-revoke a pending invitation. Sets status="revoked" instead of hard-deleting (audit trail survives). Idempotent: re-revoke returns 200 with already_revoked: true.

The BookInvitation.status enum gains a new "revoked" value alongside pending / accepted / expired. The partial unique constraint on (book, identifier_type, identifier) still covers pending only, so a revoked row does not block re-inviting the same identifier later.

2026-05-17 : Initial public surface (additive)

The /api/v1/ prefix went live with 10 endpoints across two domains:

Authentication & Session

  • POST /api/v1/auth/token/ : self-service token issuance from a logged-in browser session.
  • DELETE /api/v1/auth/token/ : revoke the calling token.
  • POST /api/v1/me/sessions/revoke/ : stolen-device cascade; revokes all of the calling user's tokens.
  • GET /api/v1/auth/me/ : current authenticated identity + auth method.

Identity & Authorization

  • GET /api/v1/me/ : current user profile.
  • PATCH /api/v1/me/ : update first name, last name, phone number (E.164). Email and channel identifiers are immutable here; use the web flow or the relevant channel /link command.
  • GET /api/v1/me/quotas/ : per-plan slot snapshot.
  • GET /api/v1/books/{slug}/permissions/ : role + feature flags + permitted action list for a specific book.

Books (limited read surface, Tier 1 carryover)

  • GET /api/v1/books/ : list books where the caller is owner or admin and the plan has API access.
  • POST /api/v1/books/{slug}/submissions/ : submit a memory. Idempotency-Key required.

Conventions established at launch

  • Canonical error envelope: {code, message, details, request_id} on every 4xx and 5xx.
  • Idempotency-Key required on every non-GET. Missing or empty returns 428 Precondition Required.
  • Owner / integrator surface only. Contributor-role memberships do not have API access.
  • Three CI-blocking AST guards prevent inline authorization, missing idempotency, and ad-hoc error responses.

Subscribing to updates

This page is the canonical record. Owners with active tokens also receive an email when a breaking change is announced, sent to the account email tied to the token. The email lands the same day the Deprecation header goes live on the affected route.

Ready to try it? Create your free memory book