Error codes
Every error response is JSON with a single error field:
json
{ "error": "Invalid API key." }The message arrives in the language of the account the key belongs to; the status code is the part to branch on.
Table
| Code | When | What to do |
|---|---|---|
| 400 | A required field is missing or malformed — no title, no base_updated, an unknown status, a bad expiry date, more than 200 items in one call | Fix the request. Retrying it unchanged will not help. |
| 401 | No Authorization header, a token that is not kb_user_… / fm_user_…, an unknown key, or an expired key | Check the header, then check whether the key was rotated or has expired. |
| 403 | A read key called a write endpoint | Create or rotate the key with the read_write scope. |
| 404 | The map, node, parent or task does not exist — or belongs to somebody else | The API deliberately does not distinguish the two, so it cannot be used to probe which ids exist. |
| 409 | base_updated does not match the map's current version | Somebody changed the map in the meantime. Re-read it, re-apply your change, send it again. |
| 413 | Request body over 2 MB | Split the work into more calls; 200 nodes per call is the hard cap anyway. |
| 429 | Over the rate limit — 120 reads or 30 writes per minute per key | Back off and retry. Reads and writes are counted separately. |
The two that surprise people
404 instead of 403. Asking for someone else's map does not tell you it exists. This is on purpose: a 403 would confirm the id is real, which turns the API into an id oracle.
409 is normal, not a failure. Two clients editing one map — an assistant and a colleague with the editor open — will hit it. The correct handling is always: re-read the map, re-apply, retry. Never "force the write".
Do not retry a 400 or a 403
They mean the request is wrong, not that the moment was wrong. An automated retry loop on either one just burns your rate limit.

