Scrolls

For programs

The API

Everything on the Umbilici and tracks page, your own umbilicus points and your private windings, as JSON for scripts and notebooks. AI assistants can use the same things through the MCP server.

Public data

GET /api/v1/umbilici

Every published umbilicus file: name, scroll, source, kind, origin, volume, points, bytes, url (the download) and share_url, plus zip_url for all of them at once.

curl https://scrolls.traditionalcamber.com/api/v1/umbilici

GET /api/v1/umbilici/{name}

One file, exactly as /data/umbilici/{name} serves it: control_points (x, y, z, sometimes score) and metadata.

curl -O https://scrolls.traditionalcamber.com/api/v1/umbilici/PHerc0826_official.json

GET /api/v1/scrolls

The scrolls this site marks: slug, pherc, volume (its path in the organisers' bucket), voxel_um, shape_zyx, papyrus_z (the slices with papyrus), slices_to_mark and the marking page.

GET /api/v1/tracks

Our surface tracks (skeletons of the m7 surface prediction): path, scroll, bytes, url, share_url; and under organisers the organisers' own track datasets, which are linked rather than copied here.

curl https://scrolls.traditionalcamber.com/api/v1/tracks | python3 -c 'import json,sys; [print(f["url"]) for f in json.load(sys.stdin)["files"]]' | xargs -n1 curl -O -C -

Your own umbilicus points (key needed)

GET /api/v1/me

Which account the key belongs to: {"username": ..., "is_admin": ..., "key": {"name": ..., "prefix": ...}}.

GET /api/v1/scrolls/{scroll}/umbilicus

Your points on one scroll: rev (send it back when you change them), finished, updated_at, checks (the same checks the marking page shows), points (x, y, z, note, id, sorted by slice) and umbilicus, the same points as an umbilicus file for the spiral fit (notes starting with ? get score 50).

curl -H "Authorization: Bearer $KEY" https://scrolls.traditionalcamber.com/api/v1/scrolls/PHerc0175A/umbilicus

PUT /api/v1/scrolls/{scroll}/umbilicus

Replaces all your points on that scroll, exactly as a save from the marking page does (the same checks, revisions and history copies). Send {"points": [...], "rev": n} with the rev the GET gave you, or 0 for a scroll you haven't started. Each point is {"x": ..., "y": ..., "z": ..., "note": "optional"}; start a note with ? when unsure. At most 400 points, in a body of up to 1 MB.

An umbilicus file works too, such as the umbilicus the GET gives you: send its {"control_points": [...], "metadata": {"notes": {...}}} with "rev" added, as above. A score under 100 means unsure, and the notes in metadata.notes (by slice) are kept.

curl -X PUT -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"rev": 0, "points": [{"x": 4000, "y": 4100, "z": 6500}, {"x": 4012, "y": 4104, "z": 6600, "note": "? faint"}]}' \
  https://scrolls.traditionalcamber.com/api/v1/scrolls/PHerc0175A/umbilicus

The reply gives the new rev, saved_at, unchanged and the checks. If you, or a browser tab of yours, saved since the rev you sent, the reply is 409 with the current rev: GET again, merge, and send again. Sending the same body again, say after the reply was lost, is safe: points sent without an id get one made from the point itself, so the retry answers 200 with "unchanged": true rather than 409. The marking page keeps where you were looking; only the points change.

Your private windings (key needed)

The same windings as on My windings: a title, an optional scroll and description, files of any kind (up to 1,024 MB each, 50 per winding, 4,096 MB in all), notes (up to 500 per winding), and the accounts you share each one with. An account can keep 200 windings. Nobody else can see a winding: to any other account its id answers exactly as one that doesn't exist (404), and with no key every winding address answers 401. Each winding's link is its private link: it opens only for you and the accounts you share it with, once logged in. {id} below is the winding's id.

Each account can add 120 windings and notes an hour, run 3 uploads at once and try 30 shares an hour; past those you get 429. While the site's space for saved work is full, new windings, notes and longer descriptions get 507, and so do files while the server's disk is nearly full or everyone's windings have used their space.

GET /api/v1/windings

Your windings and those shared with you, the latest changed first: id, link, title, scroll, description, owner, your_role (owner, annotate or view), bytes, created_at, updated_at, files (name, size, content_type, sha256, url) and, on your own, shares.

POST /api/v1/windings

Makes one: {"title": "...", "scroll": "PHerc0826", "description": "..."}. Only the title is needed, 1 to 200 characters; a description can have up to 10,000. Replies 201 with the winding.

curl -X POST -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"title": "Band 3, first pass", "scroll": "PHerc0826"}' https://scrolls.traditionalcamber.com/api/v1/windings

GET, PATCH, DELETE /api/v1/windings/{id}

One winding. PATCH changes only the fields you send (owner only). DELETE removes it with its files, notes and shares, for good (owner only).

PUT /api/v1/windings/{id}/files/{name}

Adds a file (owner only), sent as the raw body, not a form, up to 1,024 MB; a file of the same name is replaced and keeps its notes. Names are 1 to 128 letters, digits, dots, dashes or underscores, starting with a letter or digit. Up to 3 uploads can run at once on your account; another gets 429 until one finishes. Replies 201 (new) or 200 (replaced) with the file's size and sha256 to check against yours.

curl -T band3_x.tif -H "Authorization: Bearer $KEY" https://scrolls.traditionalcamber.com/api/v1/windings/$ID/files/band3_x.tif

GET, HEAD, DELETE /api/v1/windings/{id}/files/{name}

Downloads a file (anyone it's shared with; range requests work, so curl -C - resumes), or deletes it (owner only; notes on it stay, without their place on the picture).

curl -H "Authorization: Bearer $KEY" -O https://scrolls.traditionalcamber.com/api/v1/windings/$ID/files/band3_x.tif

GET /api/v1/windings/{id}/zip

All the winding's files in one zip, for anyone who may see the winding. The zip is streamed as it is made, and its files are stored as they are, not compressed. A winding with no files yet answers 404. On the winding's page, Download all gives the same zip.

curl -H "Authorization: Bearer $KEY" -o band3.zip https://scrolls.traditionalcamber.com/api/v1/windings/$ID/zip

GET, POST /api/v1/windings/{id}/notes, DELETE /api/v1/windings/{id}/notes/{note}

Notes, oldest first: id, author, text, file, x, y, created_at, yours. Post {"text": "...", "file": "pic.png", "x": 0.3, "y": 0.6} as the owner or an account that may annotate; the text is 1 to 4,000 characters, file is optional, and x and y (fractions of a picture's width and height from its top left corner) place the note on a PNG, JPEG, WebP, GIF or AVIF file. The owner can delete any note, others their own.

GET /api/v1/windings/{id}/shares, PUT, DELETE /api/v1/windings/{id}/shares/{username}

Whom your winding is shared with (owner only). PUT with {"role": "view"} (look and download) or {"role": "annotate"} (add notes too) shares it with that account, or changes its role; DELETE stops sharing (their notes stay). Only accounts with a password can be shared with. On a winding shared with you, DELETE with your own username takes it off your list: you can't open it again unless its owner shares it with you again (your notes on it stay).

Errors