Every route on this page takes x-tweaklane-key: tl_pk_… and Authorization: Bearer <user token>. A request reads and writes only the data of the user in the token's sub, in that app.
Reading
| Route | Response |
|---|---|
GET /state[?screen=id] |
{layouts, killSwitch, theme, userScreens, navigation, designs, designIndex, versions}; without screen, everything the user has |
GET /layouts[?screen=id] |
{layouts: [...]} |
GET /kill-switches |
{killSwitch: {engaged, screens, components}} |
A layout entry:
| Field | Meaning |
|---|---|
screen |
screen id |
document |
the layout document; null after a reset |
deleted |
true after a reset (kept so other devices learn of it) |
serverVersion |
the version to send as baseVersion on the next write |
schemaVersion, appVersion |
as written |
editedAt, updatedAt |
edit time on the device, write time on the server |
Writing documents
| Route | Body |
|---|---|
PUT /layouts/:screen |
{document, baseVersion, catalogHash?, appVersion?, editedAt?} |
DELETE /layouts/:screen?baseVersion=n[&editedAt=…] |
resets the screen to your default |
PUT /theme, DELETE /theme |
the app theme document, same body and query |
PUT /user-screens, DELETE /user-screens |
the order of the user's own screens and the start screen |
PUT /navigation, DELETE /navigation |
the navigation document |
PUT /designs, DELETE /designs |
the active design |
PUT /designs/:id, DELETE /designs/:id |
one named design; ids are 1–40 of a–z 0–9 - |
User screens are written with PUT /layouts/<prefix><id>, validated in user-screen mode.
Optimistic concurrency. baseVersion is the serverVersion you last saw, 0 for a screen
the server has never stored. On success the API answers 200 {layout} with the new
serverVersion. If the server has moved on, it answers 409
{error: "conflict", message, current} with the server copy; resolve and write again on top of
current.serverVersion.
Every document is validated against the catalog manifest named by catalogHash. Without it, the
pinned catalog of appVersion is used, else the newest catalog this user uploaded, else the
answer is 422 catalog_missing.
Versions
| Route | Body / response |
|---|---|
POST /versions |
{version, snapshot, catalogHash, appVersion?} → 201 {id, pruned}; 409 for an existing id |
GET /versions/:id[?catalogHash=…] |
{version, snapshot, check?}; check lists the snapshot's issues against that catalog |
Version ids are UUIDs and versions are append-only. Past the limit, the oldest versions that are neither named nor a design's head are pruned.
Catalogs
POST {TWEAKLANE_URL}/catalogs
x-tweaklane-catalog-hash: <sha256 hex of the body>
The body is the catalog manifest. Catalogs are content-addressed by the sha256 of their bytes and
immutable: 201 {hash, created: true} for a new one, 200 for a known one, 409
catalog_pinned when the app version's catalog is pinned, 422 invalid_catalog.
AI
| Route | Body |
|---|---|
GET /ai/consent, PUT /ai/consent, DELETE /ai/consent |
see AI |
POST /ai/patch |
{screen, prompt, document, catalogHash, appVersion?, theme?} |
POST /ai/navigation |
{prompt, navigation, userScreens, catalogHash, appVersion?} |
POST /ai/designs |
{prompt, screen, document, designs, versions, undo?, redo?, catalogHash, appVersion?} |
200 {status: "proposed", …, provider, model} with the proposed document or operations, or
{status: "rejected", reasons}. Proposals may carry notes that explain a substitution. Errors:
403 consent_required or ai_disabled, 429 rate_limited, 502/503/504 when the model doesn't
answer.
Next: API: server routes.