# Conventions
URL: https://docs.fluenta.io/en/docs/conventions

The rules that hold for every route: lists and pages, ids, days and hours on the plant's clock, ranges, efficiency, money and compatible changes.



Every route lives under `https://api.fluenta.io/v1/external` and answers JSON.
This page gathers what holds for all of them.

## Lists and pages [#lists-and-pages]

A list comes in one of three shapes. Each operation's page says which.

| Shape of the answer                                   | Operations                                                                                  | Size                     |
| ----------------------------------------------------- | ------------------------------------------------------------------------------------------- | ------------------------ |
| `{ items, page, total }`                              | `listOrganizations`, `listOrganizationStations`, `searchReferences`, `listActivity`         | 50 per page              |
| `{ items, page, pageSize, total, from, to, groupBy }` | `getEfficiency`, `getProduction`, `getBilling`                                              | 100 per page             |
| JSON array, not paged                                 | `listModules`, `listStations`, `listOperators`, `getProgram`, `listOnHold`, `listCatalogue` | Everything in one answer |

* `page` goes in the query, starts at 1, and is 1 when you leave it out.
* `total` counts the rows of every page, not of this page.
* `getBilling` adds `currency` to its answer.
* In `listOrganizationStations` and in the ranges, a page past the last one
  answers empty `items`, not an error.

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

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

The example trims the fields of each «puesto» (a station).

## Ids [#ids]

* Every id is an integer: `organizationId`, `moduleId`, `stationId`,
  `operatorId`, `referenceId`, `templateId`.
* An id always comes from an earlier answer. `organizationId` comes from
  `getMe` or `listOrganizations`; there is no default organization.
* An id of another organization answers the same 404 as one that does not
  exist.
* The one exception is `listActivity`: each row is identified by `idx`, a
  UUID.

A «puesto» has two names for the same number: `stationId` in paths and
`operationId` in answers and filters. The `id` that `listStations` answers
works in both places.

## Days, hours and instants [#days-hours-and-instants]

A day is the plant's day, in its «Zona horaria» (time zone), never in UTC.

| Value                                                  | Format                                                                                                       | Clock                                                          |
| ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------- |
| Day: `day`, `from`, `to`                               | `YYYY-MM-DD`                                                                                                 | The plant's: `timezone` in `getOrganization` and `getSettings` |
| Hour: `hour`, `currentHour`                            | Integer from 0 to 23                                                                                         | The plant's. 9 is 09:00 to 09:59                               |
| Instant: `pausedAt`, `expiresAt`, `firstAt`            | ISO 8601                                                                                                     | UTC                                                            |
| Dates of an OC (an order lot): `dueDate`, `spliceDate` | ISO 8601. When you write them they carry their offset (`Z` or `±HH:MM`), such as `2026-11-28T00:00:00-05:00` | On a write, the plant's offset; in answers, UTC                |

* `getOrganization` answers `today`: the plant's today. Use it when someone
  says "today".
* Where `day` is optional and you leave it out, the API reads the plant's
  today.
* A date that is not a real day answers 400 `INVALID_DAY`.
* `getMyUsage` is the exception: it counts by UTC days.

## Ranges [#ranges]

A range takes `from` and `to`, both included.

| Operations                                                                                                          | Most days, counting both ends |
| ------------------------------------------------------------------------------------------------------------------- | ----------------------------- |
| `getEfficiency`, `getProduction`, `getBilling`, `getOperatorReport`, `getModuleEfficiency`, `getOperatorEfficiency` | 92 days                       |
| `getPauses` («Pausas», the pauses report)                                                                           | 31 days                       |

A longer range answers 400 `RANGE_TOO_LARGE`, with the cap in `maxDays`.
`from` after `to` answers 400 `RANGE_INVALID`. A day the plant has not reached
yet is not an error: it has no data. For a longer question, split the range
into several requests.

## Efficiency and money [#efficiency-and-money]

* **«Eficiencia»** (efficiency) is a percentage, not a 0 to 1 ratio: units ×
  100 ÷ goal, with 2 decimals. 100 is exactly the goal and it can pass 100. It
  is `null` when the goal is 0.
* **Money** is in whole currency units with up to 2 decimals: pesos or euros,
  never cents.
* **The currency** is the plant's, set by its «país» (country): `EUR` for a
  plant in España, `COP` for every other. It is an ISO 4217 code. It comes in
  the answer's `currency` when the answer carries one; otherwise in
  `currency.code` of `getOrganization`.
* A money figure can be `null`, not 0, when nothing was billed. Each field's
  description says so.

Every figure is the dashboard's figure for the same inputs. Quote it as it
comes instead of recomputing it.

## Caching [#caching]

Every answer carries `Cache-Control: no-store`. Do not keep it in a proxy or a
shared cache.

## Compatible changes [#compatible-changes]

The API grows by addition under `/v1/external`: new routes, new optional
parameters and new fields in answers. Write your program to ignore the fields
it does not know.

An error `code` keeps its name once published, and the permissions `read` and
`program` are never renamed. The [changelog](/en/docs/changelog) announces the
rest.
