Tesria

Requests and responses

Once your first request works (see Getting started with the API), this page explains what you send, what comes back, and what to do when something is refused. The /api/docs reference on your server has every field of every request; this is the part worth understanding first.

Throughout this page, your-server stands for your server’s address: whatever you type into the browser to open Tesria, without https://. For example, if you open Tesria at https://wiki-server.local, then https://your-server/api/spaces means https://wiki-server.local/api/spaces.

JSON in, JSON out

  • Every answer is JSON, except downloads such as an exported page or an attachment.

  • When you send data, send JSON too, with the header Content-Type: application/json.

  • Things are named by id, a long code such as 8c2e61f0-6a1b-4f0e-9d51-2b7c0e4a9f13. Spaces also have their short key, such as DEMO, which most space requests take instead.

  • A choice from a fixed list is a number. In permissions, for example, operation is 0 for View, 1 for Edit and 2 for Admin. The reference lists each one.

How a page’s content is stored

A page is not stored as text or HTML but as a document: a tree of blocks (headings, paragraphs, tables, panels) in the editor’s own format, which is called ProseMirror. The API sends and receives it as contentJson, which is that document written as JSON and then put in a string.

The easiest way to learn the format is to write something in the editor and read it back:

Bash / Shell
curl -H "Authorization: Bearer $TESRIA_TOKEN" https://your-server/api/pages/<page-id>

The smallest possible page, one paragraph, looks like this as a document:

JSON
{ "type": "doc", "content": [ { "type": "paragraph", "content": [ { "type": "text", "text": "Written by a script." } ] } ] }

Would rather write Markdown? The MCP server takes Markdown and converts it into the editor’s elements for you. It is made for AI assistants, but anything that speaks MCP can use it. See MCP.

Writing a page

To create a page, send the space’s id (from /api/spaces), a title and the content. Note how the document is a string inside the JSON, so its own quotes are written \":

Bash / Shell
curl -X POST https://your-server/api/pages \ -H "Authorization: Bearer $TESRIA_TOKEN" \ -H "Content-Type: application/json" \ -d '{"spaceId":"<space-id>","title":"Hello from a script","contentJson":"{\"type\":\"doc\",\"content\":[{\"type\":\"paragraph\",\"content\":[{\"type\":\"text\",\"text\":\"Written by a script.\"}]}]}"}'

The page is published straight away, at the top level of the space. Add "parentPageId":"<page-id>" to put it under another page. The answer is the new page, with its id. Everyone watching the space is told, as if you had published it in the browser.

Building those strings by hand is fiddly. In a real script, build the document as an object and let your language turn it into a string: JSON.stringify in JavaScript, json.dumps in Python.

Updating without overwriting someone

PUT https://your-server/api/pages/<page-id> changes a page. It takes a title, a contentJson and a changeComment (what changed, shown in the page’s history). Leave out contentJson to rename a page without touching what it says. Every update adds a new version, so nothing is lost: the history can always bring back the old one.

If a person might be changing the same page, send baseVersion too: the currentVersionNumber you read before making your change. If the page has moved on since, Tesria refuses with 409 and sends the page as it is now, instead of writing over the other person’s work. Read it again, make your change to the new version, and send that.

Anyone who has the page open in the editor when your update arrives sees it highlighted, to accept or reject. See Changes from assistants and the API.

When a request is refused

The status code says what kind of problem it is. Where there is more to say, the body explains, often with a code a script can check.

  • 400: something in the request is wrong. The body names the field and why, for example a label with a space in it.

  • 401: you are not signed in. The token is missing, mistyped or revoked.

  • 403: you may not do this. You can see the thing but not change it, or the token is read-only (read_only_token), or the action needs a password in the browser (reauth_required).

  • 404: not found. It does not exist, or you are not allowed to know that it does. A restricted page answers 404 rather than 403, so nobody can find out it is there by guessing.

  • 409: a conflict. A space key already taken, or a page that changed since your baseVersion.

  • 413: too large. An attachment can be up to 25 MB, a wiki pack up to 500 MB, and anything else up to 100 MB.

  • 429: too many requests. Wait the number of seconds in the Retry-After header, then try again.

  • 503: not right now. A backup is being restored and the wiki is read-only for a few minutes, or the export you asked for needs a service this server does not run.

Limits worth knowing

  • Requests with a token are not rate-limited. Requests with no token or session at all are limited to 300 a minute from one address, which an administrator can change.

  • Lists come back whole, except search (the best 50) and notifications and the audit log (the most recent, up to 200).

  • Browser sessions need one more header. If you call the API from a web page using someone’s signed-in session rather than a token, every change must carry X-Requested-With: Tesria. It stops other websites from acting with that person’s session. Scripts with a token do not need it.


Applies to

Tesria 0.5 and later

Updated

September 24, 2026

Changes

Revised.