← Back to API reference

API Changelog

Every change to Symbol's public API surface, classified and dated. See the stability policy for what each classification means and the notice periods that apply. Subscribe via the Atom feed.

  1. Additivepreview

    Form capsules and the Inbox (experimental)

    New experimental surface: an assistant can ask a person a short structured question set and read the answers back. `POST /api/preview/forms` creates a form capsule from inline sections or from a Type packaged as a form; `GET /api/preview/forms/{id}` reads one; `PATCH /api/preview/forms/{id}/responses` submits answers (latest-wins, partial submissions accumulate); `GET /api/preview/inbox` lists the caller's pending and answered forms with exact counts. `GET`/`PUT /api/preview/user/experimental` read and set the per-user experimental-features opt-in that gates all of the above — without it every form and inbox route 404s. `PUT /api/preview/types/{id}` accepts an optional `form_packaged` boolean (revocable, idempotent) and every Type response now carries `form_packaged_at`. Capsule read responses carry three new fields — `form_schema`, `form_responses`, and `form_answered_at` — which are null on every capsule that is not a form. Additive throughout: no existing request or response field changes shape or meaning, and a client that ignores the new fields behaves exactly as before. Backed by issue #1545.

  2. Additivepreview

    Skill auto-republish default now depends on the caller

    The `republish` behaviour on `PUT /api/preview/capsules/{id}` (and the newly body-accepting `POST /api/preview/capsules/{id}/versions/{versionNum}/restore`) now resolves its DEFAULT by caller type when the field is omitted. An explicit `republish` value still always wins. When omitted, a capsule edit or version restore that leaves a PUBLISHED skill stale re-publishes it only for AI/MCP callers; for a plain API key the default is now to leave the skill published-but-stale, and the response `warnings` array says so — send `republish: true` to opt in. This narrows the previous blanket `default true` (issues #1365/#1316) to AI callers, per the clarified policy in issue #1407. `POST .../restore` additionally accepts an optional JSON body `{ republish?: boolean }` (it previously took none); omitting the body is unchanged and additive. Classified additive: no request or response field changes shape, and the only behaviour change is the safer, more explicit default for non-AI callers. Backed by issue #1407.

  3. Additivepreview

    Capsules carry first-class agent frontmatter overrides

    `POST /api/preview/capsules` and `PUT /api/preview/capsules/{id}` accept an optional `agent_meta` object — `{ name?, description?, frontmatter? }` — that overrides what a capsule packaged as a Claude Code subagent serves in its `AGENT.md`. `name` takes precedence over the capsule's alias and must match the subagent charset (lowercase letters, digits, single hyphens); `description` takes precedence over the capsule's `summary`; `frontmatter` carries the remaining allowlisted subagent keys (e.g. `model`, `tools`) and rejects any key outside that allowlist. Omit the field to leave the stored value untouched, or send `null` to clear it — an all-blank object also clears it, so a capsule with no meaningful overrides renders exactly as it did before this field existed. Additive and optional throughout: no existing request or response field changes shape or meaning, and a capsule that never sends `agent_meta` keeps using the alias/summary fallbacks. Backed by issue #1388.

  4. Additivepreview

    Agent packaging is settable at write time, and edits keep published skills current

    `POST /api/preview/capsules` and `PUT /api/preview/capsules/{id}` accept an optional `agent` boolean, so a capsule can be packaged as a Claude Code subagent while it is created or edited rather than only afterwards via `/capsules/{id}/agent`. Omit it to inherit the Type's `package_as` default; send it explicitly to override that default either way. Encrypted capsules are never packaged as agents — they cannot be rendered server-side — and a refused or failed toggle is reported in the response's `warnings` array instead of failing the write. `PUT /api/preview/capsules/{id}` also accepts an optional `republish` boolean, default `true`: an edit to a capsule with a PUBLISHED skill now re-publishes that skill so the served SKILL.md follows the capsule instead of silently going stale. Send `republish: false` to opt out and leave it published-but-stale. A republish that goes ahead is silent; it is the cases that leave the skill stale — the opt-out, a changed derived name, or the publish rate limit — that are reported in the response's `warnings` array. `POST /api/preview/types`, `GET /api/preview/types/{id}` and `PUT /api/preview/types/{id}` add a `warnings` response field carrying non-blocking advisories — most notably an agent Type whose `field_schema` declares names outside the subagent frontmatter allowlist, which would make its capsules unrenderable. All fields are additive and optional; no existing request or response field changes shape or meaning. Backed by issues #1365 and #1316.

  5. Additivepreview

    Deleting a Type version in use by capsule history now returns 409

    `DELETE /api/preview/types/:id/versions/:versionNum` returns `409 Conflict` when the Type version is still the framing a capsule version's `rendered_md` is composed through. Deleting it would drop those capsule versions back to the Type's current schema and retroactively re-render history — the drift issue #1263 closes. The refusal releases on its own as the referencing capsule versions are deleted or pruned. Unreferenced Type versions delete exactly as before, and a missing version still returns `404`. Classified additive rather than breaking: the 409 is reachable only through a capsule-version-to-Type-version binding, which no row carries until this release ships, so no existing integration's behaviour changes and there is nothing to migrate.

  6. Breakingpreview

    A log Type keeps dated buckets or owner-bound logs, not both

    `POST /api/preview/types` and `PUT /api/preview/types/{id}` accept a new `log_mode` field on `log`-structure Types: `period` (the default, and today's behaviour — capsules are dated buckets governed by `rollover`/`log_timezone`) or `owned` (capsules are the dedicated logs of capsules in other Types). The two shapes are now exclusive and enforced. On an `owned` Type, `POST /api/preview/log-entries` with `type_id` — which resolves a dated bucket — is rejected with 400; the append must name the owner capsule via `capsule_id`. Previously both calls succeeded on the same Type, so one Type silently produced two incompatible capsule shapes and the shorter call was usually the wrong one. Setting `log_target_type_id` on a Type now also switches its target log Type to `owned`, and is refused when that Type already holds dated capsules; leaving `owned` is refused while any Type still logs there. Existing data is unaffected — log Types that already had Types bound to them are migrated to `owned`, and dated capsules already sitting in such a Type stay readable and directly addressable by `capsule_id`. Reads are additive: `GET /api/preview/types/{id}` and the Type-context read carry `log_addressing` (`mode`, `owner_types`, and a composed `statement`) on log Types, and a successful append reports `addressing.mode` (`bucket`/`owned`/`direct`). Backed by issue #1121.

    Announced
    July 23, 2026
  7. Additivepreview

    Capsule version detail reports the framing it was composed through

    `GET /api/preview/capsules/:id/versions/:versionNum` adds a `composition_basis` field to the response. It is `"type_version"` when the version's `rendered_md` was composed through the Type schema/template captured when the version was written — faithful, and no longer re-rendered when the Type's fields or output template are later edited — or `"live_type"` when it falls back to the Type's current schema (versions written before this shipped, or whose captured Type version was pruned). A `type_version` composed payload is immutable and served with `Cache-Control: immutable`; a `live_type` composed payload revalidates, as before. All existing fields are unchanged. Backed by issue #1263.

  8. Cosmeticpreview

    get_type_context structured shared-rule fields match the web view and search index

    The structured `content` fields of a capsule transcluded by a Type's guidance are now projected by the same rule the web read view, the read preview, and the search index use. In `get_type_context` output, a stored field value whose runtime shape no longer matches its declared field type — most commonly a value left behind under a field since retyped, e.g. a string under a field that is now `boolean`, an array under one that is now `text`, or a bare string under one that is now `list`/`multi_enum`/`multi_reference` — is omitted rather than printed. Validly-typed scalars, booleans (including `false`), and tables render exactly as before; list items are additionally trimmed and blank items dropped, matching the web read view and search index. Same-capsule, same answer everywhere; no request or response schema changes. Backed by issue #1271.

  9. Additivepreview

    A Type's "Publish as" is now an editable default, and packaging is per-capsule

    `package_as` on a Type is now editable after creation and behaves as a DEFAULT for new capsules only — it is never retroactive. Changing it no longer repackages capsules that already exist; each capsule's skill/agent membership is materialized on the capsule and individually overridable. `update_type` accepts `package_as` over REST and MCP, and a new per-capsule agent toggle is exposed at `POST`/`DELETE`/`GET /api/preview/capsules/{id}/agent` (mirroring the existing `/skill` endpoint). Existing published skills and synced agents are unchanged by the migration that backfills the per-capsule rows.

  10. Additivepreview

    Capsule versions capture token bindings, so a restore renders faithfully

    A capsule version now snapshots its `token_bindings` — how each authored `{{token}}` was classified (bound to a field vs. left open) — alongside `title`, `content_md`, `summary`, and `field_values`. An edit that changes only the bindings (the editor's open ↔ field toggle) is now a content change that records a version, where before it left no history and was silently lost by any later restore. Restoring a version applies the bindings captured at that version, so a restored body renders through the classification it had when written rather than through the capsule's current bindings. `GET /api/preview/capsules/{id}/versions/{versionNum}` returns a new `token_bindings` field (the map captured at that version); its `rendered_md` is already composed server-side through those bindings, reconciled against the version's own body and the Type's current schema. Existing versions are unaffected: they backfill to an empty `token_bindings` map, which reconciles body-driven to the same name-match default the pre-versioning read produced. Fully backward-compatible: the field is additive and no existing response field changes shape. Backed by issue #1262.

  11. Additivepreview

    Guidance transclusion delivers field-structured capsules and hashes their field content

    A capsule transcluded by a Type's guidance is now delivered through the same `content` envelope the capsule read returns: `GET /api/preview/capsules/context` adds an optional `content` object to each entry of `guidance_references`, with `format` naming the one representation (`markdown`, `composed`, `structured`, or `log`). This fixes body-less, field-structured referenced capsules (a Type with `content_md_max: 0`), which previously resolved to an empty body under `reason: "ok"`: their field content now arrives, composed through the referenced Type's `output_template` when one is set and as structured fields when not. `content_md` stays on each entry, byte-identical, as deprecated back-compat. The `reason` enum gains `"empty"` for a resolved, unencrypted reference that genuinely carries nothing to deliver — no body, no log summary, and no field value its Type's schema can render (a value stranded under a removed or renamed field, or left behind under a field since retyped, renders nowhere, so it reads as empty rather than promising content); `available` stays `true`. The guidance token now binds to a referenced capsule's stored `field_values` as well as its title and body, so editing any field of a shared, field-structured rule capsule invalidates in-flight guidance tokens on every dependent Type — the #1198 invalidation guarantee, now honoured for field-structured capsules. Prose references are unchanged byte for byte, including their hash, so tokens minted before this ships keep verifying. Backed by issue #1251.

  12. Additivepreview

    Capsule versions capture structured field values, and version detail returns the composed body

    A capsule version now snapshots its `field_values` alongside `title`, `content_md`, and `summary`, so a field-structured or body-less capsule (a Type with `content_md_max: 0`) has a usable revision history instead of a permanently-empty one. An edit that changes only field values is treated as a content change, not a metadata-only edit, so it creates a version; restoring a version restores the field values it captured. `GET /api/preview/capsules/{id}/versions/{versionNum}` returns two new fields: `field_values` (the map captured at that version) and `rendered_md` (the server-composed effective body, or `null` for a plain-markdown version), so a client can display a field-structured version without re-implementing template composition. `content_md` on a version may now be `null` where a body-less capsule was snapshotted faithfully — clients that assumed a string should fall back to `rendered_md`. Existing versions are unaffected: they backfill to an empty `field_values` map rather than to the capsule's current values. One caching note: a version detail response carrying a non-null `rendered_md` is composed against the Type's current schema and template, so it is served with `Cache-Control: private, no-cache` and a body-derived `ETag` instead of the immutable contract. `If-None-Match` still yields a 304; a response without `rendered_md` keeps the immutable contract unchanged. Backed by issue #1246.

  13. Additivepreview

    API-key create and rotate accept an Idempotency-Key so a retry is safe

    `POST /api/preview/user/api-keys` and `POST /api/preview/user/api-keys/{id}/rotate` now accept an optional `Idempotency-Key` request header (any client-chosen string, 1–128 characters). Both endpoints return a secret shown exactly once, so a lost response used to leave a client with no safe move: retrying create minted a second key whose predecessor's secret was never seen, and retrying rotate rotated again, discarding the first new secret. With the header, a retry that reuses the SAME key replays the ORIGINAL response instead of re-running the operation — same status, same body, same one-time secret — and every reply carries `Idempotency-Replay: true|false` so a caller can tell a replay from a fresh execution. Keys are scoped per user and per operation, and are retained for 24 hours; after that a reused key simply executes as a new request. Only successful (2xx) responses are recorded, so retrying after an error re-runs the operation as before. Three new error responses are possible only when the header is sent: 400 `IDEMPOTENCY_KEY_INVALID` (blank or over-long key), 409 `IDEMPOTENT_REPLAY_IN_FLIGHT` with `Retry-After` (the original request is still running — retry shortly), and 422 `IDEMPOTENCY_KEY_REUSED` (the key was already used with a DIFFERENT request payload, which is a client bug rather than a replay). Fully backward-compatible: omitting the header preserves today's behaviour byte-for-byte, and nothing is recorded.

  14. Additivepreview

    Types gain a per-Type cap on total field-value content

    `POST /api/preview/types` and `PUT /api/preview/types/{id}` now accept an optional request field `field_content_max` (integer, floor 0, ceiling 90000, default 90000) — the fields-side companion to `content_md_max`. It caps the SUMMED length of one write's `field_values`, in UTF-16 code units: one capsule's field values, or one log entry's. Field values had no aggregate bound before; the only limits were per-field rules that apply solely to fields declaring one. This matters most where fields are the ONLY content channel — every `log` Type (an entry has no body) and any Type running `content_md_max: 0`. A write over the cap is rejected with a 422 on `POST /api/preview/capsules`, `PUT /api/preview/capsules/{id}`, `POST /api/preview/log-entries`, and `PATCH /api/preview/capsules/{id}/entries/{entryId}`. Lowering a Type's cap never strands stored content: on an update the effective ceiling is the greater of the cap and the row's current summed length, so an existing capsule or entry stays editable up to what it already holds — only a fresh create/append gets the plain cap. Value length is measured as the content itself (strings by length, numbers and booleans by their rendered form, lists and table rows summed over their items/cells); field and column names are not counted. `field_content_max` is also readable in a Type's context, and is settable through the MCP `create_type`/`update_type` tools as a 'Limits / guardrails' setting — so an AI caller may change it only when a person grants that class. Backward-compatible: omitting it keeps the default of 90000, which every existing Type is backfilled to, so no stored content becomes unwritable and no existing client is affected.

  15. Additivepreview

    Guidance entitlement is remembered server-side, and share links can be revoked by their token

    Writes into a Type that carries `guidance` no longer depend on the client holding an `x-symbol-guidance-token` header from the same connection. When a caller reads a Type's guidance (`GET /api/preview/capsules/context`, `POST /api/preview/types`, or `PUT /api/preview/types/{id}`), the server records that read for the calling user and the guidance it read; a later write from that same user is accepted without the header, even from a different connection or process. Presenting the header still works exactly as before and is still checked first. Two boundaries are unchanged: one user's read never entitles another user's write, and editing a Type's guidance invalidates the entitlement until the new guidance is read. `POST /api/preview/log-entries` now enforces the same contract — a log entry is capsule content, so appending into a guidance-bearing Type requires that the caller read the guidance (authorization is still checked first, so a caller who may not append still gets 403). On `POST /api/preview/capsules` the guidance check now runs before category validation, so an invalid `categories` value no longer masks the guidance response. `GET /api/preview/types?detail=full` no longer returns the per-row `guidance_token` / `guidance_token_status` fields — they were never usable by a caller and are now redundant; read one Type's guidance through `GET /api/preview/capsules/context` instead. `DELETE /api/preview/share/{tokenId}` accepts EITHER the share row's UUID (as before) or the public `shr_…` token string, so a caller holding only the share URL can revoke without a separate id lookup; revoking an unknown or already-revoked link answers 404 for both addressing forms. Backed by issue #1235.

  16. Additivepreview

    Types can derive a capsule's alias from a field, and text fields can require a slug shape

    Two additions for capsules keyed by an external ID. A text field's `rules` may now carry `{ kind: "format", format: "slug" }`, rejecting any value that is not a valid alias (lowercase letters, digits, single hyphens; not purely numeric, UUID-shaped, or reserved; at most 60 characters) with a 422 field error. And a Type may set `slug_source` to the name of one of its declared `text` fields: capsules of that Type then take their alias from that field's value, normalized (trimmed + lowercased), so the retrieval alias can never diverge from the ID the field stores. On such a Type do not send an explicit `slug` — a value that disagrees with the derived one is a 422, and a derived-alias collision is a 409 (a capsule for that external ID already exists). `slug_source` is accepted only on a structured, non-encrypted, non-log Type and must name a declared `text` field. The rule travels inside `field_schema`; `slug_source` is a Type field of its own, optional on `POST /api/preview/types` and `PUT /api/preview/types/{id}` — and on MCP `create_type`/`update_type` — and returned on those routes' `201`/`200` responses, on `GET /api/preview/types/{id}`, and through MCP `get_type_context`. Purely additive: Types using neither behave exactly as before. Backed by issue #1199.

  17. Additivepreview

    Type guidance can transclude capsules, and says which ones it resolved

    A Type's `guidance` may now reference capsules with the usual `@type/slug` syntax, and those references are resolved and delivered alongside it instead of being left as inert text — so a rule that applies to several Types can live in one capsule rather than being copied into each Type's guidance. `GET /api/preview/capsules/context` gains an optional `guidance_references` field on its `200` response: one entry per reference, in the order the references appear, carrying `ref`, `capsule_id`, `title`, `content_md`, `available`, and a `reason` of `ok`, `unresolved`, `encrypted`, or `over_cap`. Guidance may reference at most 5 capsules and references are resolved one level deep; a referenced capsule's own references stay inert text. Encrypted capsules cannot be referenced — the server cannot read them — and Type create/update now rejects guidance that references an encrypted or non-existent capsule. Note for MCP clients: the `guidance_token` returned with a Type now covers the referenced capsules too, so editing a referenced capsule invalidates the tokens of every Type that references it; the existing 422 `guidance_token_required` response already tells the client to refetch via `get_type_context` and retry. Purely additive: guidance without references behaves exactly as before, and clients that ignore `guidance_references` keep working. Backed by issue #1198.

  18. Additivepreview

    Response fields these routes already returned are now in the OpenAPI contract

    Handler-level response-contract conformance testing surfaced several response fields that preview routes already emit on the wire but the OpenAPI document did not model, so generated clients could not see them. They are now documented (all optional, no behaviour change): `GET`/`POST`/`PUT /api/preview/types{,/{id}}` gain `content_bytes`, `guidance_hash`, `ai_settings_policy`, and `paragraph_rendering`, plus `guidance_token` and `guidance_token_status` on `POST /api/preview/types` (always) and `PUT /api/preview/types/{id}` (when the request edits guidance); `POST`/`PUT`/`GET /api/preview/projects{,/{id}}` gain `content_bytes`; `PUT /api/preview/organizations/{id}` documents the organization fields its response already spread (`description`, `icon`, `logo_url`, `allow_public_profiles`, `require_encryption`, `require_managed_account`, `billing_exempt`, `billing_grace_until`, `deletion_reservation_id`, `deletion_reserved_at`, `deletion_reserved_by`); `POST /api/preview/organizations/{id}/invitations` gains `invited_by`; `POST /api/preview/attachments/confirm` gains `deleted_at`, `unavailable_at`, and `unavailable_reason`; and `GET /api/preview/share/resolve` documents the capsule fields its allowlist already returns (`slug`, `content_excerpt`, `summary`, `language`, `is_prompt`, `prompt_meta`, `field_values`, `project_id`, `workspace_id`, `created_by`, `deleted_at`, `archived_at`). The documented success status of `POST /api/preview/capsules/{id}/duplicate` was also corrected to `201` to match the wire. Purely additive: no route behaviour changed and clients that ignore the new fields keep working. Backed by issue #1188.

  19. Additivepreview

    Types choose how paragraph field values render

    `POST /api/preview/types` and `PUT /api/preview/types/{id}` accept an optional `paragraph_rendering` field (`plain` | `markdown`, default `plain`). It controls how a Type's capsules display standalone `paragraph` field values on read: `plain` keeps the stored text literal (today's behaviour), `markdown` renders it as sanitized Markdown — bold, inline and fenced code — on capsule detail, previews, log-entry streams, and public share pages. Embedded HTML is sanitized and never executes. Values interpolated into a Type's `output_template` already rendered as Markdown and are unaffected, as are capsule create/edit inputs and table cells. Reads echo the stored value on `GET /api/preview/types/{id}`. Additive and backward-compatible: the field is optional and existing Types backfill to `plain`. Backed by issue #1034.

  20. Additivepreview

    Enabling a public profile without a username answers with a machine-readable code

    `PATCH /api/preview/profile/visibility` still refuses `public_profile_enabled: true` on an account that has not claimed a username with 409 Conflict and the same `detail` text; the response now also carries `code: "username_required"`. Clients can key off the code to send the user straight to the username claim form instead of matching on the prose. Additive and backward-compatible: status and `detail` are unchanged, no other enable-time refusal (unverified email, organization policy) gained a code, and clients that ignore `code` keep working. Backed by pull request #1148.

  21. Additivepreview

    Capsules can be duplicated in one call

    `POST /api/preview/capsules/{id}/duplicate` creates a copy of a capsule you can read, in the same workspace, Type, and project, owned by you, and answers 201 with the same body shape as `POST /api/preview/capsules`. Copied: title (suffixed `(copy)`), content, field values, tags, categories, project and collection membership, attachments, and gallery images. Reset on the copy: reference id, alias, share links, visibility, and all counters — a duplicate starts unshared and unviewed. A failure part way through never leaves a half-built capsule: every database row is written in one transaction and rolls back together. Object-storage copies are made outside that transaction and are removed by a best-effort compensating delete, so an unsuccessful duplicate leaves nothing you can reach through the API, though a failed cleanup can leave unreferenced stored objects behind. Requires the `create` scope for API-key and MCP callers, on top of read access to the source. Refusals carry a machine-readable `code`: 422 for a source that cannot be duplicated (`capsule_is_encrypted`, `capsule_is_log`, `capsule_flagged`), 413 when the copy would exceed a storage quota, 400 for attachment limits, 403 at the capsule limit. Purely additive: no existing endpoint changed. Backed by issue #1123.

  22. Cosmeticpreview

    Every preview response body is now described in the OpenAPI contract

    The OpenAPI document now carries a JSON schema for the success response of every documented `/api/preview/*` operation — previously only three responses were modeled and the rest shipped as schema-less stubs, so generated clients typed those responses as `unknown` and the api-changelog tooling was blind to response-body changes. Two public preview reads that were missing from the document entirely are now present: `GET /api/preview/capsules/by-ref` and `GET /api/preview/capsules/context`. Documented success statuses were corrected to match the wire: resource creates (capsule, type, project, collection, share, API key, invitation, attachment confirm) are `201`, the legacy collection-membership shims are `308` redirects, and organization create is the billing-gated `402`. Six fields have also been dropped from the documented `200` bodies of `PUT /api/preview/capsules/{id}` and `POST /api/preview/capsules/{id}/duplicate` — `share_token`, `share_url`, `share_verification`, `browser_url`, `partial` and `attachment_error`. Those routes have never emitted them: the document was previously wired to the MCP `update_capsule` tool's output schema, and the MCP handler adds those fields client-side (`browser_url` is derived from the client's base URL; the share fields come from a separate share-create call). Removing them corrects the document rather than changing the API. MCP tool output is unaffected. Separately, the documented `maxLength` on the `content_level`, `reason` and `truncatedBy` fields of the reference/backlink page responses narrows from 128 to 64, so those responses now reuse the same delivery-envelope definition the search and context reads already publish; every value those fields can carry is a short fixed keyword, so nothing valid is excluded. This is a documentation-only change — no request or response actually changed on the wire, so existing integrations are unaffected; callers using generated REST types gain accurate response shapes, and anyone who had been reading those six fields off the generated `updateCapsule` type was reading fields the REST response never carried. Backed by issue #1068.

  23. Additivepreview

    Types support a freeform field mode with per-field-type guardrails

    `POST /api/preview/types` and `PUT /api/preview/types/{id}` now accept three optional fields selecting an alternative field-authoring mode. `field_mode` (`structured` | `freeform`, default `structured`) switches a Type from named-slot fields to freeform: in freeform mode `freeform_schema` declares which field types an AI/MCP caller may use (short text / paragraph / list) plus per-type caps (max words / max items / max length), and `field_count_min` (paired with the existing `field_count_max`) bounds how many fields a capsule must provide — the caller names and counts the fields within those guardrails, and templating is disabled. `field_mode`, `freeform_schema`, and `field_count_min` join the AI-protected `limits` settings class, so an AI/MCP caller cannot switch a Type to freeform or widen its bounds. Freeform capsule fields are synthesized on read and included in capsule detail, previews, and public shares (caps/policies never leak). Additive and backward-compatible: every field is optional and existing Types backfill to `structured` with an empty policy and a zero floor. Backed by issue #1036.

  24. Additivepreview

    Types carry a per-Type AI settings-permission policy; MCP can author field schemas

    `POST /api/preview/types` and `PUT /api/preview/types/{id}` now accept an optional `ai_settings_policy` (`{ allow_all: boolean, allowed_classes: string[] }`) controlling which classes of a Type's settings an AI/MCP caller may change (classes: `limits`, `structure`, `identity`, `lifecycle`, `behavior`). When omitted on create, the server materializes an origin default — allow-all for AI/MCP creators, all-classes-except-`limits` for human creators — and persists it. The policy is human-controlled only: an AI/MCP caller that sends `ai_settings_policy` is rejected (403), and an AI/MCP update touching a denied setting class (or the permanently human-only `is_encrypted`) is rejected with a clear reason. The Type read payload and `get_type_context` add a derived `ai_settings_permissions` (`{ allow_all, allowed_classes, denied_classes, statement }`) stating which classes AI may and may not change. Field-schema authoring (`field_schema`, `output_template`) and the per-Type limits (`content_md_max`, `field_count_max`, `guidance_max`) — already accepted on the HTTP surface — are now reachable through the MCP `create_type` / `update_type` tools, governed by the policy above. Additive and backward-compatible: every field is optional and existing Types backfill to the human-created default. Backed by issue #1093.

  25. Additivepreview

    Capsule and Type reads advertise their log binding

    Capsule responses — `POST /api/preview/capsules`, `GET` and `PUT /api/preview/capsules/{id}`, and `GET /api/preview/capsules/by-ref` — carry a new optional `log` object when the capsule owns a log or is one: `role` (`owner` | `log`), `bound_type` (the log Type's `id`, `name` and `slug`), `owned_log_ref` (the owned log's ref, or null until a first append materializes it) and `append_via` (the exact append call to make). Previously an owner-bound log was invisible at the point of use: the response carried `owner_capsule_id: null` — null on an owner by design — and nothing about the Type's binding, so a caller could conclude no log existed. Type reads — `GET /api/preview/types/{id}` and `GET /api/preview/types` (full detail) — resolve the binding alongside the raw `log_target_type_id` as `log_target: { name, slug } | null`. Additive and backward-compatible: `log` is omitted entirely for capsules with no log role, so the standard capsule shape is unchanged, and the lean list-summary Type branch is untouched. Backed by issue #1066.

  26. Additivepreview

    Capsule reads return classification and prompt facets; context reads report grand totals

    Capsule responses — `GET` and `PUT /api/preview/capsules/{id}` and `GET /api/preview/capsules/by-ref` — now carry the `tags` and `categories` chips (`{ slug, name }[]`) and the `is_prompt` / `prompt_name` facet. All four persisted and were accepted on write, but were absent from every read, so a client could set them and never read them back. Listings additionally carry `is_prompt` / `prompt_name`; the chips stay on single-capsule reads. `GET /api/preview/capsules/context` and `GET /api/preview/projects/{id}/context` add `total` — the grand total of matching capsules across all pages, distinct from the delivered page length, so a caller paging a large Type can report how many exist rather than how many arrived. A 400 for an unknown category slug now names the offending slugs and carries the full valid set as `valid_category_slugs`, so a client can self-correct in one hop. `GET /api/preview/share/resolve` now projects its capsule through an explicit allowlist: it keeps every field the share contract has always carried, and no longer leaks internal columns (`search_vector`, `content_text`, moderation/freshness state) that a row spread had been emitting incidentally. Those columns were never documented, never part of the share contract, and are not covered by the stability policy, so this is classified additive rather than breaking — every documented field is unchanged. Backed by issue #1052.

  27. Additivepreview

    Types accept an optional default `log_entry_order`

    `POST /api/preview/types` and `PUT /api/preview/types/{id}` accept an optional `log_entry_order` request field for log-format Types: `newest_first` (the default, matching today's behaviour) or `oldest_first`. It sets the order a fresh visit renders the log's entry stream in and seeds the Stream sort toggle; readers can still flip the order per session. Omitting the field leaves it unchanged (defaulting to `newest_first` on create). Additive and backward-compatible: a client that omits the field is unaffected. Backed by issue #1060.

  28. Additivepreview

    Types can bind to a log Type via `log_target_type_id`

    `POST /api/preview/types` and `PUT /api/preview/types/{id}` accept an optional `log_target_type_id` request field: the id of a log-format Type this Type's capsules keep their log in (owner-bound logs). A capsule in the bound Type then owns a dedicated log capsule, and callers append/read through the owner ref — `POST /api/preview/log-entries` and `GET /api/preview/capsules/{id}/entries` now accept an owner capsule, not only a log capsule. A Type is a log-format Type OR binds to one, never both; sending `null` clears the binding, omitting the field leaves it unchanged. Additive and backward-compatible: a client that omits the field is unaffected. Backed by issue #1056.

  29. Additivepreview

    Types support an append-only `log` format

    `POST /api/preview/types` and `PUT /api/preview/types/{id}` accept two optional request fields for log-format Types: `rollover` (`daily` | `weekly` | `monthly` | `manual`) and `log_timezone` (an IANA timezone that fixes the rollover boundary). A Type's `structure` is chosen when it is created and is fixed thereafter: it is accepted on `POST /api/preview/types` but not on `PUT` — a log Type keeps its entries in a dedicated table, so the body shape cannot change after creation. Two new endpoints append and read entries — `POST /api/preview/log-entries` and `GET /api/preview/capsules/{id}/entries` — with `PATCH`/`DELETE /api/preview/capsules/{id}/entries/{entryId}` to edit and soft-delete. All request shapes stay backward-compatible: omitting the new fields preserves prior behaviour, and no existing fields or response shapes change. Backed by issue #1050.

  30. Additivepreview

    Capsules accept optional first-class `prompt_meta`

    `PUT /api/preview/capsules/{id}` now accepts an optional `prompt_meta` request field carrying a capsule's prompt metadata as first-class data: `models` (slugs from the curated registry served by `GET /api/preview/models`), `howToUse`, `expectedOutput`, and `editPrompt`. Previously this metadata could only be inferred from a Type's structured `field_values`; it is now stored on the capsule itself, independent of the Type's field schema, and drives the public prompt share page. Sending `null` clears the blob; omitting the field leaves it unchanged. Additive and backward-compatible: a client that omits the field is unaffected. Backed by issue #1035.

  31. Additivepreview

    Types accept an optional `share_template` on create

    `POST /api/preview/types` now accepts an optional `share_template` request field selecting how the Type's capsules render on their public share page: `"article"` (the default long-form layout) or `"prompt"` (the grab-and-run prompt layout). It mirrors the field already accepted by `PUT /api/preview/types/{id}`, so a Type can now declare its share layout at creation time instead of requiring a follow-up update. Additive and backward-compatible: a client that omits the field gets the default `"article"` template, unchanged. Backed by issue #1020.

  32. Additivepreview

    Capsule classification: optional `tags` and `categories`

    `POST /api/preview/capsules` and `PUT /api/preview/capsules/{id}` now accept two optional request fields: `tags` (free-form strings normalized to a shared slug vocabulary) and `categories` (slugs from the curated taxonomy served by `GET /api/preview/categories`; an unknown slug is rejected with 400). On `PUT`, presence replaces the set and `[]` clears it; omission leaves it unchanged. Capsule reads (including `?shape=mcp`) and the create/update responses now surface the persisted classification as `tags` and `categories` arrays of `{ slug, name }` chips. Tag autocomplete is available at `GET /api/preview/tags?q=`, scoped to tags on capsules the caller can access. Additive and backward-compatible: a client that omits the fields is unaffected. Backed by issue #1014.

  33. Additivepreview

    Capsule bodies become the source of truth; `content_md` nullable, new `token_bindings`

    `PUT /api/preview/capsules/{id}` now accepts an optional `token_bindings` request field — a per-token map classifying each `{{token}}` in the body as field-bound (resolved server-side from `field_values`) or open (a caller-supplied prompt argument). It is web-editor-facing: an MCP/AI caller that sends it is rejected with 400. Relatedly, `content_md` is now nullable: a capsule that has never written a body BORROWS its Type's `output_template` live (its read `content_md` is `null`), and sending `content_md: null` on this route un-forks an authored capsule back to borrowing. The `content` envelope on capsule reads gains two fields: `source` (`"authored"` | `"borrowed"`) naming where the effective body came from, and `bindings` (the per-token binding map, present for `markdown`/`composed` bodies). Additive and backward-compatible: a client that only sends a string `content_md` and ignores the new fields is unaffected. Backed by issue #998.

  34. Additivepreview

    `output_template` replaces `field_body_template`; capsules gain a `content` envelope

    Type write routes (`POST /api/preview/types`, `PUT /api/preview/types/{id}`) now accept `output_template` — the renamed body template for a field-structured Type (a fielded capsule is not always a prompt, so the name is generic). The old `field_body_template` is retained as a DEPRECATED input alias: both are accepted and `output_template` wins if both are sent, so existing clients keep working; the alias will be removed in a future version. Capsule reads (`GET /api/preview/capsules/{id}`, `/api/preview/capsules/by-ref`, and the MCP get_capsule / get_capsule_by_ref tools) additionally return a `content` envelope `{ format: "markdown" | "composed" | "structured", text?, fields? }` whose `format` names the single representation a consumer should use. The flat `content_md` / `content_rendered` / `field_values` fields are unchanged and remain for backward compatibility. Additive and backward-compatible: existing requests and responses are unaffected.

  35. Additivepreview

    Types gain a per-Type Max guidance length cap

    `POST /api/preview/types` and `PUT /api/preview/types/{id}` now accept an optional request field `guidance_max` (integer, floor 0, ceiling 5000, default 5000) — the sibling of `content_md_max` — capping how long a Type's own `guidance` may be. A guidance write longer than the effective cap is rejected with a 422; lowering the cap never rewrites existing guidance. `guidance_max` is also surfaced read-only in a Type's context (the AI reads it to self-limit) but is not settable through the MCP write tools. Backward-compatible: omitting it keeps the default of 5000 (the existing hard cap), so existing Types and clients are unaffected. Backed by issue #904.

  36. Cosmeticpreview

    Field interpolation tokens move to double braces `{{field_name}}`

    Read-time field interpolation in capsule bodies and Type body templates now uses double-brace `{{field_name}}` tokens instead of single-brace `{field_name}`. A lone `{` is henceforth always a literal character, so prose and code no longer risk having a stray `{name}` silently substituted. The `field_body_template` and `field_schema[].name` request/response shapes are unchanged — only the interpolation notation in their descriptions moves to double braces — and existing stored content was migrated so rendered output is identical. New body templates authored via `POST`/`PUT /api/preview/types` should use `{{field_name}}`. Backed by issue #980.

  37. Additivepreview

    Per-Type maximum field count; Type guardrails locked for AI callers

    `POST /api/preview/types` and `PUT /api/preview/types/{id}` accept a new optional request field `field_count_max` (integer, floor 0, ceiling 50, default 50) that caps how many typed fields a Type's `field_schema` may declare. It also appears on Type response bodies and, when tightened below the ceiling, in `get_type_context`. Separately, `PUT /api/preview/types/{id}` now rejects the content-bounding guardrail fields — `field_schema`, `field_body_template`, `content_md_max`, `field_count_max` — with 403 when the caller is an MCP/AI client, so an AI cannot loosen a human-authored Type's limits. Web sessions, the mobile app, personal API keys, and `create_type` are unaffected. Backed by issue #901.

  38. Additivepreview

    Types gain optional icon and color

    `POST /api/preview/types` and `PUT /api/preview/types/{id}` now accept two optional request fields: `icon` — a glyph key from the closed icon library — and `color` — a tint-token key from the closed palette — which set a Type's visual identity. Both are backward-compatible: omitting them leaves the Type with its default glyph / slug-derived tint default, and no existing fields or response shapes change. Backed by issue #957.

  39. Additivepreview

    Capsules can be exposed as prompts; Types gain cover fields

    `POST /api/preview/capsules` and `PUT /api/preview/capsules/{id}` now accept an optional request field `is_prompt`, which exposes a capsule on the MCP `prompts` surface (its arguments come from the parent Type's fields). `POST /api/preview/types` and `PUT /api/preview/types/{id}` accept two optional request fields: `prompt_default` — the default `is_prompt` value applied to new capsules created under the Type — and `structure`, the Type's cover-page structure. All four request shapes stay backward-compatible: omitting any field preserves the prior behaviour (a capsule stays non-prompt unless its Type opts in), and no existing fields or response shapes change. Backed by issue #954.

  40. Additivepreview

    Share-link verification adds a `not_applicable` reason

    The `share_verification.reason` field returned alongside created share links gains a new value, `not_applicable`. It is emitted with `verified: false` when the create path cannot round-trip a freshly minted token through the verification read path — specifically for Type/Collection (library) share tokens, whose authenticated resolve endpoint is capsule-only. `not_applicable` means verification was skipped, not that the link is broken. This is additive: the existing `access_denied` | `not_found` | `error` values and all request shapes are unchanged. Backed by issue #858 (item 3).

  41. Additivepreview

    Collection create/update accept an optional `alias`

    `POST /api/preview/types/{id}/collections` and `PATCH /api/preview/types/{id}/collections/{collectionId}` now accept an optional request field `alias` — a tenant-global shorthand that addresses a collection by `@alias` alone, without naming its parent Type, from MCP and CLI. An alias is a lowercase text token (max 60 chars, not purely numeric) and is unique per tenant (personal or workspace), unlike a collection's per-Type `slug`. Real Type slugs always take precedence: an alias that would shadow an existing Type slug is rejected, and resolution tries Type slugs first so a Type can never be shadowed by a collection alias. Renaming or removing an alias leaves a tombstone, so a bare `@alias` already written elsewhere keeps resolving to its original collection and never silently repoints. Both are additive and optional: omitting `alias` on create leaves the collection alias-less, an omitted `alias` on update leaves the stored value untouched (PATCH semantics), and passing `null` clears it. A taken alias or a Type-slug shadow returns `409`. No existing fields or response shapes change. Backed by issue #316 (migration 147).

  42. Additivepreview

    Workspaces accept an `icon` for switcher branding

    `PUT /api/preview/organizations/{id}` accepts an optional, nullable request field `icon` — a workspace icon key from a closed set (`building`, `tower`, `briefcase`, `landmark`, `store`, `users`, `rocket`, `box`, `layers`, `globe`) shown in the workspace switcher. Purely additive with PATCH semantics: omit it to leave the stored icon unchanged, or send `null` to reset to the default (`building`). Editable by a workspace owner or editor. No existing fields or response shapes change (migration 145).

  43. Additivepreview

    Types accept a `field_body_template` for field-structured capsules

    `POST /api/preview/types` and `PUT /api/preview/types/{id}` accept an optional request field `field_body_template` (string, max 10000) — a capsule body template for a field-structured Type. New capsules of the Type seed their `content_md` from it, and any `{field_name}` tokens render from the capsule's `field_values` at read time. Purely additive and optional: omit it (or send `""`) and the body is derived from the values as before; on update, an omitted field leaves the stored value untouched (PATCH semantics). No existing fields or response shapes change. Follow-up to the typed-fields work (issue #868, migration 144).

  44. Additivepreview

    Types accept a structured `field_schema`; capsules accept `field_values`

    Types can now declare optional structured fields with declarative validation. `POST /api/preview/types` and `PUT /api/preview/types/{id}` accept an optional request field `field_schema` — zero or more fields (`text` | `number` | `datetime` | `enum` | `reference`), each with an optional `required` flag (plus a custom `required_message`) and type-appropriate rules carrying per-rule messages. `POST /api/preview/capsules` and `PUT /api/preview/capsules/{id}` accept an optional `field_values` object that is validated against the owning Type's schema; all violations are returned together as RFC 9457 `application/problem+json` with an `errors[]` array. `PUT /api/preview/types/{id}` also accepts an optional `confirm_field_changes` flag: destructive schema edits (removing or renaming an enum value or field) return `409` with the required confirmation token and grandfather existing capsules once confirmed. All three fields are additive and optional — a Type with no `field_schema` behaves exactly as before, and on update an omitted field leaves the stored value untouched (PATCH semantics). Field-structured Types are mutually exclusive with end-to-end encryption (server-side validation needs plaintext). No existing fields or response shapes change. Backed by issue #868 (migration 143).

  45. Additivepreview

    Publicly shared prompts are safety-classified

    Capsules shared with the `prompt` render template now have their prompt body classified for safety (low/medium/high). High-risk prompts are withheld on the public share page behind a recipient acknowledgment; medium shows a non-blocking caution; low renders unchanged. Classification is best-effort and fails open — sharing and viewing always work even when the classifier is disabled or unavailable. A new GET /api/cron/classify-shared-prompts endpoint (Vercel cron, `Authorization: Bearer $CRON_SECRET`) backfills verdicts. No existing request/response shapes change. Backed by issue #856 (migration 143).

  46. Additivepreview

    Capsule write responses surface partial attachment failures

    `POST /api/preview/capsules` and `PUT /api/preview/capsules/{id}` may now return two optional `200` response fields: `partial` (boolean) and `attachment_error` (string). When a capsule's fields are written successfully but its attachments fail validation, the response sets `partial: true` and carries the validation detail in `attachment_error`, while the capsule fields above remain authoritative — so a client consuming only the structured response can detect the partial outcome instead of mistaking it for a clean write. Purely additive: clients that ignore the new fields are unaffected.

  47. Additivepreview

    Type update accepts an optional `share_template`

    `PUT /api/preview/types/{id}` now accepts an optional request field `share_template` (`article` | `prompt`) that controls how the Type's public share page renders. `prompt` presents each capsule as a grab-and-use prompt with body-only copy and variable highlighting; `article` (the default) keeps the standard reading layout. A shared Collection inherits its parent Type's template. The field is additive and optional: omitting it on update leaves the stored value untouched (PATCH semantics). No existing fields or response shapes change. Backed by issue #852 (migration 141).

  48. Additivepreview

    Capsule create/update accept an optional `slug` alias

    `POST /api/preview/capsules` and `PUT /api/preview/capsules/{id}` now accept an optional request field `slug` — a human-readable text alias that resolves to the same capsule as its permanent numeric ref (`@type/<slug>` works alongside `@type/<N>`). A slug is a lowercase alias (max 60 chars, not purely numeric) and is unique per (type, owner); renaming or removing one leaves a tombstone so `@type/<slug>` references already written elsewhere keep resolving. Both are additive and optional: omitting `slug` on create leaves the capsule alias-less, an omitted `slug` on update leaves the stored alias untouched (PATCH semantics), and passing `null` clears it. No existing fields or response shapes change. Backed by issue #715 (migration 136).

  49. Additivepreview

    Type create/update accept `package_as` and `format`

    `POST /api/preview/types` and `PUT /api/preview/types/{id}` now accept two optional request fields: `package_as` (`standard` | `skill`) and `format`. `package_as` controls how a Type's capsules are packaged — setting it to `skill` packages the Type (and every capsule in it) as a Claude Code skill, while `standard` keeps the default behavior. `format` records the export format. Both are additive and optional: omitting them leaves existing behavior unchanged, and on PUT an omitted field leaves the stored column untouched (PATCH semantics). No existing fields or response shapes change. Backed by the skill-packaging work (migration 133).

  50. Cosmeticpreview

    Rapid consecutive saves now always record a version

    The 60-second revision interval throttle has been removed. Previously, a same-author save landing less than 60 seconds after the prior version skipped capturing a new version row (the capsule/Type UPDATE still applied), and the PUT capsule/Type and restore responses could carry `version_skip_reason: "interval_throttle"`. Now every explicit save with changed content records a version, so `interval_throttle` is no longer emitted and is removed from the set of possible `version_skip_reason` values. No response shape change — this is a value-set narrowing. No-op saves still skip with `identical_content`, and the size, rate, quota, and capsule-cap gates are unchanged. Backed by issue #667.

  51. Additivepreview

    Workspace scoping for personal API keys

    POST /api/preview/user/api-keys accepts a new optional request field `workspaceIds` (array of strings). Each entry is either the literal "personal" or an organization-workspace UUID the caller can access. When present and non-empty the issued key's access is restricted to exactly those workspaces (intersected with the caller's live membership at request time); omitting the field or passing an empty array issues an unrestricted key with the same reach as before. Organization keys (POST /api/preview/organizations/{id}/keys) are already hard-scoped to their organization and are unaffected. Existing clients are unaffected. Backed by issue #597.

  52. Additivepreview

    Per-Type maximum capsule content length

    POST /api/preview/types and PUT /api/preview/types/{id} accept a new optional request field `content_md_max` (integer, floor 0, ceiling 90000, default 20000). It sets a per-Type cap on capsule `content_md` length that overrides the global per-capsule limit; capsule create/update reject bodies over the effective cap. Lowering a Type's cap grandfathers existing over-cap capsules (they remain editable but cannot grow). A cap of 0 makes the Type title-only. The field also appears on Type response bodies, and GET /api/preview/capsules/{id} now carries the denormalized `type_content_md_max` so editors can render a live character counter. Existing clients are unaffected — omitting the field preserves the previous behaviour. Backed by issue #601.

  53. Additivepreview

    update_type MCP tool for editing Type metadata

    The MCP server now exposes an `update_type` tool — a thin client over the existing PUT /api/preview/types/{id} endpoint. AI assistants can correct a Type's name, guidance, summary, freshness thresholds (stale_after_days / expire_after_days), orphan-warning visibility, and default project without a human opening the web UI; previously only create_type was available. PATCH semantics — only supplied fields change. The slug stays immutable and the one-way encryption flip is intentionally not exposed. The tool forwards an optional updated_at optimistic-concurrency token (mapped to the endpoint's client_updated_at) so a concurrent edit returns a conflict instead of silently overwriting, and guidance/summary edits are captured in the Type's version history. No REST contract change — the underlying endpoint is unchanged; this publishes the new MCP tool surface. Backed by issue #536.

  54. Additivepreview

    Per-Type opt-out for the orphan-capsules banner

    POST /api/preview/types and PUT /api/preview/types/{id} accept a new optional request field `hide_orphan_warning` (boolean, default false). When true, the Type's detail page suppresses the orphan-capsules banner that flags capsules with zero inbound references. The field also appears on Type response bodies. Existing clients are unaffected — omitting the field preserves the previous behaviour (warning shown).

  55. Additivepreview

    Capsule + type version history with quota gating

    Append-only snapshot history shipped for non-encrypted capsules and types. New endpoints: GET /api/preview/(capsules|types)/{id}/versions (cursor-paginated, newest-first), GET /api/preview/(capsules|types)/{id}/versions/{versionNum}, POST /api/preview/(capsules|types)/{id}/versions/{versionNum}/restore (append-only — captures the pre-restore state as a new version), and DELETE /api/preview/(capsules|types)/{id}/versions/{versionNum} for manual entries. Encrypted capsules/types never produce a version (storage-layer enforcement: record trigger, encryption-toggle purge trigger, and BEFORE INSERT reject trigger on the version tables). Storage is over-counted and tracked per-user; at 85% of plan storage the version write is skipped — the underlying capsule/type UPDATE still succeeds. PUT /api/preview/capsules/{id} and PUT /api/preview/types/{id} responses now always carry `version_skipped` (boolean) and `version_skip_reason` ("quota" | "encryption" | null) — additive, existing fields unchanged. 30-day hard retention enforced by a 6-hourly prune cron; nightly reconciliation auto-corrects counter drift above 1%. Operational notes, accounting formula, and rollback SQL in docs/versioning/README.md.

  56. Additivepreview

    GET /api/preview/types/{id} for read-side parity

    Single-type read endpoint added at GET /api/preview/types/{id}, closing the CRUD gap with capsules and projects. Callers holding a type ID (returned from createType, embedded in a capsule payload, or surfaced via list_types) can now fetch the full record without listing all types and filtering client-side. The response shape mirrors items in GET /api/preview/types — including the joined workspace_name, capsule_count, and last_capsule_at fields. Backed by issue #417.

  57. Additivepreview

    Canonical collection-membership endpoints

    Adding and removing capsules from a collection now lives at POST /api/preview/collections/{id}/capsules and DELETE /api/preview/collections/{id}/capsules. The server resolves the parent type and workspace from the collection ID alone — closing a class of cross-workspace scope drift that surfaced as 400 "Invalid collection ID" against IDs that round-tripped through list_collections. The legacy type-scoped paths (POST | DELETE /api/preview/types/{id}/collections/{collectionId}/capsules) keep working for one release cycle via a 308 Permanent Redirect (preserves method + body) and are now marked deprecated in the OpenAPI document so codegen flags them. Their summary text was updated in this release; no behavior change beyond the redirect. Bug fix #423.

  58. Additivepreview

    Read-side parity and defense-in-depth gates for preview API

    30 read- and lifecycle-side operations are now part of the published OpenAPI surface, bringing the preview spec to read/write parity with the implemented routes. New operations include capsule list/get/delete/archive plus batch archive/delete/move; capsule backlinks, graph, and references reads; project list/get/delete and capsule add/remove plus project context; type list/delete and collection list/get/delete plus collection capsule add/remove; share list/resolve/revoke; search; user profile; workspaces; and attachment delete. The underlying handlers were already shipping — this change publishes them in the contract so Scalar, generated openapi-fetch / Dart clients, and Postman pick them up. Backed by defense-in-depth public-surface gates that fail closed when an operation is referenced without an explicit allowlist entry.

  59. Additivepreview

    Typed 200-response schemas for capsule and share writes

    POST /api/preview/capsules, PUT /api/preview/capsules/{id}, and POST /api/preview/share now publish typed JSON-Schema response bodies in the OpenAPI document. New response fields documented (capsule writes: id, ref_id, title, type_id, browser_url, share_token, share_url, share_verification, attachments; share create: share_token, share_url, browser_url, share_verification). Wire shape is unchanged — the server has always emitted these fields; only the contract surface (consumed by Scalar, generated openapi-fetch, and Postman) is new. Clients can now codegen typed response models instead of hand-rolling them. Backed by Zod modules under @symbol/shared/contracts/output/*.

  60. Additivepreview

    Preview API launched

    Symbol's public HTTP API is now available under /api/preview/* on the free preview tier. The full surface — capsules, types, projects, organizations, share, collections, invitations, attachments, mentions, references, library, search, workspaces, billing (non-webhook), GDPR, and support — ships with a published OpenAPI 3.1 document, RFC 9457 Problem Details errors, generated TypeScript and Dart clients, and an MCP server. Subsequent changes will appear here, classified breaking / additive / cosmetic. Preview gives no SLA and no paid tier; v1 will follow once the surface stabilises.