The REST API

The Soleil Clusters REST API lives at /api/v1 and is authenticated with a personal access token sent as a bearer token. A token acts as you — every call runs under exactly the permissions your account has in the app. Responses are JSON, errors carry both a machine-readable code and a human sentence, list endpoints paginate, and a full OpenAPI description is published at /api/v1/openapi.json.

Base URL: https://clusters.soleilpictures.com/api/v1

Every response is JSON. Every error carries a machine-readable code and a human-readable error. A full OpenAPI description is at /api/v1/openapi.json, served without authentication — a spec you need a credential to read is not discoverable.

The authorization model

The part worth reading before anything else.

A token is not a capability. It resolves to you, and then every read and write runs as your user under the same row-level security the app uses.

The consequence: a token reaches exactly what your account reaches, and nothing more. Boards you were invited to as an editor are writable. Boards you can only view are not. Boards you cannot see return 404 rather than 403, so the API never confirms the existence of something you have no business knowing about.

There is no per-resource permission list on a token, and no way for API permissions to drift out of step with app permissions.

Endpoints

Method and pathDoes
GET /meWho this token belongs to, its scopes, and its rate-limit state
GET /workspacesWorkspaces you can see
GET /searchSearch boards and cards by text
GET /boardsList boards; filter and paginate
POST /boardsCreate a board
GET /boards/:idOne board
PATCH /boards/:idRename, change view, or reparent
DELETE /boards/:idSoft-delete — restorable
POST /boards/:id/restoreUndo a soft delete
GET /boards/:id/cardsCards on a board, paginated
POST /boards/:id/cardsAdd cards
PATCH /boards/:id/cards/:cardIdChange a card
DELETE /boards/:id/cards/:cardIdRemove a card
POST /boards/:id/cards/moveMove cards to another board
POST /uploadsUpload an image, get a key back
GET /images/:keyRead an image back
GET /resolveFind an object by a foreign identifier
GET /boards/treeA whole board hierarchy in one call
GET /boards/:id/exportExport a board, as JSON or MovieLabs OMC
POST /boards/:id/importImport from URLs — safe to re-run
POST /boards/:id/arrangeLay a board out — justified, masonry, grid
GET /boards/:id/groupsThe groups on a board
POST /boards/:id/groupsSay a set of cards belongs together
PATCH /boards/:id/cardsChange many cards in one call
DELETE /boards/:id/cardsRemove many cards in one call
POST /boards/moveReparent many boards, cycle-safe
DELETE /boardsSoft-delete many boards
GET /auditAudit log of writes and image reads
POST /webhooksWebhooks — get told when things change
GET /webhooks/:id/deliveriesEvery attempt, with its result
GET /service-accountsService accounts in a workspace
POST /service-accountsCreate one, with its first token
DELETE /service-accounts/:idRetire one and revoke its tokens
POST /service-accounts/:id/tokensMint another token — rotate without downtime
GET /service-accounts/:id/tokensIts tokens and when each was last used
DELETE /service-accounts/:id/tokens/:tokenIdRevoke one token

GET /api/v1 returns this list plus your current scopes, so the one URL a person types by hand answers usefully.

Authentication

Two ways in, both ending at a bearer token that resolves to one person's own session.

For your own scripts, mint a token under Settings → API:

curl https://clusters.soleilpictures.com/api/v1/me \
  -H "Authorization: Bearer undefined…"

For an application other people connect — including any MCP client — use OAuth, so nobody is asked to paste a credential into somebody else's software. Registration is open and dynamic; discovery starts from the WWW-Authenticate header on any 401.

Three scopes: delete · read · write. Tokens are stored only as a hash — the value is shown once and cannot be recovered. See Authentication.

Pagination

List endpoints take limit and offset. The default page is 100 and the maximum is 500.

{ "boards": [ … ], "limit": 100, "offset": 0, "has_more": true, "next_offset": 100 }

has_more is computed by fetching one row beyond the page, so it costs nothing extra. Follow next_offset until it is null.

Rate limits

1000 requests per hour per token. Every response — not only refusals — carries the current state:

HeaderMeaning
x-ratelimit-limitYour ceiling
x-ratelimit-remainingWhat is left in the window
x-ratelimit-resetUnix seconds when the window resets
retry-afterSeconds to wait — only on 429

GET /me reports the same numbers in its body, so a client can check its budget without spending a request on a real call.

Idempotency

Send an Idempotency-Key header on any POST and a retry with the same key replays the original response rather than doing the work twice:

-H "Idempotency-Key: $(uuidgen)"

Keys are remembered for 24 hours. PATCH and DELETE are idempotent by construction. A retry arriving while the first attempt is still in flight gets 409 rather than racing it. A replayed response carries idempotent-replay: true.

CORS

Access-Control-Allow-Origin is *, so the API is callable from a browser.

That it works from a browser does not mean you should. A undefined token in front-end code is readable by anyone who opens dev tools, and it acts as you. Call the API from a server.

For AI agents

  • Quickstart — working code in curl, TypeScript and Python
  • MCP — the same API as tools an assistant can call directly
  • OpenAPI — generate a client
  • /llms.txt and /llms-full.txt — this documentation, machine-readable
  • Every docs page is available as raw Markdown by appending .md

Frequently asked questions

What can the API do?

Read your workspaces, search across everything, create and manage boards and cards, upload images, and read images back. Effectively everything the app does to board structure.

Does a token have its own permissions?

No, and this is the important design point. A token resolves to your user and every call runs under the same row-level security the app runs under. A token cannot reach anything your account cannot reach.

Is there a machine-readable spec?

Yes, OpenAPI at /api/v1/openapi.json. It is served without authentication, because a spec you need a credential to read is not discoverable.

Machine-readable: /docs/api.md · /llms.txt