# Errores

Todos los endpoint fallan de la misma forma: un estado HTTP y un contenedor JSON.

```json
{
  "error": {
    "type": "invalid_request",
    "code": "invalid_input",
    "message": "duration must be one of 5, 10.",
    "param": "duration",
    "details": [ { "param": "duration", "message": "duration must be one of 5, 10." } ],
    "request_id": null,
    "doc_url": "https://doitong.com/developers/docs/errors#invalid_input"
  }
}
```

| Campo | Significado |
|---|---|
| `type` | Clase general del error. Decide qué hacer a partir de esto. |
| `code` | Motivo exacto, estable y legible por máquinas. Cada código tiene una sección más abajo; `doc_url` enlaza con ella. |
| `message` | Explicación legible, segura para registrar o mostrar a un desarrollador. |
| `param` | El parámetro de entrada o encabezado problemático, cuando existe. |
| `details` | Para errores de entrada, cada problema encontrado: `[{ "param", "message" }]`. |
| `request_id` | La solicitud a la que se refiere, cuando existe. |

No se cobra nada por una llamada que devuelve un error. Una solicitud que falla *después* de ser aceptada conserva el estado
`failed`, incluye `error: { "type", "code", "message" }` en el objeto de solicitud y se reembolsa automáticamente.

## Tipos

| Tipo | Estado HTTP | Qué hacer |
|---|---|---|
| `invalid_request` | 400, 413 o 422 | Corrige la solicitud. Reintentar la misma llamada volverá a fallar. |
| `authentication_error` | 401 | Envía una clave válida en `Authorization: Bearer <key>`. |
| `permission_error` | 403 | Usa una clave con el alcance necesario. |
| `not_found` | 404 | Comprueba el id del modelo o de la solicitud. |
| `insufficient_credits` | 402 | Recarga el saldo de API (`top_up_url` en el error). |
| `budget_exceeded` | 402 | Se ha agotado el presupuesto propio de la clave. Definitivo: pide al propietario de la clave que lo aumente. |
| `conflict` | 409 | El recurso no está en un estado que permita esta llamada. |
| `rate_limited` | 429 | Espera `Retry-After` segundos y vuelve a intentarlo. |
| `upstream_error` | 502 | El backend del modelo ha fallado. Es seguro reintentar con la misma `Idempotency-Key`: se ejecutará de nuevo; no se te ha cobrado. |
| `service_unavailable` | 503 | La API está pausada o una dependencia no funciona. Reinténtalo más tarde. |
| `internal_error` | 500 | Es un error nuestro. Reinténtalo; si persiste, contacta con soporte indicando la hora y `request_id`. |

## Códigos

### `invalid_request`

Solicitud con formato incorrecto genérico (400).

### `invalid_json`

El cuerpo no es JSON válido (400).

### `invalid_body`

El cuerpo es JSON válido, pero no es un objeto; la entrada del modelo debe ser un objeto JSON (400).

### `body_too_large`

El cuerpo supera 16 MB (413). Sube los archivos grandes y pasa su URL en su lugar (consulta [Archivos](https://doitong.com/developers/docs/files)).

### `invalid_parameter`

Un parámetro de consulta tiene un valor incorrecto, por ejemplo `limit` fuera de 1 a 100, `wait` no es un número, un
`status` o `category` desconocido, o `storage` distinto de `permanent` / `temporary` (400). `param` indica cuál es.

### `invalid_category`

`GET /openapi.json?category=` indica una categoría que no existe (400). `GET https://api.doitong.com/v1/categories` las enumera.

### `invalid_cursor`

El `cursor` de una llamada de lista no pertenece a esta lista (400). Empieza de nuevo sin cursor.

### `invalid_idempotency_key`

El encabezado `Idempotency-Key` está vacío o supera los 255 caracteres (400).

### `invalid_arguments`

Una llamada a una herramienta MCP tiene un argumento ausente o con formato incorrecto (solo MCP). `param` indica el argumento.

### `invalid_input`

La entrada del modelo no supera la validación (422): un parámetro desconocido (el mensaje sugiere el nombre válido más cercano),
un parámetro obligatorio ausente, un tipo incorrecto, un valor fuera de la lista o el intervalo permitido, o se han indicado el nombre
canónico y el nativo con valores distintos. `param` es el primer parámetro problemático y `details` los enumera todos.
No se cobra nada.

### `unsupported_file`

Un archivo tiene un tipo que no aceptamos (422), por ejemplo una carga con una extensión no compatible o un `content_type`
que no coincide con la extensión.

### `invalid_webhook_url`

`webhook_url` no es una URL pública `https://` (422). Se rechazan las direcciones privadas, locales y `http://` sin cifrar.

### `missing_api_key`

El endpoint necesita una clave de API y no se ha enviado ninguna (401).

### `invalid_api_key`

La clave es desconocida, se ha revocado o ha caducado (401).

### `insufficient_scope`

A la clave le falta el alcance que necesita esta llamada (403), por ejemplo `models:run` para ejecutar, o `models:read` para leer
solicitudes y el saldo. Consulta [Autenticación](https://doitong.com/developers/docs/authentication#scopes).

### `account_disabled`

La cuenta está cerrada o suspendida (403). Sus claves ya no funcionan.

### `not_found`

La ruta no existe (404).

### `model_not_found`

No existe ningún modelo público con este id (404). Enumera los modelos con `GET https://api.doitong.com/v1/models`.

### `request_not_found`

No existe en tu cuenta ninguna solicitud con este id (404). Las solicitudes de otras cuentas nunca son visibles.

### `insufficient_credits`

El **saldo de API** de la cuenta (créditos de API, 1000 créditos de API = 1 $ — no los créditos de las aplicaciones de Doitong) no cubre el
precio de esta llamada (402). La respuesta se envía antes de cobrar nada. El error incluye `top_up_url`; añade dinero en
[https://doitong.com/developers/dashboard#balance](https://doitong.com/developers/dashboard#balance) y vuelve a intentarlo.

```json
{ "error": { "type": "insufficient_credits", "code": "insufficient_credits",
             "message": "This request costs 560 API credits ($0.56); the API balance is 120 API credits ($0.12). Top up at https://doitong.com/developers/dashboard#balance",
             "top_up_url": "https://doitong.com/developers/dashboard#balance", "doc_url": "https://doitong.com/developers/docs/errors#insufficient_credits" } }
```

### `budget_exceeded`

La llamada superaría el presupuesto propio de la clave de API en créditos de API (402). El presupuesto lo establece el propietario
de la clave; reintentar no sirve hasta que lo aumente.

### `conflict`

La llamada entra en conflicto con el estado actual del recurso (409).

### `idempotency_mismatch`

`Idempotency-Key` ya se utilizó con un cuerpo diferente (409). Usa una clave nueva para una operación nueva.

### `idempotency_in_progress`

Ahora mismo se está creando una solicitud con la misma `Idempotency-Key` (409). Reinténtalo dentro de un momento para obtenerla.

### `request_active`

La solicitud sigue `queued` o `running`, por lo que todavía no tiene archivos que eliminar (409). Elimínalos cuando haya terminado.

### `not_cancelable`

La solicitud ya está `succeeded` o `failed` y no se puede cancelar (409).

### `rate_limited`

La clave ha realizado demasiadas solicitudes en la ventana actual (429). Consulta los encabezados `X-RateLimit-*` y `Retry-After`.

### `too_many_inflight`

La cuenta ya tiene 10 solicitudes `queued` o `running` (429). Inicia la siguiente cuando termine una de ellas.

### `upstream_error`

El backend del modelo ha rechazado o no ha podido procesar la solicitud (502 al ejecutar, o `failed` con este código en la solicitud).
Los créditos de API se reembolsan. Si el backend ha rechazado la entrada, la llamada devuelve `422 invalid_input` con su mensaje.

### `upstream_lost`

El backend del modelo ya no conoce la solicitud, por lo que nunca llegará ningún resultado. Termina como `failed` con este código
(tipo `upstream_error`) y se reembolsa. Vuelve a ejecutarla.

### `timeout`

La solicitud ha tardado más de su límite (15 minutos para imágenes, 60 para vídeo, 30 en los demás casos) y se ha detenido. Termina como
`failed` con este código y se reembolsa.

### `service_unavailable`

La API está temporalmente pausada o una dependencia, como las cargas de archivos, no funciona (503). Reinténtalo más tarde.

### `internal_error`

Algo ha fallado por nuestra parte (500). Reinténtalo; si persiste, contacta con soporte.

### `charge_failed`

No se ha podido cobrar a la cuenta debido a un problema interno (500). La solicitud se marca como `failed`; no se ha retirado nada.
Reinténtalo.

### `pricing_unavailable`

El precio del modelo no está configurado, por lo que ahora no se puede calcular ni ejecutar (500).
