Errors and limits
What it answers when it refuses
Every refusal carries a code and a message that names the thing that was wrong — the missing scope, the rule that refused, the allowance that ran out. A refusal is never a silent empty result.
- Token limit
- 600/1h
- Agent limit
- 1200/1h
- Idempotency
- 24h
- Page size
- 100 max
Every code
400
invalid_parameter
A query parameter is malformed, or a cursor is not one this API issued.
400
invalid_body
The body is not a JSON object.
400
invalid_request
The request is well formed but the rules refuse it; the message says which rule.
401
unauthorized
No token, or one that is unknown, revoked or expired. Each is said the same way.
402
plan_required
The workspace’s plan does not include this: API access, or room for more contacts.
403
forbidden
The token does not carry the scope the endpoint needs; the message names it.
404
not_found
Nothing with that id in this workspace.
409
conflict
The record is in a state that refuses this, such as a test that has already been decided, or a request with the same Idempotency-Key is still being answered.Retry shortly, with the same key.
422
idempotency_key_reused
The Idempotency-Key was used with a different request in the last 24 hours. A new request needs a new key.
429
rate_limited
More than 600 requests from one token in the hour. Retry-After says when to try again.Retry after the time Retry-After gives.
500
internal
Something failed on our side, and it has been recorded.Retry with the same Idempotency-Key.
{ "error": { "code": "forbidden", "message": "This token does not carry read:growth." } }Rate limits
- Workspace token600 requests an hour, per token.
- Agent credential1200 an hour, per credential. Higher, because an agent asks before every action it takes.
- X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, Retry-AfterOn every answer, not only on a 429 — so you can slow down before you are stopped.
Being over a plan’s allowance is plan_required, not rate_limited, and the message says what remains. They are different problems: one is solved by waiting, the other never is.
Retrying a write safely
Send Idempotency-Key on a write. If the same request arrives again within 24 hours, the first answer is returned rather than the work being done twice, and Idempotent-Replayed says so.
- Same key, same requestThe first answer, replayed. Nothing happens a second time.
- Same key, different requestRefused with idempotency_key_reused. A new request needs a new key — otherwise a typo could silently inherit an unrelated result.
- Still in flightRefused with conflict. Try again shortly.
curl -X POST "$XYRANIK_HOST/api/v1/actions" \
-H "Authorization: Bearer xyr_live_…" \
-H "Idempotency-Key: 1f0c9e2a-…" \
-H "Content-Type: application/json" \
-d '{ … }'Generate the key once per logical operation — a UUID — and reuse it for every retry of that operation.
What gets counted
Every call is counted where it was answered, whatever it was answered with — succeeded, refused, rate-limited or failed — so your own usage page shows refusals rather than hiding them. Counting began on 2026-09-11; nothing before that date is reconstructed.
Your workspace’s own figures are in your developer settings, which open after you sign in.