API

/api/sync (push / pull pipeline)

The component, pattern, and page sync pipeline between a workspace and its registry.

The low-level protocol behind handoff-app push/pull/sync-status, distinct from /api/registry's one-endpoint-per-data-type model. All three routes require a sync bearer (verifySyncAuth(), see Auth & scopes); upload additionally requires sync:write.

EndpointPurpose
GET /api/sync/statusConnection health + current registry statistics — what sync-status prints.
GET /api/sync/changes?since=<id>Returns every sync event recorded after since (a sync event ID, not a timestamp). since=0 (the default) returns full history. This is what pull diffs against .handoff/sync-state.json to decide what to write back into the workspace.
POST /api/sync/uploadApplies a batch of create/update/delete sync events — components, patterns, pages, source files, build artifacts, screenshots — from a push.

Upload semantics

POST /api/sync/upload takes { changes: SyncChange[] } and applies them in order. The batch is fail-fast, not partial: the first bad or failing entry ends the request, and entries after it are never attempted.

  • 200: { ok: true, appliedCount, applied: [...] }, everything applied.
  • 400: a malformed entry (missing entityType/entityId/action). Entries before it in the array have already been written.
  • 500: an entry threw while being applied, { error, applied: [...] } lists what did make it through before the failure.

openapi.yaml still documents a 207 for this route, but the handler never returns one; there is no partial-success status code anywhere in the app. Treat a non-200 as "the batch stopped part-way" and re-push.

A component/pattern data change triggers an async validation snapshot on the registry side.

Images ride along with component payloads

Component artifacts pushed through upload may reference local images (quoted strings, CSS url()). The CLI resolves these to local files, assigns content-addressed IDs (img_<sha256[:12]>), rewrites the references in the pushed artifact text to /api/handoff/assets/{assetId}/raw, and pushes the images themselves separately via the per-image ingest endpoint (POST /api/registry/assets/ingest), so the registry always serves images from its own asset store, regardless of where they lived in the workspace.

Relationship to push:all

push:all covers config/theme/navigation/tokens/dtcg/icons/logos/fonts (all through /api/registry/*), components and pages go through this cluster instead, as one of push:all's steps (or standalone via plain push). See Workspace → CLI reference for every flag on both commands.

On this page