Skip to the documentation
VersoAPI
API

One audience of one site, over HTTP.

Read the pages of a site, open a proposal against them, watch what happened, and search — with a credential that is worth exactly one audience of one site, and which discloses nothing about the audiences it is not worth.

Examples use a site whose slug is harbour — two audiences, one everybody can read and one only its members can. Responses are captured from the running software; long ones are trimmed, and a trim is always marked ….

00

What this API is

Two paragraphs, then the requests.

This is the API of the engine underneath Verso — the same one the product’s own screens call, and the one संचिका Sanchika, a separate product for government record-keeping, is built against. There is no second, reduced surface for outsiders: an editor pressing Send for review and a script posting to /api/v1/pages arrive at the same place and leave the same record behind.

Everything is addressed as one audience of one site. A site is a set of pages; an audience is who may see them, and each audience answers with its own pages, its own proposals and its own history. A credential is issued for a single audience, which is what makes it safe to hand to a piece of software — and a request that names an audience the credential is not for is not refused, it simply finds nothing there.

01

Authentication

A bearer token, issued for one audience and one audience only. Naming another in the query string grants nothing.

Where a token comes from

A site administrator issues one in the product, under Settings → API tokens (/s/<site>/settings/api). The form asks what the token is for, which audience — one, chosen from that site’s audiences, and not changeable afterwards — what it may do, and when it should expire. The secret is shown once, on that screen, and is never recoverable; only its hash is stored, so a database dump yields no working credentials. Reissue rather than recover.

Send it as an ordinary bearer credential. The token string begins vs_, so it is recognisable in a log or a support ticket.

Request
export VERSO=https://getverso.dev
export TOKEN=vs_…

curl -s "$VERSO/api/v1/pages?site=harbour" \
  -H "Authorization: Bearer $TOKEN"

The part worth reading twice

A token is bound to its audience at the moment it is issued. The grant is keyed on that audience inside the resolver, so naming a different one in the query string does not widen it — and, because a credential must never confirm what it cannot reach, the answer is 404, not 403. Both calls below carry the same valid token; the second one names the members-only audience.

Request
curl -s -o /dev/null -w '%{http_code}\n' \
  "$VERSO/api/v1/pages?site=harbour" -H "Authorization: Bearer $TOKEN"
curl -s -o /dev/null -w '%{http_code}\n' \
  "$VERSO/api/v1/pages?site=harbour&surface=internal" -H "Authorization: Bearer $TOKEN"
what came back
200
404

The blast radius of a leaked token is therefore one audience of one site, and a holder of it cannot learn that another audience exists.

What a token may do

Capabilities, not a role. Roles belong to people, who acquire responsibilities over time; a token is issued for a job and should not gain one because somebody was promoted.

read
List and fetch pages, proposals and events on this audience.
search
Query this audience’s own index.
ai
Ask questions of this audience, where that is switched on.
propose
Open a proposal. Not issuable from the product today — see below.
merge
Publish a proposal. Not issuable from the product today — see below.

Writing needs a credential the token screen will not issue

The token form offers read, search and ai and nothing else. The resolver would honour propose and merge on a token perfectly well — the write routes in §03 and §04 are real and are exercised by the test suite — but a bearer string with no second factor and no lockout should not be one form submission away from changing what a customer has published. So the writing half of this API is reachable today by a signed-in person’s own session, and a writing token is a decision that will arrive with its own review rather than as a checkbox that was already there.

No token at all, and a token that is wrong

Neither is an error. A request with no credential is an anonymous visitor: it gets whatever the public audience of that site publishes, and finds every other audience absent. A request carrying a string that is not a live token is the same anonymous visitor — the credential resolves to nobody rather than to a refusal, so nothing is learned by guessing. Both are the honest answer, and both are why 404 is the interesting status code here and 401 never appears.

02

The first request

Address the site and the audience explicitly; read the pages back.

Two query parameters address everything: site is the site’s slug and surface is the audience’s key. Both are optional, and a machine caller should nonetheless always send them — omitted, the answer is decided by the request’s host and cookies, which is right for a browser and wrong for a script. Naming them grants nothing: they decide which question is asked, never who is allowed to ask it.

Request
curl -s "$VERSO/api/v1/pages?site=harbour" \
  -H "Authorization: Bearer $TOKEN"
200
{
  "pages": [
    {
      "id": "pg_home",
      "slug": "/",
      "title": "Harbour",
      "description": "How Harbour works, for the people who use it.",
      "section": null,
      "tags": [],
      "path": "index.mdx",
      "frontmatter": {
        "title": "Harbour",
        "id": "pg_home",
        "description": "How Harbour works, for the people who use it."
      }
    },
    {
      "id": "pg_refunds",
      "slug": "/guides/refunds",
      "title": "Refunds",
      "description": "When money goes back, and how long it takes.",
      "section": "/guides",
      "tags": ["payments", "policy"],
      "path": "guides/refunds.mdx",
      "frontmatter": {
        "title": "Refunds",
        "id": "pg_refunds",
        "description": "When money goes back, and how long it takes.",
        "tags": ["payments", "policy"]
      }
    }
    …
  ]
}

Three fields are worth knowing before §03. id is permanent: it survives a page being renamed or moved to another section, so it is the thing to store. slug is the address a reader visits, and it changes when the page moves. frontmatter is the page’s own settings block, returned verbatim and never rewritten on the way out.

Answers under /api/v1 carry Cache-Control: private, no-store. What one credential may read is not what the next one may, and a shared cache that forgets that is a disclosure.

03

Pages

The pages of an audience, read at the address a reader would use — and written as proposals, never in place.

One page, record and text

A page is addressed by its slug, slashes included: /api/v1/pages/guides/refunds. The answer carries the indexed record and the exact text of the page as this audience has it, so a client that fetches, edits and sends it back is doing precisely what the editor does.

Request
curl -s "$VERSO/api/v1/pages/guides/refunds?site=harbour" \
  -H "Authorization: Bearer $TOKEN"
200
{
  "page": {
    "id": "pg_refunds",
    "slug": "/guides/refunds",
    "title": "Refunds",
    "description": "When money goes back, and how long it takes.",
    "section": "/guides",
    "tags": ["payments", "policy"],
    "path": "guides/refunds.mdx",
    "frontmatter": { … }
  },
  "source": "---\ntitle: Refunds\nid: pg_refunds\ndescription: When money goes back, and how long it takes.\ntags: [payments, policy]\n---\n\n# Refunds\n\nA refund is issued to the original method of payment within five working days.\n"
}

source is the whole file, settings block and all. Send it back through PUT with the parts you changed and nothing else touched — a client that reconstructs the file from the record instead will silently drop any setting it does not know about.

Adding a page adds a proposal

POST /api/v1/pages takes a title, optionally the section it belongs in and the text to start with. What it creates is a proposal: nothing is on the live site, the answer names the proposal now carrying the new page, and publishing is a separate act in §04 with a separate capability. The API gets no shortcut past review, because a shortcut here would be a second, quieter door onto a customer’s published documentation.

Request
curl -s -X POST "$VERSO/api/v1/pages?site=harbour" \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"title":"Returns","section":"guides","content":"# Returns\n\nSend it back within thirty days.\n"}'
201
{
  "proposalId": "prop_EzrcwQcJeLhgqMth",
  "pageId": "pg_KVqzTVeF2qUrz5Gz",
  "path": "guides/returns.mdx",
  "slug": "/guides/returns"
}

The address is derived from the title rather than asked for, and a handful of root addresses are refused because the product already occupies them — those come back 400 naming the address, and a page that already lives at the derived slug comes back 409.

PUT /api/v1/pages/{slug}
Replace the text. Body is { "source": "…" }; the answer names the proposal carrying the edit.
DELETE /api/v1/pages/{slug}
Remove the page — also a proposal, so a removal is reviewed like any other change.
04

Proposals

The queue of changes waiting on one audience, what each one would do, and the act that makes it live.

The list is filtered to the audience the credential resolves to, not to the site: a credential for the public audience must not see another audience’s drafts in flight, for exactly the reason it cannot read their pages. Absent, not refused. Filter with state=open|merged|conflicted|closed.

Request
curl -s "$VERSO/api/v1/proposals?site=harbour" \
  -H "Authorization: Bearer $TOKEN"
200
{
  "proposals": [
    {
      "id": "prop_ensgj6dsneMepU9x",
      "number": 2,
      "title": "Correct the refund window",
      "body": "Finance says five working days, not seven.",
      "state": "open",
      "origin": "api",
      "author": "API token “docs”",
      "baseRef": "main",
      "baseSha": "3f7f587ddb3dffcc055f44747b88c4ce415f4a9e",
      "headSha": "647cf72501ba594d5e68e794ed6b948a0b8dfa96",
      "mergeCommitSha": null,
      "createdAt": "2026-08-10T18:53:38.492Z",
      "updatedAt": "2026-08-10T18:53:38.550Z",
      "mergedAt": null
    }
    …
  ]
}

author says which credential wrote it — a person’s name from a session, the token’s own name from a token. That is what an operator revoking one of nine tokens needs to see in the history. origin says which door it came through: app, api, git_push, import or ai.

Opening one directly

POST /api/v1/proposals takes a title and a files map — each key a path inside the site, each value the whole file, or null to remove that one. It is the general form of everything in §03.

Request
curl -s -X POST "$VERSO/api/v1/proposals?site=harbour" \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "title": "Correct the refund window",
    "body": "Finance says five working days, not seven.",
    "files": { "guides/refunds.mdx": "---\ntitle: Refunds\nid: pg_refunds\n---\n\n# Refunds\n\nFive working days.\n" }
  }'
201
{ "proposalId": "prop_ensgj6dsneMepU9x" }

What it would actually do

Fetching one proposal returns it together with its ledger: every change it contains, grouped and ranked by consequence, each with the reason in plain language. This is the same reading a reviewer gets on screen, and it is the part of this API that has no equivalent elsewhere — a machine can ask what a change would do before anybody approves it.

Request
curl -s "$VERSO/api/v1/proposals/prop_EzrcwQcJeLhgqMth" \
  -H "Authorization: Bearer $TOKEN"
200
{
  "proposal": { "id": "prop_EzrcwQcJeLhgqMth", "number": 1, "title": "Add /guides/returns", "state": "open", … },
  "ledger": {
    "total": 5,
    "items": [
      {
        "id": "set:guides/returns.mdx:title",
        "group": "page_settings",
        "consequences": [],
        "reason": "Changed the page title: unset → Returns",
        "page": "/guides/returns",
        "path": "guides/returns.mdx"
      },
      {
        "id": "blk:guides/returns.mdx:1",
        "group": "added_removed",
        "consequences": ["added"],
        "reason": "New text was added: \"Send it back within thirty days.\"",
        "page": "/guides/returns",
        "path": "guides/returns.mdx"
      }
      …
    ]
  }
}

A proposal is fetched by id alone. The audience it belongs to is the one it names itself — the caller’s site and surface parameters get no vote — so a proposal on an audience you cannot read is a plain 404, indistinguishable from an id that never existed.

Publishing

Publishing needs merge and an accountable person: the session’s user, or the person who issued the token. A credential that names nobody accountable is refused with 403 and told why. The act records an approval covering the whole ledger in the publisher’s name and then publishes — the same record every publish made on screen leaves, so the history cannot tell which door it came through, and nobody should be able to.

Request
curl -s -X POST "$VERSO/api/v1/proposals/prop_EzrcwQcJeLhgqMth/publish" \
  -H "Authorization: Bearer $TOKEN"
200
{ "ok": true, "sha": "fb4d140d1d6f763afbe8f403315a52088a450d8c" }

A proposal already published, or one that no longer applies cleanly to the live pages, answers 409; the conflicted case carries the list of pages that disagree.

05

Events

What happened on this audience, newest first, paged backwards by id.

The feed is filtered to the audience by equality, never by “not somebody else’s”. Acts that belong to no audience — a member added, a site-wide setting changed — are absent from it by construction, so a credential learns exactly what its own audience could have watched happen and nothing more.

Request
curl -s "$VERSO/api/v1/events?site=harbour&limit=3" \
  -H "Authorization: Bearer $TOKEN"
200
{
  "events": [
    {
      "id": "ev_01KZPGBXHP46PGXKPT44JQX0NC",
      "type": "proposal.merged",
      "subject": { "type": "proposal", "id": "prop_EzrcwQcJeLhgqMth" },
      "payload": {
        "number": 1,
        "commit_sha": "fb4d140d1d6f763afbe8f403315a52088a450d8c",
        "pages_touched": ["guides/returns.mdx"]
      },
      "occurredAt": "2026-08-10T18:53:38.742Z"
    },
    {
      "id": "ev_01KZPGBXE104F5PVGBZ7R7VJ69",
      "type": "proposal.approved",
      "subject": { "type": "proposal", "id": "prop_EzrcwQcJeLhgqMth" },
      "payload": {
        "number": 1,
        "approver": "Ida Okonjo",
        "changes_total": 5,
        "changes_opened": 5,
        "approved_unopened": 0
      },
      "occurredAt": "2026-08-10T18:53:38.625Z"
    }
    …
  ],
  "nextBefore": "ev_01KZPGBX9WPGZ2ETCZ78S9PERG"
}
limit
1–200, default 50.
before
An event id. Answers events strictly older than it — pass nextBefore straight back to walk the history. It is null on the last page.
type
One event type, e.g. page.published.

Ids are ULIDs, so sorting them lexicographically is sorting them by time. That is why the cursor is an id rather than a timestamp: two things that happened in the same millisecond still have an order, and paging cannot skip or repeat one.

06

Search

Retrieval over one audience's own index — keyword and meaning, fused.

Search needs the search capability. It runs over this audience’s own physical partition of the index rather than over a shared table with a filter on it: a credential cannot reach another audience’s text because those rows are not in the tables its queries name. mode=keyword skips the meaning half, which is the right choice for very short queries; limit is 1–50 and defaults to 10.

Request
curl -s "$VERSO/api/v1/search?site=harbour&q=refund" \
  -H "Authorization: Bearer $TOKEN"
200
{
  "hits": [
    {
      "chunkId": "chk_40178cce0bbd05710210",
      "pageId": "pg_refunds",
      "slug": "/guides/refunds",
      "title": "Refunds",
      "breadcrumb": "Harbour › Refunds",
      "anchorSlug": null,
      "url": "/guides/refunds",
      "snippet": "# Refunds A refund is issued to the original method of payment within five working days.",
      "blockKeys": ["blk_zwqbUYUnHEGyr92p", "blk_fLvut4uSHX98UqPU"],
      "score": 0.03278688524590164,
      "rank": 1,
      "sources": [
        { "source": "fts", "rank": 1 },
        { "source": "vec", "rank": 1 }
      ]
    }
  ],
  "mode": "hybrid",
  "notes": [],
  "tookMs": 2,
  "indexed": true
}
mode
What actually ran, which is not always what was asked for. Ask for hybrid on a corpus with no vectors and it answers keyword.
notes
Why, when it is not what was asked for. Empty when it is.
indexed
false when this audience has never been indexed — an empty result, not an error, and the note says so.
blockKeys
The exact blocks the snippet came from. A citation that survives the page being edited around it.
anchorSlug
The heading to link to inside the page, or null above the first one.

Search is rate-limited. Over the limit answers 429 with Retry-After in seconds; honour it rather than retrying immediately.

07

Refusals, and what they disclose

One error body everywhere, and one rule about which status code a refusal is allowed to be.

Every refusal has the same two fields: a sentence a person can act on, and a code a program can switch on. There is no third shape and no route under /api/v1 that invents its own.

Request
curl -s "$VERSO/api/v1/pages/guides/nope?site=harbour" \
  -H "Authorization: Bearer $TOKEN"
404
{ "error": "Not found", "code": "not_found" }

404, not 403 — and the difference between them

The rule is worth stating precisely, because it is not “everything is a 404”. Not being able to see something is 404. Being able to see it and not being allowed to change it is 403. A credential that can already read an audience learns nothing new from being told it may not write there, so the honest refusal is the specific one; a credential that cannot read the audience must not be able to distinguish it from an audience that does not exist, so the honest refusal is silence in the shape of a 404.

Request
# a read-only credential, naming a write it can see
curl -s -X POST "$VERSO/api/v1/pages?site=harbour" \
  -H "Authorization: Bearer $READ_ONLY" \
  -H 'Content-Type: application/json' -d '{"title":"Refused"}'
403
{ "error": "Forbidden", "code": "forbidden" }

The same rule covers a site that does not exist, an audience that is closed or past its expiry, and a proposal on an audience you cannot read: all four are 404, all four with the same body, and none of them tells you which of the four it was.

The codes

not_found · 404
No such site, audience, page or proposal — or nothing you may read. Deliberately the same answer.
forbidden · 403
You can see it and may not do this to it. Also the refusal when a publish names nobody accountable, and then the sentence says so.
bad_request · 400
The request is malformed or the product reserves the address, and the sentence names the problem: Send JSON with a non-empty "title".
conflict · 409
A page already lives at that address; a proposal is already published, or no longer applies cleanly to the live pages.
— · 429
Too many searches. Carries Retry-After in seconds.

There is no 401. A missing or unrecognised credential is not an error here — it is an anonymous caller, who gets the public audience and finds every other one absent. See §01.

08

The reference

Every operation, generated from the specification this deployment is actually serving.

Below is the whole of /api/v1, rendered on the server from the same specification document that /api/openapi.json serves to machines — one source, so a route added without a line in it fails a test rather than quietly appearing here. The reference describes; it does not fire requests. There is no try-it console, and a reference that quietly shipped one would be shipping a security decision nobody made.

Verso API · 1.0.0 · every operation, expanded. Machine-readable at /api/openapi.json.
GET/api/v1/sitesList the sites this credential opens

Every site with at least one audience this credential can read *because of the credential* — a token minted for it, or a role the person holds there. Public sites the credential adds nothing to are not listed. Each audience carries the capabilities this credential has on it.

Responses
  • 200 The sites, by slug.
    • sites Site[]
  • 401 No credential was sent.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
POST/api/v1/importsImport Markdown documents (as one proposal)

Requires propose. Each file is checked the way the upload importer checks it — safe path, size, readable by the parser — and .md becomes .mdx; frontmatter is added only where a file has none. Everything importable lands in one proposal; everything else is named in skipped with its reason. Images are not accepted yet.

Parameters
  • site query · string — Site slug. Explicit addressing for machine callers; omitted, the host decides.
  • surface query · string — Surface key (audience). A bearer token is minted for exactly one surface; naming any other grants nothing.
Request body
  • title string — The proposal’s title. Defaults to “Import N documents”.
  • body string
  • into string — A folder to put everything under, e.g. guides.
  • files object[] · required — At most 300. Paths are relative to the folder being imported, and kept as given.
Responses
  • 201 The proposal carrying the import, and the receipt.
    • proposalId string · required
    • imported object[] · required
    • skipped object[] · required
  • 400 No files, too many, or nothing that could be made into a page.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
GET/api/v1/pagesList the pages of a surface

Requires read. Answers every page on the surface the credential resolves to.

Parameters
  • site query · string — Site slug. Explicit addressing for machine callers; omitted, the host decides.
  • surface query · string — Surface key (audience). A bearer token is minted for exactly one surface; naming any other grants nothing.
Responses
  • 200 The pages, in navigation order.
    • pages Page[]
  • 404 No such surface, or no read on it.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
POST/api/v1/pagesCreate a page (as a proposal)

Requires propose. Opens a proposal containing the new page and answers its id — nothing is live until the proposal publishes.

Parameters
  • site query · string — Site slug. Explicit addressing for machine callers; omitted, the host decides.
  • surface query · string — Surface key (audience). A bearer token is minted for exactly one surface; naming any other grants nothing.
Request body
  • title string · required
  • section string — Section slug, e.g. guides. Top level when absent.
  • content string — MDX body. Defaults to the title as a heading.
Responses
  • 201 The proposal now carrying the page.
    • proposalId string
    • pageId string
    • path string
    • slug string
  • 400 Missing title, or an address the product reserves.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 409 A page already lives at that address.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
GET/api/v1/pages/{slug}Read one page, record and source

Requires read. slug is the reader’s address, e.g. guides/refunds. Answers the indexed record and the exact MDX at the surface’s ref.

Parameters
  • slug path · required · string — Path-style slug; slashes allowed.
  • site query · string — Site slug. Explicit addressing for machine callers; omitted, the host decides.
  • surface query · string — Surface key (audience). A bearer token is minted for exactly one surface; naming any other grants nothing.
Responses
  • 200 The page.
    • page Page
    • source string — The complete MDX file, frontmatter included.
  • 404 No page there, or nothing you may read.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
PUT/api/v1/pages/{slug}Replace a page’s source (as a proposal)

Requires propose. Send back the edited source a GET returned.

Parameters
  • slug path · required · string
  • site query · string — Site slug. Explicit addressing for machine callers; omitted, the host decides.
  • surface query · string — Surface key (audience). A bearer token is minted for exactly one surface; naming any other grants nothing.
Request body
  • source string · required — The complete MDX file to store.
Responses
  • 200 The proposal carrying the edit.
    • proposalId string · required
    • path string
  • 404 No page there.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
DELETE/api/v1/pages/{slug}Remove a page (as a proposal)

Requires propose. The removal is a proposal like any other change.

Parameters
  • slug path · required · string
  • site query · string — Site slug. Explicit addressing for machine callers; omitted, the host decides.
  • surface query · string — Surface key (audience). A bearer token is minted for exactly one surface; naming any other grants nothing.
Responses
  • 200 The proposal carrying the removal.
    • proposalId string · required
    • path string
  • 404 No page there.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
GET/api/v1/proposalsList proposals on a surface

Requires read. Only this surface’s proposals — another audience’s drafts are absent, not refused. Filter with state=open|merged|conflicted|closed.

Parameters
  • site query · string — Site slug. Explicit addressing for machine callers; omitted, the host decides.
  • surface query · string — Surface key (audience). A bearer token is minted for exactly one surface; naming any other grants nothing.
  • state query · string
Responses
  • 200 The proposals, most recently updated first.
    • proposals Proposal[]
POST/api/v1/proposalsOpen a proposal

Requires propose. files maps repo-relative paths to full content, or null to remove a path.

Parameters
  • site query · string — Site slug. Explicit addressing for machine callers; omitted, the host decides.
  • surface query · string — Surface key (audience). A bearer token is minted for exactly one surface; naming any other grants nothing.
Request body
  • title string · required
  • body string
  • files object · required — Repo-relative path → full file content, or null to remove that path.
Responses
  • 201 Opened.
    • proposalId string · required
    • path string
  • 400 No title, no files, or a path that is not repo-relative.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
GET/api/v1/proposals/{id}Read one proposal and its ledger

Requires read on the surface the proposal itself names — the caller’s site and surface parameters get no vote.

Parameters
  • id path · required · string
Responses
  • 200 The proposal and a consequence-ranked summary of its changes.
    • proposal Proposal
    • ledger LedgerSummary
  • 404 No such proposal you may see.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
POST/api/v1/proposals/{id}/publishPublish a proposal

Requires merge, and an accountable person: the session’s user, or the user who minted the token. Records an approval covering the whole ledger in the publisher’s name, then squash-merges — the same record every other publish leaves.

Parameters
  • id path · required · string
Responses
  • 200 Live.
    • ok boolean
    • sha string
  • 403 No merge, or no accountable person behind the credential.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 409 Already published, or it conflicts with Live.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
GET/api/v1/searchSearch a surface

Requires search. Hybrid keyword + vector over this surface’s own physical index partition; mode=keyword skips vectors.

Parameters
  • q query · required · string
  • limit query · integer — 1–50, default 10.
  • mode query · string — 'hybrid' (default) or 'keyword'.
  • site query · string — Site slug. Explicit addressing for machine callers; omitted, the host decides.
  • surface query · string — Surface key (audience). A bearer token is minted for exactly one surface; naming any other grants nothing.
Responses
  • 200 Ranked results with block-level citations.
  • 429 Rate limited; honour Retry-After.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
GET/api/v1/eventsList a surface’s events

Requires read. Newest first; events with no surface (site-wide acts) are absent from this feed by construction. Page backwards with before=<id>.

Parameters
  • site query · string — Site slug. Explicit addressing for machine callers; omitted, the host decides.
  • surface query · string — Surface key (audience). A bearer token is minted for exactly one surface; naming any other grants nothing.
  • limit query · integer — 1–200, default 50.
  • before query · string — An event id; answers events strictly older.
  • type query · string — e.g. 'page.published'.
Responses
  • 200 The events.
    • events Event[]
    • nextBefore string — Cursor for the next page, or null.
GET/api/v1/org/membersList the organisation’s members

Requires org:members.

Parameters
  • X-Verso-Org header · string — Organisation slug or id. Omitted, the person’s first organisation (or, for a session, the one they are acting in).
Responses
  • 200 OK
    • members Member[]
  • 401 No session, and no personal access token.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 403 Your role — or the token’s scopes — do not allow this.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 404 No such organisation, or the subject is not in yours.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
POST/api/v1/org/membersInvite somebody

Requires org:members; inviting an owner requires org:delete. The same rules as the People screen. inviteUrl is returned once and never again.

Parameters
  • X-Verso-Org header · string — Organisation slug or id. Omitted, the person’s first organisation (or, for a session, the one they are acting in).
Request body
  • email string · required
  • role string — 'owner' | 'admin' | 'member'; default 'member'.
  • siteId string — Also give them a role on one site of this organisation.
  • siteRole string — 'owner' | 'approver' | 'editor' | 'reader'; default 'reader'.
  • title string
Responses
  • 201 Invited.
    • inviteId string
    • email string
    • expiresAt string
    • emailed boolean
    • inviteUrl string
  • 400 Malformed input; the message says which field.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 401 No session, and no personal access token.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 403 Your role — or the token’s scopes — do not allow this.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 404 No such organisation, or the subject is not in yours.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 409 Refused by a rule — the last owner, a SCIM-owned team, a duplicate. reason names it.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 429 Over the write rate limit; honour Retry-After.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
PATCH/api/v1/org/members/{userId}Change a member’s role or title

Requires org:members. Granting or removing ownership requires org:delete; the last owner cannot be demoted.

Parameters
  • userId path · required · string
  • X-Verso-Org header · string — Organisation slug or id. Omitted, the person’s first organisation (or, for a session, the one they are acting in).
Request body
  • role string — 'owner' | 'admin' | 'member'
  • title string
Responses
  • 200 OK
    • userId string
    • role string
    • title string
  • 400 Malformed input; the message says which field.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 401 No session, and no personal access token.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 403 Your role — or the token’s scopes — do not allow this.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 404 No such organisation, or the subject is not in yours.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 409 Refused by a rule — the last owner, a SCIM-owned team, a duplicate. reason names it.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 429 Over the write rate limit; honour Retry-After.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
DELETE/api/v1/org/members/{userId}Remove a member

Requires org:members. Removes every site role they held in this organisation, recorded in the audit log.

Parameters
  • userId path · required · string
  • X-Verso-Org header · string — Organisation slug or id. Omitted, the person’s first organisation (or, for a session, the one they are acting in).
Responses
  • 200 OK
    • removed string
    • siteRolesRemoved object[]
  • 400 Malformed input; the message says which field.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 401 No session, and no personal access token.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 403 Your role — or the token’s scopes — do not allow this.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 404 No such organisation, or the subject is not in yours.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 409 Refused by a rule — the last owner, a SCIM-owned team, a duplicate. reason names it.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 429 Over the write rate limit; honour Retry-After.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
GET/api/v1/org/invitesList invitations not yet accepted

Requires org:members. Tokens are never listed.

Parameters
  • X-Verso-Org header · string — Organisation slug or id. Omitted, the person’s first organisation (or, for a session, the one they are acting in).
Responses
  • 200 OK
    • invites object[]
  • 401 No session, and no personal access token.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 403 Your role — or the token’s scopes — do not allow this.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 404 No such organisation, or the subject is not in yours.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
DELETE/api/v1/org/invites/{inviteId}Revoke an invitation

Requires org:members.

Parameters
  • inviteId path · required · string
  • X-Verso-Org header · string — Organisation slug or id. Omitted, the person’s first organisation (or, for a session, the one they are acting in).
Responses
  • 200 OK
    • revoked string
    • changed boolean
  • 400 Malformed input; the message says which field.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 401 No session, and no personal access token.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 403 Your role — or the token’s scopes — do not allow this.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 404 No such organisation, or the subject is not in yours.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 409 Refused by a rule — the last owner, a SCIM-owned team, a duplicate. reason names it.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 429 Over the write rate limit; honour Retry-After.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
GET/api/v1/org/teamsList teams, with members and site grants

Requires org:members.

Parameters
  • X-Verso-Org header · string — Organisation slug or id. Omitted, the person’s first organisation (or, for a session, the one they are acting in).
Responses
  • 200 OK
    • teams Team[]
  • 401 No session, and no personal access token.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 403 Your role — or the token’s scopes — do not allow this.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 404 No such organisation, or the subject is not in yours.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
POST/api/v1/org/teamsCreate a team

Requires org:members. A name another team already has (as a slug) answers 409.

Parameters
  • X-Verso-Org header · string — Organisation slug or id. Omitted, the person’s first organisation (or, for a session, the one they are acting in).
Request body
  • name string · required — Up to 60 characters; the team’s slug is made from it.
  • description string
Responses
  • 201 Created.
    • team Team
  • 400 Malformed input; the message says which field.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 401 No session, and no personal access token.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 403 Your role — or the token’s scopes — do not allow this.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 404 No such organisation, or the subject is not in yours.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 409 Refused by a rule — the last owner, a SCIM-owned team, a duplicate. reason names it.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 429 Over the write rate limit; honour Retry-After.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
GET/api/v1/org/teams/{teamId}Read one team

Requires org:members.

Parameters
  • teamId path · required · string
  • X-Verso-Org header · string — Organisation slug or id. Omitted, the person’s first organisation (or, for a session, the one they are acting in).
Responses
  • 200 OK
    • team Team
  • 401 No session, and no personal access token.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 403 Your role — or the token’s scopes — do not allow this.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 404 No such organisation, or the subject is not in yours.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
PATCH/api/v1/org/teams/{teamId}Rename or describe a team

Requires org:members. A team your identity provider manages (managedBy: scim) answers 409.

Parameters
  • teamId path · required · string
  • X-Verso-Org header · string — Organisation slug or id. Omitted, the person’s first organisation (or, for a session, the one they are acting in).
Request body
  • name string
  • description string
Responses
  • 200 OK
    • team Team
  • 400 Malformed input; the message says which field.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 401 No session, and no personal access token.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 403 Your role — or the token’s scopes — do not allow this.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 404 No such organisation, or the subject is not in yours.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 409 Refused by a rule — the last owner, a SCIM-owned team, a duplicate. reason names it.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 429 Over the write rate limit; honour Retry-After.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
DELETE/api/v1/org/teams/{teamId}Delete a team

Requires org:members. Its site grants end with it.

Parameters
  • teamId path · required · string
  • X-Verso-Org header · string — Organisation slug or id. Omitted, the person’s first organisation (or, for a session, the one they are acting in).
Responses
  • 200 OK
    • deleted string
  • 400 Malformed input; the message says which field.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 401 No session, and no personal access token.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 403 Your role — or the token’s scopes — do not allow this.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 404 No such organisation, or the subject is not in yours.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 409 Refused by a rule — the last owner, a SCIM-owned team, a duplicate. reason names it.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 429 Over the write rate limit; honour Retry-After.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
POST/api/v1/org/teams/{teamId}/membersAdd a member to a team

Requires org:members. Only a member of the organisation can join. A team your identity provider manages answers 409; so does a team that can publish when no publishing seat is free.

Parameters
  • teamId path · required · string
  • X-Verso-Org header · string — Organisation slug or id. Omitted, the person’s first organisation (or, for a session, the one they are acting in).
Request body
  • userId string · required
Responses
  • 200 Already a member.
    object
  • 201 Added.
    object
  • 400 Malformed input; the message says which field.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 401 No session, and no personal access token.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 403 Your role — or the token’s scopes — do not allow this.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 404 No such organisation, or the subject is not in yours.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 409 Refused by a rule — the last owner, a SCIM-owned team, a duplicate. reason names it.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 429 Over the write rate limit; honour Retry-After.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
DELETE/api/v1/org/teams/{teamId}/membersRemove a member from a team

Requires org:members. A team your identity provider manages answers 409.

Parameters
  • teamId path · required · string
  • userId query · required · string
  • X-Verso-Org header · string — Organisation slug or id. Omitted, the person’s first organisation (or, for a session, the one they are acting in).
Responses
  • 200 OK
    object
  • 400 Malformed input; the message says which field.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 401 No session, and no personal access token.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 403 Your role — or the token’s scopes — do not allow this.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 404 No such organisation, or the subject is not in yours.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 409 Refused by a rule — the last owner, a SCIM-owned team, a duplicate. reason names it.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 429 Over the write rate limit; honour Retry-After.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
POST/api/v1/org/teams/{teamId}/grantsGrant a team a role on a site

Requires org:members, and the right to manage that site (org:sites, and owning it unless you manage people). The site must be in this organisation. A publishing role no free seat can cover for every member answers 409.

Parameters
  • teamId path · required · string
  • X-Verso-Org header · string — Organisation slug or id. Omitted, the person’s first organisation (or, for a session, the one they are acting in).
Request body
  • siteId string · required
  • role string · required — 'owner' | 'approver' | 'editor' | 'commenter' | 'reader'
Responses
  • 200 OK
    object
  • 400 Malformed input; the message says which field.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 401 No session, and no personal access token.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 403 Your role — or the token’s scopes — do not allow this.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 404 No such organisation, or the subject is not in yours.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 409 Refused by a rule — the last owner, a SCIM-owned team, a duplicate. reason names it.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 429 Over the write rate limit; honour Retry-After.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
DELETE/api/v1/org/teams/{teamId}/grantsRevoke a team’s role on a site

Requires org:members, and the right to manage that site.

Parameters
  • teamId path · required · string
  • siteId query · required · string
  • X-Verso-Org header · string — Organisation slug or id. Omitted, the person’s first organisation (or, for a session, the one they are acting in).
Responses
  • 200 OK
    object
  • 400 Malformed input; the message says which field.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 401 No session, and no personal access token.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 403 Your role — or the token’s scopes — do not allow this.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 404 No such organisation, or the subject is not in yours.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 409 Refused by a rule — the last owner, a SCIM-owned team, a duplicate. reason names it.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 429 Over the write rate limit; honour Retry-After.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
GET/api/v1/org/sitesList the organisation’s sites

Requires org:sites. Closed sites are included and marked. An owner or admin sees every site; a creator, the sites they run.

Parameters
  • X-Verso-Org header · string — Organisation slug or id. Omitted, the person’s first organisation (or, for a session, the one they are acting in).
Responses
  • 200 OK
    • sites OrgSite[]
  • 401 No session, and no personal access token.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 403 Your role — or the token’s scopes — do not allow this.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 404 No such organisation, or the subject is not in yours.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
POST/api/v1/org/sitesCreate a site

Requires org:sites. The same single operation as the welcome flow: repository, audiences and front page, whole or not at all.

Parameters
  • X-Verso-Org header · string — Organisation slug or id. Omitted, the person’s first organisation (or, for a session, the one they are acting in).
Request body
  • name string · required
  • slug string — Wanted address; the next free one is used if taken (slugChanged).
  • audiences string[] — Any of 'public', 'customers', 'company'. Default ['public'].
  • pack string — Starter content: 'blank' (default), 'product', 'handbook', 'api', 'support', 'mix'.
Responses
  • 201 Created.
    • site OrgSite
    • slugChanged boolean
  • 400 Malformed input; the message says which field.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 401 No session, and no personal access token.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 403 Your role — or the token’s scopes — do not allow this.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 404 No such organisation, or the subject is not in yours.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 409 Refused by a rule — the last owner, a SCIM-owned team, a duplicate. reason names it.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 429 Over the write rate limit; honour Retry-After.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
PATCH/api/v1/org/sites/{siteId}Close, reopen, or file a site

Requires org:sites, and the right to manage that site (a creator manages the sites they own). closed stops the site resolving for readers (nothing in it moves); collectionId files it, null unfiles it. Both land or neither does. Id or slug.

Parameters
  • siteId path · required · string
  • X-Verso-Org header · string — Organisation slug or id. Omitted, the person’s first organisation (or, for a session, the one they are acting in).
Request body
  • closed boolean
  • collectionId string
Responses
  • 200 OK
    • site OrgSite
  • 400 Malformed input; the message says which field.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 401 No session, and no personal access token.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 403 Your role — or the token’s scopes — do not allow this.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 404 No such organisation, or the subject is not in yours.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 409 Refused by a rule — the last owner, a SCIM-owned team, a duplicate. reason names it.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 429 Over the write rate limit; honour Retry-After.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
GET/api/v1/org/collectionsList collections

Requires org:sites. Grouping only — a collection grants nothing and hides nothing.

Parameters
  • X-Verso-Org header · string — Organisation slug or id. Omitted, the person’s first organisation (or, for a session, the one they are acting in).
Responses
  • 200 OK
    • collections Collection[]
  • 401 No session, and no personal access token.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 403 Your role — or the token’s scopes — do not allow this.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 404 No such organisation, or the subject is not in yours.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
POST/api/v1/org/collectionsCreate a collection

Requires org:sites and org:members (an owner or admin): the folders are the organisation’s.

Parameters
  • X-Verso-Org header · string — Organisation slug or id. Omitted, the person’s first organisation (or, for a session, the one they are acting in).
Request body
  • name string · required
  • parentId string
Responses
  • 201 Created.
    • collection Collection
  • 400 Malformed input; the message says which field.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 401 No session, and no personal access token.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 403 Your role — or the token’s scopes — do not allow this.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 404 No such organisation, or the subject is not in yours.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 409 Refused by a rule — the last owner, a SCIM-owned team, a duplicate. reason names it.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 429 Over the write rate limit; honour Retry-After.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
PATCH/api/v1/org/collections/{collectionId}Rename or move a collection

Requires org:sites and org:members. A move inside itself is refused (409), and then nothing changes.

Parameters
  • collectionId path · required · string
  • X-Verso-Org header · string — Organisation slug or id. Omitted, the person’s first organisation (or, for a session, the one they are acting in).
Request body
  • name string
  • parentId string
Responses
  • 200 OK
    • collection Collection
  • 400 Malformed input; the message says which field.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 401 No session, and no personal access token.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 403 Your role — or the token’s scopes — do not allow this.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 404 No such organisation, or the subject is not in yours.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 409 Refused by a rule — the last owner, a SCIM-owned team, a duplicate. reason names it.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 429 Over the write rate limit; honour Retry-After.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
DELETE/api/v1/org/collections/{collectionId}Delete a collection

Requires org:sites and org:members. Its sites and child collections move up one level.

Parameters
  • collectionId path · required · string
  • X-Verso-Org header · string — Organisation slug or id. Omitted, the person’s first organisation (or, for a session, the one they are acting in).
Responses
  • 200 OK
    • deleted string
  • 400 Malformed input; the message says which field.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 401 No session, and no personal access token.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 403 Your role — or the token’s scopes — do not allow this.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 404 No such organisation, or the subject is not in yours.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 409 Refused by a rule — the last owner, a SCIM-owned team, a duplicate. reason names it.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 429 Over the write rate limit; honour Retry-After.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
GET/api/v1/sites/{siteSlug}/configRead a site’s verso.json

Requires admin on the site. A token also needs the site:config scope. Answers the file on the default branch and anything the parser dropped.

Parameters
  • siteSlug path · required · string
  • X-Verso-Org header · string — Organisation slug or id. Omitted, the person’s first organisation (or, for a session, the one they are acting in).
Responses
  • 200 OK
    • site string
    • ref string
    • config object
    • problems string[]
    • exists boolean
  • 401 No session, and no personal access token.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 403 Your role — or the token’s scopes — do not allow this.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 404 No such organisation, or the subject is not in yours.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
PATCH/api/v1/sites/{siteSlug}/configChange top-level sections of verso.json

Requires admin on the site (and site:config on a token). Committed to the default branch through the same validation as the settings screens; a key set to null is removed; an invalid patch writes nothing and answers the problems.

Parameters
  • siteSlug path · required · string
  • X-Verso-Org header · string — Organisation slug or id. Omitted, the person’s first organisation (or, for a session, the one they are acting in).
Request body
object
Responses
  • 200 OK
    • commitSha string
    • changed boolean
    • config object
  • 400 Malformed input; the message says which field.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 401 No session, and no personal access token.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 403 Your role — or the token’s scopes — do not allow this.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 404 No such organisation, or the subject is not in yours.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 409 Refused by a rule — the last owner, a SCIM-owned team, a duplicate. reason names it.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'
  • 429 Over the write rate limit; honour Retry-After.
    • error string · required — A sentence a person can act on.
    • code string · required — 'not_found' | 'forbidden' | 'bad_request' | 'conflict'