{"openapi":"3.0.0","paths":{"/v1/external/me":{"get":{"operationId":"getMe","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalMeDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"401":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"403":{"description":"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":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"The user the credential acts as, the credential itself, its «Permisos», its rate limit and the organizations it reaches.","tags":["Account"],"x-required-scope":"read"}},"/v1/external/me/usage":{"get":{"description":"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.","operationId":"getMyUsage","parameters":[{"name":"days","required":false,"in":"query","description":"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.","schema":{"minimum":1,"maximum":90,"default":30,"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalMyUsageDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"400":{"description":"`days` is not a whole number from 1 to 90: `INVALID_REQUEST`, with the field in `details`."},"401":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"403":{"description":"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":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"What this credential has asked of the API: totals, by route and by day. Only its own requests, in every organization.","tags":["Account"],"x-required-scope":"read"}},"/v1/external/organizations":{"get":{"operationId":"listOrganizations","parameters":[{"name":"search","required":false,"in":"query","description":"Part of the organization’s name, any case.","schema":{"maxLength":255,"type":"string"}},{"name":"page","required":false,"in":"query","description":"1-based page; 50 organizations per page.","schema":{"minimum":1,"maximum":1000000,"default":1,"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalOrganizationsPageDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"401":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"403":{"description":"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":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"The organizations the credential reaches, by name, 50 per page. A credential of an account with platform-wide access searches every organization.","tags":["Account"],"x-required-scope":"read"}},"/v1/external/organizations/{organizationId}":{"get":{"description":"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`).\n\n`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.","operationId":"getOrganization","parameters":[{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalOrganizationDetailDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"401":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"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":{"description":"The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both."},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"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.","tags":["Organization"],"x-required-scope":"read"}},"/v1/external/organizations/{organizationId}/access":{"get":{"operationId":"getOrganizationAccess","parameters":[{"name":"organizationId","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalOrganizationAccessDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"401":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"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":{"description":"The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both."},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"The user’s roles and the credential’s «Permisos» in one organization.","tags":["Organization"],"x-required-scope":"read"}},"/v1/external/organizations/{organizationId}/activity":{"get":{"operationId":"listActivity","parameters":[{"name":"page","required":false,"in":"query","description":"1-based page; 50 rows per page, newest first.","schema":{"minimum":1,"maximum":1000000,"default":1,"type":"integer"}},{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalActivityPageDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"401":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"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":{"description":"The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both."},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"«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.","tags":["Activity"],"x-required-scope":"read"}},"/v1/external/organizations/{organizationId}/analytics/activity":{"get":{"description":"`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.","operationId":"getStationActivity","parameters":[{"name":"day","required":false,"in":"query","description":"Plant day, YYYY-MM-DD on the plant’s clock. Omitted: the plant’s today, on its own clock.","schema":{"maxLength":10,"pattern":"^\\d{4}-\\d{2}-\\d{2}$","example":"2026-11-09","type":"string"}},{"name":"view","required":true,"in":"query","description":"`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.","schema":{"type":"string","enum":["events","hourly","operators","consistency"]}},{"name":"operationId","required":false,"in":"query","description":"The puesto, for `view=events`.","schema":{"minimum":1,"maximum":2147483647,"type":"number"}},{"name":"moduleId","required":false,"in":"query","description":"Restrict `hourly`, `operators` or `consistency` to one módulo.","schema":{"minimum":1,"maximum":2147483647,"type":"number"}},{"name":"operatorId","required":false,"in":"query","description":"Restrict `hourly` to one operario: their own marcas and share of each hour’s meta.","schema":{"minimum":1,"maximum":2147483647,"type":"number"}},{"name":"idleMinutes","required":false,"in":"query","description":"Gaps between marcas longer than this many minutes count as «pausas sin registro». Default 5.","schema":{"type":"number","enum":[5,10,15]}},{"name":"organizationId","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalStationActivityDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"400":{"description":"`INVALID_DAY`; `OPERATION_REQUIRED` for `view=events` without `operationId`."},"401":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"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":{"description":"The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both."},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"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.","tags":["Analytics"],"x-required-scope":"read"}},"/v1/external/organizations/{organizationId}/analytics/billing":{"get":{"description":"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.","operationId":"getBilling","parameters":[{"name":"from","required":true,"in":"query","description":"First plant day of the range, YYYY-MM-DD on the plant's clock, included. At most 92 days from `from` to `to`.","schema":{"maxLength":10,"pattern":"^\\d{4}-\\d{2}-\\d{2}$","example":"2026-10-01","type":"string"}},{"name":"to","required":true,"in":"query","description":"Last plant day of the range, YYYY-MM-DD on the plant’s clock, included; not before `from`.","schema":{"maxLength":10,"pattern":"^\\d{4}-\\d{2}-\\d{2}$","example":"2026-10-31","type":"string"}},{"name":"groupBy","required":false,"in":"query","description":"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.","schema":{"default":"day","type":"string","enum":["day","module","operator","operation","reference","category"]}},{"name":"page","required":false,"in":"query","description":"1-based page; 100 rows per page, by day then group.","schema":{"minimum":1,"maximum":1000000,"default":1,"type":"number"}},{"name":"moduleId","required":false,"in":"query","description":"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`.","schema":{"minimum":1,"maximum":2147483647,"type":"integer"}},{"name":"operatorId","required":false,"in":"query","description":"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`.","schema":{"minimum":1,"maximum":2147483647,"type":"integer"}},{"name":"operationId","required":false,"in":"query","description":"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`.","schema":{"minimum":1,"maximum":2147483647,"type":"integer"}},{"name":"organizationId","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalBillingPageDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"400":{"description":"`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":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"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":{"description":"`ORGANIZATION_NOT_FOUND`; a filter’s id of another organization: `MODULE_NOT_FOUND`, `OPERATOR_NOT_FOUND`, `OPERATION_NOT_FOUND`."},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"description":"`ANALYTICS_TIMEOUT`: ask a shorter range."}},"security":[{"apiToken":[]}],"summary":"«Facturación» over a range of plant days, per day, or per day and módulo, operario or puesto.","tags":["Analytics"],"x-rate-limit-heavy":true,"x-required-scope":"read"}},"/v1/external/organizations/{organizationId}/analytics/billing/modules":{"get":{"description":"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.\n\nHow 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.\n\nMoney 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.\n\nDays 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.\n\nPlan: 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.\n\nSee also: `getBilling` for «Facturación» over a range, by day, «módulo», «operario» or «puesto»; `getMonth` for the last 30 days in one call.","operationId":"getBillingByModule","parameters":[{"name":"day","required":false,"in":"query","description":"Plant day, YYYY-MM-DD on the plant’s clock. Omitted: the plant’s today, on its own clock.","schema":{"maxLength":10,"pattern":"^\\d{4}-\\d{2}-\\d{2}$","example":"2026-11-09","type":"string"}},{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"responses":{"200":{"description":"The day’s «Facturación», one row per «módulo» that billed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalBillingByModuleDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"400":{"description":"`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":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"`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":{"description":"`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":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"«Facturación por módulo» for one plant day: each módulo’s billed units and amount, in the plant’s currency.","tags":["Analytics"],"x-required-scope":"read"}},"/v1/external/organizations/{organizationId}/analytics/consistency":{"get":{"description":"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.\n\nHow 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.\n\nNaming: 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.\n\nDays 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.\n\nPlan: 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.","operationId":"getConsistency","parameters":[{"name":"day","required":false,"in":"query","description":"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`.","schema":{"maxLength":10,"pattern":"^\\d{4}-\\d{2}-\\d{2}$","example":"2026-11-09","type":"string"}},{"name":"moduleId","required":false,"in":"query","description":"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`.","schema":{"minimum":1,"maximum":2147483647,"type":"integer"}},{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"responses":{"200":{"description":"The «marcas» log against the hourly buckets, row by row, and the count of each status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalConsistencyReportDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"400":{"description":"`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":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"`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":{"description":"`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":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"The marcas log against the hourly buckets for one plant day: which puesto-hours agree, which differ, and by how many units.","tags":["Analytics"],"x-required-scope":"read"}},"/v1/external/organizations/{organizationId}/analytics/day-report":{"get":{"description":"`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.","operationId":"getDayReport","parameters":[{"name":"day","required":false,"in":"query","description":"Plant day, YYYY-MM-DD on the plant’s clock. Omitted: the plant’s today, on its own clock.","schema":{"maxLength":10,"pattern":"^\\d{4}-\\d{2}-\\d{2}$","example":"2026-11-09","type":"string"}},{"name":"organizationId","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalDayReportDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"400":{"description":"`INVALID_DAY`."},"401":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"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":{"description":"The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both."},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"Reportes › Eficiencia for one plant day: every módulo hour by hour, each puesto’s units, meta and efficiency.","tags":["Analytics"],"x-required-scope":"read"}},"/v1/external/organizations/{organizationId}/analytics/efficiency":{"get":{"description":"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.","operationId":"getEfficiency","parameters":[{"name":"from","required":true,"in":"query","description":"First plant day of the range, YYYY-MM-DD on the plant's clock, included. At most 92 days from `from` to `to`.","schema":{"maxLength":10,"pattern":"^\\d{4}-\\d{2}-\\d{2}$","example":"2026-10-01","type":"string"}},{"name":"to","required":true,"in":"query","description":"Last plant day of the range, YYYY-MM-DD on the plant’s clock, included; not before `from`.","schema":{"maxLength":10,"pattern":"^\\d{4}-\\d{2}-\\d{2}$","example":"2026-10-31","type":"string"}},{"name":"groupBy","required":false,"in":"query","description":"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.","schema":{"default":"day","type":"string","enum":["day","module","operator","operation","reference","category"]}},{"name":"page","required":false,"in":"query","description":"1-based page; 100 rows per page, by day then group.","schema":{"minimum":1,"maximum":1000000,"default":1,"type":"number"}},{"name":"moduleId","required":false,"in":"query","description":"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`.","schema":{"minimum":1,"maximum":2147483647,"type":"integer"}},{"name":"operatorId","required":false,"in":"query","description":"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`.","schema":{"minimum":1,"maximum":2147483647,"type":"integer"}},{"name":"operationId","required":false,"in":"query","description":"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`.","schema":{"minimum":1,"maximum":2147483647,"type":"integer"}},{"name":"organizationId","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalEfficiencyPageDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"400":{"description":"`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":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"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":{"description":"`ORGANIZATION_NOT_FOUND`; a filter’s id of another organization: `MODULE_NOT_FOUND`, `OPERATOR_NOT_FOUND`, `OPERATION_NOT_FOUND`."},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"description":"`ANALYTICS_TIMEOUT`: ask a shorter range."}},"security":[{"apiToken":[]}],"summary":"«Eficiencia» over a range of plant days: units made against the meta, per day, or per day and módulo, operario or puesto.","tags":["Analytics"],"x-rate-limit-heavy":true,"x-required-scope":"read"}},"/v1/external/organizations/{organizationId}/analytics/efficiency/modules":{"get":{"description":"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.\n\nThe 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.\n\n`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.\n\nDays 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.\n\nPlan: 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.\n\nSee also: `getDayReport` for the same day with each «módulo» hour by hour; `getBillingByModule` for the day’s «Facturación» by «módulo».","operationId":"getEfficiencyByModule","parameters":[{"name":"day","required":false,"in":"query","description":"Plant day, YYYY-MM-DD on the plant’s clock. Omitted: the plant’s today, on its own clock.","schema":{"maxLength":10,"pattern":"^\\d{4}-\\d{2}-\\d{2}$","example":"2026-11-09","type":"string"}},{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"responses":{"200":{"description":"The day’s «Eficiencia», one row per «módulo».","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalEfficiencyByModuleDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"400":{"description":"`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":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"`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":{"description":"`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":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"Reportes › Eficiencia for one plant day, módulo by módulo: units, meta, efficiency and pausas.","tags":["Analytics"],"x-required-scope":"read"}},"/v1/external/organizations/{organizationId}/analytics/hourly":{"get":{"description":"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.\n\nHow 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.\n\nSource: «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.\n\n`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.\n\nMoney 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.\n\nDays 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.\n\nPlan: 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.\n\nSee also: `getOperatorsActivity` for the same day by «operario»; `getConsistency` to check the log against the buckets.","operationId":"getHourlyActivity","parameters":[{"name":"day","required":false,"in":"query","description":"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`.","schema":{"maxLength":10,"pattern":"^\\d{4}-\\d{2}-\\d{2}$","example":"2026-11-09","type":"string"}},{"name":"moduleId","required":false,"in":"query","description":"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`.","schema":{"minimum":1,"maximum":2147483647,"type":"integer"}},{"name":"operatorId","required":false,"in":"query","description":"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`.","schema":{"minimum":1,"maximum":2147483647,"type":"integer"}},{"name":"idleMinutes","required":false,"in":"query","description":"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.","schema":{"type":"number","enum":[5,10,15]}},{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"responses":{"200":{"description":"The day hour by hour, and its totals.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalHourlyActivityDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"400":{"description":"`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":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"`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":{"description":"`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":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"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.","tags":["Analytics"],"x-required-scope":"read"}},"/v1/external/organizations/{organizationId}/analytics/leaderboard":{"get":{"description":"`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.","operationId":"getLeaderboard","parameters":[{"name":"day","required":false,"in":"query","description":"Plant day, YYYY-MM-DD on the plant’s clock. Omitted: the plant’s today, on its own clock.","schema":{"maxLength":10,"pattern":"^\\d{4}-\\d{2}-\\d{2}$","example":"2026-11-09","type":"string"}},{"name":"organizationId","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalLeaderboardDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"400":{"description":"`INVALID_DAY`."},"401":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"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":{"description":"The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both."},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"The «Clasificación» of one plant day: every operario’s units, meta, efficiency and «Facturado», best first.","tags":["Analytics"],"x-required-scope":"read"}},"/v1/external/organizations/{organizationId}/analytics/month":{"get":{"description":"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.\n\nIt 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`.\n\n`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.\n\nMoney 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`.\n\nDays 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.\n\nPlan: 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.\n\nHeavy 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`.","operationId":"getMonth","parameters":[{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"responses":{"200":{"description":"The plant’s last 30 plant days, one row per day worked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalMonthDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"401":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"`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":{"description":"`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":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"The plant’s last 30 days, up to its today: units, meta, efficiency and «Facturación» per day. The 30-day chart of Reportes.","tags":["Analytics"],"x-rate-limit-heavy":true,"x-required-scope":"read"}},"/v1/external/organizations/{organizationId}/analytics/operators":{"get":{"description":"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.\n\nHow 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`.\n\nSource: «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.\n\n`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.\n\nMoney 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.\n\nDays 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.\n\nPlan: 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.\n\nSee also: `getHourlyActivity` with `operatorId` for one of these people hour by hour.","operationId":"getOperatorsActivity","parameters":[{"name":"day","required":false,"in":"query","description":"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`.","schema":{"maxLength":10,"pattern":"^\\d{4}-\\d{2}-\\d{2}$","example":"2026-11-09","type":"string"}},{"name":"moduleId","required":false,"in":"query","description":"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`.","schema":{"minimum":1,"maximum":2147483647,"type":"integer"}},{"name":"idleMinutes","required":false,"in":"query","description":"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.","schema":{"type":"number","enum":[5,10,15]}},{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"responses":{"200":{"description":"The day by «operario», and its totals.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalOperatorsActivityDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"400":{"description":"`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":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"`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":{"description":"`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":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"Analítica by operario for one plant day: each one’s units, pace, active hours, channels, efficiency and billing, with the totals.","tags":["Analytics"],"x-required-scope":"read"}},"/v1/external/organizations/{organizationId}/analytics/operators/{operatorId}":{"get":{"operationId":"getOperatorReport","parameters":[{"name":"operatorId","required":true,"in":"path","schema":{"type":"number"}},{"name":"from","required":true,"in":"query","description":"First plant day, YYYY-MM-DD, included. At most 92 days from `from` to `to`.","schema":{"maxLength":10,"pattern":"^\\d{4}-\\d{2}-\\d{2}$","example":"2026-11-01","type":"string"}},{"name":"to","required":true,"in":"query","description":"Last plant day, YYYY-MM-DD, included.","schema":{"maxLength":10,"pattern":"^\\d{4}-\\d{2}-\\d{2}$","example":"2026-11-07","type":"string"}},{"name":"organizationId","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalOperatorReportDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"400":{"description":"`INVALID_DAY`, `RANGE_INVALID`, `RANGE_TOO_LARGE`."},"401":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"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":{"description":"The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both."},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"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.","tags":["Analytics"],"x-rate-limit-heavy":true,"x-required-scope":"read"}},"/v1/external/organizations/{organizationId}/analytics/pauses":{"get":{"description":"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).\n\nWhat 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.\n\nDurations 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`.\n\nRange: `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’).\n\nDays 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.\n\nNaming: 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.\n\nPlan: 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.\n\nHeavy 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`.","operationId":"getPauses","parameters":[{"name":"from","required":true,"in":"query","description":"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`.","schema":{"maxLength":10,"pattern":"^\\d{4}-\\d{2}-\\d{2}$","example":"2026-10-01","type":"string"}},{"name":"to","required":true,"in":"query","description":"Last plant day of the range, `YYYY-MM-DD` on the plant’s own clock, included. Before `from`: 400 `RANGE_INVALID`.","schema":{"maxLength":10,"pattern":"^\\d{4}-\\d{2}-\\d{2}$","example":"2026-10-31","type":"string"}},{"name":"moduleId","required":false,"in":"query","description":"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`.","schema":{"minimum":1,"maximum":2147483647,"type":"integer"}},{"name":"operatorId","required":false,"in":"query","description":"Only the «pausas» this «operario» took (the `id` of one from `listOperators`). Of another organization or unknown: 404 `OPERATOR_NOT_FOUND`.","schema":{"minimum":1,"maximum":2147483647,"type":"integer"}},{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"responses":{"200":{"description":"The «pausas» of the range, newest first, and the figures over all of them.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalPausesReportDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"400":{"description":"`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":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"`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":{"description":"`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":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"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.","tags":["Analytics"],"x-rate-limit-heavy":true,"x-required-scope":"read"}},"/v1/external/organizations/{organizationId}/analytics/production":{"get":{"description":"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.","operationId":"getProduction","parameters":[{"name":"from","required":true,"in":"query","description":"First plant day of the range, YYYY-MM-DD on the plant's clock, included. At most 92 days from `from` to `to`.","schema":{"maxLength":10,"pattern":"^\\d{4}-\\d{2}-\\d{2}$","example":"2026-10-01","type":"string"}},{"name":"to","required":true,"in":"query","description":"Last plant day of the range, YYYY-MM-DD on the plant’s clock, included; not before `from`.","schema":{"maxLength":10,"pattern":"^\\d{4}-\\d{2}-\\d{2}$","example":"2026-10-31","type":"string"}},{"name":"groupBy","required":false,"in":"query","description":"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.","schema":{"default":"day","type":"string","enum":["day","module","operator","operation","reference","category"]}},{"name":"page","required":false,"in":"query","description":"1-based page; 100 rows per page, by day then group.","schema":{"minimum":1,"maximum":1000000,"default":1,"type":"number"}},{"name":"moduleId","required":false,"in":"query","description":"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`.","schema":{"minimum":1,"maximum":2147483647,"type":"integer"}},{"name":"operatorId","required":false,"in":"query","description":"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`.","schema":{"minimum":1,"maximum":2147483647,"type":"integer"}},{"name":"operationId","required":false,"in":"query","description":"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`.","schema":{"minimum":1,"maximum":2147483647,"type":"integer"}},{"name":"organizationId","required":true,"in":"path","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalProductionPageDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"400":{"description":"`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":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"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":{"description":"`ORGANIZATION_NOT_FOUND`; a filter’s id of another organization: `MODULE_NOT_FOUND`, `OPERATOR_NOT_FOUND`, `OPERATION_NOT_FOUND`."},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"description":"`ANALYTICS_TIMEOUT`: ask a shorter range."}},"security":[{"apiToken":[]}],"summary":"«Producido» over a range of plant days, per day, or per day and módulo, referencia or categoría.","tags":["Analytics"],"x-rate-limit-heavy":true,"x-required-scope":"read"}},"/v1/external/organizations/{organizationId}/board":{"get":{"description":"`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.","operationId":"getLiveBoard","parameters":[{"name":"day","required":false,"in":"query","description":"The plant day, `YYYY-MM-DD` in the organization’s clock. Omitted: the plant’s today.","schema":{"format":"date","example":"2026-11-10","type":"string"}},{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalLiveBoardDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"401":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"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":{"description":"The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both."},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"The live board of the whole floor for a day: each módulo’s «eficiencia», units and goal so far, and the plant’s.","tags":["Board"],"x-required-scope":"read"}},"/v1/external/organizations/{organizationId}/catalogue":{"get":{"description":"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.\n\nEach `id` is the `templateId` of `updateCatalogueEntry`, `deleteCatalogueEntry` and `addReferencesFromCatalogue`.","operationId":"listCatalogue","parameters":[{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ExternalCatalogueEntryDto"}}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"401":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"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":{"description":"The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both."},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"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.","tags":["Catalogue"],"x-required-scope":"read"},"post":{"operationId":"createCatalogueEntry","parameters":[{"name":"Idempotency-Key","in":"header","description":"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`.","required":false,"schema":{"type":"string","minLength":1,"maxLength":255}},{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalCreateCatalogueEntryDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalCatalogueEntryDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"400":{"description":"`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":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"`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":{"description":"The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both."},"409":{"description":"`IDEMPOTENCY_IN_PROGRESS` (with `Retry-After`): the same `Idempotency-Key` is still being answered; retry it unchanged. Or the dashboard’s own conflict."},"422":{"description":"`IDEMPOTENCY_KEY_REUSED`: the `Idempotency-Key` answered another request. Nothing changed."},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"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`).","tags":["Catalogue"],"x-required-scope":"program"}},"/v1/external/organizations/{organizationId}/catalogue/{templateId}":{"patch":{"operationId":"updateCatalogueEntry","parameters":[{"name":"templateId","required":true,"in":"path","schema":{"type":"integer"}},{"name":"Idempotency-Key","in":"header","description":"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`.","required":false,"schema":{"type":"string","minLength":1,"maxLength":255}},{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalUpdateCatalogueEntryDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalCatalogueEntryDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"400":{"description":"`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":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"`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":{"description":"The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both."},"409":{"description":"`IDEMPOTENCY_IN_PROGRESS` (with `Retry-After`): the same `Idempotency-Key` is still being answered; retry it unchanged. Or the dashboard’s own conflict."},"422":{"description":"`IDEMPOTENCY_KEY_REUSED`: the `Idempotency-Key` answered another request. Nothing changed."},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"Edit a template of the «Catálogo de referencias»: only the fields sent change; null clears an optional one. No OC changes.","tags":["Catalogue"],"x-required-scope":"program"},"delete":{"operationId":"deleteCatalogueEntry","parameters":[{"name":"templateId","required":true,"in":"path","schema":{"type":"integer"}},{"name":"Idempotency-Key","in":"header","description":"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`.","required":false,"schema":{"type":"string","minLength":1,"maxLength":255}},{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalConfirmedDeleteDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalCatalogueEntryDeletedDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"400":{"description":"`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":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"`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":{"description":"The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both."},"409":{"description":"`IDEMPOTENCY_IN_PROGRESS` (with `Retry-After`): the same `Idempotency-Key` is still being answered; retry it unchanged. Or the dashboard’s own conflict."},"422":{"description":"`IDEMPOTENCY_KEY_REUSED`: the `Idempotency-Key` answered another request. Nothing changed."},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"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.","tags":["Catalogue"],"x-required-scope":"program"}},"/v1/external/organizations/{organizationId}/catalogue/{templateId}/delete-preview":{"post":{"operationId":"previewDeleteCatalogueEntry","parameters":[{"name":"templateId","required":true,"in":"path","schema":{"type":"integer"}},{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalCatalogueDeletePreviewDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"401":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"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":{"description":"The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both."},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"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.","tags":["Catalogue"],"x-required-scope":"read"}},"/v1/external/organizations/{organizationId}/categories":{"get":{"description":"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.\n\n`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.\n\nFeed 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.\n\nNo id beyond `organizationId`; no time field; the read creates nothing.\n\nNot paged: the whole answer comes in one response.\n\nPlan: 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.\n\nRate 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.","operationId":"listCategories","parameters":[{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalCategoriesDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"401":{"description":"`INVALID_API_TOKEN`: the credential is missing, malformed, unknown, expired or revoked. One body for all of them."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"`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":{"description":"`ORGANIZATION_NOT_FOUND`: the organization does not exist or is out of the credential’s reach. One body for both."},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"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».","tags":["Organization"],"x-required-scope":"read"}},"/v1/external/organizations/{organizationId}/floor":{"get":{"description":"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`.\n\nEach «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`).\n\n`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.\n\nAn organization with no «módulo» answers `modules: []`; a «módulo» with no «puesto» answers `stations: []`.\n\nNot paged: the whole answer comes in one response.\n\nPlan: 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.\n\nRate 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.","operationId":"getFloor","parameters":[{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalFloorDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"401":{"description":"`INVALID_API_TOKEN`: the credential is missing, malformed, unknown, expired or revoked. One body for all of them."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"`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":{"description":"`ORGANIZATION_NOT_FOUND`: the organization does not exist or is out of the credential’s reach. One body for both."},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"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.","tags":["Plant"],"x-rate-limit-heavy":true,"x-required-scope":"read"}},"/v1/external/organizations/{organizationId}/modules":{"get":{"description":"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.\n\nEach `id` is the `moduleId` of `getModule`, `listStations`, `listModuleRestTimes`, `getProgram` and `getHoursBoard`, and of every `moduleId` filter.","operationId":"listModules","parameters":[{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ExternalModuleDto"}}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"401":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"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":{"description":"The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both."},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"The organization’s módulos (production lines), in the floor’s order, with how many puestos each has.","tags":["Plant"],"x-required-scope":"read"}},"/v1/external/organizations/{organizationId}/modules/{moduleId}":{"get":{"description":"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).\n\n`moduleId` comes from `listModules` or `getFloor`. Its «puestos» are `listStations`, its «descansos» `listModuleRestTimes`, its queue of OCs `getProgram`.\n\nNo time field.\n\nNot paged: the whole answer comes in one response.\n\nPlan: 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.\n\nRate 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.","operationId":"getModule","parameters":[{"name":"moduleId","required":true,"in":"path","description":"The «módulo»: an `id` of `listModules` or `getFloor`, or the `moduleId` of a «puesto».","schema":{"type":"integer"}},{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalModuleDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"401":{"description":"`INVALID_API_TOKEN`: the credential is missing, malformed, unknown, expired or revoked. One body for all of them."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"`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":{"description":"`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":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"One «módulo» (production line): its name, its place on the floor, how many «puestos» it has and its colour.","tags":["Plant"],"x-required-scope":"read"}},"/v1/external/organizations/{organizationId}/modules/{moduleId}/analytics/efficiency":{"get":{"description":"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.\n\n`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.\n\nRange: `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.\n\nDays 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.\n\nPlan: 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.\n\nHeavy 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`.\n\nSee also: `getModuleDayHistory` for one of those days hour by hour; `getEfficiency` with `groupBy=module` for every «módulo» at once.","operationId":"getModuleEfficiency","parameters":[{"name":"moduleId","required":true,"in":"path","description":"The «módulo» (a production line): the `id` of one from `listModules`. Unknown or of another organization: 404 `MODULE_NOT_FOUND`.","schema":{"type":"integer"}},{"name":"from","required":true,"in":"query","description":"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`.","schema":{"maxLength":10,"pattern":"^\\d{4}-\\d{2}-\\d{2}$","example":"2026-10-01","type":"string"}},{"name":"to","required":true,"in":"query","description":"Last plant day of the range, `YYYY-MM-DD` on the plant’s own clock, included. Before `from`: 400 `RANGE_INVALID`.","schema":{"maxLength":10,"pattern":"^\\d{4}-\\d{2}-\\d{2}$","example":"2026-10-31","type":"string"}},{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"responses":{"200":{"description":"The «módulo»’s «Eficiencia», one row per plant day worked in the range.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalModuleEfficiencyDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"400":{"description":"`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":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"`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":{"description":"`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":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"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».","tags":["Analytics"],"x-rate-limit-heavy":true,"x-required-scope":"read"}},"/v1/external/organizations/{organizationId}/modules/{moduleId}/days/{day}/board":{"get":{"description":"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.\n\n`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).\n\nPlan: the organization needs «Reportes por módulo» (`getPlan.features.moduleReports`); without it the answer is 403 `PLAN_LIMIT`.","operationId":"getHoursBoard","parameters":[{"name":"moduleId","required":true,"in":"path","description":"The «módulo» (a production line): the `id` of one from `listModules`. Unknown or of another organization: 404 `MODULE_NOT_FOUND`.","schema":{"type":"integer"}},{"name":"day","required":true,"in":"path","description":"The plant day, `YYYY-MM-DD` in the organization’s clock.","schema":{"format":"date","example":"2026-11-10","type":"string"}},{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalHoursBoardDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"401":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"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":{"description":"The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both."},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"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.","tags":["Board"],"x-required-scope":"read"}},"/v1/external/organizations/{organizationId}/modules/{moduleId}/days/{day}/history":{"get":{"description":"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.\n\nHow 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`.\n\n`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.\n\nMoney 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.\n\nDays 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.\n\nNaming: 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.\n\nPlan: 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.\n\nSee 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».","operationId":"getModuleDayHistory","parameters":[{"name":"moduleId","required":true,"in":"path","description":"The «módulo» (a production line): the `id` of one from `listModules`. Unknown or of another organization: 404 `MODULE_NOT_FOUND`.","schema":{"type":"integer"}},{"name":"day","required":true,"in":"path","description":"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`.","schema":{"format":"date","example":"2026-11-10","type":"string"}},{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"responses":{"200":{"description":"The «módulo»’s day, hour by hour and «puesto» by «puesto».","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalModuleDayHistoryDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"400":{"description":"`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":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"`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":{"description":"`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":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"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».","tags":["Analytics"],"x-required-scope":"read"}},"/v1/external/organizations/{organizationId}/modules/{moduleId}/days/{day}/references/{referenceId}/goal":{"put":{"operationId":"setDayGoals","parameters":[{"name":"moduleId","required":true,"in":"path","schema":{"type":"integer"}},{"name":"day","required":true,"in":"path","description":"A plant day, `YYYY-MM-DD`, on the organization’s clock: today or a past day, never a future one.","schema":{"format":"date","type":"string"}},{"name":"referenceId","required":true,"in":"path","schema":{"type":"integer"}},{"name":"Idempotency-Key","in":"header","description":"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`.","required":false,"schema":{"type":"string","minLength":1,"maxLength":255}},{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalSetDayGoalsDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalHoursBoardDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"400":{"description":"`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":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"`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":{"description":"The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both."},"409":{"description":"`IDEMPOTENCY_IN_PROGRESS` (with `Retry-After`): the same `Idempotency-Key` is still being answered; retry it unchanged. Or the dashboard’s own conflict."},"422":{"description":"`IDEMPOTENCY_KEY_REUSED`: the `Idempotency-Key` answered another request. Nothing changed."},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"«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.","tags":["Program"],"x-required-scope":"program"},"delete":{"operationId":"clearDayGoals","parameters":[{"name":"moduleId","required":true,"in":"path","schema":{"type":"integer"}},{"name":"day","required":true,"in":"path","description":"A plant day, `YYYY-MM-DD`, on the organization’s clock.","schema":{"format":"date","type":"string"}},{"name":"referenceId","required":true,"in":"path","schema":{"type":"integer"}},{"name":"Idempotency-Key","in":"header","description":"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`.","required":false,"schema":{"type":"string","minLength":1,"maxLength":255}},{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalHoursBoardDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"400":{"description":"`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":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"`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":{"description":"The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both."},"409":{"description":"`IDEMPOTENCY_IN_PROGRESS` (with `Retry-After`): the same `Idempotency-Key` is still being answered; retry it unchanged. Or the dashboard’s own conflict."},"422":{"description":"`IDEMPOTENCY_KEY_REUSED`: the `Idempotency-Key` answered another request. Nothing changed."},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"«↺ Meta base»: the day reads the referencia’s own «Meta por hora» again. Answers the módulo’s day as `getHoursBoard` does.","tags":["Program"],"x-required-scope":"program"}},"/v1/external/organizations/{organizationId}/modules/{moduleId}/program":{"get":{"description":"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.\n\n`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.","operationId":"getProgram","parameters":[{"name":"moduleId","required":true,"in":"path","description":"The «módulo» (a production line): the `id` of one from `listModules`. Unknown or of another organization: 404 `MODULE_NOT_FOUND`.","schema":{"type":"integer"}},{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ExternalReferenceDto"}}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"401":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"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":{"description":"The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both."},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"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.","tags":["Program"],"x-required-scope":"read"}},"/v1/external/organizations/{organizationId}/modules/{moduleId}/program/order":{"patch":{"operationId":"reorderProgram","parameters":[{"name":"moduleId","required":true,"in":"path","schema":{"type":"integer"}},{"name":"Idempotency-Key","in":"header","description":"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`.","required":false,"schema":{"type":"string","minLength":1,"maxLength":255}},{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalReorderProgramDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ExternalReferenceDto"}}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"400":{"description":"`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":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"`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":{"description":"The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both."},"409":{"description":"`IDEMPOTENCY_IN_PROGRESS` (with `Retry-After`): the same `Idempotency-Key` is still being answered; retry it unchanged. Or the dashboard’s own conflict."},"422":{"description":"`IDEMPOTENCY_KEY_REUSED`: the `Idempotency-Key` answered another request. Nothing changed."},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"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.","tags":["Program"],"x-required-scope":"program"}},"/v1/external/organizations/{organizationId}/modules/{moduleId}/rest-times":{"get":{"description":"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.\n\nEach 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: []`.\n\n`moduleId` comes from `listModules` or `getFloor`. `getOrganization` answers the same rows for every «módulo» at once.\n\nA «descanso» is the whole «módulo»’s; a «pausa» is one «operario»’s stop at one «puesto» (`pausedAt` on a «puesto», `getPauses`).\n\nNot paged: the whole answer comes in one response.\n\nPlan: 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.\n\nRate 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.","operationId":"listModuleRestTimes","parameters":[{"name":"moduleId","required":true,"in":"path","description":"The «módulo»: an `id` of `listModules` or `getFloor`, or the `moduleId` of a «puesto».","schema":{"type":"integer"}},{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalModuleRestTimesDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"401":{"description":"`INVALID_API_TOKEN`: the credential is missing, malformed, unknown, expired or revoked. One body for all of them."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"`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":{"description":"`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":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"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.","tags":["Plant"],"x-required-scope":"read"}},"/v1/external/organizations/{organizationId}/modules/{moduleId}/stations":{"get":{"description":"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.\n\n`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`.","operationId":"listStations","parameters":[{"name":"moduleId","required":true,"in":"path","description":"The «módulo» (a production line): the `id` of one from `listModules`. Unknown or of another organization: 404 `MODULE_NOT_FOUND`.","schema":{"type":"integer"}},{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ExternalStationDto"}}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"401":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"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":{"description":"The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both."},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"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.","tags":["Plant"],"x-required-scope":"read"}},"/v1/external/organizations/{organizationId}/operators":{"get":{"description":"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.\n\nEach `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.","operationId":"listOperators","parameters":[{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ExternalOperatorDto"}}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"401":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"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":{"description":"The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both."},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"The organization’s operarios by name, «Activo» or «Inactivo», and the puesto each is seated at.","tags":["Plant"],"x-required-scope":"read"}},"/v1/external/organizations/{organizationId}/operators/presence":{"get":{"description":"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).\n\nAn «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`.\n\n`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».\n\nNot paged: the whole answer comes in one response.\n\nPlan: 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.\n\nRate 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.","operationId":"getOperatorPresence","parameters":[{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalPresenceDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"401":{"description":"`INVALID_API_TOKEN`: the credential is missing, malformed, unknown, expired or revoked. One body for all of them."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"`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":{"description":"`ORGANIZATION_NOT_FOUND`: the organization does not exist or is out of the credential’s reach. One body for both."},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"Which «operarios» have a tablet connected right now, and whether the app is open on it. An «operario» missing from the answer is offline.","tags":["Plant"],"x-required-scope":"read"}},"/v1/external/organizations/{organizationId}/operators/{operatorId}":{"get":{"description":"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»).\n\n`operatorId` comes from `listOperators`, from the `operator.id` of a «puesto» or from `getOperatorPresence`. `station.id` feeds `getStation` for the live counts.\n\nThe code an «operario» signs in to the app with is a credential and is never answered. No time field.\n\nNot paged: the whole answer comes in one response.\n\nPlan: 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.\n\nRate 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.","operationId":"getOperator","parameters":[{"name":"operatorId","required":true,"in":"path","description":"The «operario»: an `id` of `listOperators`, the `operator.id` of a «puesto», or an `operatorId` of `getOperatorPresence`.","schema":{"type":"integer"}},{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalOperatorDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"401":{"description":"`INVALID_API_TOKEN`: the credential is missing, malformed, unknown, expired or revoked. One body for all of them."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"`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":{"description":"`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":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"One «operario» by id: name, «Activo» or «Inactivo», and the «puesto» the «operario» is seated at.","tags":["Plant"],"x-required-scope":"read"}},"/v1/external/organizations/{organizationId}/operators/{operatorId}/analytics/efficiency":{"get":{"description":"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`.\n\nThis 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.\n\n`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.\n\nRange: `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.\n\nDays 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.\n\nPlan: 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.\n\nHeavy 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`.\n\nSee also: `getOperatorReport` for the same days plus one day hour by hour; `getLeaderboard` for every «operario» on one day.","operationId":"getOperatorEfficiency","parameters":[{"name":"operatorId","required":true,"in":"path","description":"The «operario»: the `id` of one from `listOperators`. Unknown or of another organization: 404 `OPERATOR_NOT_FOUND`.","schema":{"type":"integer"}},{"name":"from","required":true,"in":"query","description":"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`.","schema":{"maxLength":10,"pattern":"^\\d{4}-\\d{2}-\\d{2}$","example":"2026-10-01","type":"string"}},{"name":"to","required":true,"in":"query","description":"Last plant day of the range, `YYYY-MM-DD` on the plant’s own clock, included. Before `from`: 400 `RANGE_INVALID`.","schema":{"maxLength":10,"pattern":"^\\d{4}-\\d{2}-\\d{2}$","example":"2026-10-31","type":"string"}},{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"responses":{"200":{"description":"The «operario»’s «Eficiencia», one row per plant day worked in the range.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalOperatorEfficiencyDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"400":{"description":"`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":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"`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":{"description":"`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":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"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.","tags":["Analytics"],"x-rate-limit-heavy":true,"x-required-scope":"read"}},"/v1/external/organizations/{organizationId}/plan":{"get":{"description":"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.\n\n`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.\n\nHow the plan is paid (trial dates, seats, the «licencia») is never answered. No id beyond `organizationId`; no time field.\n\nNot paged: the whole answer comes in one response.\n\nPlan: 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.\n\nRate 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.","operationId":"getPlan","parameters":[{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalPlanDetailDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"401":{"description":"`INVALID_API_TOKEN`: the credential is missing, malformed, unknown, expired or revoked. One body for all of them."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"`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":{"description":"`ORGANIZATION_NOT_FOUND`: the organization does not exist or is out of the credential’s reach. One body for both."},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"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.","tags":["Organization"],"x-required-scope":"read"}},"/v1/external/organizations/{organizationId}/references":{"get":{"description":"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.\n\nPaged: 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`.","operationId":"searchReferences","parameters":[{"name":"search","required":false,"in":"query","description":"Part of the «Corte (OC)», the «Referencia» name or the «Categoría» (garment), any case. Omitted: every referencia matches.","schema":{"maxLength":255,"type":"string"}},{"name":"moduleId","required":false,"in":"query","description":"Only the referencias of this módulo (an id from `listModules`).","schema":{"minimum":1,"maximum":2147483647,"type":"integer"}},{"name":"status","required":false,"in":"query","description":"`in_program`: in a módulo’s «Programa», not finished. `on_hold`: in the «banco de espera». `finished`: closed. Omitted: all three.","schema":{"type":"string","enum":["in_program","on_hold","finished"]}},{"name":"page","required":false,"in":"query","description":"1-based page; 50 referencias per page, newest first.","schema":{"minimum":1,"maximum":1000000,"default":1,"type":"integer"}},{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalReferencePageDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"401":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"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":{"description":"The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both."},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"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.","tags":["Program"],"x-required-scope":"read"},"post":{"operationId":"createReference","parameters":[{"name":"Idempotency-Key","in":"header","description":"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`.","required":false,"schema":{"type":"string","minLength":1,"maxLength":255}},{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalCreateReferenceDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalReferenceDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"400":{"description":"`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":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"`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":{"description":"The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both."},"409":{"description":"`IDEMPOTENCY_IN_PROGRESS` (with `Retry-After`): the same `Idempotency-Key` is still being answered; retry it unchanged. Or the dashboard’s own conflict."},"422":{"description":"`IDEMPOTENCY_KEY_REUSED`: the `Idempotency-Key` answered another request. Nothing changed."},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"Create an OC (a referencia, a cut of a garment) at the end of a módulo’s «Programa», as «Nueva referencia» does.","tags":["Program"],"x-required-scope":"program"}},"/v1/external/organizations/{organizationId}/references/from-catalogue":{"post":{"operationId":"addReferencesFromCatalogue","parameters":[{"name":"Idempotency-Key","in":"header","description":"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`.","required":false,"schema":{"type":"string","minLength":1,"maxLength":255}},{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalAddFromCatalogueDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalReferencesAddedDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"400":{"description":"`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":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"`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":{"description":"The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both."},"409":{"description":"`IDEMPOTENCY_IN_PROGRESS` (with `Retry-After`): the same `Idempotency-Key` is still being answered; retry it unchanged. Or the dashboard’s own conflict."},"422":{"description":"`IDEMPOTENCY_KEY_REUSED`: the `Idempotency-Key` answered another request. Nothing changed."},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"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.","tags":["Program"],"x-required-scope":"program"}},"/v1/external/organizations/{organizationId}/references/on-hold":{"get":{"description":"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.\n\nEach `id` is the `referenceId` of `getReference`; `reactivateReference` puts one back in a «Programa».","operationId":"listOnHold","parameters":[{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ExternalReferenceDto"}}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"401":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"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":{"description":"The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both."},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"The «banco de espera»: every paused referencia of the organization, newest pause first, with why, since when and from which módulo.","tags":["Program"],"x-required-scope":"read"}},"/v1/external/organizations/{organizationId}/references/{referenceId}":{"get":{"description":"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.\n\n`referenceId` comes from `getProgram`, `searchReferences` or `listOnHold`; one that is unknown or belongs to another organization answers 404 `REFERENCE_NOT_FOUND`.","operationId":"getReference","parameters":[{"name":"referenceId","required":true,"in":"path","description":"The «referencia»: an `id` of `getProgram`, `searchReferences` or `listOnHold`. Unknown or of another organization: 404 `REFERENCE_NOT_FOUND`.","schema":{"type":"integer"}},{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalReferenceDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"401":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"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":{"description":"The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both."},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"One referencia (cut, OC): its módulo, state, units, produced and «saldo», goals, price, dates, «Subprocesos» and «Red» marks.","tags":["Program"],"x-required-scope":"read"},"patch":{"operationId":"updateReference","parameters":[{"name":"referenceId","required":true,"in":"path","schema":{"type":"integer"}},{"name":"Idempotency-Key","in":"header","description":"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`.","required":false,"schema":{"type":"string","minLength":1,"maxLength":255}},{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalUpdateReferenceDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalReferenceDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"400":{"description":"`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":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"`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":{"description":"The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both."},"409":{"description":"`IDEMPOTENCY_IN_PROGRESS` (with `Retry-After`): the same `Idempotency-Key` is still being answered; retry it unchanged. Or the dashboard’s own conflict."},"422":{"description":"`IDEMPOTENCY_KEY_REUSED`: the `Idempotency-Key` answered another request. Nothing changed."},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"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.","tags":["Program"],"x-required-scope":"program"},"delete":{"operationId":"deleteReference","parameters":[{"name":"referenceId","required":true,"in":"path","schema":{"type":"integer"}},{"name":"Idempotency-Key","in":"header","description":"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`.","required":false,"schema":{"type":"string","minLength":1,"maxLength":255}},{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalConfirmedDeleteDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalReferenceDeletedDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"400":{"description":"`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":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"`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":{"description":"The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both."},"409":{"description":"`IDEMPOTENCY_IN_PROGRESS` (with `Retry-After`): the same `Idempotency-Key` is still being answered; retry it unchanged. Or the dashboard’s own conflict."},"422":{"description":"`IDEMPOTENCY_KEY_REUSED`: the `Idempotency-Key` answered another request. Nothing changed."},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"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.","tags":["Program"],"x-required-scope":"program"}},"/v1/external/organizations/{organizationId}/references/{referenceId}/arrive":{"post":{"operationId":"markReferenceArrived","parameters":[{"name":"referenceId","required":true,"in":"path","schema":{"type":"integer"}},{"name":"Idempotency-Key","in":"header","description":"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`.","required":false,"schema":{"type":"string","minLength":1,"maxLength":255}},{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalArriveReferenceDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalReferenceDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"400":{"description":"`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":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"`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":{"description":"The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both."},"409":{"description":"`IDEMPOTENCY_IN_PROGRESS` (with `Retry-After`): the same `Idempotency-Key` is still being answered; retry it unchanged. Or the dashboard’s own conflict."},"422":{"description":"`IDEMPOTENCY_KEY_REUSED`: the `Idempotency-Key` answered another request. Nothing changed."},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"«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.","tags":["Program"],"x-required-scope":"program"}},"/v1/external/organizations/{organizationId}/references/{referenceId}/delete-preview":{"post":{"operationId":"previewDeleteReference","parameters":[{"name":"referenceId","required":true,"in":"path","schema":{"type":"integer"}},{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalReferenceDeletePreviewDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"401":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"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":{"description":"The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both."},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"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.","tags":["Program"],"x-required-scope":"read"}},"/v1/external/organizations/{organizationId}/references/{referenceId}/hold":{"patch":{"operationId":"holdReference","parameters":[{"name":"referenceId","required":true,"in":"path","schema":{"type":"integer"}},{"name":"Idempotency-Key","in":"header","description":"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`.","required":false,"schema":{"type":"string","minLength":1,"maxLength":255}},{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalHoldReferenceDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalReferenceDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"400":{"description":"`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":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"`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":{"description":"The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both."},"409":{"description":"`IDEMPOTENCY_IN_PROGRESS` (with `Retry-After`): the same `Idempotency-Key` is still being answered; retry it unchanged. Or the dashboard’s own conflict."},"422":{"description":"`IDEMPOTENCY_KEY_REUSED`: the `Idempotency-Key` answered another request. Nothing changed."},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"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.","tags":["Program"],"x-required-scope":"program"}},"/v1/external/organizations/{organizationId}/references/{referenceId}/reactivate":{"patch":{"operationId":"reactivateReference","parameters":[{"name":"referenceId","required":true,"in":"path","schema":{"type":"integer"}},{"name":"Idempotency-Key","in":"header","description":"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`.","required":false,"schema":{"type":"string","minLength":1,"maxLength":255}},{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalReactivateReferenceDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalReferenceDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"400":{"description":"`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":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"`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":{"description":"The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both."},"409":{"description":"`IDEMPOTENCY_IN_PROGRESS` (with `Retry-After`): the same `Idempotency-Key` is still being answered; retry it unchanged. Or the dashboard’s own conflict."},"422":{"description":"`IDEMPOTENCY_KEY_REUSED`: the `Idempotency-Key` answered another request. Nothing changed."},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"Bring an OC back from the «banco de espera» (▶) into a módulo, at the end of its queue, with its counts.","tags":["Program"],"x-required-scope":"program"}},"/v1/external/organizations/{organizationId}/references/{referenceId}/sacrifice":{"patch":{"operationId":"recordSacrifice","parameters":[{"name":"referenceId","required":true,"in":"path","schema":{"type":"integer"}},{"name":"Idempotency-Key","in":"header","description":"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`.","required":false,"schema":{"type":"string","minLength":1,"maxLength":255}},{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalSacrificeReferenceDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalReferenceDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"400":{"description":"`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":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"`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":{"description":"The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both."},"409":{"description":"`IDEMPOTENCY_IN_PROGRESS` (with `Retry-After`): the same `Idempotency-Key` is still being answered; retry it unchanged. Or the dashboard’s own conflict."},"422":{"description":"`IDEMPOTENCY_KEY_REUSED`: the `Idempotency-Key` answered another request. Nothing changed."},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"Set the «descuento por sacrificio» of an OC: units written off its «saldo» without being production. Absolute; 0 removes it.","tags":["Program"],"x-required-scope":"program"}},"/v1/external/organizations/{organizationId}/references/{referenceId}/split":{"post":{"operationId":"splitReference","parameters":[{"name":"referenceId","required":true,"in":"path","schema":{"type":"integer"}},{"name":"Idempotency-Key","in":"header","description":"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`.","required":false,"schema":{"type":"string","minLength":1,"maxLength":255}},{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalSplitReferenceDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalSplitReferenceResultDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"400":{"description":"`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":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"`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":{"description":"The organization does not exist or is out of the credential’s reach: `ORGANIZATION_NOT_FOUND`. One body for both."},"409":{"description":"`IDEMPOTENCY_IN_PROGRESS` (with `Retry-After`): the same `Idempotency-Key` is still being answered; retry it unchanged. Or the dashboard’s own conflict."},"422":{"description":"`IDEMPOTENCY_KEY_REUSED`: the `Idempotency-Key` answered another request. Nothing changed."},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"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.","tags":["Program"],"x-required-scope":"program"}},"/v1/external/organizations/{organizationId}/settings":{"get":{"description":"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.\n\n`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.\n\nA 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.\n\nThe «descansos» are not here: they belong to each «módulo» (`listModuleRestTimes`, with a `moduleId` from `listModules`).\n\nNot paged: the whole answer comes in one response.\n\nPlan: 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.\n\nRate 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.","operationId":"getSettings","parameters":[{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalSettingsDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"401":{"description":"`INVALID_API_TOKEN`: the credential is missing, malformed, unknown, expired or revoked. One body for all of them."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"`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":{"description":"`ORGANIZATION_NOT_FOUND`: the organization does not exist or is out of the credential’s reach. One body for both."},"429":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"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`.","tags":["Organization"],"x-required-scope":"read"}},"/v1/external/organizations/{organizationId}/stations":{"get":{"description":"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`.\n\nPaged: 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.\n\nEach «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`).\n\nThe 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.\n\nA «puesto» is `station` in this API’s paths and answers, and `OPERATION` in its error code.\n\nPlan: 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.\n\nRate 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.","operationId":"listOrganizationStations","parameters":[{"name":"moduleId","required":false,"in":"query","description":"Only the «puestos» of this «módulo»: an `id` of `listModules` or `getFloor`. One of another organization answers 404 `MODULE_NOT_FOUND`.","schema":{"minimum":1,"maximum":2147483647,"type":"integer"}},{"name":"page","required":false,"in":"query","description":"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.","schema":{"minimum":1,"maximum":1000000,"default":1,"type":"integer"}},{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalStationPageDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"400":{"description":"`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":{"description":"`INVALID_API_TOKEN`: the credential is missing, malformed, unknown, expired or revoked. One body for all of them."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"`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":{"description":"`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":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"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.","tags":["Plant"],"x-required-scope":"read"}},"/v1/external/organizations/{organizationId}/stations/{stationId}":{"get":{"description":"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.\n\nEach «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`).\n\nThe 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.\n\n`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.\n\nNot paged: the whole answer comes in one response.\n\nPlan: 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.\n\nRate 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.","operationId":"getStation","parameters":[{"name":"stationId","required":true,"in":"path","description":"The «puesto»: an `id` of `listStations`, `listOrganizationStations` or `getFloor`, or the `station.id` of an «operario». Named `operationId` where a route filters by it.","schema":{"type":"integer"}},{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalStationDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"401":{"description":"`INVALID_API_TOKEN`: the credential is missing, malformed, unknown, expired or revoked. One body for all of them."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"`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":{"description":"`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":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"One «puesto» (station) as the live board reads it now: «operación», seated «operario», current «referencia», units this hour and today.","tags":["Plant"],"x-required-scope":"read"}},"/v1/external/organizations/{organizationId}/stations/{stationId}/analytics/events":{"get":{"description":"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».\n\nSource: «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.\n\nMoney 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.\n\nDays 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.\n\nNaming: 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`.\n\nPlan: 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.\n\nSee 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.","operationId":"getStationEvents","parameters":[{"name":"stationId","required":true,"in":"path","description":"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`.","schema":{"type":"integer"}},{"name":"day","required":false,"in":"query","description":"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`.","schema":{"maxLength":10,"pattern":"^\\d{4}-\\d{2}-\\d{2}$","example":"2026-11-09","type":"string"}},{"name":"idleMinutes","required":false,"in":"query","description":"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.","schema":{"type":"number","enum":[5,10,15]}},{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"responses":{"200":{"description":"The «puesto»’s «marcas» of the day, summarised.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalStationEventsDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"400":{"description":"`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":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"`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":{"description":"`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":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"One puesto’s marcas of a day, summarised from the marcas log: units, billing, online and offline marcas, pace and «pausas sin registro».","tags":["Analytics"],"x-required-scope":"read"}},"/v1/external/organizations/{organizationId}/stations/{stationId}/days/{day}/report":{"get":{"description":"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.\n\nHow 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.\n\n`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.\n\nMoney 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.\n\nDays 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.\n\nNaming: 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`.\n\nPlan: 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.\n\nSee 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.","operationId":"getStationDayReport","parameters":[{"name":"stationId","required":true,"in":"path","description":"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`.","schema":{"type":"integer"}},{"name":"day","required":true,"in":"path","description":"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`.","schema":{"format":"date","example":"2026-11-10","type":"string"}},{"name":"organizationId","required":true,"in":"path","description":"The organization, from `getMe` or `listOrganizations`.","schema":{"type":"integer"}}],"responses":{"200":{"description":"The «puesto»’s day, hour by hour.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalStationDayReportDto"}}},"headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}}},"400":{"description":"`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":{"description":"Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`."},"402":{"description":"The organization is blocked: `ORG_BLOCKED`."},"403":{"description":"`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":{"description":"`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":{"$ref":"#/components/responses/RateLimited"}},"security":[{"apiToken":[]}],"summary":"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.","tags":["Analytics"],"x-required-scope":"read"}}},"info":{"title":"Fluenta External API","description":"The Fluenta API for your own programs and for AI apps: the plant, the «Programa», the boards and the reports of the organizations a user belongs to.\n\n**Credentials.** Two kinds, both sent as `Authorization: Bearer fluenta_live_…`:\n- An **API token** («token de API»), created by hand in Configuración › Tokens de API and shown once. It works for organizations on **Light and Pro**.\n- A **connection** («conexión»): an AI app (Claude, ChatGPT) a user allowed through OAuth, «Conectar con IA». It works for organizations on **Pro**.\n\nA dashboard session is not accepted here, and neither credential is accepted anywhere else. `getMe` says which one was presented (`credential`), what it may do and where its rate limit stands.\n\n**Who a call acts as.** The user who created the token or connected the app, with the role they have in the dashboard, read again on every request. Every organization route names its organization in the path (`organizationId`, from `getMe` or `listOrganizations`); there is no default one. An organization the user does not belong to, or that does not exist, is a 404 `ORGANIZATION_NOT_FOUND`, never a 403. A credential of an account with platform-wide access reaches every organization (`getMe` answers `reachesAllOrganizations: true`).\n\n**Plans.** The API gives each organization what its plan gives it in the dashboard. Each report and analytics route needs one plan feature, named in its description. On every Light, the routes that need a Pro feature answer 403 `PLAN_LIMIT` (the body’s `feature` names the plan feature that is missing), and so does every route for a connection, which needs Pro. The routes that need `reports` answer 403 `PLAN_LIMIT` on a Light of plan version 2 only: a Light of plan version 1 keeps `reports`, and they answer it as they answer Pro (`getPlan` says which, in `planVersion`). A Pro trial and an exempt organization answer as Pro. A blocked organization answers 402 `ORG_BLOCKED`. `getPlan` lists what an organization’s plan allows.\n\n**«Permisos»** are scopes: `read` («Leer», always granted) and `program` («Gestionar el Programa»). Each operation names the one it needs in `x-required-scope`; without it, 403 `INSUFFICIENT_SCOPE`. A «Permiso» never widens the user’s role.\n\n**Rate limit.** 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. Every answer carries `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset` (epoch seconds) for the budget closest to being spent. Over it, 429 `RATE_LIMITED` with `Retry-After` in seconds. A 403 `INSUFFICIENT_SCOPE` and a 404 `ORGANIZATION_NOT_FOUND` count against the last of those budgets, whatever the route.\n\n**Errors** are `{ statusCode, code, message }`, the message in Spanish for the user; branch on `code`. A query or a body that fails validation is a 400 `INVALID_REQUEST` with `details`: one `{ field, message }` per failure, `field` the parameter or body field as a path (`page`, `items.0.name`; null when the failure names none). No answer is cacheable (`Cache-Control: no-store`).","version":"1.0","contact":{}},"tags":[{"name":"Account","description":"The credential presented: who it acts as, its «Permisos», its rate limit, its own usage and the organizations it reaches. These routes name no organization."},{"name":"Organization","description":"One organization: its row, what the credential may do in it, its plan and what the plan allows, its shift («jornada») and its categorías."},{"name":"Plant","description":"The plant floor: módulos, puestos (stations) and operarios, as they are set up and as they are right now."},{"name":"Board","description":"The live board of the plant and the hours board of one módulo on one day."},{"name":"Program","description":"The «Programa»: each módulo’s ordered queue of referencias (OCs). The writes need the «Permiso» `program`."},{"name":"Catalogue","description":"The «catálogo de referencias»: the plant’s reusable templates. The writes need the «Permiso» `program`."},{"name":"Analytics","description":"Reports and figures over one day or a range: efficiency, production, billing, pauses and «Analítica». On a Light organization these answer 403 `PLAN_LIMIT`, as their dashboard screens do; a Light of plan version 1 keeps the ones that need `reports`."},{"name":"Activity","description":"What conexiones and API tokens changed in the organization, newest first."}],"servers":[{"url":"https://api.fluenta.io","description":"Fluenta API"}],"components":{"securitySchemes":{"apiToken":{"scheme":"bearer","bearerFormat":"fluenta_live_…","type":"http","description":"An API token created in Configuración › Tokens de API, or a connected app’s access token"}},"schemas":{"ExternalActivityDto":{"type":"object","properties":{"idx":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"action":{"enum":["reference.create","reference.addFromCatalogue","reference.update","programme.reorder","reference.hold","reference.reactivate","reference.split","reference.arrive","reference.sacrifice","reference.delete","dayGoals.set","dayGoals.clear","catalogue.create","catalogue.update","catalogue.delete"],"type":"string","description":"What the write was, e.g. `reference.create`."},"outcome":{"enum":["ok","refused","failed"],"type":"string","description":"`ok`: it changed what `summary` says. `refused`: Fluenta refused it (`errorCode`), nothing changed. `failed`: it broke, nothing changed."},"errorCode":{"type":"string","nullable":true,"description":"The refusal’s `code`; null when `ok`."},"summary":{"type":"string","description":"What changed, in Spanish, as «Actividad de conexiones» shows it (or what was tried, when not `ok`)."},"actorName":{"type":"string","description":"The usuario the conexión acted as."},"appName":{"type":"string","nullable":true,"description":"The connected app («Claude», «ChatGPT»…)."}},"required":["idx","createdAt","action","outcome","errorCode","summary","actorName","appName"]},"ExternalActivityHourDto":{"type":"object","properties":{"hour":{"type":"integer","minimum":0,"maximum":23,"description":"Clock hour of the plant day, 0–23 on the plant’s own clock: the hour that starts at it (9 is 09:00–09:59)."},"units":{"type":"number","description":"Units made, in pieces, as the hourly buckets the reports read hold them. Every «puesto» counts, a «puesto de salida» or not. Under `operatorId`, that person’s own «marcas»."},"billableUnits":{"type":"number","nullable":true,"description":"Units of the «puestos de salida» among them, in pieces: the ones the plant bills. Null, not 0, when no «puesto de salida» is in what was asked."},"billedAmount":{"type":"number","nullable":true,"description":"What the billable units are worth: each at the price per unit it was billed at, 2 decimals. An amount in the currency this answer’s `currency` names, in whole currency units with up to 2 decimals (pesos or euros, never cents). Null, not 0, when no «puesto de salida» is in what was asked."},"goal":{"type":"number","description":"The hour’s «meta», in units: the sum of the «meta» of each «puesto» that worked in it. Under `operatorId`, that person’s share of it, 2 decimals."},"efficiency":{"type":"number","nullable":true,"description":"«Eficiencia»: this row’s `units` × 100 ÷ its `goal`, 2 decimals. A percentage, not a 0–1 ratio: 100 is exactly the goal and it can pass 100. Null when the goal is 0."},"events":{"type":"number","description":"How many «marcas» the log holds for the hour."},"online":{"type":"number","description":"«Marcas» that arrived live, one by one, stamped with the app’s own clock."},"offline":{"type":"number","description":"«Marcas» that arrived in a batch: registered while the app was offline and sent when it came back."},"legacy":{"type":"number","description":"«Marcas» stamped with the server’s clock: from an app too old to send its own time, or with a client timestamp outside the accepted skew window (more than 5 minutes ahead of the server or more than 60 days behind it). They say nothing about connectivity, so `offlineShare` leaves them out."},"secondsPerUnitP50":{"type":"number","nullable":true,"description":"Median seconds between consecutive «marcas», by the time each was registered, with «descansos» taken off. Null with fewer than 5 timed gaps."},"secondsPerUnitP90":{"type":"number","nullable":true,"description":"90th percentile of the same seconds: 9 of 10 «marcas» came faster than this. Null with fewer than 5 timed gaps."},"idleGapsOverN":{"type":"number","description":"«Pausas sin registro»: how many gaps between consecutive «marcas» lasted longer than `idleMinutes` minutes, «descansos» taken off. A gap counts in the hour of its later «marca». Inferred from the log; not the «pausas» an «operario» takes with «Pausar»."}},"required":["hour","units","billableUnits","billedAmount","goal","efficiency","events","online","offline","legacy","secondsPerUnitP50","secondsPerUnitP90","idleGapsOverN"]},"ExternalActivityHourTotalsDto":{"type":"object","properties":{"units":{"type":"number","description":"Units made, in pieces, as the hourly buckets the reports read hold them. Every «puesto» counts, a «puesto de salida» or not. Under `operatorId`, that person’s own «marcas»."},"billableUnits":{"type":"number","nullable":true,"description":"Units of the «puestos de salida» among them, in pieces: the ones the plant bills. Null, not 0, when no «puesto de salida» is in what was asked."},"billedAmount":{"type":"number","nullable":true,"description":"What the day’s billable units are worth, 2 decimals. An amount in the currency this answer’s `currency` names, in whole currency units with up to 2 decimals (pesos or euros, never cents). Null, not 0, when no «puesto de salida» is in what was asked."},"goal":{"type":"number","description":"The day’s «meta», in units: the sum of each «puesto»’s «meta» for each hour it worked. Under `operatorId`, that person’s share of it, 2 decimals."},"efficiency":{"type":"number","nullable":true,"description":"«Eficiencia»: this row’s `units` × 100 ÷ its `goal`, 2 decimals. A percentage, not a 0–1 ratio: 100 is exactly the goal and it can pass 100. Null when the goal is 0."},"events":{"type":"number","description":"How many «marcas» the log holds for the day."},"online":{"type":"number","description":"«Marcas» that arrived live, one by one, stamped with the app’s own clock."},"offline":{"type":"number","description":"«Marcas» that arrived in a batch: registered while the app was offline and sent when it came back."},"legacy":{"type":"number","description":"«Marcas» stamped with the server’s clock: from an app too old to send its own time, or with a client timestamp outside the accepted skew window (more than 5 minutes ahead of the server or more than 60 days behind it). They say nothing about connectivity, so `offlineShare` leaves them out."},"secondsPerUnitP50":{"type":"number","nullable":true,"description":"Median seconds between consecutive «marcas», by the time each was registered, with «descansos» taken off. Null with fewer than 5 timed gaps. The day’s own median, not an average of the hours’."},"secondsPerUnitP90":{"type":"number","nullable":true,"description":"90th percentile of the same seconds: 9 of 10 «marcas» came faster than this. Null with fewer than 5 timed gaps. The day’s own percentile, not an average of the hours’."},"idleGapsOverN":{"type":"number","description":"«Pausas sin registro» of the day: the sum of the hours’ `idleGapsOverN`."},"offlineShare":{"type":"number","nullable":true,"description":"A percentage, 0–100: `offline` × 100 ÷ (`online` + `offline`). Null when both are 0."},"eventsWithoutBucket":{"type":"number","description":"«Marcas» registered while the «puesto» had no «referencia»: they are in the log and in its unit counts, and no hourly bucket (so no report) holds them."},"idleMinutesApplied":{"type":"number","description":"The `idleMinutes` threshold the gap counts of this answer were counted against, in minutes: the one asked, or the default 5."}},"required":["units","billableUnits","billedAmount","goal","efficiency","events","online","offline","legacy","secondsPerUnitP50","secondsPerUnitP90","idleGapsOverN","offlineShare","eventsWithoutBucket","idleMinutesApplied"]},"ExternalActivityHourlyDto":{"type":"object","properties":{"moduleId":{"type":"number","nullable":true,"description":"The «módulo» the answer was narrowed to: the `moduleId` asked. Null for the whole plant."},"operatorId":{"type":"number","nullable":true,"description":"The «operario» the answer was narrowed to: the `operatorId` asked. Null for every «operario»."},"hours":{"description":"One entry per clock hour of the plant day with activity, earliest first. An hour with none is no entry.","type":"array","items":{"$ref":"#/components/schemas/ExternalActivityHourDto"}},"totals":{"description":"The whole day, over what was asked.","allOf":[{"$ref":"#/components/schemas/ExternalActivityHourTotalsDto"}]}},"required":["moduleId","operatorId","hours","totals"]},"ExternalActivityOperatorDto":{"type":"object","properties":{"operatorId":{"type":"number","nullable":true,"description":"The «operario»: the `id` of one from `listOperators`. In practice never null: work with nobody seated is `totals.unattributedEventUnits`."},"operatorName":{"type":"string","nullable":true,"description":"That «operario»’s name. Null when it is unknown."},"units":{"type":"number","description":"Units the person made that day, in pieces: their own «marcas», as the «Clasificación» counts them."},"eventUnits":{"type":"number","description":"Units of every «marca» the log holds for the person, in pieces, the ones registered with no «referencia» included. Beside `units` for comparison; `efficiency` uses `units`."},"activeHours":{"type":"number","description":"How many different clock hours of the day the person had activity in."},"unitsPerHour":{"type":"number","nullable":true,"description":"Pace, in units per hour: `units` ÷ `activeHours`. Null with no active hour."},"secondsPerUnitP50":{"type":"number","nullable":true,"description":"Median seconds between consecutive «marcas», by the time each was registered, with «descansos» taken off. Null with fewer than 5 timed gaps."},"idleGaps":{"type":"number","description":"«Pausas sin registro»: how many gaps between the person’s consecutive «marcas» lasted longer than `idleMinutes` minutes, «descansos» taken off. Inferred from the log; not the «pausas» taken with «Pausar»."},"online":{"type":"number","description":"«Marcas» that arrived live, one by one, stamped with the app’s own clock."},"offline":{"type":"number","description":"«Marcas» that arrived in a batch: registered while the app was offline and sent when it came back."},"legacy":{"type":"number","description":"«Marcas» stamped with the server’s clock: from an app too old to send its own time, or with a client timestamp outside the accepted skew window (more than 5 minutes ahead of the server or more than 60 days behind it). They say nothing about connectivity, so `offlineShare` leaves them out."},"offlineShare":{"type":"number","nullable":true,"description":"A percentage, 0–100: `offline` × 100 ÷ (`online` + `offline`). Null when both are 0."},"goal":{"type":"number","description":"The person’s «meta» for the day, in units, 2 decimals: their share of the «meta» of each «puesto»-hour they marked in (when two people shared a «puesto» in an hour, each takes the part that matches their part of its units)."},"efficiency":{"type":"number","nullable":true,"description":"«Eficiencia»: `units` × 100 ÷ the unrounded `goal`, 2 decimals. A percentage, not a 0–1 ratio: 100 is exactly the goal and it can pass 100. Null when the goal is 0."},"billableUnits":{"type":"number","nullable":true,"description":"Units among them the person made on a «puesto de salida», in pieces: the ones the plant bills. Null for someone who sat on none."},"totalPrice":{"type":"number","nullable":true,"description":"«Facturado»: what the person’s own «marcas» on a «puesto de salida» are worth, each at its price per unit. An amount in the currency this answer’s `currency` names, in whole currency units with up to 2 decimals (pesos or euros, never cents). Null, not 0, for someone who sat on none."}},"required":["operatorId","operatorName","units","eventUnits","activeHours","unitsPerHour","secondsPerUnitP50","idleGaps","online","offline","legacy","offlineShare","goal","efficiency","billableUnits","totalPrice"]},"ExternalActivityOperatorTotalsDto":{"type":"object","properties":{"units":{"type":"number","description":"Units of every row added up, in pieces."},"eventUnits":{"type":"number","description":"The rows’ `eventUnits` added up, in pieces: every «marca» the log holds for the rows’ «operarios»."},"activeHours":{"type":"number","description":"Clock hours of the day with activity, over the rows’ «operarios»; the denominator of `unitsPerHour`."},"unitsPerHour":{"type":"number","nullable":true,"description":"Pace of the whole answer, in units per hour: `units` ÷ `activeHours`. Null with no active hour."},"idleGaps":{"type":"number","description":"«Pausas sin registro» of every row added up."},"online":{"type":"number","description":"«Marcas» that arrived live, one by one, stamped with the app’s own clock."},"offline":{"type":"number","description":"«Marcas» that arrived in a batch: registered while the app was offline and sent when it came back."},"legacy":{"type":"number","description":"«Marcas» stamped with the server’s clock: from an app too old to send its own time, or with a client timestamp outside the accepted skew window (more than 5 minutes ahead of the server or more than 60 days behind it). They say nothing about connectivity, so `offlineShare` leaves them out."},"offlineShare":{"type":"number","nullable":true,"description":"A percentage, 0–100: `offline` × 100 ÷ (`online` + `offline`). Null when both are 0."},"goal":{"type":"number","description":"The «meta» of every row added up, in units, 2 decimals: the «meta» of every «puesto»-hour worked."},"efficiency":{"type":"number","nullable":true,"description":"«Eficiencia»: `units` × 100 ÷ the unrounded `goal`, 2 decimals. A percentage, not a 0–1 ratio: 100 is exactly the goal and it can pass 100. Null when the goal is 0."},"billableUnits":{"type":"number","nullable":true,"description":"Units made on «puestos de salida», in pieces: the ones the plant bills. Null, not 0, when no row has any."},"totalPrice":{"type":"number","nullable":true,"description":"«Facturado» of every row added up. An amount in the currency this answer’s `currency` names, in whole currency units with up to 2 decimals (pesos or euros, never cents). Null, not 0, when no row has any."},"operators":{"type":"number","description":"How many rows `operators` carries."},"unattributedEventUnits":{"type":"number","description":"Units of «marcas» registered with nobody seated at the «puesto», in pieces. They are in no row: without this figure the work would be missing between the rows and the plant’s total."},"idleMinutesApplied":{"type":"number","description":"The `idleMinutes` threshold the gap counts of this answer were counted against, in minutes: the one asked, or the default 5."}},"required":["units","eventUnits","activeHours","unitsPerHour","idleGaps","online","offline","legacy","offlineShare","goal","efficiency","billableUnits","totalPrice","operators","unattributedEventUnits","idleMinutesApplied"]},"ExternalActivityOperatorsDto":{"type":"object","properties":{"moduleId":{"type":"number","nullable":true,"description":"The «módulo» the answer was narrowed to: the `moduleId` asked. Null for the whole plant."},"operators":{"description":"One row per «operario» with production that day.","type":"array","items":{"$ref":"#/components/schemas/ExternalActivityOperatorDto"}},"totals":{"description":"The whole day, over what was asked.","allOf":[{"$ref":"#/components/schemas/ExternalActivityOperatorTotalsDto"}]}},"required":["moduleId","operators","totals"]},"ExternalActivityPageDto":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/ExternalActivityDto"}},"page":{"type":"integer","description":"1-based."},"total":{"type":"integer","description":"Every row the caller may see, across pages."}},"required":["items","page","total"]},"ExternalAddFromCatalogueDto":{"type":"object","properties":{"moduleId":{"type":"integer","minimum":1,"maximum":2147483647,"description":"The módulo the OCs join, each at the end of its queue, in the order sent."},"items":{"minItems":1,"maxItems":50,"description":"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».","type":"array","items":{"$ref":"#/components/schemas/ExternalCatalogueItemDto"}}},"required":["moduleId","items"]},"ExternalArriveReferenceDto":{"type":"object","properties":{"goal":{"type":"integer","minimum":1,"maximum":1000000,"description":"«Meta por hora» on the line, asked at «Llegó»."},"units":{"type":"integer","minimum":1,"maximum":10000000,"description":"The units the corte’s «ficha técnica» says. Absent, the brand’s. Units that differ are reported to the brand, never applied."},"note":{"type":"string","maxLength":500,"description":"A note for the brand, optional."}},"required":["goal"]},"ExternalBillingByModuleDto":{"type":"object","properties":{"day":{"type":"string","description":"Plant day, `YYYY-MM-DD` on the plant’s own clock."},"currency":{"type":"string","description":"ISO 4217 code of the currency every money figure of this answer is in. It is the plant’s, set by its «país»: `EUR` for a plant in España, `COP` for every other."},"modules":{"description":"One row per «módulo» that billed that day, by id. A «módulo» that billed nothing is no row.","type":"array","items":{"$ref":"#/components/schemas/ExternalModuleBillingRowDto"}}},"required":["day","currency","modules"]},"ExternalBillingPageDto":{"type":"object","properties":{"from":{"type":"string","description":"First day of the range, YYYY-MM-DD."},"to":{"type":"string","description":"Last day of the range, YYYY-MM-DD."},"groupBy":{"enum":["day","module","operator","operation","reference","category"],"type":"string","description":"The grouping asked. Under a filter, `day` rows also name the módulo, operario or puesto they are about."},"page":{"type":"number","description":"1-based."},"pageSize":{"type":"number","description":"Rows per page."},"total":{"type":"number","description":"Rows of the whole range, across pages."},"currency":{"type":"string","description":"ISO 4217 code of the plant’s currency, set by its país: EUR in España, COP elsewhere."},"items":{"type":"array","items":{"$ref":"#/components/schemas/ExternalBillingRowDto"}}},"required":["from","to","groupBy","page","pageSize","total","currency","items"]},"ExternalBillingRowDto":{"type":"object","properties":{"day":{"type":"string","description":"Plant day, YYYY-MM-DD."},"module":{"$ref":"#/components/schemas/ExternalModuleRefDto"},"operator":{"$ref":"#/components/schemas/ExternalOperatorRefDto"},"operation":{"$ref":"#/components/schemas/ExternalOperationRefDto"},"billableUnits":{"type":"number","nullable":true,"description":"Units billed: the puestos de salida’s, plus (by day and módulo) a typed hour no puesto held the billing role in. Null where nothing was billed."},"billedAmount":{"type":"number","nullable":true,"description":"Σ units × the price each was billed at, in `currency`, rounded to the cent where its dashboard figure rounds it (the route says where). Null — not 0 — where nothing priced them."}},"required":["day","billableUnits","billedAmount"]},"ExternalBoardBucketDto":{"type":"object","properties":{"referenceId":{"type":"integer","nullable":true},"referenceName":{"type":"string","nullable":true},"produced":{"type":"integer","description":"Units the output puestos tapped for this referencia."}},"required":["referenceId","referenceName","produced"]},"ExternalBoardCountDto":{"type":"object","properties":{"referenceId":{"type":"integer","nullable":true},"referenceName":{"type":"string","nullable":true},"counted":{"type":"integer"}},"required":["referenceId","referenceName","counted"]},"ExternalBoardDayGoalDto":{"type":"object","properties":{"referenceId":{"type":"integer"},"goal":{"type":"integer"},"steps":{"nullable":true,"type":"array","items":{"$ref":"#/components/schemas/ExternalBoardDayGoalStepDto"}},"source":{"enum":["manual","freeze"],"type":"string"}},"required":["referenceId","goal","steps","source"]},"ExternalBoardDayGoalStepDto":{"type":"object","properties":{"from":{"type":"integer","description":"Minute of the day it starts."},"goal":{"type":"integer"}},"required":["from","goal"]},"ExternalBoardEffectiveDto":{"type":"object","properties":{"referenceId":{"type":"integer","nullable":true},"referenceName":{"type":"string","nullable":true},"units":{"type":"integer"},"source":{"enum":["manual","app"],"type":"string","description":"manual when the hour was typed, app (taps) when it was not."}},"required":["referenceId","referenceName","units","source"]},"ExternalBoardEntriesDto":{"type":"object","properties":{"referenceId":{"type":"integer"},"referenceName":{"type":"string","nullable":true},"total":{"type":"integer","nullable":true},"enteredBefore":{"type":"integer"},"enteredToday":{"type":"integer"},"enteredToDate":{"type":"integer"},"producedToDate":{"type":"integer"},"inside":{"type":"integer","description":"«Prendas dentro», signed."}},"required":["referenceId","referenceName","total","enteredBefore","enteredToday","enteredToDate","producedToDate","inside"]},"ExternalBoardReferenceDto":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string","nullable":true},"total":{"type":"integer","nullable":true},"goal":{"type":"integer","nullable":true,"description":"The referencia’s base hourly goal."},"producedBefore":{"type":"integer","description":"Units before the day."},"producedToDate":{"type":"integer","description":"Units through the day."}},"required":["id","name","total","goal","producedBefore","producedToDate"]},"ExternalBoardRowDto":{"type":"object","properties":{"hour":{"type":"integer","minimum":0,"maximum":23},"minutes":{"type":"integer","description":"Minutes of the hour the plant works: 60 less its breaks."},"goal":{"type":"integer","description":"The hour’s goal."},"produced":{"type":"integer","description":"The automatic report: the output puestos’ taps."},"entered":{"type":"integer","description":"Units the entry puestos tapped (0 with no entry puesto)."},"counted":{"type":"integer","nullable":true,"description":"The manual report: what was typed for the hour; null when nothing was typed."},"countedByReference":{"type":"array","items":{"$ref":"#/components/schemas/ExternalBoardCountDto"}},"countedAt":{"type":"string","nullable":true,"format":"date-time"},"countedBy":{"type":"string","nullable":true},"effectiveByReference":{"description":"What counts for each referencia in the hour.","type":"array","items":{"$ref":"#/components/schemas/ExternalBoardEffectiveDto"}},"accDay":{"type":"integer","description":"Running day total: typed hours as typed, blank hours as tapped."},"balanceDay":{"type":"integer"},"automaticAccDay":{"type":"integer","description":"Running day total of the taps alone."},"accReference":{"type":"integer"},"balanceReference":{"type":"integer"},"referenceId":{"type":"integer","nullable":true,"description":"The referencia that leads the hour."},"referenceName":{"type":"string","nullable":true},"mixedReference":{"type":"boolean","description":"More than one referencia in the hour."},"efficiency":{"type":"number","nullable":true},"efficiencyAcc":{"type":"number","nullable":true},"met":{"type":"boolean","nullable":true,"description":"Met the hour’s goal; null when there was nothing to judge."},"accReferenceToDate":{"type":"integer","nullable":true,"description":"«Ac. OC»: the leading referencia’s units across days."},"balanceReferenceToDate":{"type":"integer","nullable":true,"description":"«Sld. OC», signed."},"buckets":{"type":"array","items":{"$ref":"#/components/schemas/ExternalBoardBucketDto"}},"steps":{"description":"Per-«Subproceso» figures; present only while «El operario escoge la referencia» is on.","type":"array","items":{"$ref":"#/components/schemas/ExternalBoardStepDto"}},"counting":{"type":"integer","description":"Units of the steps that count (0 and 2); present with `steps`."}},"required":["hour","minutes","goal","produced","entered","counted","countedByReference","countedAt","countedBy","effectiveByReference","accDay","balanceDay","automaticAccDay","accReference","balanceReference","referenceId","referenceName","mixedReference","efficiency","efficiencyAcc","met","accReferenceToDate","balanceReferenceToDate","buckets"]},"ExternalBoardShiftDto":{"type":"object","properties":{"hours":{"type":"array","items":{"type":"integer"}},"extraHour":{"type":"integer","nullable":true},"endMin":{"type":"integer","nullable":true,"description":"The shift’s end that day, minutes from midnight."},"window":{"nullable":true,"type":"object","allOf":[{"$ref":"#/components/schemas/ExternalBoardWindowDto"}]}},"required":["hours","extraHour","endMin","window"]},"ExternalBoardStepDto":{"type":"object","properties":{"step":{"type":"integer","enum":[0,1,2]},"name":{"type":"string","nullable":true},"done":{"type":"integer"},"expected":{"type":"number"},"goal":{"type":"number"},"minutes":{"type":"number"}},"required":["step","name","done","expected","goal","minutes"]},"ExternalBoardTotalsDto":{"type":"object","properties":{"goal":{"type":"integer"},"produced":{"type":"integer","description":"Typed hours as typed, blank hours as tapped."},"automatic":{"type":"integer","description":"The taps alone."},"balance":{"type":"integer"},"efficiency":{"type":"number","nullable":true},"entered":{"type":"integer"}},"required":["goal","produced","automatic","balance","efficiency","entered"]},"ExternalBoardTypingDto":{"type":"object","properties":{"lastTyped":{"nullable":true,"description":"The last hour somebody typed a count for.","type":"object","allOf":[{"$ref":"#/components/schemas/ExternalLastTypedDto"}]},"untypedSince":{"type":"integer","nullable":true,"description":"The first closed shift hour with no typed count; null when every one is typed."}},"required":["lastTyped","untypedSince"]},"ExternalBoardWindowDto":{"type":"object","properties":{"firstHour":{"type":"integer"},"lastHour":{"type":"integer"},"extraHour":{"type":"integer","description":"Where «Extra» is typed."}},"required":["firstHour","lastHour","extraHour"]},"ExternalBrandCategoriesDto":{"type":"object","properties":{"companyAdminId":{"type":"integer","description":"The brand («cuenta de marca»): what a referencia’s `companyAdminId` holds when the OC is for it."},"name":{"type":"string","description":"The brand’s name."},"categories":{"type":"array","items":{"$ref":"#/components/schemas/ExternalCategoryDto"}}},"required":["companyAdminId","name","categories"]},"ExternalCatalogueDeletePreviewDto":{"type":"object","properties":{"entry":{"$ref":"#/components/schemas/ExternalCatalogueEntryDto"},"changesNoReference":{"type":"boolean","description":"Deleting a template changes no OC: the ones made from it keep their values. The name is free again."},"confirmationToken":{"type":"string","description":"Send it back, unchanged, to `deleteCatalogueEntry` once the usuario said yes. Valid 15 minutes, for this conexión, this organization and this template as shown here."},"expiresAt":{"type":"string","format":"date-time"}},"required":["entry","changesNoReference","confirmationToken","expiresAt"]},"ExternalCatalogueEntryDeletedDto":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"deleted":{"type":"boolean","enum":[true]}},"required":["id","name","deleted"]},"ExternalCatalogueEntryDto":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string","description":"«Referencia»."},"description":{"type":"string","nullable":true,"description":"«Descripción»: for search only, never copied to an OC."},"garment":{"type":"string","nullable":true,"description":"«Categoría»."},"goal":{"type":"integer","nullable":true,"description":"«Meta por hora»."},"price":{"type":"number","nullable":true,"description":"«Precio por unidad»."},"color":{"type":"string","nullable":true},"orderNumber":{"type":"string","nullable":true,"description":"«Corte (OC)» a new OC from it starts with."},"total":{"type":"integer","nullable":true,"description":"«Total de unidades» a new OC from it starts with."},"step1Name":{"type":"string","nullable":true},"step1Goal":{"type":"integer","nullable":true},"step2Name":{"type":"string","nullable":true},"step2Goal":{"type":"integer","nullable":true}},"required":["id","name","description","garment","goal","price","color","orderNumber","total","step1Name","step1Goal","step2Name","step2Goal"]},"ExternalCatalogueItemDto":{"type":"object","properties":{"templateId":{"type":"integer","minimum":1,"maximum":2147483647,"description":"The catálogo template (an id from `listCatalogue`)."},"orderNumber":{"type":"string","maxLength":255,"description":"«Corte (OC)». Omitted, the template’s; blank on both, the template’s name."},"total":{"type":"integer","nullable":true,"minimum":1,"maximum":1000000,"description":"«Total de unidades». Omitted, the template’s."},"goal":{"type":"integer","minimum":1,"maximum":100000,"description":"«Meta por hora». Omitted, the template’s; a template without one needs it here."},"dueDate":{"type":"string","format":"date-time","description":"«Fecha de compromiso», ISO 8601 with its offset."}},"required":["templateId"]},"ExternalCatalogueItemFailureDto":{"type":"object","properties":{"templateId":{"type":"integer"},"statusCode":{"type":"integer","description":"The HTTP status it got."},"code":{"type":"string","nullable":true,"description":"The refusal’s `code`, when it had one."},"message":{"type":"string","description":"Spanish, for the usuario."}},"required":["templateId","statusCode","code","message"]},"ExternalCategoriesDto":{"type":"object","properties":{"own":{"description":"The plant’s own «Categorías», for «Otros clientes» (its own clients).","type":"array","items":{"type":"string"}},"brands":{"description":"One group per brand the plant works for in the «Red de talleres», with that brand’s categories. Empty for a plant in no red.","type":"array","items":{"$ref":"#/components/schemas/ExternalBrandCategoriesDto"}}},"required":["own","brands"]},"ExternalCategoryDto":{"type":"object","properties":{"code":{"type":"string","example":"01"},"short":{"type":"string","example":"Blusa","description":"What an OC’s `garment` takes."},"name":{"type":"string","example":"Blusa · Vestido"},"emoji":{"type":"string","nullable":true}},"required":["code","short","name","emoji"]},"ExternalCategoryRefDto":{"type":"object","properties":{"key":{"type":"string","description":"The categoría without accents or case; `sin` for OCs with none, `sin-oc` for units with no OC."},"label":{"type":"string","description":"Its most frequent spelling over the range; «Sin categoría», «Sin OC»."}},"required":["key","label"]},"ExternalClosedHoursDto":{"type":"object","properties":{"throughHour":{"type":"integer","description":"The last hour that counts: the figure runs from the shift’s start through it."},"produced":{"type":"integer"},"goal":{"type":"integer"},"efficiency":{"type":"number","nullable":true},"source":{"enum":["manual","automatic","none"],"type":"string"}},"required":["throughHour","produced","goal","efficiency","source"]},"ExternalConfirmedDeleteDto":{"type":"object","properties":{"confirmationToken":{"type":"string","maxLength":2048,"description":"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`."}}},"ExternalConsistencyDto":{"type":"object","properties":{"totals":{"description":"The day’s rows counted by `status`, and both sides added up.","allOf":[{"$ref":"#/components/schemas/ExternalConsistencyTotalsDto"}]},"rows":{"description":"One row per «puesto», hour and «referencia» of the plant day that either side holds.","type":"array","items":{"$ref":"#/components/schemas/ExternalConsistencyRowDto"}}},"required":["totals","rows"]},"ExternalConsistencyReportDto":{"type":"object","properties":{"totals":{"description":"The day’s rows counted by `status`, and both sides added up.","allOf":[{"$ref":"#/components/schemas/ExternalConsistencyTotalsDto"}]},"rows":{"description":"One row per «puesto», hour and «referencia» of the plant day that either side holds.","type":"array","items":{"$ref":"#/components/schemas/ExternalConsistencyRowDto"}},"day":{"type":"string","description":"Plant day, `YYYY-MM-DD` on the plant’s own clock."},"moduleId":{"type":"number","nullable":true,"description":"The «módulo» the check was narrowed to: the `moduleId` of the request. Null for the whole plant."}},"required":["totals","rows","day","moduleId"]},"ExternalConsistencyRowDto":{"type":"object","properties":{"operationId":{"type":"number","description":"The «puesto» (station) of this row. A «puesto» is called `operation` in ids and `station` in paths: this is the `id` `listStations` returns and the `stationId` of the station routes."},"hour":{"type":"integer","minimum":0,"maximum":23,"description":"Clock hour of the plant day, 0–23 on the plant’s own clock: the hour that starts at it (9 is 09:00–09:59)."},"referenceId":{"type":"number","nullable":true,"description":"The «referencia» (order lot) of this row: its `id` in the «Programa». Null when the «puesto» had none."},"isBilling":{"type":"boolean","description":"True when the «puesto» was a «puesto de salida» in that hour: the one whose units the plant bills."},"eventUnits":{"type":"number","nullable":true,"description":"Units the «marcas» log holds for this «puesto», hour and «referencia», in pieces. Null when `status` is `bucket_only`."},"bucketTotal":{"type":"number","nullable":true,"description":"Units the hourly bucket (what the reports read) holds for the same, in pieces. Null when `status` is `events_only`."},"status":{"type":"string","enum":["ok","mismatch","bucket_only","events_only"],"description":"`ok`: both sides hold the same units. `mismatch`: both exist and differ. `bucket_only`: the bucket holds units the log does not (expected for days before the log existed). `events_only`: the log holds «marcas» no bucket received."},"moduleId":{"type":"number","nullable":true,"description":"The «módulo» of the «puesto», as `listModules` returns it. Null when the «puesto» no longer exists."},"modulePosition":{"type":"number","nullable":true,"description":"The «módulo»’s place on the board, raw and 0-based: the dashboard prints it + 1. Null when the «puesto» no longer exists."},"operationPosition":{"type":"number","nullable":true,"description":"The «puesto»’s place in its «módulo», raw and 0-based: the dashboard prints it + 1 («puesto 1» is 0). Null when it no longer exists."},"operationName":{"type":"string","nullable":true,"description":"The «puesto»’s name: the «operación» (sewing task) done at it. Null when it has none or no longer exists."},"referenceName":{"type":"string","nullable":true,"description":"The «referencia»’s name. Null when the row has none."}},"required":["operationId","hour","referenceId","isBilling","eventUnits","bucketTotal","status","moduleId","modulePosition","operationPosition","operationName","referenceName"]},"ExternalConsistencyTotalsDto":{"type":"object","properties":{"ok":{"type":"number","description":"How many rows have `status` `ok`."},"mismatch":{"type":"number","description":"How many rows have `status` `mismatch`."},"bucketOnly":{"type":"number","description":"How many rows have `status` `bucket_only`."},"eventsOnly":{"type":"number","description":"How many rows have `status` `events_only`."},"eventUnits":{"type":"number","description":"Units the «marcas» log holds over every row, in pieces."},"bucketUnits":{"type":"number","description":"Units the hourly buckets hold over every row, in pieces. Equal to `eventUnits` on a day that is all `ok`."},"online":{"type":"number","description":"«Marcas» that arrived live, one by one, stamped with the app’s own clock."},"offline":{"type":"number","description":"«Marcas» that arrived in a batch: registered while the app was offline and sent when it came back."},"legacy":{"type":"number","description":"«Marcas» stamped with the server’s clock: from an app too old to send its own time, or with a client timestamp outside the accepted skew window (more than 5 minutes ahead of the server or more than 60 days behind it). They say nothing about connectivity, so `offlineShare` leaves them out."},"eventsWithoutBucket":{"type":"number","description":"«Marcas» registered while the «puesto» had no «referencia»: they are in the log and in its unit counts, and no hourly bucket (so no report) holds them."}},"required":["ok","mismatch","bucketOnly","eventsOnly","eventUnits","bucketUnits","online","offline","legacy","eventsWithoutBucket"]},"ExternalCreateCatalogueEntryDto":{"type":"object","properties":{"name":{"type":"string","description":"«Referencia»: stored trimmed with inner whitespace collapsed; unique in\nthe catálogo, case-insensitively (409 otherwise).","maxLength":255},"description":{"type":"string","nullable":true,"description":"«Descripción»: for search only, never copied to an OC.","maxLength":255},"garment":{"type":"string","nullable":true,"description":"«Categoría»: one of the plant's categories (any case; stored in the\nlist's spelling) when the plant has a list, free text otherwise.","example":"Camisa","maxLength":255},"goal":{"type":"integer","nullable":true,"description":"«Meta por hora»: a whole number from 1 to 100000.","example":60,"minimum":1,"maximum":100000},"orderNumber":{"type":"string","nullable":true,"maxLength":255,"description":"«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.","example":"OC-4471"},"total":{"type":"integer","nullable":true,"description":"«Total de unidades» by default: a whole number from 1 to 1000000, the total a new OC made from this entry starts with.","example":600,"minimum":1,"maximum":1000000},"price":{"type":"number","nullable":true,"description":"«Precio por unidad»: 0 or more, at most two decimals. Not stored on a\nplan without facturación.","example":1250.5,"minimum":0,"maximum":1000000000},"color":{"type":"string","nullable":true,"description":"«Color»: `#RRGGBB`, or `#RGB` (expanded). Not stored on a plan without\nthe referencia colour.","example":"#1f6feb","maxLength":32},"step1Name":{"type":"string","nullable":true,"maxLength":60,"description":"«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».","example":"Patines"},"step1Goal":{"type":"integer","nullable":true,"description":"«Meta subproceso 1»: a whole number in the «Meta por hora» range, 1 to 100000.","example":40,"minimum":1,"maximum":100000},"step2Name":{"type":"string","nullable":true,"description":"«Subproceso 2»: the second step; needs step 1.","maxLength":60},"step2Goal":{"type":"integer","nullable":true,"description":"«Meta subproceso 2».","example":30,"minimum":1,"maximum":100000}},"required":["name"]},"ExternalCreateReferenceDto":{"type":"object","properties":{"moduleId":{"type":"integer","minimum":1,"maximum":2147483647,"description":"The módulo whose «Programa» the OC joins, at the end of its queue (an id from `listModules`)."},"name":{"type":"string","maxLength":255,"description":"«Referencia»: the garment style’s name."},"orderNumber":{"type":"string","maxLength":255,"description":"«Corte (OC)», the cut’s order number. Omitted or blank, the OC takes its name, as the dashboard’s form does."},"garment":{"type":"string","maxLength":255,"description":"«Categoría»: one of the organization’s categories when it keeps a list (`getOrganization`)."},"goal":{"type":"integer","minimum":1,"maximum":100000,"description":"«Meta por hora»: units per hour."},"total":{"type":"integer","nullable":true,"minimum":1,"maximum":1000000,"description":"«Total de unidades». Required unless the plant has «El operario escoge la referencia» on: 400 `TOTAL_REQUIRED`."},"price":{"type":"number","minimum":0,"maximum":1000000000,"description":"«Precio por unidad», in the organization’s currency (`getOrganization`)."},"color":{"type":"string","example":"#1f6feb","description":"The OC’s colour, `#RRGGBB`. Omitted, Fluenta picks one, as on the dashboard."},"dueDate":{"type":"string","format":"date-time","description":"«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":{"type":"string","format":"date-time","description":"«F. Empalme»: when it starts on its módulo. ISO 8601 with its offset."},"priority":{"type":"boolean","description":"«OC prioritaria»."},"continuity":{"type":"boolean","description":"«Continuidad»: the same garment as the cut before it."},"step1Name":{"type":"string","nullable":true,"maxLength":60,"description":"«Subproceso 1»: needs `step1Goal`."},"step1Goal":{"type":"integer","nullable":true,"description":"«Meta subproceso 1», units per hour."},"step2Name":{"type":"string","nullable":true,"maxLength":60,"description":"«Subproceso 2»: needs step 1 and `step2Goal`."},"step2Goal":{"type":"integer","nullable":true,"description":"«Meta subproceso 2»."},"saveToCatalog":{"type":"boolean","description":"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."}},"required":["moduleId","name","goal"]},"ExternalCurrencyDto":{"type":"object","properties":{"code":{"enum":["COP","EUR"],"type":"string","description":"ISO 4217 code every «Precio» and «Facturación» figure of the organization is in."},"locale":{"type":"string","example":"es-CO","description":"The locale its amounts are written in."},"fractionDigits":{"type":"integer","description":"Decimals shown: 0 for pesos, 2 for euros."}},"required":["code","locale","fractionDigits"]},"ExternalDayGoalStepDto":{"type":"object","properties":{"from":{"type":"integer","description":"Minute of the day the rate starts at."},"goal":{"type":"integer","description":"Units per hour from then."}},"required":["from","goal"]},"ExternalDayGoalStepInputDto":{"type":"object","properties":{"from":{"type":"integer","minimum":0,"maximum":1439,"description":"Minute of the day the rate starts at (600 = 10:00)."},"goal":{"type":"integer","minimum":1,"maximum":100000,"description":"Units per hour from then."}},"required":["from","goal"]},"ExternalDayReportDto":{"type":"object","properties":{"day":{"type":"string","description":"Plant day, YYYY-MM-DD."},"modules":{"type":"array","items":{"$ref":"#/components/schemas/ExternalDayReportModuleDto"}}},"required":["day","modules"]},"ExternalDayReportHourDto":{"type":"object","properties":{"hour":{"type":"integer","minimum":0,"maximum":23,"description":"Clock hour of the plant day, 0–23 on the plant’s own clock: the hour that starts at it (9 is 09:00–09:59)."},"units":{"type":"number","description":"Units made in the hour, in pieces, by every «puesto» of the answer. In a «puesto»’s report with «El operario escoge la referencia» on: its complete units only."},"goal":{"type":"number","description":"The hour’s «meta», in units: the sum of the «meta» of each «puesto» that worked in it. In a «puesto»’s report with «El operario escoge la referencia» on: the sum of each step’s expected units."},"efficiency":{"type":"number","nullable":true,"description":"«Eficiencia»: this row’s `units` × 100 ÷ its `goal`, 2 decimals. A percentage, not a 0–1 ratio: 100 is exactly the goal and it can pass 100. Null when the goal is 0."},"billedAmount":{"type":"number","description":"The hour’s «Facturación»: the sum, over its rows on a «puesto de salida», of `units` × `unitPrice`, each rounded to 2 decimals; 0 with none. In the plant’s currency (the answer’s `currency` where it carries one, else `getOrganization` `currency.code`), in whole currency units, never cents."},"secondsPerUnit":{"type":"number","nullable":true,"description":"Average seconds between consecutive «marcas» in the hour, as the app timed them: total seconds ÷ units, 2 decimals. Null with no units."},"pausedMinutes":{"type":"number","description":"Minutes the hour’s «meta» lost to «pausas» already closed: their part inside the «jornada» and outside «descansos». 0 with none."},"pauses":{"type":"number","description":"How many «pausas» started in the hour, open ones included. 0 with none."},"rows":{"description":"One row per «puesto» and «referencia» worked in the hour. Empty for an hour that only had «pausas».","type":"array","items":{"$ref":"#/components/schemas/ExternalDayReportRowDto"}}},"required":["hour","units","goal","efficiency","billedAmount","secondsPerUnit","pausedMinutes","pauses","rows"]},"ExternalDayReportModuleDto":{"type":"object","properties":{"moduleId":{"type":"number"},"moduleName":{"type":"string","nullable":true},"units":{"type":"number"},"goal":{"type":"number"},"efficiency":{"type":"number","nullable":true},"pausedMinutes":{"type":"number"},"pauses":{"type":"number"},"hours":{"type":"array","items":{"$ref":"#/components/schemas/ExternalDayReportHourDto"}}},"required":["moduleId","moduleName","units","goal","efficiency","pausedMinutes","pauses","hours"]},"ExternalDayReportRowDto":{"type":"object","properties":{"operationId":{"type":"number","description":"The «puesto» (station) this row is about. A «puesto» is called `operation` in ids and `station` in paths: this is the `id` `listStations` returns and the `stationId` of the station routes."},"operationName":{"type":"string","nullable":true,"description":"The «puesto»’s name as it was stored with that hour: the «operación» (sewing task) done at it, such as «pegar cuello». Null when it had none."},"operatorId":{"type":"number","nullable":true,"description":"The «operario» who made these units: the `id` of one from `listOperators`. Null when nobody was seated."},"operatorName":{"type":"string","nullable":true,"description":"That «operario»’s name. Null when nobody was seated."},"referenceId":{"type":"number","nullable":true,"description":"The «referencia» (the order lot, also called «OC» or «corte») the units were made on: its `id` in the «Programa». Null when the «puesto» had none."},"referenceName":{"type":"string","nullable":true,"description":"That «referencia»’s name as it was stored with the hour. Null when the «puesto» had none."},"units":{"type":"number","description":"Units the «puesto» made on this «referencia» in the hour, in pieces."},"goal":{"type":"number","description":"The «puesto»’s «meta» for the hour, in units: its «meta por hora» as it stood then, already shortened for «descansos», «pausas» and the edges of the «jornada»."},"efficiency":{"type":"number","nullable":true,"description":"«Eficiencia»: this row’s `units` × 100 ÷ its `goal`, 2 decimals. A percentage, not a 0–1 ratio: 100 is exactly the goal and it can pass 100. Null when the goal is 0."},"isBilling":{"type":"boolean","description":"True when the «puesto» was a «puesto de salida» in that hour (its setting «Reporte y facturación»): the one whose units count as the «módulo»’s output and are billed. Only such a row carries a price."},"unitPrice":{"type":"number","nullable":true,"description":"The price per unit of the row’s «referencia», at which its units are billed. In the plant’s currency (the answer’s `currency` where it carries one, else `getOrganization` `currency.code`), in whole currency units with up to 2 decimals, never cents. Null on a «puesto» that is not «de salida»."},"secondsPerUnit":{"type":"number","nullable":true,"description":"Average seconds between consecutive «marcas», as the app timed them: total seconds ÷ units, 2 decimals. Null with no units."}},"required":["operationId","operationName","operatorId","operatorName","referenceId","referenceName","units","goal","efficiency","isBilling","unitPrice","secondsPerUnit"]},"ExternalDeleteRefusalDto":{"type":"object","properties":{"code":{"type":"string","nullable":true},"message":{"type":"string","description":"Spanish, for the usuario."}},"required":["code","message"]},"ExternalEfficiencyByModuleDto":{"type":"object","properties":{"day":{"type":"string","description":"Plant day, `YYYY-MM-DD` on the plant’s own clock."},"modules":{"description":"One row per «módulo» name, in name order. A «módulo» with only «pausas» that day is a row too; one with nothing is no row.","type":"array","items":{"$ref":"#/components/schemas/ExternalModuleEfficiencyRowDto"}}},"required":["day","modules"]},"ExternalEfficiencyDayDto":{"type":"object","properties":{"day":{"type":"string","description":"Plant day, `YYYY-MM-DD` on the plant’s own clock."},"units":{"type":"number","description":"Units made that day, in pieces: by every «puesto» of the «módulo», or the «operario»’s own «marcas»."},"goal":{"type":"number","description":"The day’s «meta», in units: for a «módulo», the sum of each «puesto»’s «meta» for each hour it worked; for an «operario», their share of the «meta» of each «puesto»-hour they marked in, 2 decimals."},"efficiency":{"type":"number","nullable":true,"description":"«Eficiencia»: this row’s `units` × 100 ÷ its `goal`, 2 decimals. A percentage, not a 0–1 ratio: 100 is exactly the goal and it can pass 100. Null when the goal is 0."}},"required":["day","units","goal","efficiency"]},"ExternalEfficiencyPageDto":{"type":"object","properties":{"from":{"type":"string","description":"First day of the range, YYYY-MM-DD."},"to":{"type":"string","description":"Last day of the range, YYYY-MM-DD."},"groupBy":{"enum":["day","module","operator","operation","reference","category"],"type":"string","description":"The grouping asked. Under a filter, `day` rows also name the módulo, operario or puesto they are about."},"page":{"type":"number","description":"1-based."},"pageSize":{"type":"number","description":"Rows per page."},"total":{"type":"number","description":"Rows of the whole range, across pages."},"items":{"type":"array","items":{"$ref":"#/components/schemas/ExternalEfficiencyRowDto"}}},"required":["from","to","groupBy","page","pageSize","total","items"]},"ExternalEfficiencyRowDto":{"type":"object","properties":{"day":{"type":"string","description":"Plant day, YYYY-MM-DD."},"module":{"$ref":"#/components/schemas/ExternalModuleRefDto"},"operator":{"$ref":"#/components/schemas/ExternalOperatorRefDto"},"operation":{"$ref":"#/components/schemas/ExternalOperationRefDto"},"units":{"type":"number","description":"Units made, every puesto counted. A puesto’s, with «El operario escoge la referencia» on: its complete units, the Total of its «reporte por operación»."},"goal":{"type":"number","description":"The meta for the same span: per puesto-hour its meta, summed. An operario’s is their share of each puesto-hour they marked in (fractional, 2 decimals). A puesto’s, with «El operario escoge la referencia» on: the sum of each hour’s expected units (2 decimals)."},"efficiency":{"type":"number","nullable":true,"description":"units × 100 / goal, 2 decimals, rounded as the dashboard rounds it; null with no meta. A puesto’s, with «El operario escoge la referencia» on: every marca (each subproceso included) over its goal, null under half a unit of meta."}},"required":["day","units","goal","efficiency"]},"ExternalEventsSummaryDto":{"type":"object","properties":{"events":{"type":"number","description":"How many «marcas» the «puesto» registered that day. A «marca» is one tap on the app."},"units":{"type":"number","description":"Units those «marcas» add up to, in pieces."},"billableUnits":{"type":"number","description":"Units among them registered while the «puesto» was a «puesto de salida», in pieces: the ones the plant bills. 0 when it never was."},"billedAmount":{"type":"number","description":"What the billable units are worth: each at the price per unit of its «referencia», 2 decimals; 0 with none. An amount in the currency this answer’s `currency` names, in whole currency units with up to 2 decimals (pesos or euros, never cents)."},"online":{"type":"number","description":"«Marcas» that arrived live, one by one, stamped with the app’s own clock."},"offline":{"type":"number","description":"«Marcas» that arrived in a batch: registered while the app was offline and sent when it came back."},"legacy":{"type":"number","description":"«Marcas» stamped with the server’s clock: from an app too old to send its own time, or with a client timestamp outside the accepted skew window (more than 5 minutes ahead of the server or more than 60 days behind it). They say nothing about connectivity, so `offlineShare` leaves them out."},"eventsWithoutBucket":{"type":"number","description":"«Marcas» registered while the «puesto» had no «referencia»: they are in the log and in its unit counts, and no hourly bucket (so no report) holds them."},"secondsPerUnitP50":{"type":"number","nullable":true,"description":"Median seconds between one «marca» and the one before it, as the app’s own stopwatch timed them. Null with no timed «marca»."},"secondsPerUnitP90":{"type":"number","nullable":true,"description":"90th percentile of the same seconds: 9 of 10 «marcas» came faster than this. Null with no timed «marca»."},"idleGapsOverN":{"type":"number","description":"«Pausas sin registro»: how many gaps between consecutive «marcas» lasted longer than `idleMinutes` minutes. They are inferred from the log; they are not the «pausas» an «operario» takes with «Pausar»."},"idleMinutes":{"type":"number","description":"The threshold `idleGapsOverN` was counted against, in minutes: the `idleMinutes` asked, or the default 5."},"firstAt":{"type":"string","nullable":true,"format":"date-time","description":"When the day’s first «marca» was registered, as an instant: ISO 8601 in UTC. Null on a day with none."},"lastAt":{"type":"string","nullable":true,"format":"date-time","description":"When the day’s last «marca» was registered, as an instant: ISO 8601 in UTC. Null on a day with none."}},"required":["events","units","billableUnits","billedAmount","online","offline","legacy","eventsWithoutBucket","secondsPerUnitP50","secondsPerUnitP90","idleGapsOverN","idleMinutes","firstAt","lastAt"]},"ExternalFloorDto":{"type":"object","properties":{"today":{"type":"string","nullable":true,"format":"date","description":"The plant’s today on its own clock, `YYYY-MM-DD`: the day every `unitsToday` is about. Null only when the clock could not be read."},"currentHour":{"type":"integer","nullable":true,"minimum":0,"maximum":23,"description":"The hour of the day the plant is living now on its own clock, 0 to 23: the hour every `unitsThisHour` and `hourGoal` is about. Null only when the clock could not be read."},"modules":{"description":"Every «módulo» of the organization, in the floor’s order (`position`). Empty for an organization without one.","type":"array","items":{"$ref":"#/components/schemas/ExternalFloorModuleDto"}}},"required":["today","currentHour","modules"]},"ExternalFloorModuleDto":{"type":"object","properties":{"id":{"type":"integer","description":"The `moduleId` the módulo routes take."},"name":{"type":"string","nullable":true,"description":"The «módulo»’s name as the board shows it; null when it has none."},"position":{"type":"integer","description":"Its place on the floor, top to bottom (0-based)."},"stationCount":{"type":"integer","description":"How many «puestos» (stations) the módulo has."},"colorHue":{"type":"integer","nullable":true,"minimum":1,"maximum":12,"description":"The thread colour the módulo wears on every dashboard screen, one of the dashboard’s 12; null when it wears the automatic one."},"stations":{"description":"The «módulo»’s «puestos» in the board’s order, each as the live board reads it now. Empty for a «módulo» without one.","type":"array","items":{"$ref":"#/components/schemas/ExternalStationDto"}}},"required":["id","name","position","stationCount","colorHue","stations"]},"ExternalHoldReferenceDto":{"type":"object","properties":{"reason":{"type":"string","maxLength":255,"description":"Why it pauses, as the dashboard offers it: «Problema de tela», «Falta insumo», «Calidad», «Otro», or free text."},"note":{"type":"string","maxLength":500,"description":"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»."}}},"ExternalHolidayDto":{"type":"object","properties":{"day":{"type":"string","format":"date","example":"2026-12-08"},"name":{"type":"string","example":"Inmaculada Concepción"},"worked":{"type":"boolean","description":"The plant works this national holiday (it is in `workedHolidays`)."}},"required":["day","name","worked"]},"ExternalHourlyActivityDto":{"type":"object","properties":{"moduleId":{"type":"number","nullable":true,"description":"The «módulo» the answer was narrowed to: the `moduleId` asked. Null for the whole plant."},"operatorId":{"type":"number","nullable":true,"description":"The «operario» the answer was narrowed to: the `operatorId` asked. Null for every «operario»."},"hours":{"description":"One entry per clock hour of the plant day with activity, earliest first. An hour with none is no entry.","type":"array","items":{"$ref":"#/components/schemas/ExternalActivityHourDto"}},"totals":{"description":"The whole day, over what was asked.","allOf":[{"$ref":"#/components/schemas/ExternalActivityHourTotalsDto"}]},"day":{"type":"string","description":"Plant day, `YYYY-MM-DD` on the plant’s own clock."},"currency":{"type":"string","description":"ISO 4217 code of the currency every money figure of this answer is in. It is the plant’s, set by its «país»: `EUR` for a plant in España, `COP` for every other."}},"required":["moduleId","operatorId","hours","totals","day","currency"]},"ExternalHoursBoardDto":{"type":"object","properties":{"moduleId":{"type":"integer"},"day":{"type":"string","format":"date"},"rows":{"type":"array","items":{"$ref":"#/components/schemas/ExternalBoardRowDto"}},"billingSlots":{"type":"integer","description":"Output puestos reporting that day; 0 means no automatic comparison."},"entrySlots":{"type":"integer","description":"Entry puestos reporting."},"shift":{"nullable":true,"description":"Null when the plant has no «jornada» set.","type":"object","allOf":[{"$ref":"#/components/schemas/ExternalBoardShiftDto"}]},"totals":{"$ref":"#/components/schemas/ExternalBoardTotalsDto"},"references":{"type":"array","items":{"$ref":"#/components/schemas/ExternalBoardReferenceDto"}},"dayGoals":{"description":"«Metas del día» in force for the day.","type":"array","items":{"$ref":"#/components/schemas/ExternalBoardDayGoalDto"}},"entriesByReference":{"type":"array","items":{"$ref":"#/components/schemas/ExternalBoardEntriesDto"}}},"required":["moduleId","day","rows","billingSlots","entrySlots","shift","totals","references","dayGoals","entriesByReference"]},"ExternalLastClosedDto":{"type":"object","properties":{"day":{"type":"string","format":"date"},"efficiency":{"type":"number","nullable":true}},"required":["day","efficiency"]},"ExternalLastTypedDto":{"type":"object","properties":{"hour":{"type":"integer"},"by":{"type":"string","nullable":true,"description":"Who typed the count."},"at":{"type":"string","format":"date-time"}},"required":["hour","by","at"]},"ExternalLeaderboardDto":{"type":"object","properties":{"day":{"type":"string","description":"Plant day, YYYY-MM-DD."},"operators":{"type":"array","items":{"$ref":"#/components/schemas/ExternalLeaderboardRowDto"}}},"required":["day","operators"]},"ExternalLeaderboardRowDto":{"type":"object","properties":{"operatorId":{"type":"number"},"operatorName":{"type":"string","nullable":true},"units":{"type":"number"},"goal":{"type":"number","description":"Their share of the meta of every puesto-hour they marked in, 2 decimals."},"efficiency":{"type":"number","nullable":true},"billedAmount":{"type":"number","nullable":true,"description":"«Facturado»: Σ price of their own marcas on a puesto de salida; null for one who never sat on one."}},"required":["operatorId","operatorName","units","goal","efficiency","billedAmount"]},"ExternalLiveBoardDto":{"type":"object","properties":{"day":{"type":"string","format":"date"},"plant":{"$ref":"#/components/schemas/ExternalPlantDayStatusDto"},"modules":{"description":"Every módulo, in the floor’s order.","type":"array","items":{"$ref":"#/components/schemas/ExternalModuleDayStatusDto"}}},"required":["day","plant","modules"]},"ExternalMeConnectionDto":{"type":"object","properties":{"app":{"type":"string","description":"The connected app’s display name, or the name its user gave an API token."},"scopes":{"type":"array","items":{"type":"string","enum":["read","program"]},"description":"The «Permisos» the credential holds; always with `read`."},"expiresAt":{"type":"string","nullable":true,"format":"date-time","description":"When the credential presented expires (ISO 8601, UTC). A cached identity must not outlive it. Null for an API token that never expires."}},"required":["app","scopes","expiresAt"]},"ExternalMeCredentialDto":{"type":"object","properties":{"kind":{"enum":["oauth","personal"],"type":"string","description":"`oauth`: an app connected through «Conectar con IA». `personal`: an API token its user created in Configuración."},"name":{"type":"string","description":"The connected app’s name, or the name its user gave the API token."},"prefix":{"type":"string","example":"fluenta_live_ab1","description":"The first 16 characters of the secret, as Configuración lists it. Not enough to authenticate."},"expiresAt":{"type":"string","nullable":true,"format":"date-time","description":"When it expires (ISO 8601, UTC). Null for an API token that never expires."}},"required":["kind","name","prefix","expiresAt"]},"ExternalMeDto":{"type":"object","properties":{"user":{"$ref":"#/components/schemas/ExternalMeUserDto"},"connection":{"$ref":"#/components/schemas/ExternalMeConnectionDto"},"credential":{"$ref":"#/components/schemas/ExternalMeCredentialDto"},"rateLimit":{"description":"The budget of the routes that name no organization (`getMe`, `getMyUsage`, `listOrganizations`): a request to any of them spends it. An organization’s routes have a budget per organization, by its plan: read it from the `X-RateLimit-*` headers of their answers.","allOf":[{"$ref":"#/components/schemas/ExternalRateLimitDto"}]},"organizations":{"description":"The first page (50, by name) of the organizations the credential reaches. `listOrganizations` pages and searches the rest.","type":"array","items":{"$ref":"#/components/schemas/ExternalOrganizationDto"}},"organizationsTotal":{"type":"number","description":"How many organizations the credential reaches in all; above `organizations.length` when there are more pages."},"reachesAllOrganizations":{"type":"boolean","description":"True for a credential of an account with platform-wide access: it reaches every organization that exists."}},"required":["user","connection","credential","rateLimit","organizations","organizationsTotal","reachesAllOrganizations"]},"ExternalMeUserDto":{"type":"object","properties":{"name":{"type":"string","description":"The user the credential acts as."},"roles":{"type":"array","items":{"type":"string","enum":["SUPER","COMPANY_ADMIN","ADMIN","MODERATOR","USER"]},"description":"The user’s dashboard roles, read from their row on this request."}},"required":["name","roles"]},"ExternalModuleBillingRowDto":{"type":"object","properties":{"moduleId":{"type":"integer","description":"The «módulo»’s id, as `listModules` returns it."},"moduleName":{"type":"string","nullable":true,"description":"The «módulo»’s name."},"billableUnits":{"type":"number","description":"Units billed that day, in pieces: the units of the «módulo»’s «puestos de salida», plus an hour whose «conteo manual» was typed while no «puesto» held that role."},"billedAmount":{"type":"number","nullable":true,"description":"What those units are worth: each at the price per unit it was billed at, summed, then rounded to 2 decimals once per «módulo». An amount in the currency this answer’s `currency` names, in whole currency units with up to 2 decimals (pesos or euros, never cents). Null, not 0, where nothing priced them."}},"required":["moduleId","moduleName","billableUnits","billedAmount"]},"ExternalModuleDayHistoryDto":{"type":"object","properties":{"day":{"type":"string","description":"Plant day, `YYYY-MM-DD` on the plant’s own clock."},"module":{"description":"The «módulo» (production line) the answer is about.","allOf":[{"$ref":"#/components/schemas/ExternalModuleRefDto"}]},"currency":{"type":"string","description":"ISO 4217 code of the currency every money figure of this answer is in. It is the plant’s, set by its «país»: `EUR` for a plant in España, `COP` for every other."},"hours":{"description":"The day hour by hour, earliest first: one entry per clock hour with a «marca» or a «pausa». An hour with «pausas» and no «marca» is an entry with no rows; an hour with neither is no entry.","type":"array","items":{"$ref":"#/components/schemas/ExternalModuleHistoryHourDto"}}},"required":["day","module","currency","hours"]},"ExternalModuleDayStatusDto":{"type":"object","properties":{"moduleId":{"type":"integer"},"name":{"type":"string","nullable":true},"produced":{"type":"integer","description":"The day’s units: the typed count where an hour was typed, the output puesto’s taps where it was not."},"goal":{"type":"integer","description":"What the day owes so far: on today, the hour in progress only up to the minute."},"efficiency":{"type":"number","nullable":true,"description":"«Eficiencia» (%) of the day so far; null when the módulo has no goal yet (⏳ on the board)."},"source":{"enum":["manual","automatic"],"type":"string","description":"manual when anything was typed that day."},"recordedToday":{"type":"boolean","description":"Anything recorded on the day asked: a count or a tap."},"lastClosed":{"nullable":true,"description":"The last worked day before the one asked, for comparison.","type":"object","allOf":[{"$ref":"#/components/schemas/ExternalLastClosedDto"}]},"closed":{"nullable":true,"description":"The closed hours so far, accumulated (what the board’s bar wears); null before the first hour ends.","type":"object","allOf":[{"$ref":"#/components/schemas/ExternalClosedHoursDto"}]},"typing":{"$ref":"#/components/schemas/ExternalBoardTypingDto"}},"required":["moduleId","name","produced","goal","efficiency","source","recordedToday","lastClosed","closed","typing"]},"ExternalModuleDto":{"type":"object","properties":{"id":{"type":"integer","description":"The `moduleId` the módulo routes take."},"name":{"type":"string","nullable":true},"position":{"type":"integer","description":"Its place on the floor, top to bottom (0-based)."},"stationCount":{"type":"integer","description":"How many «puestos» (stations) the módulo has."},"colorHue":{"type":"integer","nullable":true,"minimum":1,"maximum":12,"description":"The thread colour the módulo wears on every dashboard screen, one of the dashboard’s 12; null when it wears the automatic one."}},"required":["id","name","position","stationCount","colorHue"]},"ExternalModuleEfficiencyDto":{"type":"object","properties":{"module":{"description":"The «módulo» (production line) the answer is about.","allOf":[{"$ref":"#/components/schemas/ExternalModuleRefDto"}]},"from":{"type":"string","description":"The first plant day asked, `YYYY-MM-DD`: the `from` of the request."},"to":{"type":"string","description":"The last plant day asked, `YYYY-MM-DD`: the `to` of the request."},"days":{"description":"One row per plant day the «módulo» worked in the range, oldest first. A day with no work is no row.","type":"array","items":{"$ref":"#/components/schemas/ExternalEfficiencyDayDto"}}},"required":["module","from","to","days"]},"ExternalModuleEfficiencyRowDto":{"type":"object","properties":{"moduleId":{"type":"number","nullable":true,"description":"The «módulo»’s id, as `listModules` returns it. Null when more than one «módulo» of that day shares this row’s name: the row then adds them up, as the dashboard’s table does (`getEfficiency` with `groupBy=module` keeps them apart)."},"moduleName":{"type":"string","nullable":true,"description":"The «módulo»’s name. The table groups by name: two «módulos» that share one are one row."},"units":{"type":"number","description":"Units made, in pieces. Every «puesto» counts, a «puesto de salida» or not."},"goal":{"type":"number","description":"The day’s «meta», in units: the sum of each «puesto»’s «meta» for each hour it worked."},"efficiency":{"type":"number","nullable":true,"description":"«Eficiencia»: this row’s `units` × 100 ÷ its `goal`, 2 decimals. A percentage, not a 0–1 ratio: 100 is exactly the goal and it can pass 100. Null when the goal is 0."},"pausedMinutes":{"type":"number","description":"Minutes the «módulo»’s «metas» lost that day to «pausas» already closed: their part inside the «jornada» and outside «descansos». 0 with none."},"pauses":{"type":"number","description":"How many «pausas» started at the «módulo»’s «puestos» that day, open ones included. 0 with none."}},"required":["moduleId","moduleName","units","goal","efficiency","pausedMinutes","pauses"]},"ExternalModuleHistoryHourDto":{"type":"object","properties":{"hour":{"type":"integer","minimum":0,"maximum":23,"description":"Clock hour of the plant day, 0–23 on the plant’s own clock: the hour that starts at it (9 is 09:00–09:59)."},"units":{"type":"number","description":"Units made in the hour by every «puesto» of the answer, in pieces."},"goal":{"type":"number","description":"The hour’s «meta», in units: the sum of the «meta» of each «puesto» that worked in it."},"efficiency":{"type":"number","nullable":true,"description":"«Eficiencia»: this row’s `units` × 100 ÷ its `goal`, 2 decimals. A percentage, not a 0–1 ratio: 100 is exactly the goal and it can pass 100. Null when the goal is 0."},"billedAmount":{"type":"number","description":"The hour’s «Facturación»: the sum of its rows’ `billedAmount`, 2 decimals; 0 when no «puesto de salida» worked in it. An amount in the currency this answer’s `currency` names, in whole currency units with up to 2 decimals (pesos or euros, never cents)."},"secondsPerUnit":{"type":"number","nullable":true,"description":"Average seconds between consecutive «marcas», as the app timed them: total seconds ÷ units, 2 decimals. Null with no units."},"pausedMinutes":{"type":"number","description":"Minutes the hour’s «meta» lost to «pausas» already closed: their part inside the «jornada» and outside «descansos». 0 with none."},"pauses":{"type":"number","description":"How many «pausas» started in the hour, open ones included. 0 with none."},"rows":{"description":"One row per «puesto» and «referencia» worked in the hour. Empty for an hour that only had «pausas».","type":"array","items":{"$ref":"#/components/schemas/ExternalModuleHistoryRowDto"}}},"required":["hour","units","goal","efficiency","billedAmount","secondsPerUnit","pausedMinutes","pauses","rows"]},"ExternalModuleHistoryRowDto":{"type":"object","properties":{"operationId":{"type":"integer","description":"The «puesto» (station) this row is about. A «puesto» is called `operation` in ids and `station` in paths: this is the `id` `listStations` returns and the `stationId` of the station routes."},"operationName":{"type":"string","nullable":true,"description":"The «puesto»’s name as it was stored with that hour: the «operación» (sewing task) done at it, such as «pegar cuello». Null when it had none."},"operatorId":{"type":"number","nullable":true,"description":"The «operario» who made these units: the `id` of one from `listOperators`. When two shared the «puesto» in the hour, one of them. Null when nobody was seated."},"referenceId":{"type":"number","nullable":true,"description":"The «referencia» (the order lot, also called «OC» or «corte») the units were made on: its `id` in the «Programa». Null when the «puesto» had none."},"referenceName":{"type":"string","nullable":true,"description":"That «referencia»’s name as it was stored with the hour. Null when the «puesto» had none."},"units":{"type":"number","description":"Units this «puesto» made on this «referencia» in the hour, in pieces."},"goal":{"type":"number","description":"The «puesto»’s «meta» for the hour, in units: its «meta por hora» as it stood then, already shortened for «descansos», «pausas» and the edges of the «jornada»."},"efficiency":{"type":"number","nullable":true,"description":"«Eficiencia»: this row’s `units` × 100 ÷ its `goal`, 2 decimals. A percentage, not a 0–1 ratio: 100 is exactly the goal and it can pass 100. Null when the goal is 0."},"isBilling":{"type":"boolean","description":"True when the «puesto» was a «puesto de salida» in that hour (its setting «Reporte y facturación»): the one whose units count as the «módulo»’s output and are billed. Only such a row carries money."},"billedAmount":{"type":"number","nullable":true,"description":"What this row bills: its `units` × the price per unit of its «referencia», 2 decimals. An amount in the currency this answer’s `currency` names, in whole currency units with up to 2 decimals (pesos or euros, never cents). Null on a «puesto» that is not «de salida»."}},"required":["operationId","operationName","operatorId","referenceId","referenceName","units","goal","efficiency","isBilling","billedAmount"]},"ExternalModuleRefDto":{"type":"object","properties":{"id":{"type":"number","description":"The «módulo»’s id, as `listModules` returns it."},"name":{"type":"string","nullable":true,"description":"The «módulo»’s name. Null when it has none."}},"required":["id","name"]},"ExternalModuleRestTimesDto":{"type":"object","properties":{"moduleId":{"type":"integer","description":"The módulo’s id."},"moduleName":{"type":"string","nullable":true},"restTimes":{"description":"The módulo’s «descansos», every weekday alike, by start: no goal is owed inside one.","type":"array","items":{"$ref":"#/components/schemas/ExternalRestTimeDto"}}},"required":["moduleId","moduleName","restTimes"]},"ExternalMonthDayDto":{"type":"object","properties":{"day":{"type":"string","description":"Plant day, `YYYY-MM-DD` on the plant’s own clock."},"units":{"type":"number","description":"Units made, in pieces. Every «puesto» counts, a «puesto de salida» or not."},"goal":{"type":"number","description":"The day’s «meta», in units: the sum of each «puesto»’s «meta» for each hour it worked."},"efficiency":{"type":"number","nullable":true,"description":"«Eficiencia»: this row’s `units` × 100 ÷ its `goal`, 2 decimals. A percentage, not a 0–1 ratio: 100 is exactly the goal and it can pass 100. Null when the goal is 0."},"billedAmount":{"type":"number","nullable":true,"description":"The day’s «Facturación»: the units of the «puestos de salida» at the price each was billed at, rounded to 2 decimals once over the day. An amount in the currency this answer’s `currency` names, in whole currency units with up to 2 decimals (pesos or euros, never cents). Null, not 0, on a day nothing was billed."}},"required":["day","units","goal","efficiency","billedAmount"]},"ExternalMonthDto":{"type":"object","properties":{"currency":{"type":"string","description":"ISO 4217 code of the currency every money figure of this answer is in. It is the plant’s, set by its «país»: `EUR` for a plant in España, `COP` for every other."},"days":{"description":"The 30 plant days up to the plant’s today, today included, oldest first. A day with no work is no row.","type":"array","items":{"$ref":"#/components/schemas/ExternalMonthDayDto"}}},"required":["currency","days"]},"ExternalMyUsageDto":{"type":"object","properties":{"from":{"type":"string","format":"date","example":"2026-10-12","description":"The first day the figures count, `YYYY-MM-DD`, in UTC, from its first hour."},"to":{"type":"string","format":"date","example":"2026-11-11","description":"The last day, `YYYY-MM-DD`, in UTC: today, up to the moment of this request."},"credential":{"$ref":"#/components/schemas/ExternalUsageCredentialDto"},"totals":{"description":"Everything this credential asked in the period. The last seconds before this request may not be counted yet.","allOf":[{"$ref":"#/components/schemas/ExternalUsageTotalsDto"}]},"endpoints":{"description":"By method and route, most requests first.","type":"array","items":{"$ref":"#/components/schemas/ExternalUsageEndpointDto"}},"days":{"description":"By day (UTC), oldest first. A day with no request is left out.","type":"array","items":{"$ref":"#/components/schemas/ExternalUsageDayDto"}}},"required":["from","to","credential","totals","endpoints","days"]},"ExternalOperationRefDto":{"type":"object","properties":{"id":{"type":"number"},"name":{"type":"string","nullable":true},"moduleId":{"type":"number","nullable":true},"moduleName":{"type":"string","nullable":true}},"required":["id","name","moduleId","moduleName"]},"ExternalOperatorDayDto":{"type":"object","properties":{"day":{"type":"string","description":"Plant day, YYYY-MM-DD."},"units":{"type":"number"},"goal":{"type":"number"},"efficiency":{"type":"number","nullable":true}},"required":["day","units","goal","efficiency"]},"ExternalOperatorDto":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string","nullable":true},"active":{"type":"boolean","description":"«Activo» or «Inactivo»."},"station":{"nullable":true,"description":"The puesto the operario is seated at; null when none.","type":"object","allOf":[{"$ref":"#/components/schemas/ExternalOperatorStationDto"}]}},"required":["id","name","active","station"]},"ExternalOperatorEfficiencyDto":{"type":"object","properties":{"operator":{"description":"The «operario» the answer is about.","allOf":[{"$ref":"#/components/schemas/ExternalOperatorRefDto"}]},"from":{"type":"string","description":"The first plant day asked, `YYYY-MM-DD`: the `from` of the request."},"to":{"type":"string","description":"The last plant day asked, `YYYY-MM-DD`: the `to` of the request."},"days":{"description":"One row per plant day they worked in the range, oldest first: their own «marcas» against their share of each «puesto»-hour’s «meta». A day with no work is no row.","type":"array","items":{"$ref":"#/components/schemas/ExternalEfficiencyDayDto"}}},"required":["operator","from","to","days"]},"ExternalOperatorHourDto":{"type":"object","properties":{"hour":{"type":"number"},"operationId":{"type":"number","nullable":true},"operationName":{"type":"string","nullable":true},"referenceId":{"type":"number","nullable":true},"referenceName":{"type":"string","nullable":true},"units":{"type":"number"},"goal":{"type":"number","description":"Their share of the puesto-hour’s meta, unrounded."},"efficiency":{"type":"number","nullable":true},"isBilling":{"type":"boolean"},"unitPrice":{"type":"number","nullable":true},"billedAmount":{"type":"number","nullable":true,"description":"Σ price of their own marcas there; null off a puesto de salida."},"secondsPerUnit":{"type":"number","nullable":true},"pausedMinutes":{"type":"number"},"pauses":{"type":"number"}},"required":["hour","operationId","operationName","referenceId","referenceName","units","goal","efficiency","isBilling","unitPrice","billedAmount","secondsPerUnit","pausedMinutes","pauses"]},"ExternalOperatorPresenceDto":{"type":"object","properties":{"operatorId":{"type":"integer","description":"The «operario»’s id: an `id` of `listOperators`, what `getOperator` takes."},"state":{"type":"string","enum":["active","background"],"description":"`active`: the «operario»’s app is open on the tablet («En la app»). `background`: the tablet is connected with the app out of sight or the screen locked. There is no offline value: an offline «operario» has no row."}},"required":["operatorId","state"]},"ExternalOperatorRefDto":{"type":"object","properties":{"id":{"type":"number","description":"The «operario»’s id, as `listOperators` returns it."},"name":{"type":"string","nullable":true,"description":"The «operario»’s name. A deleted «operario» reads «Operario eliminado»."}},"required":["id","name"]},"ExternalOperatorReportDto":{"type":"object","properties":{"operator":{"$ref":"#/components/schemas/ExternalOperatorRefDto"},"from":{"type":"string","description":"First day, YYYY-MM-DD."},"to":{"type":"string","description":"Last day, YYYY-MM-DD."},"days":{"description":"One row per day they worked in the range.","type":"array","items":{"$ref":"#/components/schemas/ExternalOperatorDayDto"}},"hours":{"description":"Hour by hour, when `from` = `to`; empty for a longer range (ask one day at a time).","type":"array","items":{"$ref":"#/components/schemas/ExternalOperatorHourDto"}}},"required":["operator","from","to","days","hours"]},"ExternalOperatorStationDto":{"type":"object","properties":{"id":{"type":"integer","description":"The puesto’s id."},"name":{"type":"string","nullable":true},"position":{"type":"integer","nullable":true},"moduleId":{"type":"integer","nullable":true},"modulePosition":{"type":"integer","nullable":true,"description":"The módulo’s place on the floor."}},"required":["id","name","position","moduleId","modulePosition"]},"ExternalOperatorsActivityDto":{"type":"object","properties":{"moduleId":{"type":"number","nullable":true,"description":"The «módulo» the answer was narrowed to: the `moduleId` asked. Null for the whole plant."},"operators":{"description":"One row per «operario» with production that day.","type":"array","items":{"$ref":"#/components/schemas/ExternalActivityOperatorDto"}},"totals":{"description":"The whole day, over what was asked.","allOf":[{"$ref":"#/components/schemas/ExternalActivityOperatorTotalsDto"}]},"day":{"type":"string","description":"Plant day, `YYYY-MM-DD` on the plant’s own clock."},"currency":{"type":"string","description":"ISO 4217 code of the currency every money figure of this answer is in. It is the plant’s, set by its «país»: `EUR` for a plant in España, `COP` for every other."}},"required":["moduleId","operators","totals","day","currency"]},"ExternalOrganizationAccessDto":{"type":"object","properties":{"organizationId":{"type":"number","description":"The organization the request acted on."},"roles":{"type":"array","items":{"type":"string","enum":["SUPER","COMPANY_ADMIN","ADMIN","MODERATOR","USER"]},"description":"The user’s dashboard roles, read from their row on this request. A write needs the role its dashboard route needs."},"scopes":{"type":"array","items":{"type":"string","enum":["read","program"]},"description":"The «Permisos» the credential holds."},"connectorEnabled":{"type":"boolean","description":"Always true in a 200: an organization that does not answer this credential answers 403 `PLAN_LIMIT`."}},"required":["organizationId","roles","scopes","connectorEnabled"]},"ExternalOrganizationDetailDto":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"country":{"type":"string","nullable":true,"enum":["CO","MX","PE","EC","VE","DO","GT","SV","HN","NI","PA","CR","AR","CL","ES"],"description":"«País», ISO 3166-1 alpha-2."},"countryName":{"type":"string","nullable":true,"example":"Colombia"},"department":{"type":"string","nullable":true},"city":{"type":"string","nullable":true},"timezone":{"type":"string","example":"America/Bogota","description":"«Zona horaria»: the plant’s clock. Every plant day and hour of this API is in it; the usage days of `getMyUsage` and every ISO 8601 instant are UTC."},"today":{"type":"string","nullable":true,"format":"date","description":"The plant’s today in its own clock, `YYYY-MM-DD`: the day to ask for when the usuario says «hoy»."},"currentHour":{"type":"integer","nullable":true,"minimum":0,"maximum":23,"description":"The hour the plant is living now, in its own clock."},"currency":{"$ref":"#/components/schemas/ExternalCurrencyDto"},"plan":{"$ref":"#/components/schemas/ExternalPlanDto"},"switches":{"$ref":"#/components/schemas/ExternalSwitchesDto"},"shift":{"description":"The «jornada»: when the plant works.","allOf":[{"$ref":"#/components/schemas/ExternalShiftDto"}]},"moduleRestTimes":{"description":"The «descansos» of every módulo, in the floor’s order (`listModules`).","type":"array","items":{"$ref":"#/components/schemas/ExternalModuleRestTimesDto"}},"categories":{"$ref":"#/components/schemas/ExternalCategoriesDto"}},"required":["id","name","country","countryName","department","city","timezone","today","currentHour","currency","plan","switches","shift","moduleRestTimes","categories"]},"ExternalOrganizationDto":{"type":"object","properties":{"id":{"type":"number","description":"The `organizationId` of every organization route of this API."},"name":{"type":"string"},"connectorEnabled":{"type":"boolean","description":"Whether the organization answers this credential: a connected app needs «Conectar con IA» (Plan Pro), an API token reaches Light and Pro. Without it every route of the organization answers 403 `PLAN_LIMIT`."}},"required":["id","name","connectorEnabled"]},"ExternalOrganizationsPageDto":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/ExternalOrganizationDto"}},"page":{"type":"number","description":"1-based."},"total":{"type":"number","description":"Every reachable organization that matches `search`, across pages."}},"required":["items","page","total"]},"ExternalPauseRowDto":{"type":"object","properties":{"pauseId":{"type":"string","format":"uuid","description":"The «pausa»’s id, stable across calls."},"day":{"type":"string","description":"The plant day the «pausa» started on, `YYYY-MM-DD` on the plant’s own clock."},"moduleId":{"type":"integer","description":"The «módulo» of the «puesto» it was taken at, as `listModules` returns it."},"moduleName":{"type":"string","nullable":true,"description":"That «módulo»’s name."},"operationId":{"type":"integer","description":"The «puesto» (station) this row is about. A «puesto» is called `operation` in ids and `station` in paths: this is the `id` `listStations` returns and the `stationId` of the station routes."},"operationName":{"type":"string","description":"The «puesto»’s name when the «pausa» started."},"operationPosition":{"type":"integer","minimum":0,"description":"The «puesto»’s place in its «módulo», raw and 0-based: the dashboard prints it + 1 («puesto 1» is 0)."},"operatorId":{"type":"integer","nullable":true,"description":"The «operario» who took the «pausa»: the `id` of one from `listOperators`. Null when unknown."},"operatorName":{"type":"string","nullable":true,"description":"That «operario»’s name; a deleted one reads «Operario eliminado». Null when unknown."},"startedAt":{"type":"string","format":"date-time","description":"When it started, as an instant: ISO 8601 in UTC."},"endedAt":{"type":"string","nullable":true,"format":"date-time","description":"When it ended, as an instant: ISO 8601 in UTC. Null while the «pausa» is open."},"startMinute":{"type":"integer","minimum":0,"maximum":1439,"description":"The minute of the plant day it started, on the plant’s own clock: 0 is 00:00, 540 is 09:00."},"endMinute":{"type":"integer","nullable":true,"minimum":0,"maximum":1440,"description":"The minute of the plant day it ended, on the plant’s own clock (1440 is the end of the day). Null while open."},"durationMinutes":{"type":"integer","nullable":true,"description":"The whole «pausa» by the clock, in minutes: `endMinute` − `startMinute`. Null while open."},"minutes":{"type":"integer","nullable":true,"description":"The minutes the «pausa» took off the «puesto»’s «metas»: the part of it inside the «jornada» and outside its «descansos». It is what the reports’ `pausedMinutes` add up; `summary.minutes` adds up `durationMinutes` instead. Null while open or not yet counted."},"endedBy":{"nullable":true,"enum":["operator","admin","shift_end","operator_left","disabled","plan","superseded","day_end"],"type":"string","description":"Why the «pausa» ended: `operator` («Reanudar» on the app), `admin` (ended from the board), `shift_end` (end of the «jornada»), `day_end` (end of the plant day), `operator_left` (the «operario» left the «puesto»), `disabled` («Habilitar pausas» switched off), `plan` (the organization lost the plan feature), `superseded` (a newer «pausa» at the same «puesto»). Null while open."},"endedByName":{"type":"string","nullable":true,"description":"The administrador who ended it from the board, when `endedBy` is `admin`. Null otherwise."}},"required":["pauseId","day","moduleId","moduleName","operationId","operationName","operationPosition","operatorId","operatorName","startedAt","endedAt","startMinute","endMinute","durationMinutes","minutes","endedBy","endedByName"]},"ExternalPauseSpanDto":{"type":"object","properties":{"pauseId":{"type":"string","format":"uuid","description":"The «pausa»’s id, the same one `getPauses` lists it under (`rows[].pauseId`)."},"startMinute":{"type":"integer","minimum":0,"maximum":1439,"description":"The minute of the plant day it started, on the plant’s own clock: 0 is 00:00, 540 is 09:00."},"endMinute":{"type":"integer","nullable":true,"minimum":0,"maximum":1440,"description":"The minute of the plant day it ended, on the plant’s own clock (1440 is the end of the day). Null while the «pausa» is open."},"durationMinutes":{"type":"integer","nullable":true,"description":"The whole «pausa», in minutes: `endMinute` − `startMinute`. Null while open."},"operatorId":{"type":"integer","nullable":true,"description":"The «operario» who took the «pausa»: the `id` of one from `listOperators`. Null when unknown."},"operatorName":{"type":"string","nullable":true,"description":"That «operario»’s name; a deleted one reads «Operario eliminado». Null when unknown."},"endedBy":{"nullable":true,"enum":["operator","admin","shift_end","operator_left","disabled","plan","superseded","day_end"],"type":"string","description":"Why the «pausa» ended: `operator` («Reanudar» on the app), `admin` (ended from the board), `shift_end` (end of the «jornada»), `day_end` (end of the plant day), `operator_left` (the «operario» left the «puesto»), `disabled` («Habilitar pausas» switched off), `plan` (the organization lost the plan feature), `superseded` (a newer «pausa» at the same «puesto»). Null while open."},"endedByName":{"type":"string","nullable":true,"description":"The administrador who ended it from the board, when `endedBy` is `admin`. Null otherwise."}},"required":["pauseId","startMinute","endMinute","durationMinutes","operatorId","operatorName","endedBy","endedByName"]},"ExternalPausesReportDto":{"type":"object","properties":{"from":{"type":"string","description":"The first plant day asked, `YYYY-MM-DD`: the `from` of the request."},"to":{"type":"string","description":"The last plant day asked, `YYYY-MM-DD`: the `to` of the request."},"rows":{"description":"One row per «pausa», newest first, at most 2000; `truncated` says when the range holds more.","type":"array","items":{"$ref":"#/components/schemas/ExternalPauseRowDto"}},"summary":{"description":"The figures over every «pausa» of the range and filters, never only the rows sent.","allOf":[{"$ref":"#/components/schemas/ExternalPausesSummaryDto"}]},"truncated":{"type":"boolean","description":"True when the range holds more «pausas» than `rows` carries: ask a shorter range or filter by «módulo» or «operario» to list them all. `summary` is complete either way."}},"required":["from","to","rows","summary","truncated"]},"ExternalPausesSummaryDto":{"type":"object","properties":{"count":{"type":"integer","description":"How many «pausas» the range holds, open ones included."},"openCount":{"type":"integer","description":"How many of them are still open."},"minutes":{"type":"integer","description":"Total minutes of the closed «pausas» by the clock: the sum of each one’s `durationMinutes`."},"averageMinutes":{"type":"number","nullable":true,"description":"Average minutes per closed «pausa» by the clock (`minutes` ÷ the closed ones), 2 decimals. Null with none closed."},"longestMinutes":{"type":"integer","description":"The longest closed «pausa» by the clock, in minutes. 0 with none closed."},"operators":{"type":"integer","description":"How many different «operarios» took a «pausa» in the range."}},"required":["count","openCount","minutes","averageMinutes","longestMinutes","operators"]},"ExternalPlanDetailDto":{"type":"object","properties":{"tier":{"type":"string","enum":["light","pro"],"description":"The «Plan» the organization’s features and limits follow. `light`: the live floor and the «Programa»; whether it keeps any report follows `planVersion`. `pro`: every report, «Facturación», «Pausas» and «Conectar con IA» too. A trial reads as the plan it is a trial of."},"planVersion":{"type":"integer","enum":[1,2],"description":"The «versión del plan» the organization was created on, never changed by a switch of plan. It matters on Light only. `1`: its Light keeps `features.reports`. `2`: its Light has no report at all. Pro is the same on both."},"features":{"description":"What the plan allows: one boolean per plan feature (`true`: the routes behind it answer; `false`: they answer 403 `PLAN_LIMIT` with that key as `feature`), and `maxReferencesPerModule`.","allOf":[{"$ref":"#/components/schemas/ExternalPlanFeatureFlagsDto"}]},"rateLimits":{"description":"The rate limits this API counts one credential against in this organization, in requests per window. Past one, the answer is 429 `RATE_LIMITED` with `Retry-After`.","allOf":[{"$ref":"#/components/schemas/ExternalPlanRateLimitsDto"}]}},"required":["tier","planVersion","features","rateLimits"]},"ExternalPlanDto":{"type":"object","properties":{"tier":{"type":"string","enum":["light","pro"]},"features":{"$ref":"#/components/schemas/ExternalPlanFeaturesDto"}},"required":["tier","features"]},"ExternalPlanFeatureFlagsDto":{"type":"object","properties":{"reports":{"type":"boolean","description":"«Clasificación», «reporte por operación», plant «eficiencia» and reports: the analytics day report, efficiency and leaderboard tools."},"billingAnalytics":{"type":"boolean","description":"«Facturación»: piece-rate billing figures and «Precio»."},"moduleReports":{"type":"boolean","description":"«Reportes por módulo»: the hour-by-hour board of a módulo (`getHoursBoard`)."},"operatorReports":{"type":"boolean","description":"The per-operario report."},"analytics":{"type":"boolean","description":"«Analítica»: the per-tap event log views."},"pace":{"type":"boolean","description":"«Eficiencia» and «ritmo» tiles."},"rework":{"type":"boolean","description":"«Reproceso» puestos."},"pauses":{"type":"boolean","description":"«Pausas» on the tablet and in the reports."},"operatorPicksReference":{"type":"boolean","description":"«El operario escoge la referencia» and the «Subprocesos» of a lot."},"referenceColor":{"type":"boolean","description":"The referencia colour field."},"aiConnector":{"type":"boolean","description":"«Conectar con IA»: an AI app can be connected to the organization (OAuth). Without it a connected app answers 403 `PLAN_LIMIT`; a «token de API» still reads the organization."},"maxReferencesPerModule":{"type":"integer","nullable":true,"description":"Referencias a módulo’s «Programa» may hold; null when the plan sets no limit."},"publicApi":{"type":"boolean","description":"«API externa» with a «token de API»: the organization can be read with one."}},"required":["reports","billingAnalytics","moduleReports","operatorReports","analytics","pace","rework","pauses","operatorPicksReference","referenceColor","aiConnector","maxReferencesPerModule","publicApi"]},"ExternalPlanFeaturesDto":{"type":"object","properties":{"reports":{"type":"boolean","description":"«Clasificación», «reporte por operación», plant «eficiencia» and reports: the analytics day report, efficiency and leaderboard tools."},"billingAnalytics":{"type":"boolean","description":"«Facturación»: piece-rate billing figures and «Precio»."},"moduleReports":{"type":"boolean","description":"«Reportes por módulo»: the hour-by-hour board of a módulo (`getHoursBoard`)."},"operatorReports":{"type":"boolean","description":"The per-operario report."},"analytics":{"type":"boolean","description":"«Analítica»: the per-tap event log views."},"pace":{"type":"boolean","description":"«Eficiencia» and «ritmo» tiles."},"rework":{"type":"boolean","description":"«Reproceso» puestos."},"pauses":{"type":"boolean","description":"«Pausas» on the tablet and in the reports."},"operatorPicksReference":{"type":"boolean","description":"«El operario escoge la referencia» and the «Subprocesos» of a lot."},"referenceColor":{"type":"boolean","description":"The referencia colour field."},"aiConnector":{"type":"boolean","description":"«Conectar con IA» (Plan Pro): an AI app can be connected to the organization. True whenever a connected app reads this; false on a Light organization, which only an API token reaches."},"maxReferencesPerModule":{"type":"integer","nullable":true,"description":"Referencias a módulo’s «Programa» may hold; null when the plan sets no limit."}},"required":["reports","billingAnalytics","moduleReports","operatorReports","analytics","pace","rework","pauses","operatorPicksReference","referenceColor","aiConnector","maxReferencesPerModule"]},"ExternalPlanRateLimitsDto":{"type":"object","properties":{"windowSeconds":{"type":"integer","example":60,"description":"The length of every window below, in seconds. A window is fixed: its count restarts when it ends (`X-RateLimit-Reset`)."},"organization":{"type":"integer","description":"Requests per window one credential may make in this organization, on any route: the ordinary budget, set by `tier`."},"heavy":{"type":"integer","description":"Requests per window one credential may make in this organization on the heavy routes (the range reads, the whole floor `getFloor`, a month, the «Pausas» report). A heavy request spends one of this budget and one of `organization`. The same on Light and on Pro."},"credential":{"type":"integer","description":"Requests per window one credential may make across every organization it reaches, added up."},"account":{"type":"integer","description":"Requests per window one credential may make on the routes that name no organization (`getMe`, `getMyUsage`, `listOrganizations`), counted apart from the three above."}},"required":["windowSeconds","organization","heavy","credential","account"]},"ExternalPlantClosedDto":{"type":"object","properties":{"average":{"type":"number","nullable":true,"description":"The simple average of the módulos’ closed-hours figures; null before the first hour ends anywhere."},"modules":{"type":"integer"}},"required":["average","modules"]},"ExternalPlantDayStatusDto":{"type":"object","properties":{"produced":{"type":"integer"},"goal":{"type":"integer"},"efficiency":{"type":"number","nullable":true,"description":"The plant’s «eficiencia» (%), weighted by units and goals."},"modules":{"type":"integer","description":"Módulos on the board."},"producing":{"type":"integer","description":"Módulos producing."},"lastClosed":{"nullable":true,"type":"object","allOf":[{"$ref":"#/components/schemas/ExternalLastClosedDto"}]},"closed":{"$ref":"#/components/schemas/ExternalPlantClosedDto"}},"required":["produced","goal","efficiency","modules","producing","lastClosed","closed"]},"ExternalPresenceDto":{"type":"object","properties":{"at":{"type":"string","format":"date-time","description":"The instant the tablets were read, ISO 8601 in UTC (not on the plant’s clock)."},"operators":{"description":"Only the «operarios» with a connected tablet, ordered by `operatorId`. An «operario» missing here is offline; empty when no tablet is connected.","type":"array","items":{"$ref":"#/components/schemas/ExternalOperatorPresenceDto"}}},"required":["at","operators"]},"ExternalProductionPageDto":{"type":"object","properties":{"from":{"type":"string","description":"First day of the range, YYYY-MM-DD."},"to":{"type":"string","description":"Last day of the range, YYYY-MM-DD."},"groupBy":{"enum":["day","module","operator","operation","reference","category"],"type":"string","description":"The grouping asked. Under a filter, `day` rows also name the módulo, operario or puesto they are about."},"page":{"type":"number","description":"1-based."},"pageSize":{"type":"number","description":"Rows per page."},"total":{"type":"number","description":"Rows of the whole range, across pages."},"items":{"type":"array","items":{"$ref":"#/components/schemas/ExternalProductionRowDto"}}},"required":["from","to","groupBy","page","pageSize","total","items"]},"ExternalProductionRowDto":{"type":"object","properties":{"day":{"type":"string","description":"Plant day, YYYY-MM-DD."},"module":{"$ref":"#/components/schemas/ExternalModuleRefDto"},"reference":{"nullable":true,"description":"With `groupBy=reference`; null for units typed or marked with no OC («Sin OC»).","type":"object","allOf":[{"$ref":"#/components/schemas/ExternalReferenceRefDto"}]},"category":{"$ref":"#/components/schemas/ExternalCategoryRefDto"},"units":{"type":"number","description":"«Producido»: the «conteo manual» of every typed hour, the puestos de salida’s marcas of every other, 0 for an hour folded into a typed «Extra»."}},"required":["day","units"]},"ExternalRateLimitDto":{"type":"object","properties":{"limit":{"type":"number","description":"Requests per minute of the budget this request was counted against."},"remaining":{"type":"number","description":"Requests left in that budget’s window, this one counted."},"reset":{"type":"number","description":"When that window ends, in epoch seconds."}},"required":["limit","remaining","reset"]},"ExternalRateLimitedDto":{"type":"object","required":["statusCode","code","message","retryAfterSeconds"],"properties":{"statusCode":{"type":"integer","enum":[429]},"code":{"type":"string","enum":["RATE_LIMITED"]},"message":{"type":"string","description":"In Spanish, for the user."},"retryAfterSeconds":{"type":"integer","minimum":1,"description":"The `Retry-After` header’s value."}}},"ExternalReactivateReferenceDto":{"type":"object","properties":{"moduleId":{"type":"integer","minimum":1,"maximum":2147483647,"description":"The módulo it re-enters, at the end of its queue: the one it left or any other of the organization."},"note":{"type":"string","maxLength":500,"description":"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»."}},"required":["moduleId"]},"ExternalReferenceDayGoalDto":{"type":"object","properties":{"day":{"type":"string","format":"date","description":"The plant day it is about."},"goal":{"type":"integer","description":"Units per hour in force at the close of the day."},"steps":{"nullable":true,"description":"The rates by minute of day; null for one rate all day.","type":"array","items":{"$ref":"#/components/schemas/ExternalDayGoalStepDto"}}},"required":["day","goal","steps"]},"ExternalReferenceDeletePreviewDto":{"type":"object","properties":{"reference":{"$ref":"#/components/schemas/ExternalReferenceDto"},"deletable":{"type":"boolean","description":"Whether the delete can run. False: `refusal` says why (an OC of the «Red», or one outside its módulo’s «Programa»: in the «banco de espera» or closed), and no `confirmationToken` is issued."},"refusal":{"nullable":true,"type":"object","allOf":[{"$ref":"#/components/schemas/ExternalDeleteRefusalDto"}]},"clearsModule":{"type":"boolean","description":"True when deleting it empties its módulo’s «Programa»: then EVERY puesto of the módulo is cleared (operario unassigned, goal and counters zeroed). False: the puestos sewing it move to the next OC of the queue."},"stationsOnIt":{"type":"integer","description":"Puestos sewing this OC right now."},"confirmationToken":{"type":"string","nullable":true,"description":"Send it back, unchanged, to `deleteReference` once the usuario said yes. Valid 15 minutes, for this conexión, this organization and this OC as shown here."},"expiresAt":{"type":"string","nullable":true,"format":"date-time"}},"required":["reference","deletable","refusal","clearsModule","stationsOnIt","confirmationToken","expiresAt"]},"ExternalReferenceDeletedDto":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"orderNumber":{"type":"string","nullable":true},"moduleId":{"type":"integer","nullable":true},"moduleName":{"type":"string","nullable":true},"deleted":{"type":"boolean","enum":[true]}},"required":["id","name","orderNumber","moduleId","moduleName","deleted"]},"ExternalReferenceDto":{"type":"object","properties":{"id":{"type":"integer","description":"The `referenceId` the referencia routes take."},"status":{"enum":["in_program","on_hold","finished"],"type":"string"},"moduleId":{"type":"integer","nullable":true,"description":"The módulo it is queued in, or left from while on hold."},"moduleName":{"type":"string","nullable":true},"position":{"type":"integer","description":"Its place in the módulo’s «Programa», 0-based."},"name":{"type":"string","description":"«Referencia»: the style."},"orderNumber":{"type":"string","nullable":true,"description":"«Corte (OC)»: the cut order, e.g. OC-25020."},"garment":{"type":"string","nullable":true,"description":"«Categoría»: the garment (blusa, pantalón…)."},"companyAdminId":{"type":"integer","nullable":true,"description":"The brand («cuenta de marca») the OC is for; null for the plant’s own clients or still unset."},"otrosClientes":{"type":"boolean","description":"«Otros clientes»: explicitly for the plant’s own clients, never a brand’s."},"networkOrderId":{"type":"integer","nullable":true,"description":"The brand’s «OC de la red» it was dispatched from; null for the plant’s own OCs."},"priority":{"type":"boolean","description":"«OC prioritaria»."},"total":{"type":"integer","nullable":true,"description":"«Total de unidades»; null for a lot without units (queued by an operario)."},"produced":{"type":"integer","description":"Units produced to date: the typed count where an hour was typed, the output puestos’ taps where it was not."},"sacrificed":{"type":"integer","description":"«Descuento por sacrificio»: units written off; they close the cut but are never production."},"pendingUnits":{"type":"integer","nullable":true,"description":"«Saldo»: units still to make, total − produced − sacrificed, never below 0; null for a lot without units."},"goal":{"type":"integer","description":"The base «Meta por hora»."},"dayGoal":{"nullable":true,"description":"«Metas del día»: today’s override of the hourly goal; null when today has none.","type":"object","allOf":[{"$ref":"#/components/schemas/ExternalReferenceDayGoalDto"}]},"price":{"type":"number","nullable":true,"description":"«Precio por unidad», in the organization’s currency."},"color":{"type":"string","nullable":true},"spliceDate":{"type":"string","nullable":true,"format":"date-time","description":"«F. Empalme»: when it is due to start on its módulo."},"dueDate":{"type":"string","nullable":true,"format":"date-time","description":"«F. Compromiso»: when it is promised to the client."},"startedAt":{"type":"string","nullable":true,"format":"date-time","description":"When its first unit was counted."},"finishedAt":{"type":"string","nullable":true,"format":"date-time","description":"When it was finished."},"holdSince":{"type":"string","nullable":true,"format":"date-time"},"holdReason":{"type":"string","nullable":true,"description":"Why it was paused: tela, insumo, calidad, otro."},"continuity":{"type":"boolean","description":"«Continuidad»: the same garment as the one before it, so the splice needs less preparation."},"splitGroup":{"type":"string","nullable":true,"description":"A cut divided between two módulos: both halves share this key."},"splitMaster":{"type":"boolean","description":"The original half of a split."},"sacrificeNote":{"type":"string","nullable":true},"sacrificedAt":{"type":"string","nullable":true,"format":"date-time"},"sacrificedByName":{"type":"string","nullable":true},"networkClosedAt":{"type":"string","nullable":true,"format":"date-time","description":"When a devolución or a traslado of the «Red» closed this brand OC."},"anticipated":{"type":"boolean","description":"«OC anticipada»: a brand’s cut that has not reached the plant yet."},"expectedArrival":{"type":"string","nullable":true,"format":"date","description":"«Llega el»: the day an anticipada is expected."},"arrivedAt":{"type":"string","nullable":true,"format":"date-time","description":"When the plant marked «Llegó»."},"step1Name":{"type":"string","nullable":true,"description":"«Subproceso» 1 of the lot."},"step1Goal":{"type":"integer","nullable":true},"step2Name":{"type":"string","nullable":true,"description":"«Subproceso» 2 of the lot."},"step2Goal":{"type":"integer","nullable":true},"source":{"type":"string","nullable":true,"enum":["operator"],"description":"`operator` when an operario queued it from the tablet; null otherwise."}},"required":["id","status","moduleId","moduleName","name","orderNumber","garment","companyAdminId","otrosClientes","networkOrderId","priority","total","produced","sacrificed","pendingUnits","goal","price","color","startedAt","finishedAt","holdSince","holdReason","splitGroup","splitMaster","sacrificeNote","sacrificedAt","sacrificedByName","anticipated","expectedArrival","step1Name","step1Goal","step2Name","step2Goal","source"]},"ExternalReferencePageDto":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/ExternalReferenceDto"}},"page":{"type":"integer","description":"1-based."},"total":{"type":"integer","description":"Every referencia that matches, across pages."}},"required":["items","page","total"]},"ExternalReferenceRefDto":{"type":"object","properties":{"id":{"type":"number"},"name":{"type":"string","nullable":true},"orderNumber":{"type":"string","nullable":true,"description":"The corte (OC) number."},"garment":{"type":"string","nullable":true,"description":"Its categoría, as typed on the OC."}},"required":["id","name","orderNumber","garment"]},"ExternalReferencesAddedDto":{"type":"object","properties":{"created":{"type":"array","items":{"$ref":"#/components/schemas/ExternalReferenceDto"}},"failed":{"type":"array","items":{"$ref":"#/components/schemas/ExternalCatalogueItemFailureDto"}}},"required":["created","failed"]},"ExternalReorderProgramDto":{"type":"object","properties":{"references":{"minItems":1,"maxItems":1000,"description":"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.","type":"array","items":{"type":"integer"}},"note":{"type":"string","maxLength":500,"description":"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»."}},"required":["references"]},"ExternalRestTimeDto":{"type":"object","properties":{"id":{"type":"integer"},"startHour":{"type":"integer","minimum":0,"maximum":23},"startMin":{"type":"integer","minimum":0,"maximum":59},"endHour":{"type":"integer","minimum":0,"maximum":23},"endMin":{"type":"integer","minimum":0,"maximum":59}},"required":["id","startHour","startMin","endHour","endMin"]},"ExternalSacrificeReferenceDto":{"type":"object","properties":{"sacrificed":{"type":"integer","minimum":0,"maximum":999999,"description":"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":{"type":"string","maxLength":255,"description":"Why: «tela con defecto», «faltante de corte»…"}},"required":["sacrificed"]},"ExternalSetDayGoalsDto":{"type":"object","properties":{"goal":{"type":"integer","minimum":1,"maximum":100000,"description":"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":{"nullable":true,"description":"The day’s rates by minute of day, at most 48, distinct minutes. Omitted or null: one rate, `goal`, all day.","type":"array","items":{"$ref":"#/components/schemas/ExternalDayGoalStepInputDto"}}},"required":["goal"]},"ExternalSettingsDto":{"type":"object","properties":{"timezone":{"type":"string","example":"America/Bogota","description":"«Zona horaria»: the plant’s clock, as an IANA time zone name. The plant’s own fields are on it, never UTC: every plant day (`day`, `today`, the `from` and `to` of a range), every `hour` of a plant day and the minutes from midnight of the «jornada» and of a «descanso». Not on it: the usage days of `getMyUsage`, which are UTC days, and every ISO 8601 instant, such as the `at` of `getOperatorPresence`, which is UTC."},"today":{"type":"string","nullable":true,"format":"date","description":"The plant’s today on its own clock, `YYYY-MM-DD`: the day to send where a route takes a `day`. Null only when the clock could not be read."},"currentHour":{"type":"integer","nullable":true,"minimum":0,"maximum":23,"description":"The hour of the day the plant is living now on its own clock, 0 to 23 (14 is 14:00 to 14:59). Null only when the clock could not be read."},"switches":{"description":"The plant’s switches of «Configuración», each as it takes effect: stored on and allowed by the plan. A plant that never saved its settings answers the defaults.","allOf":[{"$ref":"#/components/schemas/ExternalSwitchesDto"}]},"shift":{"description":"The «jornada» (the working shift): its start, its end on each weekday, the «jornada continua» weekdays (no «descanso» applies), the «jornada especial» days and the days off. Times are minutes from midnight on the plant’s clock; weekdays are 0 Sunday to 6 Saturday. `configured: false` for a plant that never saved one.","allOf":[{"$ref":"#/components/schemas/ExternalShiftDto"}]}},"required":["timezone","today","currentHour","switches","shift"]},"ExternalShiftDto":{"type":"object","properties":{"configured":{"type":"boolean","description":"Whether the plant has set its «jornada». Without it nothing built on the shift (boards, goals by hour, the Programa’s dates) can be judged."},"startMin":{"type":"integer","nullable":true,"description":"When the «jornada» starts, minutes from midnight (330 = 05:30); null when not configured."},"endMinsByWeekday":{"type":"array","items":{"type":"integer","nullable":true},"description":"When it ends on each weekday, minutes from midnight, indexed 0 Sunday … 6 Saturday; null for a weekday the plant does not work. Empty when not configured."},"continuousDays":{"description":"«Jornada continua» weekdays (0 Sunday … 6 Saturday): no break of any módulo applies inside the shift.","type":"array","items":{"type":"integer"}},"specialDays":{"description":"«Jornada especial»: days the whole plant works until another hour, weekend or holiday included.","type":"array","items":{"$ref":"#/components/schemas/ExternalSpecialDayDto"}},"holidays":{"description":"Days off the plant added by hand, beyond the national holidays.","type":"array","items":{"type":"string","format":"date"}},"workedHolidays":{"description":"National holidays the plant works.","type":"array","items":{"type":"string","format":"date"}},"nationalHolidays":{"description":"The national holidays of the plant’s country (from its zona horaria) for this plant year and the next: a day not `worked` is a day off.","type":"array","items":{"$ref":"#/components/schemas/ExternalHolidayDto"}}},"required":["configured","startMin","endMinsByWeekday","continuousDays","specialDays","holidays","workedHolidays","nationalHolidays"]},"ExternalSpecialDayDto":{"type":"object","properties":{"day":{"type":"string","format":"date","example":"2026-12-19"},"endMin":{"type":"integer","description":"When the whole plant stops that day, minutes from midnight."},"reason":{"type":"string","nullable":true}},"required":["day","endMin","reason"]},"ExternalSplitReferenceDto":{"type":"object","properties":{"units":{"type":"integer","minimum":1,"maximum":1000000,"description":"Units for the NEW portion (a «-B» OC); the original keeps the rest."},"moduleId":{"type":"integer","minimum":1,"maximum":2147483647,"description":"The módulo the new portion goes to: never the original’s."},"note":{"type":"string","maxLength":500,"description":"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»."}},"required":["units","moduleId"]},"ExternalSplitReferenceResultDto":{"type":"object","properties":{"original":{"description":"The original, with the units it kept.","allOf":[{"$ref":"#/components/schemas/ExternalReferenceDto"}]},"portion":{"description":"The new «-B» portion, at the end of the target módulo.","allOf":[{"$ref":"#/components/schemas/ExternalReferenceDto"}]}},"required":["original","portion"]},"ExternalStationActivityDto":{"type":"object","properties":{"day":{"type":"string","description":"Plant day, YYYY-MM-DD."},"view":{"enum":["events","hourly","operators","consistency"],"type":"string"},"operationId":{"type":"number","nullable":true,"description":"The puesto `events` read."},"events":{"$ref":"#/components/schemas/ExternalEventsSummaryDto"},"hourly":{"$ref":"#/components/schemas/ExternalActivityHourlyDto"},"operators":{"$ref":"#/components/schemas/ExternalActivityOperatorsDto"},"consistency":{"$ref":"#/components/schemas/ExternalConsistencyDto"}},"required":["day","view"]},"ExternalStationDayReportDto":{"type":"object","properties":{"day":{"type":"string","description":"Plant day, `YYYY-MM-DD` on the plant’s own clock."},"operationId":{"type":"integer","description":"The «puesto» the answer is about: the `stationId` of the path. A «puesto» is called `operation` in ids and `station` in paths."},"currency":{"type":"string","description":"ISO 4217 code of the currency every money figure of this answer is in. It is the plant’s, set by its «país»: `EUR` for a plant in España, `COP` for every other."},"hours":{"description":"The day hour by hour, earliest first: one entry per clock hour the «puesto» had a «marca» or a «pausa». An hour with neither is no entry.","type":"array","items":{"$ref":"#/components/schemas/ExternalStationReportHourDto"}}},"required":["day","operationId","currency","hours"]},"ExternalStationDto":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string","description":"The «operación» sewn at this puesto."},"position":{"type":"integer","description":"Its place in the módulo (the board’s order)."},"moduleId":{"type":"integer"},"isBilling":{"type":"boolean","description":"«Reporte y facturación»: the output puesto whose units are the módulo’s production and its billing."},"isEntry":{"type":"boolean","description":"The line’s entry puesto: its units count garments going into the módulo."},"isRework":{"type":"boolean","description":"«Reproceso» puesto."},"operator":{"nullable":true,"description":"The operario seated at the puesto; null when it is empty.","type":"object","allOf":[{"$ref":"#/components/schemas/ExternalStationOperatorDto"}]},"operatorActive":{"type":"boolean","description":"The seated operario is working at the puesto."},"pausedAt":{"type":"string","nullable":true,"format":"date-time","description":"Since when the puesto is on a «pausa»; null when it is not."},"currentReference":{"nullable":true,"description":"The referencia (cut) the puesto is on now.","type":"object","allOf":[{"$ref":"#/components/schemas/ExternalStationReferenceDto"}]},"currentStep":{"type":"integer","enum":[0,1,2],"description":"The «Subproceso» the puesto is on: 0 the whole unit, 1 or 2 one of the lot’s steps."},"stepUnitsToday":{"description":"Today’s units of the current referencia per «Subproceso»; zeros while «El operario escoge la referencia» is off.","allOf":[{"$ref":"#/components/schemas/ExternalStepUnitsDto"}]},"goalReference":{"type":"integer","description":"The current referencia’s base hourly goal at this puesto."},"hourGoal":{"type":"integer","nullable":true,"description":"The goal of the hour in progress, prorated by its breaks; null when the puesto has not been stamped this hour."},"unitsThisHour":{"type":"integer","description":"Units made in the hour in progress (0 once the hour of the last count has passed, as the board shows it)."},"unitsToday":{"type":"integer","description":"Units made today at the puesto, every referencia (0 once its day has passed, as the board shows it)."},"color":{"type":"string","nullable":true,"enum":["GRAY","RED","YELLOW","GREEN"],"description":"The semáforo of the hour in progress at the last count; null when the puesto has not counted this hour."},"total":{"type":"integer","nullable":true,"description":"«Total de unidades» of the current referencia; null for a lot without units."},"totalCompleted":{"type":"integer","description":"Units of the current referencia made so far («ACUM»)."},"remaining":{"type":"integer","nullable":true,"description":"What is left of the current referencia («SALDO»); null for a lot without units."},"reworkCurrent":{"type":"integer","description":"«Reproceso» units of the hour."},"reworkTotal":{"type":"integer","description":"«Reproceso» units in all."}},"required":["id","name","position","moduleId","isBilling","isEntry","isRework","operator","operatorActive","pausedAt","currentReference","currentStep","stepUnitsToday","goalReference","hourGoal","unitsThisHour","unitsToday","color","total","totalCompleted","remaining","reworkCurrent","reworkTotal"]},"ExternalStationEventsDto":{"type":"object","properties":{"day":{"type":"string","description":"Plant day, `YYYY-MM-DD` on the plant’s own clock."},"operationId":{"type":"integer","description":"The «puesto» the answer is about: the `stationId` of the path. A «puesto» is called `operation` in ids and `station` in paths."},"currency":{"type":"string","description":"ISO 4217 code of the currency every money figure of this answer is in. It is the plant’s, set by its «país»: `EUR` for a plant in España, `COP` for every other."},"events":{"description":"The «puesto»’s «marcas» of the day, summarised from the «marcas» log.","allOf":[{"$ref":"#/components/schemas/ExternalEventsSummaryDto"}]}},"required":["day","operationId","currency","events"]},"ExternalStationOperatorDto":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string","nullable":true},"active":{"type":"boolean","description":"The operario’s own state, «Activo» or «Inactivo»."}},"required":["id","name","active"]},"ExternalStationPageDto":{"type":"object","properties":{"items":{"description":"The «puestos» of this page, at most 50, by «módulo» in the floor’s order and then in the board’s, each as the live board reads it now. Empty past the last page.","type":"array","items":{"$ref":"#/components/schemas/ExternalStationDto"}},"page":{"type":"integer","minimum":1,"description":"The page answered, 1-based: the `page` asked, or 1."},"total":{"type":"integer","minimum":0,"description":"How many «puestos» match in all, across every page: the last page is `ceil(total / 50)`."}},"required":["items","page","total"]},"ExternalStationReferenceDto":{"type":"object","properties":{"id":{"type":"integer","nullable":true},"name":{"type":"string","nullable":true},"stationGoal":{"type":"integer","nullable":true,"description":"This puesto’s own hourly goal for the referencia («meta individual»), which can differ from the referencia’s base `goal`."},"price":{"type":"number","nullable":true,"description":"The price per unit this puesto bills at; null on a puesto that does not bill."}},"required":["id","name","stationGoal","price"]},"ExternalStationReportHourDto":{"type":"object","properties":{"hour":{"type":"integer","minimum":0,"maximum":23,"description":"Clock hour of the plant day, 0–23 on the plant’s own clock: the hour that starts at it (9 is 09:00–09:59)."},"units":{"type":"number","description":"Units made in the hour, in pieces, by every «puesto» of the answer. In a «puesto»’s report with «El operario escoge la referencia» on: its complete units only."},"goal":{"type":"number","description":"The hour’s «meta», in units: the sum of the «meta» of each «puesto» that worked in it. In a «puesto»’s report with «El operario escoge la referencia» on: the sum of each step’s expected units."},"efficiency":{"type":"number","nullable":true,"description":"«Eficiencia»: this row’s `units` × 100 ÷ its `goal`, 2 decimals. A percentage, not a 0–1 ratio: 100 is exactly the goal and it can pass 100. Null when the goal is 0."},"billedAmount":{"type":"number","description":"The hour’s «Facturación»: the sum, over its rows on a «puesto de salida», of `units` × `unitPrice`, each rounded to 2 decimals; 0 with none. In the plant’s currency (the answer’s `currency` where it carries one, else `getOrganization` `currency.code`), in whole currency units, never cents."},"secondsPerUnit":{"type":"number","nullable":true,"description":"Average seconds between consecutive «marcas» in the hour, as the app timed them: total seconds ÷ units, 2 decimals. Null with no units."},"pausedMinutes":{"type":"number","description":"Minutes the hour’s «meta» lost to «pausas» already closed: their part inside the «jornada» and outside «descansos». 0 with none."},"pauses":{"type":"number","description":"How many «pausas» started in the hour, open ones included. 0 with none."},"rows":{"description":"One row per «referencia» the «puesto» worked in the hour (and per «Subproceso», with «El operario escoge la referencia» on). Empty for an hour that only had «pausas».","type":"array","items":{"$ref":"#/components/schemas/ExternalStationReportRowDto"}},"steps":{"description":"Only with «El operario escoge la referencia» on: the hour by «referencia» and «Subproceso». The hour’s `units` are then its complete units and `goal` the sum of each step’s expected units. Absent, not empty, with the switch off.","type":"array","items":{"$ref":"#/components/schemas/ExternalStationStepDto"}},"pauseSpans":{"description":"The «pausas» that started in the hour, open ones included, by start. Empty with none.","type":"array","items":{"$ref":"#/components/schemas/ExternalPauseSpanDto"}}},"required":["hour","units","goal","efficiency","billedAmount","secondsPerUnit","pausedMinutes","pauses","rows","pauseSpans"]},"ExternalStationReportRowDto":{"type":"object","properties":{"operationId":{"type":"number","description":"The «puesto» (station) this row is about. A «puesto» is called `operation` in ids and `station` in paths: this is the `id` `listStations` returns and the `stationId` of the station routes."},"operationName":{"type":"string","nullable":true,"description":"The «puesto»’s name as it was stored with that hour: the «operación» (sewing task) done at it, such as «pegar cuello». Null when it had none."},"operatorId":{"type":"number","nullable":true,"description":"The «operario» who made these units: the `id` of one from `listOperators`. Null when nobody was seated."},"operatorName":{"type":"string","nullable":true,"description":"That «operario»’s name. Null when nobody was seated."},"referenceId":{"type":"number","nullable":true,"description":"The «referencia» (the order lot, also called «OC» or «corte») the units were made on: its `id` in the «Programa». Null when the «puesto» had none."},"referenceName":{"type":"string","nullable":true,"description":"That «referencia»’s name as it was stored with the hour. Null when the «puesto» had none."},"units":{"type":"number","description":"Units the «puesto» made on this «referencia» in the hour, in pieces."},"goal":{"type":"number","description":"The «puesto»’s «meta» for the hour, in units: its «meta por hora» as it stood then, already shortened for «descansos», «pausas» and the edges of the «jornada»."},"efficiency":{"type":"number","nullable":true,"description":"«Eficiencia»: this row’s `units` × 100 ÷ its `goal`, 2 decimals. A percentage, not a 0–1 ratio: 100 is exactly the goal and it can pass 100. Null when the goal is 0."},"isBilling":{"type":"boolean","description":"True when the «puesto» was a «puesto de salida» in that hour (its setting «Reporte y facturación»): the one whose units count as the «módulo»’s output and are billed. Only such a row carries a price."},"unitPrice":{"type":"number","nullable":true,"description":"The price per unit of the row’s «referencia», at which its units are billed. In the plant’s currency (the answer’s `currency` where it carries one, else `getOrganization` `currency.code`), in whole currency units with up to 2 decimals, never cents. Null on a «puesto» that is not «de salida»."},"secondsPerUnit":{"type":"number","nullable":true,"description":"Average seconds between consecutive «marcas», as the app timed them: total seconds ÷ units, 2 decimals. Null with no units."},"step":{"type":"integer","enum":[0,1,2],"description":"Only with «El operario escoge la referencia» on: the «Subproceso» the units were marked on. 0 is the whole unit («unidad completa»); 1 and 2 are the two named steps the «referencia» defines."},"stepName":{"type":"string","nullable":true,"description":"Only with «El operario escoge la referencia» on: the step’s name on the «referencia»; null for the whole unit."}},"required":["operationId","operationName","operatorId","operatorName","referenceId","referenceName","units","goal","efficiency","isBilling","unitPrice","secondsPerUnit"]},"ExternalStationStepDto":{"type":"object","properties":{"referenceId":{"type":"number","nullable":true,"description":"The «referencia» (the order lot, also called «OC» or «corte») the units were made on: its `id` in the «Programa». Null when the «puesto» had none."},"referenceName":{"type":"string","nullable":true,"description":"That «referencia»’s name as it was stored with the hour. Null when the «puesto» had none."},"step":{"type":"integer","enum":[0,1,2],"description":"The «Subproceso»: 0 is the whole unit («unidad completa»); 1 and 2 are the two named steps the «referencia» defines."},"name":{"type":"string","nullable":true,"description":"The step’s name on the «referencia»; null for the whole unit."},"units":{"type":"number","description":"Units marked on this step in the hour, in pieces."},"goal":{"type":"number","description":"Expected units: the step’s «meta por hora» × the minutes the «puesto» worked on it in the hour ÷ 60, 2 decimals."},"efficiency":{"type":"number","nullable":true,"description":"«Eficiencia»: `units` × 100 ÷ `goal`. A percentage, not a 0–1 ratio: 100 is exactly the goal and it can pass 100. Null when the goal is 0. Also null when the step asked for less than half a unit."},"counts":{"type":"boolean","description":"True when its units are complete units (the whole unit and step 2), which the hour’s `units` add up; false for step 1, whose units are work in progress."}},"required":["referenceId","referenceName","step","name","units","goal","efficiency","counts"]},"ExternalStepUnitsDto":{"type":"object","properties":{"whole":{"type":"integer","description":"Units of the whole unit."},"step1":{"type":"integer","description":"Units of «Subproceso» 1."},"step2":{"type":"integer","description":"Units of «Subproceso» 2."}},"required":["whole","step1","step2"]},"ExternalSwitchesDto":{"type":"object","properties":{"pausesEnabled":{"type":"boolean","description":"«Habilitar pausas»: the tablets offer «Pausar». The effective flag: stored and allowed by the plan."},"operatorPicksReference":{"type":"boolean","description":"«El operario escoge la referencia»: the operario picks a referencia and a «Subproceso» on the tablet. The effective flag."},"showMoneyOnBoard":{"type":"boolean","description":"Whether the live board shows money on the floor. Reports are unaffected."}},"required":["pausesEnabled","operatorPicksReference","showMoneyOnBoard"]},"ExternalUpdateCatalogueEntryDto":{"type":"object","properties":{"name":{"type":"string","description":"«Referencia»: stored trimmed with inner whitespace collapsed; unique in\nthe catálogo, case-insensitively (409 otherwise).","maxLength":255},"description":{"type":"string","nullable":true,"description":"«Descripción»: for search only, never copied to an OC.","maxLength":255},"garment":{"type":"string","nullable":true,"description":"«Categoría»: one of the plant's categories (any case; stored in the\nlist's spelling) when the plant has a list, free text otherwise.","example":"Camisa","maxLength":255},"goal":{"type":"integer","nullable":true,"description":"«Meta por hora»: a whole number from 1 to 100000.","example":60,"minimum":1,"maximum":100000},"orderNumber":{"type":"string","nullable":true,"maxLength":255,"description":"«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.","example":"OC-4471"},"total":{"type":"integer","nullable":true,"description":"«Total de unidades» by default: a whole number from 1 to 1000000, the total a new OC made from this entry starts with.","example":600,"minimum":1,"maximum":1000000},"price":{"type":"number","nullable":true,"description":"«Precio por unidad»: 0 or more, at most two decimals. Not stored on a\nplan without facturación.","example":1250.5,"minimum":0,"maximum":1000000000},"color":{"type":"string","nullable":true,"description":"«Color»: `#RRGGBB`, or `#RGB` (expanded). Not stored on a plan without\nthe referencia colour.","example":"#1f6feb","maxLength":32},"step1Name":{"type":"string","nullable":true,"maxLength":60,"description":"«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».","example":"Patines"},"step1Goal":{"type":"integer","nullable":true,"description":"«Meta subproceso 1»: a whole number in the «Meta por hora» range, 1 to 100000.","example":40,"minimum":1,"maximum":100000},"step2Name":{"type":"string","nullable":true,"description":"«Subproceso 2»: the second step; needs step 1.","maxLength":60},"step2Goal":{"type":"integer","nullable":true,"description":"«Meta subproceso 2».","example":30,"minimum":1,"maximum":100000}}},"ExternalUpdateReferenceDto":{"type":"object","properties":{"name":{"type":"string","maxLength":255,"description":"«Referencia»."},"orderNumber":{"type":"string","nullable":true,"maxLength":255,"description":"«Corte (OC)»; null clears it."},"garment":{"type":"string","nullable":true,"maxLength":255,"description":"«Categoría»; null clears it."},"goal":{"type":"integer","minimum":1,"maximum":100000,"description":"«Meta por hora». The days before today keep the goal they were measured with."},"total":{"type":"integer","nullable":true,"minimum":1,"maximum":1000000,"description":"«Total de unidades»; null takes them away, only where the plant’s operarios pick the referencia."},"price":{"type":"number","minimum":0,"maximum":1000000000,"description":"«Precio por unidad». A new price re-prices today’s units; past days keep theirs."},"color":{"type":"string","example":"#1f6feb","description":"`#RRGGBB`."},"dueDate":{"type":"string","nullable":true,"format":"date-time","description":"«Fecha de compromiso», ISO 8601 with its offset; null clears it."},"spliceDate":{"type":"string","nullable":true,"format":"date-time","description":"«F. Empalme», ISO 8601 with its offset; null clears it."},"priority":{"type":"boolean","description":"«OC prioritaria»."},"continuity":{"type":"boolean","description":"«Continuidad»."},"step1Name":{"type":"string","nullable":true,"maxLength":60,"description":"«Subproceso 1»; null clears it (with its goal)."},"step1Goal":{"type":"integer","nullable":true},"step2Name":{"type":"string","nullable":true,"maxLength":60,"description":"«Subproceso 2»; null clears it."},"step2Goal":{"type":"integer","nullable":true},"note":{"type":"string","maxLength":500,"description":"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»."}}},"ExternalUsageCredentialDto":{"type":"object","properties":{"kind":{"enum":["oauth","personal"],"type":"string"},"name":{"type":"string","description":"The connected app’s name, or the name its user gave the API token."},"prefix":{"type":"string","example":"fluenta_live_ab1"}},"required":["kind","name","prefix"]},"ExternalUsageDayDto":{"type":"object","properties":{"requests":{"type":"number","description":"Requests answered, refusals included."},"errors":{"type":"number","description":"Of those, the ones answered 400 or above."},"averageDurationMs":{"type":"number","description":"Mean time to answer, in milliseconds; 0 with no request."},"maxDurationMs":{"type":"number","description":"The slowest answer, in milliseconds."},"lastAt":{"type":"string","nullable":true,"format":"date-time","description":"The latest request counted (ISO 8601, UTC); null when there is none."},"day":{"type":"string","format":"date","example":"2026-11-10","description":"The day, `YYYY-MM-DD`, in UTC: usage is counted by the hour of the server’s clock, never by a plant’s."}},"required":["requests","errors","averageDurationMs","maxDurationMs","lastAt","day"]},"ExternalUsageEndpointDto":{"type":"object","properties":{"requests":{"type":"number","description":"Requests answered, refusals included."},"errors":{"type":"number","description":"Of those, the ones answered 400 or above."},"averageDurationMs":{"type":"number","description":"Mean time to answer, in milliseconds; 0 with no request."},"maxDurationMs":{"type":"number","description":"The slowest answer, in milliseconds."},"lastAt":{"type":"string","nullable":true,"format":"date-time","description":"The latest request counted (ISO 8601, UTC); null when there is none."},"method":{"type":"string","example":"GET"},"route":{"type":"string","example":"/v1/external/organizations/:organizationId/modules","description":"The route as the API declares it, never the URL requested."}},"required":["requests","errors","averageDurationMs","maxDurationMs","lastAt","method","route"]},"ExternalUsageTotalsDto":{"type":"object","properties":{"requests":{"type":"number","description":"Requests answered, refusals included."},"errors":{"type":"number","description":"Of those, the ones answered 400 or above."},"averageDurationMs":{"type":"number","description":"Mean time to answer, in milliseconds; 0 with no request."},"maxDurationMs":{"type":"number","description":"The slowest answer, in milliseconds."},"lastAt":{"type":"string","nullable":true,"format":"date-time","description":"The latest request counted (ISO 8601, UTC); null when there is none."}},"required":["requests","errors","averageDurationMs","maxDurationMs","lastAt"]}},"headers":{"RateLimitLimit":{"description":"Requests per window of the budget this request is closest to spending.","schema":{"type":"integer","minimum":0}},"RateLimitRemaining":{"description":"Requests left in that budget’s window; 0 on a 429.","schema":{"type":"integer","minimum":0}},"RateLimitReset":{"description":"When that window ends, in epoch seconds (UTC).","schema":{"type":"integer","minimum":0}},"RetryAfter":{"description":"Seconds to wait before the same request can be answered. The same call before then is refused again and counts against nothing.","schema":{"type":"integer","minimum":1}}},"responses":{"RateLimited":{"description":"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.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalRateLimitedDto"}}}}}}}