three doors, one rule
Nothing reaches a reader except through something the maker published.
This host is the JSON behind sk8 studio. An artifact reaches you through a published zine, a note through a published issue, a collection only once its maker makes it public. The deeper the door, the more key it asks for.
- base
https://api.sk8.studio, JSON both ways- errors
{ error, code }— branch oncode; the prose may change- reads are live
- What a holder contains now, in its order now. Re-read rather than cache.
- ids
- uuids. A malformed one is a 404, same as a missing one.
door 1no pass
open · collections and previews: 300 reads per 15 min- carry
- Nothing. No cookie, no key.
- send
- A plain request. Nothing in the headers.
- except
- A draft preview: its signed token is in the URL and is the whole credential, for seven days.
What sk8.studio reads to show a maker's public collections, the newsletter index, and a draft a maker chose to share. Nothing to hold, nothing to send.
GET/public/collections/:idone public collection: its order, glosses, arrangement and items
curl https://api.sk8.studio/public/collections/6f1c…
{
"collection": { "title": "what the river kept",
"curator": "bushsk8r" },
"arrangement": { "title": "upstream",
"text": "Three sources, one reading…",
"acts": { "intro": "…" } },
"items": [
{ "kind": "source", "gloss": "where it started",
"visible": true },
{ "kind": "note", "visible": false,
"note": { "title": "the bend", "type": "reading" } },
{ "kind": "artifact", "visible": true,
"artifact": { "title": "silt", "zineTitle": "low water" } }
]
}visible: false is the rule at work: that note shows its title and type, not its content, until it lives in a published issue. An artifact shows when an ungated published zine carries it, and names the zine.GET/public/collectionsevery purist's public collections
curl https://api.sk8.studio/public/collections
{ "collections": [
{ "id": "6f1c…", "title": "what the river kept",
"curator": "bushsk8r" }
] }GET/gallery/the newsletter index, newest first
curl https://api.sk8.studio/gallery/
{ "newsletters": [
{ "id": "a40e…", "title": "issue four: low water" }
] }GET/gallery/preview/:tokena draft behind a signed link, seven days
curl https://api.sk8.studio/gallery/preview/eyJ…
{ "error": "preview link is invalid or expired",
"code": "not_found" }door 2gallery pass
keyword at the door · 10 wrong ones per 15 min- carry
- The
gallery_tokencookie, set when the keyword is right. - send
- The browser sends it on its own, from sk8.studio's subdomains only. From a shell:
curl -b gallery_token=… - lasts
- 24 hours. A gated zine also needs its own keyword once; the unlock is kept on the pass, not in a second cookie.
The gallery: zines read as books, newsletters read issue by issue. A pass is cheap to mint and worthless without the keyword, which travels by word of mouth and is not written here.
GET /auth/gallery/Mint a pass. It holds nothing yet.
POST /auth/gallery/Send
{ galleryPassId, keyword }. The keyword admits; ten wrong ones in fifteen minutes and you wait.Set-Cookie: gallery_tokenSend it back with every read. When it lapses, ask again; keeping the cookie doesn't stretch the pass.
GET/gallery/zine?since=the shelf: every published zine
curl -b gallery_token=… \ https://api.sk8.studio/gallery/zine
{ "zines": [
{ "id": "c2b9…", "title": "low water",
"path": "/gallery/zine/bushsk8r/low-water",
"isGated": false, "artifacts": [ … ] },
{ "id": "e71d…", "title": "the other bank",
"path": "/gallery/zine/bushsk8r/the-other-bank",
"isGated": true, "artifacts": [] }
] }since is an ISO timestamp; a bad one is ignored. A gated zine lists with no artifacts.GET/gallery/zine/:handle/:slugone zine, its artifacts, where each came from and what it became
curl -b gallery_token=… \ https://api.sk8.studio/gallery/zine/bushsk8r/low-water
{ "title": "low water", "curator": "bushsk8r",
"path": "/gallery/zine/bushsk8r/low-water",
"artifacts": [
{ "title": "silt", "artifactType": "image",
"sources": [ { "title": "river survey, 1931" } ],
"became": [ { "title": "the other bank" } ] }
] }sources are what an artifact drew on, became what later drew on it — each only where you could reach it anyway. A zine is named by its maker's handle and its title; a renamed one answers 301 with "code": "moved" and its new path.POST/gallery/zine/:handle/:slug/unlocka zine's own keyword, once per pass
{ "error": "keyword required",
"code": "zine_gated", "gated": true }{ "message": "zine unlocked" }GET/gallery/newsletter/:handle/:slugone issue, its sections in their order now
curl -b gallery_token=… \ https://api.sk8.studio/gallery/newsletter/bushsk8r/issue-four-low-water
{ "newsletter": {
"title": "issue four: low water",
"acts": [ "intro", "complication", "resolution" ],
"content": [ { "title": "upstream", "notes": [ … ] } ]
} }acts lists the acts the issue moves through; each section may name its act. A note turned back to draft drops out of the read.door 3agent key
a maker's own · not issued to the public- carry
- An agent key, never a cookie.
- send
Authorization: Bearer atelier_<element>_…on every call, or the same key asx-api-key.- from
- The maker makes it in the studio, bound to one element.
The machine makes; the human curates. A maker holds keys for up to five agents, each bound to an element, and lets them work on a canvas. What an agent writes stays in the studio until the maker puts it in something and publishes.
| paper | writes | A new artifact. It closes the canvas's open version and opens the next major. Retry-safe with an Idempotency-Key. |
|---|---|---|
| rock | reads | What is on the canvas now. |
| water | revises | An artifact in place; the open version moves a minor step. |
| fire | deletes | An artifact. Versions are left as they were. |
| scissors | later | Postponed. |
A key reads atelier_<element>_…, is shown once when the maker makes it in the studio, and is stored only as a hash. Rotating it ends the old one.
Never an agent's: crediting a source, arranging, ordering, collecting, publishing.
Those are the maker's judgment, and they take the maker's own session.
when it refuses
Every refusal is { error, code }. Codes are only ever added, never renamed; the error prose is for people.
| status | code | means |
|---|---|---|
| 400 | invalid_request | The body or query didn't parse. The message says which field. |
| 401 | unauthorized | No pass, or one that is forged, malformed or expired. Mint a new one. |
| 401 | zine_gated | This zine has its own keyword. Unlock it, then read again. |
| 403 | forbidden | Your pass or key is real but doesn't open this. |
| 404 | not_found | Missing, unpublished, private, or not a uuid. They look the same on purpose. |
| 429 | rate_limited | Too many calls, or too many wrong keywords. Wait out the window. |