FluentaAPI

Quickstart

From zero to your first answer in five steps, with curl, JavaScript and Python.

By the end of this page you will have created an API token, know which organizations it reaches, and have read the modules and one efficiency figure of one of them.

The base URL is https://api.fluenta.io/v1/external. Every request carries the token in the Authorization header.

1. Create a token

The dashboard is in Spanish; its labels are quoted here as it prints them.

  1. Sign in at app.fluenta.io and open «Configuración» (Settings) → «Tokens de API» (API tokens).
  2. Press «Crear token» (Create token). Type a «Nombre» (name), keep the «Leer» (Read) permission and pick a «Vencimiento» (expiry).
  3. In the «Token creado» (Token created) window, press «Copiar token» (Copy token). The token is shown once.

Keep it in an environment variable:

export FLUENTA_TOKEN='fluenta_live_xxxxxxxxxxxxxxxx'

2. Check the token

curl https://api.fluenta.io/v1/external/me \
  -H "Authorization: Bearer $FLUENTA_TOKEN"
{
  "user": { "name": "Ana Pérez", "roles": ["ADMIN"] },
  "connection": {
    "app": "Reporte semanal",
    "scopes": ["read"],
    "expiresAt": "2027-02-09T15:04:05.000Z"
  },
  "credential": {
    "kind": "personal",
    "name": "Reporte semanal",
    "prefix": "fluenta_live_xxx",
    "expiresAt": "2027-02-09T15:04:05.000Z"
  },
  "rateLimit": { "limit": 60, "remaining": 59, "reset": 1794355260 },
  "organizations": [
    { "id": 42, "name": "Confecciones Ejemplo", "connectorEnabled": true }
  ],
  "organizationsTotal": 1,
  "reachesAllOrganizations": false
}

user is the person the token acts as: you. credential.kind is personal for an API token.

3. List your organizations

/me carries the first 50. This route pages them and searches them by name:

curl https://api.fluenta.io/v1/external/organizations \
  -H "Authorization: Bearer $FLUENTA_TOKEN"
{
  "items": [
    { "id": 42, "name": "Confecciones Ejemplo", "connectorEnabled": true }
  ],
  "page": 1,
  "total": 1
}

The id is the organizationId of every other route.

4. Read the modules of one organization

curl https://api.fluenta.io/v1/external/organizations/42/modules \
  -H "Authorization: Bearer $FLUENTA_TOKEN"
[
  { "id": 7, "name": "Módulo 1", "position": 0, "stationCount": 12, "colorHue": null }
]

GET /organizations/42/floor answers the modules with their stations in one response. It counts against a lower rate limit: see Rate limits.

5. Read one figure

The plant's efficiency for today, from the live board. This route is on every plan:

curl https://api.fluenta.io/v1/external/organizations/42/board \
  -H "Authorization: Bearer $FLUENTA_TOKEN"

The response, shown here without its modules list (one row per module):

{
  "day": "2026-11-10",
  "plant": {
    "produced": 1840,
    "goal": 2000,
    "efficiency": 92,
    "modules": 1,
    "producing": 1,
    "lastClosed": { "day": "2026-11-09", "efficiency": 88.5 },
    "closed": { "average": 91.3, "modules": 1 }
  }
}

plant.efficiency is a percentage: 92 means 92% of the goal.

Optional: a range of days

Efficiency day by day comes from a report route. Days are YYYY-MM-DD, on the plant's clock:

curl "https://api.fluenta.io/v1/external/organizations/42/analytics/efficiency?from=2026-11-10&to=2026-11-10" \
  -H "Authorization: Bearer $FLUENTA_TOKEN"
{
  "from": "2026-11-10",
  "to": "2026-11-10",
  "groupBy": "day",
  "page": 1,
  "pageSize": 100,
  "total": 1,
  "items": [
    { "day": "2026-11-10", "units": 1840, "goal": 2000, "efficiency": 92 }
  ]
}

This route follows the plan. An organization whose plan does not include reports answers 403 with "code": "PLAN_LIMIT": nothing is wrong with the token, and /board above keeps answering. The two programs below read /board first, then try the range and say so when the plan does not include it. Plans has the detail.

The same in JavaScript

Save it as quickstart.mjs and run it with Node.js:

const BASE = 'https://api.fluenta.io/v1/external';
const headers = { Authorization: `Bearer ${process.env.FLUENTA_TOKEN}` };

async function get(path) {
  const response = await fetch(`${BASE}${path}`, { headers });
  const body = await response.json();
  if (!response.ok) {
    const error = new Error(`${body.code}: ${body.message}`);
    error.code = body.code;
    throw error;
  }
  return body;
}

const me = await get('/me');
const { items } = await get('/organizations');
const organizationId = items[0].id;
const modules = await get(`/organizations/${organizationId}/modules`);
const board = await get(`/organizations/${organizationId}/board`);

console.log(me.user.name, modules.length, board.plant.efficiency);

// Optional: a range of days. This route follows the plan.
try {
  const range = await get(
    `/organizations/${organizationId}/analytics/efficiency?from=2026-11-10&to=2026-11-10`
  );
  console.log(range.items);
} catch (error) {
  if (error.code !== 'PLAN_LIMIT') throw error;
  console.log('This plan does not include reports.');
}

The same in Python

import os

import requests

BASE = "https://api.fluenta.io/v1/external"
session = requests.Session()
session.headers["Authorization"] = f"Bearer {os.environ['FLUENTA_TOKEN']}"


class ApiError(RuntimeError):
    def __init__(self, body):
        super().__init__(f"{body['code']}: {body['message']}")
        self.code = body["code"]


def get(path, params=None):
    response = session.get(f"{BASE}{path}", params=params, timeout=30)
    body = response.json()
    if not response.ok:
        raise ApiError(body)
    return body


me = get("/me")
organization_id = get("/organizations")["items"][0]["id"]
modules = get(f"/organizations/{organization_id}/modules")
board = get(f"/organizations/{organization_id}/board")

print(me["user"]["name"], len(modules), board["plant"]["efficiency"])

# Optional: a range of days. This route follows the plan.
try:
    efficiency = get(
        f"/organizations/{organization_id}/analytics/efficiency",
        {"from": "2026-11-10", "to": "2026-11-10"},
    )
    print(efficiency["items"])
except ApiError as error:
    if error.code != "PLAN_LIMIT":
        raise
    print("This plan does not include reports.")

On this page