API

Errors and limits

Response codes of the Ghosty Studio API, what each one means and how to retry; sizes and caps.

Updated 2026-09-16

Codes

CodeBodyWhat to do
202{ turnId, key, estado, enCola, reemplazoAnterior, repetido, inyectado }The turn was accepted, not finished. Listen to the . enCola (queued) > 0 = there are turns ahead. repetido (repeated) = you had already sent that turnId, it was not duplicated. estado = state; reemplazoAnterior = replaced the previous one; inyectado (injected) = your message joined the turn already running: no new turn, keep listening to the same turnId.
400{ error }Invalid body: missing content, id/optionId, empty model… Don't retry without changing it.
401textNo token or expired. Refresh and retry once.
402{ error: "quota_exhausted" | "trial_expired" | "own_key_required", message }Allowance exhausted, trial expired or a model that requires your own key. The message is for display.
403textA scope is missing. Don't retry: request the scope at the next login.
404text or { error: "not_found" }The agent isn't yours, or the resource doesn't exist. Someone else's agent responds 404 and not 403, on purpose.
405{ error: "method_not_allowed" }Wrong method.
409{ error: "agente_no_acp", motor }The agent doesn't speak this API's conversation protocol (engine without ACP). motor = engine.
413{ error: "audio demasiado grande" }Audio or file above the cap ("audio too large").
503{ error: "reiniciando" } + Retry-After: 10Deploy in progress ("restarting"). Retry with the same turnId after the Retry-After: it doesn't duplicate.

Idempotency

POST …/messages accepts a turnId of yours (UUID). If you repeat the request with the same turnId — because of a timeout, a 503, a bad network — the server replies repetido: true and doesn't create a second turn. Always use it.

A correct retry honors Retry-After and reuses the turnId:

typescript
async function sendTurn(url: string, body: { content: string; turnId: string }, token: string) {  for (let attempt = 0; attempt < 3; attempt++) {    const res = await fetch(url, {      method: "POST",      headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },      body: JSON.stringify(body), // same turnId each attempt: the server does not duplicate    });    if (res.status !== 503) return res;    const wait = Number(res.headers.get("Retry-After") ?? 10) * 1000;    await new Promise((r) => setTimeout(r, wait));  }  throw new Error("the platform kept restarting after 3 attempts");}

Concurrency

An agent runs one turn at a time per conversation. A new message in a conversation with a turn in progress joins that turn (inyectado: true): the agent reads it as soon as the tool it is running finishes and answers in the same reply, keeping the work done so far. It is the way to correct it mid-work. If the turn was already closing and the message could not get in, the previous one is stopped and replaced (reemplazoAnterior: true). Only whoever requested the turn can replace it. Different conversations of the same agent run in parallel up to the plan's cap; those waiting for a machine count in enCola.

Sizes

WhatCap
Attachments per message8
Audio for POST /api/v2/me/sttper plan; exceeding it returns 413
Signed URL of a file6 hours; request it again with GET /api/v2/me/files/:id
OAuth2 authorization code60 s
Access token1 h
Refresh token90 days, rotates on every use
Unanswered permission10 min → denied

Rate

There is no published requests-per-second limit in beta; the real cap is the plan's token allowance and the machines awake at once. If you see 429, honor Retry-After.