Buscar K
Aparência
Aparência
POST /v1/tenants, com o Client Token, cria o tenant de um cliente final a partir de um template, com os valores daquele cliente nas variáveis. O tenant novo já vem com os Dataflows, as tabelas, os Dashboards e os agendamentos do template. Usuários e login seguem como em qualquer tenant.
Template, Agente ETL e Client Token você prepara uma vez. A criação se repete para cada cliente final.
Um template. Você monta o produto num tenant de desenvolvimento, com Dataflows, tabelas, Dashboards e agendamentos, e empacota tudo num template no HEC. Veja Templates e linhas de produto.
Um Agente ETL. O agente executa as cargas do tenant e pode atender todos os tenants ou um só:
| Modo | Quando usar | Na criação do tenant |
|---|---|---|
| Um agente na nuvem para todos os tenants | A origem é uma API ou um sistema acessível pela internet | Mande o token do agente em token. O token fica em HorusETL › Agentes |
| Um agente por tenant | A origem é um banco na rede do cliente final, como um SQL Server | Crie o tenant sem token. A API cria um agente só para ele e devolve o token desse agente em agent.token, para você instalar na rede do cliente |
Para instalar o agente, veja Instalar o Agente ETL.
O Client Token. Fica no HEC, no cadastro do Cliente, aba Desenvolvedor.
O template leva a estrutura do produto e deixa de fora o que muda de um cliente final para outro: chave de API, CNPJ, URL do ERP. Esses valores ficam nas variáveis globais do tenant, que os Dataflows leem entre chaves. Um nó HTTP Request escreve {API_KEY} no cabeçalho, e cada tenant põe ali o valor dele na hora da carga.
GET https://api.seusistema.com.br/pedidos
Authorization: Bearer {API_KEY}Crie as variáveis no tenant de desenvolvimento, em Variáveis Globais do editor de Dataflows. O template leva só os nomes. Os valores de cada cliente final chegam pela API, em development_variables, quando o tenant é criado. As variáveis estão descritas no HorusETL.
Conexão de banco do template (um PostgreSQL ou SQL Server, por exemplo) nasce vazia no tenant novo e se preenche pela API depois da criação, em Preencher as conexões.
curl https://api.horusbi.com.br/v1/tenants/templates \
-H "Authorization: Bearer $CLIENT_TOKEN"{
"status": "success",
"data": [
{
"id": 540,
"nome": "Produto BI",
"versao": 3,
"status": "current",
"linha": { "id": 12, "nome": "Produto BI" },
"vigente": true
},
{
"id": 512,
"nome": "Produto BI",
"versao": 2,
"status": "deprecated",
"linha": { "id": 12, "nome": "Produto BI" },
"vigente": false
}
]
}A lista traz todos os templates do seu Cliente, inclusive as versões antigas de cada Linha de Produto. vigente diz qual usar: numa Linha de Produto, é a versão corrente dela; fora de Linha, é todo template que não foi descontinuado.
Para não guardar id de versão no seu sistema, mande templateGroupId com o id da Linha na criação do tenant. A API aplica a versão vigente da Linha naquele momento, e uma versão nova publicada no HEC passa a valer para os próximos tenants sem você mudar nada.
O Cliente também tem um Template Padrão, campo do cadastro do Cliente no HEC, que POST /v1/tenants aplica quando não recebe nem templateId nem templateGroupId. Ele aponta para um id fixo e não segue a Linha de Produto.
A API só aplica template do seu Cliente. Um id de template de outro Cliente responde Template not found, igual a um id que não existe.
curl -X POST https://api.horusbi.com.br/v1/tenants \
-H "Authorization: Bearer $CLIENT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"nome": "Loja Exemplo (cliente 1234)",
"external_id": "1234",
"ativo": true,
"dominio": "bi.suaempresa.com.br",
"templateGroupId": 12,
"token": "<token do Agente ETL>",
"development_variables": {
"API_KEY": "chave-da-api-deste-cliente",
"CNPJ_EMPRESA": "12345678000190"
}
}'| Campo | Obrigatório | O que é |
|---|---|---|
nome | sim | Nome do tenant. Precisa ser único na plataforma inteira, então inclua o ID do cliente no seu sistema |
ativo | sim | true para o tenant já aceitar login |
external_id | não | O ID do cliente final no seu sistema, até 255 caracteres. Único entre os tenants do seu Cliente. Repetir a criação com o mesmo valor devolve o tenant que já existe |
dominio | não | Domínio do DataViz que a URL de login usa. Sem ele, o tenant fica em dataviz.horusbi.com.br |
templateId | não | Template a aplicar, pelo id da versão |
templateGroupId | não | Linha de Produto. A API aplica a versão vigente dela. Mande este ou templateId, não os dois |
token | não | Token do Agente ETL na nuvem que vai executar as cargas. Sem ele, a API cria um agente só para este tenant |
development_variables | não | Valores das variáveis globais do template, como objeto {"NOME": "valor"} ou lista [{"key": "NOME", "value": "valor"}] |
config | não | Configurações avançadas do tenant. Deixe de fora se não souber o que mandar |
Sem templateId e sem templateGroupId, vale o Template Padrão do Cliente. Sem os três, o tenant nasce vazio.
Token de agente que não existe é recusado com Token inválido!.
Cada variável em development_variables precisa de valor. 0 e texto vazio contam como valor; null recusa a criação inteira com Invalid development_variables format. Variável do template que você não mandar entra vazia no tenant e aparece em variaveisVazias na resposta.
Para mandar senha ou token, use a lista e marque a variável como protegida: {"key": "DB_PWD", "value": "...", "is_secret": true}. O valor de uma variável protegida não volta mais nas leituras, nem pela API nem na tela do HorusETL.
Se o Cliente tem domínio principal configurado no HEC, ele vale no lugar do dominio que você mandar.
Configure o timeout do seu cliente HTTP para pelo menos 2 minutos. A chamada só responde depois de criar o tenant e aplicar o template inteiro, e com template grande isso leva dezenas de segundos.
{
"status": "success",
"data": {
"id": 2048,
"nome": "Loja Exemplo (cliente 1234)",
"external_id": "1234",
"dominio": "bi.suaempresa.com.br",
"ativo": true,
"created": true,
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"status": "awaiting_credentials",
"statusDetail": { "credentials": [{ "id": 77, "nome": "ERP" }] },
"agent": { "id": 310, "nome": "Agente Loja Exemplo", "dedicado": false },
"development_variables": [
{ "key": "API_KEY", "value": "chave-da-api-deste-cliente" },
{ "key": "CNPJ_EMPRESA", "value": "12345678000190" }
],
"reloadPlan": [],
"credenciaisVazias": [{ "id": 77, "nome": "ERP" }],
"variaveisVazias": [],
"tabelasVazias": [],
"bi_desks": [{ "id": 7, "nome": "Comercial" }],
"bi_apps": [{ "id": 42, "nome": "Funil de Vendas", "deskId": 7 }],
"dw_tables": [{ "id": 3407, "nome": "Vendas", "deskId": 3 }]
}
}| Campo | O que traz |
|---|---|
token | O Tenant Token do tenant novo. Guarde junto do cadastro do cliente: com ele você cria usuários, gera login e dispara cargas |
created | true quando o tenant foi criado agora; false quando já existia com o mesmo external_id |
status, statusDetail | Em que ponto o tenant está. Veja Estado do tenant |
agent | O Agente ETL que ficou com as cargas. Com dedicado: true, ele foi criado só para este tenant, e agent.token traz o token para instalar o agente na rede do cliente. Se você mandou o token, ele não volta |
credenciaisVazias | Conexões do template que nasceram vazias, com id e nome |
variaveisVazias | Variáveis do template que ficaram sem valor |
tabelasVazias | Tabelas novas sem Dataflow de carga |
reloadPlan | Tabelas que o apply criou ou reconstruiu, em ordem de dependência, para disparar a carga |
As variáveis voltam como lista de key e value, mesmo que tenham ido como objeto, e a protegida volta sem value.
O tenant é gravado antes de o template ser aplicado. Se o apply falha, sobra um tenant incompleto, com status: "failed" e o passo em que parou em statusDetail.step.
Com external_id, tentar de novo é seguro: a mesma chamada devolve o tenant que já existe, com created: false, sem colidir no nome. Duas chamadas que chegam juntas com o mesmo external_id também caem no mesmo tenant. Se o status voltar failed, aplique o template nele com POST /v1/tenant/templates/apply, usando o token da resposta.
Sem external_id, a nova tentativa com o mesmo nome dá Nome do Tenant indisponível. Procure o tenant em GET /v1/tenants e aplique o template nele do mesmo jeito.
Para achar o tenant de um cliente pelo ID do seu sistema:
curl "https://api.horusbi.com.br/v1/tenants?external_id=1234" \
-H "Authorization: Bearer $CLIENT_TOKEN"Conexão de banco que vem do template nasce vazia no tenant novo. Com o Tenant Token, liste as conexões e grave os campos de cada uma:
curl https://api.horusbi.com.br/v1/tenant/credentials \
-H "Authorization: Bearer $TENANT_TOKEN"{
"status": "success",
"data": [
{
"id": 77,
"nome": "ERP",
"type": "ExtractPostgreSQL",
"agentId": 310,
"filled": false,
"fields": {}
}
]
}curl -X PUT https://api.horusbi.com.br/v1/tenant/credentials/77 \
-H "Authorization: Bearer $TENANT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"fields": {
"hostname": "10.0.0.12",
"database": "erp",
"username": "leitura",
"password": "senha-do-banco"
}
}'Os campos dependem do tipo de banco (type) e ficam cifrados como numa conexão salva no HorusETL. No PostgreSQL são hostname, database, username, password e, opcional, extra, com parâmetros adicionais da conexão. Para outro tipo, salve uma conexão de teste pelo HorusETL e veja os nomes no GET.
A senha só entra. O GET nunca devolve password, e segredo dentro de connection string volta como ******. No PUT, a senha omitida e o segredo mandado de volta como ****** mantêm o valor gravado; os outros campos são substituídos pelo objeto enviado. Depois de gravar, o agente relê as conexões do tenant e a próxima carga já usa os valores novos.
filled diz se a conexão tem algum campo preenchido. Conexão que não existe no tenant responde 404 com CREDENTIAL_NOT_FOUND.
A criação não dispara carga, e as tabelas do tenant nascem vazias. Sem fazer nada, os Dashboards ficam vazios até o primeiro horário do agendamento. Para carregar já, rode os agendamentos que o template criou, com o Tenant Token:
curl https://api.horusbi.com.br/v1/tenant/etl-schedules \
-H "Authorization: Bearer $TENANT_TOKEN"
curl -X POST https://api.horusbi.com.br/v1/tenant/etl-schedules/run \
-H "Authorization: Bearer $TENANT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"scheduleId": 3301}'{ "status": "success", "data": true, "executionId": "6f1c2a8e-3b7d-4f0a-9c1e-2d5b8a7f4e10" }O agendamento roda os Dataflows dele na ordem configurada, em segundo plano. data: true confirma o pedido, e executionId serve para acompanhar a carga. Com o agente offline, a resposta é a mesma, com HTTP 200; o motivo fica no estado da execução.
Disparar de novo, pela API, um agendamento ou Dataflow que ainda está rodando não começa outra carga. A resposta traz o executionId da carga aberta, com alreadyRunning: true.
POST /v1/tenant/flows/run dispara um Dataflow só e devolve executionId do mesmo jeito.
curl https://api.horusbi.com.br/v1/tenant/executions/6f1c2a8e-3b7d-4f0a-9c1e-2d5b8a7f4e10 \
-H "Authorization: Bearer $TENANT_TOKEN"{
"status": "success",
"data": {
"executionId": "6f1c2a8e-3b7d-4f0a-9c1e-2d5b8a7f4e10",
"type": "schedule",
"scheduleId": 3301,
"flowId": null,
"createdAt": "2026-10-27T14:02:11.000Z",
"status": "running",
"errorCode": null,
"message": null,
"startedAt": "2026-10-27T14:02:13.000Z",
"finishedAt": null,
"flows": [
{
"flowId": 1201,
"status": "succeeded",
"rows": 18240,
"rowsIn": 18240,
"rowsOut": 18240,
"startedAt": "2026-10-27T14:02:13.000Z",
"finishedAt": "2026-10-27T14:03:40.000Z",
"errorCode": null,
"message": null
},
{
"flowId": 1202,
"status": "running",
"rows": null,
"rowsIn": null,
"rowsOut": null,
"startedAt": "2026-10-27T14:03:41.000Z",
"finishedAt": null,
"errorCode": null,
"message": null
}
]
}
}status vai de queued (o agente ainda não começou) para running e termina em succeeded ou failed. Cada Dataflow tem o seu estado em flows, com as linhas lidas e gravadas. Consulte de tempos em tempos até finishedAt aparecer.
Quando a execução falha, errorCode diz o motivo e message traz o texto do agente:
errorCode | O que aconteceu |
|---|---|
AGENT_OFFLINE | Nenhum Agente ETL ativo para o tenant, ou o agente estava offline. Nada foi disparado |
AGENT_REJECTED | O agente recusou o comando |
FLOW_FAILED | Um Dataflow falhou. O texto do erro está em message |
SCHEDULE_FAILED | O agendamento terminou com algum Dataflow em falha |
NOT_STARTED | O agente não começou a carga em 2 horas |
TIMEOUT | A carga começou e não terminou em 24 horas |
O executionId vale só no tenant que disparou. De outro tenant, ou inexistente, responde 404 com EXECUTION_NOT_FOUND.
GET /v1/tenant/tables também traz, em cada tabela, a última carga (ultima_carga, status_ultima_carga e linhas_ultima_carga), venha ela da API ou do agendamento. Com ?linhas=true, soma o total atual de linhas de cada tabela, numa consulta mais lenta.
GET /v1/tenants (Client Token) e GET /v1/tenant (Tenant Token) trazem status e statusDetail de cada tenant. O campo junta num lugar só o que as seções acima acompanham:
status | Quando | statusDetail |
|---|---|---|
provisioning | A criação ou o apply do template pela API ainda está rodando | step, o passo atual |
failed | A montagem parou num passo, ou a última carga disparada pela API falhou | step, errorCode e message. Na carga, step é load e vem o executionId |
awaiting_credentials | Há conexão do template sem nenhum campo preenchido | credentials, com id e nome de cada uma |
loading | Há carga disparada pela API em curso | executionId |
ready | Nenhum dos casos acima | null |
Montagem que fica em provisioning por mais de 30 minutos sai como failed, com errorCode: "INTERRUPTED": o servidor parou no meio, e o caminho é aplicar o template de novo. Tenant criado pelo HEC começa a conta pelas conexões, porque só a montagem pela API é registrada.
Com o Tenant Token em mãos, o resto é igual a qualquer tenant: criar o usuário e gerar o login transparente.
Versão nova do template não chega sozinha aos tenants que já existem. Para atualizar vários de uma vez, instale a versão pela Linha de Produto no HEC. Para atualizar um tenant pela API, aplique a versão nova com o Tenant Token. Os dois caminhos, com prévia, espelho e recarga das tabelas reconstruídas, estão em Versão nova do template.
PUT /v1/tenants troca as variáveis de um tenant que já existe. Mande a lista inteira: variável que não vier é removida.
curl -X PUT https://api.horusbi.com.br/v1/tenants \
-H "Authorization: Bearer $CLIENT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"id": 2048,
"ativo": true,
"development_variables": {
"API_KEY": "chave-nova",
"CNPJ_EMPRESA": "12345678000190"
}
}'Uma variável que continua guarda descrição e proteção, então trocar a chave de uma protegida não a deixa exposta. ativo é obrigatório em todo PUT; mande o valor atual para não desativar o tenant sem querer.
Se dois Agentes ETL alimentam o mesmo tenant a partir de servidores diferentes, cada um pode ter o seu valor. Use a lista, com agent_overrides:
"development_variables": [
{ "key": "DB_HOST", "value": "10.0.0.5",
"agent_overrides": [{ "tokenId": 852, "value": "10.9.0.7" }] }
]O agente 852 recebe 10.9.0.7, e os outros recebem 10.0.0.5. tokenId é o id do agente, que vem em agent.id na resposta da criação. agent_overrides só é aceito no PUT, depois que o tenant existe.
GET /v1/tenants devolve os tenants do Cliente, cada um com o Tenant Token, o estado, as Mesas, Aplicações e tabelas, e os usuários. Com external_id, devolve só o tenant com esse ID do seu sistema.
Sem parâmetro, a lista vem inteira. Com muitos tenants, peça em páginas e deixe os usuários de fora:
curl -G https://api.horusbi.com.br/v1/tenants \
-d page=1 -d page_size=50 -d include_users=false \
-H "Authorization: Bearer $CLIENT_TOKEN"{
"status": "success",
"data": [
{
"id": 2048,
"nome": "Loja Exemplo (cliente 1234)",
"status": "ready"
}
],
"pagination": { "page": 1, "page_size": 50, "total": 167 }
}| Parâmetro | O que faz |
|---|---|
page_size | Tenants por página, de 1 a 100. É ele que liga a paginação |
page | Número da página, a partir de 1. Sem ele, vem a primeira. Sem page_size, é ignorado |
include_users | Com false, cada tenant vem sem sys_users_tenants, a parte mais pesada da lista. Vale com ou sem paginação |
external_id | Filtra pelo ID do tenant no seu sistema. Também vale com paginação |
Paginada, a lista vem em ordem de id. Peça a página seguinte enquanto page * page_size for menor que total. Sem page_size, a lista vem inteira e sem ordem garantida. page_size fora de 1 a 100 responde 400 com VALIDATION_ERROR.
PUT /v1/tenants com "ativo": false bloqueia o login de todos os usuários do tenant e grava a suspensão, com o mesmo efeito de suspender o tenant no HEC: o mês corrente é cobrado inteiro, e a cobrança para no mês seguinte. Mandar "ativo": true reativa. Em tenant pré-pago, com assinatura, o ativo só liga e desliga o acesso, e a assinatura segue sendo cobrada. Os status e o efeito de cada um na cobrança estão em Tenants.
WARNING
"ativo": false desativa o tenant, pré-pago também, e a rotina diária das 05:20 apaga as tabelas do DW dele, sem carência (as de Datalake e os registros de Cadastros ficam). "ativo": true recria as tabelas na hora, vazias: dispare a carga de novo, como em Disparar a carga. Detalhes em O que acontece com os dados.
DELETE /v1/tenants exclui o tenant e encerra a cobrança na competência seguinte. A API não tem como desfazer a exclusão. Os campos completos estão na referência da API.