Buscar K
Aparência
Aparência
A API de usuários cadastra, atualiza e remove os usuários de um tenant, com os grupos e as Mesas de cada um. O cadastro acompanha o seu sistema sem passar pelo HEC, e o usuário pode ser criado logo antes do login transparente.
Todas as chamadas desta página usam o Tenant Token. Para o mesmo cadastro feito na tela, veja Usuários do Tenant.
curl "https://api.horusbi.com.br/v1/tenant/users?email=ana.souza%40lojaexemplo.com.br" \
-H "Authorization: Bearer $TENANT_TOKEN"{
"status": "success",
"data": [
{
"id": 812,
"login": "ana.souza@lojaexemplo.com.br",
"nome": "Ana Souza",
"ultimo_login": "2026-10-20T13:02:11.000Z",
"groups": [3],
"bi_desks": [7],
"apps_deny": []
}
]
}data vazio quer dizer que o e-mail não está vinculado a este tenant. O filtro aceita email, login (o mesmo campo) ou id. Sem filtro, a chamada devolve todos os usuários ativos do tenant, inclusive as pessoas da equipe do Cliente que entram nele.
curl -X POST https://api.horusbi.com.br/v1/tenant/users \
-H "Authorization: Bearer $TENANT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"login": "ana.souza@lojaexemplo.com.br",
"nome": "Ana Souza",
"groups": [3]
}'{ "success": true, "id": 812 }O usuário é da plataforma, e o tenant guarda só um vínculo com ele. Se o e-mail já existe em outro tenant, de qualquer Cliente, a API vincula esse mesmo usuário. Por isso password só vale para usuário criado agora (quem já existia continua com a senha dele), e nome muda o nome do usuário em todos os tenants dele.
Sem password, o usuário fica sem senha e entra só pelo login transparente. Use assim quando o seu sistema for a única porta de entrada.
Usuário sem grupo e sem Mesa entra no DataViz e não vê nada. Os campos de permissão valem na criação e na edição:
| Campo | Recebe | Efeito |
|---|---|---|
groups | IDs de GET /v1/tenant/groups | O usuário herda as Mesas, as tabelas liberadas e os filtros de linha do grupo |
bi_desks | IDs de Mesa, de GET /v1/tenant | Libera a Mesa para o usuário direto, sem grupo |
apps_deny | IDs de Aplicação | Esconde Aplicações específicas, mesmo dentro de uma Mesa liberada |
tables | Tabelas, com filtro de linha | Libera a tabela para o usuário direto, só com as linhas do filtro. Veja Filtrar as linhas |
Prefira grupos. Com um grupo por perfil do seu sistema (Diretoria, Gerente, Vendedor), mudar o que um perfil vê é uma edição no HEC, sem reenviar a lista de Mesas de cada usuário. Grupos estão em Grupos e permissões, e o que um grupo libera, em Liberar acesso.
A API não dá o grupo de administrador. A chamada que tentar volta com HTTP 403, O grupo de administrador não pode ser atribuído pela API. e o código ADMIN_GROUP_NOT_ALLOWED. Quem vai administrar o tenant recebe esse grupo no HEC.
O filtro de linha fica na liberação de uma tabela, e a liberação pode ser de um grupo ou de um usuário. As duas vão no campo tables da mesma chamada que cria ou edita o grupo ou o usuário. Escolha pelo que o filtro descreve:
| O filtro descreve | Onde fica | Exemplo |
|---|---|---|
| Algo que várias pessoas dividem: loja, filial, região | No grupo | Os gerentes da loja 12 veem só a loja 12 |
| A própria pessoa | No usuário | Cada um de 180 representantes vê só as próprias vendas |
Com 180 representantes, um grupo por pessoa daria 180 grupos de um membro só. Nesse caso o filtro vai no próprio usuário, e um grupo único dá a Mesa a todos.
Os exemplos usam uma Aplicação publicada na Mesa 7 que lê a tabela Vendas, com as colunas Loja e Representante. A ordem das chamadas:
A liberação aponta para o ID da tabela e o ID da coluna do filtro. Os dois não mudam: busque uma vez por tenant e guarde.
curl https://api.horusbi.com.br/v1/tenant/tables \
-H "Authorization: Bearer $TENANT_TOKEN"{
"status": "success",
"data": [
{
"id": 297,
"nome": "Vendas",
"columns": [
{ "id": 4358, "column_name": "LOJA", "data_type": "String", "label": "Loja" },
{ "id": 4361, "column_name": "REPRESENTANTE", "data_type": "String", "label": "Representante" }
]
}
]
}Aqui a tabela é a 297, a coluna Loja é a 4358 e a coluna Representante é a 4361. Os IDs mudam de um tenant para outro, mesmo quando os dois nascem do mesmo template; o que se repete é o nome. Ao criar um tenant, procure a tabela pelo nome e a coluna pelo column_name e guarde os IDs daquele tenant. Assim cada tenant novo custa uma chamada a mais, não uma por usuário.
tables Cada item libera uma tabela:
| Campo | Recebe |
|---|---|
tableId | ID da tabela, obrigatório |
filters | Lista de filtros de linha. Cada um tem column.column_id, operator e value |
allow_export | true deixa exportar os dados da tabela |
hidden_columns | IDs de coluna que a pessoa não vê |
show_unlinked | false esconde as tabelas da Aplicação sem ligação com esta |
expires_in | Data em que a liberação acaba (AAAA-MM-DD), ou null para não acabar |
O filtro tem o mesmo formato que o HEC grava: { "column": { "column_id": 4358 }, "operator": "=", "value": "12" }. Os operadores são =, !=, >, >=, <, <=, LIKE, NOT LIKE, IN, NOT IN, BETWEEN, NOT BETWEEN, IS NULL e IS NOT NULL. IN recebe uma lista (["01", "02"]) e BETWEEN, dois valores.
A lista tables substitui as liberações que o grupo ou o usuário tem. Uma tabela que fica de fora perde a liberação, e "tables": [] tira todas. Num item de tabela que já estava liberada, o campo que não vai no corpo mantém o valor. Liberação feita por Datamart, no HEC ou em /v1/tenant/datamart, não muda com tables.
Para cada loja, crie o grupo com a Mesa e a tabela filtrada:
curl -X POST https://api.horusbi.com.br/v1/tenant/groups \
-H "Authorization: Bearer $TENANT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"nome": "Loja 12",
"bi_desks": [7],
"tables": [
{
"tableId": 297,
"allow_export": true,
"filters": [{ "column": { "column_id": 4358 }, "operator": "=", "value": "12" }]
}
]
}'{ "success": true, "id": 34 }Guarde no seu sistema a relação entre a loja e o grupo 34. Cada gerente entra no grupo da loja dele ao ser criado, com "groups": [34] em Criar o usuário.
Crie o grupo Representantes uma vez, só com a Mesa ({"nome": "Representantes", "bi_desks": [7]}), e guarde o ID que volta, aqui 40. Depois, cada representante é criado no grupo e com o próprio filtro, numa chamada só:
curl -X POST https://api.horusbi.com.br/v1/tenant/users \
-H "Authorization: Bearer $TENANT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"login": "ana.souza@lojaexemplo.com.br",
"nome": "Ana Souza",
"groups": [40],
"tables": [
{
"tableId": 297,
"allow_export": false,
"filters": [{ "column": { "column_id": 4361 }, "operator": "=", "value": "10" }]
}
]
}'{ "success": true, "id": 812 }Para os 180, repita a chamada com o login e o código de cada um. Quem já existe recebe a mesma lista por PUT /v1/tenant/users, que troca a liberação dele, então dá para mandar a cada login transparente o código que o seu sistema tem hoje.
Os filtros que chegam a uma pessoa na mesma tabela valem juntos, todos ao mesmo tempo. Quem está nos grupos Loja 01 e Loja 02 vê as linhas que são das duas lojas ao mesmo tempo, ou seja, nenhuma. Para quem cuida de várias lojas, dê a liberação no usuário com o operador IN:
{ "column": { "column_id": 4358 }, "operator": "IN", "value": ["01", "02"] }Liberação sem filtro não abre nada que outra liberação filtrou: no caso dos representantes, o grupo dá a Mesa sem liberar a tabela, e o filtro de cada um continua valendo.
O filtro vale para Vendas e para as tabelas ligadas a ela por relacionamento. Uma tabela da Aplicação sem ligação com Vendas aparece inteira, a menos que a liberação leve "show_unlinked": false (veja Tabelas sem ligação).
Para conferir, gere a URL de login transparente de um gerente ou representante e abra o Dashboard: os números têm de ser só os dele. No HEC, a Central de Permissões mostra quem acessa cada tabela.
Cada lista enviada (groups, bi_desks, apps_deny, tables) substitui a anterior inteira. Mandar "groups": [3] para quem está nos grupos 3 e 5 tira o usuário do grupo 5. Campo que não vai no corpo fica como está.
Com isso dá para sincronizar a cada login: antes de gerar a URL, envie a lista completa de grupos que o perfil do usuário pede hoje.
curl -X PUT https://api.horusbi.com.br/v1/tenant/users \
-H "Authorization: Bearer $TENANT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"login": "ana.souza@lojaexemplo.com.br", "groups": [3, 4]}'Duas exceções protegem o que a API não administra: o grupo de administrador não sai por omissão da lista, e os grupos do Cliente marcados como invisíveis para o tenant também não. Esses grupos invisíveis também não recebem membro pela API (MANAGED_BY_PROVIDER).
curl -X DELETE https://api.horusbi.com.br/v1/tenant/users/delete \
-H "Authorization: Bearer $TENANT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"login": "ana.souza@lojaexemplo.com.br"}'O vínculo com o tenant acaba, e a sessão aberta cai na próxima página que o usuário abrir. Nos outros tenants dele, nada muda. Se o seu cliente HTTP não manda corpo em DELETE, use POST /v1/tenant/users/delete com o mesmo corpo.
| Resposta | code | Causa |
|---|---|---|
HTTP 404, "success": false, Usuário não encontrado | USER_NOT_FOUND | Em PUT ou DELETE, o e-mail não está vinculado a este tenant. Crie com POST |
HTTP 409, "success": false, Login já está cadastrado | USER_LOGIN_TAKEN | O e-mail já está vinculado a este tenant. Use PUT para mudar permissões |
HTTP 404, "success": false, Grupo 99 não encontrado | GROUP_NOT_FOUND | ID de grupo que não é deste tenant. Mesa, Aplicação e tabela dão DESK_NOT_FOUND, APP_NOT_FOUND e TABLE_NOT_FOUND |
HTTP 403, "success": false, Este grupo é administrado pelo fornecedor da plataforma e não pode ser alterado pela API. | MANAGED_BY_PROVIDER | O grupo é um grupo padrão do Cliente marcado como invisível para o tenant, e a API não põe ninguém nele |
HTTP 409, message com Limite de N usuário(s) atingido... | USER_QUOTA_EXCEEDED | O tenant chegou à franquia de usuários contratada |
HTTP 400, "success": false, Liberação de tabela inválida: ... | TABLE_ACCESS_INVALID | Um item de tables tem coluna que não é da tabela, operador sem o valor que ele pede ou tabela de outro tenant. errors aponta o campo de cada problema |
Operação de usuário que falha responde com o status da causa, "success": false, o motivo em message e o code. Até a versão de 28/10/2026 da API, esses erros voltavam com HTTP 200, e a franquia com HTTP 500 e o motivo só no campo error. A lista completa está nos códigos de erro da API.