API

Errores y límites

Códigos de respuesta de la API de Ghosty Studio, qué significa cada uno y cómo reintentar; tamaños y topes.

Actualizado 2026-09-16

Códigos

CódigoCuerpoQué hacer
202{ turnId, key, estado, enCola, reemplazoAnterior, repetido, inyectado }El turno fue aceptado, no terminado. Escucha los . enCola > 0 = hay turnos delante. repetido = ya habías mandado ese turnId, no se duplicó. inyectado = tu mensaje entró al turno que ya corría: no hay turno nuevo, sigue escuchando el mismo turnId.
400{ error }Cuerpo inválido: falta content, id/optionId, modelo vacío… No reintentes sin cambiarlo.
401textoSin token o caducado. Refresca y reintenta una vez.
402{ error: "quota_exhausted" | "trial_expired" | "own_key_required", message }Bolsa agotada, trial vencido o modelo que exige llave propia. El message es para mostrar.
403textoFalta un scope. No reintentes: pide el scope en el siguiente login.
404texto o { error: "not_found" }El agente no es tuyo, o el recurso no existe. Un agente ajeno responde 404 y no 403, a propósito.
405{ error: "method_not_allowed" }Método incorrecto.
409{ error: "agente_no_acp", motor }El agente no habla el protocolo de conversaciones de esta API (motor sin ACP).
413{ error: "audio demasiado grande" }Audio o archivo por encima del tope.
503{ error: "reiniciando" } + Retry-After: 10Deploy en curso. Reintenta con el mismo turnId tras el Retry-After: no duplica.

Idempotencia

POST …/messages acepta un turnId tuyo (UUID). Si repites la petición con el mismo turnId —por un timeout, un 503, una red mala— el servidor contesta repetido: true y no crea un segundo turno. Úsalo siempre.

Un reintento correcto respeta Retry-After y reutiliza el turnId:

typescript
async function sendTurn(url: string, body: { content: string; turnId: string }, token: string) {  for (let intento = 0; intento < 3; intento++) {    const res = await fetch(url, {      method: "POST",      headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },      body: JSON.stringify(body), // mismo turnId en cada intento: el servidor no duplica    });    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("la plataforma siguió reiniciando tras 3 intentos");}

Concurrencia

Un agente corre un turno a la vez por conversación. Un mensaje nuevo en una conversación con turno en curso entra a ese turno (inyectado: true): el agente lo lee en cuanto termina la herramienta que está corriendo y contesta en la misma respuesta, sin tirar el trabajo hecho. Es la forma de corregirlo a mitad de trabajo. Si el turno ya iba cerrando y no alcanzó a entrar, se detiene el anterior y se reemplaza (reemplazoAnterior: true). Sólo quien pidió el turno puede reemplazarlo. Conversaciones distintas del mismo agente corren en paralelo hasta el tope del plan; las que esperan máquina cuentan en enCola.

Tamaños

QuéTope
Adjuntos por mensaje8
Audio para POST /api/v2/me/sttsegún plan; excederlo da 413
URL firmada de un archivo6 horas; vuelve a pedirla con GET /api/v2/me/files/:id
Código de autorización OAuth260 s
Access token1 h
Refresh token90 días, rota en cada uso
Permiso sin contestar10 min → denegado

Ritmo

No hay un límite de peticiones por segundo publicado en beta; el tope real es la bolsa de tokens del plan y las máquinas encendidas a la vez. Si ves 429, respeta Retry-After.