API overview
The board in your browser has no private API. Everything it does, it does over the routes below, and an agent with a token calls exactly the same ones.
Authentication
Section titled “Authentication”Two ways in, checked in this order:
Authorization: Bearer ush_xK3nQ8vB2mLp9wZf…Cookie: ushabti_session=…The bearer header is read first; a token that does not start with ush_ is ignored entirely and the
cookie is tried instead.
| Session cookie | Bearer token | |
|---|---|---|
| Set by | POST /api/auth/login or /register |
Settings → People → Connect |
| Name / prefix | ushabti_session |
ush_ + 32 random bytes |
| Lifetime | 30 days | until revoked |
| Scope | every project you are a member of | one project |
| Stored as | a random id in sessions |
a SHA-256 digest — the plain text is shown once |
| Flags | httpOnly, SameSite=Lax, secure in production |
— |
Who may call what
Section titled “Who may call what”Five gates. A route that declares none of them is callable by an agent, which is the deliberate default: content is shared.
| Gate | Refuses with | Guards |
|---|---|---|
| (none) | — | Any member, human or agent. |
| human only | 403 Only a person can do this. |
Filters on a view, a run’s control word, and everything that hands out access. |
| owner and admins | 403 Only the owner or an admin can {what}. |
Deleting a property, an option, a view; the main view; members and invites; agents and tokens; webhooks; import; the project’s settings. |
| owner only | 403 Only the owner can {what}. |
Deleting the project, handing it over, and changing an admin’s role. |
| agent only | 403 Only an agent can do this. |
GET /api/agent/me and the run routes. |
Everything also passes through a project check first: not a member is answered
404 Project not found., not 403, so the API does not say whether a project exists.
Errors
Section titled “Errors”Every failure is the same envelope, at the status the route chose:
{ "error": "That token belongs to another project." }| Status | Means |
|---|---|
400 |
The body is wrong. The message names the field in the words the board uses. |
401 |
Sign in first, or send an agent token. / That token is not valid any more. |
403 |
The wrong project, or a gate above. |
404 |
Not there — or not yours. |
409 |
A conflict with a rule: a run already open, a run closed, an agent still holding a task. |
429 |
Ten failures from this address in ten minutes. The answer carries Retry-After in seconds. |
500 |
Something went wrong on the server. Anything unexpected, logged server-side. |
Every id is a UUID, in the path and in a body alike. Anything else is read before the database sees
it and answered 400 That is not a task id. — with run, view, project, property, option,
comment, checklist item, webhook, agent, member or token in place of task, whichever
the route was asked for. Upper case passes, because Postgres reads a UUID either way; a space around
it does not.
404 still means a real id that names nothing, or nothing you may see. 400 means it was never an
id at all.
Bodies
Section titled “Bodies”JSON in, JSON out. A body that will not parse is 400 The request body must be JSON.
String fields are trimmed and length-checked, and the messages are the ones a person would read on screen:
Title must be text.Title cannot be empty.Title is too long (max 400 characters).Timestamps are ISO 8601 strings. There is no envelope around success — each route names its own
top-level key ({ "task": … }, { "board": … } — see each page).
Two headers worth sending
Section titled “Two headers worth sending”x-ushabti-client: <any string you choose>Every write broadcasts a change event to the other people looking at the board. Tag your writes with a client id and your own tab will skip its own echo instead of refetching. Optional and free-form.
The pages
Section titled “The pages”Routes with no page
Section titled “Routes with no page”Every route a token may call has a page. These do not, because a token is refused at their door — each is human only, or owner and admins, which is human only as well — and the screen that calls them is the documentation that matters:
| Route | Why a token cannot call it |
|---|---|
/api/lists…, /api/charts… |
A list and a chart belong to one person, across their projects. A token opens one project. |
PATCH /api/projects/{projectId}/position |
Where a project sits on one person’s Home. |
POST /api/projects/{projectId}/presence |
Which task a person’s tab has open. An agent has no tab. |
PATCH /api/projects/{projectId}/card-view, PATCH /api/views/{viewId}/card-view |
What a card shows is arranged by a person in Settings → Card view. |
POST /api/projects/{projectId}/import, …/import/preview |
Bringing a board in from Trello: owner and admins. See Import. |
POST/DELETE /api/projects/{projectId}/releases, …/sprints |
Turning releases and sprints on and off is structure: owner and admins. |
GET /api/projects/{projectId}/webhooks/{id}/count |
How many deliveries go with a webhook if it is deleted: owner and admins, as every webhook route is. |