# Get hourly activity
URL: https://docs.fluenta.io/docs/reference/analytics/get-hourly-activity
Operation: `getHourlyActivity`
Required permission: `read`

`GET /v1/external/organizations/{organizationId}/analytics/hourly`

Analítica hour by hour for one plant day, from the marcas log: units, meta, efficiency, billing and pace per hour, for the plant, one módulo or one operario.

What it is: «Analítica» hour by hour for one plant day, the hourly view of the dashboard’s «Reportes», tab «Analítica». `hours` holds one entry per clock hour with activity and `totals` the day. Without a filter it is the whole plant; `moduleId` narrows it to one «módulo»; `operatorId` to one «operario» (their own «marcas» and their share of each hour’s «meta», so its totals equal that person’s row of `getOperatorsActivity`). `moduleId` and `operatorId` are echoed in the answer, null when not sent.

How it is computed: `units`, `goal`, `efficiency`, `billableUnits` and `billedAmount` come from the hourly buckets, so they agree with the reports (`goal` is the sum of each «puesto»’s «meta» for the hour). `events`, `online`, `offline`, `legacy`, the two `secondsPerUnit` percentiles and `idleGapsOverN` come from the «marcas» log. A gap is counted in the hour of its later «marca», so the hours add up to the totals.

Source: «Analítica» reads the «marcas» log (one row per unit an «operario» registered on the app, with its time and channel) beside the hourly buckets the reports are built from, so it can tell pace, connectivity and gaps that the reports cannot.

`efficiency` is «Eficiencia»: units made × 100 ÷ the «meta» of the same span, a percentage with 2 decimals (100 = exactly the goal, it can pass 100; never a 0–1 ratio), null where the goal is 0. Every «puesto» counts towards it, a «puesto de salida» or not.

Money is «Facturación»: the units of the «puestos de salida» × the price per unit each was billed at, in the plant’s currency (`currency`, ISO 4217: `EUR` for a plant in España, `COP` for every other), in whole currency units with up to 2 decimals, never cents. Whether a money figure reads null or 0 where nothing was priced is stated on each field. `billableUnits` and `billedAmount` are null, not 0, when no «puesto de salida» is in what was asked.

Days and hours are the plant’s own: a plant day is a calendar day `YYYY-MM-DD` in the organization’s timezone (`getOrganization` `timezone`), never UTC, and an `hour` is the clock hour 0–23 that starts at it in that zone. `day` is optional: without it, the plant’s today on the organization’s own clock, as on every single-day route of the API externa; the answer’s `day` says which day was read.

Plan: needs the plan feature `analytics` («Analítica»), the one its dashboard screen is gated by. Pro, a trial of Pro and an «exenta» organization answer it; a Light organization, of either plan version, does not: 403 `PLAN_LIMIT` with `feature: "analytics"`. `getPlan` `features` lists what the organization’s plan has.

See also: `getOperatorsActivity` for the same day by «operario»; `getConsistency` to check the log against the buckets.

### Authorization

- `apiToken` — Bearer token. An API token created in Configuración › Tokens de API, or a connected app’s access token

Send it as `Authorization: Bearer <your API token>`.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `day` | query | string | no | The plant day to read, `YYYY-MM-DD` on the plant’s own clock (the organization’s timezone, never UTC). Omitted: the plant’s today on that clock; the answer’s `day` says which day was read. Not a real calendar day: 400 `INVALID_DAY`. |
| `moduleId` | query | integer | no | Narrow the answer to one «módulo»: the `id` of one from `listModules`. Omitted: the whole plant. Of another organization or unknown: 404 `MODULE_NOT_FOUND`. |
| `operatorId` | query | integer | no | Narrow the answer to one «operario» (the `id` of one from `listOperators`): their own «marcas» and their share of each hour’s «meta». Omitted: every «operario». Of another organization or unknown: 404 `OPERATOR_NOT_FOUND`. |
| `idleMinutes` | query | number | no | In minutes: a gap between two consecutive «marcas» longer than this counts as one «pausa sin registro» (`idleGapsOverN`, `idleGaps`). One of 5, 10 or 15; default 5. The answer echoes the value used. |
| `organizationId` | path | integer | yes | The organization, from `getMe` or `listOrganizations`. |

### Responses

| Status | Description |
| --- | --- |
| `200` | The day hour by hour, and its totals. |
| `400` | `INVALID_DAY`: `day` is not a real calendar day written `YYYY-MM-DD`. `INVALID_REQUEST`: a query parameter is missing, of the wrong type or outside its allowed values; `details` lists each one as `{field, message}`. Body: `{statusCode, code, message}`, the message in Spanish, for the usuario. |
| `401` | Missing, malformed, unknown, expired or revoked credential: `INVALID_API_TOKEN`. |
| `402` | The organization is blocked: `ORG_BLOCKED`. |
| `403` | `PLAN_LIMIT` with `feature: "analytics"`: the organization’s plan lacks «Analítica» (Pro, a trial of Pro and an «exenta» organization answer it; a Light organization, of either plan version, does not). `PLAN_LIMIT` with `feature: "aiConnector"`: a connected app reads an organization without «Conectar con IA». `INSUFFICIENT_SCOPE`: the credential lacks the «Permiso» `read`. Retrying does not help: the plan or the credential has to change. |
| `404` | `ORGANIZATION_NOT_FOUND`: the organization does not exist or is out of the credential’s reach. `MODULE_NOT_FOUND`: the «módulo» does not exist or is another organization’s. `OPERATOR_NOT_FOUND`: the «operario» does not exist or is another organization’s. One body for a row that is missing and for one of another organization. |
| `429` | The credential is over its rate limit: `RATE_LIMITED`. Per credential, in fixed windows of 60 seconds: 60 requests in one organization on Light and 120 on Pro; 20 on the heavy reads (the range figures, the whole floor, the month and the pauses); 300 across every organization; 60 on the routes that name no organization. Wait `Retry-After` seconds; a refused request counts against nothing. |

### Example

```bash
curl -X GET 'https://api.fluenta.io/v1/external/organizations/{organizationId}/analytics/hourly' \
  -H 'Authorization: Bearer <your API token>'
```