# Introducción
URL: https://docs.fluenta.io/docs
Qué te da la API, con qué credencial se usa y por dónde empezar.
La API de Fluenta te da por HTTP lo que ves en el panel: la planta, el
Programa, los tableros y los reportes de las organizaciones a las que
perteneces, con las mismas cifras. Sirve para tus propios programas y para
aplicaciones de IA.
Todas las rutas cuelgan de una sola URL base:
```
https://api.fluenta.io/v1/external
```
## Lo que conviene saber antes de empezar [#lo-que-conviene-saber-antes-de-empezar]
* **Dos credenciales.** Un token de API, que creas en «Configuración» →
«Tokens de API» y funciona en los planes Light y Pro, o una conexión, que es una
aplicación de IA autorizada con «Conectar con IA» y pide Pro. Las dos viajan
en el encabezado `Authorization`. Sigue en [Autenticación](./authentication.mdx).
* **Actúas como tú.** Cada llamada usa el rol que tienes en el panel, leído de
nuevo en cada solicitud, y nombra la organización en la ruta. Sigue en
[Organizaciones](./organizations.mdx).
* **El plan decide.** La API le da a cada organización lo que su plan le da en
el panel. Sigue en [Planes](./plans.mdx).
* **Permisos.** `read` («Leer») viene siempre. `program` («Gestionar el
Programa») hace falta para escribir en el Programa y en el catálogo de
referencias. Sigue en [Permisos](./permissions.mdx).
* **Errores.** Toda respuesta de error tiene la forma
`{ statusCode, code, message }`. Decide por `code`. Sigue en
[Errores](./errors.mdx).
## Por dónde empezar [#por-dónde-empezar]
Crea un token de API y haz tu primera solicitud.
Una página por operación, generada desde el documento OpenAPI.
El índice en llms.txt, cada página en Markdown y el documento OpenAPI.
El documento OpenAPI completo está en [`/openapi.json`](/openapi.json), y el
índice para agentes en [`/llms.txt`](/llms.txt).
---
# Inicio rápido
URL: https://docs.fluenta.io/docs/quickstart
De cero a tu primera respuesta en cinco pasos, con curl, JavaScript y Python.
Al terminar esta página habrás creado un token de API, sabrás a qué
organizaciones llega y habrás leído los módulos y una cifra de eficiencia de
una de ellas.
La URL base es `https://api.fluenta.io/v1/external`. Cada solicitud lleva el
token en el encabezado `Authorization`.
## 1. Crea un token [#1-crea-un-token]
1. Entra a `app.fluenta.io` y abre «Configuración» → «Tokens de API».
2. Pulsa «Crear token». Escribe un «Nombre», deja el permiso «Leer» y elige un
«Vencimiento».
3. En la ventana «Token creado», pulsa «Copiar token». El token se muestra una
sola vez.
Guárdalo en una variable de entorno:
```bash
export FLUENTA_TOKEN='fluenta_live_xxxxxxxxxxxxxxxx'
```
## 2. Comprueba el token [#2-comprueba-el-token]
```bash
curl https://api.fluenta.io/v1/external/me \
-H "Authorization: Bearer $FLUENTA_TOKEN"
```
```json
{
"user": { "name": "Ana Pérez", "roles": ["ADMIN"] },
"connection": {
"app": "Reporte semanal",
"scopes": ["read"],
"expiresAt": "2027-02-09T15:04:05.000Z"
},
"credential": {
"kind": "personal",
"name": "Reporte semanal",
"prefix": "fluenta_live_xxx",
"expiresAt": "2027-02-09T15:04:05.000Z"
},
"rateLimit": { "limit": 60, "remaining": 59, "reset": 1794355260 },
"organizations": [
{ "id": 42, "name": "Confecciones Ejemplo", "connectorEnabled": true }
],
"organizationsTotal": 1,
"reachesAllOrganizations": false
}
```
`user` es la persona como la que actúa el token: tú. `credential.kind` es
`personal` para un token de API.
## 3. Lista tus organizaciones [#3-lista-tus-organizaciones]
`/me` trae las primeras 50. Esta ruta las pagina y las busca por nombre:
```bash
curl https://api.fluenta.io/v1/external/organizations \
-H "Authorization: Bearer $FLUENTA_TOKEN"
```
```json
{
"items": [
{ "id": 42, "name": "Confecciones Ejemplo", "connectorEnabled": true }
],
"page": 1,
"total": 1
}
```
El `id` es el `organizationId` de todas las demás rutas.
## 4. Lee los módulos de una organización [#4-lee-los-módulos-de-una-organización]
```bash
curl https://api.fluenta.io/v1/external/organizations/42/modules \
-H "Authorization: Bearer $FLUENTA_TOKEN"
```
```json
[
{ "id": 7, "name": "Módulo 1", "position": 0, "stationCount": 12, "colorHue": null }
]
```
`GET /organizations/42/floor` responde los módulos con sus puestos en una sola
respuesta. Cuenta contra un límite de uso más bajo: mira
[Límites de uso](/docs/rate-limits).
## 5. Lee una cifra [#5-lee-una-cifra]
La eficiencia de la planta hoy, desde el tablero en vivo. Esta ruta está en
todos los planes:
```bash
curl https://api.fluenta.io/v1/external/organizations/42/board \
-H "Authorization: Bearer $FLUENTA_TOKEN"
```
La respuesta, aquí sin su lista `modules` (una fila por módulo):
```json
{
"day": "2026-11-10",
"plant": {
"produced": 1840,
"goal": 2000,
"efficiency": 92,
"modules": 1,
"producing": 1,
"lastClosed": { "day": "2026-11-09", "efficiency": 88.5 },
"closed": { "average": 91.3, "modules": 1 }
}
}
```
`plant.efficiency` es un porcentaje: 92 quiere decir 92% de la meta.
### Opcional: un rango de días [#opcional-un-rango-de-días]
La eficiencia día por día sale de una ruta de reportes. Los días van como
`YYYY-MM-DD`, en el reloj de la planta:
```bash
curl "https://api.fluenta.io/v1/external/organizations/42/analytics/efficiency?from=2026-11-10&to=2026-11-10" \
-H "Authorization: Bearer $FLUENTA_TOKEN"
```
```json
{
"from": "2026-11-10",
"to": "2026-11-10",
"groupBy": "day",
"page": 1,
"pageSize": 100,
"total": 1,
"items": [
{ "day": "2026-11-10", "units": 1840, "goal": 2000, "efficiency": 92 }
]
}
```
Esta ruta sigue el plan. Una organización cuyo plan no incluye reportes
responde 403 con `"code": "PLAN_LIMIT"`: el token está bien, y `/board` sigue
respondiendo. Los dos programas de abajo leen `/board` primero, luego intentan
el rango y avisan cuando el plan no lo incluye. El detalle está en
[Planes](/docs/plans).
## Lo mismo en JavaScript [#lo-mismo-en-javascript]
Guárdalo como `inicio.mjs` y ejecútalo con Node.js:
```js
const BASE = 'https://api.fluenta.io/v1/external';
const headers = { Authorization: `Bearer ${process.env.FLUENTA_TOKEN}` };
async function get(path) {
const response = await fetch(`${BASE}${path}`, { headers });
const body = await response.json();
if (!response.ok) {
const error = new Error(`${body.code}: ${body.message}`);
error.code = body.code;
throw error;
}
return body;
}
const me = await get('/me');
const { items } = await get('/organizations');
const organizationId = items[0].id;
const modules = await get(`/organizations/${organizationId}/modules`);
const board = await get(`/organizations/${organizationId}/board`);
console.log(me.user.name, modules.length, board.plant.efficiency);
// Opcional: un rango de días. Esta ruta sigue el plan.
try {
const range = await get(
`/organizations/${organizationId}/analytics/efficiency?from=2026-11-10&to=2026-11-10`
);
console.log(range.items);
} catch (error) {
if (error.code !== 'PLAN_LIMIT') throw error;
console.log('Este plan no incluye reportes.');
}
```
## Lo mismo en Python [#lo-mismo-en-python]
```python
import os
import requests
BASE = "https://api.fluenta.io/v1/external"
session = requests.Session()
session.headers["Authorization"] = f"Bearer {os.environ['FLUENTA_TOKEN']}"
class ApiError(RuntimeError):
def __init__(self, body):
super().__init__(f"{body['code']}: {body['message']}")
self.code = body["code"]
def get(path, params=None):
response = session.get(f"{BASE}{path}", params=params, timeout=30)
body = response.json()
if not response.ok:
raise ApiError(body)
return body
me = get("/me")
organization_id = get("/organizations")["items"][0]["id"]
modules = get(f"/organizations/{organization_id}/modules")
board = get(f"/organizations/{organization_id}/board")
print(me["user"]["name"], len(modules), board["plant"]["efficiency"])
# Opcional: un rango de días. Esta ruta sigue el plan.
try:
efficiency = get(
f"/organizations/{organization_id}/analytics/efficiency",
{"from": "2026-11-10", "to": "2026-11-10"},
)
print(efficiency["items"])
except ApiError as error:
if error.code != "PLAN_LIMIT":
raise
print("Este plan no incluye reportes.")
```
## Qué leer después [#qué-leer-después]
* [Autenticación](/docs/authentication): guardar, rotar y revocar el token.
* [Permisos](/docs/permissions): qué hace falta para escribir en el Programa.
* [Organizaciones y recursos](/docs/organizations): cómo pasan los ids de una
llamada a la siguiente.
* [Planes](/docs/plans): qué rutas responde cada plan.
* [Errores](/docs/errors) y [Convenciones](/docs/conventions).
* La [referencia](/docs/reference) tiene una página por operación.
---
# Autenticación
URL: https://docs.fluenta.io/docs/authentication
Las dos credenciales de la API, y cómo crear, guardar, rotar y revocar un token de API.
Aquí verás qué credencial te conviene, cómo crear un token de API, dónde
guardarlo, cómo rotarlo y revocarlo, y qué significa un 401.
## Dos credenciales [#dos-credenciales]
Las dos se envían igual: `Authorization: Bearer fluenta_live_…`.
| | Token de API | Conexión |
| -------------- | ------------------------------------------------ | -------------------------------------------------------------------------------- |
| Quién la crea | Tú, a mano, en «Configuración» → «Tokens de API» | Una aplicación de IA, como Claude o ChatGPT, cuando pulsas «Permitir» |
| Para qué sirve | Tus propios programas | Usar Fluenta desde la aplicación de IA, por MCP, en `https://app.fluenta.io/mcp` |
| Planes | Light y Pro | Pro: es la función «Conectar con IA» |
| Cómo termina | Vence, o pulsas «Revocar» | Pulsas «Desconectar» |
Las dos actúan como tú: llegan a tus organizaciones, con tu rol. `GET /me`
dice cuál presentaste en `credential.kind`: `personal` es un token de API y
`oauth` es una conexión.
Usa un token de API para un programa tuyo: un script, un reporte semanal o
una hoja de cálculo. Usa una conexión cuando quien pregunta es una aplicación de IA: mira
[IA](/docs/ai).
## Crear un token [#crear-un-token]
1. Entra a `app.fluenta.io` y abre «Configuración» → «Tokens de API».
2. Pulsa «Crear token».
3. Escribe un «Nombre» que te diga dónde lo usas. Admite hasta 64 caracteres.
4. En «Permisos», «Leer» viene siempre. Marca «Gestionar el Programa» solo si
el programa va a escribir: mira [Permisos](/docs/permissions).
5. Elige el «Vencimiento»: «Nunca», «30 días», «90 días» o «1 año».
6. Pulsa «Crear token» y copia el token con «Copiar token».
El token se muestra una sola vez. Fluenta guarda solo su hash, así que no
puede mostrártelo de nuevo: si lo pierdes, crea otro.
Puedes tener hasta 10 tokens activos y crear hasta 10 por hora.
## Enviar el token [#enviar-el-token]
```bash
curl https://api.fluenta.io/v1/external/me \
-H 'Authorization: Bearer fluenta_live_xxxxxxxxxxxxxxxx'
```
## Guardar el token [#guardar-el-token]
* Guárdalo solo del lado del servidor: en una variable de entorno o en el
gestor de secretos de tu plataforma.
* No lo pongas en código que corre en un navegador ni lo subas a un
repositorio.
* No lo pongas en una URL: la API solo lo lee del encabezado
`Authorization`.
* Usa un token por programa. Así revocas uno sin detener los demás.
* «Tus tokens» muestra los primeros 16 caracteres de cada uno. Sirven para
distinguirlos; no alcanzan para autenticarse.
Un token que no vence sirve hasta que lo revoques. Restablecer tu contraseña
no revoca tus tokens.
## Rotar un token [#rotar-un-token]
No hay una acción de rotar:
1. Crea un token nuevo con los mismos permisos.
2. Cambia el secreto en tu programa y despliégalo.
3. Mira «Último uso» del token viejo en «Tus tokens» para confirmar que ya
nadie lo usa.
4. Pulsa «Revocar» en el token viejo.
Un token no gana permisos después de creado. Si necesitas «Gestionar el
Programa», crea uno nuevo.
## Revocar un token [#revocar-un-token]
Pulsa «Revocar» en la fila del token y confirma. El efecto es inmediato: la
siguiente solicitud con ese token responde 401.
## Vencimiento [#vencimiento]
`GET /me` responde cuándo vence en `credential.expiresAt` (ISO 8601, UTC). Es
`null` en un token que no vence. Después de esa fecha el token responde 401 y
no se puede renovar: crea otro.
## Qué significa un 401 [#qué-significa-un-401]
```json
{
"statusCode": 401,
"code": "INVALID_API_TOKEN",
"message": "La credencial no es válida o venció. Si es una app conectada, vuelve a conectarla; si es un token de API, crea uno nuevo en Configuración."
}
```
La respuesta es la misma en todos estos casos, y no dice cuál fue:
* falta el encabezado `Authorization`, o no trae un token de la API;
* el token no existe, venció o fue revocado;
* tu usuario ya no está activo.
Reintentar no sirve. Revisa el encabezado y, si está bien, crea un token
nuevo. Un 403 o un 404 no son problemas del token: mira
[Errores](/docs/errors).
## El token solo abre esta API [#el-token-solo-abre-esta-api]
Un token de API se acepta bajo `/v1/external` y en ningún otro lugar: la API
que usa el panel lo rechaza. Lo contrario también vale: la sesión del panel no
se acepta en `/v1/external`.
---
# Permisos
URL: https://docs.fluenta.io/docs/permissions
Los dos permisos de un token de API, qué rutas pide cada uno y por qué ninguno supera tu rol.
Aquí verás qué puede hacer un token con cada permiso, qué rutas piden cuál y
qué más revisa la API antes de aceptar una escritura.
## Los dos permisos [#los-dos-permisos]
| Permiso | En la API | Cuándo lo tiene el token | Qué permite |
| ----------------------- | --------- | ----------------------------------- | ---------------------------------------------------------------------------------- |
| «Leer» | `read` | Siempre | Ver lo que tú ves en Fluenta: el tablero, el Programa y los reportes |
| «Gestionar el Programa» | `program` | Solo si lo marcas al crear el token | Crear, cambiar, mover y borrar OC, las metas por hora y el catálogo de referencias |
Los permisos se eligen al crear el token y no cambian después. Para sumar
«Gestionar el Programa», crea un token nuevo.
`GET /me` responde los permisos del token en `connection.scopes`.
## Qué permiso pide cada ruta [#qué-permiso-pide-cada-ruta]
Todas las rutas de esta tabla van bajo
`/v1/external/organizations/{organizationId}`, salvo las de cuenta.
| Grupo | Rutas | Permiso |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- |
| Cuenta | `GET /me`, `GET /me/usage`, `GET /organizations` | `read` |
| Lecturas | Todas las rutas `GET`: organización, planta, tableros, Programa, catálogo, reportes y actividad | `read` |
| Vistas previas de borrado | `POST /references/{referenceId}/delete-preview`, `POST /catalogue/{templateId}/delete-preview` | `read` |
| Escrituras del Programa | `POST /references`, `POST /references/from-catalogue`, `PATCH` y `DELETE /references/{referenceId}`, `PATCH …/hold`, `PATCH …/reactivate`, `PATCH …/sacrifice`, `POST …/split`, `POST …/arrive`, `PATCH /modules/{moduleId}/program/order`, `PUT` y `DELETE /modules/{moduleId}/days/{day}/references/{referenceId}/goal` | `program` |
| Escrituras del catálogo | `POST /catalogue`, `PATCH` y `DELETE /catalogue/{templateId}` | `program` |
En el documento OpenAPI, cada operación nombra su permiso en
`x-required-scope`. La [referencia](/docs/reference) lo muestra por operación.
## Si falta el permiso [#si-falta-el-permiso]
La API responde 403 `INSUFFICIENT_SCOPE` y nombra el permiso que falta en
`scope`:
```json
{
"statusCode": 403,
"code": "INSUFFICIENT_SCOPE",
"scope": "program",
"message": "Este token de API no tiene el permiso «Gestionar el Programa». Crea uno nuevo con ese permiso en Configuración."
}
```
No se cambió nada. Crea un token con ese permiso y repite la solicitud.
## Un permiso nunca supera tu rol [#un-permiso-nunca-supera-tu-rol]
Un permiso no te da nada que tu rol no te dé en el panel. La API lee tu rol en
cada solicitud: si pierdes un rol en el panel, tus tokens lo pierden en la
siguiente solicitud.
Cada escritura pide el mismo rol que su pantalla del panel:
| Escritura | Rol que pide |
| --------------------------------------------------------------------------------------------------------------------------- | --------------------- |
| Crear, editar y borrar una OC; agregar OC del catálogo; escribir en el catálogo | `ADMIN` o `MODERATOR` |
| Reordenar el Programa; pausar, reactivar y dividir una OC; marcar que llegó; el descuento por sacrificio; las metas del día | `ADMIN` |
Si tu rol no alcanza, la API responde 403 `ROLE_NOT_ALLOWED`. El cuerpo trae
`roles`, la lista de roles que sí pueden hacer ese cambio. Tampoco se cambió
nada.
Para saber qué puedes hacer en una organización antes de escribir:
```bash
curl https://api.fluenta.io/v1/external/organizations/42/access \
-H 'Authorization: Bearer fluenta_live_xxxxxxxxxxxxxxxx'
```
```json
{
"organizationId": 42,
"roles": ["ADMIN"],
"scopes": ["read", "program"],
"connectorEnabled": true
}
```
## Tres revisiones antes de escribir [#tres-revisiones-antes-de-escribir]
Una escritura pasa solo si se cumplen las tres:
* El token tiene `program`. Si no, 403 `INSUFFICIENT_SCOPE`.
* Tu rol hace ese cambio en el panel. Si no, 403 `ROLE_NOT_ALLOWED`.
* El plan de la organización incluye la función. Si no, 403 `PLAN_LIMIT`:
mira [Planes](/docs/plans).
Decide por `code`, no por el estado HTTP: los tres casos responden 403.
[Errores](/docs/errors) tiene la lista completa.
---
# Organizaciones y recursos
URL: https://docs.fluenta.io/docs/organizations
Cómo se encadenan las llamadas, de tu usuario a tus organizaciones y de una organización a sus módulos, puestos y operarios.
Aquí verás qué recursos tiene la API, de qué llamada sale cada id y cómo
trabajar con una cuenta que pertenece a varias organizaciones.
## El recorrido [#el-recorrido]
```text
usuario (tu token)
└── organizaciones
├── módulos
│ └── puestos
└── operarios
```
Un token es personal: actúa como tu usuario y llega a todas las
organizaciones a las que perteneces. No hay organización por defecto: cada
ruta nombra la suya en la ruta, como `organizationId`.
## De dónde sale cada id [#de-dónde-sale-cada-id]
| Recurso | Qué es | Su id sale de | Se usa como |
| ------------ | -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------- |
| Organización | Una planta, un taller o la cuenta de una marca | `GET /me` → `organizations[].id`, `GET /organizations` → `items[].id` | `organizationId` en la ruta |
| Módulo | Una línea de producción | `GET …/modules` → `[].id`, `GET …/floor` → `modules[].id` | `moduleId` en la ruta o como filtro |
| Puesto | Una estación numerada de un módulo | `GET …/modules/{moduleId}/stations` → `[].id`, `GET …/stations` → `items[].id`, `GET …/floor` → `modules[].stations[].id` | `stationId` en la ruta, `operationId` como filtro |
| Operario | La persona que registra su producción en un puesto | `GET …/operators` → `[].id`, el campo `operator.id` de un puesto | `operatorId` en la ruta o como filtro |
El id de un puesto tiene dos nombres. En la ruta es `stationId`. En los
filtros de los reportes es `operationId`, y en sus filas viene en
`operation.id`. Es el mismo número.
Cada operario trae en `station` el puesto donde está sentado, o `null` si no
está en ninguno. Cada puesto trae en `operator` al operario sentado, o `null`
si está vacío.
## Un ejemplo encadenado [#un-ejemplo-encadenado]
```bash
# 1. Tus organizaciones → organizationId
curl https://api.fluenta.io/v1/external/organizations \
-H "Authorization: Bearer $FLUENTA_TOKEN"
# 2. Los módulos de la organización 42 → moduleId
curl https://api.fluenta.io/v1/external/organizations/42/modules \
-H "Authorization: Bearer $FLUENTA_TOKEN"
# 3. Los puestos del módulo 7 → stationId
curl https://api.fluenta.io/v1/external/organizations/42/modules/7/stations \
-H "Authorization: Bearer $FLUENTA_TOKEN"
# 4. El puesto 311, como lo lee el tablero ahora
curl https://api.fluenta.io/v1/external/organizations/42/stations/311 \
-H "Authorization: Bearer $FLUENTA_TOKEN"
# 5. La eficiencia de ese puesto en un rango: el mismo id, como operationId
curl "https://api.fluenta.io/v1/external/organizations/42/analytics/efficiency?from=2026-11-03&to=2026-11-07&operationId=311" \
-H "Authorization: Bearer $FLUENTA_TOKEN"
```
Los pasos 1 a 4 responden en todos los planes. El paso 5 sigue el plan: mira
[Planes](/docs/plans).
`GET …/floor` responde los módulos con sus puestos en una sola llamada. Úsala
cuando necesites toda la planta, no para leer un solo puesto: cuenta contra un
límite de uso más bajo.
## Cuentas con varias organizaciones [#cuentas-con-varias-organizaciones]
Una persona puede pertenecer a varias organizaciones: una marca y sus
talleres, o alguien que trabaja para dos plantas. El mismo token llega a
todas.
* `GET /me` trae las primeras 50, por nombre, y el total en
`organizationsTotal`.
* `GET /organizations` las pagina de a 50 con `page` y las busca por nombre
con `search`.
* Cada organización responde según su propio plan. `connectorEnabled` dice si
esa organización responde a tu credencial.
* Cada organización tiene su propio límite de uso, y hay uno más para el
token entre todas: mira [Límites de uso](/docs/rate-limits).
* Cada organización tiene su propio reloj. Los días y las horas de sus
respuestas van en su zona horaria: `timezone` y `today` en
`GET /organizations/{organizationId}`.
* Tu rol y tus permisos en una organización están en
`GET /organizations/{organizationId}/access`.
Para recorrer una marca y sus talleres, lista las organizaciones y repite las
mismas llamadas con cada `organizationId`.
## Lo que no es tuyo responde 404 [#lo-que-no-es-tuyo-responde-404]
Una organización a la que no perteneces responde igual que una que no existe:
```json
{
"statusCode": 404,
"code": "ORGANIZATION_NOT_FOUND",
"message": "Organización no encontrada"
}
```
Nunca responde 403. Lo mismo pasa dentro de una organización: un módulo, un
puesto o un operario de otra organización responde 404 con
`MODULE_NOT_FOUND`, `OPERATION_NOT_FOUND` u `OPERATOR_NOT_FOUND`, igual que un
id que no existe.
Usa siempre ids que salieron de una respuesta de la API. No los construyas ni
los adivines. [Errores](/docs/errors) tiene el resto de los códigos.
---
# Planes Light y Pro
URL: https://docs.fluenta.io/docs/plans
Qué rutas responde cada plan, cómo leer el plan de una organización y qué hacer con un 403 PLAN_LIMIT.
Aquí verás qué alcanza cada plan por la API, cómo leer el plan de una
organización antes de llamar y qué significa `PLAN_LIMIT`.
## La regla [#la-regla]
La API le da a cada organización lo que su plan le da en el panel. Un token de
API funciona en el Plan Light y en el Plan Pro. En Light, las rutas de reportes
responden 403 `PLAN_LIMIT`, igual que esos reportes están bloqueados en el
panel.
El plan es de la organización, no del token. Si tu token llega a una
organización Pro y a una Light, cada una responde según su plan.
## Qué responde cada plan [#qué-responde-cada-plan]
Las rutas van bajo `/v1/external/organizations/{organizationId}`, salvo las de
cuenta. La columna «Clave» es el campo de `features` que decide el grupo.
| Grupo | Rutas | Clave | Light | Pro |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ | -------------- | --- |
| Cuenta | `/me`, `/me/usage`, `/organizations` | — | Sí | Sí |
| Organización | la organización, `/access`, `/plan`, `/settings`, `/categories` | — | Sí | Sí |
| Planta | `/floor`, `/modules`, `/modules/{moduleId}`, `…/rest-times`, `…/stations`, `/stations`, `/stations/{stationId}`, `/operators`, `/operators/presence`, `/operators/{operatorId}` | — | Sí | Sí |
| Tablero en vivo | `/board` | — | Sí | Sí |
| Programa y catálogo | `/modules/{moduleId}/program`, `/references…`, `/catalogue…`, con sus escrituras | — | Sí | Sí |
| Actividad | `/activity` | — | Sí | Sí |
| Reportes generales | `/analytics/day-report`, `/analytics/leaderboard`, `/analytics/efficiency`, `/analytics/efficiency/modules`, `/analytics/month`, `/stations/{stationId}/days/{day}/report` | `reports` | Solo versión 1 | Sí |
| Reportes por módulo | `/modules/{moduleId}/days/{day}/board`, `…/days/{day}/history`, `/modules/{moduleId}/analytics/efficiency`, `/analytics/production`, y escribir las metas del día (`PUT` y `DELETE …/goal`) | `moduleReports` | No | Sí |
| Reportes por operario | `/analytics/operators/{operatorId}`, `/operators/{operatorId}/analytics/efficiency` | `operatorReports` | No | Sí |
| Facturación | `/analytics/billing`, `/analytics/billing/modules` | `billingAnalytics` | No | Sí |
| Analítica | `/analytics/activity`, `/analytics/hourly`, `/analytics/operators`, `/analytics/consistency`, `/stations/{stationId}/analytics/events` | `analytics` | No | Sí |
| Pausas | `/analytics/pauses` | `pauses` | No | Sí |
En Light:
* **Versión del plan.** Un Light de versión 1 conserva los reportes generales;
uno de versión 2 no tiene ninguno. `planVersion` lo dice, y nunca cambia.
* **Programa.** El Programa de un módulo admite hasta 3 referencias:
`features.maxReferencesPerModule`. En Pro es `null`, sin límite.
* **Límite de uso.** Un token hace 60 solicitudes por minuto en una
organización Light y 120 en una Pro: mira [Límites de uso](/docs/rate-limits).
## Lee el plan antes de llamar [#lee-el-plan-antes-de-llamar]
`GET …/plan` responde en todos los planes:
```bash
curl https://api.fluenta.io/v1/external/organizations/42/plan \
-H 'Authorization: Bearer fluenta_live_xxxxxxxxxxxxxxxx'
```
```json
{
"tier": "light",
"planVersion": 2,
"features": {
"reports": false,
"billingAnalytics": false,
"moduleReports": false,
"operatorReports": false,
"analytics": false,
"pace": false,
"rework": false,
"pauses": false,
"operatorPicksReference": false,
"referenceColor": false,
"aiConnector": false,
"maxReferencesPerModule": 3,
"publicApi": true
},
"rateLimits": {
"windowSeconds": 60,
"organization": 60,
"heavy": 20,
"credential": 300,
"account": 60
}
}
```
Si la clave de un grupo es `true`, sus rutas responden. Si es `false`,
responden 403 `PLAN_LIMIT`.
## PLAN_LIMIT [#plan_limit]
```json
{
"statusCode": 403,
"code": "PLAN_LIMIT",
"feature": "reports",
"message": "Tu plan no incluye esta función"
}
```
`feature` nombra la clave que falta. No es un error del token ni de tus
permisos: reintentar no lo cambia. Omite esa cifra o lee una ruta que sí esté
en el plan: `/board` trae la eficiencia del día en todos los planes.
## Prueba y exenta [#prueba-y-exenta]
* **Prueba.** Una organización en su mes de prueba responde como el plan que
está probando: `tier` es ese plan. Una prueba de Pro alcanza todas las
rutas.
* **Exenta.** Una organización exenta no se factura. Por la API responde como
Pro.
* **Suscripción pausada.** Una organización bloqueada responde 402
`ORG_BLOCKED` en todas sus rutas.
## «Conectar con IA» es aparte [#conectar-con-ia-es-aparte]
«Conectar con IA» es una función del Plan Pro: permite que una aplicación de
IA, como Claude o ChatGPT, llegue a la organización con una conexión. No tiene
que ver con los tokens de API. En una organización Light, una conexión
responde 403 `PLAN_LIMIT` con `feature: "aiConnector"` en todas las rutas; un
token de API sigue funcionando. Mira [IA](/docs/ai) y
[Autenticación](/docs/authentication).
---
# 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.
---
# 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`.
---
# Límites de uso
URL: https://docs.fluenta.io/docs/rate-limits
Cuántas solicitudes por minuto acepta la API por credencial y por plan, los encabezados X-RateLimit, el 429 y cómo reintentar bien.
La API cuenta las solicitudes de cada credencial en ventanas de 60 segundos.
Al pasar de un presupuesto responde 429 `RATE_LIMITED` y te dice cuánto
esperar.
## Presupuestos [#presupuestos]
Una solicitud se cuenta contra todos los presupuestos que le aplican.
| Presupuesto | Qué cuenta | Light | Pro |
| ------------------ | ------------------------------------------------------------------------ | ----- | --- |
| Por organización | Las solicitudes de una credencial en una organización, en cualquier ruta | 60 | 120 |
| Rutas pesadas | Esa misma pareja, en las rutas pesadas | 20 | 20 |
| Toda la credencial | Las solicitudes de una credencial en todas sus organizaciones, sumadas | 300 | 300 |
| Rutas de cuenta | `getMe`, `getMyUsage` y `listOrganizations`, que no nombran organización | 60 | 60 |
* Los números son solicitudes por ventana de 60 segundos.
* Light o Pro es el plan de la organización de la ruta. Una prueba de Pro y
una organización exenta cuentan como Pro.
* Una solicitud pesada gasta uno del presupuesto pesado y uno del presupuesto
por organización.
* Las rutas de cuenta se cuentan aparte de los otros tres presupuestos.
* El límite es por credencial: cada token de API tiene sus propios
presupuestos.
`getPlan` responde los límites de una organización en `rateLimits`:
`windowSeconds`, `organization`, `heavy`, `credential` y `account`.
## Rutas pesadas [#rutas-pesadas]
Son las que llevan `x-rate-limit-heavy: true` en el documento OpenAPI:
| Operación | Qué lee |
| ---------------------------------------------- | ---------------------------------------------------------------- |
| `getFloor` | La planta entera en una respuesta |
| `getEfficiency`, `getProduction`, `getBilling` | Una cifra sobre un rango de días |
| `getOperatorReport` | El reporte de un «operario» sobre un rango |
| `getModuleEfficiency`, `getOperatorEfficiency` | La «Eficiencia» de un «módulo» o de un «operario» sobre un rango |
| `getMonth` | El mes |
| `getPauses` | El reporte de «Pausas» |
Salvo `getFloor`, son rutas de reporte: responden según el plan de la
organización. Mira [Planes](/docs/plans).
## La ventana [#la-ventana]
La ventana es fija. Se abre con la primera solicitud de un presupuesto y
termina 60 segundos después, sin importar cuántas llegaron en medio. Al
terminar, la cuenta vuelve a empezar.
## Encabezados [#encabezados]
Toda respuesta los lleva, también un error:
| Encabezado | Valor |
| ----------------------- | ---------------------------------------------------------------------- |
| `X-RateLimit-Limit` | Solicitudes por ventana del presupuesto que estás más cerca de gastar |
| `X-RateLimit-Remaining` | Solicitudes que le quedan a ese presupuesto en su ventana. 0 en un 429 |
| `X-RateLimit-Reset` | Cuándo termina esa ventana, en segundos epoch (UTC) |
| `Retry-After` | Solo en un 429: segundos que debes esperar |
`getMe` responde lo mismo en `rateLimit` (`limit`, `remaining`, `reset`) para
el presupuesto de las rutas de cuenta.
## El 429 [#el-429]
```text
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1794499260
Retry-After: 23
```
```json
{
"statusCode": 429,
"code": "RATE_LIMITED",
"retryAfterSeconds": 23,
"message": "Demasiadas solicitudes. Espera 23 segundos antes de volver a intentar."
}
```
`retryAfterSeconds` repite el valor de `Retry-After`. La misma llamada antes
de ese tiempo vuelve a ser rechazada.
## Qué cuenta y qué no [#qué-cuenta-y-qué-no]
* **Los errores también cuentan.** Una solicitud con una credencial válida
gasta su presupuesto aunque la respuesta sea un 4xx.
* Un 403 `INSUFFICIENT_SCOPE` y un 404 `ORGANIZATION_NOT_FOUND` se cuentan
contra el presupuesto de las rutas de cuenta, sea cual sea la ruta.
* **Un 429 no cuenta contra nada.** Seguir pidiendo no alarga tu ventana.
* Un 401 `INVALID_API_TOKEN` tampoco cuenta: no hay credencial verificada a
la cual cargarlo.
## Reintentar bien [#reintentar-bien]
Espera lo que dice `Retry-After` y pon un tope de intentos. Lee el token de
una variable de entorno, no del código.
```js
const BASE = 'https://api.fluenta.io/v1/external';
async function fluenta(path, maxRetries = 5) {
for (let attempt = 0; ; attempt += 1) {
const response = await fetch(`${BASE}${path}`, {
headers: { Authorization: `Bearer ${process.env.FLUENTA_TOKEN}` },
});
if (response.status !== 429) return response;
if (attempt >= maxRetries) throw new Error('Rate limited: retries spent');
const seconds = Number(response.headers.get('Retry-After')) || 1;
await new Promise((resolve) => setTimeout(resolve, seconds * 1000));
}
}
const response = await fluenta('/organizations/42/floor');
```
```python
import os
import time
import requests
BASE = "https://api.fluenta.io/v1/external"
def fluenta(path, max_retries=5):
headers = {"Authorization": f"Bearer {os.environ['FLUENTA_TOKEN']}"}
for attempt in range(max_retries + 1):
response = requests.get(f"{BASE}{path}", headers=headers, timeout=30)
if response.status_code != 429:
return response
if attempt < max_retries:
time.sleep(int(response.headers.get("Retry-After", "1")))
raise RuntimeError("Rate limited: retries spent")
response = fluenta("/organizations/42/floor")
```
Para no llegar al 429, mira `X-RateLimit-Remaining`: cuando llegue a 0, espera
hasta `X-RateLimit-Reset` antes de la siguiente solicitud.
## Ver tu propio uso [#ver-tu-propio-uso]
`getMyUsage` responde lo que tu credencial pidió a la API:
```bash
curl 'https://api.fluenta.io/v1/external/me/usage?days=7' \
-H 'Authorization: Bearer fluenta_live_xxxxxxxxxxxxxxxx'
```
* `days` va de 1 a 90 y vale 30 si no lo envías. El uso se guarda 90 días.
* `totals` suma el periodo, `endpoints` lo separa por método y ruta, y `days`
por día.
* Cada uno trae `requests` (rechazos incluidos), `errors` (las respuestas 400
o más), `averageDurationMs`, `maxDurationMs` y `lastAt`.
* Los días son UTC, no los de la planta.
* Solo cuenta esta credencial, en todas sus organizaciones. Las cifras pueden
ir unos segundos atrás.
---
# Usar la API con una IA
URL: https://docs.fluenta.io/docs/ai
Dos formas de darle Fluenta a una IA: un agente con un token de API y esta documentación, o «Conectar con IA» para Claude y ChatGPT.
Una IA puede usar Fluenta de dos formas. Las dos actúan como tú, con tu rol.
| | Agente con un token de API | «Conectar con IA» |
| ----------------------- | --------------------------------------- | ---------------------------------------- |
| Para | Cualquier agente que hace llamadas HTTP | Claude y ChatGPT |
| Credencial | Un token de API que tú creas | Una conexión que apruebas con «Permitir» |
| Plan de la organización | Light y Pro | Pro |
| Cobertura | Las 62 operaciones de la API | 38 herramientas |
| Cómo se termina | «Revocar» | «Desconectar» |
## Un agente que hace llamadas HTTP [#un-agente-que-hace-llamadas-http]
1. Crea un token de API en «Configuración» → «Tokens de API». Mira
[Autenticación](/docs/authentication).
2. Guárdalo en la configuración de secretos del agente, por ejemplo en una
variable de entorno. No lo pegues en la conversación.
3. Apunta el agente a la documentación en texto:
| Dirección | Qué trae |
| ---------------------------------------- | -------------------------------------------------------------- |
| `https://docs.fluenta.io/llms.txt` | El índice: una línea por página, para decidir qué leer |
| `https://docs.fluenta.io/llms-full.txt` | Todas las páginas, enteras, en un solo documento |
| `https://docs.fluenta.io/openapi.json` | El documento OpenAPI: rutas, parámetros, permisos y respuestas |
| `https://docs.fluenta.io/docs/errors.md` | Una página en Markdown: agrega `.md` a su dirección |
Los dos archivos `llms` están en español. Cada página en inglés está en
`https://docs.fluenta.io/en/docs/errors.md`, con el nombre de la página en
lugar de `errors`.
### Instrucciones para pegar [#instrucciones-para-pegar]
Pega esto en las instrucciones de sistema del agente:
```text
Tienes acceso a la API de Fluenta por HTTP.
- Base: https://api.fluenta.io/v1/external
- En cada solicitud envía el encabezado
Authorization: Bearer $FLUENTA_TOKEN
El token está en la variable de entorno FLUENTA_TOKEN. Nunca lo escribas en
una respuesta ni en un registro.
- Documentación: https://docs.fluenta.io/llms.txt es el índice y
https://docs.fluenta.io/openapi.json tiene todas las operaciones.
- Empieza con GET /me y luego GET /organizations. De ahí salen los permisos
del token y el organizationId de cada organización. Todas las demás rutas
llevan el organizationId en la ruta: no hay organización por defecto.
- Un id sale siempre de una respuesta anterior. Nunca inventes uno.
- Los días son YYYY-MM-DD en el reloj de la planta, no en UTC. El campo today
de GET /organizations/{organizationId} es el hoy de la planta.
- Un error es { statusCode, code, message }. Decide por code y muestra
message a la persona tal como llega. No repitas una llamada rechazada sin
cambiarla.
- Con un 429 RATE_LIMITED espera los segundos del encabezado Retry-After
antes de reintentar.
- Cita las cifras como llegan. No las recalcules.
- No borres nada sin pedir la vista previa y recibir un sí explícito de la
persona. Un cambio rechazado no cambió nada: no digas que se hizo.
```
## «Conectar con IA» [#conectar-con-ia]
Claude y ChatGPT se conectan por MCP, sin token. La dirección es:
```text
https://app.fluenta.io/mcp
```
1. Abre «Configuración» → «Conectar con IA». Ahí están la «Dirección del
conector» y los pasos para cada aplicación.
2. Pega la dirección en la aplicación: en Claude, como conector personalizado; en
ChatGPT, como servidor MCP personalizado con autenticación OAuth.
3. La aplicación te trae a Fluenta. Revisa la solicitud y pulsa «Permitir».
Lo que conviene saber:
* **Viene con el Plan Pro.** Una prueba de Pro y una organización exenta
también lo tienen. La conexión solo alcanza las organizaciones que lo
tienen.
* **Es OAuth.** Tú apruebas la conexión en Fluenta con tu sesión del panel y
eliges sus permisos: «Leer» siempre, «Gestionar el Programa» si lo activas.
* **Ofrece 38 herramientas.** 23 solo leen: tu identidad y tus organizaciones,
la planta, los tableros, el Programa, el catálogo de referencias, los
reportes, la «Actividad de conexiones» y las dos vistas previas de borrado.
15 cambian el Programa y el catálogo, y piden «Gestionar el Programa»; dos
de ellas borran, y solo con la confirmación de su vista previa.
* **Un token de API no sirve aquí.** La dirección MCP solo acepta la conexión
de la propia aplicación: rechaza un token de API como una credencial inválida.
* Las 23 rutas de lectura del 2026-11-12 no tienen herramienta MCP. Un agente
las alcanza con un token de API y esta documentación. Mira el
[registro de cambios](/docs/changelog).
«Desconectar», en Configuración, termina la conexión de inmediato.
## Seguridad [#seguridad]
* **Un token actúa como tú.** Alcanza todas las organizaciones a las que
perteneces, con el rol que tienes en cada una, leído en cada solicitud.
* **Prefiere solo «Leer».** Sin «Gestionar el Programa», toda escritura
responde 403 `INSUFFICIENT_SCOPE`. Actívalo solo si el agente debe cambiar
el Programa.
* **Ponle «Vencimiento».** Elige 30 días, 90 días o 1 año. Un token con
«Nunca» sirve hasta que lo revoques.
* **Un token por agente.** Puedes tener hasta 10 tokens activos. Con uno por
uso, revocas ese sin afectar a los demás.
* **Revócalo al terminar.** «Revocar» lo termina de inmediato.
* **Una IA puede seguir instrucciones del texto que lee.** Lo que el token
puede hacer, ese texto se lo puede pedir. Es otra razón para dar solo
«Leer».
* Cada cambio hecho por la API queda en la «Actividad de conexiones», y
`listActivity` la responde.
---
# Registro de cambios
URL: https://docs.fluenta.io/docs/changelog
Cómo cambia la API de Fluenta bajo /v1, qué cuenta como un cambio incompatible y la lista de cambios por fecha.
Esta página es donde se anuncian los cambios de la API.
## Cómo cambia la API [#cómo-cambia-la-api]
* **La versión va en la ruta.** Todas las rutas viven bajo `/v1/external`.
* **Los cambios compatibles llegan sin aviso previo.** Son rutas nuevas,
parámetros opcionales nuevos y campos nuevos en las respuestas. Se anotan
aquí. Tu programa debe ignorar los campos que no conoce.
* **Los nombres publicados se conservan.** Un `code` de error mantiene su
nombre una vez publicado. Los permisos `read` y `program` no cambian de
nombre. A la lista de acciones de `listActivity` se le agregan valores; no
se renombran.
* **Un cambio incompatible** es quitar o renombrar una ruta, un parámetro, un
campo o un `code`; cambiar el tipo o el significado de un campo; o volver
obligatorio un parámetro opcional.
* **Los cambios incompatibles se anuncian en esta página antes de salir.**
Un `code` que tu programa no conoce se trata por su estado HTTP. Mira
[Errores](/docs/errors).
## 2026-11-12 [#2026-11-12]
### Tokens de API [#tokens-de-api]
* Un usuario crea tokens de API en «Configuración» → «Tokens de API». El token
se muestra una sola vez.
* Es personal: actúa como su usuario, en todas sus organizaciones, con el rol
que tiene en cada una.
* Lleva «Leer» siempre y «Gestionar el Programa» si se activa al crearlo.
* Su «Vencimiento» es «Nunca», 30 días, 90 días o 1 año. Cada usuario puede
tener hasta 10 tokens activos.
* `getMe` responde ahora `credential` (`kind`, `name`, `prefix`, `expiresAt`)
y `rateLimit` (`limit`, `remaining`, `reset`).
Mira [Autenticación](/docs/authentication).
### Acceso para Light [#acceso-para-light]
* Un token de API alcanza las organizaciones en Light y en Pro.
* La API le da a cada organización lo que su plan le da en el panel. Las
rutas de reporte responden 403 `PLAN_LIMIT` cuando el plan no las incluye.
* «Conectar con IA» sigue siendo del Plan Pro.
Mira [Planes](/docs/plans).
### 23 rutas de lectura nuevas [#23-rutas-de-lectura-nuevas]
La API pasa de 22 a 45 lecturas. Con las 15 escrituras del Programa y las 2
vistas previas de borrado, son 62 operaciones.
| Grupo | Operaciones nuevas |
| ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Account | [`getMyUsage`](/docs/reference/account/get-my-usage) |
| Organization | [`getPlan`](/docs/reference/organization/get-plan), [`getSettings`](/docs/reference/organization/get-settings), [`listCategories`](/docs/reference/organization/list-categories) |
| Plant | [`getFloor`](/docs/reference/plant/get-floor), [`getModule`](/docs/reference/plant/get-module), [`listModuleRestTimes`](/docs/reference/plant/list-module-rest-times), [`listOrganizationStations`](/docs/reference/plant/list-organization-stations), [`getStation`](/docs/reference/plant/get-station), [`getOperatorPresence`](/docs/reference/plant/get-operator-presence), [`getOperator`](/docs/reference/plant/get-operator) |
| Analytics | [`getModuleDayHistory`](/docs/reference/analytics/get-module-day-history), [`getModuleEfficiency`](/docs/reference/analytics/get-module-efficiency), [`getOperatorEfficiency`](/docs/reference/analytics/get-operator-efficiency), [`getStationDayReport`](/docs/reference/analytics/get-station-day-report), [`getStationEvents`](/docs/reference/analytics/get-station-events), [`getHourlyActivity`](/docs/reference/analytics/get-hourly-activity), [`getOperatorsActivity`](/docs/reference/analytics/get-operators-activity), [`getConsistency`](/docs/reference/analytics/get-consistency), [`getEfficiencyByModule`](/docs/reference/analytics/get-efficiency-by-module), [`getBillingByModule`](/docs/reference/analytics/get-billing-by-module), [`getMonth`](/docs/reference/analytics/get-month), [`getPauses`](/docs/reference/analytics/get-pauses) |
* Las once de Account, Organization y Plant responden en Light y en Pro.
* Las doce de Analytics son rutas de reporte: responden según el plan de la
organización.
* Estas rutas no tienen herramienta en «Conectar con IA». Una IA las alcanza
con un token de API. Mira [Usar la API con una IA](/docs/ai).
### Filtros en los rangos [#filtros-en-los-rangos]
`getEfficiency`, `getProduction` y `getBilling` aceptan un filtro opcional:
`moduleId`, `operatorId` u `operationId`. Sin filtro responden lo mismo que
antes. Un filtro que la cifra no tiene responde 400 `FILTER_UNSUPPORTED`.
### Límite de uso con encabezados [#límite-de-uso-con-encabezados]
* Cada credencial tiene un límite por minuto: 60 solicitudes por organización
en Light y 120 en Pro.
* Toda respuesta lleva `X-RateLimit-Limit`, `X-RateLimit-Remaining` y
`X-RateLimit-Reset`.
* Pasado el límite, la API responde 429 `RATE_LIMITED` con `Retry-After`.
Mira [Límites de uso](/docs/rate-limits).
### `INVALID_REQUEST` [#invalid_request]
Una consulta o un cuerpo que no pasa su validación responde 400
`INVALID_REQUEST`, con `details`: una fila `{ field, message }` por cada
falla. Antes, ese 400 traía `message` como una lista y ningún `code`.
Mira [Errores](/docs/errors).
### Documento OpenAPI [#documento-openapi]
Las operaciones se agrupan por área: Account, Organization, Plant, Board,
Program, Catalogue, Analytics y Activity. El documento está en
[`/openapi.json`](/openapi.json).
---
# Referencia de la API
URL: https://docs.fluenta.io/docs/reference
Todas las operaciones de la API de Fluenta, generadas desde su documento OpenAPI: ruta, parámetros, permisos y respuestas.
{/* Generated by scripts/generate-openapi-docs.ts. Do not edit. */}
Estas páginas se generan desde el documento OpenAPI de la API, así que no pueden quedar desfasadas: una página por operación, con su ruta, sus parámetros, el permiso que pide y sus respuestas. El documento completo está en [`/openapi.json`](/openapi.json).
## Cuenta [#cuenta]
| Operación | Método y ruta | Qué hace |
| ----------------------------------------------------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`getMe`](/docs/reference/account/get-me) | `GET /v1/external/me` | The user the credential acts as, the credential itself, its «Permisos», its rate limit and the organizations it reaches. |
| [`getMyUsage`](/docs/reference/account/get-my-usage) | `GET /v1/external/me/usage` | What this credential has asked of the API: totals, by route and by day. Only its own requests, in every organization. |
| [`listOrganizations`](/docs/reference/account/list-organizations) | `GET /v1/external/organizations` | The organizations the credential reaches, by name, 50 per page. A credential of an account with platform-wide access searches every organization. |
## Organización [#organización]
| Operación | Método y ruta | Qué hace |
| ------------------------------------------------------------------------------- | ------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`getOrganization`](/docs/reference/organization/get-organization) | `GET /v1/external/organizations/{organizationId}` | The organization: «país», «zona horaria» and today in its clock, «moneda», «jornada», the módulos’ «descansos», «categorías» and the plan features it has. |
| [`getOrganizationAccess`](/docs/reference/organization/get-organization-access) | `GET /v1/external/organizations/{organizationId}/access` | The user’s roles and the credential’s «Permisos» in one organization. |
| [`listCategories`](/docs/reference/organization/list-categories) | `GET /v1/external/organizations/{organizationId}/categories` | The «Categorías» an OC takes its garment from: the plant’s own, for «Otros clientes», and those of each brand it works for in the «Red de talleres». |
| [`getPlan`](/docs/reference/organization/get-plan) | `GET /v1/external/organizations/{organizationId}/plan` | The organization’s plan: Light or Pro, its plan version, what it allows (reports, «Facturación», «Pausas», «Conectar con IA»…) and the rate limits this API counts a credential against. |
| [`getSettings`](/docs/reference/organization/get-settings) | `GET /v1/external/organizations/{organizationId}/settings` | The plant’s «jornada»: its «zona horaria» and today in its clock, when the shift starts and ends on each weekday, the «jornada continua» and «jornada especial» days, the days off, and its switches. The «descansos» of a «módulo» are `listModuleRestTimes`. |
## Planta [#planta]
| Operación | Método y ruta | Qué hace |
| ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`getFloor`](/docs/reference/plant/get-floor) | `GET /v1/external/organizations/{organizationId}/floor` | The whole floor in one answer: every «módulo» in the floor’s order, each with its «puestos» (stations) as the live board reads them now. Counted against the heavy rate limit. |
| [`listModules`](/docs/reference/plant/list-modules) | `GET /v1/external/organizations/{organizationId}/modules` | The organization’s módulos (production lines), in the floor’s order, with how many puestos each has. |
| [`getModule`](/docs/reference/plant/get-module) | `GET /v1/external/organizations/{organizationId}/modules/{moduleId}` | One «módulo» (production line): its name, its place on the floor, how many «puestos» it has and its colour. |
| [`listModuleRestTimes`](/docs/reference/plant/list-module-rest-times) | `GET /v1/external/organizations/{organizationId}/modules/{moduleId}/rest-times` | One «módulo»’s «descansos» (breaks), every weekday alike, by start: no goal is owed inside one. On a «jornada continua» weekday (`getSettings`) none applies. |
| [`listStations`](/docs/reference/plant/list-stations) | `GET /v1/external/organizations/{organizationId}/modules/{moduleId}/stations` | The puestos (stations) of one módulo as the live board reads them now: operación, seated operario, current referencia, units this hour and today. |
| [`listOperators`](/docs/reference/plant/list-operators) | `GET /v1/external/organizations/{organizationId}/operators` | The organization’s operarios by name, «Activo» or «Inactivo», and the puesto each is seated at. |
| [`getOperatorPresence`](/docs/reference/plant/get-operator-presence) | `GET /v1/external/organizations/{organizationId}/operators/presence` | Which «operarios» have a tablet connected right now, and whether the app is open on it. An «operario» missing from the answer is offline. |
| [`getOperator`](/docs/reference/plant/get-operator) | `GET /v1/external/organizations/{organizationId}/operators/{operatorId}` | One «operario» by id: name, «Activo» or «Inactivo», and the «puesto» the «operario» is seated at. |
| [`listOrganizationStations`](/docs/reference/plant/list-organization-stations) | `GET /v1/external/organizations/{organizationId}/stations` | The organization’s «puestos» (stations) across its «módulos», optionally of one «módulo», as the live board reads them now. 50 per page, by «módulo» in the floor’s order, then in the board’s. |
| [`getStation`](/docs/reference/plant/get-station) | `GET /v1/external/organizations/{organizationId}/stations/{stationId}` | One «puesto» (station) as the live board reads it now: «operación», seated «operario», current «referencia», units this hour and today. |
## Tableros [#tableros]
| Operación | Método y ruta | Qué hace |
| -------------------------------------------------------- | ------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`getLiveBoard`](/docs/reference/board/get-live-board) | `GET /v1/external/organizations/{organizationId}/board` | The live board of the whole floor for a day: each módulo’s «eficiencia», units and goal so far, and the plant’s. |
| [`getHoursBoard`](/docs/reference/board/get-hours-board) | `GET /v1/external/organizations/{organizationId}/modules/{moduleId}/days/{day}/board` | One módulo’s «Tablero de producción» for a day, hour by hour: goal, units typed and tapped, running totals, «eficiencia» and where each referencia stands. |
## Programa [#programa]
| Operación | Método y ruta | Qué hace |
| ------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`setDayGoals`](/docs/reference/program/set-day-goals) | `PUT /v1/external/organizations/{organizationId}/modules/{moduleId}/days/{day}/references/{referenceId}/goal` | «Metas del día»: one referencia’s hourly goal for one day of a módulo, one rate all day or rates by minute. Answers the módulo’s day as `getHoursBoard` does. |
| [`clearDayGoals`](/docs/reference/program/clear-day-goals) | `DELETE /v1/external/organizations/{organizationId}/modules/{moduleId}/days/{day}/references/{referenceId}/goal` | «↺ Meta base»: the day reads the referencia’s own «Meta por hora» again. Answers the módulo’s day as `getHoursBoard` does. |
| [`getProgram`](/docs/reference/program/get-program) | `GET /v1/external/organizations/{organizationId}/modules/{moduleId}/program` | One módulo’s «Programa»: its referencias in queue order, with units produced, «saldo», dates and today’s «Metas del día». A finished cut stays listed a few days. |
| [`reorderProgram`](/docs/reference/program/reorder-program) | `PATCH /v1/external/organizations/{organizationId}/modules/{moduleId}/program/order` | Rewrite a módulo’s «Programa» order (the FILA): every referencia of the queue, each once, in the new order. Answers the queue as `getProgram` does. |
| [`searchReferences`](/docs/reference/program/search-references) | `GET /v1/external/organizations/{organizationId}/references` | Search the organization’s referencias by «Corte (OC)», «Referencia» or «Categoría», optionally in one módulo or one state (in the «Programa», on hold, finished). 50 per page, newest first. |
| [`createReference`](/docs/reference/program/create-reference) | `POST /v1/external/organizations/{organizationId}/references` | Create an OC (a referencia, a cut of a garment) at the end of a módulo’s «Programa», as «Nueva referencia» does. |
| [`addReferencesFromCatalogue`](/docs/reference/program/add-references-from-catalogue) | `POST /v1/external/organizations/{organizationId}/references/from-catalogue` | Add several OCs to a módulo’s «Programa» from the «Catálogo de referencias», one per template, as «Agregar del catálogo» does. A refused OC never stops the rest: `failed` lists them; when none lands, the first refusal is the answer. |
| [`listOnHold`](/docs/reference/program/list-on-hold) | `GET /v1/external/organizations/{organizationId}/references/on-hold` | The «banco de espera»: every paused referencia of the organization, newest pause first, with why, since when and from which módulo. |
| [`getReference`](/docs/reference/program/get-reference) | `GET /v1/external/organizations/{organizationId}/references/{referenceId}` | One referencia (cut, OC): its módulo, state, units, produced and «saldo», goals, price, dates, «Subprocesos» and «Red» marks. |
| [`updateReference`](/docs/reference/program/update-reference) | `PATCH /v1/external/organizations/{organizationId}/references/{referenceId}` | Edit an OC, as «Editar» does: only the fields sent change. A new «Meta por hora» keeps the past days’ goals; a new price re-prices today. |
| [`deleteReference`](/docs/reference/program/delete-reference) | `DELETE /v1/external/organizations/{organizationId}/references/{referenceId}` | Delete an OC, as «Borrar» does, only with the `confirmationToken` its preview returned once the usuario said yes: 422 `CONFIRMATION_REQUIRED` without it, 409 `PREVIEW_STALE` when it ran out or the OC changed since. |
| [`markReferenceArrived`](/docs/reference/program/mark-reference-arrived) | `POST /v1/external/organizations/{organizationId}/references/{referenceId}/arrive` | «Llegó»: the taller marks a brand’s OC («OC anticipada») as physically arrived, with the «Meta por hora» and the units its «ficha técnica» says. |
| [`previewDeleteReference`](/docs/reference/program/preview-delete-reference) | `POST /v1/external/organizations/{organizationId}/references/{referenceId}/delete-preview` | What deleting an OC would do, writing nothing: the OC, whether its módulo empties (every puesto cleared), the puestos sewing it, and the `confirmationToken` `deleteReference` needs. Show it to the usuario and wait for a yes. |
| [`holdReference`](/docs/reference/program/hold-reference) | `PATCH /v1/external/organizations/{organizationId}/references/{referenceId}/hold` | Pause an OC into the «banco de espera» (⏸): it leaves its módulo’s queue with its counts, and the puestos sewing it move to the next OC. |
| [`reactivateReference`](/docs/reference/program/reactivate-reference) | `PATCH /v1/external/organizations/{organizationId}/references/{referenceId}/reactivate` | Bring an OC back from the «banco de espera» (▶) into a módulo, at the end of its queue, with its counts. |
| [`recordSacrifice`](/docs/reference/program/record-sacrifice) | `PATCH /v1/external/organizations/{organizationId}/references/{referenceId}/sacrifice` | Set the «descuento por sacrificio» of an OC: units written off its «saldo» without being production. Absolute; 0 removes it. |
| [`splitReference`](/docs/reference/program/split-reference) | `POST /v1/external/organizations/{organizationId}/references/{referenceId}/split` | Split an OC between two módulos (✂): `units` go to a new «-B» portion at the end of another módulo’s queue; the original keeps the rest. |
## Catálogo [#catálogo]
| Operación | Método y ruta | Qué hace |
| ----------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`listCatalogue`](/docs/reference/catalogue/list-catalogue) | `GET /v1/external/organizations/{organizationId}/catalogue` | The «Catálogo de referencias»: the organization’s reusable styles by name, with the goal, price, category and «Subprocesos» a new OC from each starts with. |
| [`createCatalogueEntry`](/docs/reference/catalogue/create-catalogue-entry) | `POST /v1/external/organizations/{organizationId}/catalogue` | Add a reusable style to the «Catálogo de referencias», as its «Nueva referencia» does: the name is unique in the catálogo (409 `REFERENCE_TEMPLATE_EXISTS`). |
| [`updateCatalogueEntry`](/docs/reference/catalogue/update-catalogue-entry) | `PATCH /v1/external/organizations/{organizationId}/catalogue/{templateId}` | Edit a template of the «Catálogo de referencias»: only the fields sent change; null clears an optional one. No OC changes. |
| [`deleteCatalogueEntry`](/docs/reference/catalogue/delete-catalogue-entry) | `DELETE /v1/external/organizations/{organizationId}/catalogue/{templateId}` | Delete a template of the «Catálogo de referencias» (no OC changes), only with the `confirmationToken` its preview returned once the usuario said yes: 422 `CONFIRMATION_REQUIRED` without it, 409 `PREVIEW_STALE` when it ran out or the template changed since. |
| [`previewDeleteCatalogueEntry`](/docs/reference/catalogue/preview-delete-catalogue-entry) | `POST /v1/external/organizations/{organizationId}/catalogue/{templateId}/delete-preview` | What deleting a template of the «Catálogo de referencias» would do, writing nothing, and the `confirmationToken` `deleteCatalogueEntry` needs. Show it to the usuario and wait for a yes. |
## Analítica [#analítica]
| Operación | Método y ruta | Qué hace |
| ----------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`getStationActivity`](/docs/reference/analytics/get-station-activity) | `GET /v1/external/organizations/{organizationId}/analytics/activity` | Analítica for one plant day, from the marcas log: one puesto’s marcas summarised, the day hour by hour, each operario’s pace, or the log against the hourly buckets. |
| [`getBilling`](/docs/reference/analytics/get-billing) | `GET /v1/external/organizations/{organizationId}/analytics/billing` | «Facturación» over a range of plant days, per day, or per day and módulo, operario or puesto. |
| [`getBillingByModule`](/docs/reference/analytics/get-billing-by-module) | `GET /v1/external/organizations/{organizationId}/analytics/billing/modules` | «Facturación por módulo» for one plant day: each módulo’s billed units and amount, in the plant’s currency. |
| [`getConsistency`](/docs/reference/analytics/get-consistency) | `GET /v1/external/organizations/{organizationId}/analytics/consistency` | The marcas log against the hourly buckets for one plant day: which puesto-hours agree, which differ, and by how many units. |
| [`getDayReport`](/docs/reference/analytics/get-day-report) | `GET /v1/external/organizations/{organizationId}/analytics/day-report` | Reportes › Eficiencia for one plant day: every módulo hour by hour, each puesto’s units, meta and efficiency. |
| [`getEfficiency`](/docs/reference/analytics/get-efficiency) | `GET /v1/external/organizations/{organizationId}/analytics/efficiency` | «Eficiencia» over a range of plant days: units made against the meta, per day, or per day and módulo, operario or puesto. |
| [`getEfficiencyByModule`](/docs/reference/analytics/get-efficiency-by-module) | `GET /v1/external/organizations/{organizationId}/analytics/efficiency/modules` | Reportes › Eficiencia for one plant day, módulo by módulo: units, meta, efficiency and pausas. |
| [`getHourlyActivity`](/docs/reference/analytics/get-hourly-activity) | `GET /v1/external/organizations/{organizationId}/analytics/hourly` | Analítica hour by hour for one plant day, from the marcas log: units, meta, efficiency, billing and pace per hour, for the plant, one módulo or one operario. |
| [`getLeaderboard`](/docs/reference/analytics/get-leaderboard) | `GET /v1/external/organizations/{organizationId}/analytics/leaderboard` | The «Clasificación» of one plant day: every operario’s units, meta, efficiency and «Facturado», best first. |
| [`getMonth`](/docs/reference/analytics/get-month) | `GET /v1/external/organizations/{organizationId}/analytics/month` | The plant’s last 30 days, up to its today: units, meta, efficiency and «Facturación» per day. The 30-day chart of Reportes. |
| [`getOperatorsActivity`](/docs/reference/analytics/get-operators-activity) | `GET /v1/external/organizations/{organizationId}/analytics/operators` | Analítica by operario for one plant day: each one’s units, pace, active hours, channels, efficiency and billing, with the totals. |
| [`getOperatorReport`](/docs/reference/analytics/get-operator-report) | `GET /v1/external/organizations/{organizationId}/analytics/operators/{operatorId}` | One operario’s report: units, meta and efficiency per day over a range of up to 92 days, and hour by hour for a single day. |
| [`getPauses`](/docs/reference/analytics/get-pauses) | `GET /v1/external/organizations/{organizationId}/analytics/pauses` | The «Pausas» report over a range of up to 31 plant days: every pausa, newest first (at most 2000), and the figures over the whole range. |
| [`getProduction`](/docs/reference/analytics/get-production) | `GET /v1/external/organizations/{organizationId}/analytics/production` | «Producido» over a range of plant days, per day, or per day and módulo, referencia or categoría. |
| [`getModuleEfficiency`](/docs/reference/analytics/get-module-efficiency) | `GET /v1/external/organizations/{organizationId}/modules/{moduleId}/analytics/efficiency` | One módulo’s «Eficiencia» per day over a range of up to 92 plant days: units made against the meta, every puesto counted. The chart of «Reportes por módulo». |
| [`getModuleDayHistory`](/docs/reference/analytics/get-module-day-history) | `GET /v1/external/organizations/{organizationId}/modules/{moduleId}/days/{day}/history` | One módulo’s day, hour by hour and puesto by puesto: units, meta, efficiency, «Facturación» and pausas. The table of «Reportes por módulo». |
| [`getOperatorEfficiency`](/docs/reference/analytics/get-operator-efficiency) | `GET /v1/external/organizations/{organizationId}/operators/{operatorId}/analytics/efficiency` | One operario’s «Eficiencia» per day over a range of up to 92 plant days: their own marcas against their share of each puesto-hour’s meta. |
| [`getStationEvents`](/docs/reference/analytics/get-station-events) | `GET /v1/external/organizations/{organizationId}/stations/{stationId}/analytics/events` | One puesto’s marcas of a day, summarised from the marcas log: units, billing, online and offline marcas, pace and «pausas sin registro». |
| [`getStationDayReport`](/docs/reference/analytics/get-station-day-report) | `GET /v1/external/organizations/{organizationId}/stations/{stationId}/days/{day}/report` | One puesto’s «reporte por operación» for a day: hour by hour, each referencia’s units, meta, efficiency and price, the hour’s «Facturación» and its pausas; by «Subproceso» with «El operario escoge la referencia» on. |
## Actividad [#actividad]
| Operación | Método y ruta | Qué hace |
| -------------------------------------------------------- | ---------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`listActivity`](/docs/reference/activity/list-activity) | `GET /v1/external/organizations/{organizationId}/activity` | «Actividad de conexiones»: the changes conexiones made in this organization, newest first, each as a Spanish sentence, refused attempts included. An ADMIN, or an account with platform-wide access, sees every conexión’s; any other role, only their own. |
---
# Get me
URL: https://docs.fluenta.io/docs/reference/account/get-me
Operation: `getMe`
Required permission: `read`
`GET /v1/external/me`
The user the credential acts as, the credential itself, its «Permisos», its rate limit and the organizations it reaches.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
None.
### Responses
| Status | Description |
| --- | --- |
| `200` | `ExternalMeDto` |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `403` | The credential lacks the «Permiso» the route names (`INSUFFICIENT_SCOPE`), or the organization’s plan does not include what was asked (`PLAN_LIMIT`): «Conectar con IA» for a connected app, or a report or analytics route whose plan feature a Light organization lacks (a Light of plan version 1 keeps `reports`). |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X GET 'https://api.fluenta.io/v1/external/me' \
-H 'Authorization: Bearer '
```
---
# Get my usage
URL: https://docs.fluenta.io/docs/reference/account/get-my-usage
Operation: `getMyUsage`
Required permission: `read`
`GET /v1/external/me/usage`
What this credential has asked of the API: totals, by route and by day. Only its own requests, in every organization.
Counts the requests this credential made, refusals included, over the last `days` days (30 by default, 90 at most). Days are UTC days: `from`, `to` and each `days[].day` are `YYYY-MM-DD` dates of the server’s clock, not of a plant’s, and `from` counts from its first hour. Figures are kept by the hour and may trail the last seconds. A connected app is counted as one credential across the access tokens it was issued; an API token is counted on its own. Nothing in the query names another credential.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `days` | query | number | no | How many days back the figures reach, counted in UTC days from today: `from` is that day, whole, and `to` is today. Usage is kept 90 days. |
### Responses
| Status | Description |
| --- | --- |
| `200` | `ExternalMyUsageDto` |
| `400` | `days` is not a whole number from 1 to 90: `INVALID_REQUEST`, with the field in `details`. |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `403` | The credential lacks the «Permiso» the route names (`INSUFFICIENT_SCOPE`), or the organization’s plan does not include what was asked (`PLAN_LIMIT`): «Conectar con IA» for a connected app, or a report or analytics route whose plan feature a Light organization lacks (a Light of plan version 1 keeps `reports`). |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X GET 'https://api.fluenta.io/v1/external/me/usage' \
-H 'Authorization: Bearer '
```
---
# List organizations
URL: https://docs.fluenta.io/docs/reference/account/list-organizations
Operation: `listOrganizations`
Required permission: `read`
`GET /v1/external/organizations`
The organizations the credential reaches, by name, 50 per page. A credential of an account with platform-wide access searches every organization.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `search` | query | string | no | Part of the organization’s name, any case. |
| `page` | query | number | no | 1-based page; 50 organizations per page. |
### Responses
| Status | Description |
| --- | --- |
| `200` | `ExternalOrganizationsPageDto` |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `403` | The credential lacks the «Permiso» the route names (`INSUFFICIENT_SCOPE`), or the organization’s plan does not include what was asked (`PLAN_LIMIT`): «Conectar con IA» for a connected app, or a report or analytics route whose plan feature a Light organization lacks (a Light of plan version 1 keeps `reports`). |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X GET 'https://api.fluenta.io/v1/external/organizations' \
-H 'Authorization: Bearer '
```
---
# Get organization
URL: https://docs.fluenta.io/docs/reference/organization/get-organization
Operation: `getOrganization`
Required permission: `read`
`GET /v1/external/organizations/{organizationId}`
The organization: «país», «zona horaria» and today in its clock, «moneda», «jornada», the módulos’ «descansos», «categorías» and the plan features it has.
Answers the organization in one read: where it is (`country`, `department`, `city`), its clock (`timezone`, with `today` and `currentHour` on it), its `currency`, its `plan`, the `switches` of «Configuración», the «jornada» (`shift`), every «módulo»’s «descansos» (`moduleRestTimes`) and its «categorías» (`categories`).
`organizationId` comes from `getMe` or `listOrganizations`; one that does not exist or is out of the credential’s reach answers 404 `ORGANIZATION_NOT_FOUND`. `getPlan`, `getSettings`, `listCategories` and `listModuleRestTimes` answer its parts on their own.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Responses
| Status | Description |
| --- | --- |
| `200` | `ExternalOrganizationDetailDto` |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | The credential lacks the «Permiso» the route names (`INSUFFICIENT_SCOPE`), or the organization’s plan does not include what was asked (`PLAN_LIMIT`): «Conectar con IA» for a connected app, or a report or analytics route whose plan feature a Light organization lacks (a Light of plan version 1 keeps `reports`). |
| `404` | The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X GET 'https://api.fluenta.io/v1/external/organizations/{organizationId}' \
-H 'Authorization: Bearer '
```
---
# Get organization access
URL: https://docs.fluenta.io/docs/reference/organization/get-organization-access
Operation: `getOrganizationAccess`
Required permission: `read`
`GET /v1/external/organizations/{organizationId}/access`
The user’s roles and the credential’s «Permisos» in one organization.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `organizationId` | path | number | yes | |
### Responses
| Status | Description |
| --- | --- |
| `200` | `ExternalOrganizationAccessDto` |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | The credential lacks the «Permiso» the route names (`INSUFFICIENT_SCOPE`), or the organization’s plan does not include what was asked (`PLAN_LIMIT`): «Conectar con IA» for a connected app, or a report or analytics route whose plan feature a Light organization lacks (a Light of plan version 1 keeps `reports`). |
| `404` | The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X GET 'https://api.fluenta.io/v1/external/organizations/{organizationId}/access' \
-H 'Authorization: Bearer '
```
---
# List activity
URL: https://docs.fluenta.io/docs/reference/activity/list-activity
Operation: `listActivity`
Required permission: `read`
`GET /v1/external/organizations/{organizationId}/activity`
«Actividad de conexiones»: the changes conexiones made in this organization, newest first, each as a Spanish sentence, refused attempts included. An ADMIN, or an account with platform-wide access, sees every conexión’s; any other role, only their own.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `page` | query | integer | no | 1-based page; 50 rows per page, newest first. |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Responses
| Status | Description |
| --- | --- |
| `200` | `ExternalActivityPageDto` |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | The credential lacks the «Permiso» the route names (`INSUFFICIENT_SCOPE`), or the organization’s plan does not include what was asked (`PLAN_LIMIT`): «Conectar con IA» for a connected app, or a report or analytics route whose plan feature a Light organization lacks (a Light of plan version 1 keeps `reports`). |
| `404` | The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X GET 'https://api.fluenta.io/v1/external/organizations/{organizationId}/activity' \
-H 'Authorization: Bearer '
```
---
# Get station activity
URL: https://docs.fluenta.io/docs/reference/analytics/get-station-activity
Operation: `getStationActivity`
Required permission: `read`
`GET /v1/external/organizations/{organizationId}/analytics/activity`
Analítica for one plant day, from the marcas log: one puesto’s marcas summarised, the day hour by hour, each operario’s pace, or the log against the hourly buckets.
`day` is optional: without it, the plant’s today on the organization’s own clock, as on every single-day route of the API externa.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `day` | query | string | no | Plant day, YYYY-MM-DD on the plant’s clock. Omitted: the plant’s today, on its own clock. |
| `view` | query | string | yes | `events`: one puesto’s marcas summarised (needs `operationId`). `hourly`: the day hour by hour. `operators`: each operario’s pace, channels and billing. `consistency`: the marcas log against the hourly buckets. One of: `events`, `hourly`, `operators`, `consistency`. |
| `operationId` | query | number | no | The puesto, for `view=events`. |
| `moduleId` | query | number | no | Restrict `hourly`, `operators` or `consistency` to one módulo. |
| `operatorId` | query | number | no | Restrict `hourly` to one operario: their own marcas and share of each hour’s meta. |
| `idleMinutes` | query | number | no | Gaps between marcas longer than this many minutes count as «pausas sin registro». Default 5. |
| `organizationId` | path | number | yes | |
### Responses
| Status | Description |
| --- | --- |
| `200` | `ExternalStationActivityDto` |
| `400` | `INVALID_DAY`; `OPERATION_REQUIRED` for `view=events` without `operationId`. |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | The credential lacks the «Permiso» the route names (`INSUFFICIENT_SCOPE`), or the organization’s plan does not include what was asked (`PLAN_LIMIT`): «Conectar con IA» for a connected app, or a report or analytics route whose plan feature a Light organization lacks (a Light of plan version 1 keeps `reports`). |
| `404` | The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X GET 'https://api.fluenta.io/v1/external/organizations/{organizationId}/analytics/activity' \
-H 'Authorization: Bearer '
```
---
# Get billing
URL: https://docs.fluenta.io/docs/reference/analytics/get-billing
Operation: `getBilling`
Required permission: `read`
`GET /v1/external/organizations/{organizationId}/analytics/billing`
«Facturación» over a range of plant days, per day, or per day and módulo, operario or puesto.
Groupings: day, module, operator, operation. The puestos de salida’s units at the price each was billed at, plus a typed hour no puesto held the billing role in (by day and módulo). Each figure is rounded to the cent where its dashboard counterpart rounds it: a módulo’s day is its «Facturación por módulo» row, a day is that table’s total (its módulos added up), an operario’s is the «Clasificación»’s «Facturado», and a puesto’s is its «reporte por operación» (each hour rounded, then the hours added up). Filters: one of `moduleId`, `operatorId`, `operationId` narrows the answer to that one, each row still its dashboard figure; with `groupBy=day` the rows are its own figure of each day and name it. Without a filter the answer is the whole plant’s.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `from` | query | string | yes | First plant day of the range, YYYY-MM-DD on the plant's clock, included. At most 92 days from `from` to `to`. |
| `to` | query | string | yes | Last plant day of the range, YYYY-MM-DD on the plant’s clock, included; not before `from`. |
| `groupBy` | query | string | no | One row per day, or per day and módulo / operario / puesto (`operation`) / referencia / categoría. Each figure answers the groupings the dashboard shows it by; any other is a 400 `GROUP_BY_UNSUPPORTED` that lists the ones it has. One of: `day`, `module`, `operator`, `operation`, `reference`, `category`. |
| `page` | query | number | no | 1-based page; 100 rows per page, by day then group. |
| `moduleId` | query | integer | no | Only this módulo. With `groupBy=day` (or none), its own figure of each day, each row naming it; with `module`, its rows; with `operation`, the puestos whose day is filed under it; and, for production, with `reference` or `category` too. One filter per request; a second one, or a grouping the filter does not answer, is a 400 `FILTER_UNSUPPORTED`. An id of another organization is a 404 `MODULE_NOT_FOUND`. |
| `operatorId` | query | integer | no | Only this operario: their own figure of each day (`groupBy=day`, or none) or their rows (`groupBy=operator`), as the «Clasificación» counts them. Efficiency and billing; production has no operario (400 `FILTER_UNSUPPORTED`). An id of another organization is a 404 `OPERATOR_NOT_FOUND`. |
| `operationId` | query | integer | no | Only this puesto: its own figure of each day (`groupBy=day`, or none) or its rows (`groupBy=operation`), as its «reporte por operación» totals them. Efficiency and billing; production has no puesto (400 `FILTER_UNSUPPORTED`). An id of another organization is a 404 `OPERATION_NOT_FOUND`. |
| `organizationId` | path | number | yes | |
### Responses
| Status | Description |
| --- | --- |
| `200` | `ExternalBillingPageDto` |
| `400` | `INVALID_DAY` (a day that is not a real YYYY-MM-DD), `RANGE_INVALID` (`from` after `to`), `RANGE_TOO_LARGE` (more than 92 days), `GROUP_BY_UNSUPPORTED` (a grouping this figure has no dashboard counterpart for; `supported` lists the ones it has), `FILTER_UNSUPPORTED` (a filter this figure does not have, or not under that grouping, where `supported` lists the groupings the filter answers; or more than one filter at once). |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | The credential lacks the «Permiso» the route names (`INSUFFICIENT_SCOPE`), or the organization’s plan does not include what was asked (`PLAN_LIMIT`): «Conectar con IA» for a connected app, or a report or analytics route whose plan feature a Light organization lacks (a Light of plan version 1 keeps `reports`). |
| `404` | `ORGANIZATION_NOT_FOUND`; a filter’s id of another organization: `MODULE_NOT_FOUND`, `OPERATOR_NOT_FOUND`, `OPERATION_NOT_FOUND`. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
| `503` | `ANALYTICS_TIMEOUT`: ask a shorter range. |
### Example
```bash
curl -X GET 'https://api.fluenta.io/v1/external/organizations/{organizationId}/analytics/billing' \
-H 'Authorization: Bearer '
```
---
# Get billing by module
URL: https://docs.fluenta.io/docs/reference/analytics/get-billing-by-module
Operation: `getBillingByModule`
Required permission: `read`
`GET /v1/external/organizations/{organizationId}/analytics/billing/modules`
«Facturación por módulo» for one plant day: each módulo’s billed units and amount, in the plant’s currency.
What it is: «Facturación por módulo», the table of the dashboard’s «Reportes», tab «Facturación», for one plant day: one row per «módulo» that billed, with the units it bills (`billableUnits`) and what they are worth (`billedAmount`). A «módulo» that billed nothing that day is no row.
How it is computed: the units of the «módulo»’s «puestos de salida» (the «puestos» whose output the plant invoices its client for), each at the price per unit of its «referencia» that day, plus an hour whose «conteo manual» was typed while no «puesto» held that role, at the price its «referencia» billed at that day. Summed per «módulo», then rounded to 2 decimals once.
Money is «Facturación»: the units of the «puestos de salida» × the price per unit each was billed at, in the plant’s currency (`currency`, ISO 4217: `EUR` for a plant in España, `COP` for every other), in whole currency units with up to 2 decimals, never cents. Whether a money figure reads null or 0 where nothing was priced is stated on each field.
Days and hours are the plant’s own: a plant day is a calendar day `YYYY-MM-DD` in the organization’s timezone (`getOrganization` `timezone`), never UTC, and an `hour` is the clock hour 0–23 that starts at it in that zone. `day` is optional: without it, the plant’s today on the organization’s own clock, as on every single-day route of the API externa; the answer’s `day` says which day was read.
Plan: needs the plan feature `billingAnalytics` («Facturación»), the one its dashboard screen is gated by. Pro, a trial of Pro and an «exenta» organization answer it; a Light organization, of either plan version, does not: 403 `PLAN_LIMIT` with `feature: "billingAnalytics"`. `getPlan` `features` lists what the organization’s plan has.
See also: `getBilling` for «Facturación» over a range, by day, «módulo», «operario» or «puesto»; `getMonth` for the last 30 days in one call.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `day` | query | string | no | Plant day, YYYY-MM-DD on the plant’s clock. Omitted: the plant’s today, on its own clock. |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Responses
| Status | Description |
| --- | --- |
| `200` | The day’s «Facturación», one row per «módulo» that billed. |
| `400` | `INVALID_DAY`: `day` is not a real calendar day written `YYYY-MM-DD`. `INVALID_REQUEST`: a query parameter is missing, of the wrong type or outside its allowed values; `details` lists each one as `{field, message}`. Body: `{statusCode, code, message}`, the message in Spanish, for the usuario. |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | `PLAN_LIMIT` with `feature: "billingAnalytics"`: the organization’s plan lacks «Facturación» (Pro, a trial of Pro and an «exenta» organization answer it; a Light organization, of either plan version, does not). `PLAN_LIMIT` with `feature: "aiConnector"`: a connected app reads an organization without «Conectar con IA». `INSUFFICIENT_SCOPE`: the credential lacks the «Permiso» `read`. Retrying does not help: the plan or the credential has to change. |
| `404` | `ORGANIZATION_NOT_FOUND`: the organization does not exist or is out of the credential’s reach. One body for a row that is missing and for one of another organization. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X GET 'https://api.fluenta.io/v1/external/organizations/{organizationId}/analytics/billing/modules' \
-H 'Authorization: Bearer '
```
---
# Get consistency
URL: https://docs.fluenta.io/docs/reference/analytics/get-consistency
Operation: `getConsistency`
Required permission: `read`
`GET /v1/external/organizations/{organizationId}/analytics/consistency`
The marcas log against the hourly buckets for one plant day: which puesto-hours agree, which differ, and by how many units.
What it is: a data-quality check, the consistency view of the dashboard’s «Reportes», tab «Analítica». Every unit is stored twice: once as a «marca» in the log and once added into the hourly bucket of its «puesto», hour and «referencia», which is what the reports read. `rows` holds one entry per «puesto», hour and «referencia» of the plant day with the units each side holds (`eventUnits`, `bucketTotal`) and a `status`; `totals` counts the rows of each status and adds up both sides.
How to read `status`: `ok`, both sides hold the same units; `mismatch`, both exist and differ; `bucket_only`, the reports hold units the log does not (expected for days before the log existed); `events_only`, the log holds «marcas» no bucket received. A day that is all `ok` means the reports can be trusted to the unit. It is not a production figure: use `getHourlyActivity` or the reports for that.
Naming: each row’s `operationId` is its «puesto» (a numbered station of a «módulo»). A «puesto» is an `operation` in ids, filters and codes and a `station` in paths: the same number is the `id` `listStations` returns and the `stationId` of the station routes.
Days and hours are the plant’s own: a plant day is a calendar day `YYYY-MM-DD` in the organization’s timezone (`getOrganization` `timezone`), never UTC, and an `hour` is the clock hour 0–23 that starts at it in that zone. `day` is optional: without it, the plant’s today on the organization’s own clock, as on every single-day route of the API externa; the answer’s `day` says which day was read. `moduleId` narrows the check to one «módulo» and is echoed, null when not sent.
Plan: needs the plan feature `analytics` («Analítica»), the one its dashboard screen is gated by. Pro, a trial of Pro and an «exenta» organization answer it; a Light organization, of either plan version, does not: 403 `PLAN_LIMIT` with `feature: "analytics"`. `getPlan` `features` lists what the organization’s plan has.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `day` | query | string | no | The plant day to read, `YYYY-MM-DD` on the plant’s own clock (the organization’s timezone, never UTC). Omitted: the plant’s today on that clock; the answer’s `day` says which day was read. Not a real calendar day: 400 `INVALID_DAY`. |
| `moduleId` | query | integer | no | Narrow the answer to one «módulo»: the `id` of one from `listModules`. Omitted: the whole plant. Of another organization or unknown: 404 `MODULE_NOT_FOUND`. |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Responses
| Status | Description |
| --- | --- |
| `200` | The «marcas» log against the hourly buckets, row by row, and the count of each status. |
| `400` | `INVALID_DAY`: `day` is not a real calendar day written `YYYY-MM-DD`. `INVALID_REQUEST`: a query parameter is missing, of the wrong type or outside its allowed values; `details` lists each one as `{field, message}`. Body: `{statusCode, code, message}`, the message in Spanish, for the usuario. |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | `PLAN_LIMIT` with `feature: "analytics"`: the organization’s plan lacks «Analítica» (Pro, a trial of Pro and an «exenta» organization answer it; a Light organization, of either plan version, does not). `PLAN_LIMIT` with `feature: "aiConnector"`: a connected app reads an organization without «Conectar con IA». `INSUFFICIENT_SCOPE`: the credential lacks the «Permiso» `read`. Retrying does not help: the plan or the credential has to change. |
| `404` | `ORGANIZATION_NOT_FOUND`: the organization does not exist or is out of the credential’s reach. `MODULE_NOT_FOUND`: the «módulo» does not exist or is another organization’s. One body for a row that is missing and for one of another organization. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X GET 'https://api.fluenta.io/v1/external/organizations/{organizationId}/analytics/consistency' \
-H 'Authorization: Bearer '
```
---
# Get day report
URL: https://docs.fluenta.io/docs/reference/analytics/get-day-report
Operation: `getDayReport`
Required permission: `read`
`GET /v1/external/organizations/{organizationId}/analytics/day-report`
Reportes › Eficiencia for one plant day: every módulo hour by hour, each puesto’s units, meta and efficiency.
`day` is optional: without it, the plant’s today on the organization’s own clock, as on every single-day route of the API externa.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `day` | query | string | no | Plant day, YYYY-MM-DD on the plant’s clock. Omitted: the plant’s today, on its own clock. |
| `organizationId` | path | number | yes | |
### Responses
| Status | Description |
| --- | --- |
| `200` | `ExternalDayReportDto` |
| `400` | `INVALID_DAY`. |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | The credential lacks the «Permiso» the route names (`INSUFFICIENT_SCOPE`), or the organization’s plan does not include what was asked (`PLAN_LIMIT`): «Conectar con IA» for a connected app, or a report or analytics route whose plan feature a Light organization lacks (a Light of plan version 1 keeps `reports`). |
| `404` | The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X GET 'https://api.fluenta.io/v1/external/organizations/{organizationId}/analytics/day-report' \
-H 'Authorization: Bearer '
```
---
# Get efficiency
URL: https://docs.fluenta.io/docs/reference/analytics/get-efficiency
Operation: `getEfficiency`
Required permission: `read`
`GET /v1/external/organizations/{organizationId}/analytics/efficiency`
«Eficiencia» over a range of plant days: units made against the meta, per day, or per day and módulo, operario or puesto.
Groupings: day, module, operator, operation. Every puesto counts. The figure for each day, and for a módulo within it, equals Reportes › Eficiencia for that day, rounded as there; an operario’s is the «Clasificación»’s; a puesto’s is its «reporte por operación» for the day — with «El operario escoge la referencia» on, totalled by subproceso as that report does: complete units, the sum of each hour’s expected units, and every marca over that sum. Filters: one of `moduleId`, `operatorId`, `operationId` narrows the answer to that one, each row still its dashboard figure; with `groupBy=day` the rows are its own figure of each day and name it. Without a filter the answer is the whole plant’s.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `from` | query | string | yes | First plant day of the range, YYYY-MM-DD on the plant's clock, included. At most 92 days from `from` to `to`. |
| `to` | query | string | yes | Last plant day of the range, YYYY-MM-DD on the plant’s clock, included; not before `from`. |
| `groupBy` | query | string | no | One row per day, or per day and módulo / operario / puesto (`operation`) / referencia / categoría. Each figure answers the groupings the dashboard shows it by; any other is a 400 `GROUP_BY_UNSUPPORTED` that lists the ones it has. One of: `day`, `module`, `operator`, `operation`, `reference`, `category`. |
| `page` | query | number | no | 1-based page; 100 rows per page, by day then group. |
| `moduleId` | query | integer | no | Only this módulo. With `groupBy=day` (or none), its own figure of each day, each row naming it; with `module`, its rows; with `operation`, the puestos whose day is filed under it; and, for production, with `reference` or `category` too. One filter per request; a second one, or a grouping the filter does not answer, is a 400 `FILTER_UNSUPPORTED`. An id of another organization is a 404 `MODULE_NOT_FOUND`. |
| `operatorId` | query | integer | no | Only this operario: their own figure of each day (`groupBy=day`, or none) or their rows (`groupBy=operator`), as the «Clasificación» counts them. Efficiency and billing; production has no operario (400 `FILTER_UNSUPPORTED`). An id of another organization is a 404 `OPERATOR_NOT_FOUND`. |
| `operationId` | query | integer | no | Only this puesto: its own figure of each day (`groupBy=day`, or none) or its rows (`groupBy=operation`), as its «reporte por operación» totals them. Efficiency and billing; production has no puesto (400 `FILTER_UNSUPPORTED`). An id of another organization is a 404 `OPERATION_NOT_FOUND`. |
| `organizationId` | path | number | yes | |
### Responses
| Status | Description |
| --- | --- |
| `200` | `ExternalEfficiencyPageDto` |
| `400` | `INVALID_DAY` (a day that is not a real YYYY-MM-DD), `RANGE_INVALID` (`from` after `to`), `RANGE_TOO_LARGE` (more than 92 days), `GROUP_BY_UNSUPPORTED` (a grouping this figure has no dashboard counterpart for; `supported` lists the ones it has), `FILTER_UNSUPPORTED` (a filter this figure does not have, or not under that grouping, where `supported` lists the groupings the filter answers; or more than one filter at once). |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | The credential lacks the «Permiso» the route names (`INSUFFICIENT_SCOPE`), or the organization’s plan does not include what was asked (`PLAN_LIMIT`): «Conectar con IA» for a connected app, or a report or analytics route whose plan feature a Light organization lacks (a Light of plan version 1 keeps `reports`). |
| `404` | `ORGANIZATION_NOT_FOUND`; a filter’s id of another organization: `MODULE_NOT_FOUND`, `OPERATOR_NOT_FOUND`, `OPERATION_NOT_FOUND`. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
| `503` | `ANALYTICS_TIMEOUT`: ask a shorter range. |
### Example
```bash
curl -X GET 'https://api.fluenta.io/v1/external/organizations/{organizationId}/analytics/efficiency' \
-H 'Authorization: Bearer '
```
---
# Get efficiency by module
URL: https://docs.fluenta.io/docs/reference/analytics/get-efficiency-by-module
Operation: `getEfficiencyByModule`
Required permission: `read`
`GET /v1/external/organizations/{organizationId}/analytics/efficiency/modules`
Reportes › Eficiencia for one plant day, módulo by módulo: units, meta, efficiency and pausas.
What it is: the table of the dashboard’s «Reportes», tab «Eficiencia», for one plant day: one row per «módulo» (a production line) with the `units` every one of its «puestos» made, the `goal` (the sum of each «puesto»’s «meta» for each hour it worked), the `efficiency` and the day’s «pausas» (`pausedMinutes`, `pauses`). `modules` is ordered by name; a «módulo» with only «pausas» that day is a row too, and one with nothing is no row.
The dashboard’s table groups by the «módulo»’s name, and so does this: two «módulos» that share a name are one row, whose `moduleId` is then null. `getEfficiency` with `groupBy=module` keeps them apart by id.
`efficiency` is «Eficiencia»: units made × 100 ÷ the «meta» of the same span, a percentage with 2 decimals (100 = exactly the goal, it can pass 100; never a 0–1 ratio), null where the goal is 0. Every «puesto» counts towards it, a «puesto de salida» or not.
Days and hours are the plant’s own: a plant day is a calendar day `YYYY-MM-DD` in the organization’s timezone (`getOrganization` `timezone`), never UTC, and an `hour` is the clock hour 0–23 that starts at it in that zone. `day` is optional: without it, the plant’s today on the organization’s own clock, as on every single-day route of the API externa; the answer’s `day` says which day was read.
Plan: needs the plan feature `reports` («Reportes»), the one its dashboard screen is gated by. Pro, a trial of Pro, an «exenta» organization and a Light of plan version 1 (`getPlan` `planVersion`) answer it; a Light of plan version 2 does not: 403 `PLAN_LIMIT` with `feature: "reports"`. `getPlan` `features` lists what the organization’s plan has.
See also: `getDayReport` for the same day with each «módulo» hour by hour; `getBillingByModule` for the day’s «Facturación» by «módulo».
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `day` | query | string | no | Plant day, YYYY-MM-DD on the plant’s clock. Omitted: the plant’s today, on its own clock. |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Responses
| Status | Description |
| --- | --- |
| `200` | The day’s «Eficiencia», one row per «módulo». |
| `400` | `INVALID_DAY`: `day` is not a real calendar day written `YYYY-MM-DD`. `INVALID_REQUEST`: a query parameter is missing, of the wrong type or outside its allowed values; `details` lists each one as `{field, message}`. Body: `{statusCode, code, message}`, the message in Spanish, for the usuario. |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | `PLAN_LIMIT` with `feature: "reports"`: the organization’s plan lacks «Reportes» (Pro, a trial of Pro, an «exenta» organization and a Light of plan version 1 (`getPlan` `planVersion`) answer it; a Light of plan version 2 does not). `PLAN_LIMIT` with `feature: "aiConnector"`: a connected app reads an organization without «Conectar con IA». `INSUFFICIENT_SCOPE`: the credential lacks the «Permiso» `read`. Retrying does not help: the plan or the credential has to change. |
| `404` | `ORGANIZATION_NOT_FOUND`: the organization does not exist or is out of the credential’s reach. One body for a row that is missing and for one of another organization. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X GET 'https://api.fluenta.io/v1/external/organizations/{organizationId}/analytics/efficiency/modules' \
-H 'Authorization: Bearer '
```
---
# Get hourly activity
URL: https://docs.fluenta.io/docs/reference/analytics/get-hourly-activity
Operation: `getHourlyActivity`
Required permission: `read`
`GET /v1/external/organizations/{organizationId}/analytics/hourly`
Analítica hour by hour for one plant day, from the marcas log: units, meta, efficiency, billing and pace per hour, for the plant, one módulo or one operario.
What it is: «Analítica» hour by hour for one plant day, the hourly view of the dashboard’s «Reportes», tab «Analítica». `hours` holds one entry per clock hour with activity and `totals` the day. Without a filter it is the whole plant; `moduleId` narrows it to one «módulo»; `operatorId` to one «operario» (their own «marcas» and their share of each hour’s «meta», so its totals equal that person’s row of `getOperatorsActivity`). `moduleId` and `operatorId` are echoed in the answer, null when not sent.
How it is computed: `units`, `goal`, `efficiency`, `billableUnits` and `billedAmount` come from the hourly buckets, so they agree with the reports (`goal` is the sum of each «puesto»’s «meta» for the hour). `events`, `online`, `offline`, `legacy`, the two `secondsPerUnit` percentiles and `idleGapsOverN` come from the «marcas» log. A gap is counted in the hour of its later «marca», so the hours add up to the totals.
Source: «Analítica» reads the «marcas» log (one row per unit an «operario» registered on the app, with its time and channel) beside the hourly buckets the reports are built from, so it can tell pace, connectivity and gaps that the reports cannot.
`efficiency` is «Eficiencia»: units made × 100 ÷ the «meta» of the same span, a percentage with 2 decimals (100 = exactly the goal, it can pass 100; never a 0–1 ratio), null where the goal is 0. Every «puesto» counts towards it, a «puesto de salida» or not.
Money is «Facturación»: the units of the «puestos de salida» × the price per unit each was billed at, in the plant’s currency (`currency`, ISO 4217: `EUR` for a plant in España, `COP` for every other), in whole currency units with up to 2 decimals, never cents. Whether a money figure reads null or 0 where nothing was priced is stated on each field. `billableUnits` and `billedAmount` are null, not 0, when no «puesto de salida» is in what was asked.
Days and hours are the plant’s own: a plant day is a calendar day `YYYY-MM-DD` in the organization’s timezone (`getOrganization` `timezone`), never UTC, and an `hour` is the clock hour 0–23 that starts at it in that zone. `day` is optional: without it, the plant’s today on the organization’s own clock, as on every single-day route of the API externa; the answer’s `day` says which day was read.
Plan: needs the plan feature `analytics` («Analítica»), the one its dashboard screen is gated by. Pro, a trial of Pro and an «exenta» organization answer it; a Light organization, of either plan version, does not: 403 `PLAN_LIMIT` with `feature: "analytics"`. `getPlan` `features` lists what the organization’s plan has.
See also: `getOperatorsActivity` for the same day by «operario»; `getConsistency` to check the log against the buckets.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `day` | query | string | no | The plant day to read, `YYYY-MM-DD` on the plant’s own clock (the organization’s timezone, never UTC). Omitted: the plant’s today on that clock; the answer’s `day` says which day was read. Not a real calendar day: 400 `INVALID_DAY`. |
| `moduleId` | query | integer | no | Narrow the answer to one «módulo»: the `id` of one from `listModules`. Omitted: the whole plant. Of another organization or unknown: 404 `MODULE_NOT_FOUND`. |
| `operatorId` | query | integer | no | Narrow the answer to one «operario» (the `id` of one from `listOperators`): their own «marcas» and their share of each hour’s «meta». Omitted: every «operario». Of another organization or unknown: 404 `OPERATOR_NOT_FOUND`. |
| `idleMinutes` | query | number | no | In minutes: a gap between two consecutive «marcas» longer than this counts as one «pausa sin registro» (`idleGapsOverN`, `idleGaps`). One of 5, 10 or 15; default 5. The answer echoes the value used. |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Responses
| Status | Description |
| --- | --- |
| `200` | The day hour by hour, and its totals. |
| `400` | `INVALID_DAY`: `day` is not a real calendar day written `YYYY-MM-DD`. `INVALID_REQUEST`: a query parameter is missing, of the wrong type or outside its allowed values; `details` lists each one as `{field, message}`. Body: `{statusCode, code, message}`, the message in Spanish, for the usuario. |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | `PLAN_LIMIT` with `feature: "analytics"`: the organization’s plan lacks «Analítica» (Pro, a trial of Pro and an «exenta» organization answer it; a Light organization, of either plan version, does not). `PLAN_LIMIT` with `feature: "aiConnector"`: a connected app reads an organization without «Conectar con IA». `INSUFFICIENT_SCOPE`: the credential lacks the «Permiso» `read`. Retrying does not help: the plan or the credential has to change. |
| `404` | `ORGANIZATION_NOT_FOUND`: the organization does not exist or is out of the credential’s reach. `MODULE_NOT_FOUND`: the «módulo» does not exist or is another organization’s. `OPERATOR_NOT_FOUND`: the «operario» does not exist or is another organization’s. One body for a row that is missing and for one of another organization. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X GET 'https://api.fluenta.io/v1/external/organizations/{organizationId}/analytics/hourly' \
-H 'Authorization: Bearer '
```
---
# Get leaderboard
URL: https://docs.fluenta.io/docs/reference/analytics/get-leaderboard
Operation: `getLeaderboard`
Required permission: `read`
`GET /v1/external/organizations/{organizationId}/analytics/leaderboard`
The «Clasificación» of one plant day: every operario’s units, meta, efficiency and «Facturado», best first.
`day` is optional: without it, the plant’s today on the organization’s own clock, as on every single-day route of the API externa.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `day` | query | string | no | Plant day, YYYY-MM-DD on the plant’s clock. Omitted: the plant’s today, on its own clock. |
| `organizationId` | path | number | yes | |
### Responses
| Status | Description |
| --- | --- |
| `200` | `ExternalLeaderboardDto` |
| `400` | `INVALID_DAY`. |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | The credential lacks the «Permiso» the route names (`INSUFFICIENT_SCOPE`), or the organization’s plan does not include what was asked (`PLAN_LIMIT`): «Conectar con IA» for a connected app, or a report or analytics route whose plan feature a Light organization lacks (a Light of plan version 1 keeps `reports`). |
| `404` | The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X GET 'https://api.fluenta.io/v1/external/organizations/{organizationId}/analytics/leaderboard' \
-H 'Authorization: Bearer '
```
---
# Get month
URL: https://docs.fluenta.io/docs/reference/analytics/get-month
Operation: `getMonth`
Required permission: `read`
`GET /v1/external/organizations/{organizationId}/analytics/month`
The plant’s last 30 days, up to its today: units, meta, efficiency and «Facturación» per day. The 30-day chart of Reportes.
What it is: the whole plant day by day over its last 30 plant days, today included, the 30-day chart of the dashboard’s «Reportes». Each entry of `days` is one plant day: `units` made by every «puesto», `goal` (the sum of each «puesto»’s «meta» for each hour it worked), `efficiency`, and `billedAmount`, the day’s «Facturación». `days` is oldest first, and a day with no work is no row.
It takes no parameter: the window is fixed and ends on the plant’s today, on its own clock. For another window, or to group by «módulo», «operario» or «puesto», use `getEfficiency` and `getBilling`.
`efficiency` is «Eficiencia»: units made × 100 ÷ the «meta» of the same span, a percentage with 2 decimals (100 = exactly the goal, it can pass 100; never a 0–1 ratio), null where the goal is 0. Every «puesto» counts towards it, a «puesto de salida» or not.
Money is «Facturación»: the units of the «puestos de salida» × the price per unit each was billed at, in the plant’s currency (`currency`, ISO 4217: `EUR` for a plant in España, `COP` for every other), in whole currency units with up to 2 decimals, never cents. Whether a money figure reads null or 0 where nothing was priced is stated on each field. A day with production and nothing billed keeps its units and a null `billedAmount`.
Days and hours are the plant’s own: a plant day is a calendar day `YYYY-MM-DD` in the organization’s timezone (`getOrganization` `timezone`), never UTC, and an `hour` is the clock hour 0–23 that starts at it in that zone.
Plan: needs the plan feature `reports` («Reportes»), the one its dashboard screen is gated by. Pro, a trial of Pro, an «exenta» organization and a Light of plan version 1 (`getPlan` `planVersion`) answer it; a Light of plan version 2 does not: 403 `PLAN_LIMIT` with `feature: "reports"`. `getPlan` `features` lists what the organization’s plan has.
Heavy read: besides the credential’s ordinary budget it spends the heavy one, 20 requests per 60 seconds per credential and organization; past it the answer is 429 `RATE_LIMITED` with `Retry-After`.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Responses
| Status | Description |
| --- | --- |
| `200` | The plant’s last 30 plant days, one row per day worked. |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | `PLAN_LIMIT` with `feature: "reports"`: the organization’s plan lacks «Reportes» (Pro, a trial of Pro, an «exenta» organization and a Light of plan version 1 (`getPlan` `planVersion`) answer it; a Light of plan version 2 does not). `PLAN_LIMIT` with `feature: "aiConnector"`: a connected app reads an organization without «Conectar con IA». `INSUFFICIENT_SCOPE`: the credential lacks the «Permiso» `read`. Retrying does not help: the plan or the credential has to change. |
| `404` | `ORGANIZATION_NOT_FOUND`: the organization does not exist or is out of the credential’s reach. One body for a row that is missing and for one of another organization. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X GET 'https://api.fluenta.io/v1/external/organizations/{organizationId}/analytics/month' \
-H 'Authorization: Bearer '
```
---
# Get operators activity
URL: https://docs.fluenta.io/docs/reference/analytics/get-operators-activity
Operation: `getOperatorsActivity`
Required permission: `read`
`GET /v1/external/organizations/{organizationId}/analytics/operators`
Analítica by operario for one plant day: each one’s units, pace, active hours, channels, efficiency and billing, with the totals.
What it is: «Analítica» by «operario» for one plant day, the per-person view of the dashboard’s «Reportes», tab «Analítica». `operators` holds one row per «operario» with production that day and `totals` the day; `moduleId` narrows both to one «módulo» and is echoed, null when not sent.
How it is computed: a person’s `units`, `goal`, `efficiency`, `billableUnits` and `totalPrice` follow their own «marcas», the same rule as the «Clasificación» (`getLeaderboard`), so the two agree about a person: `goal` is their share of the «meta» of each «puesto»-hour they marked in. `eventUnits`, `activeHours`, `unitsPerHour`, `secondsPerUnitP50`, `idleGaps`, `online`, `offline`, `legacy` and `offlineShare` come from the «marcas» log. «Marcas» registered with nobody seated belong to no row and are reported once, as `totals.unattributedEventUnits`.
Source: «Analítica» reads the «marcas» log (one row per unit an «operario» registered on the app, with its time and channel) beside the hourly buckets the reports are built from, so it can tell pace, connectivity and gaps that the reports cannot.
`efficiency` is «Eficiencia»: units made × 100 ÷ the «meta» of the same span, a percentage with 2 decimals (100 = exactly the goal, it can pass 100; never a 0–1 ratio), null where the goal is 0. Every «puesto» counts towards it, a «puesto de salida» or not.
Money is «Facturación»: the units of the «puestos de salida» × the price per unit each was billed at, in the plant’s currency (`currency`, ISO 4217: `EUR` for a plant in España, `COP` for every other), in whole currency units with up to 2 decimals, never cents. Whether a money figure reads null or 0 where nothing was priced is stated on each field. Here the money figure is `totalPrice`: what the person’s own «marcas» on a «puesto de salida» bill, null for someone who sat on none.
Days and hours are the plant’s own: a plant day is a calendar day `YYYY-MM-DD` in the organization’s timezone (`getOrganization` `timezone`), never UTC, and an `hour` is the clock hour 0–23 that starts at it in that zone. `day` is optional: without it, the plant’s today on the organization’s own clock, as on every single-day route of the API externa; the answer’s `day` says which day was read.
Plan: needs the plan feature `analytics` («Analítica»), the one its dashboard screen is gated by. Pro, a trial of Pro and an «exenta» organization answer it; a Light organization, of either plan version, does not: 403 `PLAN_LIMIT` with `feature: "analytics"`. `getPlan` `features` lists what the organization’s plan has.
See also: `getHourlyActivity` with `operatorId` for one of these people hour by hour.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `day` | query | string | no | The plant day to read, `YYYY-MM-DD` on the plant’s own clock (the organization’s timezone, never UTC). Omitted: the plant’s today on that clock; the answer’s `day` says which day was read. Not a real calendar day: 400 `INVALID_DAY`. |
| `moduleId` | query | integer | no | Narrow the answer to one «módulo»: the `id` of one from `listModules`. Omitted: the whole plant. Of another organization or unknown: 404 `MODULE_NOT_FOUND`. |
| `idleMinutes` | query | number | no | In minutes: a gap between two consecutive «marcas» longer than this counts as one «pausa sin registro» (`idleGapsOverN`, `idleGaps`). One of 5, 10 or 15; default 5. The answer echoes the value used. |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Responses
| Status | Description |
| --- | --- |
| `200` | The day by «operario», and its totals. |
| `400` | `INVALID_DAY`: `day` is not a real calendar day written `YYYY-MM-DD`. `INVALID_REQUEST`: a query parameter is missing, of the wrong type or outside its allowed values; `details` lists each one as `{field, message}`. Body: `{statusCode, code, message}`, the message in Spanish, for the usuario. |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | `PLAN_LIMIT` with `feature: "analytics"`: the organization’s plan lacks «Analítica» (Pro, a trial of Pro and an «exenta» organization answer it; a Light organization, of either plan version, does not). `PLAN_LIMIT` with `feature: "aiConnector"`: a connected app reads an organization without «Conectar con IA». `INSUFFICIENT_SCOPE`: the credential lacks the «Permiso» `read`. Retrying does not help: the plan or the credential has to change. |
| `404` | `ORGANIZATION_NOT_FOUND`: the organization does not exist or is out of the credential’s reach. `MODULE_NOT_FOUND`: the «módulo» does not exist or is another organization’s. One body for a row that is missing and for one of another organization. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X GET 'https://api.fluenta.io/v1/external/organizations/{organizationId}/analytics/operators' \
-H 'Authorization: Bearer '
```
---
# Get operator report
URL: https://docs.fluenta.io/docs/reference/analytics/get-operator-report
Operation: `getOperatorReport`
Required permission: `read`
`GET /v1/external/organizations/{organizationId}/analytics/operators/{operatorId}`
One operario’s report: units, meta and efficiency per day over a range of up to 92 days, and hour by hour for a single day.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `operatorId` | path | number | yes | |
| `from` | query | string | yes | First plant day, YYYY-MM-DD, included. At most 92 days from `from` to `to`. |
| `to` | query | string | yes | Last plant day, YYYY-MM-DD, included. |
| `organizationId` | path | number | yes | |
### Responses
| Status | Description |
| --- | --- |
| `200` | `ExternalOperatorReportDto` |
| `400` | `INVALID_DAY`, `RANGE_INVALID`, `RANGE_TOO_LARGE`. |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | The credential lacks the «Permiso» the route names (`INSUFFICIENT_SCOPE`), or the organization’s plan does not include what was asked (`PLAN_LIMIT`): «Conectar con IA» for a connected app, or a report or analytics route whose plan feature a Light organization lacks (a Light of plan version 1 keeps `reports`). |
| `404` | The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X GET 'https://api.fluenta.io/v1/external/organizations/{organizationId}/analytics/operators/{operatorId}' \
-H 'Authorization: Bearer '
```
---
# Get pauses
URL: https://docs.fluenta.io/docs/reference/analytics/get-pauses
Operation: `getPauses`
Required permission: `read`
`GET /v1/external/organizations/{organizationId}/analytics/pauses`
The «Pausas» report over a range of up to 31 plant days: every pausa, newest first (at most 2000), and the figures over the whole range.
What it is: the dashboard’s «Reportes», tab «Pausas». A «pausa» is a stop one «operario» takes at their «puesto» with «Pausar» on the app, ended with «Reanudar», by an administrador from the board, or by the system (end of the «jornada», the «operario» leaving the «puesto», the feature going off). It is not a «descanso» (a «módulo»’s scheduled break) and not one of «Analítica»’s «pausas sin registro» (gaps inferred from the «marcas» log).
What it returns: `rows`, one per «pausa» that started in the range, newest first, at most 2000; `summary`, the figures over every «pausa» of the range, never only the rows sent; and `truncated`, true when the range holds more than `rows` carries (then narrow the range or filter). `moduleId` keeps the «pausas» of that «módulo»’s «puestos», `operatorId` the ones that «operario» started.
Durations are in minutes. `durationMinutes` is the whole «pausa» by the clock; `minutes` is what it took off the «puesto»’s «metas» (its part inside the «jornada» and outside «descansos»), the figure the other reports’ `pausedMinutes` add up. Both are null while the «pausa» is open. `summary` counts by the clock: its `minutes`, `averageMinutes` and `longestMinutes` are over `durationMinutes`.
Range: `from` and `to` are plant days, both included, at most 31 days apart counting both (`RANGE_TOO_LARGE` past it; note the cap is shorter than the efficiency charts’).
Days and hours are the plant’s own: a plant day is a calendar day `YYYY-MM-DD` in the organization’s timezone (`getOrganization` `timezone`), never UTC, and an `hour` is the clock hour 0–23 that starts at it in that zone. `startMinute` and `endMinute` are minutes of the plant day on that clock (0 is 00:00); `startedAt` and `endedAt` are the same moments as instants, ISO 8601 in UTC.
Naming: each row’s `operationId` is its «puesto» (a numbered station of a «módulo»). A «puesto» is an `operation` in ids, filters and codes and a `station` in paths: the same number is the `id` `listStations` returns and the `stationId` of the station routes.
Plan: needs the plan feature `pauses` («Pausas»), the one its dashboard screen is gated by. Pro, a trial of Pro and an «exenta» organization answer it; a Light organization, of either plan version, does not: 403 `PLAN_LIMIT` with `feature: "pauses"`. `getPlan` `features` lists what the organization’s plan has. On a plan that has it, a plant that never switched on «Habilitar pausas» answers 200 with no rows.
Heavy read: besides the credential’s ordinary budget it spends the heavy one, 20 requests per 60 seconds per credential and organization; past it the answer is 429 `RATE_LIMITED` with `Retry-After`.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `from` | query | string | yes | First plant day of the range, `YYYY-MM-DD` on the plant’s own clock, included. At most 31 days from `from` to `to`, counting both: past it, 400 `RANGE_TOO_LARGE`. |
| `to` | query | string | yes | Last plant day of the range, `YYYY-MM-DD` on the plant’s own clock, included. Before `from`: 400 `RANGE_INVALID`. |
| `moduleId` | query | integer | no | Only the «pausas» taken at the «puestos» of this «módulo» (the `id` of one from `listModules`). Of another organization or unknown: 404 `MODULE_NOT_FOUND`. |
| `operatorId` | query | integer | no | Only the «pausas» this «operario» took (the `id` of one from `listOperators`). Of another organization or unknown: 404 `OPERATOR_NOT_FOUND`. |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Responses
| Status | Description |
| --- | --- |
| `200` | The «pausas» of the range, newest first, and the figures over all of them. |
| `400` | `INVALID_DAY`: `from` or `to` is not a real calendar day written `YYYY-MM-DD`. `RANGE_INVALID`: `from` is after `to`. `RANGE_TOO_LARGE`: more than 31 plant days from `from` to `to`, both included (`maxDays` in the body says the cap); ask a shorter range. `INVALID_REQUEST`: a query parameter is missing, of the wrong type or outside its allowed values; `details` lists each one as `{field, message}`. Body: `{statusCode, code, message}`, the message in Spanish, for the usuario. |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | `PLAN_LIMIT` with `feature: "pauses"`: the organization’s plan lacks «Pausas» (Pro, a trial of Pro and an «exenta» organization answer it; a Light organization, of either plan version, does not). `PLAN_LIMIT` with `feature: "aiConnector"`: a connected app reads an organization without «Conectar con IA». `INSUFFICIENT_SCOPE`: the credential lacks the «Permiso» `read`. Retrying does not help: the plan or the credential has to change. |
| `404` | `ORGANIZATION_NOT_FOUND`: the organization does not exist or is out of the credential’s reach. `MODULE_NOT_FOUND`: the «módulo» does not exist or is another organization’s. `OPERATOR_NOT_FOUND`: the «operario» does not exist or is another organization’s. One body for a row that is missing and for one of another organization. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X GET 'https://api.fluenta.io/v1/external/organizations/{organizationId}/analytics/pauses' \
-H 'Authorization: Bearer '
```
---
# Get production
URL: https://docs.fluenta.io/docs/reference/analytics/get-production
Operation: `getProduction`
Required permission: `read`
`GET /v1/external/organizations/{organizationId}/analytics/production`
«Producido» over a range of plant days, per day, or per day and módulo, referencia or categoría.
Groupings: day, module, reference, category. A typed hour counts its «conteo manual», any other hour the puestos de salida’s marcas, and an hour folded into a typed «Extra» 0: a módulo’s figure for a day is its «Tablero de producción» total (getHoursBoard `totals.produced`). The plant’s figure for a day (`groupBy=day`) is every módulo that produced added up — the boards’ totals, a módulo deleted since included, as Reportes keep its past days. The live board’s «unidades hoy» (getLiveBoard `plant.produced`) is the same sum over the módulos with a meta that day only, because it is the numerator of the plant’s eficiencia: it leaves out a módulo that produced with no meta (outside its jornada, or on an OC with no meta per hour), and this figure counts it. Filter: `moduleId` alone, under any grouping — a typed hour belongs to no puesto and no operario.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `from` | query | string | yes | First plant day of the range, YYYY-MM-DD on the plant's clock, included. At most 92 days from `from` to `to`. |
| `to` | query | string | yes | Last plant day of the range, YYYY-MM-DD on the plant’s clock, included; not before `from`. |
| `groupBy` | query | string | no | One row per day, or per day and módulo / operario / puesto (`operation`) / referencia / categoría. Each figure answers the groupings the dashboard shows it by; any other is a 400 `GROUP_BY_UNSUPPORTED` that lists the ones it has. One of: `day`, `module`, `operator`, `operation`, `reference`, `category`. |
| `page` | query | number | no | 1-based page; 100 rows per page, by day then group. |
| `moduleId` | query | integer | no | Only this módulo. With `groupBy=day` (or none), its own figure of each day, each row naming it; with `module`, its rows; with `operation`, the puestos whose day is filed under it; and, for production, with `reference` or `category` too. One filter per request; a second one, or a grouping the filter does not answer, is a 400 `FILTER_UNSUPPORTED`. An id of another organization is a 404 `MODULE_NOT_FOUND`. |
| `operatorId` | query | integer | no | Only this operario: their own figure of each day (`groupBy=day`, or none) or their rows (`groupBy=operator`), as the «Clasificación» counts them. Efficiency and billing; production has no operario (400 `FILTER_UNSUPPORTED`). An id of another organization is a 404 `OPERATOR_NOT_FOUND`. |
| `operationId` | query | integer | no | Only this puesto: its own figure of each day (`groupBy=day`, or none) or its rows (`groupBy=operation`), as its «reporte por operación» totals them. Efficiency and billing; production has no puesto (400 `FILTER_UNSUPPORTED`). An id of another organization is a 404 `OPERATION_NOT_FOUND`. |
| `organizationId` | path | number | yes | |
### Responses
| Status | Description |
| --- | --- |
| `200` | `ExternalProductionPageDto` |
| `400` | `INVALID_DAY` (a day that is not a real YYYY-MM-DD), `RANGE_INVALID` (`from` after `to`), `RANGE_TOO_LARGE` (more than 92 days), `GROUP_BY_UNSUPPORTED` (a grouping this figure has no dashboard counterpart for; `supported` lists the ones it has), `FILTER_UNSUPPORTED` (a filter this figure does not have, or not under that grouping, where `supported` lists the groupings the filter answers; or more than one filter at once). |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | The credential lacks the «Permiso» the route names (`INSUFFICIENT_SCOPE`), or the organization’s plan does not include what was asked (`PLAN_LIMIT`): «Conectar con IA» for a connected app, or a report or analytics route whose plan feature a Light organization lacks (a Light of plan version 1 keeps `reports`). |
| `404` | `ORGANIZATION_NOT_FOUND`; a filter’s id of another organization: `MODULE_NOT_FOUND`, `OPERATOR_NOT_FOUND`, `OPERATION_NOT_FOUND`. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
| `503` | `ANALYTICS_TIMEOUT`: ask a shorter range. |
### Example
```bash
curl -X GET 'https://api.fluenta.io/v1/external/organizations/{organizationId}/analytics/production' \
-H 'Authorization: Bearer '
```
---
# Get live board
URL: https://docs.fluenta.io/docs/reference/board/get-live-board
Operation: `getLiveBoard`
Required permission: `read`
`GET /v1/external/organizations/{organizationId}/board`
The live board of the whole floor for a day: each módulo’s «eficiencia», units and goal so far, and the plant’s.
`day` is optional: without it the board reads the plant’s today, on the organization’s own clock — the rule of every single-day route of the API externa.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `day` | query | string (date) | no | The plant day, `YYYY-MM-DD` in the organization’s clock. Omitted: the plant’s today. |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Responses
| Status | Description |
| --- | --- |
| `200` | `ExternalLiveBoardDto` |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | The credential lacks the «Permiso» the route names (`INSUFFICIENT_SCOPE`), or the organization’s plan does not include what was asked (`PLAN_LIMIT`): «Conectar con IA» for a connected app, or a report or analytics route whose plan feature a Light organization lacks (a Light of plan version 1 keeps `reports`). |
| `404` | The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X GET 'https://api.fluenta.io/v1/external/organizations/{organizationId}/board' \
-H 'Authorization: Bearer '
```
---
# List catalogue
URL: https://docs.fluenta.io/docs/reference/catalogue/list-catalogue
Operation: `listCatalogue`
Required permission: `read`
`GET /v1/external/organizations/{organizationId}/catalogue`
The «Catálogo de referencias»: the organization’s reusable styles by name, with the goal, price, category and «Subprocesos» a new OC from each starts with.
Answers the «Catálogo de referencias»: the organization’s reusable styles by name, each with the values a new OC made from it starts with (goal, price, category, «Subprocesos»). An organization with an empty catalogue answers an empty list.
Each `id` is the `templateId` of `updateCatalogueEntry`, `deleteCatalogueEntry` and `addReferencesFromCatalogue`.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Responses
| Status | Description |
| --- | --- |
| `200` | |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | The credential lacks the «Permiso» the route names (`INSUFFICIENT_SCOPE`), or the organization’s plan does not include what was asked (`PLAN_LIMIT`): «Conectar con IA» for a connected app, or a report or analytics route whose plan feature a Light organization lacks (a Light of plan version 1 keeps `reports`). |
| `404` | The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X GET 'https://api.fluenta.io/v1/external/organizations/{organizationId}/catalogue' \
-H 'Authorization: Bearer '
```
---
# Create catalogue entry
URL: https://docs.fluenta.io/docs/reference/catalogue/create-catalogue-entry
Operation: `createCatalogueEntry`
Required permission: `program`
`POST /v1/external/organizations/{organizationId}/catalogue`
Add a reusable style to the «Catálogo de referencias», as its «Nueva referencia» does: the name is unique in the catálogo (409 `REFERENCE_TEMPLATE_EXISTS`).
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `Idempotency-Key` | header | string | no | A fresh unique string per intended write (1 to 255 visible ASCII characters). Sent again unchanged only to retry THAT write after a timeout or a 409 `IDEMPOTENCY_IN_PROGRESS`: the stored answer comes back (`Idempotency-Replayed: true`) and nothing is written twice. The same key with another request is a 422 `IDEMPOTENCY_KEY_REUSED`. |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Request body
`application/json`
| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | yes | «Referencia»: stored trimmed with inner whitespace collapsed; unique in the catálogo, case-insensitively (409 otherwise). |
| `description` | string | no | «Descripción»: for search only, never copied to an OC. |
| `garment` | string | no | «Categoría»: one of the plant's categories (any case; stored in the list's spelling) when the plant has a list, free text otherwise. |
| `goal` | integer, 1–100000 | no | «Meta por hora»: a whole number from 1 to 100000. |
| `orderNumber` | string | no | «Corte (OC)» by default: the order number a new OC made from this entry starts with, still editable there. A starting value only: the entry is never linked to an OC. |
| `total` | integer, 1–1000000 | no | «Total de unidades» by default: a whole number from 1 to 1000000, the total a new OC made from this entry starts with. |
| `price` | number, 0–1000000000 | no | «Precio por unidad»: 0 or more, at most two decimals. Not stored on a plan without facturación. |
| `color` | string | no | «Color»: `#RRGGBB`, or `#RGB` (expanded). Not stored on a plan without the referencia colour. |
| `step1Name` | string | no | «Subproceso 1»: the first named step of the «referencia», with its own goal (`step1Goal`). A name without its goal is refused with a 400. Stored whether or not the plant uses «Subprocesos». |
| `step1Goal` | integer, 1–100000 | no | «Meta subproceso 1»: a whole number in the «Meta por hora» range, 1 to 100000. |
| `step2Name` | string | no | «Subproceso 2»: the second step; needs step 1. |
| `step2Goal` | integer, 1–100000 | no | «Meta subproceso 2». |
### Responses
| Status | Description |
| --- | --- |
| `201` | `ExternalCatalogueEntryDto` |
| `400` | `INVALID_REQUEST`: a body or a parameter the route cannot take, as `{statusCode, code: 'INVALID_REQUEST', message, details}`, with one `{field, message}` in `details` per failure (`field` the body field as a path, such as `items.0.name`; null when the failure names none). Or a value the dashboard refuses (its own `code`), or `INVALID_IDEMPOTENCY_KEY`. Nothing changed. |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | `ROLE_NOT_ALLOWED`: the usuario’s role does not make this change in the dashboard (it needs ADMIN or MODERATOR, or an account with platform-wide access);`INSUFFICIENT_SCOPE`: the credential lacks «Gestionar el Programa»; `PLAN_LIMIT`: the organization’s plan does not answer this credential (a connected app needs «Conectar con IA», Plan Pro). Nothing changed. |
| `404` | The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both. |
| `409` | `IDEMPOTENCY_IN_PROGRESS` (with `Retry-After`): the same `Idempotency-Key` is still being answered; retry it unchanged. Or the dashboard’s own conflict. |
| `422` | `IDEMPOTENCY_KEY_REUSED`: the `Idempotency-Key` answered another request. Nothing changed. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X POST 'https://api.fluenta.io/v1/external/organizations/{organizationId}/catalogue' \
-H 'Authorization: Bearer ' \
-H 'Content-Type: application/json' \
-d @body.json
```
---
# Update catalogue entry
URL: https://docs.fluenta.io/docs/reference/catalogue/update-catalogue-entry
Operation: `updateCatalogueEntry`
Required permission: `program`
`PATCH /v1/external/organizations/{organizationId}/catalogue/{templateId}`
Edit a template of the «Catálogo de referencias»: only the fields sent change; null clears an optional one. No OC changes.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `templateId` | path | integer | yes | |
| `Idempotency-Key` | header | string | no | A fresh unique string per intended write (1 to 255 visible ASCII characters). Sent again unchanged only to retry THAT write after a timeout or a 409 `IDEMPOTENCY_IN_PROGRESS`: the stored answer comes back (`Idempotency-Replayed: true`) and nothing is written twice. The same key with another request is a 422 `IDEMPOTENCY_KEY_REUSED`. |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Request body
`application/json`
| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | no | «Referencia»: stored trimmed with inner whitespace collapsed; unique in the catálogo, case-insensitively (409 otherwise). |
| `description` | string | no | «Descripción»: for search only, never copied to an OC. |
| `garment` | string | no | «Categoría»: one of the plant's categories (any case; stored in the list's spelling) when the plant has a list, free text otherwise. |
| `goal` | integer, 1–100000 | no | «Meta por hora»: a whole number from 1 to 100000. |
| `orderNumber` | string | no | «Corte (OC)» by default: the order number a new OC made from this entry starts with, still editable there. A starting value only: the entry is never linked to an OC. |
| `total` | integer, 1–1000000 | no | «Total de unidades» by default: a whole number from 1 to 1000000, the total a new OC made from this entry starts with. |
| `price` | number, 0–1000000000 | no | «Precio por unidad»: 0 or more, at most two decimals. Not stored on a plan without facturación. |
| `color` | string | no | «Color»: `#RRGGBB`, or `#RGB` (expanded). Not stored on a plan without the referencia colour. |
| `step1Name` | string | no | «Subproceso 1»: the first named step of the «referencia», with its own goal (`step1Goal`). A name without its goal is refused with a 400. Stored whether or not the plant uses «Subprocesos». |
| `step1Goal` | integer, 1–100000 | no | «Meta subproceso 1»: a whole number in the «Meta por hora» range, 1 to 100000. |
| `step2Name` | string | no | «Subproceso 2»: the second step; needs step 1. |
| `step2Goal` | integer, 1–100000 | no | «Meta subproceso 2». |
### Responses
| Status | Description |
| --- | --- |
| `200` | `ExternalCatalogueEntryDto` |
| `400` | `INVALID_REQUEST`: a body or a parameter the route cannot take, as `{statusCode, code: 'INVALID_REQUEST', message, details}`, with one `{field, message}` in `details` per failure (`field` the body field as a path, such as `items.0.name`; null when the failure names none). Or a value the dashboard refuses (its own `code`), or `INVALID_IDEMPOTENCY_KEY`. Nothing changed. |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | `ROLE_NOT_ALLOWED`: the usuario’s role does not make this change in the dashboard (it needs ADMIN or MODERATOR, or an account with platform-wide access);`INSUFFICIENT_SCOPE`: the credential lacks «Gestionar el Programa»; `PLAN_LIMIT`: the organization’s plan does not answer this credential (a connected app needs «Conectar con IA», Plan Pro). Nothing changed. |
| `404` | The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both. |
| `409` | `IDEMPOTENCY_IN_PROGRESS` (with `Retry-After`): the same `Idempotency-Key` is still being answered; retry it unchanged. Or the dashboard’s own conflict. |
| `422` | `IDEMPOTENCY_KEY_REUSED`: the `Idempotency-Key` answered another request. Nothing changed. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X PATCH 'https://api.fluenta.io/v1/external/organizations/{organizationId}/catalogue/{templateId}' \
-H 'Authorization: Bearer ' \
-H 'Content-Type: application/json' \
-d @body.json
```
---
# Delete catalogue entry
URL: https://docs.fluenta.io/docs/reference/catalogue/delete-catalogue-entry
Operation: `deleteCatalogueEntry`
Required permission: `program`
`DELETE /v1/external/organizations/{organizationId}/catalogue/{templateId}`
Delete a template of the «Catálogo de referencias» (no OC changes), only with the `confirmationToken` its preview returned once the usuario said yes: 422 `CONFIRMATION_REQUIRED` without it, 409 `PREVIEW_STALE` when it ran out or the template changed since.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `templateId` | path | integer | yes | |
| `Idempotency-Key` | header | string | no | A fresh unique string per intended write (1 to 255 visible ASCII characters). Sent again unchanged only to retry THAT write after a timeout or a 409 `IDEMPOTENCY_IN_PROGRESS`: the stored answer comes back (`Idempotency-Replayed: true`) and nothing is written twice. The same key with another request is a 422 `IDEMPOTENCY_KEY_REUSED`. |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Request body
`application/json`
| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `confirmationToken` | string | no | The `confirmationToken` the delete’s preview returned, exactly as it came, sent once the usuario saw the preview and said yes. Lasts 15 minutes. Without it: 422 `CONFIRMATION_REQUIRED`. |
### Responses
| Status | Description |
| --- | --- |
| `200` | `ExternalCatalogueEntryDeletedDto` |
| `400` | `INVALID_REQUEST`: a body or a parameter the route cannot take, as `{statusCode, code: 'INVALID_REQUEST', message, details}`, with one `{field, message}` in `details` per failure (`field` the body field as a path, such as `items.0.name`; null when the failure names none). Or a value the dashboard refuses (its own `code`), or `INVALID_IDEMPOTENCY_KEY`. Nothing changed. |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | `ROLE_NOT_ALLOWED`: the usuario’s role does not make this change in the dashboard (it needs ADMIN or MODERATOR, or an account with platform-wide access);`INSUFFICIENT_SCOPE`: the credential lacks «Gestionar el Programa»; `PLAN_LIMIT`: the organization’s plan does not answer this credential (a connected app needs «Conectar con IA», Plan Pro). Nothing changed. |
| `404` | The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both. |
| `409` | `IDEMPOTENCY_IN_PROGRESS` (with `Retry-After`): the same `Idempotency-Key` is still being answered; retry it unchanged. Or the dashboard’s own conflict. |
| `422` | `IDEMPOTENCY_KEY_REUSED`: the `Idempotency-Key` answered another request. Nothing changed. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X DELETE 'https://api.fluenta.io/v1/external/organizations/{organizationId}/catalogue/{templateId}' \
-H 'Authorization: Bearer ' \
-H 'Content-Type: application/json' \
-d @body.json
```
---
# Preview delete catalogue entry
URL: https://docs.fluenta.io/docs/reference/catalogue/preview-delete-catalogue-entry
Operation: `previewDeleteCatalogueEntry`
Required permission: `read`
`POST /v1/external/organizations/{organizationId}/catalogue/{templateId}/delete-preview`
What deleting a template of the «Catálogo de referencias» would do, writing nothing, and the `confirmationToken` `deleteCatalogueEntry` needs. Show it to the usuario and wait for a yes.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `templateId` | path | integer | yes | |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Responses
| Status | Description |
| --- | --- |
| `200` | `ExternalCatalogueDeletePreviewDto` |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | The credential lacks the «Permiso» the route names (`INSUFFICIENT_SCOPE`), or the organization’s plan does not include what was asked (`PLAN_LIMIT`): «Conectar con IA» for a connected app, or a report or analytics route whose plan feature a Light organization lacks (a Light of plan version 1 keeps `reports`). |
| `404` | The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X POST 'https://api.fluenta.io/v1/external/organizations/{organizationId}/catalogue/{templateId}/delete-preview' \
-H 'Authorization: Bearer '
```
---
# List categories
URL: https://docs.fluenta.io/docs/reference/organization/list-categories
Operation: `listCategories`
Required permission: `read`
`GET /v1/external/organizations/{organizationId}/categories`
The «Categorías» an OC takes its garment from: the plant’s own, for «Otros clientes», and those of each brand it works for in the «Red de talleres».
Answers the «Categorías de prenda» of «Configuración»: the kinds of garment (blusa, pantalón…) a «referencia» (an OC) is, which production figures are added up by.
`own` is the plant’s own list, plain names, for the OCs of its own clients («Otros clientes»). `brands` holds one group per brand («cuenta de marca») the plant works for in the «Red de talleres»: its `companyAdminId`, its name and its numbered catalogue (`code` «01», `short` «Blusa», `name` «Blusa · Vestido»). Both are empty for a plant that keeps no list and works for no brand.
Feed it to the «Programa»: the `garment` of `createReference` and `updateReference` takes one name of `own`, and a «referencia» read back (`getProgram`, `getReference`) carries the same string.
No id beyond `organizationId`; no time field; the read creates nothing.
Not paged: the whole answer comes in one response.
Plan: on every plan, Light included. A «token de API» reads it in any organization the user who created it belongs to; a connected AI app (a «conexión») reads it only where the organization has «Conectar con IA» (`getPlan.features.aiConnector`, a Pro feature), and answers 403 `PLAN_LIMIT` elsewhere.
Rate limit: one request of the credential’s ordinary budget in this organization (`getPlan` answers the numbers); past it, 429 `RATE_LIMITED` with `Retry-After` in seconds.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Responses
| Status | Description |
| --- | --- |
| `200` | `ExternalCategoriesDto` |
| `401` | `INVALID_API_TOKEN`: the credential is missing, malformed, unknown, expired or revoked. One body for all of them. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | `PLAN_LIMIT` with `feature: "aiConnector"`: the credential is a «conexión» and the organization’s plan has no «Conectar con IA» (Light). A «token de API» is not refused here on any plan on sale: the plant reads are on every plan. `INSUFFICIENT_SCOPE` only for a credential without the «Permiso» `read` («Leer»), which every credential is issued with. |
| `404` | `ORGANIZATION_NOT_FOUND`: the organization does not exist or is out of the credential’s reach. One body for both. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X GET 'https://api.fluenta.io/v1/external/organizations/{organizationId}/categories' \
-H 'Authorization: Bearer '
```
---
# Get floor
URL: https://docs.fluenta.io/docs/reference/plant/get-floor
Operation: `getFloor`
Required permission: `read`
`GET /v1/external/organizations/{organizationId}/floor`
The whole floor in one answer: every «módulo» in the floor’s order, each with its «puestos» (stations) as the live board reads them now. Counted against the heavy rate limit.
Answers the «Tablero en tiempo real» in one read: every «módulo» (production line) in the floor’s order, each with its «puestos» in the board’s order. It is `listModules` and `listStations` of every «módulo» together, so no id is needed beyond `organizationId`; the ids it answers feed `getModule`, `listModuleRestTimes`, `getStation` and `getOperator`.
Each «puesto» is read as the «Tablero en tiempo real» reads it now: `unitsThisHour` and `hourGoal` are units of the hour `currentHour` names, `unitsToday` units of the plant day `today` names, `color` the «semáforo» (`GRAY`, `RED`, `YELLOW`, `GREEN`), `pausedAt` an ISO 8601 UTC instant, and `currentReference.price` money per unit in the organization’s currency (`getOrganization.currency`).
`today` (`YYYY-MM-DD`) and `currentHour` (0 to 23) are the plant’s clock when it was read; both are null only when that clock could not be read, and the counts are then answered as stored. Every day and hour is on the plant’s own clock, its «zona horaria» (`getSettings.timezone`, an IANA name), never UTC and never the caller’s.
An organization with no «módulo» answers `modules: []`; a «módulo» with no «puesto» answers `stations: []`.
Not paged: the whole answer comes in one response.
Plan: on every plan, Light included. A «token de API» reads it in any organization the user who created it belongs to; a connected AI app (a «conexión») reads it only where the organization has «Conectar con IA» (`getPlan.features.aiConnector`, a Pro feature), and answers 403 `PLAN_LIMIT` elsewhere.
Rate limit: a heavy read. Besides the ordinary budget, each request spends one of the heavy budget (`getPlan.rateLimits.heavy` per window, the same on Light and on Pro, shared with the range reads); once the heavy budget is spent the heavy reads answer 429 `RATE_LIMITED` with `Retry-After` in seconds, and every other route keeps answering. To follow one «módulo» or one «puesto», call `listStations`, `listOrganizationStations` or `getStation`, which are not heavy.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Responses
| Status | Description |
| --- | --- |
| `200` | `ExternalFloorDto` |
| `401` | `INVALID_API_TOKEN`: the credential is missing, malformed, unknown, expired or revoked. One body for all of them. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | `PLAN_LIMIT` with `feature: "aiConnector"`: the credential is a «conexión» and the organization’s plan has no «Conectar con IA» (Light). A «token de API» is not refused here on any plan on sale: the plant reads are on every plan. `INSUFFICIENT_SCOPE` only for a credential without the «Permiso» `read` («Leer»), which every credential is issued with. |
| `404` | `ORGANIZATION_NOT_FOUND`: the organization does not exist or is out of the credential’s reach. One body for both. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X GET 'https://api.fluenta.io/v1/external/organizations/{organizationId}/floor' \
-H 'Authorization: Bearer '
```
---
# List modules
URL: https://docs.fluenta.io/docs/reference/plant/list-modules
Operation: `listModules`
Required permission: `read`
`GET /v1/external/organizations/{organizationId}/modules`
The organization’s módulos (production lines), in the floor’s order, with how many puestos each has.
Answers every «módulo» (production line) of the organization in the floor’s order, each as `getModule` answers it: `id`, `name`, `position`, `stationCount` and `colorHue`. An organization with no «módulo» answers an empty list.
Each `id` is the `moduleId` of `getModule`, `listStations`, `listModuleRestTimes`, `getProgram` and `getHoursBoard`, and of every `moduleId` filter.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Responses
| Status | Description |
| --- | --- |
| `200` | |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | The credential lacks the «Permiso» the route names (`INSUFFICIENT_SCOPE`), or the organization’s plan does not include what was asked (`PLAN_LIMIT`): «Conectar con IA» for a connected app, or a report or analytics route whose plan feature a Light organization lacks (a Light of plan version 1 keeps `reports`). |
| `404` | The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X GET 'https://api.fluenta.io/v1/external/organizations/{organizationId}/modules' \
-H 'Authorization: Bearer '
```
---
# Get module
URL: https://docs.fluenta.io/docs/reference/plant/get-module
Operation: `getModule`
Required permission: `read`
`GET /v1/external/organizations/{organizationId}/modules/{moduleId}`
One «módulo» (production line): its name, its place on the floor, how many «puestos» it has and its colour.
Answers one «módulo», a production line of the plant, as `listModules` lists it: `name`, `position` (its place on the floor, top to bottom), `stationCount` (how many «puestos» it has) and `colorHue` (the colour it wears on the dashboard, 1 to 12; null for the automatic one).
`moduleId` comes from `listModules` or `getFloor`. Its «puestos» are `listStations`, its «descansos» `listModuleRestTimes`, its queue of OCs `getProgram`.
No time field.
Not paged: the whole answer comes in one response.
Plan: on every plan, Light included. A «token de API» reads it in any organization the user who created it belongs to; a connected AI app (a «conexión») reads it only where the organization has «Conectar con IA» (`getPlan.features.aiConnector`, a Pro feature), and answers 403 `PLAN_LIMIT` elsewhere.
Rate limit: one request of the credential’s ordinary budget in this organization (`getPlan` answers the numbers); past it, 429 `RATE_LIMITED` with `Retry-After` in seconds.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `moduleId` | path | integer | yes | The «módulo»: an `id` of `listModules` or `getFloor`, or the `moduleId` of a «puesto». |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Responses
| Status | Description |
| --- | --- |
| `200` | `ExternalModuleDto` |
| `401` | `INVALID_API_TOKEN`: the credential is missing, malformed, unknown, expired or revoked. One body for all of them. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | `PLAN_LIMIT` with `feature: "aiConnector"`: the credential is a «conexión» and the organization’s plan has no «Conectar con IA» (Light). A «token de API» is not refused here on any plan on sale: the plant reads are on every plan. `INSUFFICIENT_SCOPE` only for a credential without the «Permiso» `read` («Leer»), which every credential is issued with. |
| `404` | `ORGANIZATION_NOT_FOUND`: the organization does not exist or is out of the credential’s reach. One body for both. `MODULE_NOT_FOUND`: no «módulo» with that `moduleId` in this organization. One body whether it does not exist, was deleted, belongs to another organization or is not a positive integer: never a 400 or a 403. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X GET 'https://api.fluenta.io/v1/external/organizations/{organizationId}/modules/{moduleId}' \
-H 'Authorization: Bearer '
```
---
# Get module efficiency
URL: https://docs.fluenta.io/docs/reference/analytics/get-module-efficiency
Operation: `getModuleEfficiency`
Required permission: `read`
`GET /v1/external/organizations/{organizationId}/modules/{moduleId}/analytics/efficiency`
One módulo’s «Eficiencia» per day over a range of up to 92 plant days: units made against the meta, every puesto counted. The chart of «Reportes por módulo».
What it is: one «módulo»’s «Eficiencia» day by day, the chart of «Reportes por módulo». Each entry of `days` is one plant day: `units` made by every «puesto» of the «módulo», `goal` the sum of each «puesto»’s «meta» for each hour it worked, and `efficiency` their ratio.
`efficiency` is «Eficiencia»: units made × 100 ÷ the «meta» of the same span, a percentage with 2 decimals (100 = exactly the goal, it can pass 100; never a 0–1 ratio), null where the goal is 0. Every «puesto» counts towards it, a «puesto de salida» or not.
Range: `from` and `to` are plant days, both included, at most 92 days apart counting both (`RANGE_TOO_LARGE` past it); for a longer span, ask consecutive ranges. `days` is oldest first, and a day with no work in the range is no row, not a row of zeros.
Days and hours are the plant’s own: a plant day is a calendar day `YYYY-MM-DD` in the organization’s timezone (`getOrganization` `timezone`), never UTC, and an `hour` is the clock hour 0–23 that starts at it in that zone.
Plan: needs the plan feature `moduleReports` («Reportes por módulo»), the one its dashboard screen is gated by. Pro, a trial of Pro and an «exenta» organization answer it; a Light organization, of either plan version, does not: 403 `PLAN_LIMIT` with `feature: "moduleReports"`. `getPlan` `features` lists what the organization’s plan has.
Heavy read: besides the credential’s ordinary budget it spends the heavy one, 20 requests per 60 seconds per credential and organization; past it the answer is 429 `RATE_LIMITED` with `Retry-After`.
See also: `getModuleDayHistory` for one of those days hour by hour; `getEfficiency` with `groupBy=module` for every «módulo» at once.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `moduleId` | path | integer | yes | The «módulo» (a production line): the `id` of one from `listModules`. Unknown or of another organization: 404 `MODULE_NOT_FOUND`. |
| `from` | query | string | yes | First plant day of the range, `YYYY-MM-DD` on the plant’s own clock, included. At most 92 days from `from` to `to`, counting both: past it, 400 `RANGE_TOO_LARGE`. |
| `to` | query | string | yes | Last plant day of the range, `YYYY-MM-DD` on the plant’s own clock, included. Before `from`: 400 `RANGE_INVALID`. |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Responses
| Status | Description |
| --- | --- |
| `200` | The «módulo»’s «Eficiencia», one row per plant day worked in the range. |
| `400` | `INVALID_DAY`: `from` or `to` is not a real calendar day written `YYYY-MM-DD`. `RANGE_INVALID`: `from` is after `to`. `RANGE_TOO_LARGE`: more than 92 plant days from `from` to `to`, both included (`maxDays` in the body says the cap); ask a shorter range. `INVALID_REQUEST`: a query parameter is missing, of the wrong type or outside its allowed values; `details` lists each one as `{field, message}`. Body: `{statusCode, code, message}`, the message in Spanish, for the usuario. |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | `PLAN_LIMIT` with `feature: "moduleReports"`: the organization’s plan lacks «Reportes por módulo» (Pro, a trial of Pro and an «exenta» organization answer it; a Light organization, of either plan version, does not). `PLAN_LIMIT` with `feature: "aiConnector"`: a connected app reads an organization without «Conectar con IA». `INSUFFICIENT_SCOPE`: the credential lacks the «Permiso» `read`. Retrying does not help: the plan or the credential has to change. |
| `404` | `ORGANIZATION_NOT_FOUND`: the organization does not exist or is out of the credential’s reach. `MODULE_NOT_FOUND`: the «módulo» does not exist or is another organization’s. One body for a row that is missing and for one of another organization. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X GET 'https://api.fluenta.io/v1/external/organizations/{organizationId}/modules/{moduleId}/analytics/efficiency' \
-H 'Authorization: Bearer '
```
---
# Get hours board
URL: https://docs.fluenta.io/docs/reference/board/get-hours-board
Operation: `getHoursBoard`
Required permission: `read`
`GET /v1/external/organizations/{organizationId}/modules/{moduleId}/days/{day}/board`
One módulo’s «Tablero de producción» for a day, hour by hour: goal, units typed and tapped, running totals, «eficiencia» and where each referencia stands.
Answers one «módulo»’s «Tablero de producción» of «Reportes por módulo» for one plant day, hour by hour: each hour’s goal, the units typed and the units tapped, the running totals and the «eficiencia», and where each «referencia» of the day stands.
`moduleId` comes from `listModules` or `getFloor`; one that is unknown or belongs to another organization answers 404 `MODULE_NOT_FOUND`. `day` is a plant day, `YYYY-MM-DD` on the organization’s own clock (`getSettings.today` is today).
Plan: the organization needs «Reportes por módulo» (`getPlan.features.moduleReports`); without it the answer is 403 `PLAN_LIMIT`.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `moduleId` | path | integer | yes | The «módulo» (a production line): the `id` of one from `listModules`. Unknown or of another organization: 404 `MODULE_NOT_FOUND`. |
| `day` | path | string (date) | yes | The plant day, `YYYY-MM-DD` in the organization’s clock. |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Responses
| Status | Description |
| --- | --- |
| `200` | `ExternalHoursBoardDto` |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | The credential lacks the «Permiso» (`INSUFFICIENT_SCOPE`), a connected app reads an organization without «Conectar con IA» (`PLAN_LIMIT`, `feature: aiConnector`), or the organization’s plan lacks «Reportes por módulo» (`PLAN_LIMIT`, `feature: moduleReports`), whatever the credential. |
| `404` | The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X GET 'https://api.fluenta.io/v1/external/organizations/{organizationId}/modules/{moduleId}/days/{day}/board' \
-H 'Authorization: Bearer '
```
---
# Get module day history
URL: https://docs.fluenta.io/docs/reference/analytics/get-module-day-history
Operation: `getModuleDayHistory`
Required permission: `read`
`GET /v1/external/organizations/{organizationId}/modules/{moduleId}/days/{day}/history`
One módulo’s day, hour by hour and puesto by puesto: units, meta, efficiency, «Facturación» and pausas. The table of «Reportes por módulo».
What it is: the hour-by-hour table of «Reportes por módulo» for one «módulo» (a production line) on one plant day, the numbers the dashboard shows there. `hours` holds one entry per clock hour that had a «marca» or a «pausa», in order; each totals the hour (`units`, `goal`, `efficiency`, `billedAmount`, `secondsPerUnit`, `pausedMinutes`, `pauses`) and lists, in `rows`, one row per «puesto» and «referencia» worked in it. An hour with «pausas» and no «marca» has no rows.
How it is computed: a row’s `goal` is that «puesto»’s «meta» for the hour as it stood then (already shortened for «descansos», «pausas» and the edges of the «jornada»); the hour’s `goal` is the sum of its «puestos»’ and its `efficiency` is its `units` × 100 ÷ that `goal`.
`efficiency` is «Eficiencia»: units made × 100 ÷ the «meta» of the same span, a percentage with 2 decimals (100 = exactly the goal, it can pass 100; never a 0–1 ratio), null where the goal is 0. Every «puesto» counts towards it, a «puesto de salida» or not.
Money is «Facturación»: the units of the «puestos de salida» × the price per unit each was billed at, in the plant’s currency (`currency`, ISO 4217: `EUR` for a plant in España, `COP` for every other), in whole currency units with up to 2 decimals, never cents. Whether a money figure reads null or 0 where nothing was priced is stated on each field. A row’s `billedAmount` is null on a «puesto» that is not «de salida»; the hour’s is the sum over the ones that are.
Days and hours are the plant’s own: a plant day is a calendar day `YYYY-MM-DD` in the organization’s timezone (`getOrganization` `timezone`), never UTC, and an `hour` is the clock hour 0–23 that starts at it in that zone. The day is the path’s `day`, required.
Naming: each row’s `operationId` is its «puesto» (a numbered station of a «módulo»). A «puesto» is an `operation` in ids, filters and codes and a `station` in paths: the same number is the `id` `listStations` returns and the `stationId` of the station routes.
Plan: needs the plan feature `moduleReports` («Reportes por módulo»), the one its dashboard screen is gated by. Pro, a trial of Pro and an «exenta» organization answer it; a Light organization, of either plan version, does not: 403 `PLAN_LIMIT` with `feature: "moduleReports"`. `getPlan` `features` lists what the organization’s plan has.
See also: `getHoursBoard` for the same «módulo» and day as its «Tablero de producción» (typed counts, running totals), `getModuleEfficiency` for this «módulo» over a range, `getStationDayReport` for one of its «puestos».
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `moduleId` | path | integer | yes | The «módulo» (a production line): the `id` of one from `listModules`. Unknown or of another organization: 404 `MODULE_NOT_FOUND`. |
| `day` | path | string (date) | yes | The plant day to read, `YYYY-MM-DD` on the organization’s own clock (its timezone, never UTC). Required here: it is part of the path. Not a real calendar day: 400 `INVALID_DAY`. |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Responses
| Status | Description |
| --- | --- |
| `200` | The «módulo»’s day, hour by hour and «puesto» by «puesto». |
| `400` | `INVALID_DAY`: `day` is not a real calendar day written `YYYY-MM-DD`. Body: `{statusCode, code, message}`, the message in Spanish, for the usuario. |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | `PLAN_LIMIT` with `feature: "moduleReports"`: the organization’s plan lacks «Reportes por módulo» (Pro, a trial of Pro and an «exenta» organization answer it; a Light organization, of either plan version, does not). `PLAN_LIMIT` with `feature: "aiConnector"`: a connected app reads an organization without «Conectar con IA». `INSUFFICIENT_SCOPE`: the credential lacks the «Permiso» `read`. Retrying does not help: the plan or the credential has to change. |
| `404` | `ORGANIZATION_NOT_FOUND`: the organization does not exist or is out of the credential’s reach. `MODULE_NOT_FOUND`: the «módulo» does not exist or is another organization’s. One body for a row that is missing and for one of another organization. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X GET 'https://api.fluenta.io/v1/external/organizations/{organizationId}/modules/{moduleId}/days/{day}/history' \
-H 'Authorization: Bearer '
```
---
# Set day goals
URL: https://docs.fluenta.io/docs/reference/program/set-day-goals
Operation: `setDayGoals`
Required permission: `program`
`PUT /v1/external/organizations/{organizationId}/modules/{moduleId}/days/{day}/references/{referenceId}/goal`
«Metas del día»: one referencia’s hourly goal for one day of a módulo, one rate all day or rates by minute. Answers the módulo’s day as `getHoursBoard` does.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `moduleId` | path | integer | yes | |
| `day` | path | string (date) | yes | A plant day, `YYYY-MM-DD`, on the organization’s clock: today or a past day, never a future one. |
| `referenceId` | path | integer | yes | |
| `Idempotency-Key` | header | string | no | A fresh unique string per intended write (1 to 255 visible ASCII characters). Sent again unchanged only to retry THAT write after a timeout or a 409 `IDEMPOTENCY_IN_PROGRESS`: the stored answer comes back (`Idempotency-Replayed: true`) and nothing is written twice. The same key with another request is a 422 `IDEMPOTENCY_KEY_REUSED`. |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Request body
`application/json`
| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `goal` | integer, 1–100000 | yes | Units per hour in force at the close of the day (the last rate’s when there are `steps`). Equal to the referencia’s own «Meta por hora» with no steps, the day reads its own goal again. |
| `steps` | object[] | no | The day’s rates by minute of day, at most 48, distinct minutes. Omitted or null: one rate, `goal`, all day. |
| `steps[].from` | integer, 0–1439 | yes | Minute of the day the rate starts at (600 = 10:00). |
| `steps[].goal` | integer, 1–100000 | yes | Units per hour from then. |
### Responses
| Status | Description |
| --- | --- |
| `200` | `ExternalHoursBoardDto` |
| `400` | `INVALID_REQUEST`: a body or a parameter the route cannot take, as `{statusCode, code: 'INVALID_REQUEST', message, details}`, with one `{field, message}` in `details` per failure (`field` the body field as a path, such as `items.0.name`; null when the failure names none). Or a value the dashboard refuses (its own `code`), or `INVALID_IDEMPOTENCY_KEY`. Nothing changed. |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | `ROLE_NOT_ALLOWED`: the usuario’s role does not make this change in the dashboard (it needs ADMIN, or an account with platform-wide access);`INSUFFICIENT_SCOPE`: the credential lacks «Gestionar el Programa»; `PLAN_LIMIT`: the organization’s plan does not answer this credential (a connected app needs «Conectar con IA», Plan Pro). Nothing changed. |
| `404` | The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both. |
| `409` | `IDEMPOTENCY_IN_PROGRESS` (with `Retry-After`): the same `Idempotency-Key` is still being answered; retry it unchanged. Or the dashboard’s own conflict. |
| `422` | `IDEMPOTENCY_KEY_REUSED`: the `Idempotency-Key` answered another request. Nothing changed. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X PUT 'https://api.fluenta.io/v1/external/organizations/{organizationId}/modules/{moduleId}/days/{day}/references/{referenceId}/goal' \
-H 'Authorization: Bearer ' \
-H 'Content-Type: application/json' \
-d @body.json
```
---
# Clear day goals
URL: https://docs.fluenta.io/docs/reference/program/clear-day-goals
Operation: `clearDayGoals`
Required permission: `program`
`DELETE /v1/external/organizations/{organizationId}/modules/{moduleId}/days/{day}/references/{referenceId}/goal`
«↺ Meta base»: the day reads the referencia’s own «Meta por hora» again. Answers the módulo’s day as `getHoursBoard` does.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `moduleId` | path | integer | yes | |
| `day` | path | string (date) | yes | A plant day, `YYYY-MM-DD`, on the organization’s clock. |
| `referenceId` | path | integer | yes | |
| `Idempotency-Key` | header | string | no | A fresh unique string per intended write (1 to 255 visible ASCII characters). Sent again unchanged only to retry THAT write after a timeout or a 409 `IDEMPOTENCY_IN_PROGRESS`: the stored answer comes back (`Idempotency-Replayed: true`) and nothing is written twice. The same key with another request is a 422 `IDEMPOTENCY_KEY_REUSED`. |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Responses
| Status | Description |
| --- | --- |
| `200` | `ExternalHoursBoardDto` |
| `400` | `INVALID_REQUEST`: a body or a parameter the route cannot take, as `{statusCode, code: 'INVALID_REQUEST', message, details}`, with one `{field, message}` in `details` per failure (`field` the body field as a path, such as `items.0.name`; null when the failure names none). Or a value the dashboard refuses (its own `code`), or `INVALID_IDEMPOTENCY_KEY`. Nothing changed. |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | `ROLE_NOT_ALLOWED`: the usuario’s role does not make this change in the dashboard (it needs ADMIN, or an account with platform-wide access);`INSUFFICIENT_SCOPE`: the credential lacks «Gestionar el Programa»; `PLAN_LIMIT`: the organization’s plan does not answer this credential (a connected app needs «Conectar con IA», Plan Pro). Nothing changed. |
| `404` | The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both. |
| `409` | `IDEMPOTENCY_IN_PROGRESS` (with `Retry-After`): the same `Idempotency-Key` is still being answered; retry it unchanged. Or the dashboard’s own conflict. |
| `422` | `IDEMPOTENCY_KEY_REUSED`: the `Idempotency-Key` answered another request. Nothing changed. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X DELETE 'https://api.fluenta.io/v1/external/organizations/{organizationId}/modules/{moduleId}/days/{day}/references/{referenceId}/goal' \
-H 'Authorization: Bearer '
```
---
# Get program
URL: https://docs.fluenta.io/docs/reference/program/get-program
Operation: `getProgram`
Required permission: `read`
`GET /v1/external/organizations/{organizationId}/modules/{moduleId}/program`
One módulo’s «Programa»: its referencias in queue order, with units produced, «saldo», dates and today’s «Metas del día». A finished cut stays listed a few days.
Answers one «módulo»’s «Programa»: its «referencias» (cuts, OCs) in queue order, each as `getReference` answers it, with the units it has produced, its «saldo», its dates and today’s «Metas del día». A cut that finished stays listed for a few days. A «módulo» with an empty «Programa» answers an empty list.
`moduleId` comes from `listModules` or `getFloor`; one that is unknown or belongs to another organization answers 404 `MODULE_NOT_FOUND`. Each `id` is the `referenceId` of `getReference` and of the «Programa» writes.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `moduleId` | path | integer | yes | The «módulo» (a production line): the `id` of one from `listModules`. Unknown or of another organization: 404 `MODULE_NOT_FOUND`. |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Responses
| Status | Description |
| --- | --- |
| `200` | |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | The credential lacks the «Permiso» the route names (`INSUFFICIENT_SCOPE`), or the organization’s plan does not include what was asked (`PLAN_LIMIT`): «Conectar con IA» for a connected app, or a report or analytics route whose plan feature a Light organization lacks (a Light of plan version 1 keeps `reports`). |
| `404` | The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X GET 'https://api.fluenta.io/v1/external/organizations/{organizationId}/modules/{moduleId}/program' \
-H 'Authorization: Bearer '
```
---
# Reorder program
URL: https://docs.fluenta.io/docs/reference/program/reorder-program
Operation: `reorderProgram`
Required permission: `program`
`PATCH /v1/external/organizations/{organizationId}/modules/{moduleId}/program/order`
Rewrite a módulo’s «Programa» order (the FILA): every referencia of the queue, each once, in the new order. Answers the queue as `getProgram` does.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `moduleId` | path | integer | yes | |
| `Idempotency-Key` | header | string | no | A fresh unique string per intended write (1 to 255 visible ASCII characters). Sent again unchanged only to retry THAT write after a timeout or a 409 `IDEMPOTENCY_IN_PROGRESS`: the stored answer comes back (`Idempotency-Replayed: true`) and nothing is written twice. The same key with another request is a 422 `IDEMPOTENCY_KEY_REUSED`. |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Request body
`application/json`
| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `references` | integer[] | yes | EVERY referencia id of the módulo’s «Programa» (`getProgram`), each once, in the new order: no additions, no omissions. Row 1 first. A finished cut still listed counts too. |
| `note` | string | no | Why («motivo»). Required when the change touches an OC a brand («marca») dispatched to this taller, else optional: 400 `PROGRAM_CHANGE_NOTE_REQUIRED` without one. Goes to the brand’s «Bandeja de cambios». |
### Responses
| Status | Description |
| --- | --- |
| `200` | |
| `400` | `INVALID_REQUEST`: a body or a parameter the route cannot take, as `{statusCode, code: 'INVALID_REQUEST', message, details}`, with one `{field, message}` in `details` per failure (`field` the body field as a path, such as `items.0.name`; null when the failure names none). Or a value the dashboard refuses (its own `code`), or `INVALID_IDEMPOTENCY_KEY`. Nothing changed. |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | `ROLE_NOT_ALLOWED`: the usuario’s role does not make this change in the dashboard (it needs ADMIN, or an account with platform-wide access);`INSUFFICIENT_SCOPE`: the credential lacks «Gestionar el Programa»; `PLAN_LIMIT`: the organization’s plan does not answer this credential (a connected app needs «Conectar con IA», Plan Pro). Nothing changed. |
| `404` | The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both. |
| `409` | `IDEMPOTENCY_IN_PROGRESS` (with `Retry-After`): the same `Idempotency-Key` is still being answered; retry it unchanged. Or the dashboard’s own conflict. |
| `422` | `IDEMPOTENCY_KEY_REUSED`: the `Idempotency-Key` answered another request. Nothing changed. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X PATCH 'https://api.fluenta.io/v1/external/organizations/{organizationId}/modules/{moduleId}/program/order' \
-H 'Authorization: Bearer ' \
-H 'Content-Type: application/json' \
-d @body.json
```
---
# List module rest times
URL: https://docs.fluenta.io/docs/reference/plant/list-module-rest-times
Operation: `listModuleRestTimes`
Required permission: `read`
`GET /v1/external/organizations/{organizationId}/modules/{moduleId}/rest-times`
One «módulo»’s «descansos» (breaks), every weekday alike, by start: no goal is owed inside one. On a «jornada continua» weekday (`getSettings`) none applies.
Answers the «Descansos por módulo» of «Configuración» for one «módulo»: its break windows (breakfast, lunch, a «pausa activa»), ordered by start. During one the «puestos» go grey and the hour’s goal shrinks by the minutes lost.
Each row is a window from `startHour`:`startMin` to `endHour`:`endMin`, hours 0 to 23 and minutes 0 to 59 on the plant’s clock (`getSettings.timezone`); the same rows apply on every worked weekday, and none applies on a «jornada continua» weekday (`getSettings.shift.continuousDays`). A «módulo» without breaks answers `restTimes: []`.
`moduleId` comes from `listModules` or `getFloor`. `getOrganization` answers the same rows for every «módulo» at once.
A «descanso» is the whole «módulo»’s; a «pausa» is one «operario»’s stop at one «puesto» (`pausedAt` on a «puesto», `getPauses`).
Not paged: the whole answer comes in one response.
Plan: on every plan, Light included. A «token de API» reads it in any organization the user who created it belongs to; a connected AI app (a «conexión») reads it only where the organization has «Conectar con IA» (`getPlan.features.aiConnector`, a Pro feature), and answers 403 `PLAN_LIMIT` elsewhere.
Rate limit: one request of the credential’s ordinary budget in this organization (`getPlan` answers the numbers); past it, 429 `RATE_LIMITED` with `Retry-After` in seconds.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `moduleId` | path | integer | yes | The «módulo»: an `id` of `listModules` or `getFloor`, or the `moduleId` of a «puesto». |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Responses
| Status | Description |
| --- | --- |
| `200` | `ExternalModuleRestTimesDto` |
| `401` | `INVALID_API_TOKEN`: the credential is missing, malformed, unknown, expired or revoked. One body for all of them. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | `PLAN_LIMIT` with `feature: "aiConnector"`: the credential is a «conexión» and the organization’s plan has no «Conectar con IA» (Light). A «token de API» is not refused here on any plan on sale: the plant reads are on every plan. `INSUFFICIENT_SCOPE` only for a credential without the «Permiso» `read` («Leer»), which every credential is issued with. |
| `404` | `ORGANIZATION_NOT_FOUND`: the organization does not exist or is out of the credential’s reach. One body for both. `MODULE_NOT_FOUND`: no «módulo» with that `moduleId` in this organization. One body whether it does not exist, was deleted, belongs to another organization or is not a positive integer: never a 400 or a 403. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X GET 'https://api.fluenta.io/v1/external/organizations/{organizationId}/modules/{moduleId}/rest-times' \
-H 'Authorization: Bearer '
```
---
# List stations
URL: https://docs.fluenta.io/docs/reference/plant/list-stations
Operation: `listStations`
Required permission: `read`
`GET /v1/external/organizations/{organizationId}/modules/{moduleId}/stations`
The puestos (stations) of one módulo as the live board reads them now: operación, seated operario, current referencia, units this hour and today.
Answers the «puestos» (stations) of one «módulo» in the board’s order, each as `getStation` answers it: the «operación» sewn there (`name`), its `position`, the seated «operario» (`operator`, null when empty), the «referencia» it is on (`currentReference`, null when none) and its counts of the hour and of the day, as the «Tablero en tiempo real» reads them now. A «módulo» with no «puesto» answers an empty list.
`moduleId` comes from `listModules` or `getFloor`; one that is unknown or belongs to another organization answers 404 `MODULE_NOT_FOUND`. Each `id` is the `stationId` of `getStation` and of the station reports. The answer carries no `today` or `currentHour`: read them from `getSettings`.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `moduleId` | path | integer | yes | The «módulo» (a production line): the `id` of one from `listModules`. Unknown or of another organization: 404 `MODULE_NOT_FOUND`. |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Responses
| Status | Description |
| --- | --- |
| `200` | |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | The credential lacks the «Permiso» the route names (`INSUFFICIENT_SCOPE`), or the organization’s plan does not include what was asked (`PLAN_LIMIT`): «Conectar con IA» for a connected app, or a report or analytics route whose plan feature a Light organization lacks (a Light of plan version 1 keeps `reports`). |
| `404` | The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X GET 'https://api.fluenta.io/v1/external/organizations/{organizationId}/modules/{moduleId}/stations' \
-H 'Authorization: Bearer '
```
---
# List operators
URL: https://docs.fluenta.io/docs/reference/plant/list-operators
Operation: `listOperators`
Required permission: `read`
`GET /v1/external/organizations/{organizationId}/operators`
The organization’s operarios by name, «Activo» or «Inactivo», and the puesto each is seated at.
Answers every «operario» of the organization by name, each as `getOperator` answers it: `name`, `active` («Activo» true, «Inactivo» false) and `station`, the «puesto» the «operario» is seated at (null for one seated nowhere). An organization with no «operario» answers an empty list.
Each `id` is the `operatorId` of `getOperator`, of the operator reports and of every `operatorId` filter. The code an «operario» signs in to the app with is a credential and is never answered.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Responses
| Status | Description |
| --- | --- |
| `200` | |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | The credential lacks the «Permiso» the route names (`INSUFFICIENT_SCOPE`), or the organization’s plan does not include what was asked (`PLAN_LIMIT`): «Conectar con IA» for a connected app, or a report or analytics route whose plan feature a Light organization lacks (a Light of plan version 1 keeps `reports`). |
| `404` | The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X GET 'https://api.fluenta.io/v1/external/organizations/{organizationId}/operators' \
-H 'Authorization: Bearer '
```
---
# Get operator presence
URL: https://docs.fluenta.io/docs/reference/plant/get-operator-presence
Operation: `getOperatorPresence`
Required permission: `read`
`GET /v1/external/organizations/{organizationId}/operators/presence`
Which «operarios» have a tablet connected right now, and whether the app is open on it. An «operario» missing from the answer is offline.
Answers the connection dot each «puesto» card of the «Tablero en tiempo real» wears: one row per «operario» whose tablet is connected to Fluenta right now, with `state` `active` («En la app»: the app is open on the screen) or `background` («Fuera de la app o pantalla bloqueada»: connected, the app out of sight).
An «operario» missing from `operators` is offline («Sin conexión»): there is no `offline` row, and an organization with no tablet connected answers `operators: []`. Rows are ordered by `operatorId`, an `id` of `listOperators`; to name them or find their «puesto», join with `listOperators` or call `getOperator`.
`at` is the instant the tablets were read, ISO 8601 in UTC (the one field here that is not on the plant’s clock). It is a snapshot, never stored: ask again to see a change. Presence says a tablet is connected, not that units are being made: for that read `unitsThisHour` on the «puesto».
Not paged: the whole answer comes in one response.
Plan: on every plan, Light included. A «token de API» reads it in any organization the user who created it belongs to; a connected AI app (a «conexión») reads it only where the organization has «Conectar con IA» (`getPlan.features.aiConnector`, a Pro feature), and answers 403 `PLAN_LIMIT` elsewhere.
Rate limit: one request of the credential’s ordinary budget in this organization (`getPlan` answers the numbers); past it, 429 `RATE_LIMITED` with `Retry-After` in seconds.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Responses
| Status | Description |
| --- | --- |
| `200` | `ExternalPresenceDto` |
| `401` | `INVALID_API_TOKEN`: the credential is missing, malformed, unknown, expired or revoked. One body for all of them. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | `PLAN_LIMIT` with `feature: "aiConnector"`: the credential is a «conexión» and the organization’s plan has no «Conectar con IA» (Light). A «token de API» is not refused here on any plan on sale: the plant reads are on every plan. `INSUFFICIENT_SCOPE` only for a credential without the «Permiso» `read` («Leer»), which every credential is issued with. |
| `404` | `ORGANIZATION_NOT_FOUND`: the organization does not exist or is out of the credential’s reach. One body for both. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X GET 'https://api.fluenta.io/v1/external/organizations/{organizationId}/operators/presence' \
-H 'Authorization: Bearer '
```
---
# Get operator
URL: https://docs.fluenta.io/docs/reference/plant/get-operator
Operation: `getOperator`
Required permission: `read`
`GET /v1/external/organizations/{organizationId}/operators/{operatorId}`
One «operario» by id: name, «Activo» or «Inactivo», and the «puesto» the «operario» is seated at.
Answers one «operario», a person who registers their own output at a «puesto», exactly as `listOperators` lists them: `name`, `active` («Activo» true, «Inactivo» false) and `station`, the «puesto» the «operario» is seated at, with its `moduleId` and both places on the floor (`position` in the «módulo», `modulePosition` of the «módulo»). `station` is null for an «operario» seated nowhere («Libre»).
`operatorId` comes from `listOperators`, from the `operator.id` of a «puesto» or from `getOperatorPresence`. `station.id` feeds `getStation` for the live counts.
The code an «operario» signs in to the app with is a credential and is never answered. No time field.
Not paged: the whole answer comes in one response.
Plan: on every plan, Light included. A «token de API» reads it in any organization the user who created it belongs to; a connected AI app (a «conexión») reads it only where the organization has «Conectar con IA» (`getPlan.features.aiConnector`, a Pro feature), and answers 403 `PLAN_LIMIT` elsewhere.
Rate limit: one request of the credential’s ordinary budget in this organization (`getPlan` answers the numbers); past it, 429 `RATE_LIMITED` with `Retry-After` in seconds.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `operatorId` | path | integer | yes | The «operario»: an `id` of `listOperators`, the `operator.id` of a «puesto», or an `operatorId` of `getOperatorPresence`. |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Responses
| Status | Description |
| --- | --- |
| `200` | `ExternalOperatorDto` |
| `401` | `INVALID_API_TOKEN`: the credential is missing, malformed, unknown, expired or revoked. One body for all of them. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | `PLAN_LIMIT` with `feature: "aiConnector"`: the credential is a «conexión» and the organization’s plan has no «Conectar con IA» (Light). A «token de API» is not refused here on any plan on sale: the plant reads are on every plan. `INSUFFICIENT_SCOPE` only for a credential without the «Permiso» `read` («Leer»), which every credential is issued with. |
| `404` | `ORGANIZATION_NOT_FOUND`: the organization does not exist or is out of the credential’s reach. One body for both. `OPERATOR_NOT_FOUND`: no «operario» with that `operatorId` in this organization. One body whether it does not exist, was deleted, belongs to another organization or is not a positive integer: never a 400 or a 403. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X GET 'https://api.fluenta.io/v1/external/organizations/{organizationId}/operators/{operatorId}' \
-H 'Authorization: Bearer '
```
---
# Get operator efficiency
URL: https://docs.fluenta.io/docs/reference/analytics/get-operator-efficiency
Operation: `getOperatorEfficiency`
Required permission: `read`
`GET /v1/external/organizations/{organizationId}/operators/{operatorId}/analytics/efficiency`
One operario’s «Eficiencia» per day over a range of up to 92 plant days: their own marcas against their share of each puesto-hour’s meta.
What it is: one «operario»’s «Eficiencia» day by day, the chart of that person’s report in the dashboard («Reportes», by «operario»). Each entry of `days` is one plant day: `units` are the «marcas» that person registered, at whichever «puestos» they sat; `goal` is their share of the «meta» of every «puesto»-hour they marked in, 2 decimals (when two people shared a «puesto» in one hour, each takes the part of its «meta» that matches their part of its units); `efficiency` is `units` × 100 ÷ the unrounded `goal`.
This is the person’s figure, the one the «Clasificación» ranks by. It is not a «puesto»’s: two «operarios» who shared a «puesto» add up to that «puesto»’s units and goal.
`efficiency` is «Eficiencia»: units made × 100 ÷ the «meta» of the same span, a percentage with 2 decimals (100 = exactly the goal, it can pass 100; never a 0–1 ratio), null where the goal is 0. Every «puesto» counts towards it, a «puesto de salida» or not.
Range: `from` and `to` are plant days, both included, at most 92 days apart counting both (`RANGE_TOO_LARGE` past it); for a longer span, ask consecutive ranges. `days` is oldest first, and a day with no work in the range is no row, not a row of zeros.
Days and hours are the plant’s own: a plant day is a calendar day `YYYY-MM-DD` in the organization’s timezone (`getOrganization` `timezone`), never UTC, and an `hour` is the clock hour 0–23 that starts at it in that zone.
Plan: needs the plan feature `operatorReports` (the «operario» reports), the one its dashboard screen is gated by. Pro, a trial of Pro and an «exenta» organization answer it; a Light organization, of either plan version, does not: 403 `PLAN_LIMIT` with `feature: "operatorReports"`. `getPlan` `features` lists what the organization’s plan has.
Heavy read: besides the credential’s ordinary budget it spends the heavy one, 20 requests per 60 seconds per credential and organization; past it the answer is 429 `RATE_LIMITED` with `Retry-After`.
See also: `getOperatorReport` for the same days plus one day hour by hour; `getLeaderboard` for every «operario» on one day.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `operatorId` | path | integer | yes | The «operario»: the `id` of one from `listOperators`. Unknown or of another organization: 404 `OPERATOR_NOT_FOUND`. |
| `from` | query | string | yes | First plant day of the range, `YYYY-MM-DD` on the plant’s own clock, included. At most 92 days from `from` to `to`, counting both: past it, 400 `RANGE_TOO_LARGE`. |
| `to` | query | string | yes | Last plant day of the range, `YYYY-MM-DD` on the plant’s own clock, included. Before `from`: 400 `RANGE_INVALID`. |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Responses
| Status | Description |
| --- | --- |
| `200` | The «operario»’s «Eficiencia», one row per plant day worked in the range. |
| `400` | `INVALID_DAY`: `from` or `to` is not a real calendar day written `YYYY-MM-DD`. `RANGE_INVALID`: `from` is after `to`. `RANGE_TOO_LARGE`: more than 92 plant days from `from` to `to`, both included (`maxDays` in the body says the cap); ask a shorter range. `INVALID_REQUEST`: a query parameter is missing, of the wrong type or outside its allowed values; `details` lists each one as `{field, message}`. Body: `{statusCode, code, message}`, the message in Spanish, for the usuario. |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | `PLAN_LIMIT` with `feature: "operatorReports"`: the organization’s plan lacks the «operario» reports (Pro, a trial of Pro and an «exenta» organization answer it; a Light organization, of either plan version, does not). `PLAN_LIMIT` with `feature: "aiConnector"`: a connected app reads an organization without «Conectar con IA». `INSUFFICIENT_SCOPE`: the credential lacks the «Permiso» `read`. Retrying does not help: the plan or the credential has to change. |
| `404` | `ORGANIZATION_NOT_FOUND`: the organization does not exist or is out of the credential’s reach. `OPERATOR_NOT_FOUND`: the «operario» does not exist or is another organization’s. One body for a row that is missing and for one of another organization. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X GET 'https://api.fluenta.io/v1/external/organizations/{organizationId}/operators/{operatorId}/analytics/efficiency' \
-H 'Authorization: Bearer '
```
---
# Get plan
URL: https://docs.fluenta.io/docs/reference/organization/get-plan
Operation: `getPlan`
Required permission: `read`
`GET /v1/external/organizations/{organizationId}/plan`
The organization’s plan: Light or Pro, its plan version, what it allows (reports, «Facturación», «Pausas», «Conectar con IA»…) and the rate limits this API counts a credential against.
Answers the organization’s «Plan» as the dashboard’s own gates read it, so a client can tell which operations would answer 403 `PLAN_LIMIT` before calling them.
`tier` is `light` or `pro`: the plan the organization’s features and limits follow, whether it pays for it, is in its trial or is «exenta». `planVersion` says which Light it is: version 1 keeps `features.reports`, version 2 does not; on Pro it changes nothing. `features` holds one boolean per plan feature, and `maxReferencesPerModule`, the «referencias» a «módulo»’s «Programa» may hold (null: no limit). `rateLimits` holds the requests per window of `windowSeconds` seconds this API allows one credential.
How the plan is paid (trial dates, seats, the «licencia») is never answered. No id beyond `organizationId`; no time field.
Not paged: the whole answer comes in one response.
Plan: on every plan, Light included. A «token de API» reads it in any organization the user who created it belongs to; a connected AI app (a «conexión») reads it only where the organization has «Conectar con IA» (`getPlan.features.aiConnector`, a Pro feature), and answers 403 `PLAN_LIMIT` elsewhere.
Rate limit: one request of the credential’s ordinary budget in this organization (`getPlan` answers the numbers); past it, 429 `RATE_LIMITED` with `Retry-After` in seconds.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Responses
| Status | Description |
| --- | --- |
| `200` | `ExternalPlanDetailDto` |
| `401` | `INVALID_API_TOKEN`: the credential is missing, malformed, unknown, expired or revoked. One body for all of them. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | `PLAN_LIMIT` with `feature: "aiConnector"`: the credential is a «conexión» and the organization’s plan has no «Conectar con IA» (Light). A «token de API» is not refused here on any plan on sale: the plant reads are on every plan. `INSUFFICIENT_SCOPE` only for a credential without the «Permiso» `read` («Leer»), which every credential is issued with. |
| `404` | `ORGANIZATION_NOT_FOUND`: the organization does not exist or is out of the credential’s reach. One body for both. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X GET 'https://api.fluenta.io/v1/external/organizations/{organizationId}/plan' \
-H 'Authorization: Bearer '
```
---
# Search references
URL: https://docs.fluenta.io/docs/reference/program/search-references
Operation: `searchReferences`
Required permission: `read`
`GET /v1/external/organizations/{organizationId}/references`
Search the organization’s referencias by «Corte (OC)», «Referencia» or «Categoría», optionally in one módulo or one state (in the «Programa», on hold, finished). 50 per page, newest first.
Searches the organization’s «referencias» (cuts, OCs) across every «módulo» and every state: in a «Programa», in the «banco de espera» or finished. `search` matches part of the «Corte (OC)», of the «Referencia» name or of the «Categoría»; `moduleId` and `status` narrow the answer.
Paged: 50 per page, newest first, as `{items, page, total}`; `total` counts every match. A `moduleId` that is unknown or belongs to another organization answers 404 `MODULE_NOT_FOUND`. Each `id` is the `referenceId` of `getReference`.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `search` | query | string | no | Part of the «Corte (OC)», the «Referencia» name or the «Categoría» (garment), any case. Omitted: every referencia matches. |
| `moduleId` | query | integer | no | Only the referencias of this módulo (an id from `listModules`). |
| `status` | query | string | no | `in_program`: in a módulo’s «Programa», not finished. `on_hold`: in the «banco de espera». `finished`: closed. Omitted: all three. One of: `in_program`, `on_hold`, `finished`. |
| `page` | query | integer | no | 1-based page; 50 referencias per page, newest first. |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Responses
| Status | Description |
| --- | --- |
| `200` | `ExternalReferencePageDto` |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | The credential lacks the «Permiso» the route names (`INSUFFICIENT_SCOPE`), or the organization’s plan does not include what was asked (`PLAN_LIMIT`): «Conectar con IA» for a connected app, or a report or analytics route whose plan feature a Light organization lacks (a Light of plan version 1 keeps `reports`). |
| `404` | The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X GET 'https://api.fluenta.io/v1/external/organizations/{organizationId}/references' \
-H 'Authorization: Bearer '
```
---
# Create reference
URL: https://docs.fluenta.io/docs/reference/program/create-reference
Operation: `createReference`
Required permission: `program`
`POST /v1/external/organizations/{organizationId}/references`
Create an OC (a referencia, a cut of a garment) at the end of a módulo’s «Programa», as «Nueva referencia» does.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `Idempotency-Key` | header | string | no | A fresh unique string per intended write (1 to 255 visible ASCII characters). Sent again unchanged only to retry THAT write after a timeout or a 409 `IDEMPOTENCY_IN_PROGRESS`: the stored answer comes back (`Idempotency-Replayed: true`) and nothing is written twice. The same key with another request is a 422 `IDEMPOTENCY_KEY_REUSED`. |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Request body
`application/json`
| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `moduleId` | integer, 1–2147483647 | yes | The módulo whose «Programa» the OC joins, at the end of its queue (an id from `listModules`). |
| `name` | string | yes | «Referencia»: the garment style’s name. |
| `orderNumber` | string | no | «Corte (OC)», the cut’s order number. Omitted or blank, the OC takes its name, as the dashboard’s form does. |
| `garment` | string | no | «Categoría»: one of the organization’s categories when it keeps a list (`getOrganization`). |
| `goal` | integer, 1–100000 | yes | «Meta por hora»: units per hour. |
| `total` | integer, 1–1000000 | no | «Total de unidades». Required unless the plant has «El operario escoge la referencia» on: 400 `TOTAL_REQUIRED`. |
| `price` | number, 0–1000000000 | no | «Precio por unidad», in the organization’s currency (`getOrganization`). |
| `color` | string | no | The OC’s colour, `#RRGGBB`. Omitted, Fluenta picks one, as on the dashboard. |
| `dueDate` | string (date-time) | no | «Fecha de compromiso»: promised to the client. ISO 8601 with its offset (the plant’s), e.g. `2026-11-28T00:00:00-05:00`. |
| `spliceDate` | string (date-time) | no | «F. Empalme»: when it starts on its módulo. ISO 8601 with its offset. |
| `priority` | boolean | no | «OC prioritaria». |
| `continuity` | boolean | no | «Continuidad»: the same garment as the cut before it. |
| `step1Name` | string | no | «Subproceso 1»: needs `step1Goal`. |
| `step1Goal` | integer | no | «Meta subproceso 1», units per hour. |
| `step2Name` | string | no | «Subproceso 2»: needs step 1 and `step2Goal`. |
| `step2Goal` | integer | no | «Meta subproceso 2». |
| `saveToCatalog` | boolean | no | Also add the OC’s name, category, goal, price and colour to the «Catálogo de referencias» when it lacks the name (the dashboard’s «Guardar en el catálogo»). Never fails the OC. |
### Responses
| Status | Description |
| --- | --- |
| `201` | `ExternalReferenceDto` |
| `400` | `INVALID_REQUEST`: a body or a parameter the route cannot take, as `{statusCode, code: 'INVALID_REQUEST', message, details}`, with one `{field, message}` in `details` per failure (`field` the body field as a path, such as `items.0.name`; null when the failure names none). Or a value the dashboard refuses (its own `code`), or `INVALID_IDEMPOTENCY_KEY`. Nothing changed. |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | `ROLE_NOT_ALLOWED`: the usuario’s role does not make this change in the dashboard (it needs ADMIN or MODERATOR, or an account with platform-wide access);`INSUFFICIENT_SCOPE`: the credential lacks «Gestionar el Programa»; `PLAN_LIMIT`: the organization’s plan does not answer this credential (a connected app needs «Conectar con IA», Plan Pro). Nothing changed. |
| `404` | The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both. |
| `409` | `IDEMPOTENCY_IN_PROGRESS` (with `Retry-After`): the same `Idempotency-Key` is still being answered; retry it unchanged. Or the dashboard’s own conflict. |
| `422` | `IDEMPOTENCY_KEY_REUSED`: the `Idempotency-Key` answered another request. Nothing changed. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X POST 'https://api.fluenta.io/v1/external/organizations/{organizationId}/references' \
-H 'Authorization: Bearer ' \
-H 'Content-Type: application/json' \
-d @body.json
```
---
# Add references from catalogue
URL: https://docs.fluenta.io/docs/reference/program/add-references-from-catalogue
Operation: `addReferencesFromCatalogue`
Required permission: `program`
`POST /v1/external/organizations/{organizationId}/references/from-catalogue`
Add several OCs to a módulo’s «Programa» from the «Catálogo de referencias», one per template, as «Agregar del catálogo» does. A refused OC never stops the rest: `failed` lists them; when none lands, the first refusal is the answer.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `Idempotency-Key` | header | string | no | A fresh unique string per intended write (1 to 255 visible ASCII characters). Sent again unchanged only to retry THAT write after a timeout or a 409 `IDEMPOTENCY_IN_PROGRESS`: the stored answer comes back (`Idempotency-Replayed: true`) and nothing is written twice. The same key with another request is a 422 `IDEMPOTENCY_KEY_REUSED`. |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Request body
`application/json`
| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `moduleId` | integer, 1–2147483647 | yes | The módulo the OCs join, each at the end of its queue, in the order sent. |
| `items` | object[] | yes | One OC per catálogo template, prefilled from it as the dashboard’s «Agregar del catálogo» does: name, «Corte (OC)», category, goal, units, price, colour and «Subprocesos». |
| `items[].templateId` | integer, 1–2147483647 | yes | The catálogo template (an id from `listCatalogue`). |
| `items[].orderNumber` | string | no | «Corte (OC)». Omitted, the template’s; blank on both, the template’s name. |
| `items[].total` | integer, 1–1000000 | no | «Total de unidades». Omitted, the template’s. |
| `items[].goal` | integer, 1–100000 | no | «Meta por hora». Omitted, the template’s; a template without one needs it here. |
| `items[].dueDate` | string (date-time) | no | «Fecha de compromiso», ISO 8601 with its offset. |
### Responses
| Status | Description |
| --- | --- |
| `201` | `ExternalReferencesAddedDto` |
| `400` | `INVALID_REQUEST`: a body or a parameter the route cannot take, as `{statusCode, code: 'INVALID_REQUEST', message, details}`, with one `{field, message}` in `details` per failure (`field` the body field as a path, such as `items.0.name`; null when the failure names none). Or a value the dashboard refuses (its own `code`), or `INVALID_IDEMPOTENCY_KEY`. Nothing changed. |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | `ROLE_NOT_ALLOWED`: the usuario’s role does not make this change in the dashboard (it needs ADMIN or MODERATOR, or an account with platform-wide access);`INSUFFICIENT_SCOPE`: the credential lacks «Gestionar el Programa»; `PLAN_LIMIT`: the organization’s plan does not answer this credential (a connected app needs «Conectar con IA», Plan Pro). Nothing changed. |
| `404` | The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both. |
| `409` | `IDEMPOTENCY_IN_PROGRESS` (with `Retry-After`): the same `Idempotency-Key` is still being answered; retry it unchanged. Or the dashboard’s own conflict. |
| `422` | `IDEMPOTENCY_KEY_REUSED`: the `Idempotency-Key` answered another request. Nothing changed. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X POST 'https://api.fluenta.io/v1/external/organizations/{organizationId}/references/from-catalogue' \
-H 'Authorization: Bearer ' \
-H 'Content-Type: application/json' \
-d @body.json
```
---
# List on hold
URL: https://docs.fluenta.io/docs/reference/program/list-on-hold
Operation: `listOnHold`
Required permission: `read`
`GET /v1/external/organizations/{organizationId}/references/on-hold`
The «banco de espera»: every paused referencia of the organization, newest pause first, with why, since when and from which módulo.
Answers the «banco de espera»: every «referencia» of the organization that was paused out of its «Programa», the most recently paused first, each with why it was paused, since when and from which «módulo». An organization with none on hold answers an empty list.
Each `id` is the `referenceId` of `getReference`; `reactivateReference` puts one back in a «Programa».
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Responses
| Status | Description |
| --- | --- |
| `200` | |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | The credential lacks the «Permiso» the route names (`INSUFFICIENT_SCOPE`), or the organization’s plan does not include what was asked (`PLAN_LIMIT`): «Conectar con IA» for a connected app, or a report or analytics route whose plan feature a Light organization lacks (a Light of plan version 1 keeps `reports`). |
| `404` | The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X GET 'https://api.fluenta.io/v1/external/organizations/{organizationId}/references/on-hold' \
-H 'Authorization: Bearer '
```
---
# Get reference
URL: https://docs.fluenta.io/docs/reference/program/get-reference
Operation: `getReference`
Required permission: `read`
`GET /v1/external/organizations/{organizationId}/references/{referenceId}`
One referencia (cut, OC): its módulo, state, units, produced and «saldo», goals, price, dates, «Subprocesos» and «Red» marks.
Answers one «referencia» (a cut, an OC): its «módulo», its state, its units, what it has produced and its «saldo», its goals, its price, its dates, its «Subprocesos» and its «Red de talleres» marks.
`referenceId` comes from `getProgram`, `searchReferences` or `listOnHold`; one that is unknown or belongs to another organization answers 404 `REFERENCE_NOT_FOUND`.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `referenceId` | path | integer | yes | The «referencia»: an `id` of `getProgram`, `searchReferences` or `listOnHold`. Unknown or of another organization: 404 `REFERENCE_NOT_FOUND`. |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Responses
| Status | Description |
| --- | --- |
| `200` | `ExternalReferenceDto` |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | The credential lacks the «Permiso» the route names (`INSUFFICIENT_SCOPE`), or the organization’s plan does not include what was asked (`PLAN_LIMIT`): «Conectar con IA» for a connected app, or a report or analytics route whose plan feature a Light organization lacks (a Light of plan version 1 keeps `reports`). |
| `404` | The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X GET 'https://api.fluenta.io/v1/external/organizations/{organizationId}/references/{referenceId}' \
-H 'Authorization: Bearer '
```
---
# Update reference
URL: https://docs.fluenta.io/docs/reference/program/update-reference
Operation: `updateReference`
Required permission: `program`
`PATCH /v1/external/organizations/{organizationId}/references/{referenceId}`
Edit an OC, as «Editar» does: only the fields sent change. A new «Meta por hora» keeps the past days’ goals; a new price re-prices today.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `referenceId` | path | integer | yes | |
| `Idempotency-Key` | header | string | no | A fresh unique string per intended write (1 to 255 visible ASCII characters). Sent again unchanged only to retry THAT write after a timeout or a 409 `IDEMPOTENCY_IN_PROGRESS`: the stored answer comes back (`Idempotency-Replayed: true`) and nothing is written twice. The same key with another request is a 422 `IDEMPOTENCY_KEY_REUSED`. |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Request body
`application/json`
| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | no | «Referencia». |
| `orderNumber` | string | no | «Corte (OC)»; null clears it. |
| `garment` | string | no | «Categoría»; null clears it. |
| `goal` | integer, 1–100000 | no | «Meta por hora». The days before today keep the goal they were measured with. |
| `total` | integer, 1–1000000 | no | «Total de unidades»; null takes them away, only where the plant’s operarios pick the referencia. |
| `price` | number, 0–1000000000 | no | «Precio por unidad». A new price re-prices today’s units; past days keep theirs. |
| `color` | string | no | `#RRGGBB`. |
| `dueDate` | string (date-time) | no | «Fecha de compromiso», ISO 8601 with its offset; null clears it. |
| `spliceDate` | string (date-time) | no | «F. Empalme», ISO 8601 with its offset; null clears it. |
| `priority` | boolean | no | «OC prioritaria». |
| `continuity` | boolean | no | «Continuidad». |
| `step1Name` | string | no | «Subproceso 1»; null clears it (with its goal). |
| `step1Goal` | integer | no | |
| `step2Name` | string | no | «Subproceso 2»; null clears it. |
| `step2Goal` | integer | no | |
| `note` | string | no | Why («motivo»). Required when the change touches an OC a brand («marca») dispatched to this taller, else optional: 400 `PROGRAM_CHANGE_NOTE_REQUIRED` without one. Goes to the brand’s «Bandeja de cambios». |
### Responses
| Status | Description |
| --- | --- |
| `200` | `ExternalReferenceDto` |
| `400` | `INVALID_REQUEST`: a body or a parameter the route cannot take, as `{statusCode, code: 'INVALID_REQUEST', message, details}`, with one `{field, message}` in `details` per failure (`field` the body field as a path, such as `items.0.name`; null when the failure names none). Or a value the dashboard refuses (its own `code`), or `INVALID_IDEMPOTENCY_KEY`. Nothing changed. |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | `ROLE_NOT_ALLOWED`: the usuario’s role does not make this change in the dashboard (it needs ADMIN or MODERATOR, or an account with platform-wide access);`INSUFFICIENT_SCOPE`: the credential lacks «Gestionar el Programa»; `PLAN_LIMIT`: the organization’s plan does not answer this credential (a connected app needs «Conectar con IA», Plan Pro). Nothing changed. |
| `404` | The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both. |
| `409` | `IDEMPOTENCY_IN_PROGRESS` (with `Retry-After`): the same `Idempotency-Key` is still being answered; retry it unchanged. Or the dashboard’s own conflict. |
| `422` | `IDEMPOTENCY_KEY_REUSED`: the `Idempotency-Key` answered another request. Nothing changed. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X PATCH 'https://api.fluenta.io/v1/external/organizations/{organizationId}/references/{referenceId}' \
-H 'Authorization: Bearer ' \
-H 'Content-Type: application/json' \
-d @body.json
```
---
# Delete reference
URL: https://docs.fluenta.io/docs/reference/program/delete-reference
Operation: `deleteReference`
Required permission: `program`
`DELETE /v1/external/organizations/{organizationId}/references/{referenceId}`
Delete an OC, as «Borrar» does, only with the `confirmationToken` its preview returned once the usuario said yes: 422 `CONFIRMATION_REQUIRED` without it, 409 `PREVIEW_STALE` when it ran out or the OC changed since.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `referenceId` | path | integer | yes | |
| `Idempotency-Key` | header | string | no | A fresh unique string per intended write (1 to 255 visible ASCII characters). Sent again unchanged only to retry THAT write after a timeout or a 409 `IDEMPOTENCY_IN_PROGRESS`: the stored answer comes back (`Idempotency-Replayed: true`) and nothing is written twice. The same key with another request is a 422 `IDEMPOTENCY_KEY_REUSED`. |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Request body
`application/json`
| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `confirmationToken` | string | no | The `confirmationToken` the delete’s preview returned, exactly as it came, sent once the usuario saw the preview and said yes. Lasts 15 minutes. Without it: 422 `CONFIRMATION_REQUIRED`. |
### Responses
| Status | Description |
| --- | --- |
| `200` | `ExternalReferenceDeletedDto` |
| `400` | `INVALID_REQUEST`: a body or a parameter the route cannot take, as `{statusCode, code: 'INVALID_REQUEST', message, details}`, with one `{field, message}` in `details` per failure (`field` the body field as a path, such as `items.0.name`; null when the failure names none). Or a value the dashboard refuses (its own `code`), or `INVALID_IDEMPOTENCY_KEY`. Nothing changed. |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | `ROLE_NOT_ALLOWED`: the usuario’s role does not make this change in the dashboard (it needs ADMIN or MODERATOR, or an account with platform-wide access);`INSUFFICIENT_SCOPE`: the credential lacks «Gestionar el Programa»; `PLAN_LIMIT`: the organization’s plan does not answer this credential (a connected app needs «Conectar con IA», Plan Pro). Nothing changed. |
| `404` | The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both. |
| `409` | `IDEMPOTENCY_IN_PROGRESS` (with `Retry-After`): the same `Idempotency-Key` is still being answered; retry it unchanged. Or the dashboard’s own conflict. |
| `422` | `IDEMPOTENCY_KEY_REUSED`: the `Idempotency-Key` answered another request. Nothing changed. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X DELETE 'https://api.fluenta.io/v1/external/organizations/{organizationId}/references/{referenceId}' \
-H 'Authorization: Bearer ' \
-H 'Content-Type: application/json' \
-d @body.json
```
---
# Mark reference arrived
URL: https://docs.fluenta.io/docs/reference/program/mark-reference-arrived
Operation: `markReferenceArrived`
Required permission: `program`
`POST /v1/external/organizations/{organizationId}/references/{referenceId}/arrive`
«Llegó»: the taller marks a brand’s OC («OC anticipada») as physically arrived, with the «Meta por hora» and the units its «ficha técnica» says.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `referenceId` | path | integer | yes | |
| `Idempotency-Key` | header | string | no | A fresh unique string per intended write (1 to 255 visible ASCII characters). Sent again unchanged only to retry THAT write after a timeout or a 409 `IDEMPOTENCY_IN_PROGRESS`: the stored answer comes back (`Idempotency-Replayed: true`) and nothing is written twice. The same key with another request is a 422 `IDEMPOTENCY_KEY_REUSED`. |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Request body
`application/json`
| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `goal` | integer, 1–1000000 | yes | «Meta por hora» on the line, asked at «Llegó». |
| `units` | integer, 1–10000000 | no | The units the corte’s «ficha técnica» says. Absent, the brand’s. Units that differ are reported to the brand, never applied. |
| `note` | string | no | A note for the brand, optional. |
### Responses
| Status | Description |
| --- | --- |
| `200` | `ExternalReferenceDto` |
| `400` | `INVALID_REQUEST`: a body or a parameter the route cannot take, as `{statusCode, code: 'INVALID_REQUEST', message, details}`, with one `{field, message}` in `details` per failure (`field` the body field as a path, such as `items.0.name`; null when the failure names none). Or a value the dashboard refuses (its own `code`), or `INVALID_IDEMPOTENCY_KEY`. Nothing changed. |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | `ROLE_NOT_ALLOWED`: the usuario’s role does not make this change in the dashboard (it needs ADMIN, or an account with platform-wide access);`INSUFFICIENT_SCOPE`: the credential lacks «Gestionar el Programa»; `PLAN_LIMIT`: the organization’s plan does not answer this credential (a connected app needs «Conectar con IA», Plan Pro). Nothing changed. |
| `404` | The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both. |
| `409` | `IDEMPOTENCY_IN_PROGRESS` (with `Retry-After`): the same `Idempotency-Key` is still being answered; retry it unchanged. Or the dashboard’s own conflict. |
| `422` | `IDEMPOTENCY_KEY_REUSED`: the `Idempotency-Key` answered another request. Nothing changed. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X POST 'https://api.fluenta.io/v1/external/organizations/{organizationId}/references/{referenceId}/arrive' \
-H 'Authorization: Bearer ' \
-H 'Content-Type: application/json' \
-d @body.json
```
---
# Preview delete reference
URL: https://docs.fluenta.io/docs/reference/program/preview-delete-reference
Operation: `previewDeleteReference`
Required permission: `read`
`POST /v1/external/organizations/{organizationId}/references/{referenceId}/delete-preview`
What deleting an OC would do, writing nothing: the OC, whether its módulo empties (every puesto cleared), the puestos sewing it, and the `confirmationToken` `deleteReference` needs. Show it to the usuario and wait for a yes.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `referenceId` | path | integer | yes | |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Responses
| Status | Description |
| --- | --- |
| `200` | `ExternalReferenceDeletePreviewDto` |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | The credential lacks the «Permiso» the route names (`INSUFFICIENT_SCOPE`), or the organization’s plan does not include what was asked (`PLAN_LIMIT`): «Conectar con IA» for a connected app, or a report or analytics route whose plan feature a Light organization lacks (a Light of plan version 1 keeps `reports`). |
| `404` | The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X POST 'https://api.fluenta.io/v1/external/organizations/{organizationId}/references/{referenceId}/delete-preview' \
-H 'Authorization: Bearer '
```
---
# Hold reference
URL: https://docs.fluenta.io/docs/reference/program/hold-reference
Operation: `holdReference`
Required permission: `program`
`PATCH /v1/external/organizations/{organizationId}/references/{referenceId}/hold`
Pause an OC into the «banco de espera» (⏸): it leaves its módulo’s queue with its counts, and the puestos sewing it move to the next OC.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `referenceId` | path | integer | yes | |
| `Idempotency-Key` | header | string | no | A fresh unique string per intended write (1 to 255 visible ASCII characters). Sent again unchanged only to retry THAT write after a timeout or a 409 `IDEMPOTENCY_IN_PROGRESS`: the stored answer comes back (`Idempotency-Replayed: true`) and nothing is written twice. The same key with another request is a 422 `IDEMPOTENCY_KEY_REUSED`. |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Request body
`application/json`
| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `reason` | string | no | Why it pauses, as the dashboard offers it: «Problema de tela», «Falta insumo», «Calidad», «Otro», or free text. |
| `note` | string | no | Why («motivo»). Required when the change touches an OC a brand («marca») dispatched to this taller, else optional: 400 `PROGRAM_CHANGE_NOTE_REQUIRED` without one. Goes to the brand’s «Bandeja de cambios». |
### Responses
| Status | Description |
| --- | --- |
| `200` | `ExternalReferenceDto` |
| `400` | `INVALID_REQUEST`: a body or a parameter the route cannot take, as `{statusCode, code: 'INVALID_REQUEST', message, details}`, with one `{field, message}` in `details` per failure (`field` the body field as a path, such as `items.0.name`; null when the failure names none). Or a value the dashboard refuses (its own `code`), or `INVALID_IDEMPOTENCY_KEY`. Nothing changed. |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | `ROLE_NOT_ALLOWED`: the usuario’s role does not make this change in the dashboard (it needs ADMIN, or an account with platform-wide access);`INSUFFICIENT_SCOPE`: the credential lacks «Gestionar el Programa»; `PLAN_LIMIT`: the organization’s plan does not answer this credential (a connected app needs «Conectar con IA», Plan Pro). Nothing changed. |
| `404` | The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both. |
| `409` | `IDEMPOTENCY_IN_PROGRESS` (with `Retry-After`): the same `Idempotency-Key` is still being answered; retry it unchanged. Or the dashboard’s own conflict. |
| `422` | `IDEMPOTENCY_KEY_REUSED`: the `Idempotency-Key` answered another request. Nothing changed. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X PATCH 'https://api.fluenta.io/v1/external/organizations/{organizationId}/references/{referenceId}/hold' \
-H 'Authorization: Bearer ' \
-H 'Content-Type: application/json' \
-d @body.json
```
---
# Reactivate reference
URL: https://docs.fluenta.io/docs/reference/program/reactivate-reference
Operation: `reactivateReference`
Required permission: `program`
`PATCH /v1/external/organizations/{organizationId}/references/{referenceId}/reactivate`
Bring an OC back from the «banco de espera» (▶) into a módulo, at the end of its queue, with its counts.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `referenceId` | path | integer | yes | |
| `Idempotency-Key` | header | string | no | A fresh unique string per intended write (1 to 255 visible ASCII characters). Sent again unchanged only to retry THAT write after a timeout or a 409 `IDEMPOTENCY_IN_PROGRESS`: the stored answer comes back (`Idempotency-Replayed: true`) and nothing is written twice. The same key with another request is a 422 `IDEMPOTENCY_KEY_REUSED`. |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Request body
`application/json`
| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `moduleId` | integer, 1–2147483647 | yes | The módulo it re-enters, at the end of its queue: the one it left or any other of the organization. |
| `note` | string | no | Why («motivo»). Required when the change touches an OC a brand («marca») dispatched to this taller, else optional: 400 `PROGRAM_CHANGE_NOTE_REQUIRED` without one. Goes to the brand’s «Bandeja de cambios». |
### Responses
| Status | Description |
| --- | --- |
| `200` | `ExternalReferenceDto` |
| `400` | `INVALID_REQUEST`: a body or a parameter the route cannot take, as `{statusCode, code: 'INVALID_REQUEST', message, details}`, with one `{field, message}` in `details` per failure (`field` the body field as a path, such as `items.0.name`; null when the failure names none). Or a value the dashboard refuses (its own `code`), or `INVALID_IDEMPOTENCY_KEY`. Nothing changed. |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | `ROLE_NOT_ALLOWED`: the usuario’s role does not make this change in the dashboard (it needs ADMIN, or an account with platform-wide access);`INSUFFICIENT_SCOPE`: the credential lacks «Gestionar el Programa»; `PLAN_LIMIT`: the organization’s plan does not answer this credential (a connected app needs «Conectar con IA», Plan Pro). Nothing changed. |
| `404` | The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both. |
| `409` | `IDEMPOTENCY_IN_PROGRESS` (with `Retry-After`): the same `Idempotency-Key` is still being answered; retry it unchanged. Or the dashboard’s own conflict. |
| `422` | `IDEMPOTENCY_KEY_REUSED`: the `Idempotency-Key` answered another request. Nothing changed. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X PATCH 'https://api.fluenta.io/v1/external/organizations/{organizationId}/references/{referenceId}/reactivate' \
-H 'Authorization: Bearer ' \
-H 'Content-Type: application/json' \
-d @body.json
```
---
# Record sacrifice
URL: https://docs.fluenta.io/docs/reference/program/record-sacrifice
Operation: `recordSacrifice`
Required permission: `program`
`PATCH /v1/external/organizations/{organizationId}/references/{referenceId}/sacrifice`
Set the «descuento por sacrificio» of an OC: units written off its «saldo» without being production. Absolute; 0 removes it.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `referenceId` | path | integer | yes | |
| `Idempotency-Key` | header | string | no | A fresh unique string per intended write (1 to 255 visible ASCII characters). Sent again unchanged only to retry THAT write after a timeout or a 409 `IDEMPOTENCY_IN_PROGRESS`: the stored answer comes back (`Idempotency-Replayed: true`) and nothing is written twice. The same key with another request is a 422 `IDEMPOTENCY_KEY_REUSED`. |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Request body
`application/json`
| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `sacrificed` | integer, 0–999999 | yes | Units written off the OC’s «saldo» without being production («descuento por sacrificio»). Absolute: replaces the figure stored; 0 removes it. Never past what is left to make. |
| `note` | string | no | Why: «tela con defecto», «faltante de corte»… |
### Responses
| Status | Description |
| --- | --- |
| `200` | `ExternalReferenceDto` |
| `400` | `INVALID_REQUEST`: a body or a parameter the route cannot take, as `{statusCode, code: 'INVALID_REQUEST', message, details}`, with one `{field, message}` in `details` per failure (`field` the body field as a path, such as `items.0.name`; null when the failure names none). Or a value the dashboard refuses (its own `code`), or `INVALID_IDEMPOTENCY_KEY`. Nothing changed. |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | `ROLE_NOT_ALLOWED`: the usuario’s role does not make this change in the dashboard (it needs ADMIN, or an account with platform-wide access);`INSUFFICIENT_SCOPE`: the credential lacks «Gestionar el Programa»; `PLAN_LIMIT`: the organization’s plan does not answer this credential (a connected app needs «Conectar con IA», Plan Pro). Nothing changed. |
| `404` | The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both. |
| `409` | `IDEMPOTENCY_IN_PROGRESS` (with `Retry-After`): the same `Idempotency-Key` is still being answered; retry it unchanged. Or the dashboard’s own conflict. |
| `422` | `IDEMPOTENCY_KEY_REUSED`: the `Idempotency-Key` answered another request. Nothing changed. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X PATCH 'https://api.fluenta.io/v1/external/organizations/{organizationId}/references/{referenceId}/sacrifice' \
-H 'Authorization: Bearer ' \
-H 'Content-Type: application/json' \
-d @body.json
```
---
# Split reference
URL: https://docs.fluenta.io/docs/reference/program/split-reference
Operation: `splitReference`
Required permission: `program`
`POST /v1/external/organizations/{organizationId}/references/{referenceId}/split`
Split an OC between two módulos (✂): `units` go to a new «-B» portion at the end of another módulo’s queue; the original keeps the rest.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `referenceId` | path | integer | yes | |
| `Idempotency-Key` | header | string | no | A fresh unique string per intended write (1 to 255 visible ASCII characters). Sent again unchanged only to retry THAT write after a timeout or a 409 `IDEMPOTENCY_IN_PROGRESS`: the stored answer comes back (`Idempotency-Replayed: true`) and nothing is written twice. The same key with another request is a 422 `IDEMPOTENCY_KEY_REUSED`. |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Request body
`application/json`
| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `units` | integer, 1–1000000 | yes | Units for the NEW portion (a «-B» OC); the original keeps the rest. |
| `moduleId` | integer, 1–2147483647 | yes | The módulo the new portion goes to: never the original’s. |
| `note` | string | no | Why («motivo»). Required when the change touches an OC a brand («marca») dispatched to this taller, else optional: 400 `PROGRAM_CHANGE_NOTE_REQUIRED` without one. Goes to the brand’s «Bandeja de cambios». |
### Responses
| Status | Description |
| --- | --- |
| `201` | `ExternalSplitReferenceResultDto` |
| `400` | `INVALID_REQUEST`: a body or a parameter the route cannot take, as `{statusCode, code: 'INVALID_REQUEST', message, details}`, with one `{field, message}` in `details` per failure (`field` the body field as a path, such as `items.0.name`; null when the failure names none). Or a value the dashboard refuses (its own `code`), or `INVALID_IDEMPOTENCY_KEY`. Nothing changed. |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | `ROLE_NOT_ALLOWED`: the usuario’s role does not make this change in the dashboard (it needs ADMIN, or an account with platform-wide access);`INSUFFICIENT_SCOPE`: the credential lacks «Gestionar el Programa»; `PLAN_LIMIT`: the organization’s plan does not answer this credential (a connected app needs «Conectar con IA», Plan Pro). Nothing changed. |
| `404` | The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both. |
| `409` | `IDEMPOTENCY_IN_PROGRESS` (with `Retry-After`): the same `Idempotency-Key` is still being answered; retry it unchanged. Or the dashboard’s own conflict. |
| `422` | `IDEMPOTENCY_KEY_REUSED`: the `Idempotency-Key` answered another request. Nothing changed. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X POST 'https://api.fluenta.io/v1/external/organizations/{organizationId}/references/{referenceId}/split' \
-H 'Authorization: Bearer ' \
-H 'Content-Type: application/json' \
-d @body.json
```
---
# Get settings
URL: https://docs.fluenta.io/docs/reference/organization/get-settings
Operation: `getSettings`
Required permission: `read`
`GET /v1/external/organizations/{organizationId}/settings`
The plant’s «jornada»: its «zona horaria» and today in its clock, when the shift starts and ends on each weekday, the «jornada continua» and «jornada especial» days, the days off, and its switches. The «descansos» of a «módulo» are `listModuleRestTimes`.
Answers what «Configuración» shows under «Jornada», «Zona horaria» and the switches of «Aplicación». Read it first when a question depends on when the plant works, or on what «hoy» is.
`timezone` is the plant’s «zona horaria» (an IANA name, e.g. `America/Bogota`). `today` (`YYYY-MM-DD`) and `currentHour` (0 to 23) are that clock now: the day to send wherever a route takes a `day`. In `shift`, every time is minutes from midnight on that clock (330 is 05:30), every weekday is 0 Sunday to 6 Saturday, and every day is `YYYY-MM-DD`: `startMin` for every worked day, `endMinsByWeekday` per weekday (null: not worked), `continuousDays` the «jornada continua» weekdays (no «descanso» applies), `specialDays` the «jornada especial» dates with the minute each ends at, `holidays` the days off the plant added by hand, `nationalHolidays` its country’s holidays (a day off unless `worked`) and `workedHolidays` the national holidays it works.
A plant that never saved its «jornada» answers `shift.configured: false`, a null `startMin` and empty lists; the read creates nothing. `switches` are the effective flags: stored on and allowed by the plan.
The «descansos» are not here: they belong to each «módulo» (`listModuleRestTimes`, with a `moduleId` from `listModules`).
Not paged: the whole answer comes in one response.
Plan: on every plan, Light included. A «token de API» reads it in any organization the user who created it belongs to; a connected AI app (a «conexión») reads it only where the organization has «Conectar con IA» (`getPlan.features.aiConnector`, a Pro feature), and answers 403 `PLAN_LIMIT` elsewhere.
Rate limit: one request of the credential’s ordinary budget in this organization (`getPlan` answers the numbers); past it, 429 `RATE_LIMITED` with `Retry-After` in seconds.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Responses
| Status | Description |
| --- | --- |
| `200` | `ExternalSettingsDto` |
| `401` | `INVALID_API_TOKEN`: the credential is missing, malformed, unknown, expired or revoked. One body for all of them. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | `PLAN_LIMIT` with `feature: "aiConnector"`: the credential is a «conexión» and the organization’s plan has no «Conectar con IA» (Light). A «token de API» is not refused here on any plan on sale: the plant reads are on every plan. `INSUFFICIENT_SCOPE` only for a credential without the «Permiso» `read` («Leer»), which every credential is issued with. |
| `404` | `ORGANIZATION_NOT_FOUND`: the organization does not exist or is out of the credential’s reach. One body for both. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X GET 'https://api.fluenta.io/v1/external/organizations/{organizationId}/settings' \
-H 'Authorization: Bearer '
```
---
# List organization stations
URL: https://docs.fluenta.io/docs/reference/plant/list-organization-stations
Operation: `listOrganizationStations`
Required permission: `read`
`GET /v1/external/organizations/{organizationId}/stations`
The organization’s «puestos» (stations) across its «módulos», optionally of one «módulo», as the live board reads them now. 50 per page, by «módulo» in the floor’s order, then in the board’s.
Answers the «puestos» of the «Tablero en tiempo real» as one flat list, across every «módulo» or narrowed to one with `moduleId` (an `id` of `listModules` or `getFloor`). The dashboard has no such screen: it lists them one «módulo» at a time, which is `listStations`.
Paged: 50 «puestos» per page, ordered by «módulo» in the floor’s order and then in the board’s. `page` is 1-based (default 1); `total` counts every «puesto» that matches, so the last page is `ceil(total / 50)`, and a page past it answers `items: []` with the same `total`. A deleted «puesto», or one of a deleted «módulo», is never listed.
Each «puesto» is read as the «Tablero en tiempo real» reads it now: `unitsThisHour` and `hourGoal` are units of the hour `currentHour` names, `unitsToday` units of the plant day `today` names, `color` the «semáforo» (`GRAY`, `RED`, `YELLOW`, `GREEN`), `pausedAt` an ISO 8601 UTC instant, and `currentReference.price` money per unit in the organization’s currency (`getOrganization.currency`).
The answer carries no `today` or `currentHour`: read them from `getSettings`. Every day and hour is on the plant’s own clock, its «zona horaria» (`getSettings.timezone`, an IANA name), never UTC and never the caller’s.
A «puesto» is `station` in this API’s paths and answers, and `OPERATION` in its error code.
Plan: on every plan, Light included. A «token de API» reads it in any organization the user who created it belongs to; a connected AI app (a «conexión») reads it only where the organization has «Conectar con IA» (`getPlan.features.aiConnector`, a Pro feature), and answers 403 `PLAN_LIMIT` elsewhere.
Rate limit: one request of the credential’s ordinary budget in this organization (`getPlan` answers the numbers); past it, 429 `RATE_LIMITED` with `Retry-After` in seconds.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `moduleId` | query | integer | no | Only the «puestos» of this «módulo»: an `id` of `listModules` or `getFloor`. One of another organization answers 404 `MODULE_NOT_FOUND`. |
| `page` | query | integer | no | 1-based page; 50 «puestos» per page, by «módulo» in the floor’s order, then in the board’s order. A page past the last answers no item. |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Responses
| Status | Description |
| --- | --- |
| `200` | `ExternalStationPageDto` |
| `400` | `INVALID_REQUEST`: `page` or `moduleId` is not a positive integer (`page=0`, `page=abc`, `moduleId=abc`), or is past its maximum. `details` holds one `{field, message}` per failure, `field` naming the parameter. |
| `401` | `INVALID_API_TOKEN`: the credential is missing, malformed, unknown, expired or revoked. One body for all of them. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | `PLAN_LIMIT` with `feature: "aiConnector"`: the credential is a «conexión» and the organization’s plan has no «Conectar con IA» (Light). A «token de API» is not refused here on any plan on sale: the plant reads are on every plan. `INSUFFICIENT_SCOPE` only for a credential without the «Permiso» `read` («Leer»), which every credential is issued with. |
| `404` | `ORGANIZATION_NOT_FOUND`: the organization does not exist or is out of the credential’s reach. One body for both. `MODULE_NOT_FOUND`: no «módulo» with that `moduleId` in this organization. One body whether it does not exist, was deleted, belongs to another organization or is not a positive integer: never a 400 or a 403. Answered for the `moduleId` filter: a «módulo» of another organization is a 404, never an empty page. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X GET 'https://api.fluenta.io/v1/external/organizations/{organizationId}/stations' \
-H 'Authorization: Bearer '
```
---
# Get station
URL: https://docs.fluenta.io/docs/reference/plant/get-station
Operation: `getStation`
Required permission: `read`
`GET /v1/external/organizations/{organizationId}/stations/{stationId}`
One «puesto» (station) as the live board reads it now: «operación», seated «operario», current «referencia», units this hour and today.
Answers one «puesto», a numbered station of a «módulo», as its card on the «Tablero en tiempo real» reads now: the «operación» sewn there (`name`), its place in the «módulo» (`position`), the seated «operario» (`operator`, null when empty), the «referencia» it is on (`currentReference`, null when none) and its counts.
Each «puesto» is read as the «Tablero en tiempo real» reads it now: `unitsThisHour` and `hourGoal` are units of the hour `currentHour` names, `unitsToday` units of the plant day `today` names, `color` the «semáforo» (`GRAY`, `RED`, `YELLOW`, `GREEN`), `pausedAt` an ISO 8601 UTC instant, and `currentReference.price` money per unit in the organization’s currency (`getOrganization.currency`).
The answer carries no `today` or `currentHour`: read them from `getSettings`. Every day and hour is on the plant’s own clock, its «zona horaria» (`getSettings.timezone`, an IANA name), never UTC and never the caller’s.
`stationId` comes from `listStations`, `listOrganizationStations` or `getFloor`, or from the `station.id` of an «operario» (`listOperators`, `getOperator`). A «puesto» is `station` in this API’s paths and answers, and `OPERATION` in its error code.
Not paged: the whole answer comes in one response.
Plan: on every plan, Light included. A «token de API» reads it in any organization the user who created it belongs to; a connected AI app (a «conexión») reads it only where the organization has «Conectar con IA» (`getPlan.features.aiConnector`, a Pro feature), and answers 403 `PLAN_LIMIT` elsewhere.
Rate limit: one request of the credential’s ordinary budget in this organization (`getPlan` answers the numbers); past it, 429 `RATE_LIMITED` with `Retry-After` in seconds.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `stationId` | path | integer | yes | The «puesto»: an `id` of `listStations`, `listOrganizationStations` or `getFloor`, or the `station.id` of an «operario». Named `operationId` where a route filters by it. |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Responses
| Status | Description |
| --- | --- |
| `200` | `ExternalStationDto` |
| `401` | `INVALID_API_TOKEN`: the credential is missing, malformed, unknown, expired or revoked. One body for all of them. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | `PLAN_LIMIT` with `feature: "aiConnector"`: the credential is a «conexión» and the organization’s plan has no «Conectar con IA» (Light). A «token de API» is not refused here on any plan on sale: the plant reads are on every plan. `INSUFFICIENT_SCOPE` only for a credential without the «Permiso» `read` («Leer»), which every credential is issued with. |
| `404` | `ORGANIZATION_NOT_FOUND`: the organization does not exist or is out of the credential’s reach. One body for both. `OPERATION_NOT_FOUND`: no «puesto» with that `stationId` in this organization. One body whether it does not exist, was deleted (or its «módulo» was), belongs to another organization or is not a positive integer: never a 400 or a 403. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X GET 'https://api.fluenta.io/v1/external/organizations/{organizationId}/stations/{stationId}' \
-H 'Authorization: Bearer '
```
---
# Get station events
URL: https://docs.fluenta.io/docs/reference/analytics/get-station-events
Operation: `getStationEvents`
Required permission: `read`
`GET /v1/external/organizations/{organizationId}/stations/{stationId}/analytics/events`
One puesto’s marcas of a day, summarised from the marcas log: units, billing, online and offline marcas, pace and «pausas sin registro».
What it is: one «puesto»’s «marcas» of one plant day in a single summary (`events`), as «Analítica» reads them in the dashboard («Reportes», tab «Analítica»): how many «marcas» and units, how many of them billable and for how much, how they arrived (live, queued offline, or from an app too old to send a time), the seconds between consecutive «marcas» (median and 90th percentile), how many gaps longer than `idleMinutes` minutes there were («pausas sin registro»: gaps inferred from the log, not the «pausas» an «operario» takes with «Pausar»), and the first and last «marca».
Source: «Analítica» reads the «marcas» log (one row per unit an «operario» registered on the app, with its time and channel) beside the hourly buckets the reports are built from, so it can tell pace, connectivity and gaps that the reports cannot.
Money is «Facturación»: the units of the «puestos de salida» × the price per unit each was billed at, in the plant’s currency (`currency`, ISO 4217: `EUR` for a plant in España, `COP` for every other), in whole currency units with up to 2 decimals, never cents. Whether a money figure reads null or 0 where nothing was priced is stated on each field.
Days and hours are the plant’s own: a plant day is a calendar day `YYYY-MM-DD` in the organization’s timezone (`getOrganization` `timezone`), never UTC, and an `hour` is the clock hour 0–23 that starts at it in that zone. `day` is optional: without it, the plant’s today on the organization’s own clock, as on every single-day route of the API externa; the answer’s `day` says which day was read. `firstAt` and `lastAt` are instants, ISO 8601 in UTC.
Naming: a «puesto» (a numbered station of a «módulo») is a `station` in paths and an `operation` in ids, filters and codes. A path’s `stationId` is the same number as the `operationId` of answers and of filters and as the `id` `listStations` returns, and one that is not this organization’s answers 404 `OPERATION_NOT_FOUND`.
Plan: needs the plan feature `analytics` («Analítica»), the one its dashboard screen is gated by. Pro, a trial of Pro and an «exenta» organization answer it; a Light organization, of either plan version, does not: 403 `PLAN_LIMIT` with `feature: "analytics"`. `getPlan` `features` lists what the organization’s plan has.
See also: `getStationDayReport` for the same «puesto» and day as its report (hours, «meta», «Eficiencia»); `getStationActivity` with `view=events` is this same summary under the first release’s route.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `stationId` | path | integer | yes | The «puesto»: the `id` of a station from `listStations`. The same number is called `operationId` in answers and in filters; unknown or of another organization: 404 `OPERATION_NOT_FOUND`. |
| `day` | query | string | no | The plant day to read, `YYYY-MM-DD` on the plant’s own clock (the organization’s timezone, never UTC). Omitted: the plant’s today on that clock; the answer’s `day` says which day was read. Not a real calendar day: 400 `INVALID_DAY`. |
| `idleMinutes` | query | number | no | In minutes: a gap between two consecutive «marcas» longer than this counts as one «pausa sin registro» (`idleGapsOverN`, `idleGaps`). One of 5, 10 or 15; default 5. The answer echoes the value used. |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Responses
| Status | Description |
| --- | --- |
| `200` | The «puesto»’s «marcas» of the day, summarised. |
| `400` | `INVALID_DAY`: `day` is not a real calendar day written `YYYY-MM-DD`. `INVALID_REQUEST`: a query parameter is missing, of the wrong type or outside its allowed values; `details` lists each one as `{field, message}`. Body: `{statusCode, code, message}`, the message in Spanish, for the usuario. |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | `PLAN_LIMIT` with `feature: "analytics"`: the organization’s plan lacks «Analítica» (Pro, a trial of Pro and an «exenta» organization answer it; a Light organization, of either plan version, does not). `PLAN_LIMIT` with `feature: "aiConnector"`: a connected app reads an organization without «Conectar con IA». `INSUFFICIENT_SCOPE`: the credential lacks the «Permiso» `read`. Retrying does not help: the plan or the credential has to change. |
| `404` | `ORGANIZATION_NOT_FOUND`: the organization does not exist or is out of the credential’s reach. `OPERATION_NOT_FOUND`: the «puesto» (`stationId`) does not exist or is another organization’s. One body for a row that is missing and for one of another organization. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X GET 'https://api.fluenta.io/v1/external/organizations/{organizationId}/stations/{stationId}/analytics/events' \
-H 'Authorization: Bearer '
```
---
# Get station day report
URL: https://docs.fluenta.io/docs/reference/analytics/get-station-day-report
Operation: `getStationDayReport`
Required permission: `read`
`GET /v1/external/organizations/{organizationId}/stations/{stationId}/days/{day}/report`
One puesto’s «reporte por operación» for a day: hour by hour, each referencia’s units, meta, efficiency and price, the hour’s «Facturación» and its pausas; by «Subproceso» with «El operario escoge la referencia» on.
What it is: one «puesto»’s «reporte por operación» for one plant day, the report the dashboard opens from a «puesto»’s «Ver reporte». `hours` holds one entry per clock hour the «puesto» had a «marca» or a «pausa», in order; each totals the hour and lists, in `rows`, one row per «referencia» worked in it (who marked it last, its units, its «meta», its price per unit) and, in `pauseSpans`, the «pausas» that started in it.
How it is computed: the hour’s `goal` is the «puesto»’s «meta» for that hour as it stood then, its `efficiency` is `units` × 100 ÷ `goal`, and its `billedAmount` is the sum of each row’s `units` × `unitPrice`, each rounded to 2 decimals. With the plant’s switch «El operario escoge la referencia» on, every row also says its «Subproceso» (`step`, `stepName`) and every hour carries `steps`: the hour by «referencia» and «Subproceso», its `units` then being complete units only and its `goal` the sum of each step’s expected units. With the switch off those keys are absent.
`efficiency` is «Eficiencia»: units made × 100 ÷ the «meta» of the same span, a percentage with 2 decimals (100 = exactly the goal, it can pass 100; never a 0–1 ratio), null where the goal is 0. Every «puesto» counts towards it, a «puesto de salida» or not.
Money is «Facturación»: the units of the «puestos de salida» × the price per unit each was billed at, in the plant’s currency (`currency`, ISO 4217: `EUR` for a plant in España, `COP` for every other), in whole currency units with up to 2 decimals, never cents. Whether a money figure reads null or 0 where nothing was priced is stated on each field. `unitPrice` is null on a «puesto» that is not «de salida», and its hours bill 0.
Days and hours are the plant’s own: a plant day is a calendar day `YYYY-MM-DD` in the organization’s timezone (`getOrganization` `timezone`), never UTC, and an `hour` is the clock hour 0–23 that starts at it in that zone. The day is the path’s `day`, required.
Naming: a «puesto» (a numbered station of a «módulo») is a `station` in paths and an `operation` in ids, filters and codes. A path’s `stationId` is the same number as the `operationId` of answers and of filters and as the `id` `listStations` returns, and one that is not this organization’s answers 404 `OPERATION_NOT_FOUND`.
Plan: needs the plan feature `reports` («Reportes»), the one its dashboard screen is gated by. Pro, a trial of Pro, an «exenta» organization and a Light of plan version 1 (`getPlan` `planVersion`) answer it; a Light of plan version 2 does not: 403 `PLAN_LIMIT` with `feature: "reports"`. `getPlan` `features` lists what the organization’s plan has.
See also: `getStationEvents` for the same «puesto» and day read from the «marcas» log (pace, connectivity); `getEfficiency` or `getBilling` with `operationId` for this «puesto» over a range.
### Authorization
- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token
Send it as `Authorization: Bearer `.
### Parameters
| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `stationId` | path | integer | yes | The «puesto»: the `id` of a station from `listStations`. The same number is called `operationId` in answers and in filters; unknown or of another organization: 404 `OPERATION_NOT_FOUND`. |
| `day` | path | string (date) | yes | The plant day to read, `YYYY-MM-DD` on the organization’s own clock (its timezone, never UTC). Required here: it is part of the path. Not a real calendar day: 400 `INVALID_DAY`. |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |
### Responses
| Status | Description |
| --- | --- |
| `200` | The «puesto»’s day, hour by hour. |
| `400` | `INVALID_DAY`: `day` is not a real calendar day written `YYYY-MM-DD`. Body: `{statusCode, code, message}`, the message in Spanish, for the usuario. |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | `PLAN_LIMIT` with `feature: "reports"`: the organization’s plan lacks «Reportes» (Pro, a trial of Pro, an «exenta» organization and a Light of plan version 1 (`getPlan` `planVersion`) answer it; a Light of plan version 2 does not). `PLAN_LIMIT` with `feature: "aiConnector"`: a connected app reads an organization without «Conectar con IA». `INSUFFICIENT_SCOPE`: the credential lacks the «Permiso» `read`. Retrying does not help: the plan or the credential has to change. |
| `404` | `ORGANIZATION_NOT_FOUND`: the organization does not exist or is out of the credential’s reach. `OPERATION_NOT_FOUND`: the «puesto» (`stationId`) does not exist or is another organization’s. One body for a row that is missing and for one of another organization. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |
### Example
```bash
curl -X GET 'https://api.fluenta.io/v1/external/organizations/{organizationId}/stations/{stationId}/days/{day}/report' \
-H 'Authorization: Bearer '
```