---
url: 'https://docs.horusbi.com.br/api.md'
description: >-
  Base URL, os dois tokens, o formato dos erros com code e o limite de pedidos
  da API REST do Lumo.
---

# API

A API REST do [Lumo](/plataforma/) deixa outro sistema fazer, sem tela, o que a equipe faria no [HEC](/hec/) (Horus Enterprise Control, a suíte de administração) e nas outras suítes: criar [Tenants](/plataforma/contas) e usuários, liberar acesso a dados, disparar cargas do [HorusETL](/etl/), conversar com o [Chat com Dados](/ia/chat) e guardar arquivos. Esta página cobre o que vale para todas as rotas: endereço, os dois tokens, erros e limite de pedidos. Cada operação tem a própria página no menu, gerada da especificação OpenAPI, e o [spec completo](/api/spec) junta todas.

Para abrir o Lumo dentro do seu sistema, com o usuário entrando sem tela de login, o caminho inteiro (Tenant, usuário, login e carga) está em [Embed](/parceiros/embed/).

## Base URL

```text
https://api.horusbi.com.br/v1
```

Todo caminho do menu é relativo a ela: `GET /tenant/users` é `https://api.horusbi.com.br/v1/tenant/users`.

## Autenticação {#autenticacao}

Todo pedido leva um token no header `Authorization`:

```http
Authorization: Bearer SEU_TOKEN
```

São dois tokens, com escopos diferentes:

| Token | Para quê | Rotas | Onde pegar |
|---|---|---|---|
| Client Token | Administrar os Tenants do Cliente (a empresa que contrata o Lumo, como o parceiro whitelabel) | `/tenants` | Cadastro do Cliente no HEC, aba **Desenvolvedor**, campo **Chave de API** |
| Tenant Token | Operar dentro de um Tenant | `/tenant` | Cadastro do Tenant no HEC, aba **Desenvolvedor**, ou o campo `token` de cada Tenant em `GET /tenants` |

A aba **Desenvolvedor** só aparece para quem tem a permissão de desenvolvedor. O botão **Gerar Novo Token** desativa o token atual na hora: toda integração que usa o antigo passa a receber 401.

O Tenant Token só vale com o Tenant ativo. Tenant inativo ou excluído responde 401, igual a um token errado: a resposta não diz qual dos dois foi. Antes de trocar o token, confira o campo `ativo` do Tenant em `GET /tenants`, com o Client Token.

O Tenant Token age em nome de qualquer usuário ativo do Tenant. No Chat com Dados, por exemplo, você escolhe o usuário pelo `userId` e o assistente responde com o acesso dele. Guarde o token só no servidor, nunca no navegador.

## Erros {#erros}

Toda resposta de erro em JSON traz `code`, um código fixo ao lado de `message`:

```json
{ "status": "error", "message": "Template not found", "code": "TEMPLATE_NOT_FOUND" }
```

Decida pelo `code`. A mensagem pode mudar de redação; o código não muda. Os campos que as respostas de erro já tinham antes do `code` (`status`, `success`, `message`, `error`) continuam no corpo.

Nas rotas `/tenant` (Tenant Token), o status HTTP segue a causa. Nas rotas `/tenants` (Client Token), criar, alterar e excluir recusam com 400, qualquer que seja a causa. Erro interno é sempre 500.

> \[!NOTE]
> Até 28/10/2026, parte dos erros das rotas `/tenant` voltava com HTTP 200 e `"success": false`, e a franquia de usuários voltava 500. Integração que só olhava `success` agora recebe esses erros como 4xx.

Duas respostas não têm JSON nem `code`: o 401, com o texto `Unauthorized`, e o 429 do [limite de pedidos](#limite-de-pedidos). O envio de arquivo acima de 200 MiB também responde em texto, com 413 (veja [Upload grande](/api/upload)).

### Códigos {#codigos}

| `code` | Status | Quando |
|---|---|---|
| `VALIDATION_ERROR` | 400 | Corpo ou parâmetro inválido |
| `INVALID_VARIABLES` | 400 | `development_variables` em formato inválido, como `null` |
| `AGENT_TOKEN_INVALID` | 400 | Token de Agente ETL que não existe |
| `TENANT_NAME_TAKEN` | 400 | Nome de Tenant já usado na plataforma |
| `TENANT_NOT_FOUND` | 404 (400 em `/tenants`) | Tenant que não existe ou não é do Cliente |
| `TEMPLATE_NOT_FOUND`, `PRODUCT_LINE_NOT_FOUND` | 404 (400 em `/tenants`) | Template ou Linha de Produto que não existe, é de outro Cliente ou não tem versão vigente |
| `USER_NOT_FOUND`, `USER_NOT_IN_TENANT` | 404 | Usuário que não existe ou não está no Tenant |
| `USER_LOGIN_TAKEN` | 409 | Login já cadastrado |
| `USER_QUOTA_EXCEEDED` | 409 | Limite de usuários do Tenant atingido |
| `USER_LOGIN_NOT_ALLOWED` | 403 | Usuário da equipe da plataforma, que não entra pela API |
| `MANAGED_BY_PROVIDER` | 403 | Grupo administrado pelo Cliente (parceiro): invisível ao atribuir usuários, ou bloqueado para editar e excluir em `/groups` |
| `ADMIN_GROUP_NOT_ALLOWED` | 403 | Atribuir ou editar o grupo Administradores pela API |
| `GROUP_NOT_FOUND` | 404 | Grupo que não existe |
| `TABLE_ACCESS_INVALID` | 400 | Item de `tables` em usuário ou grupo com filtro, coluna ou tabela inválida. `errors` aponta o campo |
| `APP_NOT_FOUND`, `DESK_NOT_FOUND`, `TABLE_NOT_FOUND`, `DATAMART_NOT_FOUND` | 404 | Aplicação, Mesa, tabela ou Datamart citado no pedido não existe |
| `FLOW_NOT_FOUND`, `SCHEDULE_NOT_FOUND` | 404 | Dataflow ou agendamento que não existe ou está inativo |
| `EXECUTION_NOT_FOUND` | 404 | `executionId` que não é deste Tenant |
| `CREDENTIAL_NOT_FOUND` | 404 | Conexão que não existe |
| `AGENT_OFFLINE` | 409 | Em `POST /tenant/templates/reload`, item cujo Agente ETL não está ativo no Tenant. Nada é disparado |
| `FILE_NOT_FOUND` | 404 em `GET /tenant/files/{id}`, 400 nas outras rotas de arquivo | Arquivo que não existe, foi excluído ou é de outro Tenant |
| `FILE_IN_USE` | 400 | Imagem de Cadastro que ainda aparece em algum registro |
| `FILE_TOO_LARGE` | 400 | Arquivo acima de 2 GB na confirmação do envio grande |

Erro sem código próprio recebe um genérico pelo status: `VALIDATION_ERROR` (400), `NOT_FOUND` (404), `INTERNAL_ERROR` (500) e `REQUEST_FAILED` (qualquer outro). Código que a sua integração não conhece, trate pelo status HTTP.

Fora o `AGENT_OFFLINE` do `POST /tenant/templates/reload`, o disparo de uma carga volta 200 mesmo que ela venha a falhar. A falha (`AGENT_OFFLINE`, `FLOW_FAILED` e outros) aparece depois em `errorCode`, dentro de `GET /tenant/executions/{id}`. O limite de IA do chat chega como evento, descrito em [Streaming do chat](/api/streaming#erros).

## Limite de pedidos {#limite-de-pedidos}

A API aceita até 100 pedidos por segundo por endereço IP, contando todas as rotas. Acima disso, responde 429 com o texto `Too Many Requests` e o header `Retry-After`, com os segundos até liberar. Espere esse tempo e repita o pedido.

Cada processo da API conta à parte, então às vezes passa um pouco mais que 100. Não conte com essa folga: planeje a integração para ficar abaixo de 100.

## O que tem nesta seção

| Página | Para quê |
|---|---|
| [Streaming do chat](/api/streaming) | Ler a resposta do Chat com Dados evento a evento (SSE), com exemplo em Node.js e Python |
| [Upload grande](/api/upload) | Enviar arquivo de até 2 GB por URL pré-assinada |
| [Spec completo](/api/spec) | Todas as operações numa página só |

Cada grupo do menu (Tenants, Usuários, Chat com Dados, Arquivos e os outros) lista as operações com parâmetros, corpo, respostas e exemplos. O arquivo da especificação fica em [`/api.yml`](/api.yml), para gerar cliente ou importar no Postman.
