# Errores
URL: https://docs.fluenta.io/docs/errors

La forma de toda respuesta de error y cada código que la API puede responder: su estado HTTP, cuándo aparece y qué hacer.



Toda respuesta de error tiene la misma forma:

```json
{
  "statusCode": 404,
  "code": "ORGANIZATION_NOT_FOUND",
  "message": "Organización no encontrada"
}
```

* **Decide por `code`.** Un código conserva su nombre una vez publicado.
* **`message` está en español y es para la persona.** Muéstralo tal como
  llega; no lo interpretes en tu programa.
* Algunos códigos agregan campos que nombran lo que falló: `scope`, `feature`,
  `roles`, `field`, `maxDays`, `supported`, `reason`, `retryAfterSeconds`,
  `details`. Las tablas dicen cuál.
* Una escritura rechazada no cambió nada.

## Solicitudes que no pasan la validación [#solicitudes-que-no-pasan-la-validación]

Una consulta o un cuerpo que no pasa su validación responde 400
`INVALID_REQUEST` con `details`: una fila por cada falla.

```bash
curl 'https://api.fluenta.io/v1/external/me/usage?days=0' \
  -H 'Authorization: Bearer fluenta_live_xxxxxxxxxxxxxxxx'
```

```json
{
  "statusCode": 400,
  "code": "INVALID_REQUEST",
  "details": [{ "field": "days", "message": "days must not be less than 1" }],
  "message": "La solicitud no es válida: revisa «days»."
}
```

`field` es el parámetro o el campo del cuerpo, como ruta (`page`,
`items.0.name`). Es `null` cuando la falla no nombra ninguno. El `message` de
cada fila llega como lo redactó la validación.

## 400: la solicitud [#400-la-solicitud]

| Código                    | Cuándo                                                                                                                      | Qué hacer                                                                                          |
| ------------------------- | --------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| `INVALID_REQUEST`         | La consulta o el cuerpo no pasa su validación. Trae `details`.                                                              | Corrige los campos que nombra `details`. No reintentes sin cambiar la solicitud.                   |
| `INVALID_DAY`             | `day`, `from` o `to` no es un día real en `YYYY-MM-DD`. `field` dice cuál.                                                  | Envía un día real, en el reloj de la planta.                                                       |
| `RANGE_INVALID`           | `from` es posterior a `to`.                                                                                                 | Invierte las fechas.                                                                               |
| `RANGE_TOO_LARGE`         | El rango cubre más días que `maxDays`: 92, o 31 en `getPauses`.                                                             | Pide un rango más corto o divídelo en varios.                                                      |
| `GROUP_BY_UNSUPPORTED`    | La cifra no se agrupa por ese `groupBy`. `supported` lista los que sí.                                                      | Usa uno de `supported`.                                                                            |
| `FILTER_UNSUPPORTED`      | Un filtro (`moduleId`, `operatorId`, `operationId`) que la cifra no tiene bajo esa agrupación, o más de un filtro a la vez. | Envía un solo filtro y una agrupación de `supported`. Si `supported` llega vacío, quita el filtro. |
| `OPERATION_REQUIRED`      | `getStationActivity` con `view=events` y sin `operationId`.                                                                 | Agrega el `operationId` del «puesto».                                                              |
| `INVALID_IDEMPOTENCY_KEY` | El encabezado `Idempotency-Key` no tiene de 1 a 255 caracteres ASCII visibles, sin espacios.                                | Envía una clave válida.                                                                            |

## 401: la credencial [#401-la-credencial]

| Código              | Cuándo                                                                                                                                                 | Qué hacer                                                                                                   |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------- |
| `INVALID_API_TOKEN` | Falta el encabezado, la credencial no es de esta API, no existe, venció, fue revocada o su usuario está inactivo. Un solo cuerpo para todos los casos. | No reintentes. Crea un token nuevo en «Configuración» → «Tokens de API», o vuelve a conectar la aplicación. |

## 402: la suscripción [#402-la-suscripción]

| Código        | Cuándo                                          | Qué hacer                                                                                                                           |
| ------------- | ----------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `ORG_BLOCKED` | La suscripción de la organización está pausada. | No reintentes: la organización vuelve a responder cuando su suscripción deja de estar pausada. Tus otras organizaciones no cambian. |

## 403: el permiso, el plan o el rol [#403-el-permiso-el-plan-o-el-rol]

| Código               | Cuándo                                                                                                       | Qué hacer                                                                                                                                                               |
| -------------------- | ------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `INSUFFICIENT_SCOPE` | La credencial no tiene el permiso que pide la ruta. `scope` dice cuál.                                       | Un token nunca gana un permiso: crea uno nuevo que lo tenga. Una aplicación conectada se vuelve a conectar con el permiso activado. Mira [Permisos](/docs/permissions). |
| `PLAN_LIMIT`         | El plan de la organización no incluye lo que pediste. `feature` nombra la función del plan que falta.        | No reintentes. `getPlan` lista lo que permite el plan. Mira [Planes](/docs/plans).                                                                                      |
| `ROLE_NOT_ALLOWED`   | En una escritura: tu rol en esa organización no hace ese cambio en el panel. `roles` lista los roles que sí. | Un permiso nunca amplía un rol. Pide el cambio a alguien con uno de esos roles.                                                                                         |

## 404: algo que no existe o está fuera de tu alcance [#404-algo-que-no-existe-o-está-fuera-de-tu-alcance]

Una fila de otra organización y una que no existe responden lo mismo. La API
no dice cuál de las dos.

| Código                      | Cuándo                                                                       | Qué hacer                                                     |
| --------------------------- | ---------------------------------------------------------------------------- | ------------------------------------------------------------- |
| `ORGANIZATION_NOT_FOUND`    | La organización no existe o tu usuario no pertenece a ella. Nunca es un 403. | Toma el `organizationId` de `getMe` o de `listOrganizations`. |
| `MODULE_NOT_FOUND`          | El «módulo» no está en esa organización.                                     | Toma el id de `listModules` o de `getFloor`.                  |
| `OPERATION_NOT_FOUND`       | El «puesto» (`stationId` u `operationId`) no está en esa organización.       | Toma el id de `listStations` o de `listOrganizationStations`. |
| `OPERATOR_NOT_FOUND`        | El «operario» no está en esa organización.                                   | Toma el id de `listOperators`.                                |
| `REFERENCE_NOT_FOUND`       | La «referencia» no está en esa organización.                                 | Búscala con `searchReferences`, `getProgram` o `listOnHold`.  |
| `CATALOGUE_ENTRY_NOT_FOUND` | Esa entrada no está en el catálogo de la organización.                       | Toma el id de `listCatalogue`.                                |

## 409: todavía en curso, o ya cambió [#409-todavía-en-curso-o-ya-cambió]

| Código                    | Cuándo                                                                                                                               | Qué hacer                                                               |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------- |
| `IDEMPOTENCY_IN_PROGRESS` | La misma `Idempotency-Key` todavía se está respondiendo. Trae `Retry-After`.                                                         | Espera esos segundos y reintenta la misma solicitud con la misma clave. |
| `PREVIEW_STALE`           | En un borrado: la confirmación venció (dura 15 minutos) o lo que mostraba la vista previa cambió. `reason` es `expired` o `changed`. | Pide la vista previa otra vez y muéstrasela a la persona.               |

## 422: una escritura que no se puede hacer como llegó [#422-una-escritura-que-no-se-puede-hacer-como-llegó]

| Código                   | Cuándo                                                                                                                           | Qué hacer                                                                                                                                       |
| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `CONFIRMATION_REQUIRED`  | Un borrado sin el `confirmationToken` de su vista previa, o con uno que no es de ese borrado. `reason` es `missing` o `invalid`. | Llama `previewDeleteReference` o `previewDeleteCatalogueEntry`, muestra la respuesta a la persona y envía el `confirmationToken` sin cambiarlo. |
| `IDEMPOTENCY_KEY_REUSED` | Esa `Idempotency-Key` ya respondió otra solicitud.                                                                               | Usa una clave nueva para cada escritura distinta.                                                                                               |

## 429: el límite de uso [#429-el-límite-de-uso]

| Código         | Cuándo                                                                                                                | Qué hacer                                                                   |
| -------------- | --------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
| `RATE_LIMITED` | La credencial gastó un presupuesto de su límite. Trae el encabezado `Retry-After` y `retryAfterSeconds` en el cuerpo. | Espera esos segundos y reintenta. Mira [Límites de uso](/docs/rate-limits). |

## 503: la consulta tardó demasiado [#503-la-consulta-tardó-demasiado]

| Código              | Cuándo                                                                             | Qué hacer                |
| ------------------- | ---------------------------------------------------------------------------------- | ------------------------ |
| `ANALYTICS_TIMEOUT` | Un rango de `getEfficiency`, `getProduction` o `getBilling` pasó su tiempo máximo. | Pide un rango más corto. |

## Qué reintentar [#qué-reintentar]

* **Sí:** un 429, después de `Retry-After`. Un 409 `IDEMPOTENCY_IN_PROGRESS`,
  con la misma clave.
* **Sí, con cuidado:** una escritura que terminó en un tiempo de espera
  agotado o en un 5xx pudo haberse aplicado o no. Si la enviaste con una
  `Idempotency-Key`, reintenta esa misma solicitud con la misma clave: la API
  devuelve la primera respuesta, con `Idempotency-Replayed: true`, y no
  escribe dos veces. Por eso conviene enviar una clave nueva en cada
  escritura.
* **No:** cualquier otro 4xx. Repetir la misma solicitud responde lo mismo.

Una escritura también puede responder un rechazo de las reglas del panel, con
un `code` propio que no está en estas tablas. Trátalo por su estado HTTP y
muestra su `message`.
