# Convenciones
URL: https://docs.fluenta.io/docs/conventions

Las reglas que valen para todas las rutas: listas y páginas, ids, días y horas en el reloj de la planta, rangos, eficiencia, dinero y cambios compatibles.



Todas las rutas viven bajo `https://api.fluenta.io/v1/external` y responden
JSON. Esta página reúne lo que vale para todas.

## Listas y páginas [#listas-y-páginas]

Hay tres formas de lista. La página de cada operación dice cuál usa.

| Forma de la respuesta                                 | Operaciones                                                                                 | Tamaño                |
| ----------------------------------------------------- | ------------------------------------------------------------------------------------------- | --------------------- |
| `{ items, page, total }`                              | `listOrganizations`, `listOrganizationStations`, `searchReferences`, `listActivity`         | 50 por página         |
| `{ items, page, pageSize, total, from, to, groupBy }` | `getEfficiency`, `getProduction`, `getBilling`                                              | 100 por página        |
| Arreglo JSON, sin páginas                             | `listModules`, `listStations`, `listOperators`, `getProgram`, `listOnHold`, `listCatalogue` | Todo en una respuesta |

* `page` va en la consulta, empieza en 1 y vale 1 si no lo envías.
* `total` cuenta las filas de todas las páginas, no las de la página.
* `getBilling` agrega `currency` a su respuesta.
* En `listOrganizationStations` y en los rangos, una página después de la
  última responde `items` vacío, no un error.

```bash
curl 'https://api.fluenta.io/v1/external/organizations/42/stations?page=2' \
  -H 'Authorization: Bearer fluenta_live_xxxxxxxxxxxxxxxx'
```

```json
{
  "items": [{ "id": 412, "name": "Pegar cuello", "moduleId": 7 }],
  "page": 2,
  "total": 51
}
```

El ejemplo recorta los campos de cada «puesto».

## Ids [#ids]

* Todos los ids son números enteros: `organizationId`, `moduleId`,
  `stationId`, `operatorId`, `referenceId`, `templateId`.
* Un id sale siempre de una respuesta anterior. `organizationId` sale de
  `getMe` o de `listOrganizations`; no hay organización por defecto.
* Un id de otra organización responde el mismo 404 que uno que no existe.
* La única excepción es `listActivity`: cada fila se identifica con `idx`, un
  UUID.

Un «puesto» tiene dos nombres para el mismo número: `stationId` en las rutas y
`operationId` en las respuestas y en los filtros. El `id` que responde
`listStations` sirve en los dos lugares.

## Días, horas e instantes [#días-horas-e-instantes]

Un día es el día de la planta, en su «Zona horaria», nunca en UTC.

| Dato                                         | Formato                                                                                       | Reloj                                                            |
| -------------------------------------------- | --------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- |
| Día: `day`, `from`, `to`                     | `YYYY-MM-DD`                                                                                  | El de la planta: `timezone` en `getOrganization` y `getSettings` |
| Hora: `hour`, `currentHour`                  | Entero de 0 a 23                                                                              | El de la planta. 9 es de 09:00 a 09:59                           |
| Instante: `pausedAt`, `expiresAt`, `firstAt` | ISO 8601                                                                                      | UTC                                                              |
| Fechas de una OC: `dueDate`, `spliceDate`    | ISO 8601. Al escribirlas llevan su desfase (`Z` o `±HH:MM`), como `2026-11-28T00:00:00-05:00` | Al escribir, el desfase de la planta; en las respuestas, UTC     |

* `getOrganization` responde `today`: el hoy de la planta. Úsalo cuando alguien
  diga «hoy».
* Donde `day` es opcional y no lo envías, la API lee el hoy de la planta.
* Una fecha que no es un día real responde 400 `INVALID_DAY`.
* `getMyUsage` es la excepción: cuenta por días UTC.

## Rangos [#rangos]

Los rangos llevan `from` y `to`, los dos incluidos.

| Operaciones                                                                                                         | Máximo, contando los dos extremos |
| ------------------------------------------------------------------------------------------------------------------- | --------------------------------- |
| `getEfficiency`, `getProduction`, `getBilling`, `getOperatorReport`, `getModuleEfficiency`, `getOperatorEfficiency` | 92 días                           |
| `getPauses` («Pausas»)                                                                                              | 31 días                           |

Un rango más largo responde 400 `RANGE_TOO_LARGE`, con el tope en `maxDays`.
`from` después de `to` responde 400 `RANGE_INVALID`. Un día que la planta
todavía no vive no es un error: no tiene datos. Para una pregunta más larga,
divide el rango en varias solicitudes.

## Eficiencia y dinero [#eficiencia-y-dinero]

* **«Eficiencia»** es un porcentaje, no una razón de 0 a 1: unidades × 100 ÷
  meta, con 2 decimales. 100 es la meta exacta y puede pasar de 100. Es `null`
  cuando la meta es 0.
* **El dinero** va en unidades enteras de la moneda con hasta 2 decimales:
  pesos o euros, nunca centavos.
* **La moneda** es la de la planta, según su «país»: `EUR` para una planta en
  España, `COP` para las demás. Es un código ISO 4217. Viene en el `currency`
  de la respuesta cuando lo trae; si no, en `currency.code` de
  `getOrganization`.
* Una cifra de dinero puede ser `null`, y no 0, cuando nada se facturó. La
  descripción de cada campo lo dice.

Cada cifra es la del panel para los mismos datos. Cítala como llega en vez de
recalcularla.

## Caché [#caché]

Toda respuesta lleva `Cache-Control: no-store`. No la guardes en un proxy ni
en una caché compartida.

## Cambios compatibles [#cambios-compatibles]

La API crece por adición bajo `/v1/external`: rutas nuevas, parámetros
opcionales nuevos y campos nuevos en las respuestas. Escribe tu programa para
que ignore los campos que no conoce.

Un `code` de error conserva su nombre una vez publicado, y los permisos `read`
y `program` no cambian de nombre. El [registro de cambios](/docs/changelog)
anuncia lo demás.
