# PointStack Agent API — Errors & recovery Deep-dive companion to https://pointstack.dev/llms.txt. Every error is a JSON envelope: `{ "error": { "code, message, hint, docs, retryAfter? } }`. The `code` is stable — branch on it, not on `message`. Below is what each means, what you (the agent) should do, and how to guide the user. ## How to behave generally - Read `code` programmatically; surface `message`/`hint` to the user verbatim. - On `retryAfter` (seconds), wait at least that long before retrying; use exponential backoff if it persists. - Never loop-retry a 4xx that isn't `rate_limited` — fix the request instead. - Treat the room token (`psr_…`) and user token (`psk_…`) as secrets: never echo them back to the user in logs you don't control; do show the user their own newly created room token once so they can save it. ## unauthorized (401) The token is missing or not recognised (wrong/revoked/typo). - Agent: stop; do not retry with the same token. - Guide the user: "I need a PointStack token. Create one at https://pointstack.dev/account (Agent API token), then share it with me — or I can create a room without a token and send you a link to claim." ## forbidden (403) The token is valid but not allowed for this resource. Common causes: - A room token used against a different room → use that room's own token, or the owner's user token. - Setting `teamId` you're not a member of → ask the user which of their teams to use, or omit `teamId`. - Acting on a room the user token doesn't own. - Guide the user: name the specific blocker (wrong room / team membership) and ask for the right token or team. ## not_found (404) The room or story id doesn't exist (or the room was deleted / TTL-swept). - Agent: don't retry; re-fetch with `GET` to confirm current state. If a headless room you created is gone, it likely expired unclaimed — create a fresh one. - Guide the user: "That room no longer exists; I'll make a new one." ## validation_failed (400) The request body is malformed or breaks a hard limit: > 50 stories per request, > 200 per room, unknown deck, empty story title, or a body that isn't valid JSON. - Agent: fix the payload and retry once; split large backlogs into batches of <= 50. - Over-long text is *not* an error: a title past 150 chars, details past 5k, or a link past 500 is silently truncated to the limit. Truncate it yourself first if losing the tail would matter, and re-read with `GET` if you need to be sure what was stored. - Guide the user only if intent is ambiguous (e.g. which deck they want). ## rate_limited (429) Per-token / per-IP budget exceeded. `retryAfter` is set. - Agent: back off for `retryAfter` seconds, then resume. Batch writes to reduce request count. If you're tokenless, switching to a user token raises the budget. - Guide the user: "PointStack is rate-limiting us; I'll continue in a moment. For heavier use, generate a user token at /account." ## tokenless_unavailable (503) The global tokenless-create circuit breaker is open (abuse protection). Tokenless creation is temporarily off for everyone; authenticated paths are unaffected. - Agent: prefer a user token if you have one (it bypasses this). Otherwise wait `retryAfter` and retry. - Guide the user: "Anonymous room creation is briefly unavailable. If you create a token at https://pointstack.dev/account and share it, I can proceed right now." ## room_full (409) The room hit its 20-seat cap (or a story-count limit on add). - Agent: for seats, nothing to retry — tell the user the room is full. For story limits, stop adding; the room is at 200 stories. - Guide the user accordingly. ## Network / 5xx (other than 503 above) Transient. Retry with backoff a few times; if it persists, tell the user PointStack seems to be having trouble and to try again later.