RTFC API

Errors

Every error carries the same envelope:

json
{
  "detail": "Debate not found",
  "error": {
    "code": "debate_not_found",
    "message": "Debate not found",
    "details": {},
    "request_id": "req_b39e2875c54c4b5585f0608e6fe183db"
  }
}

Branch on error.code. The HTTP status is too coarse - a 403 is either "not your debate" or "your plan does not include this", and they need different handling. message is prose that will get reworded; detail exists for older clients.

Codes are append-only: an existing code never changes meaning, and new ones may appear. Treat an unrecognised code as its HTTP status.

Codes

CodeStatusMeaning
unauthenticated401No token
invalid_token401Expired, revoked, or malformed
insufficient_scope403Valid token, missing scope. details names it.
forbidden403Not yours
plan_feature_unavailable403Your plan does not include this feature
quota_exceeded402Allowance exhausted. details has usage and reset time.
concurrency_limit402Too many debates running at once
debate_not_found404
claim_not_found404
query_not_found404Panel does not exist
session_not_active409Retryable - the session is still starting
idempotency_conflict409Same key, still in flight
idempotency_key_reused422Same key, different body
invalid_request400Malformed
validation_failed422Body failed validation; detail lists fields
rate_limited429Slow down; honour Retry-After
upstream_unavailable503A dependency is down. Retry with backoff.
internal_error500Our fault. Quote the request_id.

What to retry

Retry with backoff429, 500, 502, 503, and session_not_active
Retry only with a fixed request400, 422
Do not retry401, 403, 404 - the answer will not change

For anything that creates or spends, retry with the same Idempotency-Key so a retry after an ambiguous timeout cannot act twice.

Request ids

Every response carries X-Request-Id, and it appears inside the error envelope. Log it. Quoting it in a support request is the difference between us finding the exact call in seconds and not finding it.

You can send your own instead - pass X-Request-Id and it is echoed back and used in our logs, so one id spans both systems.