api.sk8.studio /health llms.txt sk8.studio

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 on code; 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
request
curl https://api.sk8.studio/public/collections/6f1c…
200abridged · synthetic
{
  "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
request
curl https://api.sk8.studio/public/collections
200abridged · synthetic
{ "collections": [
  { "id": "6f1c…", "title": "what the river kept",
    "curator": "bushsk8r" }
] }
GET/gallery/the newsletter index, newest first
request
curl https://api.sk8.studio/gallery/
200abridged · synthetic
{ "newsletters": [
  { "id": "a40e…", "title": "issue four: low water" }
] }
The index is open; reading an issue is door 2.
GET/gallery/preview/:tokena draft behind a signed link, seven days
request
curl https://api.sk8.studio/gallery/preview/eyJ…
404once expired
{ "error": "preview link is invalid or expired",
  "code": "not_found" }
The token is the whole credential, and only the maker makes one. A preview is not published work: don't cache it, don't index it.

door 2gallery pass

keyword at the door · 10 wrong ones per 15 min
carry
The gallery_token cookie, 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.

  1. GET /auth/gallery/

    Mint a pass. It holds nothing yet.

  2. POST /auth/gallery/

    Send { galleryPassId, keyword }. The keyword admits; ten wrong ones in fifteen minutes and you wait.

  3. Set-Cookie: gallery_token

    Send 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
request
curl -b gallery_token=… \
  https://api.sk8.studio/gallery/zine
200abridged · synthetic
{ "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
request
curl -b gallery_token=… \
  https://api.sk8.studio/gallery/zine/bushsk8r/low-water
200abridged · synthetic
{ "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" } ] }
  ] }
Lineage both ways: 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
401before unlocking
{ "error": "keyword required",
  "code": "zine_gated", "gated": true }
200after
{ "message": "zine unlocked" }
GET/gallery/newsletter/:handle/:slugone issue, its sections in their order now
request
curl -b gallery_token=… \
  https://api.sk8.studio/gallery/newsletter/bushsk8r/issue-four-low-water
200abridged · synthetic
{ "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 as x-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.

Every change an agent makes is logged against it: the record of who revised what.
paperwritesA new artifact. It closes the canvas's open version and opens the next major. Retry-safe with an Idempotency-Key.
rockreadsWhat is on the canvas now.
waterrevisesAn artifact in place; the open version moves a minor step.
firedeletesAn artifact. Versions are left as they were.
scissorslaterPostponed.

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.

statuscodemeans
400invalid_requestThe body or query didn't parse. The message says which field.
401unauthorizedNo pass, or one that is forged, malformed or expired. Mint a new one.
401zine_gatedThis zine has its own keyword. Unlock it, then read again.
403forbiddenYour pass or key is real but doesn't open this.
404not_foundMissing, unpublished, private, or not a uuid. They look the same on purpose.
429rate_limitedToo many calls, or too many wrong keywords. Wait out the window.