API
Everything the board does, over HTTP
A JSON REST API for projects, columns, cards, comments, checklists, milestones, members and search. Mint a bearer token in Settings. Available on every plan, including Free - plan limits apply here exactly as they do on the board.
Client libraries
Three first-party SDKs, each maintained against the same spec. Anything a client does not wrap yet is still one HTTP call away.
-
Python
pip install craaft -
TypeScript
npm i craaft -
PHP
composer require craaft/craaft
Getting started
Three calls from a blank terminal to a card on the board.
-
Mint a token
Settings, then API keys. Tokens look like
cra_followed by 24 characters. They are shown once and stored only as a hash - if you lose one, revoke it and make another. -
Check it works
curl -H "Authorization: Bearer $CRAAFT_API_TOKEN" \ https://craaft.io/api/v1/me -
Create a card
curl -X POST \ -H "Authorization: Bearer $CRAAFT_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{"title":"Ship it","column":"todo","position":1}' \ https://craaft.io/api/v1/projects/$PROJECT_ID/cards
Authentication
Everything you need to put on the wire, and what a token can never reach.
- Base URL
https://craaft.io/api/v1- the version prefix is mandatory, and unversioned paths return 404.- Header
Authorization: Bearer cra_...on every request. The scheme is case-insensitive; thecra_prefix is not optional.- Tokens act as you
- A token carries exactly the permissions of the person who made it, on the same boards. There is no separate scope system, and nothing a token can reach that you could not reach yourself.
- No CSRF header
- Bearer-authenticated requests skip CSRF entirely. That protection exists for cookie-authenticated browser sessions, which is a different path.
- Not reachable by token
- Sign-in, API key management, billing and avatar upload stay session-only. A key that could mint another key is a lateral-movement problem, so it cannot.
Bulk endpoints
Three endpoints batch card work: create, update, and move. Each takes up to 100 items and runs as one transaction, so a bad item rolls the whole batch back rather than leaving a half-imported board. The error names the offending index.
POST /api/v1/projects/{id}/cards/bulk create up to 100
PATCH /api/v1/cards/bulk update up to 100
POST /api/v1/cards/bulk/move move up to 100
One bulk call spends one rate-limit token. Looping 100 single creates spends 100, and leaves a partial board if it fails halfway.
Things worth knowing
The four that most often cost people an afternoon.
positionis a float, not an index- Reordering is a midpoint calculation. To drop a card between siblings at 2 and 3, send
2.5. To put one at the top of a column whose first card is at 1, send0.5. columnis the key, not the id- Card payloads take the column's stable key, which you read from
columns[].keyon the project. The column's UUID is for column endpoints. - Two different moves
- Reordering inside a board is
PATCH /cards/{id}withcolumnandposition. Moving to a different board isPOST /cards/{id}/movewith atargetProjectId. - 404 means “no” without saying why
- A missing resource and one you cannot access return the same 404, deliberately, so the API cannot be used to discover what exists. Do not treat it as “not found” and retry.
Rate limits and errors
Each token gets a bucket of 60 requests, refilling at one per second. Over it you get a 429 with a Retry-After header giving the exact seconds until the next token is free. Honour it rather than backing off blindly.
| Status | Meaning | What to do |
|---|---|---|
400 |
Malformed body or missing field | Fix the payload and retry |
401 |
Missing, bad, or revoked token | Mint a fresh one |
402 |
A plan limit, with the limit in the body | Upgrade, or stay inside it |
404 |
Missing, or you have no access | Stop; do not probe |
409 |
Duplicate, or a column that still has cards | Adjust the input |
413 |
Upload over the 25 MiB cap | Shrink the file |
429 |
Rate limited | Sleep for Retry-After |
Every error body is {"error": "..."} with a message meant for a human reading a log.
Prefer an agent over curl?
The MCP server exposes the same API as tools - Claude, your editor, or any MCP client, with your own token.