{"openapi":"3.0.3","info":{"title":"spicemine API","version":"0.155.1","description":"Multi-org, multi-tenant, multi-SCM hosting (fossil, git, hg, jj, lore) on mélangej (ABCL on Jooby/Netty). Engine wire protocols mount at /{org}/{repo} beside this API; fossil/git/hg/jj clients use HTTP Basic user:token, lore clients use JWTs from /api/v0/lore/token."},"components":{"securitySchemes":{"bearer":{"type":"http","scheme":"bearer","description":"smt_ personal access token"}}},"paths":{"/api/v0/ping":{"get":{"summary":"Liveness + stack fingerprint","responses":{"200":{"description":"ok"}}}},"/api/v0/health":{"get":{"summary":"Does it work, not only answer: status ok, degraded or down, version, uptime, and per check its name, ok and ms - nothing about the deployment. 200 unless down, 503 when down, so a plain HTTP monitor reads it. Checks: database (the database answers); schema (every migration this build carries is applied, none changed); storage (the data directory takes a file); disk (the data directory has room); engine:<name> (each engine in use runs); hg-sidecar (the hgweb sidecar answers, when hg is in use); pictures (the picture decoder reads HEIC, AVIF and WebP, when one is configured); mirror-queue (the mirror queue is not stuck on one job); sub-clock (the subs' clock is going round); backup (the last backup succeeded, when backups are configured); offsite (the last offsite push succeeded, when an offsite copy is configured); mail (no mail has waited over an hour or been given up in two days, when mail is on). Run at most every 10 s; each has a budget, past which it fails rather than waits","responses":{"200":{"description":"ok or degraded"},"503":{"description":"down: a critical check failed"}}}},"/api/v0/admin/health":{"get":{"summary":"The same checks for an instance admin, with each one's detail, free bytes, the mirror queue, failing mirrors and subs, and open event listeners","security":[{"bearer":[]}],"responses":{"200":{"description":"ok or degraded"},"503":{"description":"down"},"403":{"description":"not an instance admin"}}}},"/api/v0/help/routes":{"get":{"summary":"Help's capture tool (HELP.md s8), instance admin only: every page route, one public example of it, and the pictures and flows stored for it","security":[{"bearer":[]}],"responses":{"200":{"description":"ok"}}}},"/api/v0/help/shots":{"put":{"summary":"Store a picture (light.png, dark.png: a PNG, 3 MB at most) or a flow (flow-<slug>.webm: a WebM, 12 MB) for a page route; ?route=&name=, the bytes as the body. Instance admin only","security":[{"bearer":[]}],"responses":{"201":{"description":"stored"},"400":{"description":"not a PNG or WebM by its bytes, too large, or not one of the names"},"403":{"description":"not an instance admin"},"404":{"description":"no such page route"}}}},"/api/v0/help/harvest":{"put":{"summary":"Hand over the HTML a signed-in demo account was shown for a page route (?route=&example=), to harvest its help from; under 4 MB. Instance admin only","security":[{"bearer":[]}],"responses":{"201":{"description":"harvested"},"403":{"description":"not an instance admin"},"404":{"description":"no such page route"}}}},"/api/v0/session/token":{"post":{"summary":"Exchange username/password for a bearer token scoped to named repositories or spaces, or - only when asked - to the whole account","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["username","password"],"properties":{"username":{"type":"string"},"password":{"type":"string"},"name":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"},"description":"repo:<space>/<name>:<verb> or space:<slug>:<verb>, verb read|write (default read). An element the server cannot read is a 400 naming it - nothing is minted. A scope only ever narrows what the account may do, and a scoped token can clone and push but cannot create or delete anything."},"account_wide":{"type":"boolean","description":"true mints a token that can do everything this account can, in every space, now and later. Required when scopes is empty: there is no default, because the empty scope set is the most privileged value there is."}}}}}},"responses":{"201":{"description":"token created"},"401":{"description":"bad credentials"}}},"delete":{"summary":"End the token this request carries (any token: it can only end itself). For signing out on a device (0.65.0)","security":[{"bearer":[]}],"responses":{"200":{"description":"ok"}}}},"/api/v0/whoami":{"get":{"summary":"The calling user and their org memberships","security":[{"bearer":[]}],"responses":{"200":{"description":"ok"}}}},"/api/v0/me/page":{"get":{"summary":"Your me page (0.75.0): its address (/@name) and its blocks in order - kind, title, ref, item, url, starts_at, ends_at - with whether each shows now and its taps in the last 7 days","security":[{"bearer":[]}],"responses":{"200":{"description":"ok"}}},"put":{"summary":"Replace your me page, in order. Kinds: project, edition, journal, link, voice, reach; at most 30 blocks. project, journal: ref <space>/<project> you may see; edition: and item, an edition id; voice: and item, a sound's path, in a project you may read; link: url http(s); reach: url https or mailto:, or a title alone. title at most 80 characters. starts_at, ends_at: UTC. A block with the id of one of yours keeps it and its counts; one left out is gone, with its counts","security":[{"bearer":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["blocks"],"properties":{"blocks":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"kind":{"type":"string"},"title":{"type":"string"},"ref":{"type":"string"},"item":{"type":"string"},"url":{"type":"string"},"starts_at":{"type":"string"},"ends_at":{"type":"string"}}}}}}}}},"responses":{"200":{"description":"saved; the page as it now is"},"400":{"description":"a block refused, by its place"}}}},"/api/v0/me/page/taps":{"get":{"summary":"How often each of your blocks was followed, per UTC day, over the last ?days= (1-90, default 30). Nothing about a visitor is kept","security":[{"bearer":[]}],"parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"description":"ok"}}}},"/api/v0/me/follows":{"get":{"summary":"The spaces you follow (0.77.0), most recently followed first: slug, name, since","security":[{"bearer":[]}],"responses":{"200":{"description":"ok"}}}},"/api/v0/me/following":{"get":{"summary":"What the spaces you follow have posted, newest first - one list, nothing ranked - only what you may read and only what is out; each post with its space, project and appreciations. ?before=<the last post's id> for the page below, ?limit= up to 100","security":[{"bearer":[]}],"parameters":[{"name":"before","in":"query","required":false,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"description":"ok"}}}},"/api/v0/me/lists":{"get":{"summary":"Your lists (0.95.0): owner, slug, title, description, visibility, url (/@you/lists/slug), items_count; oldest first","security":[{"bearer":[]}],"responses":{"200":{"description":"ok"}}},"post":{"summary":"Make a list: slug (lower-case letters, digits, hyphens, up to 40), title (up to 80), description, visibility public or private. 409 when you have one at that slug or 50 already","security":[{"bearer":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["slug","title"],"properties":{"slug":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"visibility":{"type":"string","enum":["public","private"]}}}}}},"responses":{"201":{"description":"made; body is the list"},"400":{"description":"slug or title refused"},"409":{"description":"slug taken, or too many lists"}}}},"/api/v0/lists/{owner}/{slug}":{"get":{"summary":"A list with its items in order (id, type repo|url|gone, label, note, position, added_at; repo and engine, or url) - only the items whose project you may read; a deleted project stays as gone. A private list is 404 to anyone but its owner","parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}},"patch":{"summary":"Change your list's title, description or visibility","security":[{"bearer":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string"},"description":{"type":"string"},"visibility":{"type":"string","enum":["public","private"]}}}}}},"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"saved; body is the list"},"400":{"description":"an empty title"},"404":{"description":"not yours, or not there"}}},"delete":{"summary":"Delete your list and its items","security":[{"bearer":[]}],"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}}},"/api/v0/lists/{owner}/{slug}/items":{"post":{"summary":"Add to your list, at the end: repo <space>/<project> you may read (404 otherwise), or url http(s) with label and note. 409 when it is there already or the list holds 200","security":[{"bearer":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"repo":{"type":"string"},"url":{"type":"string"},"label":{"type":"string"},"note":{"type":"string"}}}}}},"parameters":[{"name":"owner","in":"path","required":true,"schema":{"type":"string"}},{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"added; body is the item"},"400":{"description":"not an address"},"404":{"description":"the list is not yours, or the project is not one you may read"},"409":{"description":"already there, or the list is full"}}}},"/api/v0/links/{id}":{"patch":{"summary":"Change an item of your list by its link id: note, label (a url item's), move up or down","security":[{"bearer":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"note":{"type":"string"},"label":{"type":"string"},"move":{"type":"string","enum":["up","down"]}}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"the item as it now is"},"404":{"description":"not an item of yours"}}},"delete":{"summary":"Take an item out of your list, by its link id","security":[{"bearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"ok"}}}},"/api/v0/orgs/{org}/follow":{"put":{"summary":"Follow a space (0.77.0): the answer says whether you do and how many do","security":[{"bearer":[]}],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}},"delete":{"summary":"Stop following a space","security":[{"bearer":[]}],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}}},"/api/v0/repos/{org}/{repo}/posts/{id}/appreciate":{"post":{"summary":"Thank a post (0.77.0, from Behance's appreciate): one tap from a named account, on a post you can read that is not yours (409); {on: false} takes it back. The answer says how many and whether you did","security":[{"bearer":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"on":{"type":"boolean"}}}}}},"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"ok"}}}},"/api/v0/repos/{org}/{repo}/posts/{id}/appreciations":{"get":{"summary":"Who thanked a post, first first","parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"ok"}}}},"/api/v0/repos/{org}/{repo}/posts/{id}/carry":{"post":{"summary":"Carry a post with credit (0.78.0): its words into a project you may post in (to: <space>/<project>), as a new post with its trail - origin and who wrote it, via when it was itself carried, the origin's terms - and an optional line of your own (note). Only a public, published post of a public project whose terms let anybody share it: any license in the catalog; all rights reserved, its own terms or none: 409. Posts lists say carried","security":[{"bearer":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["to"],"properties":{"to":{"type":"string"},"note":{"type":"string","maxLength":280},"visibility":{"type":"string"}}}}}},"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"201":{"description":"carried; the new post with its trail"},"403":{"description":"you may not post there"},"409":{"description":"not carriable: not public, or its terms do not let it be shared, or already there"}}}},"/api/v0/users":{"post":{"summary":"Create a user (instance admin only; no self-serve signup)","security":[{"bearer":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["username","password"],"properties":{"username":{"type":"string"},"password":{"type":"string"},"admin":{"type":"boolean"}}}}}},"responses":{"201":{"description":"created"},"403":{"description":"not instance admin"},"409":{"description":"taken/invalid"}}}},"/api/v0/users/{name}":{"delete":{"summary":"Delete an account and what is only its (instance admin, account token; never oneself; refused while the personal space still holds projects)","security":[{"bearer":[]}],"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"deleted"},"403":{"description":"not instance admin"},"404":{"description":"no such user"},"409":{"description":"oneself, or the space still holds projects"}}}},"/api/v0/orgs":{"get":{"summary":"Orgs visible to the caller","security":[{"bearer":[]}],"responses":{"200":{"description":"ok"}}},"post":{"summary":"Create an org (instance admin only)","security":[{"bearer":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["slug"],"properties":{"slug":{"type":"string"},"display_name":{"type":"string"}}}}}},"responses":{"201":{"description":"created"},"403":{"description":"forbidden"},"409":{"description":"taken/invalid"}}}},"/api/v0/orgs/{org}":{"get":{"summary":"Org detail (members only)","security":[{"bearer":[]}],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}}},"/api/v0/orgs/{org}/members":{"post":{"summary":"Add/update a member (org admin)","security":[{"bearer":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["username"],"properties":{"username":{"type":"string"},"role":{"type":"string","enum":["member","writer","admin"]}}}}}},"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"set"},"403":{"description":"forbidden"},"404":{"description":"no such user/org"}}}},"/api/v0/orgs/{org}/policy":{"get":{"summary":"What this space is allowed (space admin): its mirror allowance, its disk allowance in MB and what it holds, its project allowance and how many it has; each with where the number came from (TENANCY.md)","security":[{"bearer":[]}],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}},"put":{"summary":"Set this space's allowances (instance admin): mirrors, sub refresh minutes, disk in MB, projects. -1 is no limit, 0 is none. The refusal of a write lands on the next write after the disk line is crossed, never on the push that crosses it.","security":[{"bearer":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"mirror_limit":{"type":"integer","minimum":-1},"sub_refresh_minutes":{"type":"integer","minimum":0},"disk_limit_mb":{"type":"integer","minimum":-1},"repo_limit":{"type":"integer","minimum":-1}}}}}},"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"set; body carries the new policy"},"400":{"description":"not an integer"},"403":{"description":"not instance admin"}}},"delete":{"summary":"Clear this space's allowances, all four, back to the instance defaults (instance admin). NOT the same as setting 0.","security":[{"bearer":[]}],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}}},"/api/v0/orgs/{org}/repos":{"get":{"summary":"Repos in the org the caller can read (public repos: no auth needed). Each carries name, the lower-case key in every address, and display_name, the name as it was typed (0.63.0)","parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}},"post":{"summary":"Create a repo (writer role). Engine defaults to fossil. name may carry capitals (0.63.0): they are kept as display_name, and the key is name in lower case, so a name that differs from another only in capitals is refused (409).","security":[{"bearer":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"engine":{"type":"string","enum":["fossil","git","lore","hg","jj","sub"]},"visibility":{"type":"string","enum":["public","members","private"]}}}}}},"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"created; body carries sync_url"},"403":{"description":"forbidden"},"409":{"description":"exists/invalid"}}}},"/api/v0/repos/{org}/{repo}":{"get":{"summary":"Repo detail incl. engine, sync_url and display_name. {org} and {repo} are found in any case (0.63.0)","parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}},"delete":{"summary":"Delete a repo (org admin); data is parked, not erased","security":[{"bearer":[]}],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}}},"/api/v0/repos/{org}/{repo}/timeline":{"get":{"summary":"Recent commits, engine-normalized: hash/when/user/comment, plus native_id where the engine has a second name for a revision (jj's change id). Every branch at once, newest first; ?branch= (0.51.0) gives that branch's history instead (400 for a malformed name, 404 for one the repository does not have)","parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer"}},{"name":"branch","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}}},"/api/v0/repos/{org}/{repo}/import":{"post":{"summary":"Fetch history IN from source_url (writer). fossil, git, jj: an http(s) address, re-runnable; for fossil this is the only way an existing project arrives, since a push from a different project code is refused. lore (0.49.0): a lore://, grpc:// or grpcs:// address on another loreserver, into an empty project only, its revisions replayed; source_token is the person's own token for that server, used for this fetch and not kept; the answer says how many revisions came in. Mirrors are rebuilt afterwards. Not MIRRORS.md, which serves history OUT.","security":[{"bearer":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["source_url"],"properties":{"source_url":{"type":"string"},"source_token":{"type":"string"}}}}}},"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"imported"},"501":{"description":"engine lacks import"},"502":{"description":"fetch failed"}}}},"/api/v0/repos/{org}/{repo}/tree":{"get":{"summary":"Every path at the default branch tip, or at ?branch='s (0.51.0); ?dir= keeps the ones under a prefix","parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}},{"name":"dir","in":"query","required":false,"schema":{"type":"string"}},{"name":"branch","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}}},"/api/v0/repos/{org}/{repo}/raw/{path}":{"get":{"summary":"One file's bytes at the default branch tip, or at ?branch='s (0.51.0), content type from the extension; 404 above 16 MB","parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}},{"name":"path","in":"path","required":true,"schema":{"type":"string"}},{"name":"branch","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}},"put":{"summary":"Write one file as a commit on the default branch, as the caller (write right; fossil, git, hg, jj). The body is the file; X-Message is the commit message. The API twin of the files view's drop zone.","security":[{"bearer":[]}],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}},{"name":"path","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"committed; body carries the revision"},"400":{"description":"bad path or empty body"},"501":{"description":"engine takes no files"},"502":{"description":"the engine refused"}}}},"/api/v0/repos/{org}/{repo}/branches":{"get":{"summary":"The repository's branches (0.51.0): {default, branches:[{name, commit, when, default}]}, sorted by name. git and jj: refs/heads (jj's bookmarks); hg: named branches, closed ones left out; fossil: branches, closed ones left out; lore: branches","parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}},"post":{"summary":"Make a branch (0.52.0; writer; a scoped token that may push will do): {name, from?}, from defaulting to the default branch. git, jj, fossil, lore; hg makes a branch with a commit, so 501. The journal records it with its commit","security":[{"bearer":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"from":{"type":"string"}}}}}},"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"made; {name, commit}"},"400":{"description":"bad name, or no such branch to start from"},"409":{"description":"exists"},"501":{"description":"not offered for this engine"}}}},"/api/v0/repos/{org}/{repo}/branches/{name}":{"delete":{"summary":"Remove a branch (0.52.0; writer): deleted for git and jj, closed for fossil, archived for lore; the default branch is refused (409). The answer and the journal carry the commit it pointed at, so it can be made again","security":[{"bearer":[]}],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}},{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"removed; {name, commit}"},"404":{"description":"no such branch"},"409":{"description":"the default branch"},"501":{"description":"not offered for this engine"}}}},"/api/v0/repos/{org}/{repo}/default-branch":{"put":{"summary":"Choose the default branch (0.52.0; space admin, account token): {name}. git and jj: HEAD; fossil: the main-branch setting; hg and lore: 501","security":[{"bearer":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string"}}}}}},"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"{name, commit}"},"404":{"description":"no such branch"},"501":{"description":"not offered for this engine"}}}},"/api/v0/repos/{org}/{repo}/issues":{"get":{"summary":"Open issues from the index, newest first; ?state=open|closed|all, ?q= the quick-search grammar (words, status:, sev:, pri:, label:, by:, assigned:, @me)","parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}},{"name":"state","in":"query","required":false,"schema":{"type":"string"}},{"name":"q","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}},"post":{"summary":"File an issue: one commit of .spicemine/issues/NNNN-slug.md as the caller (members of the space; severity, priority and labels are taken from writers only)","security":[{"bearer":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["title"],"properties":{"title":{"type":"string"},"body":{"type":"string"},"severity":{"type":"string"},"priority":{"type":"string"},"labels":{"type":"array","items":{"type":"string"}}}}}}},"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"filed; body is the issue"},"400":{"description":"a field the tracker does not know"},"403":{"description":"not a member"},"502":{"description":"the engine refused"}}}},"/api/v0/repos/{org}/{repo}/issues/{n}":{"get":{"summary":"One issue, read from its file at the tip, with its comments and the field changes each carried","parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}},{"name":"n","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"ok"}}}},"/api/v0/repos/{org}/{repo}/issues/{n}/comments":{"post":{"summary":"Append a comment and any field changes as one commit (members comment; status, resolution, assigned, severity and priority are taken from writers only; resolved needs a resolution)","security":[{"bearer":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"body":{"type":"string"},"status":{"type":"string"},"resolution":{"type":"string"},"assigned":{"type":"string"},"severity":{"type":"string"},"priority":{"type":"string"}}}}}},"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}},{"name":"n","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"201":{"description":"committed; body is the issue"},"400":{"description":"empty, or a field the tracker does not know"},"404":{"description":"no such issue"}}}},"/api/v0/repos/{org}/{repo}/editions":{"get":{"summary":"Editions of this repository, newest first: a name, the revision it names, a note, who and when, the zip's size and address","parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}},"post":{"summary":"Name the tip as an edition and make its zip with the engine's own archive (write right; fossil, git, hg, jj). Names are unique per repository and never move.","security":[{"bearer":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"note":{"type":"string"}}}}}},"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"named; body is the edition"},"400":{"description":"bad name or note"},"409":{"description":"name taken, or nothing to name"},"501":{"description":"engine makes no archives"}}}},"/api/v0/repos/{org}/{repo}/editions/{id}/zip":{"get":{"summary":"The edition's zip: the files of its revision, the same bytes every time","parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"ok"}}}},"/api/v0/repos/{org}/{repo}/posts":{"get":{"summary":"Journal posts, the pinned one first, then newest first: public ones for a reader, all of them for a member of the space - only what is out. ?queued=1 (0.76.0): the posts still to come, soonest first, for those who may post (403 for anyone else); each post says queued","parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}},{"name":"queued","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}},"post":{"summary":"Write a post (the space's admins, or its writers when the project allows; a token needs the repository's write scope). 280 characters, plain text. A kind (0.67.0): text, the default; quote, with cite naming who said it; or link, with href an http or https address, cite its title, and text a line about it that may be empty. A post read back carries kind, cite and href (null when unused).","security":[{"bearer":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["text"],"properties":{"text":{"type":"string","maxLength":280},"visibility":{"type":"string","enum":["public","members"]},"kind":{"type":"string","enum":["text","quote","link","chat"]},"cite":{"type":"string","maxLength":120},"href":{"type":"string","format":"uri","maxLength":2000},"publish_at":{"type":"string","description":"0.76.0: UTC, YYYY-MM-DD HH:MM[:SS] or with T and Z, a minute to a year ahead; the post waits in the queue until then"},"queue":{"type":"boolean","description":"0.76.0: true sets it at the project's next free place in its queue, queue_hours after the last one queued (journal-settings)"}}}}}},"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"posted; body is the post"},"400":{"description":"empty, too long, an unknown visibility or kind, or a link without an http(s) address"},"403":{"description":"not an owner"},"409":{"description":"the journal is full"},"429":{"description":"50 a day"}}}},"/api/v0/repos/{org}/{repo}/posts/{id}":{"patch":{"summary":"Change a post's visibility or pin it (one pinned per project); publish_now: true sends a queued post out now (409 when it is out already, 0.76.0). A post is never edited: text is refused.","security":[{"bearer":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"visibility":{"type":"string"},"pinned":{"type":"boolean"},"publish_now":{"type":"boolean"}}}}}},"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"ok"}}},"delete":{"summary":"Delete a post: the row is removed, not hidden","security":[{"bearer":[]}],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"ok"}}}},"/api/v0/repos/{org}/{repo}/showcase/modules":{"get":{"summary":"The case study: the showcase's modules after the cover, in order. Each has a kind (text, image, video, grid), paths (files in this repository at the tip of the default branch) and text (a text module's words, or the caption under the others)","parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}},"put":{"summary":"Replace the case study's modules, in the order given (write right). text: words, up to 4000 characters, no paths. image and video: one picture or film. grid: 2 to 12 pictures. A caption up to 200 characters. At most 40 modules; [] clears them. All or nothing: a refusal names the module by its place from 1.","security":[{"bearer":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["modules"],"properties":{"modules":{"type":"array","maxItems":40,"items":{"type":"object","required":["kind"],"properties":{"kind":{"type":"string","enum":["text","image","video","grid"]},"paths":{"type":"array","items":{"type":"string"},"maxItems":12},"text":{"type":"string","maxLength":4000}}}}}}}}},"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"saved; body is the modules"},"400":{"description":"a module refused, named by its place"},"403":{"description":"no write right"}}}},"/api/v0/repos/{org}/{repo}/showcase/credits":{"get":{"summary":"The case study's medium and tools (short phrases, in order) and credits (name and role, only people who are still collaborators); to a writer also collaborators, the names that may be credited","parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}},"put":{"summary":"Replace the medium, the tools and the credits at once (write right); a field left out is emptied. At most 8 phrases a facet, each up to 40 characters; at most 20 credits, each a collaborator named once, a role up to 200 characters. All or nothing: a refused credit is named by its place from 1.","security":[{"bearer":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"medium":{"type":"array","maxItems":8,"items":{"type":"string","maxLength":40}},"tools":{"type":"array","maxItems":8,"items":{"type":"string","maxLength":40}},"credits":{"type":"array","maxItems":20,"items":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"role":{"type":"string","maxLength":200}}}}}}}}},"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"saved; body is the medium, tools and credits"},"400":{"description":"a facet or a credit refused"},"403":{"description":"no write right"}}}},"/api/v0/repos/{org}/{repo}/showcase/credits/me":{"delete":{"summary":"Take the caller's own name off the credits, whoever put it there","security":[{"bearer":[]}],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}}},"/api/v0/think/personas":{"get":{"summary":"The Think personas on this instance, in the picker's order: id, name and focus (and Japanese ones where a script has them), group, level, the language its lines are in, how many questions it asks in order (the opening line and its agenda; 0 for a persona that only listens), and whether it uses project facts","responses":{"200":{"description":"ok"}}}},"/api/v0/repos/{org}/{repo}/think/{persona}/agenda":{"get":{"summary":"One persona's questions for this project, in order: the opening line, then its agenda, each with a short label, with the project's facts filled in, and the persona's closing line. For an interview fetched before the first take. 404 for a persona that does not exist, 409 for one with no agenda; read access to the project required","parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}},{"name":"persona","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"the persona and its questions"},"404":{"description":"no such project, or no such persona"},"409":{"description":"the persona has no agenda"}}}},"/api/v0/repos/{org}/{repo}/journal-settings":{"get":{"summary":"The journal's choice of host events: host is all, some (the default: no refreshes, pushes or saves) or none, and quiet lists the kinds some leaves out. Readers of the project","parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}},"put":{"summary":"Set the journal's choice of host events (space admins). Posts always show; the system log keeps every event either way","security":[{"bearer":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["host"],"properties":{"host":{"type":"string","enum":["all","some","none"]}}}}}},"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"saved"},"400":{"description":"not all, some or none"},"403":{"description":"not a space admin"}}}},"/api/v0/repos/{org}/{repo}/log":{"get":{"summary":"The system log: every event the host recorded for this project, newest first, with who and the detail. Members of the space only (404 to anyone else). kind narrows to one kind; before pages by event id; limit up to 500","security":[{"bearer":[]}],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}},{"name":"kind","in":"query","required":false,"schema":{"type":"string"}},{"name":"before","in":"query","required":false,"schema":{"type":"integer"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"description":"ok"}}}},"/api/v0/search":{"get":{"summary":"Search spaces, projects and issues by name, description, showcase, tags, medium, tools and terms; every word must match; only what the caller may read; file contents are not searched","parameters":[{"name":"q","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}}},"/api/v0/user/export":{"get":{"summary":"Your latest take-your-work zip: state none, building, ready, failed or expired; its size, how many repositories, and the zip address when ready","security":[{"bearer":[]}],"responses":{"200":{"description":"ok"}}},"post":{"summary":"Start a zip of every repository in every space you administer, each in its engine's own form, with manifest.json; built in the background, kept 24 hours. A new one replaces the last.","security":[{"bearer":[]}],"responses":{"202":{"description":"building; poll GET"},"403":{"description":"a repository-scoped token"},"404":{"description":"nothing to take"},"503":{"description":"two are being made already"}}}},"/api/v0/user/export/zip":{"get":{"summary":"The ready zip","security":[{"bearer":[]}],"responses":{"200":{"description":"ok"}}}},"/api/v0/user/tokens":{"get":{"summary":"This account's live tokens: id, name, created_at, last_used_at, hint (the last characters), scopes, account_wide, and current for the one making this request","security":[{"bearer":[]}],"responses":{"200":{"description":"ok"}}}},"/api/v0/user/tokens/{id}":{"delete":{"summary":"Revoke one of this account's tokens","security":[{"bearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"revoked"},"403":{"description":"a repository-scoped token"},"404":{"description":"not a live token of this account"}}}},"/api/v0/user/sessions":{"get":{"summary":"This account's live web sign-ins: id, device, created_at, last_seen_at","security":[{"bearer":[]}],"responses":{"200":{"description":"ok"}}}},"/api/v0/user/sessions/{id}":{"delete":{"summary":"End one web sign-in","security":[{"bearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"ended"},"403":{"description":"a repository-scoped token"},"404":{"description":"not a live web sign-in of this account"}}}},"/api/v0/user/signout-everywhere":{"post":{"summary":"Sign out everywhere: every web sign-in and every token but this one; keep_this false ends this one too. Answers how many of each were ended.","security":[{"bearer":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"keep_this":{"type":"boolean"}}}}}},"responses":{"200":{"description":"ok"}}}},"/api/v0/repos/{org}/{repo}/license":{"get":{"summary":"The repository's stated terms: key null and source none when nothing is stated","parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}},"put":{"summary":"Declare the terms by catalog key, or clear them with null (write right). Records only; commits nothing.","security":[{"bearer":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"key":{"type":"string","nullable":true}}}}}},"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}},"post":{"summary":"Declare the terms AND commit a LICENSE file naming them, with the plain sentence and the address of the full text (write right)","security":[{"bearer":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["key"],"properties":{"key":{"type":"string"}}}}}},"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"committed"},"400":{"description":"not in the catalog"},"501":{"description":"engine takes no files"}}}},"/api/v0/repos/{org}/{repo}/links":{"get":{"summary":"Saves of this repository as two numbers, saves and people, counting public ones and the caller's own; whether the caller saved it; and (0.95.0) lists, how many lists hold it as the caller may see them, with in_lists naming each by owner, slug and title","parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}}},"/api/v0/repos/{org}/{repo}/license/detect":{"post":{"summary":"Read the repository's own files for its terms now (writer): LICENSE and its spellings, COPYING, UNLICENSE, LICENSES/ and an asset directory's license.txt at the default branch tip, matched against the catalog by SPDX identifier, REUSE file name or the licence's own title. The answer is GET .../license: source detected or uncertain with path and rev, or the declaration when there is one. The same reading runs after every push","security":[{"bearer":[]}],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"the terms as now known"},"403":{"description":"no write right"}}}},"/api/v0/repos/{org}/{repo}/ai-policy":{"get":{"summary":"The project's AI policy (0.99.0): ok, attribution, ask or no - what the owner permits automated collection to do - and its source (repo, org or instance). Anyone who may see the project. The same word rides in X-AI-Policy on this and every answer about the project, in robots.txt and at /.well-known/ai-policy","parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}},"put":{"summary":"State the project's AI policy (writer): one of the four words, or null to inherit the space's or the instance's","security":[{"bearer":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"ai_policy":{"type":"string","enum":["ok","attribution","ask","no"],"nullable":true}}}}}},"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"the policy as it now stands"},"400":{"description":"not one of the words"},"403":{"description":"no write right"}}}},"/api/v0/orgs/{org}/ai-policy":{"put":{"summary":"The space's default AI policy (space admin): its projects carry it unless they say their own; null clears it","security":[{"bearer":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"ai_policy":{"type":"string","enum":["ok","attribution","ask","no"],"nullable":true}}}}}},"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"default_ai_policy"},"400":{"description":"not one of the words"},"403":{"description":"not a space admin"}}}},"/api/v0/repos/{org}/{repo}/decoy":{"put":{"summary":"Set the project's decoy mode (GUARDIAN.md s3, 0.106.0; write right): off - a client the guardian has denied gets 429 - or suspected - it gets the project's decoy repository, a real repository whose README says what it is and how to ask - or null to inherit the instance's word. Answers the word in effect and its source (repo, instance). decoy_mode also rides on GET .../repos/{org}/{repo}","security":[{"bearer":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"decoy_mode":{"type":"string","enum":["off","suspected"],"nullable":true}}}}}},"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"decoy_mode, source"},"400":{"description":"not one of the words"},"403":{"description":"no write right"},"404":{"description":"not a project you can read"}}}},"/api/v0/repos/{org}/{repo}/access-requests":{"get":{"summary":"Requests to read this project (0.98.0): a space admin sees the queue (?state=pending, the default; granted, declined, or all), anyone else their own. Each: id, user, message, state, created_at, decided_by, decided_at","security":[{"bearer":[]}],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}},{"name":"state","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}},"post":{"summary":"Ask to read this project, with a message of up to 1000 characters. You must see the project (404 otherwise) and not read it (409); one request waits at a time (409), a decline holds for 7 days (409), 10 waiting across projects is the most (429). A space admin grants or declines it; a grant is read on this one project","security":[{"bearer":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}}}}}},"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"the request"},"400":{"description":"the message is too long"},"404":{"description":"not a project you can see"},"409":{"description":"you read it, one waits, or a recent decline"},"429":{"description":"too many waiting"}}}},"/api/v0/repos/{org}/{repo}/access-requests/{id}":{"post":{"summary":"Decide a request (space admin): {grant: true} writes read for the asker on this project, false declines; 409 when it is not pending","security":[{"bearer":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["grant"],"properties":{"grant":{"type":"boolean"}}}}}},"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"the request, decided"},"403":{"description":"not a space admin"},"404":{"description":"no such request"},"409":{"description":"not pending"}}}},"/api/v0/repos/{org}/{repo}/allowlist":{"get":{"summary":"This project's allowlist (space admin): addresses (cidr) and user agents (ua) the guardian never judges for this project - the owner's own infrastructure. Each: id, kind, value, created_by, created_at","security":[{"bearer":[]}],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}},"post":{"summary":"Add an entry to this project's allowlist (space admin): {kind: cidr|ua, value}. A cidr is an address or block, v4 or v6; a ua is 2 to 100 characters matched inside the user agent","security":[{"bearer":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["kind","value"],"properties":{"kind":{"type":"string","enum":["cidr","ua"]},"value":{"type":"string"}}}}}},"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"the entry"},"400":{"description":"not a cidr or a ua"},"403":{"description":"not a space admin"}}}},"/api/v0/repos/{org}/{repo}/allowlist/{id}":{"delete":{"summary":"Remove an entry from this project's allowlist (space admin)","security":[{"bearer":[]}],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"204":{"description":"gone"},"403":{"description":"not a space admin"},"404":{"description":"no such entry"}}}},"/api/v0/admin/guardian":{"get":{"summary":"The guardian slot (instance admin): whether it is on and which header names the client, the challenge and deny thresholds, the decision cache in minutes, how many clients are remembered, the newest 100 decisions (at, client, verdict, reason, score, path) and the instance allowlist","security":[{"bearer":[]}],"responses":{"200":{"description":"the slot"},"403":{"description":"not an instance admin"}}}},"/api/v0/admin/guardian/allowlist":{"post":{"summary":"Add an entry to the instance allowlist (instance admin): {kind: cidr|ua, value}","security":[{"bearer":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["kind","value"],"properties":{"kind":{"type":"string","enum":["cidr","ua"]},"value":{"type":"string"}}}}}},"responses":{"201":{"description":"the entry"},"400":{"description":"not a cidr or a ua"},"403":{"description":"not an instance admin"}}}},"/api/v0/admin/guardian/allowlist/{id}":{"delete":{"summary":"Remove an entry from the instance allowlist (instance admin)","security":[{"bearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"204":{"description":"gone"},"403":{"description":"not an instance admin"},"404":{"description":"no such entry"}}}},"/api/v0/admin/extensions":{"get":{"summary":"The installed extensions (instance admin): each one's name, title, summary, enabled, default, locked (turned off for good by the deployer: -Dspice.extensions.off), provides (roles, e.g. home), reserves (names no space may take), group (a heading such as viewers, or null) and viewers (the kinds of file or name suffixes it shows on a file page, 0.131.0); and home, the extension serving / to a visitor, or null. An extension that is off is absent: its routes 404, its parts of pages and its API paths gone, its data untouched","security":[{"bearer":[]}],"responses":{"200":{"description":"the extensions"},"403":{"description":"not an instance admin"}}}},"/api/v0/admin/extensions/{name}":{"put":{"summary":"Turn an extension on or off (instance admin): {enabled: true|false}. Kept across restarts. Answers as GET","security":[{"bearer":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["enabled"],"properties":{"enabled":{"type":"boolean"}}}}}},"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"the extensions"},"400":{"description":"enabled not a boolean"},"403":{"description":"not an instance admin"},"404":{"description":"not installed"},"409":{"description":"turned off for good by the deployer"}}}},"/api/v0/admin/home":{"put":{"summary":"Choose the home page (instance admin): {extension: name} - an enabled extension that provides home. Answers as GET","security":[{"bearer":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["extension"],"properties":{"extension":{"type":"string"}}}}}},"responses":{"200":{"description":"the extensions"},"400":{"description":"no name"},"403":{"description":"not an instance admin"},"409":{"description":"not a home extension, or not enabled"}}}},"/api/v0/repos/{org}/{repo}/fork":{"post":{"summary":"Fork this project (0.96.0): a full copy into org, under name (the project's by default). You must be able to read the source (404 otherwise) and create in the destination (403); the copy is never more visible than the source; lore has no fork (409); quotas are the destination's (409)","security":[{"bearer":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["org"],"properties":{"org":{"type":"string"},"name":{"type":"string"}}}}}},"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"the copy, with fork_of"},"403":{"description":"no writer role in the destination"},"404":{"description":"source or destination not found"},"409":{"description":"name taken, no fork for this engine, or a quota"}}}},"/api/v0/repos/{org}/{repo}/forks":{"get":{"summary":"The forks of this project you may read: org and name each","parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}}},"/api/v0/repos/{org}/{repo}/save":{"post":{"summary":"Save this repository for the caller (read right). visibility private keeps it off the journal and out of everyone else's count.","security":[{"bearer":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"visibility":{"type":"string","enum":["public","private"]}}}}}},"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"saved; body carries the counts"}}},"delete":{"summary":"Remove the caller's save","security":[{"bearer":[]}],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}}},"/api/v0/repos/{org}/{repo}/mirrors":{"get":{"summary":"Mirrors of this repo, their state, which engines it COULD be mirrored in, and the space's allowance","parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}},"post":{"summary":"Add a mirror in another engine's format and queue its build (writer)","security":[{"bearer":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["engine"],"properties":{"engine":{"type":"string","enum":["git","hg","fossil"]}}}}}},"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"202":{"description":"queued; GET the collection for state"},"403":{"description":"writer role required, or the space is at its mirror limit (POLICY.md)"},"409":{"description":"no conversion from this repo's engine"}}}},"/api/v0/repos/{org}/{repo}/mirrors/{engine}":{"delete":{"summary":"Remove a mirror (writer); the bytes are parked, the repo untouched","security":[{"bearer":[]}],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}},{"name":"engine","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}}},"/api/v0/repos/{org}/{repo}/mirrors/{engine}/refresh":{"post":{"summary":"Rebuild one mirror from the primary's current tip (writer)","security":[{"bearer":[]}],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}},{"name":"engine","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"202":{"description":"queued"},"404":{"description":"no such mirror"}}}},"/api/v0/repos/{org}/{repo}/sub":{"get":{"summary":"A sub's source, state, the revision it last copied, and whether a fetch is running","parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"the sub"},"404":{"description":"no such repository, or not a sub"}}}},"/api/v0/repos/{org}/{repo}/sub/refresh":{"post":{"summary":"Fetch a sub from its source now (writer); poll GET until revision moves","security":[{"bearer":[]}],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"202":{"description":"queued"},"403":{"description":"writer role required"},"409":{"description":"no source set"}}}},"/api/v0/repos/{org}/{repo}/grants":{"get":{"summary":"Who was let into this one project without joining its space, and as what: read, or write (space admin)","security":[{"bearer":[]}],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"the grants"},"403":{"description":"not a space admin"},"404":{"description":"no such repository"}}}},"/api/v0/repos/{org}/{repo}/grants/{user}":{"put":{"summary":"Let an account into this project (space admin): read opens files, history, editions and the clone address; write adds push, the editor and the drop zone. Replaces what the account held.","security":[{"bearer":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["verb"],"properties":{"verb":{"type":"string","enum":["read","write"]}}}}}},"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}},{"name":"user","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"set; body carries the grants"},"400":{"description":"verb is not read or write"},"403":{"description":"not a space admin"},"404":{"description":"no such repository or account"}}},"delete":{"summary":"Take an account's grant on this project away (space admin). Nobody is signed out; the next request answers as the visibility says.","security":[{"bearer":[]}],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}},{"name":"user","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"removed; body carries the grants"},"403":{"description":"not a space admin"},"404":{"description":"no such repository, account or grant"}}}},"/api/v0/licenses":{"get":{"summary":"The catalog of terms a repository can state: key, name, category (code, creative, data, other), a plain sentence, the address of the canonical text and text_url, this instance's own copy of the full text (null for all-rights-reserved and custom)","responses":{"200":{"description":"ok"}}}},"/api/v0/licenses/{key}":{"get":{"summary":"One licence's full text as text/plain (0.105.0): the SPDX License List's rendering, placeholders as published. 404 for a key with no text","parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"the text"},"404":{"description":"no text for that key"}}}},"/api/v0/repos/{org}/{repo}/adopt":{"post":{"summary":"Give an EMPTY fossil repository the project code of what you are about to push (0.113.0): {project_code}, the 40 hex characters `fossil info` prints. A fossil push from a project with another code is refused by fossil itself; a project that lives nowhere else has no address to fetch from, so its code comes in this way. Write right; 409 once the repository has history; 501 for another engine","security":[{"bearer":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["project_code"],"properties":{"project_code":{"type":"string"}}}}}},"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"project_code, as adopted"},"400":{"description":"not 40 hex"},"403":{"description":"no write right"},"409":{"description":"the repository has history"},"501":{"description":"not a fossil repository"}}}},"/api/v0/orgs/{org}/webhooks":{"get":{"summary":"The space's webhooks (0.111.0, space admin): id, url, events (a comma list of kinds or *), active, failures, created_by, created_at. A hook gets every matching event of the space's projects as the event row, POSTed and signed (X-Spicemine-Signature: sha256= of HMAC-SHA256 over timestamp.body under the hook's secret; X-Spicemine-Event, -Delivery, -Timestamp); five failures in a row turn it off","security":[{"bearer":[]}],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}},"post":{"summary":"Add a webhook to the space: {url, events?}. The url must be http(s) and resolve to public addresses only - loopback, private and link-local are refused, no redirect is followed. Answers the hook WITH its secret, once","security":[{"bearer":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["url"],"properties":{"url":{"type":"string"},"events":{"type":"string"}}}}}},"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"the hook, with secret"},"400":{"description":"a refused address or events"},"403":{"description":"not a space admin"},"409":{"description":"twenty already"}}}},"/api/v0/orgs/{org}/webhooks/{id}":{"delete":{"summary":"Remove the space's webhook and its deliveries","security":[{"bearer":[]}],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"204":{"description":"gone"},"404":{"description":"no such hook"}}},"put":{"summary":"Turn the space's webhook on or off: {active}. On resets its failure count","security":[{"bearer":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["active"],"properties":{"active":{"type":"boolean"}}}}}},"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"the hook"},"404":{"description":"no such hook"}}}},"/api/v0/orgs/{org}/webhooks/{id}/deliveries":{"get":{"summary":"The hook's newest 50 deliveries: event_id, attempt, status, ok (true, false when given up, null while pending), error, at, next_at, payload","security":[{"bearer":[]}],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"ok"}}}},"/api/v0/orgs/{org}/webhooks/{id}/deliveries/{did}/redeliver":{"post":{"summary":"Send a delivery again: a fresh delivery of the same event, due now","security":[{"bearer":[]}],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"integer"}},{"name":"did","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"201":{"description":"the new delivery"},"404":{"description":"no such hook or delivery"}}}},"/api/v0/repos/{org}/{repo}/webhooks":{"get":{"summary":"The project's webhooks (0.111.0, space admin): as the space's, for this one project's events","security":[{"bearer":[]}],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"ok"}}},"post":{"summary":"Add a webhook to the project: {url, events?}; the secret once","security":[{"bearer":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["url"],"properties":{"url":{"type":"string"},"events":{"type":"string"}}}}}},"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"the hook, with secret"},"400":{"description":"a refused address or events"},"403":{"description":"not a space admin"},"404":{"description":"not a project you can read"},"409":{"description":"twenty already"}}}},"/api/v0/repos/{org}/{repo}/webhooks/{id}":{"delete":{"summary":"Remove the project's webhook","security":[{"bearer":[]}],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"204":{"description":"gone"},"404":{"description":"no such hook"}}},"put":{"summary":"Turn the project's webhook on or off: {active}","security":[{"bearer":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["active"],"properties":{"active":{"type":"boolean"}}}}}},"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"the hook"},"404":{"description":"no such hook"}}}},"/api/v0/repos/{org}/{repo}/webhooks/{id}/deliveries":{"get":{"summary":"The project hook's newest 50 deliveries","security":[{"bearer":[]}],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"ok"}}}},"/api/v0/repos/{org}/{repo}/webhooks/{id}/deliveries/{did}/redeliver":{"post":{"summary":"Send a project hook's delivery again","security":[{"bearer":[]}],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"integer"}},{"name":"did","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"201":{"description":"the new delivery"},"404":{"description":"no such hook or delivery"}}}},"/api/v0/machines":{"get":{"summary":"Your robots (0.110.0, GUARDIAN.md s1.5): the machine accounts you own - a CI runner, a package manager, a mirror - each with id, username and created_at. A robot takes the same path a person does: an account, a token, an access request, your grant; with a token it is never scored or decoyed","security":[{"bearer":[]}],"responses":{"200":{"description":"ok"}}},"post":{"summary":"Make a robot: {username}. Answers its first token, once. A robot has no password and cannot sign in; its tokens are its only door and you revoke them like your own. 403 for a robot calling, 409 for a name that is taken or a twenty-first robot","security":[{"bearer":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["username"],"properties":{"username":{"type":"string"}}}}}},"responses":{"201":{"description":"username, token, owner"},"400":{"description":"no username"},"403":{"description":"a robot calling"},"409":{"description":"name taken, or too many"}}}},"/api/v0/machines/{name}/token":{"post":{"summary":"Another token for your robot: {name} optional. Answers it once","security":[{"bearer":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"}}}}}},"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"username, token"},"404":{"description":"not your robot"}}}},"/api/v0/machines/{name}":{"delete":{"summary":"Remove your robot: its tokens, sessions, memberships and grants go with it","security":[{"bearer":[]}],"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"gone"},"404":{"description":"not your robot"},"409":{"description":"it still owns something"}}}},"/api/v0/pair":{"post":{"summary":"Ask to pair this device (0.109.0, MOBILE.md): {name} is what the device is called. Answers a six-letter code to show the person, the secret to poll with, how long they last (seconds) and where the person approves (signed in, under /settings). No credential: this is how a device without one gets one","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"}}}}}},"responses":{"201":{"description":"code, secret, expires_in, approve_at, poll"}}}},"/api/v0/pair/{secret}":{"get":{"summary":"Poll a pairing by its secret: {state: pending} until the person approves; then, once, {state: approved, token} - the row forgets the token as it is handed over. 404 for a secret that opens nothing, a pairing past its ten minutes, or one already claimed","parameters":[{"name":"secret","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"pending, or approved with the token"},"404":{"description":"no such pairing"}}}},"/api/v0/repos/{org}/{repo}/zines":{"get":{"summary":"The project's zines (0.108.0, VIEWERS.md s13): every PDF under zines/ or named *.zine.pdf, each with org, repo, project, path, title, blurb, layout, cover, cover_url, read_url (the page-by-page reader), pdf_url (the print form), reading and reading_url (0.121.0: the pages in reading order, one to a PDF page, when the creator's browser made one), and series, series_position, language, published and pages (0.124.0) when the creator said them. Whoever may read the project. To publish one: PUT the PDF under zines/ and, beside it, a JSON of the same name with title, blurb, layout and cover","security":[{"bearer":[]}],"parameters":[{"name":"org","in":"path","required":true,"schema":{"type":"string"}},{"name":"repo","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"the shelf"},"404":{"description":"not a project you can read"}}}},"/api/v0/zines":{"get":{"summary":"Every public, open project's zines on this instance (0.108.0), the same shape as a project's shelf; cached a minute","responses":{"200":{"description":"the shelf"}}}},"/api/v0/events":{"get":{"summary":"Durable event feed (create/delete/import/push/mirror/policy/grant/upload/license/save), oldest first","security":[{"bearer":[]}],"parameters":[{"name":"since","in":"query","required":false,"schema":{"type":"integer"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"description":"ok"}}}},"/api/v0/events/stream":{"get":{"summary":"Server-sent events: live repo events as they happen (event: repo)","security":[{"bearer":[]}],"responses":{"200":{"description":"ok"}}}},"/api/v0/lore/token":{"post":{"summary":"Mint an RS256 JWT for the lore gRPC endpoint; pass to lore CLI as --identity-token and --access-token","security":[{"bearer":[]}],"responses":{"201":{"description":"token minted"}}}},"/api/v0/lore/jwks.json":{"get":{"summary":"JWKS for verifying spicemine-minted lore JWTs","responses":{"200":{"description":"ok"}}}},"/api/v0/openapi.json":{"get":{"summary":"This document","responses":{"200":{"description":"ok"}}}}}}