---
url: 'https://docs.horusbi.com.br/dataviz/02-apps/ai-concepts.md'
---
# Aba Conceitos IA
A aba **Conceitos IA** é onde você cadastra e gerencia os **fatos** da aplicação — definições explícitas dos conceitos do seu negócio que o chat usa para responder com precisão e consistência.
::: tip Conceito antes de UI
Esta página é a **referência rápida da UI**. Para entender o que é um fato, quando criar, e os erros comuns (como o anti-padrão "dicionário gigante" com centenas de fatos pulverizados), veja **[Fatos: Ensinando seu Negócio](/ia/chat/fatos)** — leitura recomendada antes de cadastrar fatos novos.
:::
***
## Cadastrando um Fato
### Manualmente
1. Clique no botão **"Novo Insight"**.
2. Preencha os campos do formulário.
3. Clique em **Salvar**.

### Com Inteligência Artificial
1. Clique no botão **"Gerar Insight Automaticamente"**.
2. Descreva em linguagem natural o conceito que deseja criar.
3. A IA sugere a configuração do fato.
4. **Revise e ajuste** se necessário — em modelos ambíguos, a sugestão pode escolher uma coluna parecida em vez da que você quer.
5. Clique em **Salvar**.

***
## Campos do Fato
### Campos Obrigatórios
| Campo | Descrição |
|-------|-----------|
| **Nome** | Termo que os usuários utilizarão nas perguntas ("Faturamento", "Inadimplência"). Use a linguagem que sua empresa de fato usa. |
| **Valor Principal** | Coluna ou Expressão que representa esse conceito (deve ser uma **medida** — algo que se soma, conta ou agrega). |
### Campos Opcionais
| Campo | Descrição |
|-------|-----------|
| **Prompt** | Descrição adicional para ajudar a IA a entender o contexto e diferenciar conceitos similares. Use para nuance, não para criar outro fato. |
| **Valores Alternativos** | Outras métricas relacionadas que **compartilham o mesmo contexto** (data, filtros). É o caminho para evitar fatos redundantes. |
| **Coluna de Data** | Permite que a IA filtre por períodos temporais ("ontem", "último mês", "trimestre passado"). |
| **Filtros Padrão** | Contexto fixo aplicado automaticamente (ex.: apenas vendas com status "Confirmado"). |
Para entender o **propósito** de cada campo e como combiná-los bem, veja **[Os campos em detalhe](/ia/chat/fatos#os-campos-em-detalhe)**.
***
## Gerenciando Fatos
### Visualizar e Editar
A tabela lista todos os fatos cadastrados com:
* Nome
* Valores associados
* Configuração de data
Clique na seta para **expandir** e editar os detalhes do fato.

### Duplicar Fato
Utilize o ícone de **copiar** para criar um novo fato baseado em um existente. Útil quando você precisa de uma variação que de fato representa um conceito diferente.

::: warning
Duplicar é tentador, mas é o ponto de entrada para o anti-padrão "dicionário gigante". Antes de duplicar, pergunte: **isto é um conceito diferente, ou é o mesmo conceito com um agrupamento/período diferente?** No segundo caso, não duplique — o chat aplica agrupamentos e períodos sozinho a partir da pergunta.
:::
### Excluir Fato
Clique no ícone de **lixeira** para remover o fato. Uma confirmação será solicitada.

### Expandir/Recolher Todos
Utilize o botão **"Expandir Todos"** para ver os detalhes de todos os fatos de uma vez. Útil em revisões periódicas do catálogo.

***
## Próximos passos
* **[Fatos: Ensinando seu Negócio](/ia/chat/fatos)** — conceito, boas práticas e anti-padrões.
* **[Exemplos de Fatos](/ia/chat/exemplos-de-fatos)** — casos práticos lado a lado.
* **[Chat com Dados — Visão Geral](/ia/chat/)** — como o chat consome os fatos cadastrados.
---
---
url: 'https://docs.horusbi.com.br/dataviz/02-apps/dashboards.md'
---
# Aba Dashboards
A aba **Dashboards** é onde se criam e editam as telas visuais da Aplicação, posicionando Widgets (gráficos, KPIs, mapas) em um grid responsivo que se adapta a diferentes tamanhos de tela.
***
## 🖥️ Visão Geral da Interface
A tela de edição é dividida em quatro áreas:
1. **Barra de Dashboards** — Lista de Dashboards com abas clicáveis para navegação rápida
2. **Canvas de Edição** — Grid (grade) onde os Widgets são posicionados e redimensionados
3. **Sidebar Direita** — Propriedades e configurações do Widget selecionado
4. **Barra de Filtros** — Permite testar filtros durante a edição para validar o comportamento dos dados
***
## 📋 Gerenciando Dashboards
### Criar Novo Dashboard
1. Clique no botão **" + "** ou **"Criar Novo Dashboard"**
2. Preencha:
* **Nome**: Título que aparecerá na aba
* **Colunas**: Quantidade de colunas do grid (12, 24, 36 ou 60)
* **Widgets Flutuantes**: Permite posicionamento vertical livre
* **Criar usando IA**: Abre o assistente de IA para sugerir Widgets automaticamente
3. Clique em **Criar**

### Editar Dashboard
Clique com o botão direito na aba do Dashboard e selecione **Editar** para alterar:
* Nome
* Quantidade de colunas do grid
* Widgets Flutuantes

### Clonar Dashboard
Clique com o botão direito e selecione **Clonar** para criar uma cópia exata do Dashboard. Útil para criar variações sem reconfigurar do zero.

### Excluir Dashboard
Clique com o botão direito e selecione **Excluir**. Uma confirmação será solicitada antes da remoção.

### Reordenar Dashboards
Para alterar a ordem de exibição dos Dashboards na Aplicação:
* Clique no botão de engrenagem para habilitar a edição das abas

* Utilize as setas **`(< ou >)`** que aparecerão em cada aba para movê-las para a esquerda ou direita
* Clique no ícone de confirmação **`(✓)`** para salvar e sair do modo de edição

***
## 📐 Sistema de Grid
As Dashboards utilizam um grid (grade) responsivo para posicionar os Widgets com precisão.
### Colunas do Grid
| Colunas | Uso Recomendado |
|---------|-----------------|
| **12** | Dashboards simples, poucos Widgets |
| **24** | Uso geral, boa precisão de posicionamento |
| **36** | Dashboards complexos, muitos Widgets |
| **60** | Precisão máxima, Widgets pequenos e detalhados |
### Widgets Flutuantes
Quando ativado, permite posicionar Widgets em qualquer posição vertical, ignorando o empilhamento automático. Útil para layouts onde Widgets devem ficar lado a lado com alturas diferentes.
***
## 🧩 Trabalhando com Widgets
### Adicionar Widget
1. Abra a Sidebar de Widgets (ícone no menu ou arraste da Sidebar direita)
2. Escolha o tipo de Widget desejado
3. O Widget aparece no Canvas para posicionamento

### Mover e Redimensionar
* **Arrastar**: Clique e arraste para mover o Widget na grade
* **Redimensionar**: Arraste as bordas ou cantos do Widget para ajustar o tamanho

### Configurar Widget
Clique no Widget para selecioná-lo. A Sidebar direita exibirá todas as opções de configuração:
* Título e subtítulo
* Fonte de dados (colunas e Expressões)
* Cores e estilos visuais
* Comportamentos interativos (drill-down, cross-filtering)

### Excluir Widget
O Widget pode ser excluído de duas maneiras:
* Pelo menu: Clique no ícone de opções `( ... )` ou com o botão direito do mouse no Widget e selecione **Excluir Widget**
* Pela Sidebar direita: Com o Widget selecionado, clique no botão **Excluir Widget** localizado na parte inferior do painel

### 📱 Editor Modo Mobile
O editor permite configurar como a Dashboard se comporta em dispositivos móveis:
* **Ocultar Widgets no mobile** — Alguns Widgets podem ser removidos da visualização mobile para otimizar a experiência em telas pequenas
* **Reordenar Widgets** — A ordem dos Widgets pode ser diferente no mobile para priorizar as informações mais relevantes
Utilize o ícone de dispositivo móvel na barra de ferramentas para alternar entre os modos de edição desktop e mobile.

***
## 🔧 Barra de Filtros na Edição
Durante a edição, é possível aplicar filtros para testar como os Widgets se comportam com dados filtrados. Os filtros aplicados durante a edição são temporários e não afetam outros usuários.

***
## 🤖 Criação com IA
Ao marcar **"Criar usando IA"** na criação de uma Dashboard:
1. Uma nova Dashboard vazia é criada
2. A Sidebar de IA é aberta automaticamente
3. Descreva em linguagem natural o que deseja visualizar *("Criar Dashboard de vendas com KPIs e gráfico de evolução mensal")*
4. A IA sugere Widgets baseados nas Tabelas e Expressões disponíveis na Aplicação

### O que a IA analisa?
A IA não cria Widgets aleatórios. Ela analisa:
* **Tabelas disponíveis** na Aplicação e suas colunas
* **Relacionamentos** definidos entre as Tabelas
* **Rótulos das colunas** (por isso é importante utilizar nomes descritivos na modelagem)
> \[!TIP]
> A qualidade da Dashboard gerado pela IA depende muito da modelagem prévia. Relacionamentos corretos, rótulos claros e Fatos (Conceitos IA) bem definidos resultam em Dashboards mais relevantes e funcionais.
***
## ⌨️ Atalhos de Teclado
| Atalho | Ação |
|--------|------|
| `Delete` | Remove o Widget selecionado |
| `Ctrl + S` | Salva as alterações |
***
## 💡 Boas Práticas
1. **Comece pelo grid de 12 colunas** — Aumente as colunas apenas se houver necessidade de mais precisão no posicionamento
2. **Agrupe Widgets relacionados** — Mantenha métricas do mesmo tema próximas para facilitar a leitura
3. **Use espaço em branco** — Não sobrecarregue a tela com Widgets; o respiro visual melhora a compreensão
4. **Teste em diferentes tamanhos** — Verifique como a Dashboard se comporta em telas menores e dispositivos móveis
5. **Nomeie Dashboards claramente** — Utilize nomes descritivos para facilitar a navegação entre as abas
---
---
url: 'https://docs.horusbi.com.br/dataviz/02-apps/expressions.md'
---
# Aba Expressões (Fórmulas Calculadas)
A aba **Expressões** permite criar métricas e colunas calculadas que combinam dados de diferentes Tabelas ou aplicam lógica de negócio personalizada. É nela que dados brutos se transformam em indicadores de desempenho (KPIs), percentuais, categorizações e outras métricas avançadas.
> \[!TIP]
> Utilize Expressões para cálculos que dependem de mais de uma Tabela. Para cálculos simples dentro de uma única Tabela, prefira criar colunas calculadas diretamente no **HorusDW** — assim a métrica ficará disponível para todas as Aplicações.
***
## 🖥️ Visão Geral da Interface
A interface de Expressões é composta por:
1. **Barra de Ferramentas** — Busca, filtros por tipo/comportamento, ordenação e modo desenvolvedor
2. **Lista de Expressões** — Tabela com todas as Expressões criadas, exibindo label, tipo, comportamento e máscara
3. **Janela de Edição** — Modal que abre ao criar ou editar uma Expressão, com duas abas: Editor e Documentação
### Recursos da Lista
| Recurso | Descrição |
|---------|-----------|
| **Busca** | Filtra por label, código da Expressão ou descrição breve |
| **Filtro por Tipo** | Número, Texto ou Data |
| **Filtro por Comportamento** | Medida (agrega valores) ou Agrupador (categoriza dados) |
| **Ordenação** | Por nome (A-Z), recentes ou tipo |
| **Modo Desenvolvedor** | Exibe o código SQL *inline* na lista para visualização rápida |
***
## ➕ Criando uma Nova Expressão
### Manualmente
1. Clique no botão **"Nova Expressão"** (botão azul localizado no canto inferior direito da página). Caso ainda não exista nenhuma expressão criada, clique em **Criar primeira expressão**.
2. Na janela que abre, preencha os campos na aba **Editor**
3. Opcionalmente, documente a Expressão na aba **Documentação**
4. Clique em **Salvar Expressões** (botão azul localizado no canto superior direito da página, *`ícone de disquete`*)

### 🤖 Com Inteligência Artificial
1. Clique no botão **"Gerar Expressão Automaticamente"**
2. Preencha:
* **Nome**: Como a Expressão será chamada
* **Tipo de Dado**: Número, Data ou Texto
* **Comportamento**: Medida ou Agrupador
* **Modelo de IA**: Selecione o modelo desejado (GPT-4, Claude, etc.)
* **Descrição**: Explique em linguagem natural o que deve ser calculado

3. Clique em **Gerar**
4. A janela de edição abrirá com a Expressão gerada para revisão e ajuste
***
## 📝 Campos da Expressão
### Aba Editor
| Campo | Descrição | Valores |
|-------|-----------|---------|
| **Label** | Nome de exibição da Expressão | Texto livre |
| **Descrição Breve** | Explicação curta do propósito (máx. 150 caracteres) | Texto livre |
| **Tipo** | Tipo de dado retornado | Número, Data, Texto |
| **Comportamento** | Como a Expressão se comporta | Medida (agrega valores) ou Agrupador (categoriza dados) |
| **Máscara** | Formatação do valor (apenas para Número) | Moeda, Porcentagem, Decimais, etc. |
| **Código** | Fórmula SQL da Expressão | Editor Mônaco com autocomplete |
> \[!NOTE]
> A **Descrição Breve** é exibida na lista de Expressões e serve para documentar rapidamente o propósito da Expressão. É um recurso interno para desenvolvedores, não exibido para usuários finais da Aplicação.
### Aba Documentação
A aba Documentação oferece um editor de texto rico (WYSIWYG) para documentar a Expressão em detalhes:
* Explique a lógica de negócio por trás do cálculo
* Documente dependências e premissas
* Registre decisões de implementação
* Adicione exemplos de uso
> \[!IMPORTANT]
> A documentação é um recurso interno para desenvolvedores. Ela não é exibida para usuários finais, mas é essencial para manter o projeto organizado e facilitar manutenções futuras.
### 🎭 Máscaras Disponíveis para Números
| Máscara | Exemplo |
|---------|---------|
| Número Inteiro | 1.234 |
| Moeda (BRL) | R$ 1.234,56 |
| Moeda (USD) | $ 1,234.56 |
| Moeda (GBP) | £ 1,234.56 |
| Porcentagem | 12,34% |
| 1 a 4 decimais | 1.234,5678 |
| Segundos | 3h 20m 10s |
| Minutos | 02:30 (180 minutos) |
| Tamanho de Arquivo | 1,5 GB |
***
## 📐 Sintaxe de Fórmulas
### Referenciando Colunas e Expressões
| O que referenciar | Sintaxe | Exemplo |
|-------------------|---------|---------|
| Coluna de Tabela | `[Tabela]."Coluna"` | `[Fato Vendas]."VALOR_TOTAL"` |
| Expressão de Tabela | `[Tabela].[Expressão]` | `[Funcionários].[Tempo de Casa]` |
| Outra Expressão | `["Nome da Expressão"]` | `["Faturamento Realizado"]` |
### Funções SQL Disponíveis
O sistema utiliza **Apache Doris SQL**, que suporta:
```sql
-- Agregações
SUM(...), COUNT(...), AVG(...), MIN(...), MAX(...), COUNT(DISTINCT ...)
-- Operadores matemáticos
+, -, *, /
-- Lógica condicional
CASE WHEN ... THEN ... ELSE ... END
```
### Exemplos Práticos
**Soma simples:**
```sql
SUM([Fato Vendas]."VALOR_TOTAL")
```
**Reutilizando Expressões:**
```sql
["Faturamento Realizado"] / ["Meta de Faturamento"]
```
> \[!TIP]
> Essa divisão só é segura porque `Faturamento Realizado` e `Meta de Faturamento` agregam cada Tabela Fato (Vendas e Metas) de forma independente antes de combinar os dois valores. Se o modelo de dados unisse as duas Tabelas Fato diretamente numa única consulta, os valores seriam multiplicados (explosão cartesiana) em vez de comparados. Veja o problema e a correção executando as consultas abaixo:
**Categorização com CASE:**
```sql
CASE
WHEN [Vendas]."VALOR" > 1000 THEN 'Alto'
WHEN [Vendas]."VALOR" > 500 THEN 'Médio'
ELSE 'Baixo'
END
```
***
## 🔧 Recursos Avançados
### 🔀 Relacionamentos Secundários com `use()` {#relacionamentos-secundarios-com-use}
Utilize `use()` quando for necessário analisar dados usando uma chave de Relacionamento diferente da padrão definida no Modelo de Dados.
> \[!IMPORTANT]
> Para utilizar `use()`, é necessário primeiro configurar o Relacionamento Alternativo na **aba Tabelas**. A coluna alternativa deve estar cadastrada como Relacionamento Secundário entre as duas Tabelas.
**Sintaxe:**
```sql
use([TabelaReferência], [TabelaDados].COLUNA_ALTERNATIVA) EXPRESSÃO
```
**Parâmetros:**
* **TabelaReferência**: A Tabela de Dimensão com a qual deseja se relacionar (Calendário, Filial)
* **TabelaDados.COLUNA\_ALTERNATIVA**: A coluna da Tabela de Fatos cadastrada como Relacionamento Secundário
***
**Como configurar:**
1. Acesse a **aba Tabelas**
2. Selecione o Relacionamento entre as duas Tabelas
3. Clique em **" + Adicionar Chave "** para incluir colunas alternativas
4. O primeiro Relacionamento é o padrão; os demais ficam disponíveis para `use()`

**Exemplo — Análise pela data de fechamento ao invés de data de abertura:**
```sql
use([Calendário], [Oportunidades].DATA_GANHO) SUM([Oportunidades].VALOR)
```
Neste exemplo, ao invés de utilizar a coluna padrão de data das Oportunidades, o sistema relacionará os dados pela coluna `DATA_GANHO`, permitindo analisar o valor das oportunidades pela data em que foram ganhas.
***
### 📊 Expressões Analíticas com `${...}` {#expressoes-analiticas-com}
Utilize Expressões Analíticas quando for necessário calcular valores com **filtros diferentes** dos aplicados na visualização. Isso é essencial para comparações temporais (ano anterior, mês anterior) ou cálculos de participação.
**Sintaxe:**
```sql
${ EXPRESSÃO, FILTRO1, FILTRO2, ... }
```
**Componentes:**
* **EXPRESSÃO**: O cálculo a ser realizado (`SUM([Vendas]."VALOR")`)
* **FILTROS**: Condições que **substituem** os filtros visuais para esta Expressão
**Operadores suportados nos filtros:**
| Operador | Exemplo |
|----------|---------|
| `=` | `[Tabela]."Coluna" = 'valor'` |
| `!=` | `[Tabela]."Coluna" != 'valor'` |
| `IN` | `[Tabela]."Coluna" IN ('a', 'b', 'c')` |
| `NOT IN` | `[Tabela]."Coluna" NOT IN ('x', 'y')` |
| `LIKE` | `[Tabela]."Coluna" LIKE '%texto%'` |
| `NOT LIKE` | `[Tabela]."Coluna" NOT LIKE '%excluir%'` |
| `>`, `<`, `>=`, `<=` | `[Tabela]."Coluna" > 100` |
| `BETWEEN` | `[Tabela]."DATA" BETWEEN '2024-01-01' AND '2024-12-31'` |
***
#### Lendo Filtros Aplicados pelo Usuário
Dentro de Expressões Analíticas, é possível ler os valores dos filtros aplicados na visualização utilizando a sintaxe `<...>`:
**Sintaxe básica:**
```sql
<[Tabela]."Coluna", valor_padrão>
```
Se o usuário aplicou um filtro na coluna especificada, retorna o valor filtrado. Caso contrário, retorna o `valor_padrão`.
**Para campos de data com intervalo:**
| Modificador | Descrição | Exemplo |
|-------------|-----------|---------|
| `:START` | Início do período filtrado | `<[Calendário]."DATA":START, CURRENT_DATE>` |
| `:END` | Fim do período filtrado | `<[Calendário]."DATA":END, CURRENT_DATE>` |
| *(sem modificador)* | Valor exato do filtro | `<[Dim Filial]."NOME", 'Todas'>` |
**Como funciona com diferentes tipos de filtros:**
* **Filtro BETWEEN**: `:START` retorna a data inicial, `:END` retorna a data final
* **Filtro RELATIVE** ("Últimos 30 dias"): Calcula as datas automaticamente
* **Filtro IN com datas**: `:START` retorna a menor data, `:END` retorna a maior
* **Sem filtro aplicado**: Retorna o valor padrão especificado
***
#### 📈 Exemplos de Expressões Analíticas
**Vendas do mesmo período no ano anterior:**
```sql
${
SUM([Fato Vendas]."Valor Total"),
[Calendário]."DATA" BETWEEN
ADDYEARS(<[Calendário]."DATA":START, CURRENT_DATE>, -1)
AND
ADDYEARS(<[Calendário]."DATA":END, CURRENT_DATE>, -1)
}
```
**Acumulado do ano (YTD):**
```sql
${
SUM([Fato Vendas]."Valor Total"),
[Calendário]."DATA" BETWEEN
YEARSTART(<[Calendário]."DATA":START, CURRENT_DATE>)
AND
<[Calendário]."DATA":END, CURRENT_DATE>
}
```
**Vendas do mês anterior:**
```sql
${
SUM([Fato Vendas]."Valor Total"),
[Calendário]."DATA" BETWEEN
MONTHSTART(ADDMONTHS(<[Calendário]."DATA":START, CURRENT_DATE>, -1))
AND
MONTHEND(ADDMONTHS(<[Calendário]."DATA":END, CURRENT_DATE>, -1))
}
```
**Variação percentual ano a ano:**
```sql
(
SUM([Fato Vendas]."Valor Total")
-
${
SUM([Fato Vendas]."Valor Total"),
[Calendário]."DATA" BETWEEN
ADDYEARS(<[Calendário]."DATA":START, CURRENT_DATE>, -1)
AND
ADDYEARS(<[Calendário]."DATA":END, CURRENT_DATE>, -1)
}
)
/
${
SUM([Fato Vendas]."Valor Total"),
[Calendário]."DATA" BETWEEN
ADDYEARS(<[Calendário]."DATA":START, CURRENT_DATE>, -1)
AND
ADDYEARS(<[Calendário]."DATA":END, CURRENT_DATE>, -1)
}
```
***
### 📅 Funções de Data Disponíveis {#funcoes-de-data-disponiveis}
Estas funções são utilizadas **dentro de Expressões Analíticas** para manipular datas:
#### Funções de Adição/Subtração
| Função | Descrição | Exemplo |
|--------|-----------|---------|
| `ADDDAYS(data, dias)` | Adiciona dias em data | `ADDDAYS(TODAY(), 7)` |
| `ADDMONTHS(data, meses)` | Adiciona meses em data | `ADDMONTHS(TODAY(), -1)` |
| `ADDYEARS(data, anos)` | Adiciona anos em data | `ADDYEARS(TODAY(), -1)` |
| `SUBDAYS(data, dias)` | Subtrai dias da data | `SUBDAYS(TODAY(), 30)` |
| `SUBMONTHS(data, meses)` | Subtrai meses da data | `SUBMONTHS(TODAY(), 3)` |
| `SUBYEARS(data, anos)` | Subtrai anos da data | `SUBYEARS(TODAY(), 1)` |
#### Funções de Início/Fim de Período
| Função | Descrição | Exemplo |
|--------|-----------|---------|
| `MONTHSTART(data)` | Primeiro dia do mês | `MONTHSTART(TODAY())` → `2024-12-01` |
| `MONTHEND(data)` | Último dia do mês | `MONTHEND(TODAY())` → `2024-12-31` |
| `YEARSTART(data)` | Primeiro dia do ano | `YEARSTART(TODAY())` → `2024-01-01` |
| `YEAREND(data)` | Último dia do ano | `YEAREND(TODAY())` → `2024-12-31` |
| `QUARTERSTART(data)` | Primeiro dia do trimestre | `QUARTERSTART(TODAY())` |
| `QUARTEREND(data)` | Último dia do trimestre | `QUARTEREND(TODAY())` |
| `WEEKSTART(data)` | Primeiro dia da semana (domingo) | `WEEKSTART(TODAY())` |
| `WEEKEND(data)` | Último dia da semana (sábado) | `WEEKEND(TODAY())` |
#### Funções de Extração
| Função | Descrição | Retorno |
|--------|-----------|---------|
| `DAYOFWEEK(data)` | Dia da semana | 0 (domingo) a 6 (sábado) |
| `DAYOFMONTH(data)` | Dia do mês | 1 a 31 |
| `MONTH(data)` | Mês do ano | 1 a 12 |
| `YEAR(data)` | Ano | Ex: 2024 |
| `DATEDIFF(data1, data2)` | Diferença em dias entre datas | Número inteiro |
#### Constantes e Funções Gerais
| Função/Constante | Descrição |
|------------------|-----------|
| `TODAY()` | Data de hoje |
| `CURRENT_DATE` | Data atual (constante) |
| `MIN(valor1, valor2, ...)` | Menor valor entre os argumentos |
| `MAX(valor1, valor2, ...)` | Maior valor entre os argumentos |
> \[!TIP]
> `FIRSTDAYOFMONTH` e `LASTDAYOFMONTH` são aliases para `MONTHSTART` e `MONTHEND` respectivamente.
***
## ✅ Validações
O editor valida automaticamente:
* **Referências inválidas** — Colunas ou Expressões que não existem no modelo
* **Campos obrigatórios** — Label, Tipo e Fórmula devem estar preenchidos
* **Expressões circulares** — Uma Expressão não pode referenciar a si mesma
Erros aparecem em vermelho abaixo do campo com problema.
***
## 💡 Dicas de Uso
1. **Utilize o autocomplete** — O editor sugere Tabelas, colunas e Expressões durante a digitação
2. **Teste incrementalmente** — Crie Expressões simples primeiro, depois combine em Expressões mais complexas
3. **Documente as Expressões** — Utilize a aba Documentação para registrar a lógica de negócio
4. **Adicione descrições breves** — Facilita encontrar Expressões na busca e entender rapidamente seu propósito
5. **Prefira reutilização** — Crie Expressões básicas e combine-as em Expressões mais complexas
6. **Atenção aos tipos** — Certifique-se de que as colunas usadas em comparações são do tipo correto
7. **Utilize o modo desenvolvedor** — Ative para visualizar o código das Expressões diretamente na lista
---
---
url: 'https://docs.horusbi.com.br/dataviz/02-apps/general.md'
---
# Aba Geral (Configurações da Aplicação)
A aba **Geral** concentra as configurações globais da Aplicação, incluindo identidade visual, organização de Dashboards, Filtros Padrão e funcionalidades avançadas.
***
## 🎨 Identidade da Aplicação
### Nome
O título principal da Aplicação, exibido na Home, nas listagens e nas buscas.
### Descrição
Texto breve que aparece abaixo do nome na listagem de Aplicações. Utilize para descrever o propósito e o público-alvo da análise.
### Cor Padrão
Cor tema da Aplicação, utilizada em:
* Ícone na Home e nas listagens
* Elementos de destaque dentro da Aplicação
* Identificação visual rápida
Clique no seletor de cores para escolher ou digite um código hexadecimal diretamente.
### Ícone
Ícone que representa a Aplicação na Home e menus. Escolha entre centenas de ícones disponíveis na biblioteca integrada.
***
## 📋 Gerenciamento de Dashboards
Nesta seção é possível reorganizar e configurar as Dashboards sem a necessidade de abrir o editor de Widgets.
### Reordenar Dashboards
Clique no botão da engrenagem `(localizado ao lado da listagem das abas)` para abrir o editor de reposicionamento das abas de Dashboard e reposicione as mesmas de acordo com a ordem desejada.
### Editar Dashboard
Clique no ícone de três pontos `( ... )` ao lado do nome da Dashboard `(posicione o mouse sobre a aba para exibir a opção)` para alterar:
* Nome
* Quantidade de colunas do grid (12, 24, 36 ou 60)
* Widgets Flutuantes (posicionamento vertical livre)
### Remover Dashboard
Clique no ícone de lixeira para excluir a Dashboard. Uma confirmação será solicitada antes da remoção.
***
## 🔧 Filtros Padrão {#filtros-padrao}
Configure filtros que são aplicados automaticamente toda vez que a Aplicação é aberta.
**Exemplo de uso:**
* Filtrar por "Ano = Ano Atual" para que a Aplicação sempre abra com dados do ano corrente
* Filtrar por "Status = Ativo" para excluir registros inativos por padrão
> \[!TIP]
> O usuário pode remover ou alterar os Filtros Padrão durante a navegação. Eles serão reaplicados automaticamente ao reabrir a Aplicação.
> \[!TIP]
> Para usar o Filtro Padrão de data junto com Tabela Calendário, datas alternativas e granularidade por Widget, veja a receita [Calendário e Filtros Dinâmicos](/guia/receitas/calendario-filtros).
***
## 🔬 Configurações Avançadas
> \[!NOTE]
> As opções abaixo só aparecem para usuários com **permissão completa de edição**. Usuários com edição limitada não terão acesso a estas configurações.
### Mostrar Última Atualização
Quando ativado, exibe a data/hora da última carga de dados no rodapé da Aplicação. Útil para que os usuários saibam quão recentes são os dados exibidos.
**Selecionar Tabelas:** É possível escolher quais Tabelas considerar para exibir a data de atualização. Se nenhuma for selecionada, o sistema considera todas as Tabelas da Aplicação.
***
## 📊 Dimensões Dinâmicas
Dimensões Dinâmicas transformam dados armazenados em formato **EAV (Entity-Attribute-Value)** em colunas utilizáveis em Dashboards e Relatórios. Esse recurso é ideal para cenários onde os atributos variam entre registros (campos customizáveis por cliente).
### O que é EAV?
É um padrão de armazenamento onde atributos variáveis são guardados em linhas ao invés de colunas fixas:
| Entidade | Atributo | Valor |
|----------|----------|-------|
| Cliente 1 | Segmento | Varejo |
| Cliente 1 | Região | Sul |
| Cliente 2 | Segmento | Atacado |
| Cliente 2 | Região | Norte |
### Criando uma Dimensão Dinâmica
1. Clique em **"Nova Dimensão"**
2. Configure:
* **Nome**: Como a Dimensão será chamada nos Relatórios
* **Descrição**: Explicação do propósito (opcional)
* **Coluna de Entidade**: Identifica o registro (ID do Cliente)
* **Coluna de Atributo**: Contém o nome do atributo ("Segmento", "Região")
* **Coluna de Valor**: Contém o valor correspondente ao atributo
* **Filtros Padrão**: Restringe quais atributos considerar
* **Ativo**: Habilita ou desabilita a Dimensão
3. Clique em **Salvar**

### Resultado
Após configuração, os valores únicos da **Coluna de Atributo** se tornam colunas virtuais que podem ser usadas em:
* Filtros de Dashboard e Explorer
* Agrupamentos de Relatórios
* Expressões calculadas
***
## 🗑️ Ações de Manutenção
### Excluir Aplicação
Remove permanentemente a Aplicação e todos as suas Dashboards, Expressões e configurações.
> \[!CAUTION]
> Esta ação não pode ser desfeita. Certifique-se de que nenhum usuário depende desta Aplicação antes de excluí-la.

---
---
url: 'https://docs.horusbi.com.br/dataviz/02-apps/tables.md'
---
# Aba Tabelas (Modelo de Dados)
A aba **Tabelas** é onde se define o Modelo de Dados da Aplicação. Nela, são selecionadas quais Tabelas do HorusDW estarão disponíveis para análise e como elas se relacionam entre si.
> \[!TIP]
> Um Modelo de Dados bem construído é a base para filtros automáticos, análises consistentes e respostas precisas da IA.
***
## 🖥️ Visão Geral da Interface
A tela é dividida em três áreas:
1. **Canvas Central** — Área visual onde as Tabelas são posicionadas e conectadas por linhas de Relacionamento
2. **Sidebar Esquerda** — Lista de Tabelas disponíveis no HorusDW para adicionar ao modelo
3. **Sidebar Direita** — Propriedades da Tabela ou Relacionamento selecionado

***
## ➕ Adicionando Tabelas
### Pela Sidebar
1. Abra a Sidebar esquerda clicando no ícone de menu
2. Navegue pelas Tabelas disponíveis (organizadas por Mesa)
3. Clique na Tabela desejada pelo ícone de `+` para adicioná-la ao Canvas
As Tabelas adicionadas aparecem como "nós" no Canvas central, exibindo:
* Nome da Tabela
* Lista de colunas disponíveis
* Indicador visual de colunas usadas em Relacionamentos
***
## 🔗 Criando Relacionamentos
Os Relacionamentos (ou *Joins*) definem como diferentes Tabelas conversam entre si. Na prática, funcionam como um cruzamento automático de informações: ao aplicar um filtro em uma Tabela de referência (selecionar um "Cliente"), o sistema propaga esse filtro automaticamente, exibindo apenas os dados correspondentes nas outras Tabelas conectadas (as "Vendas" daquele cliente).
### Direção e Cardinalidade
Todos os Relacionamentos no Horus são **direcionais** e do tipo **um-para-muitos** (1:N). A seta no Canvas aponta da Tabela com valores únicos (1) para a Tabela com valores repetidos (N):
```mermaid
erDiagram
CLIENTES ||--o{ VENDAS : "1 cliente : N vendas"
```
Na prática:
* A Tabela de **origem** da seta (Clientes) deve ter **valores únicos** na coluna de chave — cada cliente aparece uma única vez
* A Tabela de **destino** da seta (Vendas) pode ter **valores repetidos** — um cliente pode ter muitas vendas
O sistema **valida essa unicidade ao salvar**. Se a coluna de chave da Tabela de origem contiver valores duplicados, o salvamento será bloqueado com um erro. Isso garante que os dados não sejam multiplicados incorretamente.
> \[!TIP]
> Uma forma simples de pensar: a seta sempre sai da Tabela de referência (Dimensão) e aponta para a Tabela de Dados (Fato). A Dimensão tem registros únicos (cada cliente, cada produto, cada data), a Fato tem os eventos que se repetem (vendas, pedidos, metas).
### Tipo de Tabela
Ao selecionar uma Tabela no Canvas, a Sidebar exibe o campo **Tipo da Tabela** com duas opções:
| Tipo | Quando usar |
|------|-------------|
| **Normal** | Tabelas comuns — Dimensões (chave única) e Fatos (eventos). É o padrão. |
| **Tabela de Ligação (n-n)** | Tabelas intermediárias para Relacionamentos muitos-para-muitos. Ex: uma Tabela `Aluno_Curso` que liga `Alunos` a `Cursos`, onde cada aluno pode ter vários cursos e cada curso vários alunos. |
O DataViz não suporta Relacionamentos muitos-para-muitos diretos — sempre é necessária uma Tabela intermediária. Ao marcar uma Tabela como **Tabela de Ligação (n-n)**, o sistema entende que ela funciona como ponte e relaxa a exigência de unicidade nessa Tabela.
### Passo a passo para conectar Tabelas
1. **Inicie a conexão (Arraste e Solte):** Clique e segure o mouse sobre a primeira Tabela e arraste até a segunda Tabela que deseja conectar.
2. **Confirme a ligação visual:** Ao soltar, uma linha de conexão aparecerá na tela.
3. **Configure as colunas de chave:** No painel lateral direito, selecione as colunas que representam o mesmo dado em ambas as Tabelas.
* *Exemplo:* Ligar `Clientes.ID_CLIENTE` com `Vendas.ID_CLIENTE`.
### Configurando as Colunas de Chave
Após criar a conexão visual, é necessário especificar quais colunas conectam as Tabelas:
| Campo | Descrição |
|-------|-----------|
| **Coluna Esquerda** | Coluna da Tabela de origem (chave única) |
| **Coluna Direita** | Coluna da Tabela de destino (chave estrangeira) |
> \[!IMPORTANT]
> As colunas devem ter **tipos compatíveis**. O sistema validará automaticamente e alertará sobre incompatibilidades.
***
## ✅ Validações Automáticas
Ao salvar, o sistema verifica automaticamente a integridade do modelo:
### Relacionamentos Incompletos
Toda linha de conexão deve ter as colunas de chave definidas. Relacionamentos sem configuração impedem o salvamento.
### Chaves Duplicadas na Origem
A coluna de chave da Tabela de origem (lado "1" do Relacionamento) deve conter **valores únicos**. Se o sistema detectar duplicatas, o salvamento será bloqueado.
**Soluções sugeridas:**
* Escolher uma coluna diferente para a chave
* Corrigir os dados duplicados na origem (HorusDW)
* Adicionar mais colunas à chave (chave composta)
### ⚠️ Relacionamento entre Tabelas Fato
Tabelas Fato (como Vendas e Metas) normalmente **não possuem chaves únicas entre si**, ambas contêm eventos repetidos. Por isso, conectar duas Fatos diretamente não funciona: nenhuma das duas consegue ser o lado "1" do Relacionamento, e o resultado seria uma multiplicação incorreta dos dados (explosão cartesiana).
> \[!WARNING]
> A solução para este caso é conectar Fatos através de **Dimensões compartilhadas** (Tabelas de Referência com chaves únicas). Consulte o guia completo de [Modelagem de Dados](./data-modeling) para entender o problema, as soluções e testar com exemplos interativos.
***
## 🛠️ Funcionalidades Adicionais
### Visualizar Dados da Tabela
Passe o mouse sobre uma Tabela e selecione "Visualizar Dados" para ver uma amostra dos registros armazenados.

### Trocar Tabela
Para substituir uma Tabela por outra (mantendo os Relacionamentos existentes), passe o mouse sobre a Tabela e selecione "Trocar Tabela". Útil quando:
* A Tabela foi renomeada no HorusDW
* É necessário utilizar uma versão diferente dos mesmos dados

### Recalcular Posições
Se as Tabelas ficarem sobrepostas ou desorganizadas no Canvas, utilize o botão "Recalcular Posição" para reorganizar automaticamente o layout.

### Remover Tabela ou Relacionamento
Selecione o item e pressione `Delete` ou `Backspace`.

----------------------
## 🔀 Relacionamentos Secundários {#relacionamentos-secundarios}
Quando uma Tabela possui múltiplas colunas que podem se conectar à mesma Dimensão, é possível configurar **Relacionamentos Secundários** (também chamados de "Relacionamentos Alternativos"). Isso é essencial para análises que precisam utilizar diferentes chaves de data ou referência.
### Configurando Múltiplas Chaves de Relacionamento
Na Sidebar de configuração do Relacionamento, é possível adicionar mais de uma coluna de chave:
1. Selecione o Relacionamento entre duas Tabelas
2. Na Sidebar direita, será exibida a lista de chaves configuradas
3. Clique em **"+ Adicionar Chave"** para incluir colunas alternativas
4. O **primeiro** Relacionamento da lista é o **padrão** (usado automaticamente)
5. Os demais ficam disponíveis para uso com a função `use()` nas Expressões

**Exemplo de configuração:**
| Ordem | Chave Primária (Calendário) | Chave Estrangeira (Oportunidades) | Uso |
|-------|-----------------------------|------------------------------------|-----|
| 1º | Período | Data de Fechamento | **Padrão** - usado automaticamente |
| 2º | Período | Data de Ganho da Oportunidade | Disponível via `use()` |
| 3º | Período | Data de Perda da Oportunidade | Disponível via `use()` |
> \[!IMPORTANT]
> A ordem dos Relacionamentos importa! O primeiro é sempre o padrão. Arraste para reordenar se necessário.
### Usando Relacionamentos Alternativos nas Expressões
Na aba **Expressões**, a função `use()` permite alternar qual Relacionamento está ativo:
```sql
-- Soma de valores pela data de ganho (ao invés da data padrão)
use([Calendário], [Oportunidades].DATA_GANHO) SUM([Oportunidades].VALOR)
-- Soma de valores pela data de perda
use([Calendário], [Oportunidades].DATA_PERDA) SUM([Oportunidades].VALOR)
```
> \[!TIP]
> A coluna usada em `use()` deve estar cadastrada como Relacionamento Secundário na aba Tabelas. Caso contrário, o sistema não encontrará a chave.
> \[!WARNING]
> **Limitações do `use()`:**
>
> * Aceita apenas **colunas físicas** da Tabela, não Expressões/colunas calculadas
> * **Chaves compostas não são suportadas** - aceita apenas uma coluna como chave
>
> Para cenários que exigem chaves compostas ou uso de Expressões, recomenda-se criar uma coluna calculada no **HorusDW** que concatene/processe as chaves (`CONCAT(COD_FILIAL, '_', COD_PRODUTO)`) e utilizar essa coluna física como chave do Relacionamento.
***
## ⌨️ Atalhos de Teclado
| Atalho | Ação |
|--------|------|
| `Delete` ou `Backspace` | Remove a Tabela ou Relacionamento selecionado |
| `Ctrl + S` | Salva as alterações |
| Scroll do mouse | Zoom in/out no Canvas |
| Arrastar o Canvas | Move a visualização |
***
## 🎯 Filtros Pré-Aplicados {#filtros-pre-aplicados}
Ao selecionar uma Tabela no Canvas, a Sidebar localizada à direita exibe a seção **Filtros Pré-Aplicados**. São filtros fixos que o sistema aplica automaticamente em toda consulta que envolver aquela Tabela.

### Para que servem
| Situação | Exemplo |
|----------|---------|
| Restringir o escopo de dados | Só produtos do tipo Combustível |
| Excluir registros irrelevantes | Remover clientes inativos |
| Controlar acesso por filial | Só dados da filial São Paulo |
### Propagação automática de filtros
Um filtro colocado numa Tabela de Referência (Dimensão) se propaga para todas as Tabelas de Dados (Fatos) conectadas a ela. Não é necessário repetir o mesmo filtro em cada Tabela.
**Exemplo:** Ao aplicar `Tipo = "Combustível"` na Tabela Produto, as consultas de Vendas, Estoque e Compras conectadas a Produto já trazem apenas combustíveis automaticamente.
```mermaid
flowchart LR
P["Produto Filtro: Tipo = Combustível"]
P --> V[Vendas]
P --> E[Estoque]
P --> C[Compras]
```
A propagação também funciona em cadeia: se Categoria filtra Produto, e Produto está conectado a Vendas, o filtro de Categoria já reflete nas Vendas.
### Como funciona a propagação entre Dimensões
Quando duas Tabelas de Referência (Dimensões) estão conectadas às mesmas Tabelas de Dados, os filtros de uma podem afetar os resultados da outra — **mas apenas se o Relatório incluir colunas da Tabela de Dados que conecta ambas**.
**Exemplo:** Calendário e Empresa estão ambos conectados a Faturamento.
```mermaid
flowchart TB
Cal["Calendário Filtro: Março/2026"] --> Fat[Faturamento]
Emp[Empresa] --> Fat
```
* Se o Relatório inclui `SUM(Faturamento.Valor)` e `Empresa.Nome`, o filtro de Calendário **será aplicado** — porque Faturamento já faz parte da consulta e conecta as duas Dimensões
* Se o Relatório inclui apenas `Empresa.Nome` (sem colunas de Faturamento), o filtro de Calendário **não afeta** a lista de empresas — o sistema reconhece que Faturamento está presente apenas pelo filtro e não cria a ponte entre elas.
Esse comportamento é o esperado na maioria dos cenários analíticos:
* Um filtro de `Filial = "São Paulo"` na Tabela Empresa faz com que a lista de Vendedores exiba apenas os da filial SP — desde que o Relatório utilize colunas da Fato que conecta ambos
* Um filtro de data no Calendário filtra vendedores quando o Relatório consulta dados de vendas no período
> \[!TIP]
> Para visualizar **todos os registros** de uma Tabela de Referência (listar todas as empresas cadastradas), basta não incluir colunas da Tabela de Dados que conecta as Dimensões no mesmo Relatório, ou consultar a Tabela diretamente pelo HorusDW.
### Filtros em Tabelas Fato NÃO propagam para Dimensões
Filtros Pré-Aplicados em Tabelas de Dados (Fatos) **não** são incluídos na consulta quando o Relatório contém apenas colunas de Tabelas de Referência (Dimensões). A Tabela Fato só participa da query se houverem colunas selecionadas no Relatório.
**Exemplo:** Se `Vendas` tem um Filtro Pré-Aplicado `STATUS = 'ATIVO'` e o Relatório consulta apenas `Calendario > Mês`, a consulta retornará todos os meses do Calendário sem filtrar por Vendas.
Da mesma forma, filtros entre duas Dimensões conectadas apenas via uma Fato **só se aplicam** se o Relatório incluir colunas dessa tabela. Sem a Fato no Relatório, não há caminho de JOIN válido entre as Dimensões, e o filtro é ignorado.
### 💡 Dicas para Filtros Pré-Aplicados
* **Adicione o filtro na Tabela de Referência (Dimensão)** — não na Tabela de Dados (Fato) — para propagação automática em todas as Fatos conectadas. Além de mais simples, isso evita efeitos colaterais
* **Não duplique filtros** em várias Tabelas. Um filtro na Dimensão já basta
* **Quanto mais conexões o modelo tiver**, mais os filtros se propagam entre si. Modelos bem organizados (Star ou Snowflake) minimizam propagações indesejadas
> **Exemplo prático:** Se a Aplicação tem as Fatos Faturamento, Clientes e Estoque, e são configurados Filtros Pré-Aplicados em cada uma delas, ao consultar apenas `Empresa > Nome` no Explorer, o filtro de Fato será ignorado porque nenhuma Fato tem colunas selecionadas. Se o filtro estivesse na Dimensão Empresa, ele seria aplicado diretamente.
***
## 💡 Boas Práticas
1. **Use nomes claros** — Renomeie as Tabelas no HorusDW para facilitar o entendimento
2. **Evite ciclos** — Não crie Relacionamentos circulares entre Tabelas
3. **Documente Relacionamentos Secundários** — Adicione comentários para mapear quando usar cada caminho
4. **Estruture o modelo em Star ou Snowflake** — Dimensões no centro, Fatos conectadas a elas. Evite conexões diretas entre Fatos ou entre Dimensões não relacionadas
---
---
url: 'https://docs.horusbi.com.br/ia/agentes.md'
---
# Agentes de IA
Um **Agente de IA** é uma persona de investigação reutilizável: você define um nome, o que ela deve investigar, como ela deve responder e em quais aplicações ela pode consultar dados. A partir daí, o mesmo agente pode **responder no chat** e/ou ser **usado por um alerta** — a mesma definição, reaproveitada em vários lugares.
::: tip Em uma frase
Um agente é "quem investiga". Você define a persona uma vez, no módulo **Agentes de IA**; o chat conversa com ela e os alertas a chamam para gerar o conteúdo.
:::
Dois agentes já vêm prontos, sem nenhuma configuração:
| Agente | O que faz |
|---|---|
| **Lumia** | O assistente de conversa do Lumo. Sempre disponível no [chat](/ia/chat/) — veja o card dela no módulo Agentes de IA mostrando que ela responde no chat. |
| **Agente padrão de investigação** | Quem investiga os blocos de IA de alerta que **não** têm um agente próprio selecionado. É o comportamento de sempre, sem mudança nenhuma para quem já usa alertas com IA. |
Além desses, qualquer usuário com permissão pode criar **agentes próprios** — personas com instruções e escopo específicos, como "Analista de inadimplência" ou "Vigia do funil comercial".
***
## Como criar um agente
No módulo **Agentes de IA**, o botão **Criar agente** abre um campo único: **descreva em uma frase o que você quer**.
```
Ex.: acompanhar inadimplência por filial e avisar quando o índice subir
```
A partir dessa frase, a IA monta um **rascunho completo** — nome, as instruções de "Como investigar" e "Como responder", a aplicação sugerida e as opções de esforço e modelo — e abre o formulário já **preenchido**, pronto para você revisar. Nada é salvo nesse momento; é só um ponto de partida melhor do que uma tela em branco.
::: info Prefere montar do zero?
O link discreto **"ou preencher do zero"**, abaixo do campo, abre o mesmo formulário vazio para quem prefere escrever cada instrução manualmente.
:::
### O formulário
O editor tem **duas colunas sempre visíveis**: a configuração à esquerda, o teste ao vivo à direita. Testar não é uma etapa separada — faz parte de editar.
Do lado da configuração:
* **Geral** — nome, uma descrição curta e a chave **"Disponível no chat"**, que decide se o agente aparece no seletor da Lumia para conversas sob demanda.
* **Instruções** — dois campos separados:
* **Como investigar** — o que o agente deve olhar e como deve raciocinar antes de responder.
* **Como responder** — o tom e o formato da resposta, o que não pode faltar.
* **Escopo** — em quais aplicações o agente pode consultar dados (até 3). Menos aplicações significa investigação mais focada.
* **Avançado** (recolhido por padrão) — ferramentas, esforço e modelo. Quem nunca abrir esse bloco já tem um agente funcional com os padrões do produto:
* **Ferramentas**: a lista do que o agente pode fazer para investigar (consultar dados, validar um nome digitado contra o valor exato no banco). Nesta etapa do produto, são todas de **leitura** — o agente não escreve nem altera nada.
* **Esforço de investigação**: quanto mais esforço, mais passos o agente pode dar até responder — e mais tempo leva. As opções são **Rápido**, **Padrão** e **Minucioso**.
* **Modelo**: **Rápido** ou **Aprofundado**, com o modelo real usado hoje em cada opção mostrado ao lado (esse mapeamento muda com o tempo, por isso é sempre exibido). Também é possível escolher um modelo específico; se ele deixar de estar disponível, o agente volta automaticamente para o Rápido, sem quebrar a execução agendada.
Do lado do teste (o **playground**): converse com o agente exatamente como ele está configurado no momento — mesmo com o rascunho ainda não salvo — ou use **"Executar briefing"** para simular o uso agendado, o mesmo caminho que um alerta segue. Nada dessa conversa de teste fica gravada no seu histórico de chat.
Só quando você estiver satisfeito é que o botão **Salvar** grava o agente de fato.
***
## Como o alerta usa um agente
Um alerta continua sendo a **tarefa**: ele decide **quando** disparar (agendamento ou uma condição) e **para quem** enviar. O que muda é o bloco de conteúdo de IA, agora chamado **Agente de IA**, que passa a ter dois campos:
* **Agente** — quem investiga. Você escolhe um dos seus agentes ou deixa em branco.
* **Briefing** — o que investigar *nesta execução específica*: o pedido, os pontos de atenção, o formato esperado. É o mesmo campo de texto de sempre.
A separação é: **instruções** (como investigar, como responder) pertencem ao agente; **briefing** (o quê, quando) pertence ao alerta. Um agente pode ser reaproveitado por vários alertas diferentes, cada um com seu próprio briefing.
::: info Sem agente selecionado, nada muda
Um bloco de IA sem agente escolhido roda exatamente como sempre rodou — com o **Agente padrão de investigação**. Alertas já configurados continuam funcionando sem qualquer ajuste.
:::
### Transformando um bloco existente em agente reutilizável
Se você já tem um alerta com um bloco de IA configurado (aplicações, briefing, tudo pronto) e quer reaproveitar essa mesma investigação em outro alerta, use **"Promover a agente"**. Em um clique, ele cria um agente novo com o escopo e o modelo daquele bloco — o alerta original continua entregando exatamente o mesmo conteúdo de antes, só que agora a persona tem nome e pode ser importada em outros alertas.
### Se um agente usado por um alerta é excluído
O alerta não quebra: ele volta a rodar com o Agente padrão de investigação, e você é avisado. Antes de excluir um agente em uso, a tela mostra quantos alertas dependem dele.
***
## Como cheguei nisso
Sempre que um agente responde — no chat, na pré-visualização de um alerta ou numa execução de verdade — os passos que ele seguiu ficam disponíveis para consulta, no painel **"Como cheguei nisso"**.
Ao abrir, você vê:
1. **O plano** — o que o agente pretendia investigar, quando ele explicita isso antes de começar.
2. **Os passos** — cada consulta que ele fez, em linguagem de gente: qual ferramenta usou, o que buscou, quantas linhas encontrou.
3. **A conclusão** — como aquilo virou a resposta final.
O painel nunca mostra consulta bruta ao banco, identificadores internos de coluna ou erros técnicos — ele existe para dar **confiança**, não para depurar.
Cada agente também tem uma aba própria de **Execuções**, com o histórico de investigações que ele já fez, de onde vieram (chat, alerta ou um teste) e quantos passos cada uma levou.
***
## O que o agente não faz
Para usar bem um agente, vale ter expectativa certa do que ele é:
* **Não escreve nem altera nada nos seus dados.** Hoje, todo agente só consulta — nenhuma ferramenta de ação está disponível.
* **Não age fora do BI.** Ele não envia mensagens, não aciona sistemas externos nem executa nada além de consultar seus dados e responder.
* **Não inventa número.** Todo valor que aparece na resposta veio de uma consulta real aos seus dados. Se o agente não conseguiu apurar algo, ele diz isso — em vez de arriscar um número que pareça plausível.
* **A qualidade da resposta depende de como suas aplicações e indicadores estão organizados.** Um agente investigando uma aplicação bem modelada, com nomes de coluna claros e indicadores bem definidos, responde melhor do que um investigando uma base confusa. Cuidar do modelo de dados continua sendo o alavancador nº 1 da qualidade das respostas.
Para manter o uso previsível, existe também um teto diário de investigações automáticas por conta (as que rodam sozinhas, sem alguém esperando na tela — como um alerta agendado). Ao atingir o teto do dia, o agente entrega um aviso honesto em vez de investigar; conversas no chat e testes no playground não contam para esse limite.
***
## Curadoria: o que a IA enxerga (avançado)
Por padrão, um agente enxerga todas as colunas visíveis das aplicações no seu escopo. Se você quer ser mais seletivo — por exemplo, esconder colunas técnicas ou pouco relevantes do que a IA usa para responder — use a curadoria de colunas, na tela **Colunas Visíveis** de cada aplicação (veja [Administração de Fatos do Tenant](./index.md)).
A regra é simples:
* **Se você não marcar nenhuma coluna de uma tabela**, o agente continua vendo **todas** as colunas dela. Nada muda até você decidir curar.
* **Assim que você marca ao menos uma coluna de uma tabela**, o agente passa a enxergar só as colunas marcadas daquela tabela.
Essa curadoria vale tanto para o chat quanto para os agentes usados em alertas — é a mesma configuração, um único lugar para ajustar.
***
## Recurso em beta
Agentes de IA é um recurso em **beta**, disponível mediante habilitação. Se você não vê o módulo no menu, fale com o administrador do seu tenant sobre ativar o acesso antecipado.
***
## Próximos passos
* **[Conectar Assistente de IA (MCP)](/ia/mcp)** — o card "Use no MCP" deste mesmo módulo: leva seus indicadores para dentro do Claude, Claude Code ou Cursor.
* **[Chat com Dados](/ia/chat/)** — como a Lumia responde e como ensinar o vocabulário do seu negócio com fatos.
* **[Alertas Inteligentes](/dataviz/04-features/alerts/)** — disparo, canais e os outros tipos de conteúdo que podem compor uma mensagem.
* **[Conteúdo: IA](/dataviz/04-features/alerts/content-ai.md)** — o bloco de IA do alerta em detalhe (briefing, relatórios de exemplo, memória).
---
---
url: 'https://docs.horusbi.com.br/dataviz/04-features/alerts.md'
---
# Alertas Inteligentes
O módulo de **Alertas** permite que o Lumo monitore seus dados 24/7 e notifique os usuários proativamente, seja por um agendamento fixo ou quando uma regra de negócio for atendida.
Um alerta é montado em três camadas:
```
┌────────────┐ ┌────────────┐ ┌────────────┐
│ DISPARO │ → │ CONTEÚDO │ → │ CANAIS │
│ (quando?) │ │ (o quê?) │ │ (por onde?)│
└────────────┘ └────────────┘ └────────────┘
```
| Camada | Responde a | Opções |
|---|---|---|
| **Disparo** | Quando o alerta roda? | [Agendamento](./triggers.md), [Condicional](./conditions.md) |
| **Conteúdo** | O que vai na mensagem? | [Texto](./content-text.md), [Gráfico](./content-chart.md), [Relatório](./content-report.md), [IA](./content-ai.md) |
| **Canais** | Por onde sai? | [Email, WhatsApp, Telegram, Webhook](./channels.md) |
> \[!TIP]
> Um alerta pode combinar **vários blocos de conteúdo** (ex.: texto introdutório + análise da IA + relatório PDF anexo) e enviar pra **múltiplos canais** ao mesmo tempo.
***
## Tipos de Disparo
### Por Agendamento
O alerta dispara em **horários fixos**, independente do que está nos dados.
* **Diário**: ex., "todo dia útil às 08:00"
* **Semanal**: ex., "segundas às 09:00"
* **Mensal**: ex., "dia 05 de cada mês"
Uso típico: **digest diário** com KPIs do dia anterior, **fechamento mensal**, **briefing semanal de equipe**.
[Detalhes em Agendamento →](./triggers.md)
### Condicional
O alerta monitora os dados continuamente e **só dispara se uma regra for atendida**.
* **Verificação**: a cada atualização de tabela, a cada hora, ou diariamente
* **Lógica**: você define filtros sobre uma aplicação. O sistema testa periodicamente se "existem dados que atendem àqueles filtros"
* **Silêncio é normal**: se a consulta retorna 0 registros, nada é enviado
Uso típico: **anomalias** (margem negativa, estoque abaixo do mínimo), **SLA estourado**, **transações suspeitas**.
[Detalhes em Condicional →](./conditions.md)
***
## Tipos de Conteúdo
Cada alerta pode ter um ou mais **blocos de conteúdo** que compõem a mensagem entregue.
| Tipo | Quando usar |
|---|---|
| **[Texto](./content-text.md)** | Mensagem fixa com variáveis dinâmicas (ex.: "Vendas de hoje: {{venda\_total}}") |
| **[Gráfico](./content-chart.md)** | Snapshot visual de um widget ou dashboard inteiro |
| **[Relatório](./content-report.md)** | Tabela detalhada exportada em PDF ou Excel |
| **[IA](./content-ai.md)** | Análise gerada por IA com base em relatórios de exemplo e briefing |
> \[!NOTE]
> A ordem dos blocos no editor é a ordem em que aparecem na mensagem entregue.
***
## Canais de Entrega
Os mesmos blocos de conteúdo podem ser enviados simultaneamente para:
* 📧 **Email**: anexo de PDF/XLSX, imagens inline
* 💬 **WhatsApp**: texto + imagens, PDF/XLSX como documento
* 📲 **Telegram**: texto + imagens, PDF/XLSX como documento
* 🔌 **Webhook**: payload JSON, ideal para integrações
[Configuração de canais →](./channels.md)
> \[!TIP]
> Integrando com sistemas externos via webhook (n8n, Make, Zapier, WhatsApp via Meta, Slack)? Veja a seção dedicada **[Webhook](./webhook/)** com contrato completo do payload, exemplos por ferramenta e código receptor pronto em Node.js e Python.
***
## Fluxo Completo
```mermaid
flowchart LR
A[Disparo] -->|agendamento ou condição atendida| B[Gerar conteúdos]
B --> C[Texto]
B --> D[Gráfico]
B --> E[Relatório]
B --> F[IA]
C & D & E & F --> G[Empacotar entrega]
G --> H[Email]
G --> I[WhatsApp]
G --> J[Telegram]
G --> K[Webhook]
```
Cada entrega gera um registro auditável com status, erros e os assets gerados. Falha em um canal não impede os demais, mas se **qualquer** canal esperado falhar, a entrega inteira é marcada com status de erro (filosofia "fail loud", sem falha silenciosa).
***
## Permissões
O criador do alerta tem controle total (editar, excluir, forçar envio). Administradores podem ajudar a editar, mas a autoria não muda. Veja [Permissões e Compartilhamento](./permissions.md) para o detalhamento por papel.
***
## Próximos Passos
1. **[Configure o disparo](./triggers.md)** (agendado) ou **[a condição](./conditions.md)** (regra)
2. **Adicione conteúdos**: [texto](./content-text.md), [gráfico](./content-chart.md), [relatório](./content-report.md) ou [IA](./content-ai.md)
3. **Escolha os canais** de entrega: [Email/WhatsApp/Telegram/Webhook](./channels.md)
4. **Teste com o botão "Pré-visualizar"** antes de ativar
---
---
url: 'https://docs.horusbi.com.br/lumo/referencia/app.md'
description: >-
Referência do YAML de app de BI no Lumo: tabelas, relacionamentos, expressões,
indicadores e um exemplo completo.
---
# App
Um app é uma aplicação de BI. Ele vive em `apps/--.yaml` e reúne as tabelas do DW que o app enxerga, os relacionamentos entre elas, as expressões calculadas e os indicadores que ensinam a IA a responder sobre os dados.
O app não carrega dados. Crie antes os flows e as tabelas do DW, depois o app:
```bash
lumo new app --name "Comercial"
```
Ligue o autocomplete no editor colando esta linha no topo do arquivo:
```yaml
# yaml-language-server: $schema=https://docs.horusbi.com.br/schemas/v2/app.schema.json
```
Os dashboards e os widgets moram em arquivos próprios, com `kind: app-dashboard`, validados pelo [app-dashboard.schema.json](https://docs.horusbi.com.br/schemas/v2/app-dashboard.schema.json). Esta página cobre o app.
## Modelo de configuração
```yaml
# ── header ────────────────────────────────────────────────
id: integer # obrigatório, >= 1
kind: app # obrigatório, literal "app"
lumo: v2 # obrigatório, literal "v2"
tenantId: integer # obrigatório, >= 1
---
# ── body ──────────────────────────────────────────────────
nome: string # obrigatório, mínimo 1 caractere
descricao: string | null
icon: string | null
color: string | null # hex: #RGB, #RRGGBB ou #RRGGBBAA
tags: [string]
config: # obrigatório
show_last_loadedat: boolean # exibe a data da última carga
show_last_loadedat_tables: [integer] # ids de tabela consideradas
tables: # tabelas do DW que o app enxerga
- tableId: integer # obrigatório
tipo: string | null # dimension | n2n | null. default: dimension
nome_visualizacao: string | null # renomeia a tabela SÓ neste app
guid: string | null
columns: # overlay por coluna, só neste app
NOME_DA_COLUNA:
visible: boolean # false esconde do gerador de relatórios
label: string | null # renomeia a coluna só neste app
relationships:
- from_table_id: integer # obrigatório
to_table_id: integer # obrigatório
column_left_id: integer # obrigatório
column_right_id: integer # obrigatório
relationshipType: string # obrigatório: ONE_TO_ONE | ONE_TO_MANY | MANY_TO_ONE | MANY_TO_MANY
from_column: string
to_column: string
ignoreTime: boolean # ignora a hora ao casar colunas de data
facts: # indicadores (dicionário de negócio da IA)
- nome: string # obrigatório
primaryColumn: string # obrigatório, formato ":"
alternativeColumns: [string] | null
dateColumnId: integer | null
prompt: string | null
filters: []
expressions: [] # colunas calculadas do app
filtros: [] # filtros pré-aplicados
dynamic_dimensions: []
```
`tables[].tipo` é legado do motor de BI: `dimension` cobre **toda** tabela normal, tanto fato quanto dimensão. Use `n2n` apenas em tabelas ponte de muitos-para-muitos. Os valores `fact`, `dim` e `bridge` não existem, e o servidor rejeita.
## Configuração completa
```yaml
# apps/comercial--13044.yaml
# yaml-language-server: $schema=https://docs.horusbi.com.br/schemas/v2/app.schema.json
id: 13044
kind: app
lumo: v2
tenantId: 853
---
nome: Comercial
descricao: Vendas por cliente, produto e região.
icon: mdi:chart-bar
color: "#10b981"
config:
show_last_loadedat: true
show_last_loadedat_tables: [44940]
tables:
# Toda tabela normal usa tipo: dimension, inclusive a tabela fato.
- tableId: 44940
tipo: dimension
columns:
# visible: false só tira a coluna do gerador de relatórios do usuário
# final. Ela continua utilizável em expressões e widgets: isso é
# limpeza de interface, não controle de acesso.
PEDIDO_ID:
visible: false
- tableId: 44650
tipo: dimension
- tableId: 44892
tipo: dimension
# Renomeia a tabela SÓ neste app. Depois disso, expressões e dataSources
# precisam endereçá-la por este nome: [Vendedor]."NOME", não pelo nome
# físico do DW.
nome_visualizacao: Vendedor
relationships:
# A dimensão calendário liga em muitos registros do fato.
- from_table_id: 44650
from_column: DATA
column_left_id: 821860
to_table_id: 44940
to_column: DATA_EMISSAO
column_right_id: 761306
relationshipType: ONE_TO_MANY
# A coluna do fato é DateTime e a do calendário é Date. Sem ignoreTime,
# nenhuma linha casa.
ignoreTime: true
- from_table_id: 44892
from_column: CLIENTE_ID
column_left_id: 807839
to_table_id: 44940
to_column: CLIENTE_ID
column_right_id: 761309
relationshipType: ONE_TO_MANY
ignoreTime: false
facts:
# Os ids de coluna saem de: lumo info app:13044 --full
- nome: Faturamento
primaryColumn: "761310:SUM"
dateColumnId: 761306
prompt: >-
Valor total vendido no período, somando o valor líquido dos itens.
Agrupar por Cliente ou Produto para ver a concentração da carteira.
filters: []
- nome: Clientes Ativos
primaryColumn: "761309:DCOUNT"
dateColumnId: 761306
prompt: Quantidade de clientes distintos com pedido no período.
filters: []
tags: [comercial]
```
## Especificação: header
### `id`
**Tipo:** integer (>= 1) · **Obrigatório:** sim
### `kind`
**Tipo:** string · **Obrigatório:** sim · **Valor:** `app`
### `lumo`
**Tipo:** string · **Obrigatório:** sim · **Valor:** `v2`
### `tenantId`
**Tipo:** integer (>= 1) · **Obrigatório:** sim
## Especificação: body
### `nome`
**Tipo:** string (mínimo 1 caractere) · **Obrigatório:** sim
Nome do app.
### `config`
**Tipo:** object · **Obrigatório:** sim
Configuração de exibição do app.
| Campo | Tipo | O que faz |
|---|---|---|
| `show_last_loadedat` | boolean | Exibe no app a data da última carga. |
| `show_last_loadedat_tables` | array de integer | Ids das tabelas consideradas nessa data. |
```yaml
config:
show_last_loadedat: true
show_last_loadedat_tables: [44940]
```
### `descricao`
**Tipo:** string ou null · **Obrigatório:** não
### `icon`
**Tipo:** string ou null · **Obrigatório:** não
Ícone do app, no formato `conjunto:nome`, como `mdi:chart-bar`.
### `color`
**Tipo:** string ou null · **Obrigatório:** não · **Formato:** hex `#RGB`, `#RRGGBB` ou `#RRGGBBAA`
Cor de destaque do app. Qualquer outro formato é rejeitado.
```yaml
color: "#10b981"
```
### `tables`
**Tipo:** array de objetos · **Obrigatório:** não
Tabelas do DW que o app enxerga. Cada item exige `tableId`. Veja [Especificação: table](#especificacao-table).
Uma tabela com `table_type: cloud` não pode entrar num app.
### `relationships`
**Tipo:** array de objetos · **Obrigatório:** não
Ligações entre as tabelas do app. Veja [Especificação: relationship](#especificacao-relationship).
### `facts`
**Tipo:** array de objetos · **Obrigatório:** não
Indicadores: dicionário de negócio que a IA usa e que aparecem no cockpit de Indicadores. Veja [Especificação: indicador](#especificacao-fact).
### `expressions`
**Tipo:** array · **Obrigatório:** não · **Default:** `[]`
Colunas calculadas no escopo do app.
### `filtros`
**Tipo:** array · **Obrigatório:** não · **Default:** `[]`
Filtros pré-aplicados quando o app abre.
### `dynamic_dimensions`
**Tipo:** array · **Obrigatório:** não · **Default:** `[]`
### `tags`
**Tipo:** array de string · **Obrigatório:** não · **Default:** `[]`
## Especificação: table {#especificacao-table}
Cada item de `tables`.
### `tableId`
**Tipo:** integer (>= 1) · **Obrigatório:** sim
Id da tabela do DW. Liste com `lumo list table`.
### `tipo`
**Tipo:** string ou null · **Obrigatório:** não · **Valores:** `dimension`, `n2n`, `null` · **Default:** `dimension`
Papel da tabela dentro do app. O nome é legado do motor de BI e não corresponde ao conceito de star schema: `dimension` cobre toda tabela normal, tanto o fato de vendas quanto a dimensão de clientes. Reserve `n2n` para tabelas ponte de muitos-para-muitos.
### `nome_visualizacao`
**Tipo:** string ou null · **Obrigatório:** não · **Default:** `null`
Renomeia a tabela apenas neste app. Tem efeito semântico: uma vez definido, expressões e dataSources precisam endereçar a tabela por este nome, na forma `[Vendedor]."COLUNA"`, e não mais pelo nome físico do DW.
Omitir o campo mantém o nome físico. Omitir num `push` apaga um rename anterior, porque o arquivo é a verdade.
### `columns`
**Tipo:** object · **Obrigatório:** não
Overlay por coluna, no escopo deste app. Chaveado por nome de coluna. Liste as colunas com `lumo columns table:`.
| Campo | Tipo | O que faz |
|---|---|---|
| `visible` | boolean | `false` esconde a coluna do gerador de relatórios do usuário final. A coluna continua utilizável em expressões e widgets. Serve para limpar a interface, e não para controlar acesso. |
| `label` | string ou null | Renomeia a coluna apenas neste app. |
Liste apenas as colunas que fogem do padrão. Omitir `columns` preserva o overlay atual, e `{}` apaga todos os overrides. Só funciona em app em rascunho.
### `guid`
**Tipo:** string ou null · **Obrigatório:** não
### `appTableId`
**Tipo:** integer ou null · **Obrigatório:** não
Id da associação entre app e tabela, atribuído pelo servidor.
## Especificação: relationship {#especificacao-relationship}
Cada item de `relationships`.
### `from_table_id`
**Tipo:** integer (>= 1) · **Obrigatório:** sim
Tabela de origem.
### `to_table_id`
**Tipo:** integer (>= 1) · **Obrigatório:** sim
Tabela de destino.
### `column_left_id`
**Tipo:** integer (>= 1) · **Obrigatório:** sim
Id da coluna de origem. Descubra com `lumo columns table:`.
### `column_right_id`
**Tipo:** integer (>= 1) · **Obrigatório:** sim
Id da coluna de destino.
### `relationshipType`
**Tipo:** string · **Obrigatório:** sim · **Valores:** `ONE_TO_ONE`, `ONE_TO_MANY`, `MANY_TO_ONE`, `MANY_TO_MANY`
Cardinalidade. Uma dimensão ligando ao fato é `ONE_TO_MANY`.
### `from_column`
**Tipo:** string · **Obrigatório:** não
Nome da coluna de origem.
### `to_column`
**Tipo:** string · **Obrigatório:** não
Nome da coluna de destino.
### `ignoreTime`
**Tipo:** boolean · **Obrigatório:** não · **Default:** `false`
Ignora a hora ao casar duas colunas de data. Ligue quando a coluna do fato for `DateTime` e a do calendário for `Date`. Sem isso, nenhuma linha casa.
## Especificação: indicador (`facts`) {#especificacao-fact}
A chave YAML continua `facts` por herança do nome interno, mas o produto chama esse conceito de **Indicador**: cada item ensina a IA a mapear um termo de negócio para a coluna, a agregação e os filtros corretos, e controla como o indicador aparece no cockpit de Indicadores. Para o conceito do ponto de vista de quem usa a aplicação, veja [Indicadores](/dataviz/04-features/indicadores/) e [Monitoramento: o vigia](/dataviz/04-features/indicadores/monitoramento).
Um bloco ausente preserva o que está gravado no servidor. Um bloco presente é o estado desejado: uma chave omitida dentro dele remove o valor correspondente. A regra vale para `apresentacao` e `monitoramento`; `meta` é a exceção, descrita no campo dela.
### `nome` {#fact-nome}
**Tipo:** string (mínimo 1 caractere) · **Obrigatório:** sim
O termo que o usuário fala, como `Faturamento` ou `Clientes Ativos`.
### `primaryColumn`
**Tipo:** string · **Obrigatório:** sim · **Formato:** `:`
Coluna e agregação que respondem pelo indicador. Agregações: `SUM`, `COUNT`, `AVG`, `MIN`, `MAX`, `DCOUNT`. Os ids de coluna saem de `lumo info app: --full`.
```yaml
primaryColumn: "761310:SUM"
```
### `alternativeColumns`
**Tipo:** array de string ou null · **Obrigatório:** não
Métricas relacionadas, que compartilham os mesmos filtros e a mesma data. Mesmo formato de `primaryColumn`.
### `dateColumnId`
**Tipo:** integer ou null · **Obrigatório:** não
Coluna de data que o indicador usa para filtrar período.
### `prompt`
**Tipo:** string ou null · **Obrigatório:** não
Contexto extra para a IA. Explique o que o conceito significa no negócio e como cortá-lo.
### `filters`
**Tipo:** array · **Obrigatório:** não · **Default:** `[]`
Filtros sempre aplicados ao indicador.
### `defaultDatePeriod`
**Tipo:** string ou null · **Obrigatório:** não
Um destes 14 valores: `Today`, `CurrentMonth`, `MonthToDate`, `PreviousMonth`, `Yesterday`, `CurrentYear`, `NextYear`, `DayBeforeYesterday`, `AllTime`, `PreviousYear`, `ThisWeek`, `PreviousWeek`, `NextWeek`, `Last7Days`.
### `apresentacao`
**Tipo:** object · **Obrigatório:** não
Como o indicador aparece no cockpit: ícone, cor, formato do número e direção de leitura. Nada aqui muda uma resposta da IA no chat.
| Campo | Tipo | O que faz |
|---|---|---|
| `icone` | string ou null | Ícone do indicador, no formato `conjunto:nome`, como `ph:hand-coins`. |
| `cor` | string ou null | Cor de destaque do indicador, em hex, como `#2E7D32`. |
| `formato` | string ou null | `currency`, `percent`, `integer` ou `decimal`. |
| `direcao` | string ou null | Sentido que representa melhora: `up_good`, `down_good` ou `neutral`. Custo e inadimplência usam `down_good`. |
```yaml
apresentacao:
icone: "ph:hand-coins"
cor: "#2E7D32"
formato: currency
direcao: up_good
```
### `monitoramento`
**Tipo:** object · **Obrigatório:** não
Grão da série, janela de histórico e farol de alerta do indicador. Mesma semântica de bloco de `apresentacao`.
| Campo | Tipo | O que faz |
|---|---|---|
| `grao` | string ou null | Granularidade da série: `hour`, `day`, `week` ou `month`. |
| `janelaDias` | integer ou null | Dias de histórico mantidos, de 1 a 1095. |
| `farol` | string (`auto`) ou object | `auto` aplica uma banda estatística aprendida a partir do histórico. Para faixa manual, um objeto com `verde` e `ambar` juntos, em percentual: `{ verde: 5, ambar: 10 }`. `verde` precisa ser menor que `ambar`. O `lumo lint` não verifica essa ordem: um farol invertido passa validado e só aparece errado depois, no cockpit. |
```yaml
monitoramento:
farol:
verde: 5
ambar: 10
```
### `meta`
**Tipo:** number ou null · **Obrigatório:** não · **Faixa:** maior que 0 e menor que 1e14
Valor-alvo do indicador, para o período em `defaultDatePeriod`. Uma meta por indicador; trocar o período desvincula a meta anterior.
Exige `dateColumnId` preenchido no mesmo indicador quando `meta` é um número. Com `meta: null` essa exigência não vale.
`meta` não segue a semântica de bloco acima: é um campo solto. Ausente preserva o valor gravado, e `null` é a única forma de apagá-lo. Omitir a chave não apaga nada.
Seguir, fixar, notificar e promover um indicador são ações da pessoa usuária no cockpit, não do CLI. O CLI cria e mantém o indicador curado; o resto acontece na aplicação.
## Campos gerenciados pelo servidor
| Campo | O que é |
|---|---|
| `_state` | `draft`, `published` ou `inconsistent`. |
| `deskId` | Desk em que o app foi publicado. Mude com `lumo app publish`. |
| `version` | Versão do recurso. |
| `criado_em`, `criado_por`, `publicado_em`, `publicado_por` | Auditoria. |
---
---
url: 'https://docs.horusbi.com.br/dataviz/04-features/apresentacoes.md'
---
# Apresentações
O módulo de **Apresentações** reúne tudo o que você monta para exibir dados fora do dia a dia de navegação — em TVs corporativas, reuniões de gestão ou como um relatório visual pra compartilhar. Ele tem dois tipos de item:
| Tipo | O que é | Ideal para |
|---|---|---|
| **Apresentação** (Dashboards) | Playlist rotativa dos seus Dashboards já existentes | TV de fábrica/escritório, reunião recorrente com os mesmos painéis |
| **Deck** (Slides Desenhados) | Editor estilo PowerPoint dentro da plataforma, com texto, imagens, formas e **gráficos reais** dos seus dashboards | Apresentação de resultados, relatório executivo, storytelling com dados |
> \[!IMPORTANT]
> **Slides Desenhados está em fase beta.** A funcionalidade só aparece para usuários habilitados como testers pelo administrador do tenant. Se você não vê a opção **Deck** ao criar uma Apresentação, fale com o administrador do BI.
***
## 🗂️ Onde encontrar
Na lista de **Apresentações**, uma grade unificada mostra Apresentações (playlist de dashboards) e Decks (slides desenhados) lado a lado, cada um com um ícone próprio para diferenciar o tipo.
* **Filtro por tipo** — na barra da lista, filtre para ver só Apresentações, só Decks, ou os dois juntos.
* **Botão "Criar"** — abre um menu com as duas opções:
| Opção | Resultado |
|---|---|
| **Apresentação** | Cria uma playlist rotativa de Dashboards (o modo clássico) |
| **Deck** | Abre o [Editor de Slides Desenhados](./editor.md) para montar uma apresentação do zero |
***
## 🧭 Qual tipo usar
| | Apresentação (Dashboards) | Deck (Slides Desenhados) |
|---|---|---|
| **Forma** | Playlist de dashboards já prontos, tela cheia | Slides montados livremente, como uma apresentação de PowerPoint |
| **Bom para** | Monitoramento contínuo, TV sempre ligada com os mesmos painéis | Contar uma história com os dados: contexto, texto e gráficos juntos |
| **Conteúdo** | Dashboards inteiros, como já existem na plataforma | Texto, imagens, formas, tabelas e [Gráfico BI](./grafico-bi.md) (gráficos vivos inseridos slide a slide) |
| **Montagem** | Escolher dashboards e ordem | Desenhar cada slide, ou [gerar com IA](./ia.md) |
> \[!TIP]
> Os dois tipos convivem na mesma playlist pública. Veja [Modo Kiosk](./kiosk.md) para publicar uma Apresentação que mistura Dashboards e Decks na mesma rotação de TV.
***
## 📚 Nesta seção
* **[Editor de Slides Desenhados](./editor.md)** — como montar um Deck: elementos, salvar, apresentar e exportar (PDF, imagem, PPTX).
* **[Gráfico BI](./grafico-bi.md)** — o diferencial dos Decks: inserir gráficos reais e vivos dos seus dashboards dentro dos slides.
* **[Modo Kiosk](./kiosk.md)** — publicar uma Apresentação pública para TV, misturando Dashboards e Decks na mesma playlist.
* **[Gerar Apresentação com IA](./ia.md)** — descreva o tema e a IA monta o Deck inteiro, com texto e gráficos reais escolhidos dos seus dados. Beta.
***
## 🗺️ Próximos passos
1. Se você já tem dashboards prontos e só quer uma TV rodando eles, clique em **Criar > Apresentação**.
2. Se quer montar uma apresentação com narrativa, texto e gráficos reais, clique em **Criar > Deck** e veja o **[Editor de Slides Desenhados](./editor.md)**.
3. Para acelerar a montagem do Deck, experimente **[gerar a apresentação com IA](./ia.md)** e depois ajustar.
---
---
url: 'https://docs.horusbi.com.br/lumo/apresentacoes.md'
---
# Apresentações pelo Lumo CLI
O Lumo já versionava aplicações, dataflows, tabelas, credenciais. **Apresentação, não.** Nem o kiosk que roda no telão, nem os slides desenhados — as duas coisas só existiam dentro do navegador.
Agora existem no Lumo. Isso significa que uma apresentação pode ser **versionada em git**, revisada num merge request, gerada por um agente de IA e **verificada antes de ir pro telão**.
## Os dois recursos
| Recurso | O que é |
| --- | --- |
| **`deck`** | Os **slides desenhados** — o documento da apresentação (slides, textos, formas, gráficos). |
| **`presentation`** | O **kiosk**: a playlist que roda no telão. Cada item é uma **dashboard** ou um **deck** — dá para misturar os dois. |
## Deck: slides versionados
```bash
lumo pull deck:91 # traz os slides pro workspace
# edite decks/vendas-q1--91.yaml
lumo diff # o que mudou
lumo push deck:91 # sobe
```
O deck é um YAML como qualquer outro recurso: `body.nome` e `body.slides`. Cada slide tem seus elementos — textos, formas e **gráficos de BI**.
### Verificar antes de publicar
```bash
lumo deck preview deck:91
```
Este é o comando que importa. Ele pega **cada gráfico** do deck e faz a pergunta que ninguém quer descobrir no telão: *isso ainda resolve contra dados reais?*
Resolve a consulta de cada um e **falha (código de saída ≠ 0)** se algum não resolver. Pega aplicação errada, gráfico apagado, referência obsoleta — **antes** de alguém ver a apresentação quebrada.
Por isso ele serve como portão numa esteira de CI:
```bash
lumo push deck:91 && lumo deck preview deck:91
```
### Exportar
```bash
lumo deck export deck:91 --format pdf
```
## Kiosk: a playlist do telão
```bash
lumo new presentation
```
Um kiosk **misto** — uma dashboard e um deck na mesma playlist:
```yaml
header:
kind: presentation
lumo: v2
tenantId: 550
id: 12
body:
nome: Telão da Loja
dark_mode: true
mode: automatic
base_width: 1920
items:
- type: dashboard
dashboardId: 501
order: 0
duration: 15 # segundos
- type: deck
deckId: 91
order: 1
duration: 20
```
O `lumo lint` valida antes de subir: item do tipo `deck` **precisa** de `deckId`, item `dashboard` **precisa** de `dashboardId`.
### Publicar e conferir o link
```bash
lumo presentation publish presentation:12 # devolve o link público
lumo presentation check presentation:12 # o link realmente responde?
```
O `check` é o segundo portão: confere que o link público **de fato carrega**, e **falha (≠ 0)** se não carregar.
::: tip Ele não publica nada
O `check` só **lê**. Isso é de propósito: descobrir o link republicando exigiria permissão de publicação — uma identidade de CI só-leitura nem conseguiria rodar o portão — e reescreveria o registro de *quem publicou e quando* a cada verificação.
:::
Para despublicar:
```bash
lumo presentation unpublish presentation:12
```
## Colocando um gráfico real num slide
Um gráfico dentro de um slide aponta para um gráfico de verdade de uma aplicação. Para referenciá-lo, você precisa do par **aplicação + identificador do gráfico**:
```bash
lumo app widgets app:90
```
Lista os gráficos daquela aplicação com o identificador de cada um. Copie o par e use no slide — depois rode `lumo deck preview` para confirmar que resolve.
## O fluxo completo
```bash
lumo init
lumo app widgets app:90 # descubra os gráficos disponíveis
lumo new deck # escreva os slides
lumo lint # a estrutura está válida?
lumo push deck:99
lumo deck preview deck:99 # os gráficos resolvem com dados reais?
lumo new presentation # monte o kiosk (dashboards + decks)
lumo push presentation:12
lumo presentation publish presentation:12
lumo presentation check presentation:12 # o link responde?
```
## Deixando a IA escrever
A skill **`/lumo-decks`** ensina o agente a escrever deck e kiosk — a estrutura dos slides, os tipos de elemento e como referenciar gráficos reais.
Com ela instalada, dá para pedir em português (*"monte uma apresentação de vendas do trimestre com os gráficos do app Comercial"*) e o agente escreve os YAMLs, sobe e **roda os dois portões** (`deck preview` e `presentation check`) para provar que funciona.
Veja [Integrações com IA](../integracoes/).
## 🗺️ Próximos passos
* [Referência de Comandos](../comandos/)
* [Revisando o trabalho da IA](../revisar/)
* [Apresentações no dataviz](/dataviz/04-features/apresentacoes/) — a mesma coisa, pelo navegador
---
---
url: 'https://docs.horusbi.com.br/etl/architecture.md'
---
# Arquitetura do Sistema
Esta seção explica como as peças do **HorusETL** se encaixam, como os dados fluem e como a segurança é mantida.
O sistema foi desenhado para ser **híbrido** — você desenha seus fluxos na nuvem (SaaS), mas executa o processamento pesado dentro da sua própria infraestrutura (On-Premise), garantindo que seus dados nunca precisem sair da sua rede se você não quiser.
***
## 🏛️ Os 3 Pilares
A arquitetura é dividida em três componentes principais que trabalham em conjunto:
### 1. 🎨 Frontend (O Desenho)
É a interface web onde você acessa o sistema.
* **Função** — Onde você desenha os fluxos, configura os nós e monitora as execuções
* **Conceito** — Nada é processado aqui; apenas definido e configurado
### 2. 🧠 Backend (O Controle)
É o cérebro que vive na nuvem.
* **Função** — Armazena seus desenhos (fluxos), gerencia usuários, permissões e orquestra quando cada tarefa deve rodar
* **Conceito** — Ele coordena o que fazer e quando, mas não processa os dados diretamente
### 3. ⚙️ Engine / Agente (A Execução)
É o software instalado na infraestrutura (Servidor Windows, Linux ou Docker).
* **Função** — Recebe as ordens do Backend e executa o trabalho de processamento: conecta no banco, processa os arquivos e move os dados
* **Conceito** — Ele vai até onde os dados estão e executa o processamento
***
## 📐 Diagrama Geral
```mermaid
graph TD
subgraph Cloud [Nuvem HorusBI]
User((Usuário)) -->|Acessa via HTTPS| Frontend[Frontend Web]
Frontend -->|Salva Configurações| Backend[Backend API]
end
subgraph Client [Sua Infraestrutura]
Agent[Agente HorusETL]
DB[(Bancos de Dados)]
File[Arquivos Locais]
end
Backend -.->|Comando via WebSocket| Agent
Agent -->|Lê Dados| DB
Agent -->|Lê/Escreve| File
Agent -.->|Logs e Status| Backend
```
> \[!NOTE]
> A seta de comando é pontilhada — isso indica que o Agente mantém uma conexão constante, aguardando ordens. O Backend **nunca** inicia uma conexão direta para dentro da sua rede.
***
## 🔗 Próximos Passos
Entenda os detalhes de cada parte da arquitetura:
* 🌐 [**Comunicação e Redes**](./communication.md) — Como o agente se comunica com a nuvem e quais portas são usadas
* 🔐 [**Segurança e Dados**](./security.md) — Como os dados são protegidos e isolados
* 🔄 [**Ciclo de Vida de Execução**](./execution-lifecycle.md) — O passo a passo do que acontece quando você clica em "Executar"
---
---
url: 'https://docs.horusbi.com.br/dw/architecture.md'
---
# Arquitetura Lógica do HorusDW
Esta seção explica os conceitos fundamentais de organização e funcionamento do Horus Data Warehouse. O foco é entender **como o Horus organiza seus dados** para que você tire o máximo proveito da ferramenta.
***
## 📚 Tópicos
* 🏗️ [**Hierarquia da Informação**](./hierarchy) — Entenda a diferença entre Mesas, Tabelas e Datamarts e como eles organizam seus dados
* ⚙️ [**O Motor do Horus**](./engine) — Conheça o armazenamento híbrido e como a IA trabalha para normalizar seus dados automaticamente
* 🔐 [**Segurança e Governança**](./security) — Como funciona o modelo de permissões por delegação
> \[!TIP]
> **Resumo rápido:** O Horus separa *onde guarda* (Mesas) de *onde mostra* (Datamarts), usa um motor híbrido para ser rápido e econômico ao mesmo tempo, e utiliza IA para traduzir nomes técnicos para português automaticamente.
---
---
url: 'https://docs.horusbi.com.br/hec/archive.md'
---
# Arquivo
A tela **Lixeira** (no menu lateral em **Arquivo > Lixeira**) centraliza as versões anteriores de **Aplicações**, **Fluxos** e **Tabelas** que foram substituídas ou excluídas. É a forma segura de recuperar conteúdo perdido em uma publicação destrutiva.
> \[!IMPORTANT]
> Itens arquivados são mantidos por **90 dias**. Após esse período, são removidos definitivamente em uma limpeza automática diária.
> \[!NOTE]
> **A Lixeira é escopada por mesa — não é uma lista global.** Você só vê, restaura ou baixa o backup de itens (Aplicações, Tabelas, Fluxos) de mesas às quais tem acesso:
>
> * **Item de Mesa de Aplicação** segue o **allow-list**: aparece só se você tem acesso àquela mesa (Admin do Tenant enxerga todas).
> * **Item de Mesa de Dados** segue o **bloqueio (deny-list)**: se você foi bloqueado daquela Mesa de Dados, o item **não** aparece na sua Lixeira — e esse bloqueio vale também para o Admin do Tenant.
>
> Ou seja, a Lixeira não é porta dos fundos: um usuário bloqueado de uma Mesa de Dados não recupera nem baixa o backup de uma tabela daquela mesa por aqui. Modelo completo em **[Controle de Acesso às Mesas](/dw/desks/controle-de-acesso)**.
## Como funciona
Sempre que você publica uma App, um Fluxo ou uma Tabela **substituindo** uma versão já publicada na mesma mesa, o sistema cria automaticamente um snapshot completo da versão anterior antes da substituição. Esse snapshot aparece na tela de Arquivo com o motivo "Substituído".
Itens **excluídos manualmente** pelo usuário também aparecem na tela, marcados como "Excluído".
## Restaurar um item
1. Acesse **Arquivo > Lixeira**.
2. Use os filtros (Tipo, Motivo, Período, Busca) para localizar a versão que deseja recuperar.
3. Clique em **Restaurar**.
4. Se o item tiver itens vinculados (ex: uma Tabela com Fluxo arquivado relacionado), o modal mostrará checkboxes — você decide quais quer restaurar junto.
5. Confirme.
O restore cria um **rascunho** com o conteúdo do snapshot. O snapshot original permanece intacto no Arquivo — você pode restaurá-lo quantas vezes precisar.
## Histórico de saves de fluxos
Para Fluxos arquivados, há também o botão **Histórico** na tela de Arquivo. Ele lista todas as versões salvas (não publicadas) do fluxo, permitindo restaurar qualquer ponto intermediário como rascunho.
## Excluir definitivamente
A ação **Excluir definitivamente** marca o item para remoção. Ele some da tela de Arquivo imediatamente, e em até 90 dias é apagado em definitivo por uma rotina automática do sistema. Ação irreversível pelo usuário.
## Baixar um backup local
Em vez (ou além) de confiar na retenção de 90 dias, você pode **baixar um arquivo .zip** com a versão atual de uma Aplicação, Fluxo ou Tabela e guardar localmente.
Onde aparece o botão:
* **No momento de excluir um item** (em Aplicações Pessoais, Fluxos, Tabelas): a janela de confirmação tem um checkbox **"Baixar backup antes de excluir"**. Marque antes de confirmar — o download começa primeiro, e em seguida o item é excluído.
* **Na Lixeira**: cada linha tem a ação **Baixar backup** ao lado de Restaurar e Excluir definitivamente.
O arquivo gerado contém o conteúdo do item em formato JSON (comprimido). Para Aplicações, o backup inclui também as Tabelas e Fluxos relacionados, e pode ser usado pelo time de suporte para uma restauração completa. Para Fluxos e Tabelas, o backup é uma referência do conteúdo — caso precise restaurar, abra um chamado anexando o arquivo.
> \[!TIP]
> O backup local sobrevive aos 90 dias de retenção e é útil para guardar versões importantes por mais tempo (ou compartilhar fora do ambiente).
## Restaurando Tabelas
Quando uma Tabela é restaurada, ela volta como um rascunho **sem dados**. Para popular a tabela, é preciso publicar o rascunho — recarregando via fluxo ETL ou importando um arquivo. O Arquivo guarda apenas a estrutura da tabela (colunas, tipos, configurações), não os dados que havia nela.
---
---
url: 'https://docs.horusbi.com.br/etl/processors/inputs/csv.md'
---
# Arquivo CSV
O nó **Arquivo CSV** lê dados de um arquivo `.csv`, `.txt` ou `.tsv` enviado pelo usuário diretamente pelo editor do ETL. O arquivo é armazenado no storage do Horus e lido pelo Agente a cada execução do fluxo.
***
## Parâmetros de Configuração
* **Arquivo** — Upload feito diretamente no editor. O arquivo é salvo no storage e reutilizado nas próximas execuções. Para trocar, clique em "Trocar arquivo".
* **Separador de colunas** — Caractere que delimita as colunas no arquivo.
| Opção | Separador |
|-------|-----------|
| `,` (vírgula) | Padrão CSV |
| `;` (ponto e vírgula) | Comum em exportações do Excel BR |
| `Tab` | TSV (Tab-Separated Values) |
| `\|` (pipe) | Menos comum, evita conflito com vírgulas no texto |
* **Codificação** — Encoding do arquivo.
| Opção | Uso |
|-------|-----|
| `UTF-8` | Padrão moderno — use sempre que possível |
| `Latin1 / ISO-8859-1` | Arquivos mais antigos |
| `Windows-1252` | Exportações do Excel em português (acentos corretos) |
* **Primeira linha é cabeçalho** — Se marcado (padrão), a primeira linha define os nomes das colunas. Se desmarcado, o Agente gera nomes genéricos (`Column1`, `Column2`, ...).
***
## Comportamento
* O arquivo é enviado uma vez e fica armazenado no storage. O fluxo não precisa de novo upload a cada execução.
* A leitura é feita via streaming — arquivos grandes não consomem memória excessiva.
* Se o arquivo contiver caracteres especiais com encoding errado (ex: `ç` em vez de `ç`), troque a codificação para Windows-1252.
***
## Limitações
* O nó não suporta arquivos com múltiplas seções ou cabeçalhos repetidos (formatos não-padrão).
* Para planilhas Excel, use o nó **Arquivo Excel** (ExtractStaticExcel).
---
---
url: 'https://docs.horusbi.com.br/dataviz/02-apps/archive.md'
---
# Arquivo de Aplicações
Ao publicar uma Aplicação substituindo uma versão anterior na mesma mesa, a versão antiga é **arquivada automaticamente** antes da substituição. Isso previne perda de conteúdo em casos como publicação acidental de uma versão vazia sobre uma versão produtiva.
> \[!IMPORTANT]
> **Tempo de armazenamento:** A versão substituída fica salva no Arquivo por 90 dias antes de ser removida definitivamente.
>
> **Onde encontrar:** Acesse a página de **[Arquivos da suíte HEC](/hec/resources/arquivos)** para saber como gerenciar essas versões.
> \[!NOTE]
> A Lixeira é **escopada por mesa**: você só vê/restaura/baixa itens de Mesas de Aplicação às quais tem acesso (Admin do Tenant enxerga todas). Ver **[Controle de Acesso às Mesas](/dw/desks/controle-de-acesso)**.
## Recuperar uma versão anterior
1. Acesse **Arquivo > Lixeira** no menu lateral.
2. Filtre por **Tipo: Aplicação** e localize a versão desejada.
3. Clique em **Restaurar** — um rascunho com o conteúdo dessa versão é criado em sua conta pessoal.
4. Edite o rascunho normalmente e publique quando estiver pronto.
A versão arquivada **não é consumida** ao restaurar — você pode restaurar a mesma versão quantas vezes quiser.
## O que é preservado
A versão arquivada da Aplicação inclui:
* Dashboards e seus widgets
* Tabelas e relacionamentos
* Expressões e medidas
* Colunas customizadas
* Dimensões dinâmicas
* Tópicos de chatbot
**Não** são preservados: favoritos, alertas e proprietários — eles seguem ligados à versão publicada atual e continuam funcionando normalmente após a substituição.
## Baixar backup local
Ao **excluir** uma Aplicação, a janela de confirmação mostra um checkbox **"Baixar backup antes de excluir"**. Marcando essa opção, você recebe um arquivo `.zip` com o conteúdo completo da aplicação (dashboards, tabelas referenciadas, expressões etc.) e em seguida a aplicação é excluída.
Você também encontra a ação **Baixar backup** ao lado de cada item na Lixeira.
O arquivo é útil pra:
* Guardar localmente uma versão importante por mais tempo que os 90 dias de retenção
* Compartilhar com o suporte caso precise recuperar uma versão que já foi excluída em definitivo
* Ter uma cópia de segurança fora do ambiente
Veja também: [Arquivo (visão geral)](/hec/archive).
---
---
url: 'https://docs.horusbi.com.br/etl/guides/archive.md'
---
# Arquivo de Fluxos
Ao publicar um Fluxo substituindo a versão atual na mesma mesa, a versão antiga é **arquivada automaticamente** antes da substituição. Isso protege contra perda de configuração em casos como publicação de um fluxo incompleto sobre um fluxo produtivo.
> \[!IMPORTANT]
> A versão arquivada é mantida por **90 dias**. Além disso, cada salvamento intermediário do fluxo fica disponível no histórico e pode ser restaurado individualmente — também com retenção de 90 dias.
## Recuperar a versão anterior
1. Acesse **Arquivo > Lixeira**.
2. Filtre por **Tipo: Fluxo** e localize a versão desejada.
3. Clique em **Restaurar**.
Se o Fluxo arquivado estava vinculado a uma Tabela arquivada (caso comum quando o publish trocou tabela), o modal mostrará a Tabela vinculada como opção. **Você decide se quer restaurar os dois juntos.** Restaurando juntos, o Fluxo restaurado já aponta para a Tabela restaurada.
## Histórico de salvamentos
Cada vez que você salva o Fluxo, o sistema guarda essa versão no histórico. Para acessar:
1. Em **Arquivo > Lixeira**, localize o Fluxo arquivado.
2. Clique em **Histórico** — abrirá uma janela com todas as versões salvas.
3. Clique em **Restaurar como rascunho** na versão desejada.
## O que é preservado
A versão arquivada do Fluxo inclui:
* O fluxo completo (todos os processadores, conexões e configurações)
* Variáveis de desenvolvimento
* Modo de sincronização dos dados (carga total ou incremental, e a coluna usada)
* Tags
* Vínculo com a Tabela de destino e o Agente
**Não** são preservados: proprietários e agendamentos — eles seguem ligados ao Fluxo vivo. Ao restaurar, você se torna automaticamente o proprietário do rascunho.
## Baixar backup local
Ao **excluir** um Fluxo, a janela de confirmação mostra um checkbox **"Baixar backup antes de excluir"**. Marcando essa opção, você recebe um arquivo `.zip` com o conteúdo do fluxo (incluindo a Tabela vinculada e suas colunas), e em seguida o fluxo é excluído.
Você também encontra a ação **Baixar backup** ao lado de cada item na Lixeira.
O arquivo serve como cópia de referência. Para recuperar um Fluxo a partir do backup local, abra um chamado anexando o `.zip` — o time de suporte faz a importação.
Veja também: [Arquivo (visão geral)](/hec/archive).
---
---
url: 'https://docs.horusbi.com.br/dw/tables/archive.md'
---
# Arquivo de Tabelas
Ao publicar uma Tabela substituindo a versão atual na mesma mesa, a versão antiga é **arquivada automaticamente** antes da substituição. Isso preserva toda a estrutura da Tabela (colunas, tipos, expressões e configurações) caso seja necessário restaurar.
> \[!IMPORTANT]
> A versão arquivada é mantida por **90 dias**. Após esse prazo, é removida em definitivo por uma rotina automática do sistema.
> \[!CAUTION]
> O Arquivo preserva **apenas a estrutura** da Tabela — não os dados que havia nela. Ao restaurar, a Tabela volta **vazia** e precisa ser recarregada (via fluxo ETL ou import).
> \[!NOTE]
> A Lixeira é **escopada por mesa**: você só vê/restaura/baixa itens de Mesas de Dados às quais tem acesso, e o bloqueio de uma Mesa de Dados é honrado aqui (Admin do Tenant inclusive). Ver **[Controle de Acesso às Mesas](/dw/desks/controle-de-acesso)**.
## Recuperar a versão anterior
1. Acesse **Arquivo > Lixeira**.
2. Filtre por **Tipo: Tabela** e localize a versão desejada.
3. Clique em **Restaurar**.
Se houver Fluxos arquivados que apontavam para essa Tabela, o modal mostrará esses Fluxos como opções vinculadas. Restaurando junto, o Fluxo restaurado já fica apontando para a nova Tabela restaurada.
## O que é preservado
A versão arquivada da Tabela inclui:
* Nome, ícone, descrição
* Todas as colunas (nome, tipo, máscara, rótulo, expressões, ordem de exibição)
* Colunas de identificação única e colunas de particionamento
* Modo de tratamento de duplicatas (manter todos, manter único, agregar)
* Origem da Tabela (arquivo, fonte em cloud, etc.)
* Configurações de agentes (particionamento por agente, coluna de agente)
**Não** são preservados: os dados que estavam na Tabela. Após o restore, a Tabela volta como um rascunho com a estrutura completa, porém vazia — precisa ser recarregada.
## Baixar backup local
Ao **excluir** uma Tabela, a janela de confirmação mostra um checkbox **"Baixar backup antes de excluir"**. Marcando essa opção, você recebe um arquivo `.zip` com a estrutura completa da tabela (colunas, tipos, configurações), e em seguida a tabela é excluída.
Você também encontra a ação **Baixar backup** ao lado de cada item na Lixeira.
> \[!CAUTION]
> Assim como no Arquivo, o backup local **preserva apenas a estrutura** da Tabela — não os dados nela. Para recuperar uma Tabela a partir do backup local, abra um chamado anexando o `.zip` — o time de suporte faz a importação. Os dados precisam ser recarregados via fluxo ETL ou import.
Veja também: [Arquivo (visão geral)](/hec/archive).
---
---
url: 'https://docs.horusbi.com.br/etl/processors/inputs/excel.md'
---
# Arquivo Excel
O nó **Arquivo Excel** permite ler dados de planilhas `.xlsx` ou `.xls` armazenadas localmente na máquina onde o Agente está rodando.
***
## ⚙️ Parâmetros de Configuração
### Caminho do Arquivo
* **Descrição** — O caminho absoluto para o arquivo Excel no sistema de arquivos do Agente
* **Tipo** — Texto (String)
* **Exemplo** — `C:\Dados\vendas_2024.xlsx` (Windows) ou `/mnt/dados/vendas.xlsx` (Linux)
* **Variáveis** — Suporta substituição de variáveis, ex: `{DIR_ENTRADA}/arquivo.xlsx`
***
## 📋 Comportamento
* **Abas** — Por padrão, o processador lê a **primeira aba** (Worksheet) do arquivo
* **Cabeçalhos** — Assume que a primeira linha contém os nomes das colunas
* **Tipagem** — Tenta inferir os tipos de dados (Data, Número, Texto) automaticamente
***
## ⚠️ Limitações
* Requer que o arquivo esteja acessível localmente pelo serviço do Agente
* Para arquivos muito grandes (gigabytes), prefira converter para CSV ou Parquet antes de processar, pois o Excel é carregado em memória
---
---
url: 'https://docs.horusbi.com.br/etl/processors/inputs/parquet.md'
---
# Arquivo Parquet
O nó **Arquivo Parquet** lê arquivos de dados armazenados localmente no formato Apache Parquet.
***
## 📖 O que é Parquet?
Parquet é um formato de armazenamento colunar otimizado para analytics. É extremamente rápido para leitura e escrita, e mantém os tipos de dados (Inteiro, String, Data) preservados. O HorusETL utiliza Parquet como formato padrão para troca de dados entre nós em modo desenvolvimento.
***
## ⚙️ Parâmetros de Configuração
### Caminho do Arquivo
* **Descrição** — Caminho completo para o arquivo `.parquet`
* **Exemplo** — `/data/input/clientes.parquet`
* **Suporte a HTTP** — Se o caminho começar com `http` ou `https`, o Horus fará o download do arquivo para uma pasta temporária antes de processá-lo
***
## ⚡ Performance
Este é o processador de input mais rápido disponível. Use-o sempre que possível para processar grandes volumes de dados que já foram extraídos anteriormente.
---
---
url: 'https://docs.horusbi.com.br/hec/resources/arquivos.md'
---
# Arquivos — Repositório de Arquivos do Tenant
**Arquivos** é o repositório centralizado de todos os arquivos do tenant: imagens de Cadastros (writeback), uploads manuais, arquivos gerados pelo sistema e por pipelines ETL. Aqui você vê tudo que está armazenado, quem enviou, quanto ocupa e pode gerenciar visibilidade, links e exclusão.
Arquivos é acessado pelo menu lateral, na seção **Armazenamento** (ao lado de **Lixeira**). Clicar em Arquivos abre a página dedicada `/files`, sempre operando no tenant da sessão corrente — exatamente como a página Lixeira funciona.
> **Armazenamento = Arquivos + Lixeira:** a seção "Armazenamento" no menu agrupa duas funcionalidades distintas: **Arquivos** (repositório de arquivos do tenant, esta página) e **Lixeira** (apps e tabelas desativadas). São funcionalidades independentes, apenas co-localizadas na mesma seção do menu.
***
## O que aparece na página
A tabela lista todos os arquivos ativos do tenant da sessão (excluídos ficam ocultos), ordenados do mais recente para o mais antigo.
| Coluna | Descrição |
|--------|-----------|
| **Nome** | Nome do arquivo |
| **Tamanho** | Tamanho em bytes/KB/MB/GB |
| **Tipo** | MIME type (ex.: `image/png`, `application/octet-stream`) |
| **Origem** | Como o arquivo foi criado (veja abaixo) |
| **Enviado por** | Nome do usuário, ou `"API"` / `"ETL"` / `"Sistema"` quando não há usuário |
| **Data** | Data e hora de criação |
| **Visibilidade** | Público ou Privado |
### Origens possíveis
| Origem | Quando aparece |
|--------|----------------|
| `HEC` | Upload manual feito nesta página |
| `API` | Upload via integração REST headless |
| `Sistema` | Criado pelo próprio sistema: imagens de Cadastro (writeback) ou exportações |
| `ETL` | Gerado por um pipeline ETL |
***
## Controle de acesso
A tela **Arquivos** (gestão global) é governada pela função de sistema **Armazenamento** (`files`):
* **Visualizar** a tela e listar todos os arquivos → `files.read`. Sem ela, o item de menu não aparece e a página não abre.
* **Tornar público/privado** → `files.update`.
* **Excluir** → `files.delete`.
* **Fazer upload** por esta tela → `files.create`.
Por padrão, todos que já tinham gestão de arquivos (função **Tabelas**) receberam **Armazenamento** equivalente — sem mudança de comportamento.
> **Arquivos de ETL** (CSV/Excel de Dataflows) são recurso do workspace de ETL: quem tem **Dataflows (ETL)** (`flow`) vê/usa/sobe esses arquivos **no editor de ETL**, independentemente de **Armazenamento**. Isso é intencional — flows são clonáveis/compartilháveis, então o arquivo referenciado precisa ser acessível a quem edita o flow. A tela Arquivos do HEC mostra todos os tipos (inclusive ETL) e é a superfície de gestão global.
### Notas de implementação (quirks)
* O **proxy** de download é por **token assinado** (sem identidade de usuário); o controle por usuário acontece no momento de **emitir o link** (backend). Um link assinado é um portador — vale até expirar.
* `público` (acessível na internet sem token) **≠** controle de acesso por usuário (esse é por função). Imagens de Cadastro são públicas por padrão.
* A **REST API** de arquivos é por tenant/bearer, **não** filtra por usuário.
***
## Filtrar por origem
Use o filtro de origem no topo da tabela para ver apenas arquivos de um tipo específico. Útil para localizar imagens de Cadastro (`Sistema`), uploads de integrações (`API`) ou carregar apenas os arquivos subidos manualmente (`HEC`).
***
## Metadados de um arquivo
Clique em qualquer arquivo para abrir o painel de detalhes. Lá você vê:
* URL canônica
* Tamanho exato
* MIME type
* Origem
* Quem enviou e quando
* Se está público ou privado
***
## Link copiável
Cada arquivo tem uma **URL canônica** no formato:
```
https://storage.horusbi.com.br/f/{id}/{nome-do-arquivo}
```
O comportamento ao acessar essa URL depende da **visibilidade** do arquivo:
* **Público:** a URL funciona diretamente no navegador ou em qualquer integração, sem autenticação.
* **Privado:** a URL sem token retorna erro. Use "Gerar link temporário" para compartilhar.
***
## Gerar link temporário (privado)
Para arquivos privados, você pode gerar um **link assinado temporário** clicando em **"Gerar link"**. O link gerado inclui um token de acesso e expira após o tempo configurado (padrão: 1 hora).
```
https://storage.horusbi.com.br/f/{id}/{nome}?token=
```
O token pode ser reutilizado por qualquer pessoa que o receber, até expirar. Não é de uso único.
> **Permissão necessária:** gerar um link temporário requer **`files.read`** (a tela Arquivos é governada pela função **Armazenamento**). Isso é intencional — um link assinado cria acesso não autenticado a um arquivo privado, o que é um privilégio maior que simples leitura.
***
## Visibilidade: público vs. privado
Cada arquivo pode ser alternado entre público e privado a qualquer momento pelo **ícone de cadeado** na tabela ou no painel de detalhes.
| Visibilidade | Comportamento |
|--------------|---------------|
| **Público** | URL canônica acessível sem autenticação. Padrão para imagens de Cadastro (`Sistema`) — mudar para privado pode quebrar Cadastros que dependam da URL. |
| **Privado** | URL canônica bloqueada; acesso somente com token assinado temporário. Padrão para arquivos `HEC`, `API` e `ETL`. |
> \[!WARNING]
> Imagens usadas em Cadastros (writeback) são criadas como **públicas** para que os dados do BI exibam a imagem corretamente. Alterar a visibilidade dessas imagens para privado pode fazer com que deixem de aparecer nos dashboards e nas exportações.
***
## Upload de arquivos
Para fazer upload de um novo arquivo, clique em **"Fazer upload"**. A interface usa upload presigned (o arquivo vai direto do seu navegador para o armazenamento, sem passar pelo servidor), o que permite arquivos grandes com barra de progresso em tempo real.
**Comportamento por nome:** se já existe um arquivo ativo com o mesmo nome no tenant, o conteúdo é substituído e os metadados atualizados — o `id` permanece o mesmo. Isso é intencional para automações que fazem upload recorrente do mesmo arquivo.
***
## Excluir arquivo
Clique no ícone de lixeira para excluir um arquivo. A exclusão é **soft-delete** — o arquivo é marcado como excluído e some da listagem, mas o conteúdo só é removido do armazenamento pelo processo de limpeza automática.
### Guard de writeback
Imagens de Cadastro (writeback, `origin: Sistema`) que ainda estejam **referenciadas em registros ativos ou no histórico** de um Cadastro **não podem ser excluídas**. O sistema bloqueia a operação e exibe:
> "Arquivo ainda referenciado por um Cadastro (writeback)"
Para excluir, primeiro remova ou substitua a imagem no Cadastro correspondente.
***
## Como o storage de Arquivos entra no custo
### O que é contado
O storage de arquivos é cobrado em **GB**, com base nos arquivos ativos (`excluido=false`) do tenant em cada dia. Não há cobrança por processamento nem por tráfego de download — apenas pelo espaço ocupado.
### Snapshot diário
Todos os dias, o sistema registra o total de bytes ocupado pelos arquivos vivos do tenant na tabela `sys_tenant_files_storage_usage`. Isso gera um histórico diário que é usado para calcular a **média de GB** do mês — o mesmo modelo usado para o storage de tabelas do DW.
```
storage_de_arquivos_GB = média( snapshot_diário_bytes / 1.073.741.824 ) no mês
```
### Como entra na fatura
O GB de arquivos é **somado ao GB de tabelas** do DW para compor o total de storage cobrado no mês:
```
storage_total_GB = média_GB_tabelas_DW + média_GB_arquivos
```
O armazenamento de arquivos aparece como **"Arquivos"** na Home (no total de storage) e na página Arquivos, sumado à cobrança do mês.
### Quando o arquivo excluído para de contar
A partir do momento em que um arquivo é excluído (soft-delete), ele **não entra mais nos snapshots diários** — ou seja, o storage cobrado cai imediatamente no dia seguinte ao da exclusão. Além disso, um **reaper diário** purga os arquivos soft-deletados do storage físico (S3), encerrando qualquer cobrança residual e reclamando os bytes do bucket.
> \[!TIP]
> Para reduzir o custo de storage: exclua arquivos de API ou ETL que não são mais necessários. Imagens de Cadastro em uso precisam permanecer para não quebrar dashboards — mas imagens de registros deletados podem ser removidas.
***
## Veja também
* **[Cobrança e limites de Cadastros](/hec/resources/cadastros-cobranca)** — linha de Cadastros na fatura e como o limite funciona.
* **[REST API — Repositório de Arquivos](/api/files)** — automação headless: upload, listagem, links assinados e exclusão via API.
* **[Cadastros — Inserir e editar dados](/dw/tables/cadastros/dados)** — como as imagens de Cadastro são criadas e vinculadas aos registros.
---
---
url: 'https://docs.horusbi.com.br/dataviz/04-features/two-factor-auth.md'
---
# Autenticação de Dois Fatores (2FA)
A **Autenticação de Dois Fatores (2FA)** adiciona uma camada extra de segurança ao acesso da plataforma Horus. Com o 2FA ativado, além da senha tradicional, é necessário fornecer um segundo fator de verificação — um código temporário gerado por um aplicativo autenticador ou enviado por e-mail — para concluir o login.
> \[!IMPORTANT]
> Mesmo que alguém descubra a senha, não conseguirá acessar a conta sem o código de verificação.
***
## 🔐 Por que Usar?
A senha sozinha pode ser comprometida de diversas formas:
* **Phishing** — E-mails falsos que capturam credenciais
* **Senha fraca** — Combinações fáceis de adivinhar
* **Senha reutilizada** — A mesma senha usada em outros serviços que sofreram vazamento
O 2FA mitiga esses riscos porque, mesmo com a senha em mãos, um invasor precisaria do segundo fator (celular ou acesso ao e-mail) para concluir a autenticação.
***
## ⚙️ Configuração Inicial
### Pré-requisito (para o método por Aplicativo)
Se optar pelo método de autenticação via aplicativo, instale um dos autenticadores abaixo no celular:
* **Google Authenticator** ([Android](https://play.google.com/) | [iOS](https://apps.apple.com/))
* **Microsoft Authenticator** ([Android](https://play.google.com/) | [iOS](https://apps.apple.com/))
* **Authy** ([Android](https://play.google.com/) | [iOS](https://apps.apple.com/))
> \[!TIP]
> Alguns aplicativos (como o Authy) permitem backup na nuvem dos tokens. Isso é útil caso haja troca de celular, evitando a perda dos códigos configurados.
### Ativando o 2FA
1. Faça login no Horus normalmente
2. Clique no **ícone do usuário** (canto superior direito)
3. Selecione **Minha Conta**
4. Na aba **Geral**, localize o tópico **Configurações de Segurança**
5. Clique em **Habilitar 2FA**
6. Escolha o método de verificação:
| Método | Como Funciona |
|--------|---------------|
| **Aplicativo Autenticador** | Um QR Code será exibido na tela. Escaneie com o aplicativo autenticador — ele passará a gerar códigos temporários de 6 dígitos (que mudam a cada 30 segundos). Digite o código exibido no campo de validação para confirmar a ativação. |
| **Código por E-mail** | A cada login, o sistema enviará um código de verificação para o e-mail cadastrado na conta. Basta inserir o código recebido no campo solicitado. |
7. Confirme a ativação seguindo as instruções do método escolhido
***
## 🔑 Login com 2FA
Após a ativação, o fluxo de login incluirá uma etapa adicional de verificação:
1. Acesse a tela de login do Horus
2. Insira seu **e-mail** e **senha**
3. Uma segunda tela solicitará o **código de verificação**:
* **Aplicativo Autenticador** — Abra o aplicativo e digite o código de 6 dígitos exibido
* **E-mail** — Verifique sua caixa de entrada e digite o código recebido
4. Clique em **Verificar**
5. Acesso concedido!
> \[!NOTE]
> Se estiver usando o método por aplicativo, o código muda a cada 30 segundos. Caso o código expire, aguarde o próximo e tente novamente.
***
## ⚠️ Desativando o 2FA
Se necessário, é possível desativar o 2FA:
1. Clique no **ícone do usuário** (canto superior direito)
2. Selecione **Minha Conta**
3. Na aba **Geral**, localize o tópico **Configurações de Segurança**
4. Clique em **Desabilitar 2FA**
5. Confirme com seu código atual de verificação
> \[!WARNING]
> Desativar o 2FA reduz a segurança da conta. Recomenda-se mantê-lo ativo sempre que possível.
***
## 🆘 Perdi o Acesso ao Segundo Fator
Se o usuário perdeu o celular, desinstalou o aplicativo autenticador ou perdeu o acesso ao e-mail cadastrado:
1. Na tela de login, após inserir e-mail e senha, clique em **"Problemas com 2FA?"** ou **"Não consigo acessar o código"**
2. Entre em contato com o **administrador do sistema** para solicitar o reset do 2FA na conta
3. Após o reset, será possível fazer login normalmente e configurar um novo 2FA
***
## 💡 Boas Práticas
1. **Mantenha o 2FA sempre ativo** — É a melhor proteção contra acessos não autorizados
2. **Use um aplicativo com backup** — Authy oferece backup em nuvem, facilitando a recuperação caso troque de celular
3. **Não compartilhe códigos** — Os códigos são pessoais e intransferíveis
4. **Atualize seu celular** — Mantenha o sistema operacional e o aplicativo autenticador atualizados
---
---
url: 'https://docs.horusbi.com.br/hec/users-groups/security-2fa.md'
---
# Autenticação de Dois Fatores (2FA)
A autenticação de dois fatores (2FA) adiciona uma **camada extra de segurança** ao login. Além da senha tradicional, o sistema exige um código temporário gerado por um aplicativo ou enviado por e-mail — garantindo que apenas o verdadeiro dono da conta consiga acessar a plataforma.
## 🔑 Métodos Disponíveis
O Horus suporta dois métodos de 2FA:
### 📱 Aplicativo Autenticador (TOTP)
Use aplicativos como **Google Authenticator**, **Authy** ou **Microsoft Authenticator** para gerar códigos de 6 dígitos que mudam a cada 30 segundos.
**Vantagens:**
* Funciona offline
* Mais seguro que email
* Códigos gerados localmente
### 📧 Email (OTP)
Receba um código de 6 dígitos no seu email cadastrado a cada login.
**Vantagens:**
* Não requer instalação de app
* Simples de usar
## ⚙️ Configuração pelo Usuário
Os usuários podem configurar 2FA na seção **Minha Conta** > **Segurança**:
1. Clique em **Habilitar 2FA**
2. Escolha o método desejado:
* **Aplicativo**: Escaneie o QR Code e digite o código gerado
* **Email**: Confirme recebendo um código de verificação
3. Guarde seus códigos de recuperação em local seguro
> \[!WARNING]
> Se você perder acesso ao seu método de 2FA, precisará solicitar ao administrador para resetar sua configuração.
## 🏠 Configurações do Tenant (Admin)
Administradores podem configurar políticas de 2FA para todo o tenant em **Administração > Gestão de Tenants > Configurações**:
### Exigir 2FA
Quando habilitado, todos os usuários serão obrigados a configurar 2FA no próximo login. Usuários que ainda não configuraram verão uma tela de setup obrigatório após inserir suas credenciais.
### Métodos Permitidos
Configure quais métodos de 2FA os usuários podem escolher:
* **Apenas TOTP**: Maior segurança, requer app autenticador
* **Apenas Email**: Mais conveniente, menor segurança
* **Ambos**: Usuário escolhe sua preferência
> \[!TIP]
> Para máxima segurança, recomendamos exigir 2FA e permitir apenas o método TOTP (aplicativo).
## 🔄 Reset de 2FA (Admin)
Se um usuário perder acesso ao seu método de 2FA:
1. Acesse **Usuários** e localize o usuário
2. Clique no ícone de edição
3. Na aba **Geral**, clique em **Resetar 2FA**
4. O usuário poderá reconfigurar o 2FA no próximo login
> \[!CAUTION]
> O reset de 2FA remove a proteção adicional da conta. Certifique-se de validar a identidade do usuário antes de executar esta ação.
## 📋 Fluxo de Login com 2FA
```mermaid
sequenceDiagram
participant U as Usuário
participant H as Horus
participant A as App/Email
U->>H: Email + Senha
H->>H: Valida credenciais
alt 2FA por TOTP
H-->>U: Solicita código TOTP
U->>A: Abre app autenticador
A-->>U: Exibe código
U->>H: Envia código
else 2FA por Email
H->>A: Envia código por email
A-->>U: Email com código
U->>H: Envia código
end
H->>H: Valida código
H-->>U: Acesso liberado
```
## ✅ Boas Práticas
1. **Backup dos códigos de recuperação**: Ao configurar TOTP, guarde os códigos de backup
2. **Não compartilhe códigos**: Códigos 2FA são pessoais e intransferíveis
3. **Use TOTP quando possível**: Mais seguro que email
4. **Treine sua equipe**: Explique a importância do 2FA para segurança
## ❓ Perguntas Frequentes
### O que acontece se eu desabilitar 2FA nas configurações do tenant?
Usuários que já configuraram 2FA continuarão usando. A configuração do tenant apenas define se novos logins exigirão 2FA de quem ainda não configurou.
### Posso trocar de método de 2FA?
Sim. Desabilite o método atual em **Minha Conta > Segurança** e configure o novo método. Se o tenant exige 2FA, você será solicitado a configurar novamente.
### O código está sempre inválido, o que fazer?
Para TOTP, verifique se o horário do seu celular está correto. Códigos TOTP são baseados em tempo e diferenças de alguns segundos podem invalidá-los.
---
---
url: 'https://docs.horusbi.com.br/dataviz/04-features/alerts/webhook/advanced.md'
---
# Avançado
Tópicos para quem está colocando o webhook em produção: idempotência, validação de origem, integrações de referência (WhatsApp via Meta, Slack), como lidar com URLs de arquivos que somem, troubleshooting e limitações atuais.
***
## Idempotência
O Lumo entrega **at-least-once** (pelo menos uma vez). O mesmo payload pode chegar **mais de uma vez** em casos como:
* Sua resposta 2xx chegou tarde (perto do timeout de 10s) e o Lumo já havia decidido tentar de novo.
* Sua infraestrutura ficou indisponível por alguns minutos no meio de uma sequência de retries.
* O processo de envio do Lumo foi reiniciado entre a entrega e o ack.
Para evitar processar o mesmo alerta duas vezes, **identifique cada entrega** por uma chave estável e ignore repetições.
### Chave de idempotência recomendada
Combine `alert.id` + `user.id` + `timestamp`:
```typescript
function chaveIdempotencia(payload: AlertWebhookPayload): string {
return `${payload.alert.id}:${payload.user.id}:${payload.timestamp}`;
}
```
### Padrão de implementação
```typescript
// Tabela: processed_alerts (chave PK)
// key TEXT PRIMARY KEY
// processed_at TIMESTAMP DEFAULT NOW()
async function processarAlerta(payload: AlertWebhookPayload) {
const key = chaveIdempotencia(payload);
const inserido = await db.query(
`INSERT INTO processed_alerts (key) VALUES ($1)
ON CONFLICT (key) DO NOTHING
RETURNING key`,
[key],
);
if (inserido.rowCount === 0) {
// Já processado antes — responda 200 mas pule a lógica.
return;
}
await processarDeVerdade(payload);
}
```
> \[!TIP]
> Mesmo quando você detecta uma duplicata, **responda 200**. Retornar erro para um payload já processado faz o Lumo tentar de novo desnecessariamente. "Duplicata reconhecida" é sucesso do ponto de vista do envio.
### TTL da chave
Mantenha as chaves de idempotência por **pelo menos 24 horas** (cobre o pior caso de retry com janelas longas + reinícios). Em sistemas de alto volume, considere TTL de 7 dias e armazenar em Redis com `EXPIRE`.
***
## Validação de Origem
> \[!WARNING]
> O Lumo não envia assinatura criptográfica do payload. As duas formas suportadas de autenticar o request no seu endpoint são: **caminho secreto na URL** e **headers HTTP personalizados** (por exemplo `Authorization: Bearer `). Os dois podem (e devem) ser combinados.
### Caminho secreto na URL
Cadastre a URL do webhook do tenant com um **token aleatório no path**:
```
https://api.exemplo.com/webhook/lumo/7f3a9b2e8c4d6f5a1b9e0c8d7f6a5b4c
```
O token deve ter alta entropia (32+ caracteres aleatórios). Gere assim:
```bash
# UUID v4
uuidgen | tr -d '-' | tr 'A-Z' 'a-z'
# ou bytes aleatórios em hex
openssl rand -hex 16
```
No seu receptor:
```typescript
app.post("/webhook/lumo/:secret", (req, res) => {
if (req.params.secret !== process.env.LUMO_WEBHOOK_SECRET) {
return res.status(404).end(); // 404 (não 401) — não revela que o caminho existe
}
// ... processar
});
```
> \[!TIP]
> **Responda 404 (não 401)** para tokens inválidos. 401 confirma que existe um endpoint protegido naquela rota; 404 dá menos informação para quem está sondando.
### Headers HTTP personalizados
Em paralelo (ou em vez) do caminho secreto, o Lumo permite enviar **headers HTTP customizados** em cada POST. É a forma mais comum de autenticar um webhook contra um endpoint genérico (ex.: middleware n8n, gateway próprio, Meta Cloud API).
Configure em **HEC → Tenants → Editar Tenant → Canais de Alerta Permitidos → Webhook → Headers HTTP Customizados**. Exemplo:
```http
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
X-Tenant-Identifier: cliente-acme
```
No seu receptor:
```typescript
app.post("/webhook/lumo", (req, res) => {
const auth = req.header("authorization");
if (auth !== `Bearer ${process.env.LUMO_WEBHOOK_TOKEN}`) {
return res.status(404).end(); // 404, não 401 — não confirma rota protegida
}
// ... processar
});
```
Limites e regras detalhadas (limites de quantidade/tamanho, headers reservados) estão na [referência do payload](./payload.md#headers-http-que-sua-url-recebe).
> \[!TIP]
> Para defesa em profundidade, combine **caminho secreto + header de autenticação**. Mesmo se o token de um for vazado (log indevido, captura de tela), o outro ainda protege o endpoint.
### Boas práticas adicionais
* **Rotacione o token** periodicamente. Mude no Lumo e no seu receptor ao mesmo tempo.
* **Não logue o token** em arquivos de log nem em mensagens de erro.
* Cadastre o token via **variável de ambiente** no seu serviço (`LUMO_WEBHOOK_SECRET`), nunca hardcoded.
* Considere **restringir** o endpoint a um caminho `/webhook/lumo/...` específico, não a um `/webhook/...` genérico.
### Por que não recomendamos hoje:
* **Filtrar por IP de origem**: o Lumo roda em infraestrutura Kubernetes com IPs dinâmicos. Não publicamos lista de IPs fixos.
* **Validar `User-Agent`**: o header `Horus-Alert-Webhook/1.0` é trivial de falsificar — trate apenas como sinal informativo.
***
## Integração WhatsApp via Meta Cloud API
> \[!INFO]
> Este é o caso de integração customizada mais frequente. O cliente quer mandar notificações via uma **instância própria de WhatsApp Business** (na conta dele com a Meta), em vez de usar o gateway nativo do Lumo. O caminho é: webhook do Lumo → seu middleware → Meta Cloud API.
### Pré-requisitos
1. Conta no [Meta for Developers](https://developers.facebook.com/).
2. Aplicação **WhatsApp Business** criada e um número de telefone aprovado pela Meta.
3. Token de acesso permanente (System User Access Token recomendado para produção).
4. `phone_number_id` do número aprovado.
5. **Modelos de mensagem** (`message templates`) aprovados pela Meta — fora da janela de 24h de conversa, só dá pra mandar mensagem usando template.
### Arquitetura
```mermaid
flowchart LR
A[Lumo] -->|POST JSON| B[Seu middleware]
B -->|Mapeia destinatário| C{Tem template aprovado?}
C -->|sim| D[Meta Cloud API]
C -->|não| E[Fallback: email]
D -->|envia| F[WhatsApp do destinatário]
```
### Implementação de referência (Node.js)
```typescript
import express from "express";
const app = express();
app.use(express.json({ limit: "4mb" }));
const META_TOKEN = process.env.META_WHATSAPP_TOKEN!;
const META_PHONE_NUMBER_ID = process.env.META_PHONE_NUMBER_ID!;
const LUMO_SECRET = process.env.LUMO_WEBHOOK_SECRET!;
// Mapeamento de email/user.id no Lumo para telefone no WhatsApp.
// Em produção, isto vem do seu banco.
const TELEFONES: Record = {
"maria@empresa.com": "+5511999990000",
"joao@empresa.com": "+5511988880000",
};
app.post("/webhook/lumo/:secret", async (req, res) => {
if (req.params.secret !== LUMO_SECRET) return res.status(404).end();
// 1. Responde imediato — não bloqueie o Lumo.
res.status(200).json({ received: true });
// 2. Processa em background.
try {
await encaminharParaWhatsApp(req.body);
} catch (err) {
console.error("[whatsapp] falha", err);
}
});
async function encaminharParaWhatsApp(payload: any) {
const { alert, user, content } = payload;
const telefone = TELEFONES[user.email];
if (!telefone) {
console.warn(`[whatsapp] sem telefone mapeado para ${user.email}`);
return;
}
// Monta o texto consolidado a partir dos blocos.
const linhas: string[] = [`*${alert.nome}*`, ""];
const anexos: { url: string; tipo: "pdf" | "xlsx" | "png" }[] = [];
for (const bloco of content) {
if (bloco.type === "text" || bloco.type === "ai") {
if (bloco.generated.text) linhas.push(bloco.generated.text, "");
} else if (bloco.type === "report") {
if (bloco.generated.pdf) anexos.push({ url: bloco.generated.pdf, tipo: "pdf" });
if (bloco.generated.xlsx) anexos.push({ url: bloco.generated.xlsx, tipo: "xlsx" });
} else if (bloco.type === "chart") {
if ("chart" in bloco.generated) {
anexos.push({ url: bloco.generated.chart, tipo: "png" });
} else if (bloco.generated.kind === "dashboard" && bloco.generated.url) {
anexos.push({ url: bloco.generated.url, tipo: "pdf" });
}
}
}
// 1. Envia mensagem de texto (via template aprovado pela Meta).
await enviarTemplateMeta(telefone, "lumo_alerta_generico", [linhas.join("\n")]);
// 2. Envia cada anexo como mídia. Para isso, primeiro baixa do Lumo
// (URLs do Lumo não são permanentes, baixe imediatamente) e
// faz upload na Meta para obter um media_id.
for (const anexo of anexos) {
const mediaId = await uploadParaMeta(anexo.url, anexo.tipo);
await enviarMidiaMeta(telefone, mediaId, anexo.tipo);
}
}
async function enviarTemplateMeta(
para: string,
templateName: string,
parametros: string[],
) {
const body = {
messaging_product: "whatsapp",
to: para.replace("+", ""),
type: "template",
template: {
name: templateName,
language: { code: "pt_BR" },
components: [
{
type: "body",
parameters: parametros.map((p) => ({ type: "text", text: p })),
},
],
},
};
const resp = await fetch(
`https://graph.facebook.com/v20.0/${META_PHONE_NUMBER_ID}/messages`,
{
method: "POST",
headers: {
"Authorization": `Bearer ${META_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify(body),
},
);
if (!resp.ok) {
throw new Error(`Meta API falhou: ${resp.status} ${await resp.text()}`);
}
}
async function uploadParaMeta(urlLumo: string, tipo: "pdf" | "xlsx" | "png"): Promise {
// 1. Baixa do Lumo (URLs não são permanentes — baixe agora).
const arquivo = await fetch(urlLumo);
if (!arquivo.ok) throw new Error(`download falhou: ${arquivo.status}`);
const blob = await arquivo.blob();
const mime = tipo === "pdf" ? "application/pdf"
: tipo === "xlsx" ? "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
: "image/png";
// 2. Faz upload pra Meta usando multipart.
const form = new FormData();
form.append("messaging_product", "whatsapp");
form.append("type", mime);
form.append("file", blob, `arquivo.${tipo}`);
const resp = await fetch(
`https://graph.facebook.com/v20.0/${META_PHONE_NUMBER_ID}/media`,
{
method: "POST",
headers: { "Authorization": `Bearer ${META_TOKEN}` },
body: form,
},
);
if (!resp.ok) throw new Error(`upload falhou: ${resp.status}`);
const { id } = await resp.json() as { id: string };
return id;
}
async function enviarMidiaMeta(para: string, mediaId: string, tipo: "pdf" | "xlsx" | "png") {
const typeMeta = tipo === "png" ? "image" : "document";
const body = {
messaging_product: "whatsapp",
to: para.replace("+", ""),
type: typeMeta,
[typeMeta]: { id: mediaId },
};
await fetch(
`https://graph.facebook.com/v20.0/${META_PHONE_NUMBER_ID}/messages`,
{
method: "POST",
headers: {
"Authorization": `Bearer ${META_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify(body),
},
);
}
app.listen(3000);
```
### Pontos de atenção
* **Templates da Meta** precisam ser aprovados antes do uso fora da janela de 24h de conversa. Crie um template `lumo_alerta_generico` com um parâmetro de texto e aguarde a aprovação.
* **Janela de 24h**: se o destinatário enviou alguma mensagem ao seu número nas últimas 24h, você pode mandar mensagem livre (sem template). Senão, **só template** aprovado.
* **Limite de envio da Meta**: depende da qualidade do número e da escala (Tiers 250 → 1k → 10k → 100k → ilimitado/dia). Comece pequeno.
* **Custo por mensagem**: a Meta cobra por categoria (utility, marketing, service) e por país. Confira o pricing atualizado.
> \[!TIP]
> Para casos mais simples, considere usar o canal **WhatsApp nativo do Lumo** em vez de integrar via Meta. O canal nativo já lida com instâncias, templates e fallbacks. A integração via webhook + Meta API é indicada apenas quando o cliente tem requisitos específicos da própria conta Meta.
***
## Slack via Incoming Webhook
Bem mais simples que WhatsApp. O Slack expõe um **Incoming Webhook** por canal:
1. No Slack: **Apps → Incoming Webhooks → Add to Slack → escolha o canal**. Copie a URL gerada (`https://hooks.slack.com/services/...`).
2. No seu middleware, faça um `POST` com o payload no formato do Slack:
```typescript
async function enviarSlack(webhookUrl: string, payload: any) {
const { alert, user, content } = payload;
const blocks: any[] = [
{
type: "header",
text: { type: "plain_text", text: alert.nome },
},
{
type: "context",
elements: [
{ type: "mrkdwn", text: `Destinatário: *${user.email ?? user.id}*` },
],
},
];
for (const bloco of content) {
if (bloco.type === "text" || bloco.type === "ai") {
if (bloco.generated.text) {
blocks.push({
type: "section",
text: { type: "mrkdwn", text: bloco.generated.text },
});
}
} else if (bloco.type === "chart" && "chart" in bloco.generated) {
blocks.push({
type: "image",
image_url: bloco.generated.chart,
alt_text: bloco.nome,
});
} else if (bloco.type === "report") {
const links: string[] = [];
if (bloco.generated.pdf) links.push(`<${bloco.generated.pdf}|PDF>`);
if (bloco.generated.xlsx) links.push(`<${bloco.generated.xlsx}|Excel>`);
if (links.length) {
blocks.push({
type: "section",
text: { type: "mrkdwn", text: `*${bloco.nome}*: ${links.join(" • ")}` },
});
}
}
}
await fetch(webhookUrl, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ blocks }),
});
}
```
> \[!WARNING]
> Os links de PDF/XLSX/PNG enviados ao Slack apontam direto para `storage.horusbi.com.br`. Se o storage for limpo, os links no Slack vão quebrar. Para preservar, baixe e re-hospede no seu próprio bucket antes de enviar ao Slack — ou suba os arquivos para o Slack via `files.upload` (com `chat.postMessage` referenciando o `file_id`).
***
## Lidando com URLs que somem
As URLs em `https://storage.horusbi.com.br/download/...` **não são permanentes**. Há limpeza periódica do storage; arquivos podem deixar de existir após algumas semanas.
### Regra de ouro
> \[!DANGER]
> **Nunca persista uma URL do Lumo em banco de dados como se fosse permanente.** Trate como **link efêmero** — válido só durante o processamento do webhook.
### Estratégia recomendada
```typescript
// Errado: salva URL crua no banco.
await db.insert({ alert_id: alert.id, pdf_url: bloco.generated.pdf });
// Certo: baixa, sobe no seu storage, salva URL própria.
const conteudo = await fetch(bloco.generated.pdf).then((r) => r.arrayBuffer());
const minhaUrl = await meuStorage.upload(conteudo, `alerta-${alert.id}.pdf`);
await db.insert({ alert_id: alert.id, pdf_url: minhaUrl });
```
### O que fazer se receber 404 ao baixar
Pode acontecer se o webhook ficou em fila de retry por muito tempo e o storage foi limpo entre a geração e o seu processamento.
* Trate como degradação aceitável — a entrega original do Lumo ainda foi um sucesso.
* Logue a ocorrência (não como erro crítico).
* Considere reduzir o atraso de processamento do seu lado.
***
## Troubleshooting
| Sintoma | Causa provável | Ação |
|---|---|---|
| Webhook não chega ao seu endpoint | Canal "Webhook" não habilitado no alerta | Edite o alerta, marque Webhook em Destinatários e Canais |
| Webhook não chega ao seu endpoint | URL do Webhook não cadastrada no tenant | HEC → Tenants → Canais de Alerta Permitidos → URL do Webhook |
| Webhook não chega ao seu endpoint | URL com `http://` em vez de `https://` | Trocar para HTTPS; o Lumo rejeita HTTP antes de enviar |
| Webhook não chega ao seu endpoint | DNS do seu endpoint não resolve do datacenter do Lumo | Verifique se a URL é pública (sem VPN, sem IP privado) |
| Status no histórico fica "falha permanente" | Seu endpoint demora mais de 10s | Responda 200 imediato, processe em background |
| Status no histórico fica "falha permanente" | Seu endpoint retorna status fora da faixa 2xx | Confira o que o seu servidor está retornando; corrija para 2xx em caso de sucesso |
| Status no histórico fica "falha permanente" | Erro de certificado TLS | Renove o certificado; o Lumo valida a cadeia |
| Mesma entrega chega 2+ vezes | Sua resposta de sucesso chegou perto do timeout | Implemente idempotência (chave `alert.id + user.id + timestamp`) |
| Payload chega vazio (`generated: {}`) | A geração daquele bloco falhou | Verifique no Lumo se o alerta gerou conteúdo no histórico; trate `{}` como caso válido |
| Link de PDF/XLSX/PNG retorna 404 | URL foi limpa do storage | Baixe os arquivos imediatamente ao receber o webhook |
| Payload acima de 4 MB | Muitos blocos ou anexos pesados | Divida o alerta em vários menores ou hospede arquivos pesados externamente |
| Header customizado não chega ao endpoint | Nome do header tem caracteres inválidos ou foi marcado como reservado | Cheque a configuração em HEC → Tenants → Webhook → Headers HTTP Customizados. Regras em [Referência do Payload](./payload.md#headers-http-que-sua-url-recebe) |
### Onde ver o que aconteceu
* **Histórico do alerta** (Alertas → seu alerta → Histórico): mostra status de cada tentativa, status HTTP recebido, mensagem de erro e até 10 KB da resposta do seu endpoint.
* **Notificação interna**: ao esgotar as 8 tentativas, o criador do alerta e os admins do tenant recebem uma notificação dentro do Lumo apontando o problema.
***
## Limitações Conhecidas
Limitações atuais que você precisa considerar ao desenhar a integração.
| Limitação | Mitigação |
|---|---|
| **Método HTTP fixo em `POST`** (sem PUT/PATCH) | Use middleware se o destino exige outro método |
| **Sem URL por alerta** (URL única por tenant) | Use `alert.id`/`alert.nome` no roteamento do seu lado |
| **Sem configuração de retry pelo usuário** (8 tentativas e backoff são fixos) | Use um proxy/fila se precisar de política diferente |
| **URLs de arquivos não são permanentes** (limpeza periódica do storage) | Baixe ao receber, hospede no seu próprio storage |
**Não está no escopo:**
* **Lista de IPs fixos de origem** do Lumo. O serviço roda em Kubernetes com IPs dinâmicos; não publicamos nem prometemos lista estável.
***
## Próximos Passos
* [Referência do Payload](./payload.md) — contrato técnico completo.
* [Como Receber e Testar](./receivers.md) — exemplos práticos por ferramenta.
* Voltar para a [Visão Geral do Webhook](./index.md).
---
---
url: 'https://docs.horusbi.com.br/dw/tables/cadastros.md'
---
# Cadastros — Visão Geral
Um **Cadastro** é uma **tabela editável à mão** — você preenche os dados por **formulário** e por uma **grade estilo planilha**, dentro da própria plataforma, e ela **alimenta o BI** como qualquer outra tabela. (Internamente chamamos esse tipo de tabela de **writeback**.)
***
## 🤔 O problema que resolve
Nem todo dado vem de um ETL ou de um upload de Excel. Tem informação que **muda à mão**, na cabeça das pessoas ou numa planilha solta:
* **Metas mensais** por filial, vendedor ou produto
* **De-para de categorias** (mapear códigos crus em nomes de negócio)
* **Parâmetros** e tabelas auxiliares (faixas, pesos, status válidos)
* **Listas de referência** que alguém mantém manualmente
* **Correções pontuais** que precisam entrar no dashboard
Antes, isso virava uma planilha fora da plataforma — sem governança, sem histórico, sem ninguém sabendo qual era a versão certa. O Cadastro traz esse dado **pra dentro**: governado, versionado, com permissões e já pronto pro BI.
::: tip Em uma frase
Se o dado é **digitado por uma pessoa** (e não importado de um sistema), ele provavelmente deveria viver num Cadastro.
:::
***
## 🧩 O que é um Cadastro
Um Cadastro é uma tabela cujo conteúdo você **digita e edita na plataforma**, e que pode ser consumida no BI exatamente como uma tabela comum. Cada **linha** é um registro.
Como ele se diferencia das outras tabelas do DW:
| | Tabela Física / Estática | Cadastro |
|---|---|---|
| **Origem dos dados** | Upload de Excel ou ETL | Digitação na plataforma |
| **Como edita** | Reprocessa / re-upa o arquivo | Formulário + grade, registro a registro |
| **Versionamento** | Pela origem | A própria estrutura é versionada |
| **Bom para** | Volumes grandes vindos de sistemas | Dados mantidos à mão |
Use Cadastro quando os dados **mudam à mão** e **não vêm de ETL ou upload**. Para dados que chegam de sistemas, continue usando as tabelas convencionais do DW.
***
## 📝 Rascunho e publicação
Todo Cadastro nasce como **rascunho na Minha Mesa** — visível só para você, onde monta a estrutura e testa à vontade. Quando estiver pronto, você o **publica numa Mesa**: a partir daí ele fica consumível no BI e compartilhável com outras pessoas conforme a permissão.
```mermaid
flowchart LR
A["Criar na Minha Mesa (rascunho)"] --> B["Estruturar campos + validações"]
B --> C["Inserir dados"]
C --> D["Publicar numa Mesa"]
D --> E["Consumir no BI + editar dados no DataViz"]
```
> \[!NOTE]
> **Minha Mesa** é a sua área de rascunho (sandbox), só sua. Uma **Mesa** publicada é compartilhada e governada — é de lá que o BI e outros usuários enxergam o Cadastro.
***
## 🗺️ Por onde seguir
* **[Criar e estruturar](./criar)** — campos, tipos, atributos e unicidade
* **[Validações e expressões](./validacoes-expressoes)** — qualidade do dado e colunas calculadas
* **[Inserir e editar dados](./dados)** — a grade, o formulário e o histórico
* **[Importar e exportar](./importar-exportar)** — XLSX, modos de importação e rollback
* **[Versões e publicação](./versoes-publicar)** — versões da estrutura e como publicar
E entre módulos:
* **[Cadastros no DataViz](/dataviz/04-features/cadastros/)** — editar dados sem acesso ao DW
* **[Permissões](/hec/resources/cadastros-permissoes)** — quem pode ler, criar, atualizar, excluir e importar
* **[Cobrança e limites](/hec/resources/cadastros-cobranca)** — linhas por tenant e franquia
---
---
url: 'https://docs.horusbi.com.br/dataviz/04-features/cadastros/no-bi.md'
---
# Cadastros no BI
Um Cadastro é uma **fonte de dados como qualquer outra** — depois de publicado, você monta **widgets e dashboards** em cima dele do mesmo jeito que faria com uma tabela vinda de ETL ou de um upload.
***
## 🏷️ Metadados do registro no BI
Além dos campos que você definiu, cada registro de um Cadastro carrega um conjunto de **metadados** — quem criou, quando, quem editou por último, em qual versão. O **dono do Cadastro** pode expor essas informações no BI ligando o toggle **"metadados no BI"** no editor de estrutura, e ainda **ocultar** colunas específicas que não interessam.
Quando ligados, esses metadados aparecem como colunas com o **rótulo prefixado por `#`** — o prefixo serve justamente para distingui-las dos seus campos reais:
| Coluna | O que mostra |
|---|---|
| **`# Criado em`** | Data e hora em que o registro foi criado |
| **`# Autor`** | Quem criou o registro |
| **`# Atualizado em`** | Data e hora da última edição do registro |
| **`# Autor da edição`** | Quem fez a última edição |
| **`# Versão`** | A versão da estrutura em que o registro está |
::: info O `#` distingue metadado de campo
Os campos que você cadastrou aparecem com o rótulo que você deu a eles. As colunas de metadado sempre vêm com `#` na frente, então é fácil saber, no BI, o que é dado do seu negócio e o que é informação de controle do registro.
:::
***
## ⏱️ Defasagem (dados recentes)
Quando você edita um registro, ele é salvo **na hora** na **Base de Cadastros** — um banco de dados dedicado, com backups, onde os seus registros ficam guardados. Em seguida, esses dados são refletidos no **Lakehouse**, a camada analítica que os dashboards e widgets consultam, em **instantes**.
Por causa desse caminho, pode haver uma **pequena defasagem** entre o momento em que você edita e o momento em que a mudança aparece no BI. É questão de segundos, não de horas — mas quando você acabou de salvar e abre um widget logo em seguida, o número pode ainda refletir o estado anterior por um breve instante.
```mermaid
flowchart LR
Editar[Editar registro] --> Base[(Base de Cadastros)] --> Lakehouse[(Lakehouse)] --> BI[Widgets / Dashboards]
```
> \[!NOTE]
> Um **aviso interno** sinaliza quando o BI ainda está atrás da última edição. Se você acabou de editar e o aviso aparece, basta aguardar alguns instantes e atualizar — o BI alcança o dado mais recente sozinho.
***
## 🗺️ Próximos passos
* **[Cadastros no DataViz](./)** — editar os dados de um Cadastro publicado sem acesso ao DW.
* **[Permissões](/hec/resources/cadastros-permissoes)** — quem pode ler e editar os dados de um Cadastro.
* **[Visão Geral (DW)](/dw/tables/cadastros/)** — como o Cadastro é criado e estruturado.
---
---
url: 'https://docs.horusbi.com.br/dataviz/04-features/cadastros.md'
---
# Cadastros no DataViz
Você pode **editar os dados** de Cadastros publicados direto no DataViz — **sem precisar de acesso ao DW**. É a mesma grade e o mesmo formulário do editor de Cadastros, só que ao alcance de quem consome o BI.
::: info O que é um Cadastro
Um **Cadastro** é uma tabela cujos dados são **digitados à mão** (formulário + grade estilo planilha) dentro da plataforma e que **alimenta o BI** como qualquer outra tabela. Quem cria e estrutura o Cadastro faz isso no DW; aqui no DataViz o foco é **editar os dados** de um Cadastro já publicado.
:::
***
## 📒 O menu "Cadastros"
O menu **"Cadastros"** aparece no DataViz quando você tem **acesso de escrita** a pelo menos um Cadastro **publicado**. Se você não tem esse acesso a nenhum Cadastro, o menu simplesmente não aparece.
Dentro dele, os Cadastros são listados **agrupados por mesa** — a mesma organização que você já conhece do DW. Assim, fica fácil encontrar o Cadastro certo dentro da mesa em que ele foi publicado.
> \[!NOTE]
> Só Cadastros **publicados** numa mesa aparecem aqui. Rascunhos (na Minha Mesa) e a edição de **estrutura** continuam no DW.
***
## ✏️ Editar dados
Ao abrir um Cadastro pelo menu, você cai na **mesma grade e no mesmo formulário** do editor de Cadastros:
* **Grade** — célula selecionável, navegação por setas, edição inline. É como editar uma planilha.
* **Formulário** — abre num painel lateral para criar ou editar um registro por vez, campo a campo.
O que você consegue fazer depende das suas **permissões** sobre aquele Cadastro. Você pode, por exemplo, ter acesso só para **ler**, ou também para **criar**, **atualizar**, **excluir** e **importar** registros.
O que **não** aparece aqui:
* **Estrutura** — criar ou alterar campos, tipos, validações e expressões é feito **só no DW**, por quem é dono do Cadastro.
::: info O que você pode fazer depende das permissões
As ações disponíveis (Ler, Criar, Atualizar, Excluir, Importar) são definidas pelas permissões que você recebeu naquele Cadastro. Botões de ações que você não tem permissão para usar não aparecem. Veja **[Permissões](/hec/resources/cadastros-permissoes)** para entender cada uma.
:::
***
## 🗺️ Próximos passos
* **[Cadastros no BI](./no-bi)** — usar um Cadastro como fonte de dados em widgets e dashboards.
* **[Permissões](/hec/resources/cadastros-permissoes)** — as 5 permissões de dado (Ler, Criar, Atualizar, Excluir, Importar) e onde elas são concedidas.
* **[Visão Geral (DW)](/dw/tables/cadastros/)** — o que é um Cadastro, como criar e estruturar.
---
---
url: 'https://docs.horusbi.com.br/dataviz/03-widgets/calendar.md'
---
# Calendário
O Widget **Calendário** é uma visualização em formato de mapa de calor (Heatmap) que projeta dados em uma grade de dias. É excelente para identificar padrões de comportamento temporal, sazonalidades diárias ou falhas em datas específicas.
## 📅 Comportamento
Cada célula do calendário representa um dia. A cor da célula varia de acordo com o valor da métrica (Gradiente), facilitando a identificação imediata de dias com alta ou baixa performance.
* **Heatmap**: Dias com maiores valores tendem à "Cor Final", enquanto dias com menores valores tendem à "Cor Inicial"
* **Interatividade**:
* **Navegação**: É possível navegar entre meses (setas esquerda/direita) ou selecionar um mês específico
* **Tooltip**: Ao passar o mouse, exibe detalhes do dia, valor e colunas extras
* **Filtro**: Clicar em um dia aplica um filtro naquela data específica (ou dia do mês) para toda a Aplicação
***
## ⚙️ Configuração
### 1. Aba Dados
* **Coluna de Data**: O campo temporal que guiará o calendário
* **Coluna de Valor**: A métrica numérica que definirá as cores e valores exibidos nas células
* **Colunas de Informação Extra**: Campos adicionais que aparecerão apenas no *Tooltip* (ao passar o mouse). Útil para mostrar detalhes sem poluir a grade visual
* **Filtros**: Contexto de dados específico para o Widget
***
### 🎨 Configuração Visual
O Calendário possui modos de exibição flexíveis para diferentes densidades de informação.
#### Tipo de Visualização
1. **Mês Final**: Exibe automaticamente o último mês que possui dados. Não exibe controles de navegação. Ideal para mostrar o "status atual".
2. **Selecionar Mês**: Exibe controles de navegação (setas e dropdown) permitindo que o usuário explore o histórico mês a mês do período filtrado no Dashboard.
3. **Acumulado**: Uma visualização poderosa que agrega **todos os meses** em uma única grade de 1 a 31.
* **Uso**: Identificar padrões recorrentes, como "O dia 5 é sempre o melhor dia de vendas?" ou "O dia 31 sempre tem baixa performance?".
* **Cálculo**: Soma dos valores de todos os "dias 1", todos os "dias 2", etc.
#### Modo de Exibição (Detalhes da Grade)
Controla o que é escrito dentro de cada célula:
* **Apenas Dia**: Mostra apenas o número do dia (1, 15, 30), para um visual mais limpo
* **Apenas Valor**: Mostra apenas o número da métrica (1500, 10%)
* **Dia e Valor**: Mostra ambos, podendo ficar poluído em telas pequenas
* **Sem Informação**: Deixa a célula vazia, confiando apenas na cor (Heatmap) para comunicar a informação
#### Aparência
* **Estilo**: `Padrão`, `Transparente` ou `Destacado`
#### 🌡️ Mapa de Calor (Heatmap)
Visualização em escala de cores para análise rápida de densidade, sazonalidade e outliers.
* **Cor Inicial (Mínimo)**: Cor para os valores mais baixos (Azul Claro)
* **Cor Final (Máximo)**: Cor para os picos (Azul Escuro)
* **Cor Ausente**: Cor para dias sem dados (Cinza Claro). Utilizada em dias que não possuem dados (feriados ou dias sem vendas)
#### Formatação Numérica
Opção para forçar o formato do número (mostrar apenas inteiros) para economizar espaço na célula.
***
### 🔍 Drill-through
* **Campos para Relatório**: Colunas que aparecerão na tabela de detalhes caso o usuário queira investigar a fundo os dados ao clicar para abrir relatório
---
---
url: 'https://docs.horusbi.com.br/dataviz/04-features/alerts/channels.md'
---
# Canais de Entrega
Um alerta pode enviar o mesmo conteúdo para múltiplos canais simultaneamente. O Lumo serializa cada bloco de conteúdo no formato apropriado a cada canal: uma mesma análise da IA vira texto formatado no email, texto plano no WhatsApp e dados estruturados no webhook.
| Canal | Texto | Imagem inline | PDF/XLSX | Observação |
|---|---|---|---|---|
| **Email** | ✅ HTML | ✅ Embutida | ✅ Anexo | Suporta múltiplos destinatários, formatação rica |
| **WhatsApp** | ✅ | ✅ Imagem separada | ✅ Documento | Requer instância de WhatsApp configurada no tenant |
| **Telegram** | ✅ | ✅ Imagem separada | ✅ Documento | Mensagens diretas (1:1) apenas |
| **Webhook** | ✅ JSON | URL no payload | URL no payload | Sua aplicação recebe e renderiza como quiser |
> \[!TIP]
> Misture canais conforme a urgência. Anomalias críticas no **WhatsApp** (push imediato), digests detalhados no **Email** (com anexos), integrações com sistemas internos via **Webhook**.
***
## Email
### Configuração
O Lumo usa o servidor SMTP configurado no tenant (ver **HEC > Tenants > Configurações de Email**). Cada tenant pode usar seu próprio SMTP (Gmail, Office365, SendGrid, servidor interno) ou o SMTP padrão do Lumo.
### Destinatários
Selecionados na aba "Destinatários" do alerta:
* **Usuários do tenant**: pickup direto da lista de usuários
* **Grupos de usuários**: todos os membros ativos
* **Emails externos**: pessoas fora do tenant. **Esta opção precisa estar habilitada nas configurações do tenant** (HEC > Tenants > Avançado). Se estiver desabilitada, o campo não aparece e só dá pra escolher usuários e grupos cadastrados.
### Formato
* **Assunto**: nome do alerta + data
* **Corpo HTML**: bloco de texto com variáveis renderizadas e imagens inline (gráficos)
* **Anexos**: arquivos de relatório (PDF, XLSX)
> \[!WARNING]
> SMTP configurado errado é a causa mais comum de "alertas não chegam". Teste com o botão **"Testar Configuração SMTP"** no painel do tenant antes de ativar alertas em produção.
***
## WhatsApp
### Pré-requisito: Instância configurada no Tenant
**WhatsApp só funciona se o administrador do tenant tiver configurado uma instância no HEC** (em **HEC > Tenants > Integrações > WhatsApp**). Sem isso, as mensagens não são enviadas, mesmo que o alerta esteja ativo e o canal marcado.
A instância é uma conexão com a API do WhatsApp Business (ou com um gateway equivalente conforme contratado). Cada tenant tem sua própria instância, dedicada.
> \[!WARNING]
> Sem instância configurada, o alerta dispara, gera os conteúdos, e a entrega para o canal WhatsApp falha silenciosamente (registrada como erro no histórico). Verifique a configuração antes de ativar alertas com WhatsApp.
### Telefone do Destinatário
O número de WhatsApp para onde a mensagem é enviada vem do **perfil de cada usuário do Lumo**, no mesmo lugar onde se ajusta nome, foto e outras informações pessoais (acessível pelo menu do usuário no canto superior direito da plataforma).
Se o usuário não tem telefone cadastrado no perfil, ele não recebe alertas via WhatsApp, mesmo que esteja na lista de destinatários.
### Comportamento
* Cada bloco de texto vira **uma mensagem**
* Gráficos viram **imagens enviadas separadamente**
* Relatórios PDF/XLSX viram **documentos anexados**
### Limites
| Limite | Valor |
|---|---|
| **Tamanho de anexo** | 100 MB (PDF/XLSX) |
| **Tamanho de imagem** | 5 MB |
***
## Telegram
Funcionalmente similar ao WhatsApp, mas via bot do Telegram.
### Setup
1. Administrador do tenant cadastra o **token do bot** em **HEC > Tenants > Integrações > Telegram**
2. Cada usuário precisa **iniciar conversa com o bot** uma vez para receber mensagens (limitação do Telegram, não do Lumo). Isso é feito clicando no link do bot e enviando `/start`
3. O Telegram ID do usuário é cadastrado no perfil dele (mesmo lugar onde edita nome, foto, telefone)
### Mensagens Diretas Apenas
O Telegram do Lumo envia **somente para conversas diretas (1:1)**. Não há suporte a grupos. Isso é uma decisão deliberada: enviar dados sensíveis para grupos com membros que não passaram pelo controle de acesso do tenant criaria risco de vazamento.
Se você precisa que múltiplas pessoas recebam o mesmo alerta, adicione cada uma como destinatário individual ou crie um grupo no Lumo (que expande pra cada membro).
### Vantagens vs WhatsApp
* ✅ Sem limite de mensagens/dia
* ✅ Sem custo de API (Telegram é gratuito)
* ❌ Adoção menor que WhatsApp em equipes corporativas no Brasil
***
## Webhook
Canal de integração: entrega o conteúdo do alerta como **payload JSON** numa URL HTTPS que você controla. Use para:
* Integrações customizadas com sistemas internos (helpdesk, CRM, ERP)
* Automação no-code/low-code (n8n, Make, Zapier)
* Gateways próprios de mensageria (instância própria de WhatsApp via Meta, Slack, Teams)
* Logs externos de auditoria
**Como funciona, em resumo:**
* A URL é **única por tenant** (em **HEC → Tenants → Canais de Alerta Permitidos → URL do Webhook**), não por alerta.
* Para cada entrega e cada destinatário, o Lumo faz **um `POST` JSON** no formato documentado.
* Retentativas com backoff exponencial até 8 tentativas; falha em webhook não derruba os outros canais.
> \[!TIP]
> **Para a referência completa do payload, exemplos de fluxos em n8n/Make/Zapier e código receptor em Node.js/Python, veja [Webhook](./webhook/).**
***
## Falhas e Auditoria
Toda entrega gera um registro auditável com:
* Status (sucesso, falha, parcial)
* Mensagem de erro humana quando falha (localizada no idioma do usuário)
* Tempo de cada canal
**Filosofia "fail loud"**: se qualquer canal esperado falhar, a entrega inteira fica com status de erro. Não há falha silenciosa, o usuário precisa saber que algo não foi entregue.
Veja o histórico de entregas em **Alertas > Histórico** (modal do alerta).
***
## Próximos Passos
* Volte para [Visão Geral](./index.md) e configure disparo + conteúdo
* Para regras de envio condicional, [Condicional](./conditions.md)
* Permissões de quem pode editar canais: [Permissões](./permissions.md)
---
---
url: 'https://docs.horusbi.com.br/dw/getting-started/upload.md'
---
# Carregando Dados
O primeiro passo para utilizar o HorusDW é carregar seus dados para a plataforma. O método mais comum é através do upload de planilhas Excel diretamente na sua **Minha Mesa**.
***
## 📤 Upload de Dados
Tudo começa na **[Minha Mesa](/dw/intro/)** — seu ambiente privado para importar e trabalhar dados antes de publicá-los para o restante da organização.
1. Acesse o módulo **Data Warehouse**
2. No menu lateral ou na tela inicial, clique em **Minha Mesa**
3. No canto superior direito, clique no botão **Carregar Dados** (ícone `+`)
### Requisitos do Arquivo
Para garantir que o sistema leia seus dados corretamente, sua planilha Excel (`.xlsx` ou `.xls`) deve seguir algumas regras:
| Requisito | Detalhe |
|-----------|---------|
| **Cabeçalhos na Primeira Linha** | A primeira linha deve conter apenas os nomes das colunas |
| **Aba Única** | O sistema lerá apenas a primeira aba da planilha |
| **Dados Limpos** | Evite células mescladas, linhas em branco no início ou totais/subtotais no meio dos dados |
| **Tipagem Consistente** | Uma coluna de "Datas" não deve conter textos ("Sem Previsão") misturados |
***
## 📋 Importação e Metadados
Ao selecionar o arquivo, o HorusDW fará uma leitura inicial (amostragem) para identificar as colunas e sugerir os tipos de dados automaticamente.
1. **Validação** — O sistema verifica se o arquivo atende aos requisitos
2. **Pré-visualização** — Você verá uma lista das colunas identificadas, com sugestão automática de tipo (`Texto`, `Número` ou `Data`)
3. **Confirmar** — Ao confirmar, os dados são carregados para uma nova **Tabela de Dados**
> \[!WARNING]
> Verifique atentamente os tipos sugeridos. Importar um código numérico (CNPJ) como `Número` pode fazer com que ele perca zeros à esquerda. Nesses casos, prefira o tipo `Texto`.
***
## 📂 Gerenciando Múltiplos Arquivos (Unificação)
O HorusDW permite que você alimente uma única tabela com **múltiplos arquivos**, desde que eles tenham a mesma estrutura de colunas. Isso é útil para unificar dados periódicos.
**Cenário de exemplo:** Você tem uma planilha de vendas para "Janeiro" e outra para "Fevereiro". Ao invés de criar duas tabelas separadas, você pode unificá-las em uma só.
### Passo a Passo
1. Importe o primeiro arquivo (`Vendas_Jan.xlsx`) normalmente
2. Acesse a tabela criada e clique na aba **Gerenciar Arquivos** (ou no menu de contexto da tabela `...` → `Gerenciar Arquivos`)
3. Clique em **Carregar Novo Arquivo**
4. Selecione o segundo arquivo (`Vendas_Fev.xlsx`)
O sistema anexará as novas linhas à tabela existente. Na tela de gerenciamento, você verá a lista de todos os arquivos que compõem aquela tabela, com as seguintes opções:
* **Status de processamento** — Visualize o estado de cada arquivo carregado
* **Excluir arquivo** — Remove apenas os dados pertencentes àquele arquivo específico
* **Download** — Baixe o arquivo original enviado
---
---
url: 'https://docs.horusbi.com.br/hec/notifications.md'
---
# Central de Notificações
A **Central de Notificações** é o sistema integrado que mantém você informado sobre tudo o que acontece na plataforma — desde eventos de segurança e compartilhamentos até alertas de consumo e falhas em pipelines de dados.
Acessível pelo ícone de **sino** no canto superior direito de qualquer módulo (DataViz, ETL, DW, HEC, Solver), a central reúne todas as notificações em um único lugar.
***
## ⚙️ Como Funciona
Quando um evento relevante acontece na plataforma, o sistema gera automaticamente uma notificação para os usuários afetados. As notificações aparecem de duas formas:
1. **Badge no sino**: Um indicador numérico mostra quantas notificações não lidas você tem
2. **Dropdown de notificações**: Clique no sino para ver a lista de notificações recentes
Cada notificação contém:
| Elemento | Descrição |
|----------|-----------|
| **Título** | Resumo curto do evento |
| **Mensagem** | Detalhes do que aconteceu, com contexto relevante |
| **Severidade** | Nível de importância: informativo, atenção ou crítico |
| **Ação** | Link direto para a tela relacionada ao evento |
| **Data/Hora** | Quando o evento ocorreu |
> \[!TIP]
> Clique em uma notificação para ir diretamente à tela relacionada. Por exemplo, uma notificação de Dashboard compartilhado leva você direto à aplicação.
***
## 📋 Tipos de Notificação
As notificações são organizadas por categoria conforme o tipo de evento que as originou.
### 🔐 Segurança
Eventos relacionados à segurança da sua conta e auditoria de acesso.
| Evento | Severidade | Quem Recebe | Descrição |
|--------|------------|-------------|-----------|
| **Impersonação em andamento** | Crítico | Superadministradores do cliente | Um membro do cliente está impersonando outro usuário |
| **Senha redefinida por admin** | Atenção | Usuário afetado | Um administrador redefiniu sua senha |
| **2FA desativado** | Crítico | Usuário + Admins | A autenticação de dois fatores foi desativada em uma conta |
> \[!NOTE]
> Notificações de impersonação são enviadas apenas quando a ação é realizada por um membro do cliente. A notificação é direcionada aos **Superadministradores** cadastrados na aba Usuários do Cliente, permitindo que acompanhem o uso da funcionalidade.
> \[!WARNING]
> Notificações de 2FA desativado são enviadas tanto para o próprio usuário quanto para os administradores do tenant, por se tratar de um evento de segurança crítico.
***
### 🤝 Compartilhamento e Colaboração
Eventos de compartilhamento de Dashboards pessoais entre usuários.
| Evento | Severidade | Quem Recebe | Descrição |
|--------|------------|-------------|-----------|
| **Dashboard pessoal compartilhado** | Informativo | Destinatário | Alguém compartilhou um Dashboard pessoal com você |
| **Propriedade transferida** | Atenção | Novo proprietário | Você se tornou o proprietário de um Dashboard pessoal |
| **Acesso removido** | Informativo | Usuário afetado | Seu acesso a um Dashboard pessoal compartilhado foi removido |
***
### 🗄️ Acesso a Dados
Fluxo de solicitação e aprovação de acesso a Datamarts e tabelas restritas.
| Evento | Severidade | Quem Recebe | Descrição |
|--------|------------|-------------|-----------|
| **Acesso solicitado** | Informativo | Proprietário do recurso | Um usuário solicitou acesso a um Datamart ou tabela |
| **Acesso aprovado** | Informativo | Solicitante | Sua solicitação de acesso foi aprovada |
| **Acesso negado** | Informativo | Solicitante | Sua solicitação de acesso foi negada |
| **Acesso expirado** | Atenção | Usuário afetado | Seu acesso temporário a um recurso expirou |
> \[!NOTE]
> Solicitações de acesso também geram notificações por email para o proprietário do recurso, além da notificação in-app.
***
### 🔄 Pipelines de Dados (ETL)
Eventos de execução de agendamentos e fluxos de dados.
| Evento | Severidade | Quem Recebe | Descrição |
|--------|------------|-------------|-----------|
| **Agendamento executado com sucesso** | Informativo | Destinatários configurados | O agendamento completou todos os fluxos com sucesso |
| **Agendamento falhou** | Crítico | Destinatários + Admins | Um ou mais fluxos do agendamento falharam |
| **Agendamento não executou** | Atenção | Destinatários + Admins | O agendamento não executou no horário previsto |
> \[!TIP]
> Os destinatários de notificações de ETL são configurados diretamente no agendamento, no campo de emails para alerta. Administradores do tenant sempre recebem as notificações de falha e atraso.
***
### 📊 Alertas de Dados (DataViz)
Notificações relacionadas ao sistema de alertas de dados configurados pelo usuário.
| Evento | Severidade | Quem Recebe | Descrição |
|--------|------------|-------------|-----------|
| **Alerta executado** | Informativo | Destinatários do alerta | O alerta foi executado com sucesso. Clique na notificação para visualizar o conteúdo gerado (gráficos, relatórios, textos) |
| **Falha no webhook** | Atenção | Criador do alerta + Admins | O webhook configurado para o alerta falhou permanentemente após múltiplas tentativas |
> \[!TIP]
> Ao clicar na notificação de um alerta executado, você será levado à página de preview onde pode visualizar todo o conteúdo gerado: gráficos, relatórios PDF/Excel, textos e análises de IA.
> \[!NOTE]
> Você também pode acessar o histórico completo de alertas recebidos pela página **Alertas > Histórico**, que lista todas as execuções de alertas direcionadas a você.
***
### 💰 Uso e Billing
Alertas automáticos sobre consumo de recursos do tenant. O sistema verifica o uso a cada 4 horas e notifica quando os limites estão próximos ou foram ultrapassados.
| Evento | Severidade | Quem Recebe | Descrição |
|--------|------------|-------------|-----------|
| **Uso próximo do limite** | Atenção | Admins do cliente/tenant | O consumo de um recurso atingiu 80% ou mais do limite contratado |
| **Limite excedido** | Crítico | Admins do cliente/tenant | O consumo ultrapassou 100% do limite contratado |
Os três recursos monitorados são:
| Recurso | O que é medido |
|---------|----------------|
| **Usuários** | Quantidade de usuários ativos no tenant |
| **Tokens de IA** | Consumo mensal de tokens para funcionalidades de inteligência artificial |
| **Processamento** | Volume mensal de dados processados e armazenados (em GB) |
> \[!IMPORTANT]
> Alertas de billing são enviados no máximo uma vez a cada 24 horas para cada recurso e faixa de uso. Se o uso subir de 80% para 100%, uma nova notificação é gerada — mas a notificação de 80% não será repetida até o dia seguinte.
**Exemplos de notificações de billing:**
* *"O tenant **Produção** está usando **45 de 50** usuários (90%)."*
* *"O tenant **Produção** excedeu o limite de tokens IA: **12.500 de 10.000** tokens utilizados (125%)."*
***
### 🏠 Tenant e Sistema
Eventos administrativos e de sistema.
| Evento | Severidade | Quem Recebe | Descrição |
|--------|------------|-------------|-----------|
| **WhatsApp desconectou** | Crítico | Admins | Uma instância de WhatsApp perdeu a conexão |
| **Aviso da plataforma** | Variável | Todos ou Admins | A equipe Horus pode enviar avisos manuais para todos os usuários ou apenas administradores da plataforma |
***
## ⚡ Severidades
As notificações usam três níveis de severidade para indicar a urgência:
| Nível | Significado | Exemplos |
|-------|-------------|----------|
| **Informativo** | Evento normal, sem ação necessária | Dashboard compartilhado, agendamento executado com sucesso |
| **Atenção** | Requer sua avaliação | Uso próximo do limite, agendamento não executou, acesso expirado |
| **Crítico** | Ação imediata necessária | Agendamento falhou, limite excedido, 2FA desativado |
***
## 📬 Quem Recebe o Quê?
As notificações são enviadas automaticamente para os usuários relevantes de cada evento. Você **não** precisa configurar nada — o sistema identifica os destinatários corretos com base no contexto.
A lógica geral é:
| Tipo de Evento | Quem Recebe |
|----------------|-------------|
| Impersonação | Superadministradores do cliente |
| Ações na sua conta (senha, 2FA) | Você mesmo |
| Compartilhamentos | O destinatário do compartilhamento |
| Solicitações de acesso | O proprietário do recurso ou o solicitante |
| Falhas em ETL | Destinatários configurados no agendamento + admins |
| Alertas de billing | Administradores do cliente (whitelabel) ou do tenant (standalone) |
| Eventos de sistema | Administradores do tenant |
***
## 📖 Catálogo Completo de Notificações
Referência técnica de todas as notificações geradas pela plataforma.
| Código | Evento | Tipo | Severidade | Quem Recebe |
|--------|--------|------|------------|-------------|
| **IMP-01** | Impersonação em andamento | Segurança | Crítico | Superadministradores do cliente |
| **SEC-02** | Senha redefinida por admin | Segurança | Atenção | Usuário afetado |
| **SEC-05** | 2FA desativado | Segurança | Crítico | Usuário afetado |
| **SHR-01** | Dashboard pessoal compartilhado (por usuário) | Compartilhamento | Informativo | Destinatários do compartilhamento |
| **SHR-02** | Dashboard pessoal compartilhado (por admin) | Compartilhamento | Informativo | Destinatários do compartilhamento |
| **SHR-03** | Propriedade de Dashboard transferida | Compartilhamento | Atenção | Novo proprietário |
| **SHR-04** | Acesso a Dashboard removido | Compartilhamento | Informativo | Usuário afetado |
| **DAR-01** | Acesso a dados solicitado | Acesso a Dados | Informativo | Proprietário do recurso |
| **DAR-02** | Acesso a dados aprovado | Acesso a Dados | Informativo | Solicitante |
| **DAR-03** | Acesso a dados negado | Acesso a Dados | Informativo | Solicitante |
| **DAR-04** | Acesso a dados expirado | Acesso a Dados | Atenção | Usuário afetado |
| **ETL-01** | Agendamento executado com sucesso | ETL | Informativo | Destinatários configurados |
| **ETL-02** | Agendamento falhou | ETL | Crítico | Destinatários + Admins do tenant |
| **ETL-03** | Agendamento não executou | ETL | Atenção | Destinatários configurados |
| **DAT-01** | Alerta de dados executado | DataViz | Informativo | Destinatários do alerta |
| **DAT-02** | Falha no webhook do alerta | DataViz | Atenção | Criador do alerta + Admins |
| **BIL-01** | Uso próximo do limite (≥80%) | Billing | Atenção | Admins do cliente/tenant |
| **BIL-02** | Limite de uso excedido (≥100%) | Billing | Crítico | Admins do cliente/tenant |
| **WPP-01** | WhatsApp desconectou | Sistema | Crítico | Admins do tenant |
| **SYS-03** | Aviso da plataforma | Sistema | Variável | Todos ou Admins (configurável) |
***
## ❓ Perguntas Frequentes
### Posso desativar notificações?
Atualmente, as notificações in-app não podem ser desativadas individualmente. Elas são geradas automaticamente para os eventos relevantes ao seu perfil.
### As notificações expiram?
Notificações ficam disponíveis na central por tempo indeterminado. Você pode marcá-las como lidas para manter a lista organizada.
### Recebo notificações de outros tenants?
Você recebe notificações apenas dos tenants aos quais tem acesso.
### Alertas de billing aparecem para todos os usuários?
Não. Alertas de uso e billing são enviados apenas para administradores do cliente (em tenants whitelabel) ou administradores do tenant (em tenants standalone). Usuários comuns não recebem esses alertas.
---
---
url: 'https://docs.horusbi.com.br/ia/chat.md'
---
# Chat com Dados
O **Chat com Dados** é o assistente de IA do Lumo: você pergunta em linguagem natural e ele responde olhando seus dados reais. Ele enxerga as aplicações, executa consultas e devolve insights ancorados nos seus números.
::: info Nomenclatura
O assistente do Lumo é chamado **Lumia** por padrão. Em ambientes whitelabel ele pode ser renomeado pelo administrador do tenant, então ao longo da documentação preferimos os termos genéricos — "o chat", "o assistente", "a IA do Lumo".
:::
***
## Onde o chat aparece
O chat está disponível em vários pontos da plataforma:
* **Sidebar de Insights** — no menu lateral esquerdo, ícone de chat. Ocupa um painel lateral persistente, ideal para conversas longas ou quando você está alternando entre dashboards.
* **Janela Flutuante** — abre como popup, útil para perguntas rápidas sem sair do que está vendo.
* **Dentro de uma aplicação** — o botão de chat continua acessível enquanto você navega num dashboard, como uma forma rápida de complementar com perguntas o que já está sendo exibido.
***
## "Perguntar ao chat" vs "olhar o dashboard"
São formas complementares de consumir dados.
| | Dashboard | Chat |
|---|---|---|
| **Forma** | Visual fixo, definido por quem construiu | Resposta sob demanda, em texto |
| **Bom para** | Acompanhar métricas conhecidas, comparações pré-pensadas | Pergunta nova, exploração ad-hoc, cruzamento incomum |
| **Velocidade** | Imediato — já está montado | Alguns segundos — precisa consultar |
| **Profundidade** | Limitado ao que foi montado | Pode investigar dimensões diferentes a cada pergunta |
A regra prática: **se você já sabe o que quer ver toda semana, faça dashboard.** **Se você quer perguntar coisas que mudam a cada vez, use o chat.**
***
## Modos de resposta
O chat oferece dois modos, selecionáveis acima da caixa de mensagem:
* **Rápido** — para perguntas diretas e métricas pontuais. Responde em segundos.
* **Raciocínio** — para análises mais complexas (investigação de causa, comparativos com várias dimensões). Mais lento, mas mais cuidadoso.
A regra prática: comece em **Rápido**. Se a resposta vier rasa demais, refaça a pergunta em **Raciocínio**.
***
## Escolher qual agente responde
Por padrão, quem responde no chat é o assistente do Lumo. Mas você pode ter criado **agentes próprios** — personas de investigação com instruções e escopo específicos, como "Analista de inadimplência" ou "Vigia do funil comercial". Um seletor no topo do chat deixa você escolher qual deles conduz a conversa.
No seletor aparecem o assistente padrão e todos os agentes marcados como **"Disponível no chat"**. Ao trocar de agente, as próximas perguntas passam a ser respondidas com a persona, o escopo de aplicações e o esforço configurados naquele agente — sem que você precise repetir contexto a cada mensagem.
::: tip Quando trocar de agente
Use o assistente padrão para perguntas gerais e exploração livre. Escolha um agente próprio quando quer uma investigação sempre com o mesmo recorte — as mesmas aplicações, o mesmo jeito de responder — como se tivesse um analista dedicado àquele assunto.
:::
Os agentes são criados e testados no módulo **[Agentes de IA](/ia/agentes)**. Lá você também decide quais deles ficam disponíveis aqui no chat.
***
## Como ele "pensa", em alto nível
Quando você pergunta algo, o chat:
1. **Descobre** quais dados estão disponíveis na aplicação (tabelas, colunas, indicadores configurados).
2. **Resolve** nomes mencionados (ex.: "cliente acme" vira "ACME Indústrias S.A.").
3. **Executa consultas reais** sobre seus dados — os números vêm direto do banco.
4. **Consolida** os números em uma resposta em linguagem natural.
Por isso a resposta vem como **insight** ("vendas caíram 12% no comparativo semanal, puxado pela Filial Centro") e não como tabela bruta. Para a tabela bruta, use o **Explorer** ou um **Relatório**.
Para o detalhe do processo, veja **[Como o chat responde](./como-funciona.md)**.
***
## Como você ensina o seu negócio
O chat consegue responder mesmo sem nenhuma configuração — ele lê a estrutura da aplicação por conta própria. Mas o modelo de dados real quase sempre tem **ambiguidade**: múltiplas colunas que poderiam ser "vendas", múltiplas datas que poderiam ser "tempo".
Sem configuração, o chat faz uma escolha razoável a cada conversa — mas a escolha não é determinística. **Para que a resposta seja consistente**, você configura **indicadores**: definições explícitas de cada conceito do seu negócio (qual coluna é a métrica, qual a data, quais filtros padrão).
Os indicadores têm um papel duplo: além de alimentar a IA com contexto curado, eles aparecem no **[cockpit de Indicadores](/dataviz/04-features/indicadores/)** — um painel que exibe cada métrica ao vivo com status RAG, variação vs ciclo anterior e série histórica. Cuidar dos indicadores é cuidar ao mesmo tempo da qualidade das respostas e da inteligência do cockpit.
::: tip Comece pelos indicadores
Se você nota o chat dando respostas inconsistentes, **é quase sempre porque faltam indicadores** — ou porque tem indicadores demais competindo entre si. Veja **[Indicadores: Ensinando seu Negócio](./fatos.md)** para entender o conceito e os erros comuns.
:::
***
## IA pode errar
O chat **sempre** consulta seus dados reais para chegar nos números — ele não inventa valores. Mas pode interpretar a pergunta de outro jeito que você esperava, ou tirar conclusões além do que os dados sustentam.
A linha que aparece no rodapé do chat — *"IA pode cometer erros. Verifique informações importantes."* — é literal. Para decisões importantes, vale conferir os números no dashboard ou no Explorer.
***
## Próximos passos
* **[Como o Chat Responde](./como-funciona.md)** — o caminho de uma pergunta, modos Rápido vs Raciocínio, memória entre conversas, aprendizado tácito.
* **[Indicadores: Ensinando seu Negócio](./fatos.md)** — a peça central. O que é um indicador, por que importa, e o anti-padrão "dicionário gigante".
* **[Exemplos de Indicadores](./exemplos-de-fatos.md)** — casos práticos: indicadores bem feitos, anti-exemplos, e quando vale separar de verdade.
* **[Agentes de IA](/ia/agentes)** — criar personas de investigação próprias e escolher quais respondem aqui no chat.
* **[Aba Conceitos IA](/dataviz/02-apps/ai-concepts.md)** — referência detalhada da UI de cadastro de indicadores na aplicação.
* **[Cockpit de Indicadores](/dataviz/04-features/indicadores/)** — painel ao vivo com status RAG, variação vs ciclo anterior e seus indicadores favoritos.
---
---
url: 'https://docs.horusbi.com.br/api/ai-chat.md'
---
# Chat IA — API REST (Lumia)
Esta página documenta a API REST do motor de chat IA da plataforma HorusBI (Lumia), disponível para integrações server-to-server. Com ela, você pode enviar mensagens em nome de um usuário e receber a resposta via **streaming (Server-Sent Events)** — incluindo os *tool results* estruturados (tabelas, gráficos, buscas) — ou no modo **JSON buffered** para integrações mais simples.
## Autenticação e identidade
Todos os endpoints de chat ficam sob `/v1/tenant` e usam o **Tenant Bearer Token** (`sys_tenant_api_token`):
```http
Authorization: Bearer
```
O token identifica o tenant. O `userId` vem no body (ou query) e identifica o usuário em nome de quem a ação é executada. Antes de qualquer operação, o backend valida que o `userId` pertence ao tenant (tabela `sys_users_tenants`, `excluido=false`, `ativo=true`). Usuário fora do tenant → `403`.
As permissões de IA, apps e tabelas são **herdadas automaticamente** do usuário: o motor verifica `use_ai` antes de abrir o stream (`403` se ausente) e aplica restrições de acesso por tabela/app dentro das ferramentas.
> **Modelo de confiança:** o tenant token autoriza atuar em nome de qualquer usuário ativo do tenant. Esse modelo é adequado para integração server-to-server, onde o integrador gerencia com segurança o mapeamento email → userId.
## Fluxo completo
```
1. GET /v1/tenant/users?login= → resolve email para userId
2. POST /v1/tenant/ai/threads (opcional) → cria thread antecipadamente
3. POST /v1/tenant/ai/chat → envia mensagem (SSE ou JSON)
4. GET /v1/tenant/ai/threads/:threadId/messages → consulta histórico
```
Se você omitir o `threadId` no passo 3, uma nova thread é criada automaticamente e seu handle chega no primeiro evento SSE (`event: thread`).
***
## Referência dos endpoints
### `GET /v1/tenant/users`
Resolve email → userId. Útil para obter o `userId` a partir do login do usuário antes de chamar o chat.
**Query params:**
| Parâmetro | Tipo | Descrição |
|-----------|------|-----------|
| `login` | string | E-mail do usuário (filtro exato) |
| `id` | integer | ID do usuário (alternativa) |
Sem filtro, retorna todos os usuários do tenant (comportamento original, retrocompatível).
**Resposta (200):**
```json
{
"status": "success",
"data": [
{ "id": 123, "nome": "João Silva", "login": "joao@empresa.com" }
]
}
```
***
### `POST /v1/tenant/ai/chat`
Envia uma mensagem ao motor de IA em nome de um usuário. Retorna a resposta via **SSE** (padrão) ou **JSON buffered**.
**Negociação de modo:**
| Condição | Modo |
|----------|------|
| `Accept: text/event-stream` ou `?stream=true` (padrão) | SSE (streaming) |
| `Accept: application/json` ou `?stream=false` | JSON buffered |
**Body (JSON):**
| Campo | Tipo | Obrigatório | Descrição |
|-------|------|-------------|-----------|
| `userId` | integer | sim | ID do usuário no tenant |
| `message` | string | sim | Pergunta ou comando do usuário |
| `threadId` | string | não | Handle da thread (`bi_`); omitido = cria nova |
| `modelKey` | `'fast'` | `'reasoning'` | não | Modelo preferido; sem efeito se o tenant tiver config própria de IA |
**Exemplo de body:**
```json
{
"userId": 123,
"message": "Quanto vendi em maio?",
"threadId": "bi_a1b2c3d4-...",
"modelKey": "fast"
}
```
**Resposta SSE:** veja a seção [Contrato SSE](#contrato-sse) abaixo.
**Resposta JSON buffered (200):**
```json
{
"status": "success",
"data": {
"threadId": "bi_a1b2c3d4-...",
"text": "Em maio você vendeu R$ 214.500,50...",
"toolResults": [
{
"toolName": "execute_query",
"result": { "type": "query", "title": "Vendas maio", "columns": ["Mês", "Total"], "rows": [["Maio", 214500.50]], "rowCount": 1 }
}
],
"usage": { "inputTokens": 312, "outputTokens": 87, "totalTokens": 399 }
}
}
```
***
### `GET /v1/tenant/ai/threads`
Lista as threads de conversa do usuário. Por padrão retorna apenas threads de origem `api` (threads criadas por esta API), isolando-as das conversas do frontend.
**Query params:**
| Parâmetro | Tipo | Obrigatório | Descrição |
|-----------|------|-------------|-----------|
| `userId` | integer | sim | ID do usuário |
| `origin` | string | não | Filtro de origem (padrão: `api`) |
**Resposta (200):**
```json
{
"status": "success",
"data": {
"threads": [
{
"threadId": "bi_a1b2c3d4-...",
"lastMessageAt": "2026-06-19T12:00:00Z",
"messageCount": 8
}
]
}
}
```
***
### `POST /v1/tenant/ai/threads`
Cria uma thread vazia com origem `api` e retorna o handle. Use quando precisar do `threadId` antes de enviar a primeira mensagem.
**Body:**
```json
{ "userId": 123 }
```
**Resposta (200):**
```json
{
"status": "success",
"data": { "threadId": "bi_a1b2c3d4-..." }
}
```
***
### `GET /v1/tenant/ai/threads/:threadId/messages`
Retorna o histórico completo da thread. Thread de outro usuário ou tenant → `404`.
**Path params:** `:threadId` — handle string (`bi_`)
**Query params:**
| Parâmetro | Tipo | Obrigatório | Descrição |
|-----------|------|-------------|-----------|
| `userId` | integer | sim | ID do usuário dono da thread |
**Resposta (200):**
```json
{
"status": "success",
"data": {
"messages": [
{
"role": "user",
"message": "Quanto vendi em maio?",
"enviado_em": "2026-06-19T12:00:00Z",
"contexto": null
},
{
"role": "assistant",
"message": "Em maio você vendeu R$ 214.500,50...",
"enviado_em": "2026-06-19T12:00:05Z",
"contexto": { "toolCalls": [], "toolResults": [] }
}
]
}
}
```
***
### `POST /v1/tenant/ai/threads/:threadId/cancel`
Sinaliza o cancelamento de um stream em andamento. **Fallback explícito:** o modo primário de cancelamento é fechar a conexão SSE. Este endpoint é best-effort e não garante interrupção imediata do passo atual do modelo.
**Resposta (200):**
```json
{ "status": "success", "data": { "cancelled": true } }
```
***
## Contrato SSE
### Headers de resposta
```
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
X-Accel-Buffering: no
```
### Formato dos frames
Cada evento usa o formato padrão SSE:
```
event: \n
data: \n
\n
```
**Keepalive:** o servidor emite frames de comentário (`: ping`) periodicamente (~20s) para manter a conexão ativa. Ignore-os no parser.
### Tabela de eventos
| Evento | Dados (`data`) | Descrição |
|--------|----------------|-----------|
| `thread` | `{ "threadId": "bi_..." }` | Emitido no início; informa o handle da thread (nova ou existente) |
| `text-delta` | `{ "text": "...", "messageId": "..." }` | Fragmento incremental do texto da resposta |
| `reasoning-delta` | `{ "text": "..." }` | Fragmento do raciocínio interno (modelos com reasoning) |
| `tool-call` | `{ "toolName": "execute_query", "toolCallId": "...", "description": "..." }` | O modelo está invocando uma ferramenta |
| `tool-result` | `{ "toolName": "...", "toolCallId": "...", "result": { /* ToolResultData */ } }` | Resultado estruturado da ferramenta |
| `done` | `{ "messageId": "...", "threadId": "bi_...", "usage": { "inputTokens": N, "outputTokens": N, "totalTokens": N } }` | Stream encerrado com sucesso |
| `error` | `{ "error": "mensagem" }` | Falha terminal; stream é encerrado após este evento |
> **`usage` no `done` (best-effort):** os tokens são sempre contabilizados e persistidos internamente. O campo `usage` no evento `done` é exposto quando disponível; pode estar ausente em casos excepcionais.
> **`error` é terminal:** O evento `error` encerra o stream imediatamente. Se a falha ocorrer **após eventos parciais** (`text-delta`, `tool-result`), o stream termina com `error` — neste caso, **não haverá evento `done`**. O cliente deve tratar tanto `done` quanto `error` como sinais de fim de stream e não esperar `done` após um `error`.
***
## Shapes de `ToolResultData`
O campo `result` dentro do evento `tool-result` segue a união abaixo, discriminada pelo campo `type`.
### `type: 'query'`
Resultado de uma consulta analítica.
| Campo | Tipo | Obrigatório | Descrição |
|-------|------|-------------|-----------|
| `type` | `'query'` | sim | Discriminador |
| `title` | string | sim | Título da consulta |
| `columns` | string\[] | sim | Nomes das colunas |
| `rows` | any\[]\[] | sim | Linhas de dados |
| `rowCount` | integer | sim | Total de linhas |
| `summary` | string | não | Resumo textual gerado pela IA |
| `permalink` | string | não | URL permanente para o relatório no DataViz |
| `chartUrl` | string | não | URL de imagem do gráfico |
| `chartOptions` | object | não | Opções de configuração do gráfico |
| `filtersApplied` | object | não | Filtros que foram aplicados na consulta |
**Exemplo:**
```json
{
"type": "query",
"title": "Vendas por mês",
"columns": ["Mês", "Total (R$)"],
"rows": [
["Janeiro", 198320.00],
["Fevereiro", 214500.50],
["Março", 187450.75]
],
"rowCount": 3,
"summary": "As vendas cresceram 8% de janeiro a fevereiro.",
"permalink": "https://empresa.horusbi.com.br/app/123/dashboard/456?filters=...",
"chartUrl": "https://api.horusbi.com.br/v1/exports/chart/abc123.png"
}
```
***
### `type: 'comparison'`
Resultado de uma comparação entre dois períodos. Inclui todos os campos de `query` mais:
| Campo | Tipo | Descrição |
|-------|------|-----------|
| `secondaryRows` | any\[]\[] | Linhas do período de comparação |
| `comparisonLabels` | string\[] | Rótulos dos dois períodos (ex.: `["Maio/25", "Maio/26"]`) |
| `variationColumns` | string\[] | Colunas que contêm a variação percentual |
**Exemplo:**
```json
{
"type": "comparison",
"title": "Vendas: Maio/25 vs Maio/26",
"columns": ["Vendedor", "Maio/25", "Maio/26", "Variação"],
"rows": [["Ana Lima", 45000, 52000, "+15,6%"]],
"rowCount": 1,
"secondaryRows": [["Ana Lima", 45000]],
"comparisonLabels": ["Maio/25", "Maio/26"],
"variationColumns": ["Variação"]
}
```
***
### `type: 'forecast'`
Resultado de uma previsão. Inclui todos os campos de `query` mais:
| Campo | Tipo | Descrição |
|-------|------|-----------|
| `forecastData` | object | Dados projetados e intervalos de confiança |
**Exemplo:**
```json
{
"type": "forecast",
"title": "Previsão de Vendas — Julho/26",
"columns": ["Mês", "Previsto (R$)"],
"rows": [["Julho/26", 230000]],
"rowCount": 1,
"forecastData": {
"periods": ["Julho/26", "Agosto/26"],
"values": [230000, 241500],
"confidenceLow": [210000, 218000],
"confidenceHigh": [250000, 265000]
}
}
```
***
### `type: 'search'`
Resultado de uma busca de registros em um app.
| Campo | Tipo | Obrigatório | Descrição |
|-------|------|-------------|-----------|
| `type` | `'search'` | sim | Discriminador |
| `appName` | string | sim | Nome do app pesquisado |
| `columnLabel` | string | sim | Coluna onde a busca foi feita |
| `searchText` | string | sim | Texto buscado |
| `results` | string\[] | sim | Valores encontrados |
| `resultCount` | integer | sim | Quantidade de resultados |
**Exemplo:**
```json
{
"type": "search",
"appName": "CRM",
"columnLabel": "Nome do Cliente",
"searchText": "Acme",
"results": ["Acme Corp", "Acme Industrial", "Acme Logística"],
"resultCount": 3
}
```
***
### `type: 'metadata'`
Informações estruturais de um app (campos disponíveis, etc.).
| Campo | Tipo | Obrigatório | Descrição |
|-------|------|-------------|-----------|
| `type` | `'metadata'` | sim | Discriminador |
| `appName` | string | sim | Nome do app |
**Exemplo:**
```json
{
"type": "metadata",
"appName": "Dashboard Vendas"
}
```
***
### `{ error: string }`
Falha ao executar a ferramenta (sem campo `type`). O motor continua e pode gerar mais eventos após isso.
**Exemplo:**
```json
{ "error": "Tabela 'vendas_detalhe' não acessível para este usuário." }
```
***
## Exemplos de integração
### curl — SSE
```bash
curl -N -X POST "https://api.horusbi.com.br/v1/tenant/ai/chat" \
-H "Authorization: Bearer $TENANT_TOKEN" \
-H "Accept: text/event-stream" \
-H "Content-Type: application/json" \
-d '{"userId": 123, "message": "Quanto vendi em maio?"}'
```
***
### Node.js 18+ — fetch com parser SSE
```js
const res = await fetch("https://api.horusbi.com.br/v1/tenant/ai/chat", {
method: "POST",
headers: {
Authorization: `Bearer ${TENANT_TOKEN}`,
Accept: "text/event-stream",
"Content-Type": "application/json",
},
body: JSON.stringify({ userId: 123, message: "Quanto vendi em maio?" }),
});
const reader = res.body.getReader();
const decoder = new TextDecoder();
let buf = "";
let answer = "";
while (true) {
const { value, done } = await reader.read();
if (done) break;
buf += decoder.decode(value, { stream: true });
const frames = buf.split("\n\n");
buf = frames.pop() ?? "";
for (const f of frames) {
if (f.startsWith(":")) continue; // keepalive
const event = f.match(/^event: (.+)$/m)?.[1];
const data = JSON.parse(f.match(/^data: (.+)$/m)?.[1] ?? "{}");
if (event === "thread") console.log("threadId:", data.threadId);
else if (event === "text-delta") answer += data.text;
else if (event === "tool-result") renderToolResult(data.result);
else if (event === "done")
console.log("Resposta completa. Tokens:", data.usage?.totalTokens);
}
}
console.log("Resposta:", answer);
// Renderização de tool-result
function renderToolResult(r) {
if (!r) return;
if (r.type === "query" || r.type === "comparison" || r.type === "forecast") {
console.log(`[${r.type}] ${r.title}`);
console.table(r.rows.map((row) =>
Object.fromEntries(r.columns.map((col, i) => [col, row[i]]))
));
if (r.permalink) console.log("Relatório:", r.permalink);
if (r.chartUrl) console.log("Gráfico:", r.chartUrl);
} else if (r.type === "search") {
console.log(`[search] ${r.appName} → ${r.columnLabel}:`, r.results.join(", "));
} else if (r.error) {
console.warn("[tool-error]", r.error);
}
}
```
***
### Node.js — modo JSON buffered
Para casos onde streaming não é necessário, use `?stream=false` ou `Accept: application/json`:
```js
const res = await fetch("https://api.horusbi.com.br/v1/tenant/ai/chat?stream=false", {
method: "POST",
headers: {
Authorization: `Bearer ${TENANT_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ userId: 123, message: "Qual o total de vendas de maio?" }),
});
const { status, data } = await res.json();
if (status === "success") {
console.log("Resposta:", data.text);
console.log("Thread:", data.threadId);
console.log("Tokens:", data.usage?.totalTokens);
for (const tr of data.toolResults ?? []) {
renderToolResult(tr.result);
}
}
```
***
### Python — httpx com streaming SSE
```python
import json
import httpx
TENANT_TOKEN = "seu_token_aqui"
with httpx.stream(
"POST",
"https://api.horusbi.com.br/v1/tenant/ai/chat",
headers={
"Authorization": f"Bearer {TENANT_TOKEN}",
"Accept": "text/event-stream",
"Content-Type": "application/json",
},
json={"userId": 123, "message": "Quanto vendi em maio?"},
timeout=None,
) as r:
event = None
answer = ""
for line in r.iter_lines():
if not line: # fim de um frame SSE
event = None
continue
if line.startswith(":"): # keepalive
continue
if line.startswith("event: "):
event = line[7:]
elif line.startswith("data: "):
data = json.loads(line[6:])
if event == "thread":
print("threadId:", data["threadId"])
elif event == "text-delta":
answer += data["text"]
elif event == "tool-result":
r_data = data["result"]
if r_data.get("type") == "query":
print(f"[query] {r_data['title']}: {r_data['rows']}")
elif r_data.get("type") == "search":
print(f"[search] {r_data['appName']}: {r_data['results']}")
elif event == "done":
print("threadId:", data["threadId"],
"tokens:", data.get("usage", {}).get("totalTokens"))
print("Resposta:", answer)
```
***
### Fluxo completo: email → chat → histórico
```js
const BASE = "https://api.horusbi.com.br/v1/tenant";
const headers = { Authorization: `Bearer ${TENANT_TOKEN}` };
// 1. Resolver email → userId
const usersRes = await fetch(`${BASE}/users?login=joao@empresa.com`, { headers });
const { data: users } = await usersRes.json();
const userId = users[0]?.id;
if (!userId) throw new Error("Usuário não encontrado");
// 2. Enviar mensagem (nova thread criada automaticamente)
let threadId;
const chatRes = await fetch(`${BASE}/ai/chat?stream=false`, {
method: "POST",
headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify({ userId, message: "Qual o produto mais vendido?" }),
});
const { data: chat } = await chatRes.json();
threadId = chat.threadId;
console.log("Resposta:", chat.text);
// 3. Continuar na mesma thread
const followRes = await fetch(`${BASE}/ai/chat?stream=false`, {
method: "POST",
headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify({ userId, threadId, message: "E em fevereiro?" }),
});
const { data: follow } = await followRes.json();
console.log("Follow-up:", follow.text);
// 4. Consultar histórico
const histRes = await fetch(
`${BASE}/ai/threads/${threadId}/messages?userId=${userId}`,
{ headers }
);
const { data: hist } = await histRes.json();
console.log("Mensagens na thread:", hist.messages.length);
```
***
## Erros
| Código | Situação |
|--------|----------|
| `400` | Validação do body falhou (campo obrigatório ausente, tipo incorreto) |
| `403` | `userId` não pertence ao tenant, ou usuário sem permissão `use_ai` |
| `404` | Thread não encontrada (ou pertence a outro usuário/tenant) |
| `500` | Erro interno; em SSE o evento `error` é emitido antes do encerramento |
No modo SSE, erros após o início do stream chegam como evento `error`:
```
event: error
data: {"error":"Usuário sem permissão de IA (use_ai)."}
```
No modo JSON buffered, erros retornam como:
```json
{ "status": "error", "message": "Usuário sem permissão de IA (use_ai)." }
```
---
---
url: 'https://docs.horusbi.com.br/etl/architecture/execution-lifecycle.md'
---
# Ciclo de Vida de Execução
Entenda o que acontece nos bastidores desde o momento em que você desenha um fluxo até o processamento final dos dados.
***
## 1. 🎨 Design (Frontend)
Tudo começa na tela de edição. O usuário arrasta nós (Banco de Dados, HTTP, Excel) e conecta as linhas.
* **Ação** — Ao salvar, essa "receita" é convertida em um arquivo de configuração (JSON) e armazenada no Banco de Dados do Backend (Nuvem)
* **Estado** — *Configurado*
***
## 2. 🔄 Deploy (Sincronização)
O agente, que está rodando no servidor, mantém contato constante com a nuvem via WebSocket.
* **Ação** — Quando o Backend detecta uma nova versão de fluxo ou um novo agendamento, ele avisa o Agente
* **Sincronia** — O Agente baixa a nova "receita" e a armazena localmente em disco, o que evita ter que buscar a definição do fluxo a cada execução
> \[!IMPORTANT]
> **O Agente não opera offline.** A receita fica em disco, mas a execução depende da nuvem em dois pontos: o agendamento é disparado a partir do Backend, e a entrega no HorusDW exige rede. Sem conexão, o agendamento não dispara e a carga não conclui.
***
## 3. ⚙️ Execução (Engine)
Quando chega a hora agendada (ou alguém clica em "Executar Agora"):
1. **Instanciação** — O Agente cria um processo isolado para aquele fluxo
2. **Conexão** — O Agente usa as credenciais salvas para conectar nas fontes de dados
3. **Streaming** — Os dados começam a fluir do Nó A para o Nó B na memória. Para grandes volumes, o sistema usa arquivos temporários locais (cache em disco) para não esgotar a memória RAM
***
## 4. 📊 Monitoramento (Feedback)
Enquanto trabalha, o Agente envia sinais vitais de volta para a nuvem em tempo real.
* **Logs** — "Conectou no banco", "Leu 1000 linhas", "Erro na coluna X"
* **Métricas** — Uso de memória e duração
* **Conclusão** — Ao finalizar, o Backend é notificado e atualiza o status na interface web para "Sucesso" ou "Erro"
---
---
url: 'https://docs.horusbi.com.br/etl/processors/transforms/ai-extract.md'
---
# Classificação por IA (AIExtract)
O nó **AIExtract** enriquece cada linha do DataFrame com campos extraídos ou classificados por um LLM. Você define o schema de saída — nome, tipo e descrição de cada campo — e a IA preenche esses campos para cada registro.
***
## Funcionalidades
* Classifica, normaliza ou extrai informações estruturadas de texto livre
* Schema tipado com vocabulário fechado opcional (lista de valores permitidos)
* Cardinalidade flexível: um valor por linha, vários em texto, ou expansão de linhas (explode)
* Múltiplos tiers de modelo com custo e qualidade distintos
* Cache automático por tenant + modelo + schema + registro — sem custo duplo
* Processamento em batches com paralelismo configurável
***
## Parâmetros de Configuração
### Geral
* **Colunas de entrada** (`inputColumns`) — Quais colunas enviar ao LLM como contexto. Inclua apenas o que é relevante; colunas desnecessárias aumentam custo.
* **Dica de contexto** (`contextHint`) — Instrução adicional sobre o domínio dos dados (ex: `"dados de e-commerce B2B"`). Opcional.
* **Tier do modelo** (`tier`) — Qualidade e custo do LLM. Padrão: `fast`.
### Performance
| Parâmetro | Padrão | Máximo | Descrição |
|-----------|--------|--------|-----------|
| `batchSize` | 100 | 1000 | Registros por chamada ao LLM |
| `maxParallelBatches` | 5 | 20 | Batches processados em paralelo |
### Tiers de Modelo
| Tier | Modelo | Entrada | Saída | Uso recomendado |
|------|--------|---------|-------|-----------------|
| `free` | GPT-OSS 20B | grátis | grátis | Testes — pode alucinar |
| `turbo` | Mercury 2 (diffusion) | $0,25/M | $0,75/M | Alto volume, geração ~5× mais rápida |
| `fast` | Grok 4.1 Fast | $0,20/M | $0,50/M | **Padrão** — melhor custo-benefício |
| `balanced` | Gemini 2.5 Flash | $0,30/M | $2,50/M | Schemas complexos ou textos longos |
| `smart` | Gemini 3 Flash Preview | $0,50/M | $3,00/M | Máxima precisão |
***
## Schema de Saída
Cada campo define o que a IA deve extrair. Os campos originais do input são preservados; os campos abaixo são adicionados ao output.
| Propriedade | Obrigatório | Descrição |
|-------------|-------------|-----------|
| `name` | sim | Nome da coluna — MAIÚSCULAS, sem acentos, max 30 chars |
| `type` | sim | `string`, `int`, `float` ou `bool` |
| `description` | sim | O que o campo representa. Mínimo 10 caracteres. Quanto mais preciso, melhor o resultado. |
| `values` | não | Lista de valores permitidos (vocabulário fechado, só para `type: string`) |
| `cardinality` | não | `single` (padrão), `join` ou `explode` |
| `separator` | não | Separador para `cardinality: join`. Padrão: `, ` |
| `required` | não | Se `true`, rejeita registros onde esse campo não foi retornado |
### Cardinalidade
| Modo | Comportamento | Exemplo |
|------|--------------|---------|
| `single` | 1 valor por linha | `"Eletrônicos"` |
| `join` | Múltiplos valores serializados em texto | `"Doce, Salgado, Picante"` |
| `explode` | 1 linha de output por valor — expande o DataFrame | 3 valores → 3 linhas |
Apenas **1 campo por nó** pode usar `cardinality: explode`. Múltiplos campos explode gerariam produto cartesiano. Divida em nós AIExtract separados se necessário.
***
## Boas Práticas
**Descrições de campo** — A qualidade da classificação depende da qualidade das descrições.
| Ruim | Bom |
|------|-----|
| `"Categoria do produto"` | `"Categoria principal do produto: Eletrônicos, Vestuário, Alimentos, Móveis ou Outros"` |
| `"Sentimento"` | `"Sentimento do comentário do cliente: Positivo, Negativo ou Neutro"` |
**Vocabulário fechado** — Use `values` sempre que houver uma lista finita de categorias. Reduz alucinações e garante consistência no DW.
**Colunas de entrada** — Envie apenas as colunas relevantes. Colunas irrelevantes consomem tokens e podem confundir o modelo.
**Cache** — O cache é automático por `(tenant, modelo, schema, contextHint, registro)`. Para forçar reclassificação, mude o `contextHint` ou o `tier`.
**Quantidade de campos** — Mais de 8 campos por nó aumenta custo e latência. Considere dividir em múltiplos nós.
***
## Limites
| Limite | Valor |
|--------|-------|
| Registros por batch | 1 – 1.000 |
| Campos com `cardinality: explode` por nó | máx. 1 |
| Comprimento mínimo de `description` | 10 caracteres |
| Campos recomendados por nó | até 8 |
***
## Permissões
O nó requer a permissão `use_ai_etl` no usuário que executa o fluxo. A feature pode ser desabilitada por tenant via `allow_etl_ai: false` na configuração do tenant.
---
---
url: 'https://docs.horusbi.com.br/hec/resources/cadastros-cobranca.md'
---
# Cobrança e limites de Cadastros
Os Cadastros são cobrados por **linhas por tenant**, com um **limite bloqueante**: ao atingir o limite, o tenant **não consegue criar nem importar** mais registros até liberar espaço ou contratar linhas extras. Esta página explica como o limite é calculado, como contratar mais linhas e o que a sua fatura mostra.
## 📊 Como o limite funciona
O limite de linhas do tenant é calculado assim:
```
limite = (franquia de linhas por CNPJ × nº de CNPJs) + linhas extras contratadas
```
| Componente | Descrição |
|------------|-----------|
| **Franquia por CNPJ** | Uma cota de linhas incluída para cada CNPJ vinculado ao tenant. Sem contratação específica, vale a **franquia padrão de 100.000 linhas por CNPJ**. |
| **Nº de CNPJs** | Quantidade de CNPJs do tenant. Quanto mais CNPJs, maior a franquia total. |
| **Linhas extras contratadas** | Linhas adicionais que o cliente contrata além da franquia (ver abaixo). |
Pontos importantes:
* **Linhas excluídas não contam.** Ao remover um registro, ele deixa de ocupar o limite.
* **O limite é bloqueante.** Ao atingir o teto, **criar** novos registros e **importar** planilhas ficam bloqueados. Uma importação que ultrapassaria o limite é **bloqueada inteira** — nenhuma linha entra.
> \[!WARNING]
> O limite é por **tenant** — é o **agregado de todas as linhas de todos os Cadastros** do tenant, não um limite por Cadastro. Um Cadastro grande consome a cota dos demais. Acompanhe o total para não ser surpreendido por um bloqueio.
## 🛒 Contratar linhas extras (self-service)
Quando a franquia não é suficiente, o próprio cliente pode contratar linhas adicionais — sem depender da equipe Horus.
> **Caminho**: Recursos > Gerenciar Recursos
Na tela de **Recursos**, você contrata linhas extras de Cadastro, que se somam à franquia no cálculo do limite. A contratação é **self-service** e o novo limite passa a valer para o tenant.
Para ajudar a antecipar a contratação, o sistema dispara **alertas de uso**:
| Alerta | Quando dispara |
|--------|----------------|
| **80%** | O tenant atingiu 80% do limite de linhas — hora de avaliar contratar mais. |
| **100%** | O limite foi atingido — criação e importação ficam bloqueadas até liberar espaço ou contratar extras. |
## 💰 O que entra na cobrança
A cobrança considera apenas o que foi **contratado**:
* **A franquia é grátis.** As linhas incluídas pela franquia por CNPJ não geram custo.
* **Cobra-se só o contratado.** Apenas as **linhas extras** que você contratou na tela de Recursos entram na fatura.
* A fatura mostra o **uso real** (quantas linhas o tenant está consumindo) e o **contratado** (franquia + extras), para você comparar consumo e capacidade.
::: info
Esta página cobre o lado do cliente: como o limite funciona, como contratar extras, os alertas, o bloqueio e o que a fatura mostra. Os valores comerciais (preço por linha, tamanho da franquia) são definidos no seu contrato.
:::
## 🔗 Veja também
* **[Permissões](/hec/resources/cadastros-permissoes)** — quem pode criar e importar (as ações que o limite bloqueia).
* **[Importar e exportar](/dw/tables/cadastros/importar-exportar)** — importações que ultrapassam o limite são bloqueadas inteiras.
* **[Inserir e editar dados](/dw/tables/cadastros/dados)** — cada registro criado conta para o limite; excluir libera espaço.
---
---
url: 'https://docs.horusbi.com.br/ia/chat/como-funciona.md'
---
# Como o Chat Responde
Esta página explica, em alto nível, **o que acontece entre você enviar uma pergunta e receber uma resposta** — o suficiente para entender por que o chat se comporta como se comporta e o que você pode fazer para que ele responda melhor.
***
## O caminho de uma pergunta
```mermaid
flowchart TD
A[Pergunta do usuário] --> B[Identifica a aplicação]
B --> C[Descobre dados e indicadores disponíveis]
C --> D[Resolve nomes mencionados]
D --> E[Executa uma ou mais consultas]
E --> F[Consolida o insight]
F --> G[Resposta final]
```
### 1. Identifica a aplicação
O chat enxerga **todas as aplicações que você tem acesso** e decide qual usar com base **no texto da pergunta**. Se a pergunta menciona claramente uma área ("qual o faturamento de ontem?", "como está o estoque?"), ele identifica a aplicação pelos termos. Se for ambígua, ele pode perguntar de qual aplicação você está falando ou listar as disponíveis.
> \[!TIP]
> Vale **citar a aplicação no início da conversa** quando há risco de ambiguidade — especialmente se conceitos parecidos existem em mais de uma aplicação (ex.: "faturamento" pode estar em "Vendas" e em "Financeiro" com definições diferentes).
### 2. Descobre dados e indicadores disponíveis
O chat **enxerga a estrutura da aplicação** — tabelas, colunas, expressões, dashboards — e **carrega o catálogo de indicadores** da aplicação. É a partir dessa visão que ele decide como responder.
::: info Por que os indicadores importam aqui
Os indicadores entram **nesta etapa**, como ponto de partida curado. Em vez de o chat decidir do zero "qual coluna é faturamento?", ele vê que existe um indicador `Faturamento` definido pela aplicação e usa a definição diretamente. Sem indicadores, ele faz uma escolha razoável examinando colunas e nomes — mas a escolha pode variar entre conversas.
Veja **[Indicadores: Ensinando seu Negócio](./fatos.md)** para entender como configurá-los.
:::
### 3. Resolve nomes mencionados
Se a pergunta tem termos aproximados ("cliente acme", "produto x"), o chat faz uma **busca textual** nos valores das colunas para resolver o nome exato — "cliente acme" pode virar "ACME Indústrias S.A." no banco.
Essa resolução é uma das partes mais valiosas do chat: ela isola você de saber a forma exata como cada valor está cadastrado.
### 4. Executa uma ou mais consultas
Com aplicação identificada, contexto de indicadores carregado e nomes resolvidos, o chat **executa consultas reais** no seu modelo de dados. Os números que entram no insight final são lidos diretamente do banco a cada pergunta.
Uma única pergunta pode envolver várias consultas em sequência. Por exemplo, "compare o faturamento desta semana com a média das últimas 4" envolve duas consultas — uma para o período atual, outra para o histórico — depois um cálculo de comparação.
### 5. Consolida o insight
Por fim, o chat consolida os dados em uma **mensagem em linguagem natural**: número, contexto, comparação, interpretação. É por isso que o chat raramente devolve tabelas brutas — a entrega é o **insight** interpretado.
::: tip Por que vem como insight e não como tabela
Se você quer a tabela bruta, use o **Explorer** da aplicação ou um **Relatório**. O chat é a ferramenta certa quando você quer **a resposta interpretada**, não a planilha pra interpretar.
:::
***
## O chat funciona sem indicadores?
**Sim.** O chat consegue ler a estrutura da aplicação por conta própria e responder mesmo sem nenhum indicador configurado.
O que muda **sem indicadores** é a **consistência**:
* Em modelos reais sempre há ambiguidade — múltiplas colunas que poderiam ser "vendas", múltiplas datas que poderiam ser "tempo".
* Sem indicadores, o chat faz uma escolha razoável em cada conversa. Mas a escolha **não é determinística** — pode acertar hoje e errar amanhã na mesma pergunta, porque mais de um caminho parecia válido.
**Com indicadores**, o chat tem um ponto de partida fixo: para o conceito "Faturamento" nesta aplicação, a definição certa envolve *esta* coluna, *esta* data e *estes* filtros. A ambiguidade desaparece.
***
## Modos: Rápido vs Raciocínio
Acima da caixa de mensagem há um seletor de **modelo**:
| Modo | Quando usar |
|---|---|
| **Rápido** | Perguntas diretas, métricas pontuais, comparações simples. Responde em segundos. |
| **Raciocínio** | Análises mais complexas, investigação de causa, comparações com várias dimensões. Mais lento, mas responde melhor. |
A regra prática: comece no **Rápido**. Se a resposta vier rasa demais para a complexidade da pergunta, repita a pergunta no **Raciocínio**.
O modo de raciocínio consome mais processamento de IA — é mais caro pelo tenant. Use quando o ganho de qualidade compensa.
***
## Memória entre conversas
O chat tem **dois níveis** de memória, e vale entender a diferença.
### Dentro da conversa atual
Mensagens anteriores **da mesma conversa** ficam no contexto. Se você pergunta "qual o faturamento de março?" e depois "e em abril?", o chat entende que "em abril" se refere à mesma métrica — ele lembra do que você perguntou antes.
### Entre conversas diferentes
Conversas diferentes **começam do zero**. Se você abre uma "Nova Conversa", o chat não traz memória da conversa anterior. Para retomar um fio de raciocínio, abra a conversa antiga na lista.
::: tip
Use a lista de **Conversas** para retomar análises em andamento. Cada conversa fica salva — você pode voltar dias depois.
:::
***
## Aprendizado tácito
Existe um mecanismo separado dos indicadores que melhora o chat com o tempo: quando alguém pergunta usando um nome aproximado (ex.: "cliente acme") e o chat resolve para o valor exato no banco ("ACME Indústrias S.A."), **essa associação fica gravada**. A próxima pergunta com "acme" já parte do nome resolvido — não precisa refazer a busca.
Esse aprendizado é silencioso e contínuo. **É diferente dos indicadores**: indicadores são definições explícitas de conceitos; o aprendizado tácito é só uma memória de "qual valor as pessoas chamam por qual apelido".
A consequência prática: quanto mais o chat é usado, melhor ele resolve nomes aproximados em **valores** — mas a definição dos **conceitos** continua dependendo dos indicadores que você configura.
***
## O chat não inventa números
O chat **sempre executa consultas reais** para chegar nos números que reporta. Se ele diz "vendas caíram 12%", esse 12% saiu de uma consulta de fato — não de estimativa.
O que o chat **pode errar** é:
* **Interpretar a pergunta de outro jeito** que você esperava (especialmente sem indicadores, onde a ambiguidade é maior).
* **Escolher uma coluna ou filtro diferente** do que você teria escolhido.
* **Concluir além do que os dados suportam** (ex.: tirar causa-raiz com base em correlação fraca).
A frase que aparece no rodapé do chat — *"IA pode cometer erros. Verifique informações importantes."* — é literal. Trate o chat como assistente: quando uma resposta vai virar decisão importante, confira os números no dashboard ou no Explorer.
***
## Próximos passos
* Entenda como configurar indicadores em **[Indicadores: Ensinando seu Negócio](./fatos.md)**.
* Veja casos práticos lado a lado em **[Exemplos de Indicadores](./exemplos-de-fatos.md)**.
* Para a referência da UI de gerenciamento de indicadores, veja **[Aba Conceitos IA](/dataviz/02-apps/ai-concepts.md)**.
---
---
url: 'https://docs.horusbi.com.br/dataviz/04-features/alerts/webhook/receivers.md'
---
# Como Receber e Testar
Esta página mostra como **receber e processar** o webhook do Lumo em diferentes ferramentas, do mais simples (inspeção manual no navegador) ao mais robusto (servidor próprio em produção).
Use a ordem desta página como um roteiro de adoção:
1. **Inspecionar** o payload manualmente com webhook.site para entender o que chega.
2. **Testar** com curl, simulando o que o Lumo envia.
3. **Automatizar** com uma ferramenta low-code (n8n, Make, Zapier) — cobre a maioria dos casos.
4. **Programar** um receptor próprio em Node.js ou Python para casos complexos.
5. **Hospedar** sem servidor próprio usando Cloudflare Workers ou Vercel Serverless.
***
## webhook.site (inspeção rápida)
A forma mais rápida de ver o payload real chegando, sem escrever uma linha de código.
**Passos:**
1. Acesse . Uma URL única (`https://webhook.site/`) é gerada automaticamente.
2. Copie essa URL.
3. No Lumo, em **HEC → Tenants → \[seu tenant] → Canais de Alerta Permitidos → URL do Webhook**, cole a URL e salve.
4. Em qualquer alerta com canal Webhook ativo, clique em **Forçar envio agora** (o botão "Pré-visualizar" não dispara POST — apenas renderiza na tela).
5. Volte para a aba do webhook.site — o request aparece em tempo real, com headers, corpo JSON e timing.
> \[!TIP]
> O webhook.site é **público**. Se o seu payload tiver dados sensíveis (e em produção sempre tem), use só para testes pontuais com dados sintéticos. Para inspeção em ambiente real, monte um receptor próprio (veja seções abaixo).
***
## curl (simulação local)
Útil para **simular** o que o Lumo envia, sem precisar disparar um alerta real. Copie um payload da [referência](./payload.md) ou do histórico de uma entrega já enviada, salve como `payload.json` e:
```bash
curl -X POST \
-H "Content-Type: application/json" \
-H "User-Agent: Horus-Alert-Webhook/1.0" \
--data @payload.json \
https://seu-endpoint.exemplo.com/webhook
```
Para teste local com seu receptor rodando em `localhost:3000`, exponha com um tunnel:
```bash
# com cloudflared
cloudflared tunnel --url http://localhost:3000
# ou com ngrok
ngrok http 3000
```
A URL pública gerada pode ser cadastrada como URL do Webhook do tenant para testes ponta-a-ponta.
***
## n8n (low-code)
[n8n](https://n8n.io) é a opção mais flexível para integrar o Lumo a sistemas externos sem manter um servidor próprio. Fluxo típico:
```mermaid
flowchart LR
A[Lumo] -->|POST JSON| B[Webhook Trigger]
B --> C{Switch por alert.type ou alert.nome}
C --> D[HTTP Request Meta WhatsApp API]
C --> E[Slack Incoming Webhook]
C --> F[Salvar no Google Sheets]
```
### Passo a passo
**1. Criar o trigger:**
* Adicione o nó **Webhook**.
* Método: `POST`.
* Path: deixe um valor secreto (ex.: `lumo-alerts-7f3a9b2e`). A URL final será `https://seu-n8n.exemplo.com/webhook/lumo-alerts-7f3a9b2e` — esse path secreto serve como [validação de origem](./advanced.md#caminho-secreto-na-url).
* Response Mode: **"Last Node"** (deixa o n8n responder 200 OK após processar) ou **"Immediately"** (responde 200 antes mesmo de processar — recomendado para evitar timeout de 10s).
**2. Cadastrar a URL no Lumo:**
Copie a URL de produção do nó Webhook e cole em **HEC → Tenants → Canais de Alerta Permitidos → URL do Webhook**.
**3. Roteamento:**
Use um nó **Switch** com expressões que olham `$json.alert.nome` ou `$json.alert.id` para encaminhar o payload a diferentes destinos.
**4. Destinos comuns:**
| Destino | Nó n8n | O que mandar |
|---|---|---|
| WhatsApp via Meta | **HTTP Request** | `POST https://graph.facebook.com/v20.0//messages` |
| Slack | **HTTP Request** ou **Slack** | `POST` no Incoming Webhook do canal |
| Microsoft Teams | **HTTP Request** | `POST` no Webhook Connector do canal |
| Planilha de log | **Google Sheets** | Append da linha com `alert.nome`, `timestamp`, `user.email` |
| Banco interno | **Postgres / MySQL** | INSERT na tabela de auditoria |
> \[!TIP]
> No n8n, configure **Response Mode: Immediately** no nó Webhook e processe em background. Isso evita que o Lumo dê timeout (10s) caso a sua automação seja lenta (ex.: Meta API pode ter latência).
***
## Make (ex-Integromat)
Funcionalmente equivalente ao n8n, com uma UI mais visual.
**Passo a passo:**
1. Crie um novo **Scenario** → módulo inicial **Webhooks → Custom webhook**.
2. Clique em **Add**, dê um nome (ex.: "Lumo Alerts"). O Make gera uma URL pública.
3. Cole essa URL como URL do Webhook do tenant no Lumo.
4. Force um envio no Lumo (botão **Forçar envio agora** — "Pré-visualizar" não envia POST) e o Make detecta a estrutura do payload automaticamente.
5. Encadeie módulos: **Router** (para condicionais), **HTTP → Make a request** (para chamar APIs externas), **WhatsApp Business**, **Slack**, etc.
> \[!INFO]
> O Make tem um limite de **40 segundos** para processar um cenário síncrono, mas o Lumo tem timeout de **10 segundos**. Use **Webhook Response** logo após o webhook inicial para responder 200 antes de processar.
***
## Zapier
Mais simples, mas com menos flexibilidade.
**Passo a passo:**
1. Crie um novo **Zap** → trigger **Webhooks by Zapier → Catch Hook**.
2. Copie a URL gerada e cole no Lumo como URL do Webhook do tenant.
3. Force um envio para o Zapier capturar a estrutura do payload.
4. Adicione ações (ex.: **WhatsApp by Zapier**, **Slack**, **Google Sheets**).
> \[!WARNING]
> O Zapier responde 200 de imediato apenas no plano gratuito de **Catch Hook**. Em workflows com filtros e ações pesadas, considere usar **Webhooks by Zapier → Catch Raw Hook** e responder você mesmo.
***
## Node.js + Express
Servidor próprio mínimo para casos em que você precisa de lógica que não cabe em ferramentas no-code.
```typescript
import express from "express";
import type { Request, Response } from "express";
const app = express();
// limite explícito para casar com os 4 MB do Lumo
app.use(express.json({ limit: "4mb" }));
// (Opcional, mas recomendado) caminho secreto como validação de origem.
// Veja: ../advanced.md#caminho-secreto-na-url
app.post("/webhook/lumo/:secret", async (req: Request, res: Response) => {
if (req.params.secret !== process.env.LUMO_WEBHOOK_SECRET) {
return res.status(404).end(); // não revele que o caminho existe
}
// 1. Responde IMEDIATAMENTE para evitar timeout de 10s
res.status(200).json({ received: true });
// 2. Processa em background (não bloqueia a resposta)
try {
await processarAlerta(req.body);
} catch (err) {
console.error("[lumo] falha ao processar alerta", err);
}
});
async function processarAlerta(payload: any) {
const { alert, user, content, timestamp } = payload;
console.log(`[lumo] ${alert.nome} → ${user.email ?? user.id} @ ${timestamp}`);
for (const bloco of content) {
switch (bloco.type) {
case "text":
case "ai":
console.log(`[texto] ${bloco.nome}: ${bloco.generated.text}`);
break;
case "report":
if (bloco.generated.pdf) {
await baixarEArmazenar(bloco.generated.pdf, "pdf");
}
if (bloco.generated.xlsx) {
await baixarEArmazenar(bloco.generated.xlsx, "xlsx");
}
break;
case "chart":
if ("kind" in bloco.generated && bloco.generated.kind === "dashboard") {
if (bloco.generated.url) {
await baixarEArmazenar(bloco.generated.url, bloco.generated.format);
}
} else if ("chart" in bloco.generated) {
await baixarEArmazenar(bloco.generated.chart, "png");
}
break;
}
}
}
async function baixarEArmazenar(url: string, extensao: string) {
// Importante: URLs do Lumo NÃO são permanentes. Baixe e guarde no seu storage.
const resp = await fetch(url);
if (!resp.ok) throw new Error(`download falhou: ${resp.status}`);
const buffer = await resp.arrayBuffer();
// Aqui você grava em S3, disco, banco, etc.
console.log(`baixados ${buffer.byteLength} bytes (.${extensao})`);
}
app.listen(3000, () => console.log("ouvindo em :3000"));
```
> \[!TIP]
> O padrão **"responder 200 imediatamente, processar em background"** é o mais seguro contra o timeout de 10 segundos do Lumo. Use uma fila (BullMQ, RabbitMQ, SQS) se o processamento for pesado.
***
## Python + FastAPI
Equivalente em Python, idiomatic para quem prefere async.
```python
import os
import asyncio
import httpx
from fastapi import FastAPI, Request, BackgroundTasks, HTTPException
app = FastAPI()
LUMO_SECRET = os.getenv("LUMO_WEBHOOK_SECRET", "")
@app.post("/webhook/lumo/{secret}")
async def receber_alerta(
secret: str,
request: Request,
background_tasks: BackgroundTasks,
):
if secret != LUMO_SECRET:
raise HTTPException(status_code=404)
payload = await request.json()
# Agenda processamento em background e responde 200 imediatamente
background_tasks.add_task(processar_alerta, payload)
return {"received": True}
async def processar_alerta(payload: dict) -> None:
alert = payload["alert"]
user = payload.get("user", {})
content = payload.get("content", [])
print(f"[lumo] {alert['nome']} -> {user.get('email', user.get('id'))}")
async with httpx.AsyncClient(timeout=30) as client:
for bloco in content:
tipo = bloco["type"]
gen = bloco.get("generated", {})
if tipo in ("text", "ai") and "text" in gen:
print(f"[texto] {bloco['nome']}: {gen['text']}")
elif tipo == "report":
for ext in ("pdf", "xlsx"):
if ext in gen:
await baixar(client, gen[ext], ext)
elif tipo == "chart":
if gen.get("kind") == "dashboard" and "url" in gen:
await baixar(client, gen["url"], gen.get("format", "bin"))
elif "chart" in gen:
await baixar(client, gen["chart"], "png")
async def baixar(client: httpx.AsyncClient, url: str, ext: str) -> None:
# URLs do Lumo nao sao permanentes. Salve no seu storage agora.
resp = await client.get(url)
resp.raise_for_status()
print(f"baixados {len(resp.content)} bytes (.{ext})")
```
> \[!INFO]
> Em produção, troque `BackgroundTasks` por uma fila persistente (Celery, RQ, Arq) — `BackgroundTasks` perde tarefas se o processo cair.
***
## Cloudflare Workers (serverless)
Bom quando você quer um endpoint baratíssimo, com TLS e escalonamento automático, sem manter VM. Disponível no plano gratuito.
```javascript
// worker.js — deploy via `wrangler deploy`
export default {
async fetch(request, env, ctx) {
if (request.method !== "POST") {
return new Response("Method Not Allowed", { status: 405 });
}
const url = new URL(request.url);
if (url.pathname !== `/webhook/lumo/${env.LUMO_SECRET}`) {
return new Response("Not Found", { status: 404 });
}
const payload = await request.json();
// ctx.waitUntil permite processar em background depois de responder
ctx.waitUntil(processarAlerta(payload, env));
return new Response(JSON.stringify({ received: true }), {
status: 200,
headers: { "content-type": "application/json" },
});
},
};
async function processarAlerta(payload, env) {
const { alert, user } = payload;
console.log(`[lumo] ${alert.nome} → ${user.email ?? user.id}`);
// Encaminhar para outros endpoints (Meta API, Slack, etc.) com fetch().
}
```
`LUMO_SECRET` é configurada como variável de ambiente do Worker. A URL final fica `https://.workers.dev/webhook/lumo/` e é cadastrada como URL do Webhook do tenant.
***
## Vercel Serverless Functions
Padrão equivalente, usando Next.js Route Handlers ou Vercel Functions puras:
```typescript
// app/api/webhook/lumo/[secret]/route.ts
import { NextResponse } from "next/server";
export async function POST(
request: Request,
{ params }: { params: { secret: string } }
) {
if (params.secret !== process.env.LUMO_WEBHOOK_SECRET) {
return NextResponse.json({ error: "not found" }, { status: 404 });
}
const payload = await request.json();
// Vercel Functions têm timeout de execução; despache pra fila externa
// (Upstash QStash, Inngest, ou seu próprio worker) e responda já.
await encaminharParaFila(payload);
return NextResponse.json({ received: true });
}
async function encaminharParaFila(payload: any) {
// Exemplo: POST para Upstash QStash, que processa em background.
}
```
> \[!WARNING]
> Funções serverless têm timeout de execução próprio (60s na Vercel, 10s na Cloudflare, 15min em Lambda). Se você processa de forma síncrona, garanta que o seu processamento + a resposta cabem dentro do **timeout mais apertado**: os 10 segundos do Lumo. O padrão recomendado continua sendo **responder 200 imediato, processar em fila externa**.
***
## Próximos Passos
* [Avançado](./advanced.md) — idempotência, validação de origem, integração WhatsApp via Meta, Slack, troubleshooting.
* [Referência do Payload](./payload.md) — todos os campos, JSON Schema, tipos TypeScript.
* Voltar para a [Visão Geral do Webhook](./index.md).
---
---
url: 'https://docs.horusbi.com.br/dataviz/03-widgets/svelte-parametrico.md'
---
# Componentes Reutilizáveis (Svelte Paramétrico)
Um **Componente Reutilizável** transforma um [Widget Svelte](svelte-custom.md) em uma peça **configurável** que passa a aparecer como um **novo tipo de widget** na biblioteca do Dashboard.
A ideia: quem sabe programar cria o componente **uma vez** (com o código e um conjunto de opções configuráveis); depois, qualquer pessoa que monta dashboards pode **usar e ajustar** esse componente **sem tocar em código** — só preenchendo campos.
> \[!TIP]
> Exemplo: um desenvolvedor cria um "Cartão de Meta" sofisticado e expõe como opções a coluna de valor, a meta e a cor. A partir daí, qualquer usuário adiciona esse cartão em vários dashboards e só escolhe a coluna e a cor — sem ver o código.
***
## 👥 Os dois papéis
A funcionalidade separa claramente **quem cria** de **quem usa**:
| Papel | O que faz |
|-------|-----------|
| **Autor** | Escreve o código, define quais opções ficam configuráveis (o **Contrato**), define a aparência fixa, e **publica** o componente. |
| **Quem monta dashboards** | Escolhe o componente na biblioteca e **preenche os campos** configuráveis. Não vê nem edita o código. |
***
## 🛠️ Criar um componente (Autor)
### 1. Parta de um Widget Svelte
Crie um [Widget Svelte](svelte-custom.md) normalmente. Ele usa o mesmo ambiente (Svelte 4, biblioteca `app` para dados, Tailwind, etc.).
Em vez de fixar a tabela, a coluna ou os textos diretamente no código, **leia tudo das opções configuráveis** — assim o mesmo código serve para muitos casos.
No editor, clique em **"Transformar em paramétrico"** para habilitar o Contrato.
### 2. Defina o Contrato
O **Contrato** é a lista de opções que ficarão disponíveis para quem usar o componente. Cada opção que o seu código lê **precisa estar declarada no Contrato** (e vice-versa).
Para cada campo você define o **rótulo** (nome amigável), a **chave** (como o código o lê) e o **tipo**. Os tipos disponíveis estão detalhados na [referência do Contrato](#-referência-do-contrato-manifest).
> \[!TIP]
> O Contrato pode ser editado de forma **visual** (campo a campo) ou em **JSON** (modo avançado), com validação ao vivo.
Você também pode **agrupar** campos — campos do mesmo grupo aparecem juntos para quem configura.
### 3. Defina a Aparência do widget
Na seção **"Aparência do widget"**, o autor decide opções visuais que valem para **todas as instâncias**:
* **Gráfico com fundo transparente**
* **Crescer com o conteúdo**
> \[!IMPORTANT]
> A aparência é **fixa pelo autor** — quem usa o componente **não pode** alterá-la. Se você criar um cartão com fundo transparente, todas as instâncias serão transparentes.
### 4. Salve como componente
Clique em **"Salvar como componente"**, informe **nome**, **ícone** e **categoria**. O widget que você estava editando passa a ser uma **instância** desse componente, e ele entra na biblioteca.
***
## 🧑💻 Como o código lê o Contrato
O componente recebe duas props injetadas pelo Horus:
* `app` — a ponte com os dados e o motor (a mesma [API do objeto `app`](svelte-custom.md#-3-api-do-objeto-app) do Widget Svelte: `fetchMatrix`, `columnFromString`, `addFilter`, `formatValue`, etc.).
* `config` — um objeto com **um valor por campo do Contrato**, indexado pela **chave** de cada campo.
Exemplo completo. Suponha um Contrato com três campos: `titulo` (Texto), `corPrimaria` (Cor) e `colunaValor` (Coluna):
```svelte
{config.titulo}
{total}
```
> \[!NOTE]
> O `config` chega **sempre completo**: cada campo declarado no Contrato tem um valor — o que o usuário preencheu, o valor padrão do Contrato, ou um padrão seguro por tipo. O código nunca lê `undefined` num campo declarado.
***
## 📐 Referência do Contrato (manifest)
O Contrato é um JSON com um array `fields` e, opcionalmente, um objeto `display` (a aparência fixa):
```json
{
"fields": [
{ "key": "titulo", "type": "text", "label": "Título", "default": "Meu cartão" },
{ "key": "corPrimaria", "type": "color", "label": "Cor principal", "group": "Aparência" },
{ "key": "colunaValor", "type": "column-binding", "label": "Coluna de valor", "filter": "Measure" },
{ "key": "meta", "type": "number", "label": "Meta", "min": 0, "step": 100, "expression": true },
{ "key": "modo", "type": "select", "label": "Modo", "options": [
{ "value": "compacto", "label": "Compacto" },
{ "value": "detalhado", "label": "Detalhado" }
]}
],
"display": { "transparentBackground": false, "autoGrow": false }
}
```
Propriedades comuns a todo campo: `key` (obrigatória e única), `type` (obrigatória), `label` (obrigatória), `default`, `help` (texto de ajuda), `group` (agrupamento visual).
### Tipos de campo
| `type` | Na interface | Para quê | Propriedades específicas |
|--------|--------------|----------|--------------------------|
| `column-binding` | Coluna | O usuário escolhe uma coluna de dados. O valor chega como string de coluna (use `app.columnFromString`). | `multiple` (permite várias; o valor vira array), `filter` (`"Measure"`, `"Dimension"`, `"Date"` ou `"All"`) |
| `text` | Texto | Título, legenda, rótulo curto. | — |
| `textarea` | Área de Texto | Textos longos, descrições. | — |
| `number` | Número | Valor numérico. | `min`, `max`, `step` |
| `range` | Intervalo | Valor numérico escolhido num controle deslizante. | `min`, `max`, `step` |
| `color` | Cor | Seletor de cor (o valor chega como hex, ex.: `#4F46E5`). | — |
| `boolean` | Booleano | Liga/desliga uma opção. | — |
| `select` | Seleção | Escolha entre opções pré-definidas pelo autor. | `options` (array de `{ value, label }`), `forceKind` (`"segmented"` para pílulas ou `"list"` para dropdown; sem ele a aparência é automática pelo número de opções) |
| `date` | Data | Seleção de data. | — |
| `icon` | Ícone | Seletor de ícone (o valor chega como nome iconify, ex.: `fa:star`). | — |
| `number-format` | Formato numérico | O usuário escolhe um formato de número do sistema; combine com `app.formatValue`. | — |
| `svelte_modal` | Editor Svelte (modal) | Abre uma tela de configuração personalizada, feita pelo próprio autor num dos arquivos do componente, para opções complexas. | `editorComponent` (arquivo .svelte do editor), `buttonLabel` (texto do botão que abre o modal) |
### Valores padrão
Quando o usuário não preenche um campo, o componente recebe o `default` declarado no Contrato. Sem `default`, entra um **padrão seguro por tipo**: `0` (ou o `min`) para número e intervalo, `false` para booleano, `#000000` para cor, a primeira opção para seleção, `[]` ou `""` para coluna (conforme `multiple`), `{}` para editor modal e `""` para os demais.
### Fórmulas por campo (`expression`)
Campos de cor, número, texto, área de texto, intervalo e formato numérico aceitam `"expression": true`. Com isso, quem configura pode alternar o campo para o modo **fórmula** e escrever uma expressão JavaScript avaliada em tempo de render (útil para cor condicional, metas dinâmicas, etc.). Se a expressão falhar, vale o valor fixo do campo.
***
## 🧪 Testar antes de publicar
Enquanto o editor do componente está aberto, **o widget de onde você o abriu** reflete as mudanças de código e de Contrato na hora, sem salvar nada. Esse widget mostra o selo **"Prévia da edição"**.
> \[!NOTE]
> A prévia vale **apenas para o widget que está sendo editado**. As outras instâncias do mesmo componente (no mesmo dashboard ou em outros) continuam mostrando a versão do canal delas. Ao **fechar o editor**, a prévia termina e o widget volta a mostrar o que está salvo.
Para testar um rascunho **salvo** num widget específico, use o canal da instância (abaixo).
### Canal por instância
Cada instância do componente aponta para um canal, visível no configurador do widget (apenas para quem pode editar o componente):
* **Publicado (no ar)** — a versão estável.
* **Rascunho (teste)** — o rascunho atual do componente. Útil para validar uma mudança num dashboard real antes de publicar.
No modo de edição do dashboard, instâncias em rascunho exibem o selo **"Rascunho"**.
> \[!WARNING]
> **Publicar retorna todas as instâncias ao canal Publicado.** O canal Rascunho de uma instância é um estado temporário de teste, e o rascunho de um componente é **um só** — instâncias em rascunho, em qualquer dashboard, mostram esse mesmo rascunho.
***
## 🚀 Publicar, desfazer e descartar
Um componente tem dois canais permanentes, **Rascunho** e **Publicado**, e três ações sobre eles:
| Ação | O que faz | O que acontece com o rascunho |
|------|-----------|-------------------------------|
| **Publicar** | Copia o rascunho para o canal Publicado, **em todas as dashboards** onde o componente está instalado. | Continua igual ao publicado (sem pendências). |
| **Desfazer publicação** | O canal Publicado volta à versão publicada **anterior** (um nível de desfazer), em todas as dashboards. | **Não muda.** Suas edições continuam no rascunho. |
| **Descartar rascunho** | O rascunho volta a ser uma **cópia do publicado**. Use quando quiser abandonar as mudanças em andamento. | É sobrescrito pelo conteúdo publicado. |
> \[!WARNING]
> Mudanças de código ou de Contrato **só chegam a quem usa depois de Publicar**. Antes disso, elas existem apenas no rascunho.
O indicador **"Afeta N dashboards"** mostra **onde** o componente está instalado (espaço, aplicação, dashboard e canal) — útil para saber o impacto antes de publicar.
> \[!NOTE]
> Um componente recém-criado, ainda **sem versão publicada**, já renderiza o rascunho onde foi instanciado — justamente para você conseguir testá-lo. Ele só aparece na biblioteca para **outras** pessoas adicionarem depois de publicado.
***
## 🗂️ Gerenciando a biblioteca (HEC)
Além das ações no editor do Dashboard, o console administrativo (HEC) tem a tela **Biblioteca de Widgets** (menu Recursos e Permissões), pensada para curadoria:
* **Status por componente** — se há versão publicada e se existem **mudanças não publicadas** no rascunho.
* **Publicar / Desfazer publicação / Descartar rascunho** — as mesmas ações do editor, com confirmação. Os botões só habilitam quando a ação faz sentido (por exemplo, Publicar só com mudanças pendentes) e só aparecem para quem pode editar o componente.
* **Onde está** — a matriz de instalação: espaço, aplicação, dashboard e canal de cada instância.
* **Busca** por nome ou categoria.
***
## 🧩 Usar um componente (quem monta dashboards)
1. Na biblioteca de widgets do Dashboard, escolha o componente (aparecem apenas os **publicados** disponíveis para você).
2. Adicione ao dashboard como qualquer outro widget.
3. Na aba **"Configurar"**, preencha os campos do Contrato (vincular colunas, definir textos, cores, etc.).
A **aparência** é a definida pelo autor. Você **não** edita o código nem escolhe o canal — apenas configura os valores.
***
## 🔐 Quem pode fazer o quê
| Ação | Quem pode |
|------|-----------|
| Criar, editar (código/Contrato/aparência), publicar, desfazer publicação, descartar rascunho; escolher canal da instância | **Administradores** (do cliente, ou do espaço quando não há cliente) e o suporte Horus |
| Adicionar o componente e **configurar os campos** | Qualquer usuário com permissão de **editar dashboards** |
Para quem só configura, as ações de edição e de canal **nem aparecem** — apenas os campos configuráveis.
***
## 🌐 Onde o componente fica disponível (escopo)
* **Componente do cliente** — compartilhado entre todos os **espaços** daquele cliente. Criar/editar/publicar exige um administrador do cliente (ou suporte Horus).
* **Componente do espaço** — privado a um único espaço (usado quando o espaço não pertence a um cliente). Criar/editar/publicar exige um administrador daquele espaço.
Cada usuário vê na biblioteca os componentes do **seu** cliente mais os do **seu** espaço.
***
## 🤖 Geração por Inteligência Artificial
No painel de IA do editor é possível **gerar o componente por linguagem natural**: descreva o que quer e a IA produz o código **e** o Contrato (os campos configuráveis) já acoplados. Você revisa, ajusta e publica.
***
## 📌 Regras e detalhes importantes
* **Svelte 4** — mesmo ambiente do [Widget Svelte](svelte-custom.md); recursos do Svelte 5 não são suportados.
* **Cada opção lida pelo código precisa estar no Contrato** — e cada campo do Contrato deve ser usado pelo código. A ferramenta avisa quando há divergência.
* **A aparência é decisão do autor**, fixa para todas as instâncias.
* **Valores padrão** — se um campo não for preenchido, o componente usa o padrão definido no Contrato (ou um padrão seguro por tipo), nunca um valor "vazio" inesperado.
* **Publicar é o que propaga** mudanças de código/Contrato para os dashboards — e retorna todas as instâncias ao canal Publicado.
* **Fechar o editor sem salvar** pede confirmação quando há mudanças pendentes; a prévia da edição termina junto.
---
---
url: 'https://docs.horusbi.com.br/etl/architecture/communication.md'
---
# Comunicação e Redes
O Agente do HorusETL foi projetado para ser **Firewall Friendly** — ele opera sem a necessidade de configurações complexas de rede ou abertura de portas de entrada (Inbound) na sua infraestrutura.
***
## 📡 Protocolo
Toda a comunicação entre o Agente (sua rede) e o Backend (Nuvem) é feita via **WebSocket Seguro (WSS)**.
Isso garante:
1. **Criptografia** — Todos os dados trafegados são criptografados (SSL/TLS)
2. **Tempo Real** — O servidor pode enviar comandos instantâneos para o agente (start, stop, update) sem polling excessivo
3. **Eficiência** — Uma única conexão persistente é mantida
***
## 🔌 Portas e Direção
A regra de ouro é: **O Agente sempre inicia a conexão.**
| Parâmetro | Valor |
|-----------|-------|
| **Origem** | Sua infraestrutura (Agente) |
| **Destino** | `backend.horusbi.com.br` (Nuvem) |
| **Porta** | `443` (HTTPS) |
| **Direção** | Outbound (Saída) |
> \[!TIP]
> **Para a equipe de Infra/Segurança**: Não é necessário abrir nenhuma porta de **entrada** (Inbound) no firewall. Se o servidor onde o agente está instalado consegue acessar a internet via porta 443, o agente funcionará corretamente.
### Fluxo de Conexão
1. Ao iniciar, o Agente estabelece uma conexão com a nuvem na porta 443
2. O handshake de segurança é feito e a conexão WebSocket é estabelecida
3. O Agente fica aguardando tarefas através deste túnel já aberto
4. Quando você clica em "Executar" na web, o comando desce por esse túnel
***
## ⚡ Latência e Performance
Como a conexão é persistente, a latência de comando é mínima (milissegundos). Isso permite que você veja os logs de execução na tela praticamente no mesmo instante em que eles são gerados no servidor local.
---
---
url: 'https://docs.horusbi.com.br/etl/processors/transforms/union.md'
---
# Concatenar (Union)
O nó **Concatenar** (também conhecido como Union) une verticalmente dois fluxos de dados, empilhando as linhas de um sobre o outro.
## Funcionalidades
* **União por Nome**: O nó alinha automaticamente as colunas que possuem o mesmo nome em ambos os inputs
* **Schema Dinâmico**:
* Se uma coluna existe nos dois inputs, ela é preservada
* Se uma coluna existe apenas no input A, as linhas vindas do input B terão valor `null` (ou `0` para números) nessa coluna
* O resultado final conterá a união de todas as colunas de ambos os fluxos
## Requisitos
* Necessita de exatamente **duas entradas** conectadas
* Para unir mais de dois fluxos, encadeie múltiplos nós de Union (`Union(A, Union(B, C))`)
## Performance
A operação é feita via streaming (iterador), o que significa que o consumo de memória é baixo, pois os dados não são duplicados em memória; eles apenas passam pelo nó.
---
---
url: 'https://docs.horusbi.com.br/lumo/conceitos.md'
---
# Conceitos
O Lumo CLI não inventa conceitos próprios — ele opera os **mesmos recursos** do HorusBI que você já vê na interface, só que como **arquivos YAML** num diretório local. Esta página conecta cada tipo de recurso do Lumo ao conceito da plataforma e aponta onde se aprofundar.
> \[!TIP]
> Se você é novo no HorusBI, leia esta página como mapa. Cada linha liga uma pasta do seu workspace a um conceito — e o link leva à explicação completa daquele conceito.
## O modelo mental
Um **tenant** é o seu ambiente HorusBI. O `lumo init ` cria um **workspace**: um diretório local que **espelha** esse tenant em YAML. Você edita os arquivos, e `push`/`pull` sincronizam com o servidor.
Cada pasta do workspace corresponde a um tipo de recurso da plataforma:
| Pasta | Recurso | O que é | Aprofunde em |
|---|---|---|---|
| `flows/` | **Flow** | Pipeline de ETL: extrai, transforma e carrega dados | [Introdução ao HorusETL](/etl/intro/) |
| `tables/` | **Tabela DW** | Tabela do Data Warehouse (fato ou dimensão) | [Introdução ao HorusDW](/dw/intro/) · [Arquitetura do DW](/dw/architecture/) |
| `apps/` | **App** | Aplicação de BI / dashboard com widgets | [Introdução ao DataViz](/dataviz/00-intro/) |
| `credentials/` | **Credencial** | Conexão com um banco ou fonte de dados | [Conexões de Banco](/etl/guides/conexoes-banco) |
| `schedules/` | **Agendamento** | Execução automática e recorrente de um flow | [Execução e Agendamentos](/etl/guides/execucao-agendamentos) |
| `agents/` | **Agente** | Worker que executa os flows | [Agentes de Execução](/etl/guides/agentes) |
| `dw-desks/` | **Mesa de dados** | Agrupa flows + tabelas publicados | [Mesas e Publicação](/etl/guides/mesas-publicacao) |
| `bi-desks/` | **Mesa de aplicação** | Agrupa apps publicados | [Mesas e Publicação](/etl/guides/mesas-publicacao) |
| `variables/` | **Variável** | Valor reutilizável no tenant | [Variáveis Globais](/etl/guides/variaveis-globais) |
## Conceitos que aparecem nos comandos
Alguns termos surgem direto na [Referência de Comandos](/lumo/comandos/). Aqui está o que significam e onde estudá-los a fundo:
* **Tipo de carga** (`--context Total | Incremental | Temporal` em `lumo flow run`) — define como o flow grava no DW: substituindo tudo, acrescentando o novo, ou por janela temporal. Veja o processador [Inserir no Datawarehouse](/etl/processors/outputs/datawarehouse).
* **Star schema / Relacionamentos** — tabelas Fato se conectam a Dimensões (1:N); conectar dois Fatos direto causa explosão cartesiana. Essencial para modelar apps. Veja [Modelagem de Dados](/dataviz/02-apps/data-modeling).
* **Rascunho → Publicado** — você constrói e itera um flow/app como **rascunho** (editável); ao publicar, ele vai para uma **mesa**, fica somente-leitura e é compartilhado. Por isso editar um recurso publicado exige `clone` antes. Veja [Mesas e Publicação](/etl/guides/mesas-publicacao).
* **Mesa de dados vs Mesa de aplicação** (`dw-desk` vs `bi-desk`) — flows e tabelas publicam em **mesas de dados**; apps publicam em **mesas de aplicação**. Veja [Mesas](/hec/desks/mesas).
## Próximo passo
Com o mapa em mente, siga para os [Primeiros Passos](/lumo/getting-started/) e crie seu primeiro workspace.
---
---
url: 'https://docs.horusbi.com.br/ia/mcp.md'
---
# Conectar seu Assistente de IA (MCP)
O **MCP (Model Context Protocol)** é um protocolo aberto que permite conectar assistentes de IA externos — como Claude, Claude Code e Cursor — diretamente aos dados das suas Aplicações no Lumo. Você conecta uma vez e passa a perguntar sobre os seus indicadores sem sair da ferramenta que já usa no dia a dia.
Há dois jeitos de conectar, e você não precisa escolher: **cole o endereço e autorize com um clique** (no Claude para aplicativo e site), ou **gere um token pessoal** (no Claude Code e no Cursor, que pedem token). Os dois dão exatamente o mesmo acesso.
O acesso fica no card **Use no MCP**, dentro do módulo **[Agentes de IA](/ia/agentes)**.
::: tip Recurso em beta
Disponível mediante habilitação pelo administrador do tenant.
:::
```mermaid
flowchart LR
A[Copiar o endereço em Agentes → Use no MCP] --> B[Colar no seu assistente e autorizar]
B --> C[Perguntar em linguagem natural]
C --> D[Assistente consulta o Lumo com as suas permissões]
D --> E[Resposta + link de relatório auditável]
```
***
## O que o assistente enxerga
O assistente conectado vê exatamente o que você vê — nada mais:
* Só as **Aplicações que você tem acesso**.
* Só as **colunas liberadas para IA** em cada Aplicação.
Toda consulta que ele executa retorna um **permalink**: um link para o relatório correspondente no Lumo. É a mesma consulta, aberta como relatório de verdade — dá pra conferir os números, não só confiar na resposta em texto do assistente.
## O que o assistente consegue fazer
O acesso é **somente leitura** — o assistente nunca grava, edita ou apaga nada nos seus dados. Com o MCP conectado, ele pode:
* listar as Aplicações que você tem acesso;
* consultar os **indicadores curados** (fatos) de cada Aplicação;
* explorar colunas e valores — por exemplo, resolver "cliente acme" para o nome exato cadastrado;
* executar consultas agregadas, com filtros.
> \[!NOTE]
> Existem limites de uso por minuto (por usuário e por tenant). Se o seu assistente parar de responder consultas com um erro de limite, espere um minuto e tente de novo.
::: tip O assistente não inventa números
Assim como o [Chat com Dados](/ia/chat/), o MCP sempre executa uma consulta real para responder — os números que o assistente relata vêm do banco, não de estimativa. O que o assistente pode errar é a interpretação da pergunta, não o dado em si. Na dúvida, abra o permalink do relatório.
:::
***
## Agentes prontos
Os **Agentes de IA** que você configura no Lumo aparecem no seu assistente externo como **prompts prontos** (o que Claude, Cursor e afins chamam de "skills" ou "prompts"). Cada agente carrega a persona que você definiu — o "como investigar" e o "como responder" — e o assistente passa a usar essa orientação junto das ferramentas somente-leitura.
Na prática: no seu assistente, escolha o prompt com o nome do agente e faça a pergunta. Ele responde com a persona daquele agente, consultando os seus dados. Há sempre um prompt padrão (**"data analyst"**) disponível, mesmo que você ainda não tenha criado nenhum agente.
> \[!NOTE]
> Você só vê os agentes que já veria logado no Lumo — os que abrangem Aplicações a que você tem acesso.
***
## Como ativar
1. Acesse **Agentes de IA** no menu lateral.
2. Clique no card **"Use no MCP"** — ou, dentro do chat, no atalho de plugue no cabeçalho da conversa.
3. Na página que abre estão o **endereço do servidor** (para conectar com 1 clique) e o botão para **gerar um token** (para os assistentes que pedem token).
A página mostra o **endereço do servidor MCP** — algo como `https://dataviz.horusbi.com.br/mcp`. É esse endereço que você cola no assistente.
> \[!TIP]
> Em ambientes whitelabel o endereço usa o domínio da sua empresa. Sempre copie o valor exibido na sua tela, não o exemplo desta página.
***
## Conectar com 1 clique (OAuth)
No **Claude para aplicativo e site**, não é preciso gerar nem colar token: você cola o endereço e autoriza no navegador, como faz ao entrar com Google num site qualquer.
1. No Claude, vá em **Settings → Connectors → Add custom connector**.
2. Cole o **endereço do servidor MCP** copiado da página **Use no MCP**.
3. O Claude abre a tela de autorização **no seu próprio domínio**, com a identidade visual da sua empresa.
4. Se você ainda não estiver logado, faça login normalmente. Depois **escolha o ambiente** que quer autorizar — se você tem acesso a mais de um, todos aparecem na lista, com o da sua sessão já selecionado — e clique em **Autorizar**.
Pronto. O Claude volta conectado, e o acesso é **somente leitura**.
Alguns pontos que valem saber:
* A autorização vale **para um ambiente por vez**. Para conectar outro, repita o processo — o Claude trata cada um como um conector separado.
* A conexão **se renova sozinha**: não há token para expirar na sua mão.
* Você pode **desconectar** a qualquer momento pelo próprio Claude.
* A autorização é **pessoal** e herda exatamente as suas permissões — o assistente não passa a enxergar nada que você já não enxergasse logado no Lumo.
***
## Gerando e gerenciando seu token
O **Claude Code** e o **Cursor** pedem um token em vez de abrir o navegador — é para eles que esta seção serve. Quem usa o Claude para aplicativo ou site pode pular direto para o passo anterior.
O token é **pessoal**: vale para o usuário que o criou e herda exatamente as permissões dele (as mesmas Aplicações e colunas que você já enxerga no Lumo).
* Dê um **nome livre** que ajude a identificar onde ele foi usado (ex.: "Claude Desktop", "Meu Cursor").
* O token aparece **uma única vez**, no momento em que é gerado — copie antes de sair da tela, porque ele não é mostrado de novo.
* É possível **revogar** a qualquer momento, na mesma página. A revogação corta o acesso na hora.
***
## Conectando seu assistente
Há dois jeitos de conectar, e cada assistente usa um deles. Comece pela tabela para achar o seu:
| Assistente | 1 clique | Token |
|---|:---:|:---:|
| Claude (aplicativo e site) | ✅ | — |
| ChatGPT | ✅ | — |
| Cursor | ✅ | ✅ |
| VS Code (Copilot) | ✅ | ✅ |
| Zed | ✅ | ✅ |
| Claude Code | ✅ | ✅ |
| Windsurf | | ✅ |
| Cline | | ✅ |
**Onde os dois aparecem, prefira o 1 clique** — não há token para guardar nem renovar.
### Com 1 clique (Claude, ChatGPT e outros)
O caminho sem token: cole o endereço do servidor no assistente e autorize no navegador. É como funciona no **Claude (aplicativo e site)**, no **ChatGPT** e, se preferir, também no Cursor, VS Code e Zed. Ver **[Conectar com 1 clique](#conectar-com-1-clique-oauth)** acima.
**ChatGPT** tem três detalhes que valem saber antes:
* É preciso ligar o **Modo desenvolvedor** nas configurações do ChatGPT. Em contas de equipe, um administrador precisa habilitar isso antes.
* **Não aparece no plano gratuito.** Nos planos pagos, o assistente consulta seus dados — que é exatamente o que este conector faz, já que ele é **somente leitura**.
* Fora isso, o passo é o mesmo: informar o endereço do servidor e autorizar.
### Com token
Os snippets abaixo usam `` e `` como placeholders — substitua pelos valores exibidos na página **Use no MCP**. **Cada assistente espera uma estrutura ligeiramente diferente**; copie a do seu.
**Claude Code**
```bash
claude mcp add --transport http lumo --header "Authorization: Bearer "
```
**Cursor**
```json
{
"mcpServers": {
"lumo": {
"url": "",
"headers": { "Authorization": "Bearer " }
}
}
}
```
**Windsurf** — o campo é `serverUrl`, não `url`:
```json
{
"mcpServers": {
"lumo": {
"serverUrl": "",
"headers": { "Authorization": "Bearer " }
}
}
}
```
**Zed** — vai em `context_servers`:
```json
{
"context_servers": {
"lumo": {
"url": "",
"headers": { "Authorization": "Bearer " }
}
}
}
```
**Cline** — o `type` precisa ser exatamente `streamableHttp`:
```json
{
"mcpServers": {
"lumo": {
"type": "streamableHttp",
"url": "",
"headers": { "Authorization": "Bearer " }
}
}
}
```
***
## Como melhorar as respostas do assistente
O MCP usa o mesmo catálogo de **fatos** do Chat com Dados para responder. Quanto melhor curados os indicadores e as descrições de cada Aplicação, melhores as respostas — para o assistente externo tanto quanto para o chat interno do Lumo.
::: tip Comece pelos fatos
Se o assistente estiver escolhendo a coluna errada ou dando respostas inconsistentes entre perguntas parecidas, veja **[Fatos: Ensinando seu Negócio](/ia/chat/fatos)** — é a mesma configuração que deixa o Chat com Dados mais consistente.
:::
***
## Perguntas frequentes
**A conexão dá acesso a tudo do tenant?**
Não — e isso vale tanto para o token quanto para o 1 clique. A conexão é pessoal e carrega as permissões de quem conectou: as mesmas Aplicações e colunas que essa pessoa já vê logada no Lumo. Em ambos os casos o acesso é somente leitura.
**Preciso gerar token se conectei com 1 clique?**
Não. São caminhos alternativos para a mesma coisa. O token existe para os assistentes que não abrem o navegador para autorizar (como Claude Code, Windsurf e Cline) ou para quem prefere configurá-lo à mão. O Claude para aplicativo e site e o ChatGPT só usam o 1 clique.
**Tenho acesso a mais de um ambiente. Dá para conectar todos?**
Dá, um de cada vez: cada autorização vale para o ambiente escolhido na tela de consentimento. Repita o processo para conectar outro — o assistente trata cada um como um conector separado.
**Revoguei um token por engano, e agora?**
Gere um novo na mesma página e atualize a configuração do seu assistente com o token novo.
**Por que uma consulta veio com poucas linhas, se eu esperava milhares?**
Consultas via MCP têm um limite de linhas por resposta — é assim por design, para caber no contexto do assistente. Para ver o resultado completo, abra o **permalink do relatório** que vem junto da resposta.
**Isso substitui a API REST de chat?**
São coisas diferentes. O MCP conecta um assistente de terceiros (Claude, Cursor) com o seu próprio token pessoal. Para integrar **seu próprio sistema** ao motor de IA do Lumo — sem passar por um assistente externo — use a **[API REST de Chat IA](/api/ai-chat)**, autenticada por token de tenant.
***
## Próximos passos
* **[Agentes de IA](/ia/agentes)** — o módulo onde o card "Use no MCP" vive, e como criar personas de investigação reutilizáveis.
* **[Fatos: Ensinando seu Negócio](/ia/chat/fatos)** — a peça central para respostas consistentes, seja no chat ou via MCP.
* **[Chat com Dados](/ia/chat/)** — o assistente de IA embutido no Lumo, mesma base de indicadores.
* **[API REST de Chat IA](/api/ai-chat)** — para integrar seu próprio sistema (não um assistente de terceiros) via token de tenant.
---
---
url: 'https://docs.horusbi.com.br/etl/guides/conexoes-banco.md'
---
# Conexões de Banco de Dados
Para que o HorusETL possa ler ou escrever dados em bancos externos, ele precisa de credenciais de acesso. Essas credenciais são gerenciadas de forma centralizada e segura no menu **DbConf** (Configurações de Banco).
***
## 🔐 Segurança e Privacidade
As conexões cadastradas no Horus são encriptadas.
> \[!IMPORTANT]
> **Privacidade**: Por padrão, quando você cria uma conexão, **apenas você** pode vê-la e usá-la nos seus fluxos. Isso permite que cada desenvolvedor tenha suas próprias credenciais de teste sem interferir nos outros.
* **Compartilhamento** — Para tornar uma conexão pública (disponível para outros desenvolvedores da equipe), um administrador deve acessar o painel administrativo (HEC) e alterar as permissões de visibilidade da credencial
***
## ➕ Criando uma Nova Conexão
1. Acesse o menu **DbConf**
2. Clique em **Criar nova Conexão**
3. Escolha o **Agente** que usará esta conexão (as conexões são vinculadas a agentes porque é o agente, na sua rede, que precisa alcançar o banco de dados)
4. Escolha o **Driver** (Tipo de Banco):
| Driver | Banco |
|--------|-------|
| **MySQL / MariaDB** | MySQL 5.x, 8.x e MariaDB |
| **SQL Server** | Microsoft SQL Server |
| **Oracle** | Oracle Database |
| **PostgreSQL** | PostgreSQL |
| **Firebird** | Firebird SQL |
| **ODBC** | Bancos legados ou drivers específicos do sistema |
| **InterSystems IRIS** | InterSystems IRIS (antigo Caché) |
5. Preencha os dados de conexão (Host, Porta, Usuário, Senha, Nome do Banco)
6. Use o botão **Testar Conexão** para validar se o agente consegue alcançar o banco
7. Clique em **Salvar**
***
## 🔗 Uso nos Processadores
Uma vez criada, a conexão aparecerá nos nós de banco de dados dentro do editor de fluxo. Basta selecionar a conexão na lista suspensa — não é necessário digitar senhas ou hosts dentro do fluxo.
---
---
url: 'https://docs.horusbi.com.br/dw/getting-started/configuration.md'
---
# Configurando a Tabela
Após carregar seus dados, é fundamental configurar os metadados técnicos e de negócio para garantir que a tabela seja útil e performática. Toda a configuração é feita na tela de **Edição de Tabela**.
***
## 🔑 Metadados e Chaves
A primeira aba da edição foca na estrutura da tabela.
### Tipos de Dados
Revise os tipos de dados (`Texto`, `Número`, `Data`) de cada coluna para garantir que estejam corretos.
> \[!TIP]
> Colunas de códigos (ID, CNPJ, CPF) devem ser preferencialmente `Texto` ou configuradas com o comportamento `Agrupar` para preservar a formatação e evitar operações matemáticas indesejadas (como soma de CPFs).
### Chaves (Primary Key)
Definir a chave da tabela é crucial para a performance e integridade dos dados:
| Tipo de Chave | Comportamento | Uso Recomendado |
|---------------|---------------|-----------------|
| **Chave Duplicada (Duplicate Key)** | É o padrão. Permite que existam linhas idênticas ou com a mesma chave | Tabelas de fatos (transações, logs) |
| **Chave Única (Unique Key)** | Garante que não haja duplicidade na coluna chave. Linhas com o mesmo ID são **substituídas** (Update) | Tabelas de dimensões (Clientes, Produtos) |
> \[!WARNING]
> Alterar a chave ou tipo de dados de uma tabela já existente exigirá que ela seja **Reconstruída**, o que vai necessitar de uma nova carga de dados.
***
## 📊 Comportamento das Colunas
Configure como o módulo de **DataViz** deve interpretar cada coluna por padrão, facilitando a criação de gráficos pelos usuários finais.
### Comportamento (Agregação)
Para colunas numéricas, defina a agregação padrão:
| Agregação | Uso Recomendado |
|-----------|-----------------|
| **Soma (Sum)** | Valores monetários, quantidades — ao arrastar para um gráfico, o sistema já aplicará a soma |
| **Média (Avg)** | Taxas, notas, indicadores percentuais |
| **Nenhum (None)** | Campos numéricos que **não são medidas**, mas identificadores (`Ano`, `Mês`, `ID Loja`) — orienta o DataViz a tratar como **Dimensão** (Eixo) |
### Máscaras e Formatação
Aplique máscaras visuais aos dados para melhorar a apresentação:
| Máscara | Exemplo |
|---------|---------|
| **Moeda** | `R$ 1.000,00` |
| **Porcentagem** | `10,5%` |
| **Data/Hora** | `DD/MM/AAAA` |
***
## 🧮 Fórmulas (Colunas Calculadas)
Crie novas colunas baseadas em lógica de negócio usando a aba **Expressões**. O HorusDW utiliza a sintaxe do banco de dados **Apache Doris**.
### Adicionando uma Expressão
1. Vá até a aba **Expressões**
2. Clique em **Nova Expressão**
3. Defina um **Nome** funcional ("Ticket Médio")
4. Selecione o **Tipo de Dado** e a Máscara
5. Escreva a fórmula no editor
### Exemplos Comuns
#### Ticket Médio (Divisão)
```sql
sum(VALOR_VENDA) / count(distinct ID_PEDIDO)
```
#### Classificação de Texto (Case When)
Útil para criar agrupamentos de negócio:
```sql
CASE
WHEN valor_total > 1000 THEN 'Alto Valor'
WHEN valor_total > 500 THEN 'Médio Valor'
ELSE 'Baixo Valor'
END
```
#### Distinção PF/PJ (Tamanho da String)
```sql
CASE
WHEN length(cpf_cnpj_limpo) > 11 THEN 'Pessoa Jurídica'
ELSE 'Pessoa Física'
END
```
#### Extração de Data
```sql
year(data_venda) -- Retorna o Ano (Inteiro)
month(data_venda) -- Retorna o Mês (Inteiro)
```
> \[!TIP]
> Use o autocompletar do editor para ver as colunas disponíveis. As fórmulas são processadas em tempo de consulta, garantindo que os dados estejam sempre atualizados.
---
---
url: 'https://docs.horusbi.com.br/etl/processors/inputs/firebird.md'
---
# Consulta Firebird
O nó **Consulta Firebird** permite executar uma query SQL em bancos de dados Firebird SQL (arquivos `.fdb`) e trazer o resultado para o Dataflow.
***
## ⚙️ Parâmetros de Configuração
### Conexão
* **Descrição** — A string de conexão ou a credencial cadastrada
* **Tipo** — String / Seleção
* **Valor**:
* Geralmente aponta para o caminho do arquivo `.fdb` no servidor ou um endereço IP/Porta
* Aceita Variáveis Globais
### Consulta SQL
* **Descrição** — O comando SQL que será executado
* **Tipo** — Texto (Editor SQL)
* **Exemplo**:
```sql
SELECT ID, NOME, SALDO
FROM CLIENTES
WHERE ATIVO = 1
```
***
## 🔧 Detalhes Técnicos
* **Charset** — É fundamental que a connection string especifique o `Charset` correto (`UTF8` ou `WIN1252`) para que a acentuação funcione corretamente
* **Case Sensitivity** — O Firebird pode ser sensível a maiúsculas/minúsculas dependendo de como a tabela foi criada. Se usou aspas na criação (`"Tabela"`), deve usar aspas na consulta
* **Driver** — Utiliza `FirebirdSql.Data.FirebirdClient`
***
## 📝 Exemplo de Uso
1. Arraste o nó **Consulta Firebird**
2. Configure a conexão apontando para o arquivo do banco legado
3. Escreva a query de extração
4. Conecte ao restante do fluxo ETL
---
---
url: 'https://docs.horusbi.com.br/etl/processors/inputs/intersystems-iris.md'
---
# Consulta InterSystems IRIS
O nó **Consulta InterSystems IRIS** permite executar uma query SQL na plataforma de dados InterSystems IRIS (antigo Caché/Ensemble) e trazer o resultado para o Dataflow.
***
## ⚙️ Parâmetros de Configuração
### Conexão
* **Descrição** — A string de conexão ou a credencial cadastrada
* **Tipo** — String / Seleção
* **Valor**:
* Suporta conexão via driver nativo ou ODBC tunelado
* Aceita Variáveis Globais
### Consulta SQL
* **Descrição** — O comando SQL que será executado
* **Tipo** — Texto (Editor SQL)
* **Exemplo**:
```sql
SELECT ID, PatientName, DOB
FROM User.Patients
WHERE AdmissionDate >= '2024-01-01'
```
***
## 🔧 Detalhes Técnicos
* **Saúde** — Este conector é otimizado para extração de grandes volumes de dados de prontuários eletrônicos (EHR) que rodam sobre tecnologia InterSystems (Tasy, MV)
* **Performance** — Utiliza acesso direto via ADO.NET quando possível para maximizar a velocidade de extração
***
## 📝 Exemplo de Uso
1. Arraste o nó **Consulta InterSystems IRIS**
2. Configure a conexão com o servidor de banco de dados do hospital
3. Insira a query SQL para extrair a lista de atendimentos
4. Conecte a um processador para anonimizar dados sensíveis (LGPD) antes de salvar
---
---
url: 'https://docs.horusbi.com.br/etl/processors/inputs/mysql.md'
---
# Consulta MySQL
O nó **Consulta MySQL** permite executar uma query SQL em um banco de dados MySQL ou MariaDB e trazer o resultado para o Dataflow.
***
## ⚙️ Parâmetros de Configuração
### Conexão
* **Descrição** — A string de conexão ou o nome da credencial cadastrada
* **Tipo** — String / Seleção
* **Valor**:
* Pode ser uma credencial salva no sistema (selecionável no dropdown)
* Pode ser uma **Variável Global** no formato `NOME_DA_VARIAVEL` — a variável deve conter a connection string completa
### Consulta SQL
* **Descrição** — O comando SQL `SELECT` que será executado no banco
* **Tipo** — Texto (Editor SQL)
* **Exemplo**:
```sql
SELECT id, nome, email
FROM usuarios
WHERE created_at > '2024-01-01'
```
***
## 🔧 Detalhes Técnicos
* **Streaming** — Suporta leitura eficiente de milhões de linhas através de um cursor server-side, sem esgotar a memória do Agente
* **Driver** — Utiliza o conector `MySqlConnector`, otimizado para performance assíncrona
* **Compatibilidade** — Funciona com MySQL (5.x, 8.x) e MariaDB
***
## 📝 Exemplo de Uso
1. Arraste o nó **Consulta MySQL** para o canvas
2. Selecione a conexão do seu e-commerce
3. Digite a query SQL para listar os produtos ativos
4. Conecte o fluxo para processar esses dados
---
---
url: 'https://docs.horusbi.com.br/etl/processors/inputs/odbc.md'
---
# Consulta ODBC
O nó **Consulta ODBC** permite conectar a qualquer fonte de dados que possua um driver ODBC instalado no servidor do Agente (Windows ou Linux).
***
## ⚙️ Parâmetros de Configuração
### Conexão
* **Descrição** — O nome do DSN (Data Source Name) configurado no Sistema Operacional ou a Connection String completa
* **Tipo** — String
* **Valor**:
* Exemplo DSN: `DSN=MeuBancoLegado;Uid=user;Pwd=pass;`
* Pode ser uma **Variável Global** no formato `NOME_DA_VARIAVEL`
### Consulta SQL
* **Descrição** — O comando SQL que será executado
* **Tipo** — Texto (Editor SQL)
* **Exemplo**:
```sql
-- A sintaxe depende do driver ODBC específico
SELECT * FROM tabela_legada
```
***
## 🔧 Detalhes Técnicos
* **Versatilidade** — Use este nó para conectar a bancos como DB2, Access, FileMaker, ou sistemas legados que não têm conectores nativos no Horus
* **Union Virtual** — Se a query contiver a string especial `{UNION}`, o Horus quebrará o comando em múltiplas execuções sequenciais para contornar limitações de memória de alguns drivers antigos
* **Driver** — Utiliza a biblioteca padrão `System.Data.Odbc` do .NET
***
## 📝 Exemplo de Uso
1. Instale o driver ODBC necessário na máquina onde roda o Agente Horus
2. Configure um DSN de sistema (System DSN)
3. No Horus, arraste o nó **Consulta ODBC**
4. Na conexão, coloque `DSN=NomeDoDSN`
5. Extraia os dados desejados
---
---
url: 'https://docs.horusbi.com.br/etl/processors/inputs/oracle.md'
---
# Consulta OracleDB
O nó **Consulta OracleDB** permite executar uma query SQL (PL/SQL) em um banco de dados Oracle e trazer o resultado para o Dataflow.
***
## ⚙️ Parâmetros de Configuração
### Conexão
* **Descrição** — A string de conexão (TNS) ou o nome da credencial cadastrada
* **Tipo** — String / Seleção
* **Valor**:
* Pode ser uma credencial salva no sistema (selecionável no dropdown)
* Pode ser uma **Variável Global** no formato `NOME_DA_VARIAVEL`
### Consulta SQL
* **Descrição** — O comando SQL `SELECT` que será executado no banco
* **Tipo** — Texto (Editor SQL)
* **Exemplo**:
```sql
SELECT CODIGO, NOME, DATA_NASC
FROM HR.FUNCIONARIOS
WHERE ROWNUM <= 1000
```
***
## 🔧 Detalhes Técnicos
* **Conversão de Tipos** — O driver converte automaticamente tipos `DATE` e `TIMESTAMP` do Oracle para o formato `DateTime` do .NET usado no Horus
* **Schemas** — Recomendamos sempre prefixar as tabelas com o schema (`HR.TABELA`) para evitar ambiguidades se o usuário da conexão não for o dono da tabela
* **Driver** — Utiliza o driver gerenciado (`Oracle.ManagedDataAccess`) que não requer instalação de cliente Oracle pesado (Instant Client) na maioria dos casos
***
## 📝 Exemplo de Uso
1. Arraste o nó **Consulta OracleDB** para o canvas
2. Selecione a credencial do ERP
3. Insira a query de extração de notas fiscais
4. Prossiga com o fluxo para validação ou carga
---
---
url: 'https://docs.horusbi.com.br/etl/processors/inputs/postgresql.md'
---
# Consulta PostgreSQL
O nó **Consulta PostgreSQL** permite executar uma query SQL em um banco de dados PostgreSQL e trazer o resultado para o Dataflow.
***
## ⚙️ Parâmetros de Configuração
### Conexão
* **Descrição** — A string de conexão ou o nome da credencial cadastrada
* **Tipo** — String / Seleção
* **Valor**:
* Pode ser uma credencial salva no sistema (selecionável no dropdown)
* Pode ser uma **Variável Global** no formato `NOME_DA_VARIAVEL` — a variável deve conter a connection string completa
### Consulta SQL
* **Descrição** — O comando SQL `SELECT` que será executado no banco
* **Tipo** — Texto (Editor SQL)
* **Exemplo**:
```sql
SELECT id, nome, email, data_criacao
FROM usuarios
WHERE ativo = true
```
***
## 🔧 Detalhes Técnicos
* **Streaming** — O conector utiliza `DataEnumerator` para ler os dados linha a linha (streaming), evitando carregar todo o result set na memória RAM
* **Schema** — O schema (nomes das colunas e tipos) é inferido automaticamente a partir da primeira leva de dados retornada pela query
***
## 📝 Exemplo de Uso
1. Arraste o nó **Consulta PostgreSQL** para o canvas
2. Selecione a credencial do banco de produção
3. No campo SQL, escreva a query que extrai as vendas do dia anterior
4. Conecte a saída deste nó a um **Insert Datawarehouse** para salvar os dados
---
---
url: 'https://docs.horusbi.com.br/etl/processors/inputs/sql-server.md'
---
# Consulta SQL Server
O nó **Consulta SQL Server** permite executar uma query SQL em um banco de dados Microsoft SQL Server e trazer o resultado para o Dataflow.
***
## ⚙️ Parâmetros de Configuração
### Conexão
* **Descrição** — A string de conexão ou o nome da credencial cadastrada
* **Tipo** — String / Seleção
* **Valor**:
* Pode ser uma credencial salva no sistema (selecionável no dropdown)
* Pode ser uma **Variável Global** no formato `NOME_DA_VARIAVEL` — a variável deve conter a connection string completa
### Consulta SQL
* **Descrição** — O comando SQL `SELECT` que será executado no banco
* **Tipo** — Texto (Editor SQL)
* **Exemplo**:
```sql
SELECT [Id], [Nome], [ValorTotal]
FROM [Vendas]
WHERE [DataVenda] >= DATEADD(day, -1, GETDATE())
```
***
## 🔧 Detalhes Técnicos
* **Streaming** — O conector utiliza `DataEnumerator` para ler os dados linha a linha (streaming), evitando carregar todo o result set na memória RAM
* **Driver** — Utiliza o driver oficial `Microsoft.Data.SqlClient`, suportando as versões mais recentes do SQL Server e Azure SQL
* **Schema** — O schema é inferido automaticamente. Evite usar `SELECT *` para garantir que mudanças no banco não quebrem o fluxo
***
## 📝 Exemplo de Uso
1. Arraste o nó **Consulta SQL Server** para o canvas
2. Selecione a credencial do banco corporativo
3. No campo SQL, insira a query para buscar os novos pedidos
4. Conecte a saída deste nó a um processador de transformação ou destino
---
---
url: 'https://docs.horusbi.com.br/etl/processors/inputs/lakehouse.md'
---
# Consultar Lakehouse
O nó **Consultar Lakehouse** lê dados de uma tabela do Datawarehouse diretamente dentro de um fluxo ETL. Útil para enriquecer dados de uma fonte externa com dados já processados no DW, ou para reprocessar dados existentes.
***
## Parâmetros de Configuração
* **Tabela** — Tabela do Datawarehouse a ser consultada. A lista exibe todas as tabelas do tenant, incluindo as ainda não publicadas.
* **Consulta declarativa** (`querySpec`) — Projeção, filtros, agrupamento/agregações, colunas de janela, deduplicação e ordenação configurados de forma estruturada (sem SQL cru). Veja [Consulta declarativa (querySpec)](#consulta-declarativa-queryspec) abaixo.
* **Limite de linhas** (`Limit`) — Número máximo de linhas retornadas. Use `0` para sem limite.
***
## Consulta declarativa (querySpec)
Em vez de uma cláusula `WHERE` em SQL cru, o nó Consultar Lakehouse monta a consulta a partir de campos estruturados. Todos são opcionais (omitir = comportamento padrão).
### Select
Lista de nomes de coluna a projetar. Se omitido, retorna todas as colunas da tabela.
```yaml
Select: [SALE_ID, SALE_DATE, STATUS, TOTAL_AMOUNT]
```
### Filtros (Filters)
`Filters` aceita duas formas equivalentes — use uma OU outra, nunca as duas juntas:
* **Lista simples (v1)** — array de condições `{ Column, Op, Value }`, sempre combinadas com **E** (AND) entre si. Forma retrocompatível.
* **Árvore `and`/`or` (v2)** — estrutura aninhável de blocos `{ and: [...] }` / `{ or: [...] }`, onde cada item é outro bloco `and`/`or` ou uma folha `{ Column, Op, Value }`. Permite combinar **E** e **OU** livremente, inclusive aninhado.
| Op | Significado | Formato de `Value` |
|----|--------------|---------------------|
| `eq` | igual | valor único |
| `neq` | diferente | valor único |
| `gt` | maior que | valor único |
| `gte` | maior ou igual | valor único |
| `lt` | menor que | valor único |
| `lte` | menor ou igual | valor único |
| `in` | está na lista | array de valores |
| `between` | entre dois valores (inclusive) | array `[min, max]` |
| `is_null` | é nulo | omitido |
| `is_not_null` | não é nulo | omitido |
| `like_prefix` | começa com (prefixo de texto) | string única |
**Forma v1 — lista simples (AND implícito):**
```yaml
Filters:
- Column: STATUS
Op: in
Value: [ativo, pendente]
- Column: DATA_ATUALIZACAO
Op: gte
Value: { var: LastDataPoint }
```
**Forma v2 — árvore `and`/`or` (equivalente à lista acima, mais a possibilidade de OU):**
```yaml
Filters:
and:
- Column: DATA_ATUALIZACAO
Op: gte
Value: { var: LastDataPoint }
- or:
- Column: STATUS
Op: eq
Value: ativo
- Column: PRIORIDADE
Op: gte
Value: 5
```
O exemplo acima equivale a `DATA_ATUALIZACAO >= {LastDataPoint} AND (STATUS = 'ativo' OR PRIORIDADE >= 5)`. As colunas e operadores de cada folha seguem as mesmas regras de allowlist da forma v1; a árvore tem profundidade e número de folhas limitados (proteção anti-abuso).
### Agrupamento e Agregações (GroupBy / Aggregations)
`GroupBy` é uma lista de colunas de agrupamento. `Aggregations` é uma lista de `{ Func, Column, As }`, onde `As` é o nome da coluna de saída.
Funções (`Func`) disponíveis: `min`, `max`, `sum`, `count`, `avg`, `count_distinct`.
```yaml
GroupBy: [STATUS]
Aggregations:
- Func: min
Column: SALE_DATE
As: FIRST_SALE_DATE
```
Regra: ao usar `Aggregations`, toda coluna em `Select` precisa também estar em `GroupBy` (mesma regra do `GROUP BY` em SQL padrão).
### Colunas de Janela (WindowColumns)
`WindowColumns` calcula colunas analíticas (*window functions*) e as **projeta** junto com o resultado — ao contrário de `GroupBy`/`Aggregations`, não colapsa linhas: o número de linhas de saída continua o mesmo da entrada.
Cada item é `{ As, Func, Column, PartitionBy, OrderBy, Offset }`:
* **`As`** — nome da coluna de saída (obrigatório).
* **`Func`** — função da janela, uma das: `row_number`, `rank`, `dense_rank`, `sum`, `avg`, `min`, `max`, `count`, `lag`, `lead`.
* **`PartitionBy`** — lista de colunas que definem a "partição" (a chave dentro da qual a janela é calculada). Pode ser vazia = uma única janela sobre todo o resultado.
* **`OrderBy`** — lista de `{ Column, Dir }` que define a ordem dentro de cada partição.
* **`Column`** — coluna de entrada da função.
* **`Offset`** — só para `lag`/`lead`: quantas linhas voltar/avançar (inteiro ≥ 1, padrão `1`).
Os campos obrigatórios variam por função:
| `Func` | `Column` | `OrderBy` | `Offset` | Observação |
|---|---|---|---|---|
| `row_number`, `rank`, `dense_rank` | não usa (proibido) | **obrigatório** | não usa | funções de ranking |
| `sum`, `avg`, `min`, `max`, `count` | **obrigatório** (`sum`/`avg` exigem coluna numérica) | opcional | não usa | agregação sobre a janela (acumulada, se houver `OrderBy`) |
| `lag`, `lead` | **obrigatório** | **obrigatório** | opcional (padrão `1`) | valor de uma linha deslocada dentro da partição |
```yaml
WindowColumns:
- As: RN
Func: row_number
PartitionBy: [DEAL_ID]
OrderBy:
- Column: UPDATED_AT
Dir: desc
- As: TOTAL_ACUMULADO
Func: sum
Column: VALUE
PartitionBy: [DEAL_ID]
OrderBy:
- Column: IN_DATE
Dir: asc
- As: STATUS_ANTERIOR
Func: lag
Column: STATUS
PartitionBy: [DEAL_ID]
OrderBy:
- Column: IN_DATE
Dir: asc
Offset: 1
```
### Deduplicação (Dedupe)
`Dedupe` mantém apenas **uma linha por chave** — útil para pegar o estado mais recente (ou o mais antigo) de cada registro, por exemplo o status atual de cada `DEAL_ID`.
Campos: `{ PartitionBy, OrderBy, Keep }`, todos obrigatórios:
* **`PartitionBy`** — lista de colunas que formam a chave de deduplicação.
* **`OrderBy`** — lista de `{ Column, Dir }` que define qual linha é a "primeira"/"última" dentro de cada chave.
* **`Keep`** — `first` (mantém a primeira linha da ordenação) ou `last` (mantém a última).
```yaml
Dedupe:
PartitionBy: [DEAL_ID]
OrderBy:
- Column: UPDATED_AT
Dir: desc
Keep: first
```
O exemplo acima mantém, para cada `DEAL_ID`, apenas a linha com o `UPDATED_AT` mais recente (estado atual do deal).
> Para obter "primeira e última linha" ao mesmo tempo, use `WindowColumns` (projete `RN` em ordem crescente e outra em ordem decrescente) e filtre em um nó posterior — `Dedupe` cobre o caso de manter apenas um lado (primeiro **ou** último).
### Exclusividade mútua
Um nó Consultar Lakehouse pode usar **no máximo um** destes três mecanismos por consulta:
* `GroupBy` + `Aggregations` (agrupamento/agregação — colapsa linhas), **ou**
* `WindowColumns` (colunas de janela — projeta, não colapsa), **ou**
* `Dedupe` (deduplicação por chave).
`Select`, `Filters`, `OrderBy` e `Limit` são livres e podem ser combinados com qualquer um dos três acima. Configurar mais de um dos mecanismos exclusivos no mesmo nó é rejeitado na validação.
### Ordenação (OrderBy)
Lista de `{ Column, Dir }`, onde `Dir` é `asc` ou `desc`. `Column` pode ser uma coluna de `Select`/`GroupBy` ou o alias (`As`) de uma agregação.
```yaml
OrderBy:
- Column: SALE_DATE
Dir: desc
```
### Variáveis nos filtros
O valor de um filtro pode referenciar uma variável do fluxo em vez de um literal, usando `{ var: "NomeDaVariavel" }`:
| Variável | Resultado |
|----------|-----------|
| `{ var: "StartDate" }` | início do período (modo Temporal) |
| `{ var: "EndDate" }` | fim do período (modo Temporal) |
| `{ var: "LastDataPoint" }` | último valor processado (modo Incremental) |
| `{ var: "MinhaVariavel" }` | qualquer variável do tenant ou de desenvolvimento |
| `{ var: "VarDoConfigurator" }` | qualquer variável produzida por um nó PythonConfigurator anterior no fluxo |
```yaml
Filters:
- Column: DATA_VENDA
Op: between
Value: [{ var: "StartDate" }, { var: "EndDate" }]
```
> Para **transformar** uma variável antes de usá-la no filtro (por exemplo, `LastDataPoint` menos 12 dias), crie uma variável derivada em um nó **PythonConfigurator** anterior no fluxo e referencie o resultado com `{ var: "NomeDerivado" }`.
### WhereClause (legado)
`WhereClause` — cláusula SQL livre (sem a palavra `WHERE`) — ainda é aceita por retrocompatibilidade em fluxos existentes, mas está **descontinuada**. Novos fluxos devem usar `Filters`. Não crie novos usos de `WhereClause`.
***
## Comportamento
* Consulta o Lakehouse via Data-Inserter (caminho interno). Não usa credencial externa.
* O schema do nó é inferido automaticamente a partir da definição da tabela no DW.
* As colunas retornadas têm os mesmos tipos definidos na tabela (string, número, data).
***
## Diferença: Consultar Lakehouse vs. Extrair Datalake
| | Consultar Lakehouse | Extrair Datalake |
|---|---------------------|-----------------|
| **Fonte** | Tabelas do Datawarehouse | Arquivos Parquet no Datalake |
| **Filtro** | Consulta declarativa (`querySpec`: Select/Filters/GroupBy/Aggregations/WindowColumns/Dedupe/OrderBy) | Partição por data |
| **Uso típico** | Enriquecimento, reprocessamento de dados do DW | Reprocessamento de dados brutos/intermediários |
---
---
url: 'https://docs.horusbi.com.br/hec/resources/01-content.md'
---
# Conteúdo
O painel de **Conteúdo** reúne a gestão de todos os ativos de dados e visualização do seu ambiente. Aqui você administra Aplicações (Dashboards), Tabelas, Dataflows e Datamarts — controlando propriedade, publicação e compartilhamento de cada recurso.
## 📊 Aplicações (Dashboards)
As aplicações são os Dashboards e visualizações construídos na plataforma. No HEC, a gestão é dividida em dois contextos:
### Aplicações Pessoais
Lista todas as aplicações que estão em **Mesas Pessoais** (trabalho em progresso ou conteúdo privado).
* **Publicar**: Move uma aplicação da mesa pessoal para uma **Mesa de Aplicações** (pública). Ao publicar, você pode criar uma nova aplicação ou optar por **substituir** uma já existente na mesa de destino — útil para atualizar versões
* **Clonar**: Cria uma cópia exata da aplicação para a mesa pessoal de outro usuário. Ideal para transferir trabalho ou criar templates base para outros analistas
* **Compartilhar**: Define quem são os "Responsáveis" (coautores) desta aplicação, permitindo edição colaborativa
### Aplicações Publicadas
Lista as aplicações já disponíveis em **Mesas de Aplicações** para os usuários finais.
* O foco aqui é gestão de acesso e organização
* Use para auditar onde cada aplicação está publicada e a qual mesa pertence
### Dashboards Pessoais
*(Visível apenas se a opção "Permitir usuários criarem Dashboards pessoais" estiver ativada no tenant)*
Gerenciamento administrativo de todos os Dashboards pessoais criados pelos usuários do tenant.
**Informações exibidas:**
| Coluna | Descrição |
|--------|-----------|
| **Nome** | Nome do Dashboard pessoal |
| **Aplicação** | Aplicação onde o Dashboard foi criado |
| **Proprietário** | Usuário dono do Dashboard |
| **Criado Em** | Data de criação |
| **Compartilhamentos** | Quantidade de usuários com acesso |
**Ações administrativas:**
* **Compartilhar**: Gerencie os compartilhamentos (adicionar/remover usuários com acesso)
* **Transferir Propriedade**: Passe a posse do Dashboard para outro usuário — útil quando um colaborador sai da empresa
* **Excluir**: Remova permanentemente o Dashboard
> \[!TIP]
> Use os filtros por aplicação ou proprietário para encontrar Dashboards específicos rapidamente.
***
## 🗄️ Tabelas
Gestão das tabelas do Data Warehouse.
* **Tabelas Pessoais**: Tabelas criadas por usuários (via upload de Excel ou Dataflows) em suas mesas pessoais
* **Gestão**: Permite excluir tabelas obsoletas ou gerenciar permissões de edição (Responsáveis)
> \[!NOTE]
> A edição da estrutura da tabela ou dos dados em si é feita diretamente no módulo Data Warehouse ("Minha Mesa"), não nesta tela.
***
## 🔄 Dataflows
Gestão dos fluxos de integração (ETL).
* **Fluxos Pessoais**: Pipelines de dados em desenvolvimento nas mesas pessoais dos analistas
* **Ações**:
* **Clonar**: Copia o fluxo para a mesa pessoal de outro usuário
* **Compartilhar**: Adiciona coautores ao fluxo
> \[!NOTE]
> A construção e edição do fluxo é feita no módulo ETL. O HEC gerencia a *propriedade* e o *ciclo de vida* (publicação, exclusão) dos fluxos.
***
## 📦 Datamarts
Datamarts são **"Vitrines de Negócio"** que organizam tabelas por contexto ("Vendas", "RH", "Financeiro"). Eles facilitam o consumo dos dados pelo usuário final, sem expor a complexidade da camada técnica.
* **Responsáveis**: Defina quem são os responsáveis técnicos pelo Datamart. Eles poderão controlar e liberar acesso a grupos e usuários sobre as tabelas que compõem este Datamart
* **Conteúdo**: Um Datamart é apenas um agrupador lógico — ele não armazena dados, apenas organiza as tabelas existentes para facilitar o consumo
---
---
url: 'https://docs.horusbi.com.br/dataviz/04-features/alerts/content-ai.md'
---
# Conteúdo: Análise por IA
O bloco de **IA** delega a geração da mensagem a um **[agente](/ia/agentes)** que consulta seus dados, valida dimensões e (opcionalmente) gera gráficos. Diferente do bloco de [Texto](./content-text.md) com variáveis fixas, aqui você dá um **briefing** e a IA decide o que buscar, como interpretar e como redigir.
> \[!TIP]
> IA é a opção certa quando você quer **interpretação** ("vendas caíram 12% porque...") em vez de **números frios** ("vendas: R$ X"). É também o caminho quando o conjunto de dados muda muito e seria impraticável definir variável por variável.
***
## Quem investiga: o agente
O alerta e o agente têm papéis distintos, e vale entender a divisão antes de configurar:
| | O alerta define | O agente define |
|---|---|---|
| **Quando** dispara | ✅ [Agendamento](./triggers.md) ou [condição](./conditions.md) | |
| **O que pedir** neste disparo | ✅ O briefing | |
| **Para quem** enviar | ✅ [Canais e destinatários](./channels.md) | |
| **O que ele enxerga** | | ✅ Aplicações do escopo |
| **Como investiga e como responde** | | ✅ As instruções da persona |
No bloco de IA você escolhe, no campo **Agente**, quem faz a investigação. Cada agente é uma persona reutilizável, criada uma vez no módulo **[Agentes de IA](/ia/agentes)** e reaproveitada por quantos alertas você quiser — cada um com o seu próprio briefing.
> \[!NOTE]
> **Sem agente escolhido, nada muda.** O bloco roda com o **Agente padrão**, exatamente como sempre rodou. Alertas já configurados continuam funcionando sem qualquer ajuste — escolher um agente é opcional.
### Quando um agente é escolhido
Dois campos do bloco passam a vir da definição do agente, e a tela sinaliza isso ("Definido pelo agente selecionado acima"):
* **Aplicações** — o escopo é o do agente.
* **Modo do modelo** — Rápido ou Raciocínio profundo, conforme configurado no agente.
O resto continua sendo do bloco, disparo a disparo: **briefing**, **relatórios de exemplo**, **memória** e **formato de saída**.
### "Promover a agente"
Se você já tem um bloco de IA configurado e quer reaproveitar essa mesma investigação em outros alertas, o botão **"Promover a agente"** cria, em um clique, um agente com as aplicações e o modo daquele bloco. O alerta original continua entregando exatamente o mesmo conteúdo de antes — a diferença é que agora a persona tem nome e pode ser importada em outros lugares.
> \[!TIP]
> Se um agente usado por um alerta for excluído, o alerta **não quebra**: ele volta a investigar com o Agente padrão.
📖 Veja **[Agentes de IA](/ia/agentes)** para criar e testar um agente, e para ver os passos que ele seguiu em cada investigação.
***
## Como Funciona
```mermaid
flowchart LR
A[Briefing + Apps + Exemplos] --> B[Agente]
B -->|consulta dados| C[Aplicações]
B -->|valida valores| D[Dimensões]
C & D --> B
B --> F{Formato de saída}
F -->|Somente texto| G[Texto final]
F -->|Texto + gráficos| H[Texto + Gráficos + Links]
```
A IA trabalha em ciclo: pensa, busca dados, vê resultados, pensa mais, até produzir a resposta consolidada. Cada busca é executada nas aplicações autorizadas com os filtros que a IA decidiu.
***
## Configuração
### 1. Aplicações (até 3)
A IA só pode consultar dados das aplicações selecionadas. Quanto menos apps, mais foco (e menos custo de processamento).
> \[!WARNING]
> Mais de 3 aplicações por bloco de IA bloqueia o save. Para análises com mais apps, use **múltiplos blocos de IA** no mesmo alerta, cada um focado em 1-3 aplicações.
> \[!NOTE]
> Com um **[agente](/ia/agentes)** escolhido, quem manda é o escopo dele — as aplicações do agente substituem as do bloco.
### 2. Briefing do Analista
Texto livre (50 a 4000 caracteres) descrevendo:
* **Objetivo**: o que queremos descobrir/comunicar
* **Público-alvo**: executivo, técnico, comercial
* **Métricas-chave**: quais números priorizar
* **Tom**: formal, urgente, didático
**Exemplo:**
```
Briefing:
Analise as vendas das últimas 24h comparando com a média dos últimos
7 dias. Foque em anomalias por filial (queda maior que 10% ou pico
acima de 30%). Tom executivo, mensagem curta, recomende ação imediata
se houver queda crítica.
```
> \[!TIP]
> O botão **"Sugerir Briefings"** gera 3 templates pré-prontos baseados nas aplicações selecionadas. Bom ponto de partida quando você não sabe como começar.
### 3. Relatórios de Exemplo (até 5)
Você fornece **exemplos de relatórios** que servem como referência para a IA aprender o estilo de análise. Para cada exemplo você define:
| Campo | Descrição |
|---|---|
| **Nome** | Como você se refere ao exemplo (ex.: "Vendas SP Janeiro") |
| **Aplicação** | Aplicação de onde puxar os dados |
| **Campos** | Colunas (mesma escolha do bloco de [Relatório](./content-report.md)) |
| **Filtros** | Editor visual de filtros |
| **Ordenação** | Coluna + direção |
| **Contexto** *(opcional)* | Texto livre explicando o **propósito** do exemplo |
#### "Criar do zero" vs "Importar de bookmark"
Dois caminhos para adicionar um exemplo:
* **Criar do zero**: abre um editor vazio, você monta o exemplo do nada (escolhe aplicação, campos, filtros, etc.)
* **Importar de bookmark**: seleciona um bookmark salvo da aplicação. Os campos/filtros/ordenação são copiados como ponto de partida. Você **edita à vontade depois**, o exemplo fica **desacoplado do bookmark original**. Se o bookmark for editado ou deletado, seu alerta segue funcionando.
> \[!TIP]
> O caso clássico de "importar e editar": você tem um bookmark "Vendas do Mês" e quer usar como exemplo dois períodos diferentes (mês atual e mês passado). Importe duas vezes, edite o filtro de data em cada uma, dê nomes distintos.
#### Campo de Contexto
Texto livre por exemplo. Vai pro briefing da IA como contexto daquele relatório específico. Útil quando o exemplo não é auto-explicativo:
```
Exemplo 1
nome: "Vendas Mensal Comparativo"
contexto: "Use como referência para análise sazonal. Métrica primária
é Valor de Venda, não Ticket Médio."
```
A IA lê esse contexto e sabe interpretar o exemplo do jeito certo. Bookmarks puros não permitem esse tipo de anotação.
### 4. Formato de Saída
Define **o que** será entregue como conteúdo do alerta.
| Modo | Comportamento | Padrão |
|---|---|---|
| **Somente texto** | Entrega só a mensagem final consolidada. Sem cards de gráfico, sem links. | ✅ Default |
| **Texto + gráficos e links** | Além da mensagem, inclui gráficos gerados pela IA e links como cards/anexos separados. | |
> \[!TIP]
> **"Somente texto"** é o que a maioria dos casos quer: mensagem única, limpa, direta. Marque "Texto + gráficos" só se o destinatário se beneficia dos artefatos extras (ex.: dashboard executivo que vai ler com pressa e prefere ver gráfico).
### 5. Modo do Modelo
| Modo | Custo | Velocidade | Uso |
|---|---|---|---|
| **Rápido** | 1× | 10-30s | Digests diários, briefings simples |
| **Raciocínio profundo** | 4-5× | 60-120s | Detecção de anomalia, análise causa-raiz, comparativos complexos |
> \[!NOTE]
> Com um **[agente](/ia/agentes)** escolhido, o modo vem da configuração dele.
### 6. Memória
Quantas **entregas anteriores** do mesmo alerta a IA recebe como contexto.
| Valor | Comportamento |
|---|---|
| 0 | Sem memória. Cada disparo é independente. |
| 3 (padrão) | IA vê as 3 últimas mensagens entregues. |
| 10 (máx) | IA vê até 10 entregas passadas. |
Usa-se memória pra a IA **comparar com o histórico recente** ("vendas caíram 15% vs a análise de ontem"). Memória bem dimensionada vira o "fio condutor" do alerta dia após dia.
> \[!WARNING]
> Memória alta significa mais processamento consumido a cada disparo. Para alertas diários ativos, 3 costuma ser o ponto de equilíbrio. Suba para 7-10 só se a análise depende muito de contexto longitudinal.
***
## Ferramentas Disponíveis para o Agente
Internamente, a IA tem ferramentas que decide quando chamar:
* **Consultar dados**: roda uma busca sobre uma das aplicações autorizadas (com campos, filtros, agrupamento e ordenação que a IA define). Opcionalmente gera gráfico.
* **Validar valores**: resolve um nome aproximado para o valor exato no banco (ex.: "filial cgr" para "Filial Centro Grande SP").
> \[!WARNING]
> Cada busca do agente traz no máximo **~30 linhas**. Para um **número global** (um total, um consolidado da carteira), peça um valor **agregado**: uma consulta que já devolve a soma. Detalhar muitas entidades (centenas de clientes, produtos, filiais) e contar que a IA some essa lista é arriscado: ela chega **truncada** e o total pode oscilar entre disparos.
Em **"Somente texto"**, a IA é orientada a não gerar gráficos. Mesmo que tente, eles são ignorados na entrega.
***
## Roteiro Fixo vs Exploração Livre
**Esta é provavelmente a parte mais importante de entender pra usar a IA bem.** A IA tem dois modos de trabalho, e quem escolhe é você (pelo jeito que escreve o briefing e os relatórios de exemplo).
### Modo Exploratório (padrão)
Quando você só dá o **briefing** e poucos (ou nenhum) relatórios de exemplo, a IA **explora os dados livremente**. Ela decide:
* Quais consultas fazer
* Quais colunas usar
* Que filtros aplicar
* Como agregar (por dia? por mês? por filial?)
* Em que ordem investigar
Esse modo é **poderoso pra descoberta**, ruim pra consistência. A cada disparo a IA pode tomar caminhos diferentes, porque pode haver mais de um caminho razoável para o briefing.
**Quando usar:**
* Detecção de anomalias (você não sabe de antemão o que vai aparecer)
* Investigação aberta ("descubra o que mudou na última semana")
* Análise causa-raiz onde o problema pode estar em qualquer dimensão
**O sintoma de quem está nesse modo sem perceber:**
> "A mensagem de hoje veio totalmente diferente da de ontem. Antes ela falava de filiais, hoje ela falou de produtos. Como faço pra ela sempre falar de filiais?"
Isso é a IA explorando, e é o comportamento esperado quando o briefing deixa margem em aberto.
### Modo Roteiro Fixo
Quando você quer **a mesma análise todo dia**, com **as mesmas consultas, no mesmo formato**, faça o seguinte:
1. **Crie os relatórios de exemplo com as consultas prontas.** Cada um já é a consulta exata que você quer que a IA rode (campos, filtros, agrupamento, ordenação). Pense neles como "queries prontas pra rodar como-são".
2. **No briefing, instrua explicitamente** a IA a executar essas consultas como-são (sem inventar variações) e descrever os resultados num formato fixo.
**Exemplo de briefing pra roteiro fixo:**
```
Você é o analista comercial. Sua função é executar os relatórios de
exemplo abaixo COMO ESTÃO, sem improvisar variações.
Para cada relatório:
1. Execute exatamente como definido (campos, filtros, ordenação)
2. Reporte os top 3 resultados em uma frase curta
3. NÃO faça comparações adicionais não solicitadas
4. NÃO investigue dimensões além das que estão nos relatórios
Formato da mensagem:
📊 [Nome do relatório 1]
[3 linhas resumindo top 3]
📊 [Nome do relatório 2]
[3 linhas resumindo top 3]
Tom: direto, sem floreio.
```
3. **Use o campo "Contexto" de cada relatório de exemplo** para dar instruções específicas daquele exemplo:
```
Relatório: "Vendas por Filial"
contexto: "Sempre cite VALOR LÍQUIDO, nunca bruto. Lista as 5 filiais
com maior queda vs ontem. Não comente filiais estáveis."
```
**Resultado:** mensagem com formato previsível, mesmos cortes, mesmas dimensões, todo dia. A IA continua "interpretando" os números (variação, ranking, contexto), mas dentro do roteiro que você definiu.
### Híbrido (o mais comum na prática)
A maioria dos alertas funciona melhor com **mistura**: 1-2 relatórios de exemplo definindo o "esqueleto" da análise + briefing dando margem pra IA destacar anomalias.
```
Briefing:
"Use os relatórios de exemplo como base obrigatória da análise. Sempre
reporte os números dos relatórios. ALÉM disso, se notar algo
genuinamente anômalo (variação >30% inesperada, padrão que rompe o
histórico), mencione no final como 'Observação adicional'."
```
Aqui a IA sabe o que é esperado (os relatórios) mas tem licença pra apontar algo fora do script. Útil pra digests "padrão + alertas embutidos".
### Comparação Rápida
| | Exploratório | Roteiro fixo | Híbrido |
|---|---|---|---|
| **Relatórios de exemplo** | 0-1 | 3-5 com queries prontas | 1-3 com queries prontas |
| **Briefing** | "Analise X" | "Execute os exemplos como-são" | "Use exemplos + destaque anomalias" |
| **Consistência** | Baixa | Alta | Média |
| **Descobertas** | Alta | Baixa | Média |
| **Uso ideal** | Investigação | Digest fixo | Operação do dia a dia |
### Memória + Roteiro Fixo
A [memória](#6-memória) combina muito bem com roteiro fixo. Como o formato da mensagem é consistente, a IA consegue facilmente comparar "hoje vs ontem" usando as últimas N entregas como referência:
```
Briefing (roteiro fixo + memória):
"Execute os relatórios como-são. Para cada métrica, compare com a
entrega de ontem (use a memória). Destaque com 🔼/🔽 a direção e
% de variação."
```
Com memória 0, cada disparo é independente. Com memória 3+, a IA tem "ontem, anteontem e três dias atrás" como contexto fresco.
***
## Texto Final
A IA produz uma mensagem **consolidada no último turno**. Se a IA "pensa em voz alta" entre buscas ("vou buscar os dados..."), esse texto **não é incluído** na entrega, só a versão final consolidada.
### Formato
A IA é orientada a produzir um texto que renderiza bem em todos os canais. Negrito, itálico, emojis em moderação. O canal de destino (WhatsApp/Email/Telegram) cuida da conversão do formato apropriado, você não precisa se preocupar.
***
## Cenários Comuns
### Digest Diário Comercial
```
Apps: [Vendas]
Briefing: "Resumo executivo de vendas de ontem vs média móvel de 7 dias.
Destaque variações maiores que 10%. Tom direto, 3-4 frases."
Modo: Rápido
Memória: 3
Formato de saída: Somente texto
```
### Detecção de Anomalia
```
Apps: [Vendas, Logística]
Briefing: "Investigue picos ou quedas anômalas em vendas, cruzando
com tempo de entrega e ruptura de estoque. Apresente causa
provável."
Modo: Raciocínio profundo
Memória: 7
Formato de saída: Texto + gráficos e links
```
### Análise Comparativa com Bookmark
```
Apps: [Vendas]
Briefing: "Compare a performance da última semana com os relatórios
de referência. Foque no que mudou e por quê."
Relatórios de exemplo:
- Vendas Semana Passada (importado de bookmark)
- Vendas Mesma Semana Ano Passado (importado, filtro editado)
Modo: Raciocínio profundo
Memória: 0
Formato de saída: Somente texto
```
### Digest de Formato Fixo (Roteiro)
Para quando você quer **exatamente o mesmo formato todo dia**, sem variação:
```
Apps: [Vendas]
Briefing:
"Você é o analista comercial. Execute os relatórios abaixo COMO ESTÃO,
sem variar. Para cada um, reporte os 3 primeiros resultados em uma
linha, no formato:
📊 [Nome do relatório]
1. [valor 1] - [dimensão]
2. [valor 2] - [dimensão]
3. [valor 3] - [dimensão]
Compare com a entrega de ontem (memória) e marque 🔼/🔽 na linha
de cada item. Sem texto adicional fora do formato."
Relatórios de exemplo:
- Top 3 Vendedores do Dia (com filtro data=Ontem)
- Top 3 Filiais do Dia (com filtro data=Ontem)
- Top 3 Produtos do Dia (com filtro data=Ontem)
Modo: Rápido (não precisa de raciocínio profundo se é "execute o roteiro")
Memória: 3
Formato de saída: Somente texto
```
A mensagem que chega é **previsível** dia após dia, com os mesmos cortes, mesmas dimensões. O destinatário sabe o que esperar.
***
## Boas Práticas
### ✅ Briefing direto, exemplos eloquentes
A IA segue o que você pede e imita o estilo dos exemplos. Briefing prolixo + exemplos esparsos resulta em saída inconsistente.
```
✅ "Análise diária de vendas. 3 frases. Destaque variação % vs ontem."
❌ "Faça uma análise abrangente e detalhada considerando múltiplos
aspectos das vendas, incluindo mas não se limitando a..."
```
### ✅ Contextualize exemplos importantes
Quando o exemplo tem nuance (métrica secundária, regra de negócio), use o campo "Contexto":
```
Exemplo: "Vendas Líquidas"
contexto: "ATENÇÃO: é Valor Líquido, não Valor Bruto. Já desconta
estornos e devoluções."
```
### ✅ Inconsistência na saída? Provavelmente está exploratório demais
Se você reclama "a mensagem de hoje veio diferente da de ontem", você está no [modo exploratório](#modo-exploratório-padrão). A solução é migrar pra [roteiro fixo](#modo-roteiro-fixo): defina as consultas como relatórios de exemplo e instrua a IA no briefing a executá-las "como-são".
### ✅ Pré-valide acessos do criador
Se o criador do alerta perder acesso a uma aplicação, ela é **silenciosamente removida** do contexto. Se sobrar nenhuma aplicação válida, o alerta entrega uma mensagem de erro. Verifique acessos depois de mudanças de permissão.
### ✅ Use o botão "Pré-visualizar" antes de ativar
A IA pode rodar dezenas de variações sutis dependendo do briefing. **Sempre** pré-visualize 2-3 vezes antes de ativar pra produção. Se a saída variar muito entre execuções e isso é problema, ajuste pra roteiro fixo.
### ❌ Não confunda "memória" com "histórico de dados"
A memória é só das **mensagens entregues anteriormente**, não dos dados do banco. Para a IA comparar dados históricos, ela mesma faz a consulta com filtros de data (ex.: "Últimos 30 dias").
### ❌ Não force "Texto + gráficos" se a maioria dos destinatários é WhatsApp
Gráficos como assets separados ficam visualmente confusos no chat móvel. Para WhatsApp, prefira **somente texto** com um [Relatório PDF](./content-report.md) anexo se precisar de visualização.
### ❌ Não use IA pra tarefas que variável dá conta
Se a mensagem é "Vendas de hoje: R$ X", **não use IA**. Use [Texto com variável](./content-text.md). É mais rápido, mais barato, e 100% consistente. IA é pra quando há interpretação, comparação, ranking dinâmico ou narrativa que se ajusta aos dados.
***
## Limites
| Limite | Valor |
|---|---|
| **Tempo máximo de geração** | 90 segundos. Acima disso, retorna mensagem de timeout. |
| **Briefing** | 50 a 4000 caracteres |
| **Aplicações por bloco** | 1 a 3 |
| **Relatórios de exemplo** | 5 (criados do zero + importados de bookmark, combinados) |
| **Memória** | 0 a 10 |
| **Linhas por consulta do agente** | ~30 por busca. Para totais exatos, prefira uma consulta **agregada** (um número), não a soma da lista. |
| **Destinatários por alerta com IA** | 50 (cap específico do bloco de IA) |
O custo de processamento conta no consumo de IA do tenant. Modo **Rápido** consome aproximadamente 1×, **Raciocínio Profundo** 4-5×. Veja **HEC > Tenants > Consumo IA** para o detalhamento.
***
## Próximos Passos
* Crie e teste a persona que investiga em **[Agentes de IA](/ia/agentes)**
* Combine com [Texto](./content-text.md) e [Relatório](./content-report.md) para mensagens híbridas
* Configure os [canais de entrega](./channels.md)
* Para regras "quando dispara?", veja [Condicional](./conditions.md) ou [Agendamento](./triggers.md)
---
---
url: 'https://docs.horusbi.com.br/dataviz/04-features/alerts/content-chart.md'
---
# Conteúdo: Gráfico (Snapshot)
O bloco de **Gráfico** envia uma **imagem estática** de um widget ou dashboard, renderizada no momento do disparo. É o jeito mais direto de levar dados visuais para canais como WhatsApp, Telegram e Email, onde o destinatário pode não conseguir abrir a aplicação no celular.
> \[!TIP]
> Snapshot é **estático**: congela o estado dos dados no momento do disparo. O destinatário não interage, vê a imagem como ela foi renderizada. Para acesso navegável, prefira incluir um link no [Texto](./content-text.md) ou usar a [IA](./content-ai.md) (que pode gerar links automaticamente).
***
## Tipos de Fonte
### Widget Isolado
Captura **apenas um widget** de um dashboard (um gráfico de barras, uma KPI, uma rosca).
| Campo | Descrição |
|---|---|
| **Aplicação** | Aplicação onde o widget está |
| **Dashboard** | Dashboard que contém o widget |
| **Widget** | Widget específico a capturar |
Saída: imagem PNG com apenas o widget escolhido, sem o resto do dashboard.
### Dashboard Inteiro
Captura **um dashboard completo**, todos os widgets, no layout configurado.
| Campo | Descrição |
|---|---|
| **Aplicação** | Aplicação onde o dashboard está |
| **Dashboard** | Dashboard a capturar inteiro |
Saída: imagem PNG com o dashboard renderizado em alta resolução.
> \[!WARNING]
> Dashboards muito longos (com scroll vertical extenso) geram imagens grandes que podem ser cortadas em alguns canais (WhatsApp limita 5 MB de imagem). Para dashboards extensos, considere capturar widgets específicos em vez do dashboard todo.
***
## Resolução
A captura é feita por um worker headless que renderiza a página real. Resolução padrão:
| Tipo | Resolução |
|---|---|
| Widget isolado | 1200 × altura proporcional ao widget |
| Dashboard | 1920 × 1080 (Full HD) base, pode escalar se o dashboard for maior |
Não há ajuste de resolução custom por alerta, é gerenciado globalmente pelo worker.
***
## Como Os Dados São Calculados
O snapshot **executa as consultas do widget/dashboard no momento do disparo**, com os filtros que estiverem salvos. Se o dashboard tem um filtro de data dinâmico (ex.: Ontem, Mês Atual), o valor é resolvido naquele instante.
### Dashboard com Filtro Aplicado
Você pode salvar o dashboard com filtros pré-aplicados (via bookmark) e referenciar o bookmark. O snapshot será gerado respeitando esses filtros.
***
## Cenários Comuns
### KPI Diário
```
Tipo: Widget Isolado
Dashboard: "Comercial"
Widget: "Vendas do Dia" (KPI)
```
Envia um número grande em destaque visual, formato curto, alto impacto. Bom para canais móveis (WhatsApp, Telegram).
### Resumo Visual Completo
```
Tipo: Dashboard Inteiro
Dashboard: "Painel Executivo"
```
Envia o dashboard completo. Funciona bem em Email (tela maior) e como anexo de PDF (combine com [Relatório](./content-report.md)).
### Comparativo Side-by-Side
```
Bloco 1: Snapshot do widget "Vendas Atual"
Bloco 2: Snapshot do widget "Vendas Mesmo Mês Ano Passado"
```
Dois snapshots seguidos com legenda no [Texto](./content-text.md) entre eles dão um comparativo visual sem precisar de IA.
***
## Quando Usar (e Quando Não)
### ✅ Use snapshot quando:
* O widget já está montado e dá pra "fotografar"
* O destinatário só precisa **ver** o número/tendência, não interagir
* Você quer formato visual sem custo de gerar PDF
* Canal é WhatsApp/Telegram (imagem inline funciona melhor que tabela)
### ❌ Evite snapshot quando:
* A informação muda a cada interação (drilldown necessário). Use [Relatório](./content-report.md) com XLSX pra permitir manipulação.
* Você precisa de explicação/contexto sobre os números. Use [IA](./content-ai.md).
* O dashboard tem muitos widgets pequenos. A imagem fica densa demais, considere [Relatório PDF](./content-report.md) que renderiza melhor.
***
## Limitações Atuais
* ❌ **Não há filtro por alerta** sobre o widget. Os filtros são os salvos no dashboard/widget. Para variações, mantenha bookmarks com filtros pré-configurados.
* ❌ **Não há legendas/anotações sobrepostas**. A imagem sai como o widget aparece em tela.
* ⚠️ **Dashboards com mapas** podem ter renderização imperfeita dependendo dos tiles disponíveis durante a captura.
***
## Próximos Passos
* Adicione [Texto](./content-text.md) antes/depois para contextualizar a imagem
* Considere combinar com [IA](./content-ai.md) que pode gerar **seus próprios gráficos** dinamicamente
* Configure os [canais de entrega](./channels.md)
---
---
url: 'https://docs.horusbi.com.br/dataviz/04-features/alerts/content-report.md'
---
# Conteúdo: Relatório (PDF / Excel)
O bloco de **Relatório** exporta um conjunto detalhado de dados como **PDF** ou **planilha Excel (.xlsx)**, anexado à entrega do alerta. É a opção certa quando o destinatário precisa **auditar, filtrar manualmente ou trabalhar em cima** dos dados.
> \[!TIP]
> Pense em Relatório como a "versão para impressão" do alerta: cabe nuance, tabela detalhada, granularidade que não dá pra mostrar inline numa mensagem.
***
## Estrutura do Bloco
| Campo | Descrição |
|---|---|
| **Aplicação** | Aplicação de onde vêm os dados |
| **Campos** | Colunas a incluir no relatório (escolhidas no seletor) |
| **Filtros** | Editor de filtros para segmentar as linhas |
| **Ordenação** | Coluna(s) de ordenação + direção (Crescente/Decrescente) |
| **Enviar como PDF** | Checkbox |
| **Enviar como XLSX** | Checkbox |
| **Esconder filtros aplicados** | Checkbox: não mostra a seção "Filtros Aplicados" no PDF |
Você pode marcar **PDF e XLSX ao mesmo tempo**, o destinatário recebe os dois anexos.
***
## Campos (Colunas)
Para cada campo, você escolhe:
* A **coluna** (selecionada no seletor de colunas da aplicação)
* A **agregação** quando aplicável (Soma, Média, Contagem, Máximo, Mínimo, Contagem Distinta)
* A **granularidade** para colunas de data (Dia, Mês, Ano, etc.)
**Exemplo:** "Vendas por cliente, agregado por dia"
| Campo | Tipo |
|---|---|
| Data | Granularidade: Dia |
| Cliente | Dimensão |
| Valor | Soma |
| Margem | Média |
Resultado: tabela com 1 linha por (data, cliente) com soma de valor e média de margem.
***
## Filtros
Mesmo editor de filtros usado em dashboards e bookmarks. Filtros condicionam o que entra na tabela.
**Exemplo de filtros possíveis:**
* "Data está entre Ontem e Ontem"
* "Vendedor é um de: João, Maria"
* "Margem é maior que zero"
Os filtros são montados visualmente, sem código.
***
## Ordenação
Múltiplas colunas com direção independente:
* 1ª: Valor (Soma), Decrescente (maior valor primeiro)
* 2ª: Cliente, Crescente (alfabético como desempate)
***
## Formato PDF
Quando **"Enviar como PDF"** está marcado:
* Cabeçalho com **nome do alerta e data/hora do disparo**
* (opcional) Seção **"Filtros Aplicados"**, que pode ser escondida com "Esconder filtros aplicados"
* Tabela com até **N páginas** (depende do volume)
* Rodapé com numeração de página
> \[!WARNING]
> Relatórios com milhares de linhas geram PDFs grandes. Considere usar filtros mais restritivos ou exportar apenas em XLSX (melhor para análise).
***
## Formato XLSX
Quando **"Enviar como XLSX"** está marcado:
* Uma planilha única com cabeçalho + linhas de dados
* Cada coluna tipada (números como número, datas como data) para facilitar filtros/pivot do destinatário
* Sem limite explícito de linhas a nível de formato (limite real é o de processamento do servidor)
***
## Esconder Filtros Aplicados
Em alguns cenários, mostrar a lista de filtros aplicados no topo do PDF é redundante (o destinatário já sabe o contexto pelo título do alerta) ou confidencial (filtros revelam regras internas).
Marcando **"Esconder filtros aplicados"**:
* PDF não mostra a seção de filtros
* Conteúdo da tabela é o mesmo
***
## Cenários Comuns
### Fechamento Diário de Vendas
```
Campos: filial, vendedor, valor (Soma), ticket médio (Média)
Filtros: data = Ontem
Ordenação: valor (Soma), Decrescente
PDF: sim
XLSX: sim
```
PDF pra impressão + XLSX pro time financeiro filtrar/pivotar.
### Top Clientes do Mês
```
Campos: cliente, valor (Soma), quantidade de pedidos (Contagem)
Filtros: data = Mês Atual
Ordenação: valor (Soma), Decrescente
PDF: sim
XLSX: não
```
Só PDF, relatório executivo direto.
### Auditoria de Estorno
```
Campos: data (Dia), vendedor, motivo, valor (Soma)
Filtros: tipo = "estorno", data entre Últimos 7 dias
Ordenação: data, Decrescente
XLSX: sim
PDF: não
Esconder filtros aplicados: sim
```
Só XLSX, auditor vai filtrar/agrupar do jeito que precisa. "Esconder filtros aplicados" evita mostrar a regra de estorno no cabeçalho.
***
## Limites
| Limite | Valor |
|---|---|
| **Linhas por relatório** | Sem cap explícito, processamento pode demorar para mais de 100.000 linhas |
| **Tamanho de anexo (PDF/XLSX)** | 100 MB (WhatsApp/Telegram), Email depende do SMTP |
| **Geração paralela** | Workers de exportação processam em fila, picos podem atrasar entrega |
> \[!NOTE]
> Se o relatório está demorando muito, verifique se você está usando agregações (Soma, Média) em vez de pegar a tabela bruta. Granularidade alta + sem agregação = volume de linhas explodindo.
***
## Quando Usar (e Quando Não)
### ✅ Use Relatório quando:
* Destinatário precisa **manipular** os números (filtrar, somar diferente, copiar pra outra ferramenta)
* O volume de dados não cabe num texto
* Auditoria/compliance exige documento estruturado e rastreável
* O canal suporta anexo (Email, WhatsApp, Telegram, Webhook URL)
### ❌ Não use quando:
* O valor pode ser passado num texto curto. Use [Texto](./content-text.md) com variável.
* O destinatário quer ver visualmente. Use [Gráfico](./content-chart.md) ou ative gráficos nos prompts da [IA](./content-ai.md).
* Você quer interpretação, não dados crus. Use [IA](./content-ai.md).
***
## Próximos Passos
* Combine com [Texto](./content-text.md) introdutório ou [IA](./content-ai.md) analítica
* Configure os [canais de entrega](./channels.md) que suportam anexo
---
---
url: 'https://docs.horusbi.com.br/dataviz/04-features/alerts/content-text.md'
---
# Conteúdo: Texto com Variáveis
O bloco de **Texto** envia uma mensagem escrita pelo usuário, com **variáveis dinâmicas** que são resolvidas no momento do disparo (substituídas por números reais do banco).
É o bloco mais simples e versátil: útil para introdução, contexto, marcação de período, números-chave em destaque.
> \[!TIP]
> Texto é "burro" no bom sentido: você controla cada palavra. Use-o quando o formato é fixo e você só quer plugar valores. Para análise interpretativa, use o bloco de [IA](./content-ai.md).
***
## Estrutura
```
Texto livre com {{variavel_1}} e {{variavel_2}} no meio.
Você pode escrever múltiplas linhas, parágrafos, listas, o que fizer
sentido pro canal de destino.
Total do mês: {{venda_total}}
Maior cliente: {{top_cliente}} ({{top_cliente_valor}})
```
Cada `{{nome}}` é uma **variável** definida na configuração do bloco. No momento do envio, o Lumo busca o valor no banco e substitui.
***
## Definindo Variáveis
Cada variável tem:
| Campo | Descrição |
|---|---|
| **Nome** | Identificador usado entre `{{}}` no texto |
| **Aplicação** | Aplicação de onde o valor vem |
| **Coluna** | Coluna a buscar (com a agregação desejada: soma, média, contagem, etc.) |
| **Filtros** | Opcional. Segmenta o cálculo (mesmo editor de filtros usado em relatórios) |
| **Formato** | Padrão, Número, Moeda, Percentual, Reduzido, Inteiro |
### Exemplo: Variável "venda\_total"
```
Nome: venda_total
Aplicação: Vendas
Coluna: Valor da Venda (Soma)
Filtros: data entre Ontem e Ontem
Formato: Moeda
```
Resultado no momento do disparo: `R$ 145.230,80`
***
## Formatos Disponíveis
| Formato | Entrada | Saída |
|---|---|---|
| **Padrão** | 1234.5678 | `1234.5678` |
| **Número** | 1234.5678 | `1.234,57` |
| **Moeda** | 1234.5678 | `R$ 1.234,57` |
| **Percentual** | 0.1234 | `12,34%` |
| **Reduzido** | 1234567 | `1,23M` |
| **Inteiro** | 1234.5678 | `1.235` |
> \[!NOTE]
> Variáveis que retornam **texto** (dimensões como nome do cliente, categoria, ID) ignoram o campo de formato. Variáveis numéricas (com agregação como soma ou média) respeitam o formato.
***
## Variáveis em Disparo Condicional
Quando o alerta é [condicional](./conditions.md) com agrupamento, as variáveis podem usar **placeholders especiais** que se referem ao grupo atual:
```
🚨 {{count_total}} vendas com margem negativa em {{grupo_filial}}!
Período: {{periodo}}
Prejuízo acumulado: {{prejuizo_total}}
```
Onde `{{grupo_filial}}` é o valor do agrupamento da iteração corrente. O Lumo dispara a substituição para cada grupo separadamente.
***
## Boas Práticas
### ✅ Use texto para contexto, IA para análise
```
Texto:
"📊 Vendas de {{periodo}}: {{venda_total}}"
IA:
"Briefing: explicar variação vs período anterior, destacar produtos
em queda, recomendar ações."
```
O texto entrega os números frios. A IA explica o porquê.
### ✅ Versione formatos sensíveis ao canal
WhatsApp e Email têm renderizações diferentes. Para mensagens que vão pros dois, escreva o texto de forma que seja legível em ambos (evite tabelas complexas em texto livre, prefira listas simples).
### ❌ Evite textos longos
Mais de aproximadamente 500 caracteres no WhatsApp começa a ser cortado em múltiplas mensagens, perdendo o impacto. Para conteúdo longo, use [Relatório](./content-report.md) (anexo) ou [IA](./content-ai.md) (análise).
### ❌ Não jogue muitos números soltos
5+ variáveis no mesmo texto vira lista difícil de ler no celular. Considere dividir em blocos ou migrar para [Relatório](./content-report.md).
***
## Variáveis vs IA: Quando Usar Cada Um
| Cenário | Use |
|---|---|
| "Total: R$ X" (número fixo, formato fixo) | Variável |
| "X é Y% acima da média histórica" (comparativo) | IA |
| "Top 5: A, B, C, D, E" (lista que muda) | Variável (dimensão com limite) ou Relatório |
| "Tendência preocupante porque..." (interpretação) | IA |
***
## Próximos Passos
* Combine texto com [IA](./content-ai.md) ou [Relatório](./content-report.md) no mesmo alerta
* Configure os [canais de entrega](./channels.md)
* Para dados visuais, use [Gráfico](./content-chart.md)
---
---
url: 'https://docs.horusbi.com.br/dw/desks/controle-de-acesso.md'
---
# Controle de Acesso às Mesas
As **Mesas** são os containers do Horus, mas elas não seguem todas o mesmo modelo de acesso. Existe uma diferença fundamental — e fácil de errar — entre os dois tipos de mesa: elas partem de **estados opostos**. Entender isso é o que evita tanto o susto de "por que todo mundo está vendo essa tabela?" quanto o de "publiquei o dashboard mas ninguém consegue abrir".
Esta página é a fonte de verdade sobre como o acesso funciona em cada natureza de mesa, quem passa por cima do quê, e como Publicar, Lixeira e Writeback herdam essas mesmas regras.
***
## 🗄️ Duas naturezas, dois modelos opostos
Toda mesa é de uma de duas naturezas, e cada natureza tem uma **regra padrão de acesso invertida** em relação à outra:
| | **Mesa de Aplicação** | **Mesa de Dados** |
|:--|:--|:--|
| **O que guarda** | Dashboards e Aplicações de BI | Tabelas e Dataflows do Data Warehouse |
| **Modelo de acesso** | **Lista de permissão** (allow-list) | **Lista de bloqueio** (deny-list / opt-out) |
| **Estado inicial** | Nasce **trancada** | Nasce **aberta** |
| **Quem vê por padrão** | **Ninguém** — até receber acesso explícito | **Todos** — até receber um bloqueio explícito |
| **Como se dá acesso** | Concedendo acesso (por usuário ou grupo) | Já está concedido — nada a fazer |
| **Como se restringe** | Não concedendo (ou removendo o acesso) | Adicionando um **bloqueio explícito** (por usuário ou grupo) |
A **Mesa de Aplicação** é a intuição tradicional: nada é visível até você liberar. Já a **Mesa de Dados** funciona ao contrário — o dado nasce disponível para todo mundo do tenant, e você só o esconde quando adiciona um bloqueio deliberado.
> \[!IMPORTANT]
> **Mesa de Dados nasce visível a todos.** Não existe "liberar acesso" a uma Mesa de Dados — ela já está liberada por padrão para todos os usuários do tenant. O único controle é a **restrição**: enquanto ninguém for bloqueado, todos veem suas tabelas e dataflows. Se você acha que uma Mesa de Dados está "aberta demais", o caminho não é procurar quem tem acesso — é adicionar bloqueios a quem **não** deveria ter.
O bloqueio de uma Mesa de Dados é feito na aba **Acesso aos Dados** do usuário ou do grupo, adicionando aquela mesa à sua lista de bloqueio. A partir daí, aquele usuário (ou todos os membros daquele grupo) deixa de enxergar o conteúdo daquela mesa.
***
## 🚪 Quem passa por cima (bypass)
O **Admin do Tenant** tem poderes elevados. O bypass dele **não é total** — ele vale para o allow-list de BI, mas para no bloqueio de dados. É o ponto mais importante desta página:
| Regra | **Mesa de Aplicação** (allow-list) | **Mesa de Dados** (deny-list) |
|:--|:--|:--|
| **Usuário comum** | Vê só as mesas às quais recebeu acesso | Vê todas, **menos** aquelas em que foi bloqueado |
| **Admin do Tenant** | **Vê todas** (bypass do allow-list) | Vê todas, **menos** aquelas em que foi bloqueado |
Em outras palavras:
* **Mesa de Aplicação** — o Admin do Tenant **ignora o allow-list** e enxerga todas as Aplicações. O único freio é o bloqueio explícito de uma Aplicação específica (a **Aplicação Bloqueada**, que também é honrada por ele).
* **Mesa de Dados** — o **bloqueio é uma parede dura para todos os papéis**, Admin do Tenant inclusive. Se um administrador (ou um grupo do qual ele participa) foi explicitamente bloqueado de uma Mesa de Dados, ele **não** a vê — nem no consumo dos dados, nem na Lixeira, nem no Writeback. Não há bypass para o bloqueio de dados.
> \[!WARNING]
> O bloqueio de Mesa de Dados **não tem exceção de administrador**. Se você bloqueou uma Mesa de Dados para um grupo e um Admin do Tenant é membro desse grupo, esse Admin perde o acesso àquela mesa como qualquer outro usuário. Isso é proposital — é o que garante que dados sensíveis fiquem realmente fechados, e não apenas fechados "para quem não é chefe".
***
## 📤 Publicar é uma permissão, não um privilégio de admin
Publicar um app ou uma tabela em uma mesa é uma **permissão concedível** — a função **Publicar**, na matriz de Funções do Sistema. Ela **não** é exclusiva de administrador.
* **Quem pode publicar** — qualquer usuário ou grupo que tenha a permissão **Publicar** no módulo correspondente (Aplicações, Tabelas, Mesas Publicadas)
* **Onde pode publicar** — apenas **nas mesas às quais tem acesso**. A permissão de Publicar libera o verbo; o acesso à mesa define o destino. Você não publica em uma mesa que não enxerga.
Ou seja: um analista sem qualquer papel administrativo pode receber a permissão de Publicar e passar a publicar suas entregas — sempre restrito às mesas dentro do seu escopo de acesso.
***
## 🗑️ A Lixeira respeita as mesmas permissões
A **Lixeira** (Arquivo) não é uma lista global. Ela é **escopada por mesa**: você só vê, restaura ou baixa o backup de itens — Aplicações, Tabelas e Fluxos — que pertencem a mesas às quais você tem acesso.
* **Item de Mesa de Aplicação** — segue o allow-list: só aparece na sua Lixeira se você tem acesso àquela Mesa de Aplicação (ou é Admin do Tenant, que enxerga todas)
* **Item de Mesa de Dados** — segue o bloqueio: se você foi bloqueado daquela Mesa de Dados, o item **não** aparece na sua Lixeira, e o bloqueio vale também para o Admin do Tenant
Isso significa que a Lixeira nunca é uma porta dos fundos: um usuário bloqueado de uma Mesa de Dados não recupera nem baixa o backup de uma tabela daquela mesa pela Lixeira.
Detalhes de retenção, restore e backup local estão em [Arquivo (visão geral)](/hec/archive).
***
## 🔒 Writeback e Cadastros também honram o bloqueio
O consumo de dados via **Writeback / Cadastros** soma duas camadas de controle. Além do grant de **Datamart** (que decide se você lê e exporta as linhas), o **bloqueio de Mesa de Dados é honrado**:
> \[!CAUTION]
> Um usuário **bloqueado** na Mesa de Dados **não lê nem exporta** as linhas daquela mesa — mesmo que tenha um grant de Datamart sobre a tabela. O bloqueio de mesa **prevalece sobre** o grant de datamart. Conceder acesso ao datamart não desfaz um bloqueio de mesa.
Ou seja, para um usuário efetivamente ler/exportar via Writeback, ele precisa **ao mesmo tempo** ter o grant de datamart **e** não estar bloqueado na Mesa de Dados.
***
## 🔑 Resumo
* **Mesa de Aplicação = allow-list** — nasce trancada, ninguém vê até receber acesso. O Admin do Tenant faz bypass e vê tudo (menos Aplicações explicitamente bloqueadas).
* **Mesa de Dados = deny-list** — nasce aberta, todos veem até que um bloqueio explícito seja adicionado. O bloqueio é uma **parede dura para todos**, Admin do Tenant inclusive.
* **Publicar** é permissão concedível, restrita às mesas às quais você tem acesso — não é exclusiva de admin.
* **Lixeira** e **Writeback** herdam as mesmas regras: você só alcança o que suas mesas permitem, e o bloqueio de dados vale em todos os caminhos.
***
**Veja também:**
* [Gestão de Mesas (HEC)](/hec/desks/mesas)
* [Permissões e Segurança](/hec/users-groups/permissions)
* [Segurança e Governança do DW](/dw/architecture/security)
* [Arquivo (Lixeira)](/hec/archive)
* [Arquivo de Tabelas](/dw/tables/archive) · [Arquivo de Aplicações](/dataviz/02-apps/archive)
---
---
url: 'https://docs.horusbi.com.br/lumo/referencia/credential.md'
description: >-
Referência do YAML de credencial no Lumo: propriedades, tipos, drivers
suportados e um exemplo completo.
---
# Credential
Uma credencial é a conexão com um banco ou uma API. Ela vive em `credentials/--.yaml` e é referenciada pelos nós de extração do flow.
```bash
lumo new credential --type postgres --name "ERP Produção"
```
Ligue o autocomplete no editor colando esta linha no topo do arquivo:
```yaml
# yaml-language-server: $schema=https://docs.horusbi.com.br/schemas/v2/credential.schema.json
```
O nó de extração do flow aponta para a credencial pela **chave**, não pelo id. Descubra a chave com `lumo list credential`.
## Modelo de configuração
```yaml
# ── header ────────────────────────────────────────────────
id: integer # obrigatório, >= 1
kind: credential # obrigatório, literal "credential"
lumo: v2 # obrigatório, literal "v2"
tenantId: integer # obrigatório, >= 1
---
# ── body ──────────────────────────────────────────────────
nome: string # obrigatório, mínimo 1 caractere
type: string # obrigatório: mysql | postgresql | bigquery | http | sqlserver
# | oracle | firebird | informix | odbc | iris
password: string # somente-escrita: o servidor guarda e nunca devolve no GET
tags: [string]
config: # obrigatório. aceita chaves extras conforme o driver
host: string
port: integer # 1 a 65535
database: string
username: string
password: string
```
O `password` some do arquivo depois de um `lumo pull`, porque o servidor nunca devolve o segredo. Isso é esperado. Preencha na criação ou quando for rotacionar, e omita nos pushes seguintes para manter o valor que já está no servidor.
## Configuração completa
```yaml
# credentials/erp-producao--944.yaml
# yaml-language-server: $schema=https://docs.horusbi.com.br/schemas/v2/credential.schema.json
id: 944
kind: credential
lumo: v2
tenantId: 853
---
nome: ERP Produção
type: postgresql
config:
host: db.interno.exemplo.com
port: 5432
database: erp
username: horusbi_ro
# Preencha só na criação ou na rotação do segredo. Omita nos pushes
# seguintes: o servidor mantém o valor que já tem.
password: "{{ senha }}"
tags: [erp]
```
Use um usuário só de leitura na origem. O ETL nunca escreve no sistema de origem.
## Especificação: header
### `id`
**Tipo:** integer (>= 1) · **Obrigatório:** sim
### `kind`
**Tipo:** string · **Obrigatório:** sim · **Valor:** `credential`
### `lumo`
**Tipo:** string · **Obrigatório:** sim · **Valor:** `v2`
### `tenantId`
**Tipo:** integer (>= 1) · **Obrigatório:** sim
## Especificação: body
### `nome`
**Tipo:** string (mínimo 1 caractere) · **Obrigatório:** sim
Nome exibido da credencial.
### `type`
**Tipo:** string · **Obrigatório:** sim
Driver da conexão. Os valores aceitos:
| Valor | Origem |
|---|---|
| `postgresql` | PostgreSQL |
| `mysql` | MySQL |
| `sqlserver` | SQL Server |
| `oracle` | Oracle |
| `firebird` | Firebird |
| `informix` | Informix |
| `iris` | InterSystems IRIS |
| `odbc` | Qualquer origem via ODBC |
| `bigquery` | Google BigQuery |
| `http` | API HTTP |
Qualquer outro valor é rejeitado.
### `config`
**Tipo:** object · **Obrigatório:** sim
Parâmetros da conexão. As chaves comuns a bancos relacionais:
| Campo | Tipo | O que é |
|---|---|---|
| `host` | string | Host do servidor. |
| `port` | integer (1 a 65535) | Porta. |
| `database` | string | Banco a conectar. |
| `username` | string | Usuário. |
| `password` | string | Senha. Somente-escrita, igual à do body. |
`config` aceita chaves adicionais específicas de cada driver, e o schema não as restringe. Rode `lumo scaffold credential --variant ` para ver as chaves de um driver antes de escrever o arquivo.
```yaml
config:
host: db.interno.exemplo.com
port: 5432
database: erp
username: horusbi_ro
```
### `password`
**Tipo:** string · **Obrigatório:** não
Senha da conexão. Somente-escrita: o servidor guarda o valor e nunca o devolve numa leitura, então ele não reaparece depois de um `lumo pull`.
Preencha na criação ou para rotacionar o segredo. Omita nos pushes seguintes para preservar o valor no servidor.
### `tags`
**Tipo:** array de string · **Obrigatório:** não · **Default:** `[]`
## Campos gerenciados pelo servidor
| Campo | O que é |
|---|---|
| `version` | Versão do recurso. |
| `criado_em`, `criado_por`, `publicado_em`, `publicado_por` | Auditoria. |
---
---
url: 'https://docs.horusbi.com.br/dataviz/01-getting-started/create-app.md'
---
# Criando a Primeira Aplicação
Este guia conduz desde a criação da primeira Aplicação até a visualização de um Dashboard interativo.
## ✅ Pré-requisitos
* Ter acesso ao **Horus DataViz**
* Ter permissão de criação de Aplicações na Mesa correspondente
* Existirem Tabelas publicadas no **HorusDW** (para que haja dados disponíveis para visualização)
> \[!TIP]
> Ainda não existem dados no DW? Carregue uma planilha via [Upload Excel](/dw/getting-started/upload) ou crie um Pipeline via [HorusETL](/etl/getting-started/). Consulte o [Guia completo: Do Dado ao Dashboard](/guia/jornada).
***
## 📦 1. Criar uma Aplicação
A **Aplicação** é o contêiner onde Relatórios, Dashboards e toda a lógica analítica ficam organizados.
1. Acesse a Home do DataViz.
2. No primeiro acesso, será exibido um botão de destaque **"Criar Primeira Aplicação"**.
3. Alternativamente, acesse o menu **Mesas** > **Minha Mesa**.
4. Clique no botão **"Nova Aplicação"** (ícone `+`).
5. Defina um **Nome** ("Análise de Vendas") e uma **Cor/Ícone** para facilitar a identificação.
6. Clique em **Salvar**. A plataforma redirecionará para a tela de edição da Aplicação.

***
## 🔗 2. Conectar Dados (Modelagem)
Antes de criar gráficos, é necessário indicar à Aplicação quais dados ela deve consumir.
1. No menu superior de edição, clique na aba **Tabelas**.
2. No painel à esquerda ("Tabelas Disponíveis"), será exibida a lista de todas as Tabelas disponíveis, organizadas por Mesa.
3. Utilize o campo de busca para encontrar a Tabela desejada (`Fato Vendas`).
4. Clique no ícone **`+`** ao lado do nome da Tabela para adicioná-la.
5. A Tabela aparecerá na área central (Canvas de Modelagem).
* *Nota*: Ao adicionar mais de uma Tabela, lembre-se de criar o **Relacionamento** (Join) entre elas, arrastando um ponto de conexão de uma para a outra.
6. Clique no botão **Salvar** (ícone disquete) no topo direito.

***
## 🎨 3. Criar Visualizações (Dashboards)
Com os dados conectados, é hora de criar o visual da análise.
1. No menu superior, clique na aba **Dashboards**.
2. Caso ainda não exista nenhum dashboard criado, a tela exibirá uma mensagem informativa. Clique no botão **Criar novo Dashboard**.

3. Uma grade vazia será exibida, este é o *Canvas*, a área onde os componentes visuais serão posicionados.
4. No painel lateral direito, localize a lista de Widgets disponíveis (KPI, Barras, Pizza, Tabela, etc.).
5. **Arraste e solte** o Widget desejado (por exemplo: "Barras") para dentro do *Canvas*.
6. O painel de configuração do Widget será aberto automaticamente. Preencha os campos essenciais:
* **Título**: Dê um nome ao gráfico
* **Tabela**: Selecione a Tabela conectada no passo anterior
* **Eixo X (Dimensão)**: Escolha o campo descritivo (`Nome Vendedor` ou `Data`)
* **Eixo Y (Métrica)**: Escolha o campo numérico (`Valor Total`) e a operação de agregação (`SOMA`)
7. O gráfico será atualizado em tempo real na tela.
8. Após finalizar, feche o painel de configuração. Você pode redimensionar o Widget puxando pelas bordas diretamente no Canvas.

***
## 📤 4. Visualizar e Compartilhar
1. Após salvar o Dashboard, clique no ícone **"Visualizar"** (olho) no menu superior ou volte para a Home.
2. A Aplicação agora está funcional na "Minha Mesa".
3. Para compartilhar com outros usuários, é necessário **publicar** a Aplicação em uma Mesa Compartilhada (processo realizado via HEC).
> \[!WARNING]
> A publicação é obrigatória para que outros usuários visualizem a Aplicação. Acesse **HEC > Recursos > Conteúdo** para [publicar a Aplicação](/hec/resources/01-content) e depois [configure as permissões](/hec/users-groups/permissions) de acesso às Mesas. Consulte o [passo a passo completo](/guia/jornada#etapa-5-publicar-aplicação-e-dar-acesso).
> \[!NOTE]
> Publicar exige a permissão **Publicar** (concedível, **não** exclusiva de administrador), e você publica apenas **nas Mesas às quais tem acesso**. Ver **[Controle de Acesso às Mesas](/dw/desks/controle-de-acesso)**.
---
---
url: 'https://docs.horusbi.com.br/dw/tables/cadastros/criar.md'
---
# Criar e estruturar
> **Caminho**: Minha Mesa > Novo Cadastro
Aqui você monta o **esqueleto** do Cadastro: quais **campos** ele tem, de que **tipo** são e que **regras** cada um segue. A estrutura é definida no DW e vale para todos que vão usar o Cadastro depois.
***
## ➕ Campos e tipos
Cada campo tem um **tipo**, que define o que ele aceita e como aparece na grade e no formulário. São seis tipos:
| Tipo | O que guarda | Quando usar |
|------|--------------|-------------|
| **Texto** | Texto livre | Nomes, descrições, observações |
| **Número** | Valores numéricos | Metas, quantidades, pesos, valores |
| **Data** | Datas, com **granularidade** ajustável | Competências, prazos, marcos |
| **Lista de valores** | Uma opção de uma lista fixa | Status, categorias, classificações |
| **Chave externa** | Referência a um registro de **outra tabela** | Vincular a clientes, produtos, filiais |
| **Imagem** | Um arquivo de imagem | Logos, fotos, anexos visuais |
Detalhes que valem conhecer:
* **Data** — você escolhe a **granularidade**: **ano**, **mês**, **dia** ou **data-hora**. Isso controla o que o usuário preenche e como o BI agrupa.
* **Lista de valores** — é uma *picklist* de seleção única. Cada opção pode ter uma **cor**, e na grade o valor aparece como uma **pílula colorida** — ótimo para ler status de relance.
* **Chave externa** — aponta para outra tabela. Na hora de preencher, você **busca pelo rótulo** (o nome legível), não por um código. Se o registro apontado deixar de existir, o valor aparece como **`#chave`** na grade, sinalizando um vínculo órfão.
* **Imagem** — o usuário faz **upload**; na grade e no formulário a imagem abre em **lightbox** (visualização ampliada).
***
## 🏷️ Rótulo e nome interno
Ao criar um campo, você digita só o **rótulo** — o nome visível, em linguagem de negócio (ex.: *"Meta de Vendas"*).
A partir dele, a plataforma deriva automaticamente um **nome interno** (ex.: `meta_de_vendas`), usado nos bastidores e nas expressões. Esse nome interno é **editável**, caso você queira ajustá-lo.
::: info Renomear não perde dado
Trocar o **rótulo** de um campo depois é uma operação **não-destrutiva** — os dados já preenchidos continuam intactos. Veja como isso funciona em **[Versões e publicação](./versoes-publicar)**.
:::
***
## ⚙️ Atributos do campo
Cada campo aceita alguns atributos que refinam como ele se comporta:
* **Obrigatório** — o registro não pode ser salvo sem esse campo preenchido.
* **Máscara** — define o formato de exibição/digitação (ex.: moeda, percentual, formato de data).
* **Seção** — agrupa campos relacionados no **formulário**, deixando o preenchimento mais organizado quando há muitos campos.
***
## 🔑 Unicidade
A unicidade é definida **a nível de tabela**, como **combinações de campos** que não podem se repetir — não existe "único por campo" isolado.
Por exemplo, num Cadastro de metas você pode exigir que a combinação **Filial + Mês** seja única: cada filial tem no máximo uma meta por mês, mas o mesmo mês pode aparecer várias vezes (uma por filial) e a mesma filial pode aparecer em vários meses.
> \[!NOTE]
> Defina como única a **combinação mínima** que identifica um registro de verdade. Combinações boas evitam duplicatas; combinações largas demais deixam passar dados repetidos.
***
::: tip Lista de valores ou Chave externa?
Use **Lista de valores** quando as opções são **poucas e estáveis**, vivem só dentro deste Cadastro e você quer cor por opção (status, classificações). Use **Chave externa** quando o valor é uma **entidade que já existe em outra tabela** (um cliente, um produto, uma filial) e você quer manter o vínculo — inclusive para o BI cruzar as duas tabelas.
:::
---
---
url: 'https://docs.horusbi.com.br/dataviz/04-features/personal-dashboards.md'
---
# Dashboards Pessoais
Os **Dashboards Pessoais** permitem que usuários finais criem suas próprias visualizações dentro de Aplicações publicadas, sem depender de equipes técnicas. Um técnico cria a Aplicação com toda a infraestrutura necessária (tabelas, expressões, relacionamentos e filtros), e o usuário final aproveita essa base pronta para montar visões personalizadas de acordo com suas necessidades — cada um enxerga apenas suas próprias Dashboards, sem afetar a Aplicação original nem outros usuários.
***
## 🔓 Habilitando Dashboards Pessoais
Por padrão, a funcionalidade está desabilitada. Para ativá-la:
1. Acesse **HEC > Tenants** e edite o tenant desejado
2. Vá para a aba **Avançado**
3. Ative a opção **"Permitir usuários criarem Dashboards pessoais em aplicações publicadas"**
4. (Opcional) Ative **"Permitir usuários compartilharem Dashboards pessoais"** para permitir colaboração entre usuários
> \[!IMPORTANT]
> Dashboards Pessoais só podem ser criados em **Aplicações publicadas**. Aplicações em "Minha Mesa" (não publicadas) não possuem essa funcionalidade.
***
## ✨ Criando um Dashboard Pessoal
### Acessando a Funcionalidade
Ao abrir uma Aplicação publicada, a barra de abas de Dashboards estará visível. Se a funcionalidade estiver habilitada pelo administrador, um botão **"+"** aparecerá na barra, permitindo criar sua primeira visão pessoal.
### Criando uma Nova Visão
1. Clique no botão **"+"** na barra de Dashboards
2. Escolha o tipo de criação:
| Opção | Descrição |
|-------|-----------|
| **Criar do Zero** | Uma Dashboard vazia para montar livremente |
| **Clonar Existente** | Copia uma Dashboard original ou pessoal como ponto de partida |
3. Dê um **nome** para sua Dashboard
4. Clique em **Criar**
> \[!TIP]
> Se a Dashboard original já tem algo próximo do necessário, prefira **Clonar** e ajustar ao invés de criar do zero — isso economiza tempo e mantém a consistência visual.
### Editando sua Dashboard
Após criar, é possível entrar no **Modo de Edição** para personalizar o conteúdo:
1. Clique no botão **Editar** (ícone de lápis) no canto superior direito
2. O modo de edição disponibiliza as mesmas ferramentas da criação de Dashboards convencional:
* Adicione Widgets arrastando da sidebar
* Configure cada Widget com as colunas e expressões disponíveis na Aplicação
* Reposicione e redimensione Widgets livremente no grid
3. Clique em **Salvar** para persistir suas alterações
> \[!NOTE]
> Só é possível editar Dashboards de própria autoria (onde o usuário é o "dono"). Dashboards compartilhadas por outros usuários são exibidas em modo somente leitura.
***
## 📋 Gerenciando e Compartilhando Dashboards
### Menu de Opções
Clique com o **botão direito** em uma aba de Dashboard pessoal para acessar o menu de opções:
| Ação | Descrição |
|------|-----------|
| **Renomear** | Altera o nome da Dashboard |
| **Compartilhar** | Abre a modal para compartilhar a Dashboard com outros usuários do tenant |
| **Excluir** | Remove a Dashboard permanentemente |
### Compartilhando com Outros Usuários
Se o administrador habilitou a opção de compartilhamento, é possível compartilhar visões pessoais com outros usuários do mesmo tenant:
1. Clique com o botão direito na aba da Dashboard
2. Selecione **Compartilhar**
3. Selecione os usuários que deseja incluir
Na modal de compartilhamento, as seguintes ações estão disponíveis:
| Ação | Descrição |
|------|-----------|
| **Adicionar usuários** | Selecione novos usuários para compartilhar a Dashboard |
| **Remover acesso** | Remove o compartilhamento de um usuário específico |
| **Transferir propriedade** | Passa a posse da Dashboard para outro usuário |
### Transferência de Propriedade
Apenas o **dono** (owner) de uma Dashboard pessoal pode editá-la. Se for necessário que outra pessoa assuma o controle:
1. Abra a modal de **Compartilhar**
2. Localize o usuário na lista de compartilhamentos atuais
3. Clique em **Transferir Propriedade**
4. Confirme a ação
> \[!WARNING]
> Ao transferir a propriedade, o usuário **perde** o controle da Dashboard. Ela passará para a seção "Compartilhadas comigo" na visualização do usuário anterior, e não será mais possível editá-la.
### Dashboards Recebidas
Dashboards compartilhadas aparecem na barra de abas com um ícone diferenciado. Ao clicar com o botão direito sobre elas, as opções disponíveis são:
| Ação | Descrição |
|------|-----------|
| **Informações** | Mostra quem compartilhou e quando |
| **Sair da Dashboard** | Remove a Dashboard da sua visualização (não afeta o dono original) |
### Reordenando Abas
No modo de edição, é possível reorganizar a ordem das Dashboards utilizando o botão de **gerenciamento de abas** (ícone de engrenagem). Utilize as setas para mover as abas para esquerda ou direita.
> \[!TIP]
> Nomeie as Dashboards de forma descritiva ("Vendas — Região Sul") e reordene as abas na sequência que faz sentido para o fluxo de trabalho.
***
## 🏷️ Tipos de Dashboards na Barra
A barra de abas exibe três tipos de Dashboards, cada um com um ícone distinto para fácil identificação:
| Tipo | Ícone | Descrição |
|------|-------|-----------|
| **Originais** | (sem ícone) | Dashboards da Aplicação publicada, criadas pelo desenvolvedor |
| **Minhas** | 👤 Roxo | Dashboards pessoais criadas pelo próprio usuário |
| **Compartilhadas** | 👤➡ Verde-azulado | Dashboards que outros usuários compartilharam |
***
## 🔄 Comportamento em Clonagem e Republicação
Ao clonar ou republicar Aplicações, o sistema trata as Dashboards Pessoais da seguinte forma:
> \[!IMPORTANT]
> **Clonagem para "Minha Mesa"**: Quando uma Aplicação publicada é clonada para a mesa do usuário, as visões pessoais **não** são copiadas junto. A Aplicação clonada começa sem Dashboards Pessoais.
> \[!TIP]
> **Republicação**: Quando a Aplicação é republicada (substituindo uma versão existente), as visões pessoais dos usuários **são mantidas**. Isso garante que atualizações na Aplicação não destruam as Dashboards que os usuários já criaram.
---
---
url: 'https://docs.horusbi.com.br/dw/datamarts.md'
---
# Datamarts
Os Datamarts são as **vitrines de negócio** do HorusDW. Eles permitem organizar as tabelas não por "onde estão guardadas" (Mesas), mas por "quem deve vê-las" — agrupando dados por área temática para facilitar o acesso dos usuários.
> \[!IMPORTANT]
> O gerenciamento de Datamarts é exclusivo para **Administradores** e **Donos de Datamarts**.
***
## 🧩 Estrutura: Físico vs. Lógico
No Horus, a estrutura física é separada da lógica de acesso:
| Camada | Função | Quem Gerencia? |
|--------|--------|----------------|
| **Mesa (Desk)** | **Armazenamento** — Onde a tabela está fisicamente guardada | Equipe Técnica / Engenharia de Dados |
| **Datamart** | **Gerenciamento** — Quem é o "dono" da tabela | HEC (Define o Dono do Datamart) |
| **Permissão** | **Acesso** — Quem pode ver ou editar os dados | Dono do Datamart (Tabela a Tabela) |
***
## 🔄 Ciclo de Vida do Acesso
O acesso aos dados passa por três etapas:
1. **Criação (HEC)** — O Datamart é criado no módulo administrativo (HEC). Lá são definidos o **Dono** (Owner) e quais Mesas alimentam esse Datamart
2. **Gestão (DW)** — O Dono acessa o HorusDW para definir regras finas: validade do acesso, filtros de segurança e bloqueio de colunas sensíveis
3. **Consumo (DataViz)** — Os usuários finais acessam o módulo de visualização e enxergam apenas o que foi liberado no Datamart
***
## 🚪 Ponto de Entrada
Para gerenciar seus Datamarts, acesse o menu **Minhas Tabelas**. Esta tela lista todas as tabelas pelas quais você é responsável (seja por ter criado ou por ser dono do Datamart onde elas estão).
Na coluna **"Datamarts"**, você verá em quais vitrines de negócio cada tabela está exposta.
📖 [Gestão detalhada de permissões e segurança](./managing-access)
---
---
url: 'https://docs.horusbi.com.br/dataviz.md'
---
# DataViz (Visualização)
O **Horus DataViz** é a interface de visualização e exploração de dados da Horus. É aqui que os dados transformados pelo ETL e organizados no DW ganham vida em forma de Dashboards interativos, Relatórios detalhados e análises *ad-hoc*.
> \[!NOTE]
> O DataViz consome dados armazenados no **[HorusDW](/dw/intro/)**. Carregue dados via [Upload Excel](/dw/getting-started/upload) ou crie pipelines automatizados com o [HorusETL](/etl/getting-started/). Veja o [Guia da Plataforma](/guia/) para o fluxo completo.
## 📚 Estrutura da Documentação
* **[Introdução](/dataviz/00-intro/)**: Visão geral do módulo de visualização, seus conceitos centrais e o fluxo de criação
* **[Primeiros Passos](/dataviz/01-getting-started/)**: Guias para navegar, criar e consumir seus primeiros Dashboards
* **[Aplicações](/dataviz/02-apps/)**: Documentação completa sobre a criação e edição de Aplicações (Tabelas, Expressões, Dashboards, Explorer e IA)
* **[Widgets](/dataviz/03-widgets/)**: Biblioteca completa de componentes visuais (Gráficos, Tabelas, KPIs, Mapas, etc.)
* **[Funcionalidades](/dataviz/04-features/)**: Recursos avançados como Alertas Inteligentes, Modo Apresentação, Chat com Dados e Dashboards Pessoais
## 🚀 Começar Rápido
1. Aprenda a **[Criar Aplicações](/dataviz/01-getting-started/create-app)** conectadas aos seus dados.
2. Descubra como **[Navegar e Filtrar](/dataviz/01-getting-started/)** em Dashboards existentes.
3. Explore a nossa galeria de **[Widgets](/dataviz/03-widgets/)** para escolher a melhor visualização.
4. Utilize **[Tabelas Poderosas](/dataviz/03-widgets/table)** e **[Visualizações Customizadas](/dataviz/03-widgets/svelte-custom)** para ir além do básico personalizando de acordo com suas necessidades.
---
---
url: 'https://docs.horusbi.com.br/lumo/referencia/desk.md'
description: >-
Referência do YAML de desk no Lumo: dw-desk, bi-desk, propriedades e um
exemplo completo.
---
# Desk
Um desk é uma área compartilhável do tenant. Publicar um recurso num desk é o que o torna visível para os membros daquele desk.
Existem dois tipos, e eles são recursos distintos, com comandos de publicação distintos:
| Kind | O que recebe | Como publicar |
|---|---|---|
| `dw-desk` | Flows e tabelas do DW | `lumo flow publish` |
| `bi-desk` | Apps de BI | `lumo app publish` |
```bash
lumo new dw-desk --name "Operação"
lumo new bi-desk --name "Comercial"
```
Um desk nasce vazio. Ele não lista os recursos que contém: quem carrega o vínculo é o `deskId` de cada recurso publicado.
Ligue o autocomplete no editor colando, no topo do arquivo, a linha do tipo correspondente:
```yaml
# yaml-language-server: $schema=https://docs.horusbi.com.br/schemas/v2/dw-desk.schema.json
```
```yaml
# yaml-language-server: $schema=https://docs.horusbi.com.br/schemas/v2/bi-desk.schema.json
```
## Modelo de configuração
Os dois tipos têm o mesmo corpo. Só muda o `kind` do header.
```yaml
# ── header ────────────────────────────────────────────────
id: integer # obrigatório, >= 1
kind: dw-desk # obrigatório: dw-desk ou bi-desk
lumo: v2 # obrigatório, literal "v2"
tenantId: integer # obrigatório, >= 1
---
# ── body ──────────────────────────────────────────────────
nome: string # obrigatório, mínimo 1 caractere
descricao: string | null
tags: [string]
```
## Configuração completa
```yaml
# dw-desks/operacao--1402.yaml
# yaml-language-server: $schema=https://docs.horusbi.com.br/schemas/v2/dw-desk.schema.json
id: 1402
kind: dw-desk
lumo: v2
tenantId: 853
---
nome: Operação
descricao: Flows e tabelas do DW da operação diária.
tags: [producao]
```
Um bi-desk é idêntico, trocando o `kind`:
```yaml
# bi-desks/comercial--2226.yaml
# yaml-language-server: $schema=https://docs.horusbi.com.br/schemas/v2/bi-desk.schema.json
id: 2226
kind: bi-desk
lumo: v2
tenantId: 853
---
nome: Comercial
descricao: Apps de vendas.
```
## Especificação: header
### `id`
**Tipo:** integer (>= 1) · **Obrigatório:** sim
### `kind`
**Tipo:** string · **Obrigatório:** sim · **Valores:** `dw-desk`, `bi-desk`
Decide o que o desk pode receber e contra qual schema o `lumo lint` valida o arquivo. Um flow publicado com `--desk` apontando para um bi-desk é rejeitado.
### `lumo`
**Tipo:** string · **Obrigatório:** sim · **Valor:** `v2`
### `tenantId`
**Tipo:** integer (>= 1) · **Obrigatório:** sim
## Especificação: body
### `nome`
**Tipo:** string (mínimo 1 caractere) · **Obrigatório:** sim
Nome do desk.
### `descricao`
**Tipo:** string ou null · **Obrigatório:** não
O que o desk agrupa.
### `type`
**Tipo:** string · **Obrigatório:** não · **Valor:** `dw` num dw-desk, `bi` num bi-desk
Discriminador do tipo de desk. É redundante com o `kind` do header, e o servidor o define.
### `tags`
**Tipo:** array de string · **Obrigatório:** não · **Default:** `[]`
## Campos gerenciados pelo servidor
| Campo | O que é |
|---|---|
| `version` | Versão do recurso. |
| `criado_em`, `criado_por`, `publicado_em`, `publicado_por` | Auditoria. |
---
---
url: 'https://docs.horusbi.com.br/dataviz/03-widgets/diagram.md'
---
# Diagrama
O Widget **Diagrama** é uma das ferramentas mais flexíveis do DataViz, permitindo desenhar fluxogramas, mapas de processo, plantas baixas ou qualquer visualização customizada e injetar dados reais da Aplicação nesses elementos.
## 🔧 Funcionalidades
* **Editor Completo**: Acesso total às ferramentas de desenho do Draw.io (formas, setas, ícones, imagens)
* **Vínculo com Dados**: Capacidade de criar variáveis que buscam dados do Dashboard e as exibem dentro do desenho
* **Interatividade**: Suporte a links externos configuráveis (drill-through manual)
***
## ⚙️ Como Configurar
### 1. Criando o Desenho
Na aba **Geral**, clique em **Desenhar Diagrama** para abrir o editor.
* Desenhe seu processo ou visualização
* Para exibir um dado (Valor de Vendas), é necessário usar uma **Variável**
* O formato para usar uma variável no texto de qualquer forma é: {{NomeDaVariavel}}
### 2. Configurando Variáveis
Abaixo do botão de desenho, é possível criar e gerenciar as variáveis.
* **Adicionar nova variável**: Cria um novo vinculador de dados
* **Nome da Variável**: O identificador utilizado no desenho (se o nome for `Vendas`, no desenho utilize {{Vendas}})
* **Configurar Variável**: Abre as opções de dados para esta variável
#### Tipos de Variável:
1. **Aplicação**:
* **Coluna de dados**: Busca o valor de uma métrica (Sum(Valor))
* **Filtros**: Permite filtrar especificamente este dado (Trazer apenas vendas da "Loja A")
* **Formato do Número**: Moeda, Porcentagem, Inteiro, etc.
2. **Expressão**:
* Permite escrever uma fórmula JavaScript para calcular o valor. Veja detalhes avançados abaixo
### 3. Exemplo Prático: Planta de Loja
Imagine que se deseja mostrar o estoque de 3 setores de uma loja em uma planta baixa.
1. Crie 3 variáveis: `EstoqueA`, `EstoqueB`, `EstoqueC`.
2. Configure cada uma filtrando pelo respectivo setor.
3. Abra o **Desenhar Diagrama**.
4. Desenhe 3 retângulos representando os setores.
5. Dentro de cada retângulo, escreva o texto: "Setor A: {{EstoqueA}}".
6. Salve. Ao visualizar o Dashboard, {{EstoqueA}} será substituído pelo número real.
***
## 🎯 Aba Comportamento
* **Gerar Link Externo**: Transforma todo o Widget em um botão clicável que leva para uma URL externa (abrir um sistema legado ou página da intranet)
* **Gráfico com fundo transparente**: Remove o fundo branco, útil para integrar o desenho visualmente com o fundo do Dashboard
***
## 🎨 Configuração Visual
* **Aparência**: O diagrama pode ter fundo `Padrão`, `Transparente` (para se misturar ao fundo do Dashboard) ou `Destacado`
* **Alinhamento**: O desenho pode ser alinhado à esquerda, centro ou direita dentro do contêiner
***
## 💻 Programação Avançada (JavaScript)
Ao selecionar o tipo de variável **Expressão**, há acesso total a um ambiente JavaScript assíncrono para buscar e manipular dados.
> \[!IMPORTANT]
> O código roda dentro de uma função `async`. É possível usar `await` livremente.
> O contexto (`this`) contém ferramentas para acessar a Aplicação e bibliotecas utilitárias.
### O Objeto `this`
O contexto da função expõe os seguintes objetos principais:
* **`this.app`**: Acesso à loja da Aplicação atual (dados, filtros, metadados)
* **`this.variables`**: Acesso ao valor de outras variáveis já calculadas neste Widget
* **`this.datefns`**: Biblioteca [date-fns](https://date-fns.org/) completa para manipulação de datas
### 1. Buscando Dados Internos (`this.app`)
É possível usar `this.app.fetchMatrix` para buscar qualquer dado da Aplicação, independentemente do que está na tela.
> \[!TIP]
> Ao referenciar colunas, é **obrigatório** incluir a expressão de agregação (`:SUM`, `:AGP`), caso contrário o filtro falhará.
```javascript
// Exemplo: Buscar o total de vendas de um vendedor específico
const dados = await this.app.fetchMatrix({
columns: [
'[Vendas]."VALOR_TOTAL":SUM' // Coluna calculada
],
filters: [{
column: '[Vendas]."NOME_VENDEDOR":AGP', // Coluna de filtro
operator: "=",
value: "João Silva"
}]
});
// O resultado vem em uma matriz 2D (linhas x colunas)
if (dados.Result && dados.Result.length > 0) {
const valor = dados.Result[0][0]; // Primeira linha, primeira coluna
// Formatando o valor usando a máscara da coluna
const colunaMetadata = dados.Columns[0];
return this.app.formatValue(valor, colunaMetadata);
}
return "Sem Vendas";
```
### 2. Buscando Dados Externos (`fetch`)
Como o ambiente é JavaScript padrão, é possível buscar dados de APIs externas, como previsão do tempo, cotação de moedas ou status de servidores.
```javascript
// Exemplo: Buscar a temperatura atual de uma API pública
try {
const response = await fetch('https://api.weatherapi.com/v1/current.json?q=Sao Paulo');
const data = await response.json();
return data.current.temp_c + "°C";
} catch (error) {
return "Erro de conexão";
}
```
### 3. Usando Outras Variáveis
É possível combinar variáveis simples para criar cálculos complexos.
```javascript
// Exemplo: Calcular Ticket Médio baseado em duas outras variáveis do widget
// Variáveis já criadas: {{TotalVendas}} e {{QtdPedidos}}
// Importante: Variáveis de aplicação podem vir formatadas (string com R$),
// então é bom garantir que sejam números para cálculo.
const vendas = this.variables['TotalVendas']; // Supondo que retorne o valor bruto
const pedidos = this.variables['QtdPedidos'];
if (pedidos > 0) {
return (vendas / pedidos).toFixed(2);
}
return 0;
```
### 4. Manipulação de Datas (`this.datefns`)
```javascript
// Exemplo: Retornar o nome do mês atual em português
const hoje = new Date();
return this.datefns.format(hoje, 'MMMM', { locale: this.datefns.locale.ptBR });
// Resultado: "dezembro"
```
### 5. Ordem de Execução e Dependências
É comum querer usar o resultado de uma variável dentro de outra. Para isso, é fundamental entender a ordem em que o sistema calcula os dados:
1. **Variáveis de Aplicação (Dados)**: São **sempre** calculadas primeiro.
* *Consequência*: Uma variável de *Expressão* pode acessar qualquer variável de *Aplicação* com segurança.
2. **Variáveis de Expressão (JavaScript)**: São calculadas em seguida, **na ordem em que aparecem na lista** de configuração.
* *Consequência*: Se a `ExpressaoB` depende da `ExpressaoA`, a `ExpressaoA` deve aparecer **antes** (acima) na lista de variáveis do Widget.
Se houver tentativa de acessar `this.variables['Futura']` (uma variável que ainda não foi calculada), o valor será `undefined`.
---
---
url: 'https://docs.horusbi.com.br/dataviz/04-features/alerts/conditions.md'
---
# Disparo Condicional
O alerta de tipo **condicional** monitora os dados continuamente e só dispara **quando uma regra é atendida**. Diferente do [agendado](./triggers.md), o disparo aqui depende do que está nos dados, não do relógio.
> \[!TIP]
> Se a consulta da regra retorna 0 registros, silêncio total. Se retorna 1 ou mais, o alerta dispara. Esse padrão "exception-only" é o que diferencia um alerta condicional de um relatório agendado.
***
## Como Funciona
```mermaid
flowchart LR
A[Atualização de tabela / agendamento] --> B{Roda consulta}
B --> C[Retorna registros]
C -->|com resultados| D[Dispara alerta]
C -->|sem resultados| E[Silêncio]
```
O Lumo monta uma consulta com **aplicação + filtros + agrupamentos** definidos no alerta, executa periodicamente, e dispara se houver linhas no resultado.
***
## Componentes
### Aplicação Alvo
Toda condição se ancora em **uma aplicação**. É sobre os dados dessa aplicação que a regra será avaliada.
### Filtros
A regra propriamente dita. Usa o **mesmo editor de filtros** que você já conhece de relatórios, dashboards e bookmarks.
**Exemplos comuns:**
| Cenário | Regra |
|---|---|
| Margem negativa | "Margem é menor que zero" |
| SLA estourado | "Tickets sem resolução há mais de 48 horas" |
| Estoque crítico | "Quantidade em estoque abaixo do mínimo cadastrado" |
| Transação suspeita | "Valor acima de R$ 50.000 e status pendente" |
| Venda cancelada | "Status é 'cancelado' e data de cancelamento é de hoje" |
Você monta esses filtros visualmente, sem precisar escrever código.
> \[!NOTE]
> Filtros de data podem usar **períodos relativos** (Ontem, Últimos 7 dias, Mês Atual, etc.) selecionados num dropdown. Útil para regras como "vendas de ontem com margem negativa", que vão se ajustando automaticamente a cada disparo.
### Agrupamento (uma entrega por grupo)
Opcional. Quando você define uma ou mais **colunas de agrupamento**, o alerta agrupa o resultado por essas colunas e **dispara uma vez por grupo**. Ou seja, em vez de "1 alerta com 50 linhas suspeitas", você recebe "50 alertas, um por filial afetada".
```
Regra: vendas com margem negativa
Agrupar por: filial
Resultado:
- 1 alerta para "Filial Centro" (3 vendas suspeitas)
- 1 alerta para "Filial Sul" (12 vendas suspeitas)
- ...
```
Útil quando o destinatário muda por grupo (cada gerente recebe só os da sua filial) ou quando o conteúdo de IA precisa analisar grupos isoladamente.
***
## Frequência de Verificação
Define com que cadência o Lumo roda a consulta da regra.
| Modo | Quando roda | Uso recomendado |
|---|---|---|
| **A cada hora** | Toda hora cheia | Anomalias em quase tempo real |
| **Diariamente** | Uma vez por dia (madrugada) | Regras de fechamento diário |
| **A cada atualização de tabela** | Toda vez que a tabela alvo é atualizada via ETL | Cadência alinhada com o ciclo de ingestão |
> \[!WARNING]
> Verificação "a cada hora" em aplicações com muitos alertas pode pressionar o banco de dados. Se a regra não precisa de quase tempo real, prefira "diariamente" ou alinhar com atualização de tabela.
***
## Testar a Condição
Toda regra tem um botão **"Testar Condicional"** no editor. Ele executa a consulta **agora** com os filtros atuais e mostra:
* ✅ **Condição atendida**: número de registros encontrados + amostra da tabela
* ⛔ **Condição não atendida**: nenhum registro retornado
Use o teste para validar a regra antes de ativar o alerta. Ajuste filtros ou o período até obter o comportamento esperado.
***
## Cenários Comuns
### Anomalia de Margem
```
Aplicação: Vendas
Regra:
data = Ontem
margem é menor que zero
Agrupar por: filial
Verificação: Diariamente
Conteúdo:
1. Texto: "🚨 {{count}} vendas com margem negativa em {{filial}}"
2. Relatório PDF: detalhamento das vendas
3. IA: análise das causas
Canais: WhatsApp (gerente da filial)
```
Cada gerente recebe **só os alertas da sua filial** porque o agrupamento segmenta a entrega.
### SLA Vencido
```
Aplicação: Tickets de Suporte
Regra:
status diferente de "resolvido"
idade em horas é maior que 48
Verificação: A cada hora
Conteúdo:
1. Texto: "{{count}} tickets fora do SLA"
2. Relatório XLSX: lista completa
Canais: Email (líderes de suporte)
```
Roda a cada hora. Se a fila esvazia (zero registros), o alerta cala até voltar a estourar.
### Meta Não Atingida (Fim do Mês)
```
Aplicação: Metas Comerciais
Regra:
mês = Mês Atual
data = Último dia do mês
atingimento é menor que 100%
Agrupar por: vendedor
Verificação: Diariamente
Conteúdo:
1. Texto + IA: análise individual por vendedor
Canais: WhatsApp (vendedor + gerente)
```
***
## Agendado + Condicional Juntos?
**Não no mesmo alerta.** Um alerta é ou agendado ou condicional, escolhido no momento da criação. Se você precisa de algo híbrido (ex.: "todo dia às 08h, se houver anomalias..."), use **condicional** com verificação "diariamente". O sistema roda diariamente e o filtro decide se dispara.
***
## Próximos Passos
* Configure [blocos de conteúdo](./index.md#tipos-de-conteúdo) para a mensagem
* Ajuste os [canais de entrega](./channels.md)
* Para envios garantidos sem depender de regra, veja [Agendamento](./triggers.md)
---
---
url: 'https://docs.horusbi.com.br/dataviz/04-features/alerts/triggers.md'
---
# Disparo por Agendamento
O alerta de tipo **agendado** dispara em horários fixos definidos pelo usuário, **independentemente do que está nos dados**. É o que você quer para *digests* periódicos, relatórios de fechamento, briefings de equipe.
> \[!TIP]
> Se você precisa enviar **somente quando algo anormal acontece**, use o [Disparo Condicional](./conditions.md). Agendamento é para envios regulares: o conteúdo varia, mas o disparo é garantido.
***
## Frequências Disponíveis
### Diário
Dispara em horários específicos todos os dias (ou em dias da semana selecionados).
| Campo | Descrição |
|---|---|
| **Horário** | HH:MM (ex.: `08:00`, `18:30`), no fuso horário do usuário |
| **Dias da semana** | Lista opcional. Vazio significa todos os dias |
**Exemplos:**
* "Todo dia às 08:00" para digest matinal
* "Segunda a sexta às 18:00" para fechamento do expediente
* "Sábados às 10:00" para revisão semanal
### Semanal
Dispara em um ou mais dias da semana, em horário fixo.
| Campo | Descrição |
|---|---|
| **Dias da semana** | Seleção múltipla: Dom, Seg, Ter, ..., Sáb |
| **Horário** | HH:MM |
**Exemplos:**
* "Segunda-feira às 09:00" para kickoff da semana
* "Quarta e sexta às 16:00" para checkpoints intermediários
### Mensal
Dispara em um dia específico do mês.
| Campo | Descrição |
|---|---|
| **Dia do mês** | 1 a 31. Se o mês não tiver o dia (ex.: 31 em fevereiro), dispara no último dia |
| **Horário** | HH:MM |
**Exemplos:**
* "Dia 1 às 07:00" para relatório de fechamento mensal
* "Dia 15 às 14:00" para meta de meio de mês
***
## Múltiplos Agendamentos no Mesmo Alerta
Um alerta pode ter **vários gatilhos combinados**. Útil quando o mesmo conteúdo precisa sair em horários diferentes:
```
Alerta "KPIs Comercial"
├─ Diário, 08:00 (digest)
├─ Sexta, 18:00 (consolidação semanal)
└─ Dia 1, 09:00 (consolidação mensal)
```
Cada agendamento dispara independentemente. Não há agrupamento de horários próximos.
***
## Fuso Horário
Os horários sempre seguem o **fuso configurado no perfil do usuário criador do alerta**, não o fuso do servidor. Isso garante que "08:00" seja 08:00 *para você*, mesmo que o backend rode em outro fuso.
> \[!NOTE]
> Mudança de fuso horário do usuário **não retroage** em alertas já criados. O alerta segue o fuso de criação até ser editado.
***
## Cenários Comuns
### Digest Matinal de Vendas
```
Disparo: Diário, 07:00 (seg-sex)
Conteúdo:
1. Texto: "Bom dia! Vendas de ontem ({{data_ontem}})"
2. IA: análise comparativa com semana anterior
3. Relatório PDF: top 20 produtos
Canais: WhatsApp (grupo gestores), Email
```
### Fechamento Mensal
```
Disparo: Mensal, dia 1, 06:00
Conteúdo:
1. Texto: "Fechamento {{mes_anterior}}"
2. Gráfico: dashboard de KPIs do mês
3. Relatório XLSX: detalhamento por filial
Canais: Email (todos diretores)
```
### Reminder Semanal de Reunião
```
Disparo: Semanal, segunda, 08:00
Conteúdo:
1. Texto: "Pauta da reunião 10h: revisar metas"
2. Relatório PDF: status atual das metas
Canais: WhatsApp (grupo da reunião)
```
***
## Próximos Passos
* Adicione [blocos de conteúdo](./index.md#tipos-de-conteúdo) ao alerta
* Configure os [canais de entrega](./channels.md)
* Para regras de negócio em vez de horários fixos, veja [Condicional](./conditions.md)
---
---
url: 'https://docs.horusbi.com.br/dataviz/04-features/apresentacoes/editor.md'
---
# Editor de Slides Desenhados
O **Editor de Slides Desenhados** é um editor visual completo dentro da plataforma, no estilo de um PowerPoint: você monta cada slide arrastando e soltando elementos, num quadro em branco. A interface é toda em português.
> \[!IMPORTANT]
> Slides Desenhados está em **fase beta**. Um administrador precisa habilitar a funcionalidade como teste para o seu usuário.
***
## 🎨 Elementos disponíveis
| Elemento | O que faz |
|---|---|
| **Texto** | Títulos, parágrafos, listas — com formatação de fonte, tamanho, cor e alinhamento |
| **Imagem** | Upload de logo, foto ou ilustração |
| **Forma** | Retângulos, círculos, linhas e setas — para diagramas simples ou destaque visual |
| **Tabela** | Grade de texto livre, digitada à mão (diferente de um relatório de dados) |
| **[Gráfico BI](./grafico-bi.md)** | Um gráfico **real e vivo**, vindo direto de um dashboard da sua plataforma |
O Gráfico BI é o elemento que diferencia um Deck de uma apresentação comum — veja o detalhe em **[Gráfico BI](./grafico-bi.md)**.
***
## 🖱️ Montando um slide
1. Adicione um novo slide na trilha lateral (miniaturas dos slides do Deck)
2. Escolha um elemento na barra de ferramentas e arraste para o quadro
3. Posicione e redimensione livremente — não há grade fixa, o elemento vai onde você soltar
4. Repita para montar o slide, e adicione quantos slides forem necessários
> \[!TIP]
> Duplicar um slide já formatado (título, fundo, posição dos elementos) é mais rápido do que montar um novo do zero. Use a opção de duplicar na trilha de slides para manter a identidade visual entre eles.
### Reordenando slides
Arraste as miniaturas na trilha lateral para reordenar o Deck. A ordem ali é a ordem de exibição, tanto na apresentação em tela cheia quanto na exportação.
***
## 💾 Salvando
O Deck é salvo como qualquer outro item da plataforma — as alterações ficam registradas conforme você edita. Saia do editor a qualquer momento; ao reabrir o Deck, ele volta exatamente como você deixou.
***
## ▶️ Apresentando
Clique em **Apresentar** para abrir o Deck em **modo tela cheia**, slide a slide — o mesmo formato usado numa reunião ou projeção. Use as setas do teclado (ou clique) para avançar e voltar, e `Esc` para sair.
Para deixar o Deck rodando sozinho numa TV, sem alguém controlando, veja **[Modo Kiosk](./kiosk.md)**.
***
## 📤 Exportando
O Deck pode ser exportado em três formatos:
| Formato | O que sai | Observação |
|---|---|---|
| **PDF** | Todos os slides, incluindo os Gráfico BI renderizados com os dados reais | Formato mais completo — recomendado quando o Deck tem gráficos |
| **Imagem** | Cada slide como uma imagem separada | Bom para enviar um slide isolado por chat/email |
| **PPTX** | O Deck como arquivo do PowerPoint, para edição fora da plataforma | Os Gráfico BI saem como imagem estática (não ficam vivos fora da plataforma) |
> \[!TIP]
> Se o Deck tem Gráfico BI, prefira exportar em **PDF** — é o formato com melhor fidelidade visual dos gráficos.
***
## 🗺️ Próximos passos
* **[Gráfico BI](./grafico-bi.md)** — insira gráficos reais e vivos dos seus dashboards nos slides.
* **[Modo Kiosk](./kiosk.md)** — publique o Deck para rodar sozinho numa TV, sozinho ou misturado com Dashboards.
* **[Gerar Apresentação com IA](./ia.md)** — deixe a IA montar um Deck inteiro a partir de uma descrição do tema.
---
---
url: 'https://docs.horusbi.com.br/api/spec.md'
---
# Especificação Completa
Esta página contém a especificação completa da API REST do HorusBI no formato interativo.
---
---
url: 'https://docs.horusbi.com.br/etl/guides/execucao-agendamentos.md'
---
# Execução e Agendamento
Uma vez que seu Dataflow esteja publicado em uma Mesa de Dados, você pode definir quando e como ele será executado.
***
## ▶️ Execução Manual (Forçar Carga)
Dispare uma execução manualmente a qualquer momento — ideal para cargas iniciais ou reprocessamento.
1. Na listagem da Mesa de Dados, clique no botão **Executar** ao lado do fluxo
2. O sistema apresentará opções dependendo de como o fluxo foi desenhado
### Modos de Carga
Se o seu fluxo utiliza parâmetros temporais (variáveis `{StartDate}`, `{EndDate}`), o seletor de modo será exibido:
| Modo | Descrição |
|------|-----------|
| **Total** | Executa o fluxo sem filtros de tempo. Ideal para carga histórica completa — apaga os dados antigos e recarrega tudo |
| **Temporal** | Permite definir um período específico: *Dias Passados*, *Dias Futuros*, *Mês e Ano* ou *Ano* |
| **Incremental** | Baseada em coluna de controle (`updated_at`), traz apenas registros novos ou alterados. O sistema guarda automaticamente o último datapoint processado |
> \[!NOTE]
> Para que o modo **Temporal** funcione, seus nós (SQL Query, API Request) devem utilizar as variáveis `{StartDate}` e `{EndDate}`. Exemplo: `DATA_EMISSAO BETWEEN '{StartDate}' AND '{EndDate}'`
> \[!NOTE]
> Para que o modo **Incremental** funcione, seus nós devem utilizar a variável `{LastDataPoint}`. Exemplo: `UPDATED_AT > '{LastDataPoint}'`
***
## ⏰ Agendamentos
O Agendamento é o sistema de automação do Horus — ele dispara o fluxo automaticamente em intervalos definidos.
1. Acesse o menu **Agendamentos** (na barra lateral ou botão "Relógio" no card do fluxo)
2. Clique em **Criar novo Agendamento**
3. Configure a periodicidade ("Todo dia às 03:00 AM")
4. Vincule o fluxo que deve ser executado
5. Ative o agendamento
### Status e Monitoramento
Na tela de Agendamentos, monitore as execuções:
| Status | Indicador | Descrição |
|--------|-----------|-----------|
| **Sucesso** | 🟢 | Terminou sem erros |
| **Erro Parcial** | 🟡 | Terminou, mas algumas linhas ou etapas falharam |
| **Erro** | 🔴 | O fluxo falhou e parou antes de concluir |
### Sequenciando Múltiplos Flows
Crie agendamentos com **múltiplos flows em sequência** para garantir a ordem de execução — essencial para arquiteturas em camadas:
1. Ao criar o agendamento, adicione os flows na ordem desejada
2. O sistema executa na sequência definida (RAW primeiro, depois Refined)
3. Se um flow anterior falhar, os posteriores não são executados
**Exemplo de sequência típica:**
1. `raw_vendas` — Extrai dados brutos do ERP
2. `refined_vendas` — Trata, remove duplicatas, aplica regras de negócio
3. `gold_vendas` — Gera agregações finais para dashboard
> \[!TIP]
> Configure o período de carga (últimos 30 dias) para cada flow, mantendo o mesmo período para flows do mesmo dado (vendas, por exemplo).
---
---
url: 'https://docs.horusbi.com.br/ia/chat/exemplos-de-fatos.md'
---
# Exemplos de Indicadores
Esta página mostra **como aplicar na prática** os conceitos discutidos em [Indicadores: Ensinando seu Negócio](./fatos.md). Cada exemplo tem um caso "bem feito" e um anti-exemplo do mesmo conceito pulverizado.
***
## Exemplo 1 — Faturamento bem feito
Uma única definição cobrindo cinco métricas relacionadas.
| Campo | Valor |
|---|---|
| **Nome** | Faturamento |
| **Prompt** | Faturamento comercial da empresa. Considera apenas vendas confirmadas. |
| **Valor Principal** | `[Fato Vendas]."VALOR_LIQUIDO"` (soma) |
| **Valores Alternativos** | Lucro, Margem (%), Ticket Médio, Quantidade Vendida, CMV |
| **Coluna de Data** | `[Calendário]."DATA"` (Data de Emissão) |
| **Filtros Padrão** | `Status = "Confirmado"` |
**Por que este indicador é bem configurado:**
* O **nome** é o termo que a empresa usa no dia a dia. Ninguém pergunta "qual o SUM\_VLR\_LIQ?" — todo mundo pergunta "qual o faturamento?".
* O **Valor Principal** carrega a decisão crítica: é o **Valor Líquido**, não bruto. Essa escolha estaria ambígua sem o indicador.
* Os **Valores Alternativos** absorvem cinco perguntas relacionadas sem inflar o catálogo. Lucro, Margem, Ticket Médio, Quantidade e CMV **compartilham o mesmo contexto** (mesma tabela, mesma data, mesmos filtros padrão). Faz sentido viverem juntos.
* A **Coluna de Data** garante que "faturamento do mês passado", "faturamento de 2024" e "faturamento ontem" todos ancoram na Data de Emissão — sem o chat ter que escolher entre data de emissão, vencimento ou pagamento.
* O **Filtro Padrão** `Status = "Confirmado"` é o tipo de regra que **não pode ser esquecida**. Sem ele, "faturamento" passaria a incluir vendas canceladas — distorcendo todas as respostas.
**Perguntas que este único indicador cobre:**
* "Qual o faturamento de março?"
* "Qual o lucro do trimestre passado?"
* "Qual o ticket médio desse ano?"
* "Quantas vendas confirmadas tivemos ontem?"
* "Compare a margem de 2023 com 2024."
* "Faturamento por filial nos últimos 30 dias."
* "Top 10 produtos por faturamento."
Note que **agrupamento** (por filial, por produto), **período** (mês, trimestre, ontem) e **ranking** (top 10) **são decididos pela pergunta**, não pelo indicador. Um indicador bem definido é genérico no que muda, específico no que não pode mudar.
***
## Anti-exemplo 1 — Faturamento pulverizado
O **mesmo** conceito de faturamento, dividido em seis indicadores diferentes:
| Indicador | Valor Principal | Coluna de Data | Filtros |
|---|---|---|---|
| Vendas | `VALOR_LIQUIDO` (soma) | Data de Emissão | Confirmado |
| Vendas Mensais | `VALOR_LIQUIDO` (soma) | Data de Emissão | Confirmado |
| Vendas por Filial | `VALOR_LIQUIDO` (soma) | Data de Emissão | Confirmado |
| Vendas por Produto | `VALOR_LIQUIDO` (soma) | Data de Emissão | Confirmado |
| Ticket Médio | `VALOR_LIQUIDO / QTDE` | Data de Emissão | Confirmado |
| Lucro | `LUCRO_LIQUIDO` (soma) | Data de Emissão | Confirmado |
### Por que isso piora
**1. Os quatro primeiros são literalmente o mesmo indicador.** "Vendas Mensais", "Vendas por Filial", "Vendas por Produto" são **a mesma definição** ("Vendas") com **agrupamentos diferentes**. Agrupamento é decidido pela pergunta, não pelo indicador. Cadastrar versões pré-quebradas não ajuda o chat — confunde.
**2. Ticket Médio e Lucro deveriam ser Valores Alternativos.** Eles compartilham mesma tabela, mesma data, mesmos filtros. Não são conceitos diferentes — são **medidas diferentes do mesmo conceito**.
**3. O catálogo cresce sem ganho real.** Em vez de **1 indicador cobrindo 7 perguntas**, você tem **6 indicadores cobrindo as mesmas 7 perguntas**. O chat agora precisa filtrar entre seis opções que parecem todas válidas.
**4. Inconsistência inevitável.** Quando alguém pergunta "qual foi o faturamento do mês?", o chat pode usar "Vendas" ou "Vendas Mensais" — e cada um pode aplicar lógica ligeiramente diferente se você editar um sem editar o outro. Manter seis indicadores sincronizados manualmente é fonte garantida de bugs.
::: warning Sintoma típico
"O chat às vezes responde uma coisa e às vezes responde outra para a mesma pergunta." Se isso acontece, vale conferir se você não tem múltiplos indicadores cobrindo o mesmo conceito.
:::
***
## Exemplo 2 — Inadimplência (filtros padrão críticos)
Um conceito onde os **filtros padrão são tudo**. Sem eles, o número que sai é completamente diferente.
| Campo | Valor |
|---|---|
| **Nome** | Inadimplência |
| **Prompt** | Valor de contas a receber em atraso há mais de 30 dias. Considera apenas títulos em aberto, não negociados. |
| **Valor Principal** | `[Contas a Receber]."VALOR_TITULO"` (soma) |
| **Valores Alternativos** | Quantidade de Títulos em Aberto, Dias Médios de Atraso |
| **Coluna de Data** | `[Contas a Receber]."DATA_VENCIMENTO"` |
| **Filtros Padrão** | `Status = "Aberto"` E `Dias de Atraso > 30` E `Tipo Negociação != "Renegociado"` |
**O que esse indicador resolve:**
A tabela de Contas a Receber tem títulos em vários status. Sem os filtros padrão, "inadimplência" seria interpretado como "tudo a receber" — número várias vezes maior que a inadimplência real.
A definição de "inadimplência" também varia entre empresas:
* Algumas consideram a partir de 30 dias de atraso, outras 60 ou 90.
* Algumas excluem títulos renegociados, outras incluem.
* Algumas consideram apenas Pessoa Jurídica.
**O indicador congela essas decisões.** Quando alguém da empresa pergunta "qual a inadimplência hoje?", o chat aplica a regra exata da sua empresa, sem precisar reaprender a cada conversa.
Os **Valores Alternativos** seguem o padrão de "compartilham contexto": quantidade de títulos e dias médios de atraso usam a mesma tabela, mesma data e mesmos filtros — então fazem parte do mesmo indicador.
***
## Anti-exemplo 2 — Nomes parecidos com escopos sutilmente diferentes
Esse é o anti-padrão **mais difícil de detectar**, porque na superfície parece organizado.
| Indicador | Filtros Padrão |
|---|---|
| Receita Confirmada | `Status = "Confirmado"` |
| Receita Faturada | `Status IN ("Confirmado", "Faturado")` |
| Receita Líquida | `Status = "Confirmado"` E coluna usada: `VALOR_LIQUIDO` (descontando impostos) |
À primeira vista pode parecer que são três conceitos diferentes. Na prática:
* **"Receita Confirmada" e "Receita Líquida"** têm os mesmos filtros — diferença é qual coluna usar. Isso é **Valores Alternativos**, não indicadores diferentes.
* **"Receita Faturada"** tem um filtro mais amplo. Pode ser conceito diferente, pode não ser — depende se a empresa **realmente** trata "faturada" como métrica separada ou se é só um sinônimo informal.
### A pergunta-teste
> *"Se duas pessoas diferentes da empresa, ao ouvir esses três nomes, descrevem **a mesma métrica**, eu tenho um indicador disfarçado de três. Se descrevem coisas genuinamente diferentes, eu tenho três indicadores."*
Na prática, na maioria das empresas:
* "Receita Confirmada" e "Receita Líquida" **são a mesma coisa com métricas variantes** → consolide em um indicador "Receita" com Valor Líquido como principal e Valor Bruto como alternativo.
* "Receita Faturada" **pode ser um conceito real e separado** se a empresa de fato distingue (ex.: receita reconhecida contabilmente vs receita confirmada comercialmente).
### O que fazer
Sempre que dois indicadores têm nomes parecidos:
1. Pergunte às pessoas que **realmente fazem as perguntas** se elas distinguem os dois conceitos no dia a dia.
2. Se a resposta for "é a mesma coisa" — consolide.
3. Se a resposta for "são coisas bem diferentes" — mantenha separados, mas **escreva um Prompt claro em cada um** explicando a diferença, para o chat conseguir distinguir.
***
## Quando vale separar de verdade
Nem todo desdobramento é dicionário gigante. Tem casos onde **dois indicadores genuinamente diferentes coexistem**.
### Receita Comercial vs Receita Financeira
| | Receita Comercial | Receita Financeira |
|---|---|---|
| **Tabela** | Vendas | Movimentação Financeira |
| **Valor Principal** | `VALOR_LIQUIDO` (vendas) | `VALOR_RECEITA` (juros, multas, taxas) |
| **Coluna de Data** | Data de Emissão | Data de Movimentação |
| **Filtros** | `Status = "Confirmado"` | `Tipo IN ("Juros", "Multa", "Taxa")` |
Esses são **dois conceitos genuinamente diferentes**:
* Vivem em tabelas diferentes.
* Têm naturezas de negócio diferentes (operacional vs financeiro).
* Quando alguém pergunta "qual a receita do mês?", a resposta certa **depende do contexto** — talvez uma soma das duas, talvez só uma.
A presença de **duas tabelas, duas datas e dois conjuntos de filtros distintos** é o sinal claro de que são dois indicadores: refletem o modelo real do negócio.
### Outros casos legítimos de separar
* **Métricas operacionais vs métricas estratégicas** que usam definições diferentes (ex.: "Vendas Brutas Operacionais" para o gerente da loja vs "Vendas Líquidas Contábeis" para o financeiro).
* **Métricas por linha de produto** quando cada linha tem **regras de cálculo materialmente diferentes** (não só "filtro por linha", mas fórmulas distintas).
* **Conceitos sazonais ou de campanha** que coexistem com a métrica padrão (ex.: "Faturamento da Black Friday" com janela e filtros próprios).
A pergunta que separa "vale separar" de "vale consolidar":
> *"Se eu consolidar esses dois conceitos num único indicador com Valores Alternativos, eu perco alguma definição importante? Algum filtro fica errado para algum dos casos?"*
Se a resposta é **sim** — são dois indicadores. Se a resposta é **não** — é um indicador com Valores Alternativos.
***
## Resumindo a régua
Use esta régua mental cada vez que você for criar um indicador novo:
| Pergunte-se | Se sim... | Se não... |
|---|---|---|
| O nome é como as pessoas da empresa **de fato** falam? | Continue | Renomeie antes de cadastrar |
| Já existe um indicador com **mesma tabela, mesma data, mesmos filtros**? | Adicione como Valor Alternativo do existente | Continue |
| A diferença para um indicador existente é só "como agrupar" ou "qual período"? | Não cadastre — agrupamento e período são decididos pela pergunta | Continue |
| A diferença para um indicador existente é uma nuance de uma frase? | Não cadastre — use o campo Prompt do existente | Continue |
| Tabelas, datas ou filtros **genuinamente diferentes** do que já existe? | Crie o indicador | Reavalie — talvez não seja conceito separado |
Quando em dúvida: **menos é mais**. É fácil adicionar um indicador novo depois. É muito mais difícil descobrir que o catálogo cresceu demais e ter que limpar duzentos indicadores.
***
## Próximos passos
* Volte à teoria em **[Indicadores: Ensinando seu Negócio](./fatos.md)** para revisar o porquê de cada decisão.
* Veja **[Como o chat responde](./como-funciona.md)** para entender onde os indicadores entram no caminho de uma pergunta.
* Consulte a **[Aba Conceitos IA](/dataviz/02-apps/ai-concepts.md)** para a referência detalhada da UI.
---
---
url: 'https://docs.horusbi.com.br/dataviz/02-apps/explorer.md'
---
# Explorer (Relatórios Nativos)
O **Explorer** é uma ferramenta de análise de dados *self-service* disponível em todas as Aplicações do Horus DataViz. Diferente das Dashboards, que respondem perguntas pré-definidas pelo criador, o Explorer permite que o usuário final crie Relatórios tabulares *ad-hoc* (sob demanda) de forma livre e intuitiva.
***
## 🧭 Visão Geral
O Explorer é acessado através do ícone de **Tabela** no menu superior da Aplicação. Ele oferece:
* **Seleção livre de colunas**: Escolha quais dados exibir
* **Agregações personalizadas**: Defina como os valores numéricos devem ser calculados (Soma, Média, Contagem, etc.)
* **Filtros avançados**: Aplique qualquer filtro disponível no Modelo de Dados
* **Ordenação dinâmica**: Organize os dados conforme necessário
* **Exportação**: Baixe os dados em Excel ou PDF
* **Visões Salvas**: Guarde configurações para uso futuro
> \[!TIP]
> O Explorer é ideal para análises exploratórias, auditoria de dados e extração de informações granulares que não são contempladas nas Dashboards.
***
## 🖥️ Interface do Explorer
A tela do Explorer é dividida em três áreas principais:
### 1. Barra de Filtros (Superior)
Localizada no topo da tela, permite:
* **Adicionar filtros**: Clique no **`ícone de funil`** ou interaja com os dados
* **Visualizar filtros ativos**: Veja todos os filtros aplicados
* **Remover filtros**: Clique no `X` de cada filtro

### 2. Painel Lateral (Esquerda)
O painel lateral contém:
* **Campos Selecionados**: Lista dos campos que compõem o Relatório
* **Filtros Aplicados**: Resumo visual dos filtros ativos
* **Campos Disponíveis**: Todas as colunas e Expressões disponíveis, organizadas por Tabela

### 3. Área da Tabela (Central)
Exibe os resultados do Relatório com:
* **Cabeçalho fixo**: Os nomes das colunas permanecem visíveis ao rolar à página
* **Rodapé com totalizadores**: Soma automática para colunas numéricas
* **Numeração de linhas**: Para identificação e organização de dados de forma sequencial

***
## 📋 Selecionando Campos
### Adicionando Campos
No painel lateral, os campos são organizados por Tabela. Cada campo exibe um indicador do tipo de dado:
| Indicador | Tipo de Dado |
|-----------|--------------|
| `#` | Número |
| `A` | Texto |
| 📅 | Data |
**Para adicionar um campo:**
1. Localize a Tabela desejada no painel lateral
2. Clique no nome do campo para adicioná-lo com a agregação padrão
3. Ou clique em **Funções** para escolher uma agregação específica

### Agregações Disponíveis
#### Para Números
| Agregação | Descrição |
|-----------|-----------|
| **Soma** | Soma de todos os valores |
| **Média** | Média aritmética |
| **Mínimo** | Menor valor |
| **Máximo** | Maior valor |
| **Agrupar** | Exibe cada valor único |
| **Contagem Distinta** | Quantidade de valores únicos |
| **Contagem** | Quantidade total de registros |
#### Para Datas
| Agregação | Descrição | Exemplo de Saída |
|-----------|-----------|------------------|
| **Data** | Data completa | 15/03/2024 |
| **Data e Hora** | Data com horário | 15/03/2024 14:30:00 |
| **Hora do Dia** | Horário extraído | 14:00 |
| **Hora** | Hora e minuto | 14:27 |
| **Ano/Mês** | Mês e ano | 2024/03 |
| **Mês/Dia** | Dia e mês | 15/03 |
| **Dia** | Número do dia | 15 |
| **Mês** | Número do mês | 03 |
| **Ano** | Ano completo | 2024 |
| **Dia da Semana** | Nome do dia | Sexta-feira |
| **Semana do Ano** | Número da semana | 11 |
#### Para Textos
| Agregação | Descrição |
|-----------|-----------|
| **Agrupar** | Exibe cada valor único |
| **Contagem Distinta** | Quantidade de valores únicos |
| **Contagem** | Quantidade total de registros |
| **Mínimo** | Primeiro valor alfabeticamente |
| **Máximo** | Último valor alfabeticamente |
### Reordenando Campos
Arraste e solte os campos na lista de **"Campos Selecionados"** para alterar a ordem das colunas no Relatório `(localizado ao topo do painel lateral esquerdo)`.

### Removendo Campos
Clique no ícone de **lixeira** ao lado do campo selecionado ou utilize o menu de contexto (clique direito).

***
## 🔍 Filtros
### Adicionando Filtros
Existem três formas de adicionar filtros:
1. **Barra de filtros**: Clique no botão de filtro **`(ícone de funil)`** para adicionar um novo critério
2. **Tabela (por coluna)**: Clique sobre um campo na tabela e selecione **Filtrar por Coluna**
3. **Tabela (por linha)**: Clique sobre um campo na tabela e selecione **Filtrar por Linha**

### Operadores de Filtro
| Operador | Descrição |
|----------|-----------|
| `=` | Igual a |
| `!=` | Diferente de |
| `>` | Maior que |
| `<` | Menor que |
| `>=` | Maior ou igual |
| `<=` | Menor ou igual |
| `Entre` | Dentro de um intervalo |
| `Contém` | Texto contém substring |
| `Não Contém` | Texto não contém substring |
| `Relativo` | Período relativo (últimos 7 dias) |
### Filtros de Data Relativos
Para campos de data, é possível utilizar filtros relativos que se atualizam automaticamente:
* **Hoje**, **Ontem**, **Amanhã**
* **Esta semana**, **Semana passada**
* **Este mês**, **Mês passado**
* **Este ano**, **Ano passado**
* **Últimos X dias/semanas/meses**

***
## ↕️ Ordenação
### Ordenando Dados
1. Passe o mouse sobre o cabeçalho de uma coluna e clique no ícone de menu `...`
2. Selecione "Ordenar Crescente" `(A→Z, 0→9)` ou "Ordenar Decrescente" `(Z→A, 9→0)`
3. Um indicador de seta aparece na coluna ordenada `^`

### Ordenação Múltipla
É possível realizar ordenação por múltiplas colunas. A última ordenação aplicada tem prioridade.
### Ordenação Padrão
Se nenhuma ordenação for definida, o sistema aplica automaticamente:
* **Decrescente** para a primeira coluna numérica
* **Crescente** para a primeira coluna textual
***
## 📊 Cenários Comparativos
O recurso de **Cenários** permite comparar diferentes recortes de dados lado a lado no mesmo Relatório.
### Como Usar Cenários
1. Clique no botão **"Usar Cenários"** na barra superior
2. Selecione a quantidade de cenários (2 a 5)
3. Clique em **Aplicar**

### Configurando Filtros por Cenário
Após ativar os cenários:
1. Ao adicionar um campo numérico, escolha se ele será:
* **Todos os Cenários**: O mesmo filtro para todos
* **Cenário 1, 2, 3...**: Filtro específico para cada cenário
2. Aplique filtros diferentes para cada cenário
### Exemplo de Uso
**Comparando vendas de dois períodos:**
| Produto | Vendas (Cen. 1) - Jan/2024 | Vendas (Cen. 2) - Jan/2023 |
|---------|----------------------------|----------------------------|
| Produto A | R$ 50.000 | R$ 45.000 |
| Produto B | R$ 30.000 | R$ 35.000 |
> \[!TIP]
> Cenários são ideais para:
>
> * Comparar períodos diferentes (este mês vs. mês anterior)
> * Analisar filiais/regiões lado a lado
> * Contrastar dados reais vs. projetados
***
## 🔎 Drilldown
O **Drilldown** permite detalhar uma linha específica do Relatório, mergulhando nos dados granulares.
### Como Fazer Drilldown
1. Clique com o botão direito em uma linha da Tabela
2. Selecione "Filtrar por esta linha"
3. Opcionalmente, escolha novos campos para adicionar ao detalhamento
4. Clique em **Aplicar**
O sistema aplica automaticamente todos os valores daquela linha como filtros.
***
## 📤 Exportação
### Formatos Disponíveis
* **Excel (.xlsx)**: Planilha completa com todos os dados
* **PDF**: Documento formatado para impressão
### Exportando Dados
1. Configure o Relatório (campos, filtros, ordenação)
2. Clique no botão de exportação correspondente `(localizados entre os filtros e o topo do relatório)`:
* 📊 Excel
* 📄 PDF
3. Aguarde o processamento
4. Faça o download do arquivo clicando em **Baixar** `(assim que a header ficar verde)`

> \[!NOTE]
> A visualização na tela é limitada a **500 linhas** por questões de performance. A exportação, porém, inclui **todos os dados** do Relatório.
### Permissões de Exportação
A exportação pode ser restrita por Tabela através da configuração do Datamart. Caso não seja possível realizar a exportação, solicite acesso ao administrador.
***
## ⭐ Visões Salvas (Bookmarks)
As Visões Salvas permitem guardar configurações do Explorer para uso futuro.
### O que é Salvo
* Campos selecionados e suas agregações
* Filtros aplicados
* Ordenação
* Configuração de cenários
### Salvando uma Visão
1. Configure o Relatório como desejado
2. Clique no ícone de **Favoritos** (marcador)
3. Clique em "Salvar visão atual"
4. Digite um nome para a Visão
5. Opcionalmente:
* Marque "Tornar público" para compartilhar com outros usuários
* Marque "Definir como padrão" para abrir automaticamente
6. Clique em **Salvar**

### Carregando uma Visão
1. Clique no ícone de **Favoritos**
2. Selecione a Visão desejada na lista:
* **Meus Favoritos**: Visões criadas pelo próprio usuário
* **Compartilhados Comigo**: Visões criadas por outros usuários

### Gerenciando Visões
* **Definir como padrão**: A Visão abre automaticamente ao entrar no Explorer
* **Compartilhar**: Torna a Visão pública para outros usuários
* **Excluir**: Remove a Visão Salva
***
## 🔗 Compartilhamento
### Compartilhando o Relatório
1. Configure o Relatório
2. Clique no botão de **Compartilhar**
3. O link é copiado automaticamente para a área de transferência
4. Envie o link para outros usuários

O link compartilhado contém:
* Todos os campos selecionados
* Filtros aplicados
* Ordenação
* Configuração de cenários
***
## 🔔 Criando Alertas
É possível criar alertas automáticos baseados no Relatório atual.
### Como Criar um Alerta
1. Configure o Relatório com os dados que deseja monitorar
2. Clique no ícone de **Notificação** (sino)
3. Configure:
* **Nome do alerta**
* **Frequência de verificação** (diária, semanal, etc.)
* **Condições de disparo**
* **Destinatários**
4. Salve o alerta

> \[!IMPORTANT]
> A funcionalidade de alertas está disponível apenas para usuários com permissão específica.
***
## 💡 Dicas e Boas Práticas
1. **Comece com poucos campos** — Adicione gradualmente para entender os dados
2. **Utilize filtros para limitar os dados** — Relatórios menores são mais rápidos
3. **Salve Visões frequentes** — Evite reconfigurar Relatórios usados regularmente
4. **Aproveite os cenários** — Ideais para comparações lado a lado
5. **Exporte para análises complexas** — Utilize o Excel para cálculos adicionais
***
## 🛠️ Solução de Problemas
### "Não consigo exportar"
Verifique se há permissão de exportação para as Tabelas do Relatório. Caso necessário, solicite acesso ao administrador.
### "O Relatório está lento"
* Reduza a quantidade de campos selecionados
* Aplique filtros para limitar os dados
* Considere utilizar agregações ao invés de dados granulares
### "Não encontro o campo que preciso"
* Verifique se a Tabela está visível no Modelo de Dados
* Confirme se há acesso à Tabela (Datamart)
* O campo pode estar em uma Expressão calculada
### "Os dados parecem incorretos"
* Verifique os filtros aplicados
* Confirme a agregação de cada campo numérico
* Compare com as Dashboards para validação cruzada
---
---
url: 'https://docs.horusbi.com.br/etl/processors/inputs/datalake.md'
---
# Extrair Datalake
O nó **Extrair Datalake** permite ler dados que já foram ingeridos e armazenados no Data Lake gerenciado pelo Horus. Isso é útil para reprocessar dados históricos ou cruzar informações de diferentes fontes que já foram centralizadas.
***
## ⚙️ Parâmetros de Configuração
### Tabela
* **Descrição** — Selecione a tabela do Data Lake que deseja ler
* **Origem** — As tabelas listadas são aquelas definidas no Catálogo de Dados do Horus
### Coluna de Data
* **Descrição** — O nome da coluna que representa a data de referência dos dados
* **Uso** — O Horus usa essa coluna para otimizar a leitura, carregando apenas as partições de dados relevantes se houver filtros de período
### Usar Particionamento
* **Descrição** — Habilita a leitura inteligente baseada em partições
* **Recomendação** — Mantenha ativado para melhor performance, a menos que precise fazer um *full scan* explícito
### Ler Todos os Agentes
* **Descrição** — Se marcado, consolida os dados de todos os agentes/tenants que alimentam essa tabela
* **Caso de Uso** — Relatórios consolidados ou visão global (Matriz)
***
## 🔧 Detalhes Técnicos
* **Schema** — O schema é recuperado automaticamente dos metadados da tabela selecionada
---
---
url: 'https://docs.horusbi.com.br/lumo/referencia/flow.md'
description: >-
Referência do YAML de flow do Lumo: propriedades, tipos, restrições e um
exemplo completo.
---
# Flow
Um flow é um pipeline de ETL. Ele vive em `flows/--.yaml`, extrai dados de uma origem, transforma e grava numa tabela do DW.
Crie o arquivo com `lumo new flow --name "Fato Vendas"`, edite, valide com `lumo lint` e publique com `lumo push flow:`.
Ligue o autocomplete e a validação no seu editor colando esta linha no topo do arquivo:
```yaml
# yaml-language-server: $schema=https://docs.horusbi.com.br/schemas/v2/flow.schema.json
```
## Modelo de configuração
O arquivo tem dois documentos YAML separados por `---`. O primeiro é o header, imutável depois da criação. O segundo é o body, que é o que você edita.
```yaml
# ── header ────────────────────────────────────────────────
id: integer # obrigatório, >= 1, atribuído pelo servidor
kind: flow # obrigatório, literal "flow"
lumo: v2 # obrigatório, literal "v2"
tenantId: integer # obrigatório, >= 1
---
# ── body ──────────────────────────────────────────────────
nome: string # obrigatório, mínimo 1 caractere
load_type: string # obrigatório: Total | Incremental | Temporal
load_type_column: string | null # obrigatório quando load_type é Incremental ou Temporal
tokenId: integer | null # agente que executa o flow; null = agente padrão do tenant
development_variables: [] # variáveis de entrada por execução
tags: [string]
nodes: # obrigatório
Processors: # obrigatório, lista de nós
- internalId: string # obrigatório, único dentro do flow
kind: string # obrigatório: ExtractPostgreSQL | Join | InsertDatawarehouse | ...
description: string | null
inputs: [string] # internalId (ou complete_id) dos nós de entrada; a ordem importa
options: {} # parâmetros do nó; as chaves dependem de kind
metadata: {}
complete_id: string # gerado pelo servidor
x: number # posição no editor visual
y: number
Connections: # obrigatório, arestas do DAG
- from: string # obrigatório, internalId de origem
to: string # obrigatório, internalId de destino
table: # tabela do DW alimentada por este flow
id: integer
nome: string
owners:
- userId: integer
```
`nodes` declara a mesma aresta em dois lugares: `inputs` no nó de destino e um item em `Connections`. Os dois precisam concordar. O `lumo lint` recusa um `inputs` que aponte para um nó inexistente.
## Configuração completa
Um fato de vendas que junta o cabeçalho do pedido com os itens e grava no DW.
```yaml
# flows/fato-vendas--44531.yaml
# yaml-language-server: $schema=https://docs.horusbi.com.br/schemas/v2/flow.schema.json
id: 44531
kind: flow
lumo: v2
tenantId: 853
---
nome: Fato Vendas
load_type: Temporal
# Temporal exige load_type_column, e a coluna precisa ser IMUTÁVEL.
# Use a data do fato (emissão), nunca uma data que o ERP reescreve (atualização).
load_type_column: DATA_EMISSAO
tokenId: 508
development_variables: []
nodes:
Processors:
- internalId: ext_pedidos
kind: ExtractPostgreSQL
description: Cabeçalho dos pedidos
inputs: []
options:
# Chave da credencial, não o id. Descubra com: lumo list credential
Credential: erp-producao
SQL: |
SELECT
p.id AS PEDIDO_ID,
p.cliente_id AS CLIENTE_ID,
p.data_emissao AS DATA_EMISSAO,
p.valor_total AS VALOR_TOTAL
FROM public.pedidos p
WHERE p.data_emissao >= '{StartDate}'
AND p.data_emissao <= '{EndDate}'
x: 100
y: 200
- internalId: ext_itens
kind: ExtractPostgreSQL
description: Itens do pedido
inputs: []
options:
Credential: erp-producao
SQL: |
SELECT
i.pedido_id AS PEDIDO_ID,
i.produto_id AS PRODUTO_ID,
i.quantidade AS QUANTIDADE,
i.valor_item AS VALOR_ITEM
FROM public.pedido_itens i
x: 100
y: 400
- internalId: join_itens
kind: Join
description: Enriquece cada item com o cabeçalho
# A ordem importa: inputs[0] é o primário (todas as colunas passam),
# inputs[1] é o secundário (só as colunas de ColunasATrazer passam).
inputs: [ext_itens, ext_pedidos]
options:
ChavesPrimario: [PEDIDO_ID]
ChavesSecundario: [PEDIDO_ID]
ColunasATrazer: [CLIENTE_ID, DATA_EMISSAO, VALOR_TOTAL]
TipoJoin: LEFT
x: 350
y: 300
- internalId: load_dw
kind: InsertDatawarehouse
description: Grava no Data Warehouse
inputs: [join_itens]
options:
# Preenchido por: lumo new table --from-flow flow:44531 --node join_itens
TableID: 44940
Mode: Datawarehouse
PartitionType: NONE
PrimaryKeys: []
x: 600
y: 300
Connections:
- from: ext_itens
to: join_itens
- from: ext_pedidos
to: join_itens
- from: join_itens
to: load_dw
table:
id: 44940
nome: fato_vendas
tags: [vendas]
```
Os nós de extração convertem os nomes das colunas para MAIÚSCULAS. Todo nó a jusante (`Join`, `SQLProcessor`, `InsertDatawarehouse`) precisa se referir a elas em maiúsculas.
## Especificação: header
### `id`
**Tipo:** integer (>= 1) · **Obrigatório:** sim
Id do flow no servidor. O `lumo new flow` cria o recurso e escreve o id aqui. Não edite.
### `kind`
**Tipo:** string · **Obrigatório:** sim · **Valor:** `flow`
Discrimina o tipo de recurso e decide contra qual schema o `lumo lint` valida o arquivo.
### `lumo`
**Tipo:** string · **Obrigatório:** sim · **Valor:** `v2`
Versão do formato de workspace.
### `tenantId`
**Tipo:** integer (>= 1) · **Obrigatório:** sim
Tenant dono do flow. Vem do `lumo init `.
## Especificação: body
### `nome`
**Tipo:** string (mínimo 1 caractere) · **Obrigatório:** sim
Nome exibido do flow.
```yaml
nome: Fato Vendas
```
### `load_type`
**Tipo:** string · **Obrigatório:** sim · **Valores:** `Total`, `Incremental`, `Temporal`
Estratégia de carga.
| Valor | Comportamento |
|---|---|
| `Total` | Apaga tudo e recarrega tudo. Use em dimensões e tabelas pequenas. |
| `Incremental` | Só traz o que é novo desde a última execução. Injeta `{LastDataPoint}` no SQL. |
| `Temporal` | Recarrega uma janela de datas. Injeta `{StartDate}` e `{EndDate}` no SQL. |
### `load_type_column`
**Tipo:** string ou null · **Obrigatório:** quando `load_type` é `Incremental` ou `Temporal` · **Default:** `null`
Coluna de data que delimita a janela de carga. Escolha uma coluna imutável, como a data de emissão. Uma coluna que a origem reescreve, como data de atualização, faz a carga perder registros.
```yaml
load_type: Temporal
load_type_column: DATA_EMISSAO
```
### `tokenId`
**Tipo:** integer ou null · **Obrigatório:** não · **Default:** `null`
Agente que executa o flow. `null` usa o primeiro agente ativo do tenant. Em tenant com vários agentes, aponte o agente explicitamente. Liste os disponíveis com `lumo list agent`.
Para migrar um flow de agente, edite este campo e dê `lumo push`.
### `development_variables`
**Tipo:** array · **Obrigatório:** não · **Default:** `[]`
Variáveis de entrada passadas aos nós em cada execução.
### `nodes`
**Tipo:** object · **Obrigatório:** sim
Contém o DAG. Exige as duas chaves, mesmo vazias.
```yaml
nodes:
Processors: []
Connections: []
```
### `Processors`
**Tipo:** array de objetos · **Obrigatório:** sim
Os nós do flow. Cada item exige `internalId` e `kind`. Veja [Especificação: processor](#especificacao-processor).
### `Connections`
**Tipo:** array de objetos · **Obrigatório:** sim
As arestas do DAG. Cada item exige `from` e `to`.
### `table`
**Tipo:** object ou null · **Obrigatório:** não
Tabela do DW que este flow alimenta. O `lumo new table --from-flow` preenche.
```yaml
table:
id: 44940
nome: fato_vendas
```
### `owners`
**Tipo:** array de objetos · **Obrigatório:** não
Donos do flow, cada item com `userId`.
### `tags`
**Tipo:** array de string · **Obrigatório:** não · **Default:** `[]`
Rótulos livres para organizar o workspace.
## Especificação: processor {#especificacao-processor}
Cada item de `nodes.Processors`.
### `internalId`
**Tipo:** string (mínimo 1 caractere) · **Obrigatório:** sim
Identificador do nó dentro do flow. É o que `inputs` e `Connections` usam para se referir a ele. Precisa ser único no flow.
### `kind` {#processor-kind}
**Tipo:** string (mínimo 1 caractere) · **Obrigatório:** sim
Tipo do processador, que decide quais chaves `options` aceita. Os tipos em uso hoje:
| Grupo | Valores |
|---|---|
| Extração | `ExtractPostgreSQL`, `ExtractMySQL`, `ExtractSQLServer`, `ExtractOracleDB`, `ExtractFirebird`, `ExtractInformix`, `ExtractInterSystemsIRIS`, `ExtractODBC`, `ExtractBigQuery`, `ExtractDatalake`, `ExtractLakehouse`, `ExtractStaticCSV`, `HTTPRequest`, `AIExtract` |
| Transformação | `Join`, `Union`, `SQLProcessor`, `PythonProcessor`, `PythonConfigurator` |
| Carga | `InsertDatawarehouse` |
Rode `lumo scaffold node ` para ver o fragmento YAML de um tipo, com as chaves de `options` comentadas.
### `description`
**Tipo:** string ou null · **Obrigatório:** não
Descreve o que o nó faz. Aparece no editor visual.
### `inputs`
**Tipo:** array de string · **Obrigatório:** não · **Default:** `[]`
Nós que alimentam este. Cada item é o `internalId` ou o `complete_id` de outro nó do mesmo flow. O `lumo lint` falha se a referência não existir.
Nós de extração têm `inputs: []`. **A ordem importa** em `Join` (o primeiro é o primário) e em `Union`.
```yaml
inputs: [ext_itens, ext_pedidos]
```
### `options`
**Tipo:** object · **Obrigatório:** não
Parâmetros do nó. As chaves aceitas dependem de `kind`, e o schema não as restringe. Consulte `lumo scaffold node ` ou a [referência de processadores](/etl/processors/).
### `metadata`
**Tipo:** object · **Obrigatório:** não
Dados livres associados ao nó.
### `complete_id`
**Tipo:** string · **Obrigatório:** não
Identificador qualificado que o servidor atribui, no formato `|`. Aparece depois de um `lumo pull`. Escrever `internalId` em `inputs` é suficiente.
### `x`
**Tipo:** number · **Obrigatório:** não
Posição horizontal do nó no editor visual.
### `y`
**Tipo:** number · **Obrigatório:** não
Posição vertical do nó no editor visual.
## Especificação: connection
Cada item de `nodes.Connections`.
### `from`
**Tipo:** string · **Obrigatório:** sim
`internalId` do nó de origem.
### `to`
**Tipo:** string · **Obrigatório:** sim
`internalId` do nó de destino.
## Campos gerenciados pelo servidor
O servidor calcula estes campos e ignora edições no `push`. Eles aparecem depois de um `lumo pull`.
| Campo | O que é |
|---|---|
| `_state` | `draft`, `published` ou `inconsistent`. |
| `deskId` | Desk em que o flow foi publicado. Mude com `lumo flow publish`. |
| `version` | Versão do recurso. |
| `cloned_from` | Flow de origem, quando o flow veio de um `lumo clone`. |
| `originalTableId` | Tabela de origem do clone. |
| `criado_em`, `criado_por`, `publicado_em`, `publicado_por` | Auditoria. |
---
---
url: 'https://docs.horusbi.com.br/etl/guides/mesas-publicacao.md'
---
# Fluxo de Trabalho: Mesas e Publicação
O HorusETL organiza os fluxos de dados (Dataflows) em **Mesas** (Desks), separando claramente o ambiente de desenvolvimento do ambiente de produção.
***
## 🗂️ Conceitos de Mesas
### 1. Minha Mesa (Ambiente de Desenvolvimento)
Cada usuário possui sua própria "Minha Mesa".
* **Privado** — Apenas você vê os fluxos aqui
* **Draft** — É um ambiente de rascunho. As alterações salvas aqui **não** afetam os agendamentos oficiais de produção
* **Execução** — Você pode rodar testes manuais, mas não pode criar agendamentos recorrentes (cron) diretamente aqui
### 2. Mesas de Dados (Ambiente de Produção)
São ambientes compartilhados criados pelos administradores (no HEC).
* **Colaborativo** — Vários usuários podem ter acesso para visualizar ou gerenciar fluxos em uma Mesa de Dados
* **Produção** — É aqui que os fluxos existem oficialmente para o sistema de agendamento
* **Agendável** — Apenas fluxos publicados em Mesas de Dados podem ter Agendamentos (Schedules)
***
## 🔄 Ciclo de Vida do Dataflow
O fluxo de trabalho recomendado no HorusETL segue este ciclo:
1. **Desenvolver** — Crie e edite o fluxo na "Minha Mesa"
2. **Publicar** — Quando estiver pronto, envie o fluxo para uma "Mesa de Dados"
3. **Agendar** — Crie agendamentos para a versão publicada
4. **Manter (Clonar)** — Para correções futuras, clone o fluxo de volta para a "Minha Mesa", edite e republique
### Publicando um Fluxo
Para levar seu trabalho para produção:
1. Na listagem da "Minha Mesa", localize o fluxo
2. Clique no botão de **Publicar** (ícone de nuvem/upload)
3. Selecione a **Mesa de Destino**
4. O sistema verificará se já existe uma versão deste fluxo lá:
* **Criar Novo** — Cria uma cópia independente
* **Substituir** — Atualiza a versão existente na mesa de destino, mantendo o histórico de IDs e agendamentos vinculados
### Clonando para Manutenção
Se você precisa corrigir um bug em um fluxo que já está em produção:
1. Vá até a Mesa de Dados onde o fluxo está
2. Clique no botão de **Clonar para Minha Mesa**
3. Uma cópia idêntica aparecerá na sua área pessoal
> \[!NOTE]
> **Integração com o DW**: Se o fluxo original carrega dados para uma tabela no Datawarehouse (nó `Insert Datawarehouse`), o processo de clonagem também criará uma **cópia física da tabela de destino** para o seu ambiente de rascunho. Isso garante que seus testes não afetem os dados oficiais de produção.
4. Faça as alterações necessárias e teste à vontade
5. Quando terminar, use a opção **Publicar** e escolha **Substituir** para atualizar a versão oficial
---
---
url: 'https://docs.horusbi.com.br/dataviz/04-features/alerts/delivery.md'
---
# Formato de Entrega
Depois de escolher **quais canais** recebem o alerta (ver [Canais de Entrega](./channels)), o Lumo decide **como** o conteúdo chega em cada canal. A regra central: nos canais de mensagem (WhatsApp e Telegram), cada item do alerta vira uma **mensagem separada** — até um certo volume. Acima dele, tudo é consolidado num **único PDF** para não inundar a conversa.
> \[!TIP]
> Você vê exatamente como o seu alerta será entregue no painel **"Como este alerta será entregue"**, na aba **Conteúdo** do editor. Ele se atualiza conforme você adiciona itens e escolhe canais.
***
## Inline vs. Consolidado
Cada item de conteúdo conta como um **item de mídia** quando é visual ou um arquivo: gráfico, dashboard ou relatório com PDF. Texto e análise de IA não contam (são leves).
| Itens de mídia | WhatsApp / Telegram |
|---|---|
| **Até 4** | **Inline** — 1 mensagem por item |
| **5 ou mais** | **Consolidado** — 1 PDF único + 1 mensagem de resumo |
O limite (padrão **4**) evita o cenário em que um alerta com muitos gráficos vira uma enxurrada de imagens no WhatsApp.
### Modo Inline (até 4 itens de mídia)
Cada item é entregue no formato nativo do canal:
| Item | Como chega |
|---|---|
| Texto / Análise de IA | Mensagem de texto |
| Gráfico | Imagem |
| Dashboard (imagem) | Imagem |
| Dashboard (PDF) | Documento |
| Relatório | Documento PDF **+** documento Excel (quando houver) |
### Modo Consolidado (5+ itens de mídia)
* **1 mensagem de resumo** com o nome do alerta e os textos/análises.
* **1 PDF único** com todos os gráficos e dashboards, página a página.
***
## O PDF consolidado
Quando o Lumo monta um PDF (modo consolidado, ou no WhatsApp oficial via Meta), ele segue uma diagramação enxuta — **sem páginas de índice ou separadores**:
* **1 anexo só** (ex.: um relatório) → o arquivo vai **direto**, sem reprocessar.
* **Sem texto, só visuais** → cada gráfico ocupa **uma página do tamanho do gráfico** (sem moldura A4 sobrando); dashboards e relatórios entram em sequência.
* **Com texto** → o texto e os gráficos são diagramados em páginas A4; dashboards e relatórios seguem depois.
> \[!NOTE]
> No **WhatsApp oficial (conta Meta)**, fora da janela de 24h só é possível enviar via *template* com um documento anexado. Por isso, nesse canal o alerta sempre vira **um PDF** — mesmo um alerta só de texto. Nos canais de mensagem comuns (WhatsApp por instância própria e Telegram), vale a regra inline/consolidado acima.
***
## Relatórios com PDF + Excel
Um relatório pode gerar **PDF e Excel** ao mesmo tempo. Quando isso acontece:
* O **link de download do Excel fica embutido no próprio PDF** ("Baixar este relatório em Excel") — o relatório é auto-contido mesmo quando só o PDF é encaminhado.
* Nos canais de mensagem, o **arquivo Excel também vai como anexo** junto do PDF.
Um relatório configurado **só com Excel** (sem PDF) é entregue como **link/arquivo da planilha** — não conta como item de mídia para o limite anti-flood.
***
## E os outros canais?
* **Email** — não muda: relatório completo no corpo (texto + gráficos inline) e arquivos como anexo. Não há consolidação em PDF.
* **Webhook** — recebe o payload estruturado (JSON) com URLs dos arquivos; sua aplicação renderiza como quiser.
Ver [Canais de Entrega](./channels) para a configuração de cada canal.
---
---
url: 'https://docs.horusbi.com.br/dataviz/04-features.md'
---
# Funcionalidades e Extensões
Além da criação de Dashboards e Relatórios, o Horus DataViz oferece funcionalidades avançadas para automação, exibição e inteligência.
***
## 1. Alertas Inteligentes
O módulo de Alertas permite que a plataforma monitore seus dados 24/7 e notifique os usuários proativamente, seja por agendamento ou quando uma regra de negócio for atendida. Cada alerta combina três camadas: **disparo** (quando?) → **conteúdo** (o quê?) → **canais** (por onde?).
Tipos de conteúdo disponíveis: **texto livre com variáveis**, **gráfico (snapshot)**, **relatório PDF/XLSX**, e **análise por IA** (com relatórios de exemplo editáveis e formato de saída configurável).
Canais suportados: **Email**, **WhatsApp**, **Telegram**, **Webhook**.
📖 **[Documentação completa de Alertas](/dataviz/04-features/alerts/)**: visão geral, tipos de disparo, conteúdos, canais, permissões.
***
## 2. Apresentações
Ideal para **TVs corporativas** ou reuniões de gestão, o módulo de Apresentações tem dois tipos de item, lado a lado na mesma lista: **Dashboards** (playlist rotativa clássica) e **Decks — Slides Desenhados** (editor estilo PowerPoint, em beta), que podem inclusive conviver na mesma playlist publicada.
### Como Funciona
1. Cria-se uma Apresentação e adicionam-se Dashboards e/ou Decks (podem ser de Aplicações diferentes, ou o mesmo Dashboard com filtros diferentes).
2. Define o tempo de exibição de cada Dashboard (60 segundos) — Decks avançam sozinhos pelos próprios slides.
3. Escolhe a resolução base (Full HD, 4K) para garantir que a escala fique perfeita na tela grande.
### Modos de Exibição
* **Automático**: O sistema rotaciona os itens sozinho conforme o tempo definido.
* **Manual**: O apresentador controla a troca (útil para reuniões onde a discussão em um item pode demorar mais).
### Slides Desenhados (Beta)
Um editor visual, estilo PowerPoint, dentro da própria plataforma: texto, imagens, formas e tabelas montados livremente em cada slide. O diferencial é o **Gráfico BI** — um gráfico real e vivo, inserido direto de um dashboard, com dados de verdade (não um print). Também é possível **gerar a apresentação inteira com IA**: você descreve o tema e a IA monta o texto **e** escolhe os gráficos reais relevantes dos seus dashboards, slide a slide.
📖 [Documentação completa](./apresentacoes/)
***
## 3. Chat com Dados
O **Chat com Dados** é o assistente de IA do Lumo: você pergunta em linguagem natural e ele responde olhando seus dados reais. Está disponível na sidebar de Insights e em janela flutuante.
A peça central que diferencia "responder bem" de "responder de qualquer jeito" são os **fatos** — definições explícitas dos conceitos do seu negócio cadastradas em cada aplicação.
📖 **[Documentação completa do Chat com Dados](/ia/chat/)**: visão geral, como o chat responde, **[Fatos: Ensinando seu Negócio](/ia/chat/fatos)** (a peça central) e exemplos práticos.
***
## 4. Administração de Fatos do Tenant
A tela **Admin > Fatos** é a **consulta global de todos os fatos cadastrados no tenant**, agregando o que está definido em cada aplicação. É uma ferramenta de **administração e auditoria**:
* **Visão consolidada**: ver de uma vez quais conceitos estão definidos em quais aplicações.
* **Detecção de duplicações**: identificar quando o mesmo conceito tem definições diferentes entre aplicações.
* **Auditoria de qualidade**: localizar aplicações com excesso de fatos, fatos órfãos ou nomes pouco descritivos.
::: tip Onde criar e editar fatos
O cadastro e a edição efetiva dos fatos acontecem **dentro de cada aplicação**, na aba **Conceitos IA**. A tela Admin > Fatos é o agregador read-mostly que dá visão de tenant inteiro. Veja **[Aba Conceitos IA](../02-apps/ai-concepts.md)** para o fluxo de cadastro e **[Fatos: Ensinando seu Negócio](/ia/chat/fatos)** para o conceito.
:::
### Colunas Visíveis (O que a IA vê?)
Nem todos os dados devem ser acessíveis à IA. No gerenciamento de colunas, define-se explicitamente:
* Quais colunas a IA pode ler para responder perguntas.
* Isso evita o uso de colunas técnicas (IDs internos, logs) que não fazem sentido para o negócio.
### Ajustar Rótulos com IA
Ao configurar uma Tabela, é possível usar a função **"Ajustar Rótulos com IA"** para que a IA reescreva automaticamente os nomes das colunas em português mais amigável.
* Campos como `dt_vnd` viram "Data da Venda"
* Campos como `vlr_tot` viram "Valor Total"
* Isso melhora tanto a experiência do usuário final quanto a capacidade da IA de entender o contexto.
### Modelos e Custos
O chat oferece dois modos de resposta, selecionáveis pelo usuário:
* **Rápido (1x custo)**: respostas em segundos, para perguntas diretas.
* **Raciocínio (4x-5x custo)**: mais lento, mais cuidadoso, ideal para análises complexas.
***
## 5. Agentes de IA
Um **Agente de IA** é uma persona de investigação reutilizável — nome, o que investigar, como responder e em quais aplicações ela pode consultar dados. O mesmo agente pode responder no chat e/ou ser usado por um alerta, que continua decidindo quando disparar e para quem enviar.
Dois agentes já vêm prontos (**Lumia**, no chat, e o **Agente padrão de investigação**, que roda os alertas sem agente próprio), e é possível criar agentes personalizados descrevendo em uma frase o que se quer acompanhar.
📖 **[Documentação completa de Agentes de IA](/ia/agentes)**: como criar, como o alerta importa um agente, os passos da investigação e o que o agente não faz.
::: tip Recurso em beta
Disponível mediante habilitação pelo administrador do tenant.
:::
***
## 6. Conectar Assistente de IA (MCP)
O card **Use no MCP**, dentro do módulo Agentes de IA, conecta assistentes de IA externos — Claude, Claude Code, Cursor — diretamente aos dados das suas Aplicações via **MCP (Model Context Protocol)**. Você cola o endereço do servidor no Claude e autoriza com um clique (ou gera um token pessoal, nos assistentes que pedem token), e ele passa a consultar indicadores e dados com as suas permissões. Cada consulta retorna um link de relatório auditável no Lumo.
📖 **[Documentação completa: Conectar seu Assistente de IA (MCP)](/ia/mcp)**
::: tip Recurso em beta
Disponível mediante habilitação pelo administrador do tenant.
:::
***
## 7. Dashboards Pessoais
Os Dashboards Pessoais permitem que usuários finais criem suas próprias visualizações dentro de aplicações publicadas, sem depender de equipes técnicas.
### Principais Recursos
* **Criação do Zero ou Clonagem**: Crie Dashboards vazios ou baseados em existentes.
* **Edição Completa**: Adicione, configure e organize Widgets livremente.
* **Compartilhamento**: Compartilhe suas visões com colegas do tenant.
* **Isolamento**: Suas visões não afetam a aplicação original nem outros usuários.
### Requisitos
* A aplicação deve estar **publicada** (não funciona em "Minha Mesa")
* A funcionalidade deve ser habilitada pelo administrador no **HEC > Tenants > Avançado**
📖 [Documentação completa de Dashboards Pessoais](./personal-dashboards.md)
---
---
url: 'https://docs.horusbi.com.br/dataviz/04-features/apresentacoes/ia.md'
---
# Gerar Apresentação com IA
Montar um Deck do zero, slide a slide, leva tempo. Dentro do Editor de Slides Desenhados, um botão de **IA** abre um assistente que monta a apresentação inteira por você — **texto e gráficos reais juntos**.
> \[!IMPORTANT]
> Geração por IA está em **fase beta**, junto com Slides Desenhados.
***
## 🚀 Como gerar
1. No Editor de Slides Desenhados, clique no botão de **IA**
2. Descreva o **tema** da apresentação (ex.: *"resultados do trimestre para a diretoria"*)
3. Escolha o **modelo de IA**
4. Confirme — a apresentação vai sendo montada **em tempo real**, slide a slide, na tela (streaming), então você acompanha o resultado tomando forma em vez de esperar no escuro
***
## 🧠 Não é só texto: a IA escolhe gráficos reais
A diferença desse gerador para um "gerador de slides" comum é que ele **não inventa números nem ilustrações genéricas**. A IA:
1. Olha os **dashboards e gráficos aos quais você tem acesso**
2. Escolhe os gráficos **relevantes para o tema** descrito, slide a slide
3. Insere cada um como um **[Gráfico BI](./grafico-bi.md)** de verdade — com dados reais, não uma imagem
Ou seja: o slide de "vendas do trimestre" vem com o gráfico de vendas de verdade, já plugado nos seus dados — não um placeholder para você trocar depois.
> \[!NOTE]
> A IA só enxerga e usa dados e gráficos aos quais **você** tem acesso. As mesmas permissões da plataforma valem aqui — a geração não contorna nenhuma restrição.
***
## ✏️ Revisando o resultado
O Deck gerado é um **ponto de partida editável**, não um resultado final e fechado. Depois de gerado, use as ferramentas normais do editor para ajustar:
* Trocar um gráfico por outro mais adequado
* Ajustar filtros, título, tema ou escala de cada [Gráfico BI](./grafico-bi.md)
* Editar o texto, reordenar ou remover slides
* Congelar os dados dos gráficos se for apresentar sem depender de conexão
> \[!TIP]
> Se o resultado sair genérico, peça de novo com uma descrição mais específica — quanto mais contexto no tema (público-alvo, período, o que priorizar), melhor a escolha de gráficos e o texto gerado.
***
## 🗺️ Próximos passos
* **[Gráfico BI](./grafico-bi.md)** — ajuste fino de cada gráfico inserido pela IA.
* **[Editor de Slides Desenhados](./editor.md)** — edição manual, salvar, apresentar e exportar.
* **[Modo Kiosk](./kiosk.md)** — publique o Deck gerado para rodar numa TV.
---
---
url: 'https://docs.horusbi.com.br/etl/guides/agentes.md'
---
# Gerenciamento de Agentes
O **Agente** é o componente fundamental do HorusETL — é o software que reside na sua infraestrutura (ou na nuvem) e executa o processamento de dados. O painel de Gerenciamento de Agentes permite criar tokens de autenticação, monitorar o status e realizar manutenções remotas.
> \[!TIP]
> **Multi-Tenancy**: Um único agente pode atender múltiplos tenants simultaneamente. É possível ter 100 ou mais tenants conectados ao mesmo agente, economizando recursos de infraestrutura.
***
## 🔑 Criando um Novo Agente
Para instalar um novo agente, você primeiro precisa criar um **Token** — a chave que vincula o executável na sua máquina ao seu ambiente no Horus.
1. Acesse o menu **Agentes**
2. Clique no botão **Criar novo Agente**
3. Preencha as configurações iniciais:
* **Descrição** — Um nome para identificar onde este agente está rodando (`Servidor-Principal-01`, `Laptop-Dev`)
* **Agendamentos Simultâneos (Paralelismo)** — Define quantos fluxos este agente pode executar ao mesmo tempo. Recomendação: entre 2 e 4 para máquinas padrão; para servidores mais potentes, aumente para 10, 16 ou mais. Atenção: agendamentos de um mesmo tenant são serializados, e o paralelismo atua **entre tenants diferentes** (ver [Requisitos de Hardware](./requisitos-hardware) para entender o impacto no dimensionamento)
4. Clique em **Salvar**
5. O sistema irá gerar um **Token**. Copie este código e use-o durante a instalação do agente ou na variável de ambiente Docker
> \[!NOTE]
> O token é exibido apenas uma vez por motivos de segurança. Se perdê-lo, gere um novo ou consulte a opção de visualização de token (ícone de "olho"), se tiver permissão.
***
## 📡 Monitoramento de Status
Na listagem de agentes, você pode acompanhar o status em tempo real:
| Status | Indicador | Descrição |
|--------|-----------|-----------|
| **Online** | 🟢 Verde | O agente está conectado e pronto para receber comandos |
| **Offline** | 🔴 Vermelho | O agente não está se comunicando — verifique se o serviço está rodando e se há conexão com a internet |
Ao clicar em um agente, você vê detalhes técnicos como:
* Nome do Servidor (Hostname)
* Sistema Operacional
* Uso de CPU e Memória (RAM)
* Versão do Agente
***
## 🔄 Atualização de Versão
O HorusETL recebe atualizações frequentes com novos conectores e correções.
> \[!NOTE]
> **Auto-Atualização Linux**: Quando um agente inicia, ele automaticamente verifica com o backend qual é a versão mais recente disponível. Se detectar que está desatualizado, ele baixa a nova versão e aplica a atualização automaticamente.
### Agentes Windows
Agentes instalados via MSI no Windows podem ser atualizados remotamente:
1. Abra o painel do Agente
2. Se houver uma atualização disponível, o botão **Atualizar Agente** estará visível
3. Clique no botão — o agente irá baixar a nova versão, parar o serviço, aplicar a atualização e reiniciar automaticamente (processo leva alguns minutos)
### Agentes Linux (Docker)
Agentes rodando em Docker **não** são atualizados pelo botão do painel:
1. A atualização é automática ao reiniciar o container
2. Simplesmente reinicie o agente para que ele baixe a nova imagem e aplique as mudanças
***
## 🔧 Ações de Manutenção
Dentro do painel do agente, as seguintes ações de emergência estão disponíveis:
| Ação | Descrição |
|------|-----------|
| **Reiniciar Agente** | Envia um comando para o serviço reiniciar. Útil se o agente estiver travado ou com comportamento estranho |
| **Zerar Fila** | Remove todos os agendamentos pendentes que ainda não começaram a rodar. Útil se você disparou acidentalmente muitas execuções e quer cancelar |
| **Transferir Dataflows** | Move a responsabilidade de execução de fluxos de um agente antigo para um novo |
***
## ⚠️ Troubleshooting: Múltiplas Réplicas
Se você vir um alerta amarelo de **Múltiplas Réplicas Detectadas**, significa que **dois ou mais computadores diferentes estão usando o mesmo Token**.
> \[!WARNING]
> Nunca use o mesmo token em máquinas diferentes simultaneamente. Isso causa um conflito onde os agentes disputam as tarefas, gerando erros intermitentes e logs espalhados.
* **Solução** — Desligue os agentes excedentes ou crie um novo Token para cada máquina adicional
---
---
url: 'https://docs.horusbi.com.br/dw/tables.md'
---
# Gerenciamento de Tabelas
No HorusDW, uma tabela não é apenas um local de armazenamento — é um objeto governado com diferentes propósitos, permissões e regras de edição dependendo de onde ela está localizada.
***
## 📦 Tipos de Tabelas
A grade de tabelas pode exibir diferentes ícones e tipos. Cada tipo tem suas características:
| Tipo | Descrição | Origem | Uso Principal |
|------|-----------|--------|---------------|
| **[Tabela Física](./physical)** | Dados armazenados no Lakehouse, prontos para consulta rápida | Upload de Excel ou Dataflows processados | Dashboards e Análises |
| **[Tabela Cloud (Datalake)](./datalake)** | Tabela virtual que aponta para arquivos Parquet no Datalake | Ingestão de dados brutos | Reutilização em processos de ETL |
| **Tabela Estática** | Tabela criada a partir de arquivos Excel diretamente no HorusDW | Upload manual | Dados auxiliares e de referência |
| **[Cadastro](./cadastros/)** | Tabela editável à mão (formulário + grade) que alimenta o BI | Digitação na plataforma | Metas, de-para, parâmetros |
***
## ✏️ Edição e Governança
O que você pode editar depende de **onde** a tabela está localizada:
### Na "Minha Mesa" (Sandbox)
Você tem controle total sobre a tabela:
* **Pode** — Alterar nomes de colunas, tipos de dados (Metadata), criar Fórmulas, definir Chaves Primárias
* **Pode** — Truncar, Excluir, Adicionar novos arquivos (se for tabela Excel)
### Em Mesas Publicadas (Comercial, Gold)
A tabela é governada. Edições estruturais são restritas para proteger Dashboards e processos que dependem dela:
* **Pode** — Apenas ajustes estéticos (Rótulos/Labels visuais, Máscaras de formatação)
* **Não Pode** — Mudar tipos físicos, alterar chaves primárias
### 🪄 FixLabels (IA)
Na tela de edição, o botão com ícone de estrelas (`✨`) aciona a Inteligência Artificial para normalizar a tabela automaticamente:
* **Normalizar Nomes** — Transforma códigos técnicos (`nm_cli`) em nomes legíveis (`Nome Cliente`)
* **Ajustar Tipos** — Sugere máscaras e comportamentos adequados para cada coluna
***
## 🔐 Permissões
Na listagem de tabelas, você verá a coluna **"Pessoas com Acesso"**.
> \[!NOTE]
> A permissão não é concedida individualmente por tabela, mas sim através de **Datamarts**. Se um usuário tem acesso ao Datamart *Comercial*, ele verá todas as tabelas vinculadas a esse Datamart.
---
---
url: 'https://docs.horusbi.com.br/hec/resources/clients.md'
---
# Gestão de Clientes
O **Cliente** é a entidade de nível superior na hierarquia do Horus. Ele representa a organização que licenciou a plataforma (uma consultoria de dados, uma software house, uma holding, etc.) e agrupa um ou mais [Tenants](./tenants.md) sob sua gestão.
A tela de edição do Cliente contém diversas abas de configuração. Esta página foca na aba **Usuários**, que é onde se gerenciam os membros da equipe do Cliente, seus níveis de acesso e a funcionalidade de impersonação.
> \[!NOTE]
> Para configurações das demais abas do Cliente (Geral, Suites/Visual, ChatBot, Avançado, Billing, Desenvolvedor, Tenants, Instâncias de WhatsApp), consulte a seção [Configurações do Cliente](./tenants.md#configuracoes-do-cliente) na documentação de Gestão de Tenants.
***
## 👥 Aba Usuários
A aba Usuários exibe todos os membros vinculados ao Cliente, permitindo adicionar ou remover pessoas, definir o nível de acesso de cada uma e configurar permissões de impersonação.
### Visão Geral da Tela
No topo da aba, um painel informativo mostra:
* **Usuários do Cliente**: Quantidade atual de usuários em relação ao limite contratado (`4 / 3`)
* **Extras**: Caso o número de usuários exceda o limite, a quantidade extra é destacada junto ao custo adicional estimado
> \[!WARNING]
> Usuários acima do limite contratado geram cobrança adicional conforme o valor definido na configuração de billing do Cliente.
Abaixo do painel, estão disponíveis:
* **Botão "+ Adicionar"**: Para vincular novos usuários ao Cliente
* **Campo de busca**: Filtre por nome ou login (e-mail)
* **Filtros por nível**: Botões que filtram a lista por nível de acesso (Todos, Superadministrador, Implantador, Visualizador), cada um exibindo a contagem de usuários naquele nível
### Adicionando Usuários
Para adicionar um membro à equipe do Cliente:
1. Utilize o campo de busca ao lado do botão "+ Adicionar" para localizar o usuário pelo nome ou login.
2. Selecione o usuário desejado na lista de sugestões.
3. Clique em **+ Adicionar**.
O usuário será adicionado com o nível **Visualizador** por padrão. Você pode alterar o nível de acesso usando o seletor na coluna "Nível de Acesso".
> \[!IMPORTANT]
> Ao adicionar um usuário ao Cliente, ele automaticamente recebe acesso a **todos os Tenants** daquele Cliente. As alterações só são efetivadas após clicar em **Salvar**.
### Removendo Usuários
Para remover um membro:
1. Clique no ícone de lixeira na coluna "Ações" do usuário que deseja remover.
2. O usuário será marcado visualmente para remoção (fundo avermelhado e texto riscado).
3. Caso mude de ideia, clique no ícone de desfazer que aparece no lugar da lixeira.
4. Clique em **Salvar** para efetivar a remoção.
> \[!NOTE]
> A remoção só é efetivada ao salvar. Enquanto não salvar, é possível desfazer a ação.
***
## 🔐 Níveis de Acesso
Cada usuário do Cliente possui um **nível de acesso** que determina o que ele pode fazer na plataforma. Existem três níveis:
### Superadministrador
O nível mais alto de acesso no contexto do Cliente. O Superadministrador tem controle total sobre a gestão do Cliente e seus Tenants.
**Permissões:**
* Criar novos Tenants
* Gerenciar dados do Cliente (aba "Gerenciar Dados" no menu)
* Gerenciar usuários do Cliente (adicionar, remover, alterar níveis)
* Acessar o Dashboard de Faturamento
* Exportar relatórios de billing
* Visualizar custos na Home do HEC
* Gerenciar instâncias de WhatsApp
* Acessar configurações avançadas do Cliente
### Implantador
Um nível intermediário, focado na operação e implantação de ambientes. O Implantador pode criar e configurar Tenants, mas não tem acesso à gestão administrativa do Cliente em si.
**Permissões:**
* Criar novos Tenants
* Acessar o Dashboard de Faturamento
* Exportar relatórios de billing
* Visualizar custos na Home do HEC
* Gerenciar instâncias de WhatsApp
**Restrições:**
* Não pode gerenciar dados do Cliente (a opção "Gerenciar Dados" não aparece no menu)
* Não pode adicionar, remover ou alterar níveis de outros usuários do Cliente
### Visualizador
O nível mais restrito. O Visualizador acessa os Tenants do Cliente como um usuário comum, sem privilégios administrativos.
**Permissões:**
* Acessar os Tenants do Cliente como usuário normal
* Visualizar custos na Home do HEC (quando disponível)
**Restrições:**
* Não pode criar Tenants
* Não pode gerenciar dados do Cliente
* Não pode gerenciar outros usuários do Cliente
* Não tem acesso a menus administrativos de Cliente ou Tenant no menu lateral
### Faturamento (somente leitura)
Nível para quem precisa **apenas consultar o Relatório de Uso** do cliente, sem nenhum acesso administrativo. Quem recebe esse nível:
* **Vê:** o Relatório de Uso/Faturamento do cliente, com todos os Tenants, em modo somente leitura.
* **Não vê / não faz:** os dados dos Tenants (aplicações, tabelas, dataflows, dashboards, usuários), fechar ou reabrir faturas, nem qualquer outra área administrativa.
Diferente dos demais níveis, a atribuição é **aditiva**: ela apenas libera a entrada no HEC e o Relatório de Uso, sem alterar nenhum acesso que o usuário já tenha em outros ambientes. Para atribuir, basta escolher **Faturamento (somente leitura)** no seletor de nível ao adicionar ou editar o usuário do cliente.
### Tabela Comparativa
| Funcionalidade | Superadministrador | Implantador | Visualizador | Faturamento (somente leitura) |
|:---|:---:|:---:|:---:|:---:|
| Criar Tenants | Sim | Sim | Não | Não |
| Gerenciar dados do Cliente | Sim | Não | Não | Não |
| Gerenciar usuários do Cliente | Sim | Não | Não | Não |
| Dashboard / Relatório de Faturamento | Sim | Sim | Sim | Sim |
| Exportar relatórios de billing | Sim | Sim | Sim | Não |
| Fechar / reabrir fatura | Sim | Não | Não | Não |
| Ver custos na Home do HEC | Sim | Sim | Sim | Não |
| Gerenciar instâncias de WhatsApp | Sim | Sim | Não | Não |
| Acessar Tenants como usuário normal | Sim | Sim | Sim | Não |
> \[!TIP]
> Ao adicionar um novo membro, ele sempre entra como **Visualizador**. Altere o nível antes de salvar caso seja necessário um acesso mais amplo.
***
## 🧭 Menu Lateral por Nível
O menu lateral do HEC se adapta de acordo com o nível de acesso do usuário do Cliente:
**Superadministrador** vê no menu:
* Tenants: Criar Tenant e Lista de Tenants
* Clientes: Gerenciar Dados
**Implantador** vê no menu:
* Tenants: Criar Tenant e Lista de Tenants
**Visualizador** não vê opções administrativas de Cliente ou Tenant no menu lateral. Ele navega pelos Tenants como um usuário comum, utilizando os menus padrão do HEC.
***
## 🔄 Impersonação
A funcionalidade de **impersonação** permite que um usuário do Cliente acesse a plataforma "como se fosse" outro usuário. Isso é útil para suporte, diagnóstico de problemas ou validação de configurações de permissão — sem precisar solicitar as credenciais do usuário final.
### Como Funciona
1. **Habilitação**: Na aba Usuários da edição do Cliente, marque a checkbox "Impersonar" ao lado do usuário que **terá permissão de impersonar** outros usuários.
2. **Salvamento**: Clique em **Salvar** para efetivar a permissão.
3. **Uso**: O usuário com permissão de impersonar verá a opção de impersonação no menu lateral do HEC. Ao clicar, uma lista de usuários impersonáveis será exibida.
4. **Seleção**: Ao selecionar um usuário da lista, o sistema fará login como aquele usuário, mantendo a rastreabilidade.
### Quem Pode Ser Impersonado
A impersonação possui restrições de segurança. Ao ativar a permissão para um usuário, ele poderá impersonar **apenas**:
* **Usuários finais dos Tenants** que pertencem ao mesmo Cliente
* Usuários que estejam **ativos** no sistema
* Usuários vinculados a **Tenants ativos** daquele Cliente
**Não podem ser impersonados:**
* Outros usuários do Cliente (Superadministradores, Implantadores ou Visualizadores)
* Administradores da plataforma Horus
> \[!IMPORTANT]
> A permissão de impersonar é concedida no nível do **Cliente**, não por usuário-alvo individual. Isso significa que, ao habilitar a impersonação para um membro, ele poderá impersonar qualquer usuário final elegível dos Tenants daquele Cliente.
### 🔍 Auditoria
Todas as ações realizadas durante uma sessão de impersonação são registradas no log de auditoria **vinculadas ao usuário original** (quem está impersonando), não ao usuário impersonado. Isso garante rastreabilidade completa.
Na primeira vez que um usuário utiliza a funcionalidade de impersonação, um aviso de auditoria será exibido informando que todas as ações continuarão sendo registradas em seu nome. Após reconhecer o aviso, ele não será exibido novamente.
> \[!WARNING]
> A impersonação é uma ferramenta poderosa. Conceda essa permissão apenas a membros de confiança que necessitem realizar suporte ou validações nos ambientes dos clientes finais.
### Retornando ao Acesso Original
Após concluir a tarefa como usuário impersonado, utilize a opção **"Voltar ao meu acesso"** disponível no menu lateral para encerrar a sessão de impersonação e retornar ao seu usuário original.
***
## 💰 Custos e Faturamento
Usuários do Cliente têm acesso a informações de custos e faturamento em diferentes pontos da plataforma. A visibilidade dos dados financeiros está disponível para **todos os níveis de acesso** (Superadministrador, Implantador e Visualizador), desde que o Cliente possua configuração de billing ativa.
### Home do HEC
Na tela inicial do HEC, os cards de resumo exibem o consumo do Tenant atual junto com os custos associados (quando disponíveis). Os custos são apresentados entre parênteses ao lado de cada métrica:
| Card | Informação Exibida |
|:---|:---|
| **Usuários** | Total de usuários ativos e custo associado |
| **Processamento** | Volume processado (GB) e custo |
| **Tokens de IA** | Tokens consumidos e custo |
| **CNPJs** | Quantidade de CNPJs vinculados e custo (exibido apenas quando aplicável) |
Abaixo dos cards individuais, um totalizador apresenta o **custo total estimado** do Tenant no período selecionado.
> \[!NOTE]
> Os custos só aparecem nos cards quando o Tenant pertence a um Cliente com configuração de billing ativa. Em Tenants sem billing configurado, apenas os dados de consumo (sem valores monetários) são exibidos.
### Dashboard de Faturamento
O botão **Dashboard de Faturamento** aparece na tela de edição do Cliente quando existe configuração de billing ativa. Ele abre uma página dedicada com visão detalhada de:
* Custos por categoria (usuários, processamento, IA, CNPJs)
* Histórico de consumo e faturamento
* Detalhamento por Tenant
### 📄 Exportar Relatórios
O botão **Exportar Relatórios** permite gerar relatórios de faturamento nos seguintes formatos:
| Formato | Descrição |
|:---|:---|
| **PDF** | Relatório formatado para impressão e compartilhamento |
| **Excel** | Planilha com dados detalhados para análise |
| **ZIP** | Pacote contendo PDF e Excel |
Os relatórios podem ser gerados por período mensal ou por intervalo de datas personalizado.
***
## 📊 Quota de Usuários
O sistema controla a quantidade de usuários do Cliente com base na configuração de billing:
* **Limite contratado**: Quantidade de usuários incluídos no plano (3 usuários)
* **Usuários extras**: Cada usuário além do limite gera um custo adicional por usuário (R$ 90,00/usuário extra)
* O painel no topo da aba Usuários exibe essa informação de forma clara, incluindo um alerta quando o limite é ultrapassado
> \[!TIP]
> Acompanhe regularmente a relação entre usuários ativos e o limite contratado para evitar custos adicionais inesperados. Considere remover usuários que não precisam mais de acesso ao nível do Cliente.
***
## 👥 Grupos Padrão
A configuração de **Grupos Padrão** permite que o Cliente defina previamente uma lista de [Grupos](../users-groups/groups.md) que serão criados automaticamente em **todos os novos Tenants** vinculados a ele. É útil para padronizar a estrutura inicial de permissões — por exemplo, garantir que todo Tenant nasça com grupos como *"Analistas Financeiros"*, *"Operação"* ou *"Visualizadores Comerciais"* já configurados, sem que cada Tenant precise ser ajustado manualmente após a criação.
### Onde Configurar
A definição é feita na tela de edição do Cliente:
1. Acesse a edição do Cliente.
2. Vá até a aba **Avançado**.
3. Localize a seção **Grupos Padrão**.
4. Adicione, edite ou remova grupos conforme a necessidade.
5. Para cada grupo, defina:
* **Nome** do grupo
* **Suites** às quais o grupo terá acesso (por exemplo: `bi`, `hec`)
* **Funções do Sistema** com as permissões granulares: leitura, criação, atualização, exclusão e publicação
6. Clique em **Salvar**.
> \[!NOTE]
> O botão **Restaurar padrões** preenche a lista com os mesmos 3 grupos do fallback do sistema (*DataViz*, *Visualizadores* e *Desenvolvedor*), porém o *Desenvolvedor* vem **sem a função `developer`** — esse privilégio é reservado a administradores e não pode ser concedido via Grupos Padrão.
> Se o objetivo é ter um grupo *Desenvolvedor* com a função `developer`, basta deixar a lista vazia: o sistema aplicará o fallback completo automaticamente.
### Comportamento
A criação dos grupos acontece automaticamente no momento em que um novo Tenant do Cliente é provisionado, seguindo as regras abaixo:
| Cenário | Grupos Criados no Novo Tenant |
|:---|:---|
| Cliente **não** definiu grupos padrão (lista vazia ou ausente) | **Administradores** + grupos de fallback do sistema (*DataViz* com suite `bi`, *Visualizadores* com suite `bi` e *Desenvolvedor* com função `developer` + suite `hec`) |
| Cliente definiu grupos padrão | **Administradores** + os grupos definidos pelo Cliente |
> \[!IMPORTANT]
> O grupo **Administradores** é sempre criado pelo sistema em todo Tenant novo, independentemente da configuração do Cliente. Não é necessário (nem permitido) incluí-lo na lista de grupos padrão.
### Validações
Ao definir os grupos padrão, o sistema aplica as seguintes regras:
* **Sem grupo do tipo `admin`**: o tipo administrador é exclusivo do grupo *Administradores* criado pelo sistema. Apenas grupos do tipo `group` são aceitos na lista.
* **Sem função `developer`**: o privilégio de desenvolvedor é reservado a administradores e não pode ser concedido por meio dos grupos padrão.
* **Nomes únicos**: não é permitido cadastrar dois grupos com o mesmo nome na lista.
### O que **não** é afetado
* **Tenants existentes** mantêm seus grupos atuais. A configuração de Grupos Padrão **não** altera, cria ou remove grupos de Tenants já provisionados — vale apenas para Tenants criados **após** a definição.
* **Grupos manuais**: depois que o Tenant é criado, os administradores do Tenant continuam podendo criar, editar e remover grupos normalmente, conforme descrito em [Gestão de Grupos](../users-groups/groups.md).
> \[!TIP]
> Use os Grupos Padrão para refletir a estrutura organizacional típica dos seus Tenants. Se o seu Cliente atende sempre o mesmo tipo de operação (por exemplo, varejo), pré-configurar grupos como *"Compras"*, *"Vendas"* e *"Financeiro"* economiza tempo de implantação a cada novo Tenant.
---
---
url: 'https://docs.horusbi.com.br/hec/users-groups/groups.md'
---
# Gestão de Grupos
Grupos são a forma mais eficiente de gerenciar permissões no HEC. Ao invés de conceder acesso individualmente a cada colaborador, você cria um grupo ("Analistas Financeiros"), define as permissões uma única vez e simplesmente adiciona os usuários ao grupo. Quando alguém muda de equipe ou sai da empresa, basta removê-lo do grupo — sem precisar ajustar permissões uma a uma.
> \[!NOTE]
> Clientes podem pré-definir os grupos padrão criados automaticamente em novos Tenants — veja [Clientes — Grupos Padrão](../resources/clients.md#👥-grupos-padrao).
## ➕ Criando um Grupo
1. Acesse **Usuários > Grupos**.
2. Clique em **+ Novo Grupo**.
3. Defina um **Nome** claro que reflita a função do grupo (*Gerentes de Vendas*, *Desenvolvedores*, *Visualizadores de RH*).
## ✏️ Editando Grupos e Membros
Na tela de edição do grupo, você tem controle sobre quem faz parte e o que os membros podem fazer.
### Aba Geral
* **Nome do Grupo**: Edite a identificação do grupo
* **Adicionar Usuários**: Use a barra de busca para encontrar usuários existentes no Tenant e vincule-os ao grupo
* **Remover Usuários**: Na lista de membros, use o ícone de lixeira para desvincular um usuário
> \[!TIP]
> Um usuário pode pertencer a **múltiplos grupos**. As permissões são somadas — se um grupo concede acesso à Mesa A e outro à Mesa B, o usuário verá ambas.
### Aba [Acesso aos Dados](./permissions.md#acesso-aos-dados)
Define quais Mesas e Tabelas ficam visíveis para **todos os membros deste grupo**.
* Exemplo: Ao adicionar a "Mesa Financeira" aqui, todos os membros do grupo passam a visualizá-la automaticamente
### Aba [Funções do Sistema](./permissions.md#funcoes-do-sistema)
Define o que os membros podem **fazer** na plataforma (Criar novas mesas, Editar Dataflows, Publicar aplicações).
---
---
url: 'https://docs.horusbi.com.br/dw/datamarts/managing-access.md'
---
# Gestão de Permissões e Segurança
Como dono de um Datamart, você tem controle granular sobre como os dados são consumidos pelos usuários. Ao clicar em **Gerenciar** em uma tabela, você acessa o painel de governança com as opções abaixo.
***
## ⏰ Segurança Temporal (Validade)
Defina uma **Data de Expiração** para o acesso de um usuário ou grupo.
* **Para que serve** — Ideal para auditores externos, estagiários ou projetos temporários
* **Comportamento** — Após a data definida, o usuário perde a visualização da tabela automaticamente, sem necessidade de ação manual
***
## 📤 Governança de Dados (Exportação)
O campo **Permite Exportação** define se o usuário pode baixar os dados (Excel/CSV) através dos Dashboards.
> \[!TIP]
> Mantenha desmarcado para dados sensíveis (PII) para evitar vazamento de informações.
> \[!IMPORTANT]
> **O bloqueio de Mesa de Dados prevalece sobre este grant.** Além da permissão de Datamart (ler/exportar), o Horus honra o **bloqueio da Mesa de Dados**: um usuário **bloqueado** na mesa **não lê nem exporta** as linhas — mesmo que você tenha marcado "Permite Exportação" e concedido acesso ao datamart. Para o usuário efetivamente consumir/exportar, ele precisa **ao mesmo tempo** ter o grant de datamart **e** não estar bloqueado na mesa. Ver **[Controle de Acesso às Mesas](/dw/desks/controle-de-acesso)**.
***
## 🔒 Row Level Security (RLS) — Segurança por Linhas
No botão **Restrições**, aplique filtros obrigatórios que o usuário não pode remover durante a navegação.
* **Exemplo** — Para o "Gerente Regional Sul", aplique o filtro `Regiao = 'SUL'`
* **Resultado** — Ele verá a tabela de Vendas completa, mas apenas as linhas da região Sul. Para ele, é como se as outras regiões não existissem
***
## 🔐 Column Level Security (CLS) — Segurança por Colunas
No botão **Colunas Bloqueadas**, oculte campos específicos para determinados usuários ou grupos.
* **Exemplo** — A tabela de Funcionários pode ser visível para o RH, mas a coluna `Salário` deve ficar oculta para estagiários
* **Resultado** — O usuário acessa a tabela e cria gráficos normalmente, mas a coluna `Salário` não aparece na lista de campos disponíveis para ele
---
---
url: 'https://docs.horusbi.com.br/hec/resources/tenants.md'
---
# Gestão de Tenants
O **Tenant** é a unidade fundamental de organização no Horus. Ele representa um ambiente isolado onde residem todos os seus dados, usuários, aplicações e configurações. Um cliente whitelabel pode ter múltiplos tenants.
> \[!TIP]
> **Hierarquia de Configurações**: Cliente → Tenant\
> Muitas configurações definidas no **Cliente** servem como padrão para seus **Tenants**. Porém, o Tenant pode sobrescrever essas configurações para personalizar seu próprio ambiente.
## 📝 Abas de Configuração do Tenant
Ao acessar a edição de um Tenant, você encontrará diversas abas para gerenciar cada aspecto do ambiente:
***
### Geral
O painel de controle principal do tenant.
#### Informações Básicas
| Campo | Descrição |
|-------|-----------|
| **Cliente** | Vínculo com o Cliente pai (apenas visível se existirem múltiplos clientes). |
| **Nome** | Nome de identificação do tenant. |
#### Identidade Visual
| Campo | Descrição |
|-------|-----------|
| **Logo** | Imagem de logotipo exibida no topo da aplicação. Formatos aceitos: PNG, JPG, JPEG. Máximo 5MB. |
| **Favicon** | Ícone exibido na aba do navegador. Formatos aceitos: PNG, JPG, JPEG. Máximo 5MB. |
| **Imagem de Login** | Imagem hero exibida na tela de login. Formatos aceitos: PNG, JPG, JPEG. Máximo 5MB. |
#### Domínio e Configurações
| Campo | Descrição |
|-------|-----------|
| **Domínio(s)** | URLs personalizadas para acesso ao tenant (`analytics.suaempresa.com.br`). Permite múltiplos domínios separados por vírgula. |
| **Instruções do ChatBot** | Prompt base personalizado para a IA do assistente virtual neste ambiente. Essas instruções orientam o comportamento do chatbot ao responder perguntas dos usuários. |
| **Status** | Ativa ou suspende o acesso ao tenant. Quando desativado, usuários não conseguem acessar o ambiente. |
#### Recursos do Tenant
Visualização e edição dos limites contratados para este tenant:
* **Usuários**: Limite de usuários simultâneos
* **CNPJs**: Quantidade de CNPJs vinculados (quando aplicável via billing)
* **Processamento (GB/mês)**: Limite de processamento de dados mensal
* **Tokens de IA (mês)**: Limite de tokens de IA consumidos mensalmente
> \[!NOTE]
> Os recursos são calculados com base na configuração de billing do Cliente. O Tenant pode adicionar "extras" além da base calculada pelos CNPJs.
***
### Suites/Visual
Personalize a aparência de cada módulo (suite) do Horus independentemente.
#### Configuração por Módulo
Para cada módulo (**DataViz**, **ETL**, **HEC**, **DW**):
| Campo | Descrição |
|-------|-----------|
| **Título** | Nome personalizado do módulo exibido na interface. |
| **Domínio** | URL específica para acesso direto ao módulo. |
| **Logo** | Logotipo personalizado para o módulo. |
#### Personalização Visual da Barra de Filtros
Ajuste as cores da barra de filtros globais para alinhar com a identidade visual da sua marca:
| Campo | Descrição |
|-------|-----------|
| **Usar cores personalizadas** | Ativa a personalização de cores (desabilitado usa as cores padrão). |
| **Cores dos filtros** | Array de cores em formato hexadecimal (`#4E46DD`) usadas na barra de filtros. Adicione ou remova cores conforme necessário (mínimo 1, máximo 10). |
| **Cor do botão de filtro** | Cor do botão de aplicar filtros na barra. |
> \[!TIP]
> Use o preview de cores para visualizar como ficará a barra de filtros antes de salvar as alterações.
***
### ChatBot
Integração com Telegram para funcionalidades de assistente virtual.
| Campo | Descrição |
|-------|-----------|
| **Nome de usuário do Telegram** | Username do bot do Telegram (`@meu_bot`). |
| **Token do Telegram** | Token de API fornecido pelo BotFather do Telegram. |
***
### Usuários
Gestão direta dos usuários que têm acesso a este tenant.
| Ação | Descrição |
|------|-----------|
| **Adicionar Usuário** | Busque e adicione usuários existentes na plataforma digitando nome ou email. |
| **Remover Acesso** | Revogue o acesso de usuários clicando no ícone de lixeira. |
| **Recuperar Acesso** | Desfaz a remoção antes de salvar clicando no ícone de desfazer. |
> \[!IMPORTANT]
> As alterações de usuários só são efetivadas após clicar em "Salvar Usuários".
***
### Templates
Gerencie os templates instalados neste tenant.
* Visualize quais templates estão ativos
* Instale novos templates a partir da biblioteca global de product lines
* Remova templates que não são mais necessários
> \[!TIP]
> Ao instalar ou atualizar um template, há uma opção **Espelhar template** que, além do apply de sempre, poda do tenant o conteúdo de produto fora do template (com preview obrigatório e recuperação via Arquivo). Veja [Espelhar Template (Estado Canônico)](./templates.md#4-espelhar-template-estado-canônico).
***
### Instâncias de WhatsApp
Conecte números de WhatsApp para envio de alertas e interações via chatbot.
O Horus suporta dois modos de integração com WhatsApp:
#### API Não-Oficial (WhatsApp Web)
| Aspecto | Detalhes |
|---------|----------|
| **Configuração** | Escaneie o QR Code para vincular uma nova instância do WhatsApp Web. |
| **Requisito** | É necessário manter um celular ligado 24 horas com o chip ativo. |
| **Vantagem** | Permite enviar mensagens livremente, sem restrições de templates pré-aprovados. |
| **Status** | Gerencie a conexão (Conectado/Desconectado) e reconecte quando necessário. |
#### API Oficial da Meta
| Aspecto | Detalhes |
|---------|----------|
| **Configuração** | Integração via API oficial do WhatsApp Business. |
| **Requisito** | Templates de mensagem devem ser pré-aprovados pela Meta (Facebook). |
| **Limitação** | O usuário deve interagir primeiro antes de receber mensagens completas. |
| **Fluxo de Alertas** | Sistema envia: *"Você tem alertas para receber. Digite OK para receber seus alertas pendentes."* Após o usuário responder "OK", os alertas completos são enviados. |
| **Vantagem** | Não requer celular físico ligado, maior estabilidade. |
> \[!IMPORTANT]
> **Implantação Customizada**: A integração com a API Oficial da Meta requer configuração específica e desenvolvimento sob demanda. Entre em contato com a equipe Horus para viabilizar a implantação no seu ambiente.
***
### Avançado
Configurações técnicas e de infraestrutura.
#### Configuração de Agentes ETL
| Configuração | Descrição | Valores |
|--------------|-----------|---------|
| **Preservar Contexto dos Agentes** | Restringe relacionamentos entre tabelas ao mesmo agente de origem, evitando conflitos de IDs entre sistemas diferentes. **Use quando**: clientes possuem múltiplos sistemas ERP onde o ID "1" pode significar coisas diferentes em cada sistema. | Liga/Desliga |
| **Limite Temporal de Cargas** | Restringe a quantidade de dados históricos carregados em cargas temporais. | Liga/Desliga |
| **Valor do Limite** | Define a janela de tempo para cargas temporais. | Número + Unidade (dias/meses/anos) |
| **Granularidade de Agendamento** | Define a menor unidade de tempo disponível para agendar cargas ETL. "Minutos" permite maior frequência, "Horas" simplifica a interface. | Minutos (padrão) / Horas |
> \[!NOTE]
> O **Limite Temporal** é útil para otimizar performance quando não é necessário carregar todo o histórico. Por exemplo, carregar apenas os últimos 30 dias de vendas.
#### SMTP Customizado
Configure um servidor de email próprio para o envio de notificações deste tenant, substituindo o padrão da plataforma.
| Campo | Descrição |
|-------|-----------|
| **Usar SMTP Customizado** | Ativa a configuração personalizada de email. |
| **Host SMTP** | Endereço do servidor SMTP (`smtp.gmail.com`). |
| **Porta SMTP** | Porta do servidor (normalmente 587 para TLS ou 465 para SSL). |
| **Usuário SMTP** | Usuário para autenticação no servidor. |
| **Senha SMTP** | Senha para autenticação no servidor. |
| **Email Remetente** | Endereço de email que aparecerá como remetente. |
| **Nome Remetente** | Nome que aparecerá como remetente. |
| **Usar SSL/TLS** | Ativa criptografia na conexão. |
| **Testar Configuração SMTP** | Botão para enviar um email de teste e validar as configurações antes de salvar. |
> \[!TIP]
> Use o botão **Testar Configuração SMTP** para validar suas configurações antes de salvar. Um email de teste será enviado para o endereço informado.
#### Template de Email
Personalize o visual dos emails enviados pelo sistema (header, footer, cores, logotipo, etc). O conteúdo específico de cada email é gerado automaticamente pelo sistema — aqui você customiza apenas o **template visual** que envolve esse conteúdo.
| Ação | Descrição |
|------|-----------|
| **Editar Template** | Abre o editor visual de templates MJML. |
##### Editor de Template
O editor utiliza **MJML** (Mailjet Markup Language), uma linguagem responsiva para criação de emails. A interface é dividida em:
| Área | Descrição |
|------|-----------|
| **Editor MJML** | Editor de código com syntax highlighting para escrever o template. |
| **Preview** | Visualização em tempo real do email renderizado. |
| **Variáveis** | Lista de variáveis disponíveis para uso no template. |
| **Imagens** | Gerenciador de imagens para upload e uso no template. |
##### Variáveis Disponíveis
| Variável | Descrição |
|----------|-----------|
| {{\_clientName}} | Nome do cliente ou tenant. |
| {{\_assunto}} | Assunto do email. |
| {{\_conteudo}} | Conteúdo do email (obrigatório). |
> \[!IMPORTANT]
> A variável {{\_conteudo}} é **obrigatória** e deve estar presente no template. Ela será substituída pelo conteúdo específico de cada email enviado pelo sistema.
##### Upload de Imagens
Para usar imagens no template:
1. **Salve o template** primeiro (necessário para habilitar upload).
2. Faça upload da imagem pelo gerenciador.
3. Copie a **URL** ou a **tag MJML** gerada.
4. Use no código do template (``).
##### Hierarquia de Templates
O sistema segue uma ordem de prioridade para determinar qual template usar:
```
1. Template do Tenant (se customizado)
└── 2. Template do Cliente (se customizado)
└── 3. Template Padrão da Plataforma
```
| Status | Descrição |
|--------|-----------|
| **Usando Padrão** | O tenant/cliente está usando o template herdado (do nível superior ou padrão do sistema). |
| **Customizado** | O tenant/cliente possui um template próprio. |
| **Restaurar** | Remove a customização e volta a usar o template do nível superior. |
> \[!TIP]
> Consulte a [documentação oficial do MJML](https://documentation.mjml.io/) para aprender mais sobre componentes disponíveis e boas práticas de email responsivo.
#### Gerador de JWT
Crie chaves para autenticação segura entre o Horus e sistemas externos (ERP, APIs, etc).
| Campo | Descrição |
|-------|-----------|
| **Habilitar Gerador de JWT** | Ativa a funcionalidade de geração de tokens JWT. |
| **Chave JWT** | Chave secreta compartilhada entre Horus e seu sistema externo (até 128 caracteres, mínimo 16). |
| **Gerar Aleatória** | Gera uma chave criptograficamente segura de 128 caracteres. |
| **Testar JWT** | Gera um token de teste e exibe seu payload decodificado. |
##### Caso de Uso: Integração Segura BI ↔ Sistemas Externos
Imagine um Dashboard que lista **pedidos pendentes de aprovação** com um botão "Aprovar Pedido" em cada linha. Ao clicar, o Widget precisa chamar a API do seu ERP para aprovar o pedido.
**O Problema**: Armazenar credenciais de API (tokens, senhas) no código JavaScript do frontend é uma **falha crítica de segurança** — qualquer usuário pode inspecionar o código e roubar as credenciais.
**A Solução**: O Gerador de JWT cria um **mecanismo de confiança** entre dois servidores:
```mermaid
sequenceDiagram
participant Browser as 🖥️ Browser do Usuário
participant Horus as 🔷 Horus Backend
participant ERP as 🏢 Seu ERP/API
Note over Horus,ERP: Mesma Chave JWT configurada em ambos
Browser->>Horus: 1. Widget chama app.generateJWTToken()
Horus-->>Browser: 2. Token assinado + payload
Browser->>ERP: 3. Requisição com token no header
ERP->>ERP: 4. Valida token com a chave
ERP-->>Browser: 5. Resposta (aprovado/rejeitado)
```
**O Fluxo**:
1. Widget Svelte chama `app.generateJWTToken()` — o **backend Horus** gera um token assinado com a chave JWT configurada
2. O token contém: ID do usuário logado, ID do tenant, timestamp
3. Widget envia o token para sua API externa
4. Sua API valida o token usando a **mesma chave JWT** configurada no lado dela
5. Se válido, sua API confia que a requisição veio de um usuário autenticado no Horus
**Payload do Token**:
```json
{
"userId": 123,
"tenantId": 456,
"data": "2024-01-15T10:30:00.000Z"
}
```
> \[!IMPORTANT]
> **Segurança**: A chave JWT **nunca** trafega pelo browser. Ela fica apenas nos servidores (Horus + seu sistema). O que trafega é o token assinado, que pode ser validado mas não forjado sem a chave.
Veja a [documentação completa de Widgets Svelte](./../../dataviz/03-widgets/svelte-custom.md) para exemplos de código.
#### Permissões e Segurança
| Configuração | Descrição | Padrão |
|--------------|-----------|--------|
| **Proteger mesas criadas via templates** | Quando ativado, mesas criadas automaticamente por templates ficam somente leitura, não permitindo a customização de seu conteúdo. | Desligado |
| **Permitir usuários não-admin compartilhar alertas** | Permite que usuários comuns compartilhem seus alertas com outros usuários. | Desligado |
| **Mostrar usuários admin ao compartilhar alertas** | Quando o compartilhamento por não-admins está ativado, define se usuários admin aparecem na lista de destinatários. | Desligado |
| **Usuários não-admin não podem criar apps/editar dados** | Restringe a criação de aplicações e edição de abas de dados apenas para administradores. Se ativado os usuários não administradores com permissão de editar aplicações ainda podem clonar aplicações publicadas para suas mesas, porém só podem editar Dashboards, não podendo editar modelagem de dados e expressões. | Desligado |
| **Permitir usuários criarem Dashboards pessoais em aplicações publicadas** | Permite que usuários criem Dashboards personalizados dentro de aplicações publicadas. Estes Dashboards ficam salvos na conta do usuário e não afetam a aplicação original. | Desligado |
| **Permitir usuários compartilharem Dashboards pessoais** | Quando os Dashboards pessoais estão habilitados, esta opção permite que usuários compartilhem seus Dashboards com outros usuários do tenant. | Desligado |
#### Canais de Alerta
Defina quais canais de notificação estão disponíveis para os usuários configurarem seus alertas:
| Canal | Descrição |
|-------|-----------|
| **WhatsApp** | Envio de alertas via WhatsApp (requer instância configurada). |
| **Telegram** | Envio de alertas via bot do Telegram. |
| **Email** | Envio de alertas para o email cadastrado do usuário. |
| **Webhook** | Envio de alertas para um endpoint HTTP customizado. |
Quando **Webhook** está habilitado:
| Campo | Descrição |
|-------|-----------|
| **URL do Webhook** | Endpoint HTTP que receberá as notificações de alerta. |
| **Documentação** | Botão para visualizar a documentação técnica do payload enviado. |
#### Configuração de IA
Personalize o provedor e modelo de IA usado neste tenant.
| Modo | Descrição |
|------|-----------|
| **Usar configuração do Cliente** | Herda as configurações de IA do Cliente pai. |
| **Usar configuração personalizada** | Define um provedor de IA próprio para este tenant. |
| **Modelo padrão de IA** | Quando não usa configuração personalizada, seleciona o modelo padrão entre os disponíveis na plataforma. |
**Configuração Personalizada de IA:**
| Campo | Descrição |
|-------|-----------|
| **Provedor** | OpenAI, Google Gemini ou OpenRouter. |
| **Chave da API** | Chave de acesso do provedor escolhido. |
| **Modelos Disponíveis** | Lista de modelos que estarão disponíveis (ID e Nome amigável). |
| **Modelo Padrão** | Qual modelo será usado por padrão nas requisições. |
> \[!CAUTION]
> Ao usar configuração personalizada de IA, os custos de utilização serão cobrados diretamente pelo provedor escolhido, não pela plataforma Horus.
***
### Desenvolvedor
Área destinada a integrações via API.
| Item | Descrição |
|------|-----------|
| **Chave de API** | Token de acesso para programar automações e integrações externas. |
| **Visualizar** | Ícone de olho para revelar o token. |
| **Copiar** | Copia o token para a área de transferência. |
| **Regenerar** | Cria um novo token (invalida o anterior). |
| **Documentação** | Links para Lumo Docs, Swagger e Redoc. |
> \[!WARNING]
> Use com cautela: O token dá acesso administrativo aos recursos do tenant. Nunca compartilhe ou exponha publicamente.
***
### Trial
*(Visível apenas se habilitado no Cliente)*
Gerencie o período de degustação do tenant:
* Defina data de expiração do trial
* Configure limites provisórios para o período de testes
***
## 🏢 Configurações do Cliente
O **Cliente** é a entidade pai que agrupa múltiplos Tenants. As configurações do Cliente servem como padrão para todos os seus Tenants.
### Geral (Cliente)
| Campo | Descrição |
|-------|-----------|
| **Nome** | Nome de identificação do cliente. |
| **Nome da IA** | Nome personalizado para a assistente de IA (padrão: Lumia). Apenas clientes whitelabel. |
| **Logo/Favicon/Imagem de Login** | Identidade visual padrão para todos os tenants. |
| **Domínio Principal** | URL principal do cliente. |
| **Cliente Whitelabel** | Marca o cliente como whitelabel (permite customizações avançadas). |
| **Possui Trial** | Habilita período de degustação para novos tenants. |
| **Dias de Trial** | Duração padrão do período de trial. |
| **Template Padrão** | Template instalado automaticamente em novos tenants. |
| **Tenants precisam definir CNPJ** | Torna obrigatório o cadastro de CNPJ nos tenants. |
### Suites/Visual (Cliente)
Mesma estrutura do Tenant, mas define os valores padrão para todos os tenants do cliente.
### ChatBot (Cliente)
Configuração de Telegram e instruções do chatbot que serão herdadas pelos tenants.
### Avançado (Cliente)
Todas as configurações avançadas (ETL, SMTP, JWT, Permissões, Alertas, IA) funcionam como padrão para novos tenants. Cada tenant pode sobrescrever essas configurações.
### Usuários (Cliente)
Gerenciamento de usuários no nível do cliente, com controle de quotas baseado na configuração de billing.
### Desenvolvedor (Cliente)
Chave de API e documentação no nível do cliente.
### Instâncias de WhatsApp (Cliente)
Gerenciamento de instâncias de WhatsApp compartilhadas entre todos os tenants do cliente.
### Tenants (Cliente)
Ferramenta de comparação e sincronização de configurações entre o Cliente e seus Tenants.
#### Visão Geral
Esta aba permite visualizar quais configurações dos tenants divergem das configurações padrão do cliente. É útil para identificar customizações e manter a consistência entre os ambientes.
#### Modos de Visualização
| Modo | Descrição |
|------|-----------|
| **Por Tenant** | Lista cada tenant mostrando quantas configurações divergem do cliente. Ao expandir, exibe uma tabela comparativa com o valor no cliente vs. valor no tenant. |
| **Aplicar em Massa** | Agrupa as divergências por tipo de configuração. Mostra quantos tenants possuem valor diferente para cada configuração, permitindo aplicar a configuração do cliente em todos de uma vez. |
#### Ações Disponíveis
| Ação | Descrição |
|------|-----------|
| **Usar do Cliente** | Aplica a configuração do cliente em um tenant específico para uma configuração específica. |
| **Aplicar em Massa** | Sincroniza uma configuração específica para todos os tenants que possuem valor divergente. |
| **Atualizar** | Recarrega a lista de divergências. |
> \[!TIP]
> Use o modo **Aplicar em Massa** quando precisar padronizar uma configuração específica (como canais de alerta ou configuração de IA) em todos os tenants de uma vez.
> \[!IMPORTANT]
> A sincronização sobrescreve o valor atual do tenant pelo valor configurado no cliente. Esta ação não pode ser desfeita automaticamente.
***
## 🔗 Herança de Configurações
O sistema de configurações segue uma hierarquia de herança:
```
Cliente (Padrão)
└── Tenant (Sobrescrita)
```
| Configuração | Comportamento |
|--------------|---------------|
| **Identidade Visual** | Tenant pode ter logo/cores próprias ou herdar do Cliente. |
| **SMTP** | Tenant pode definir servidor próprio ou usar do Cliente. |
| **Template de Email** | Tenant pode personalizar ou herdar do Cliente; Cliente herda do padrão da plataforma. |
| **IA** | Tenant pode usar configuração própria, do Cliente, ou padrão da plataforma. |
| **Agentes ETL** | Configurações são herdadas e podem ser sobrescritas. |
| **Permissões** | Flags de permissões são independentes por Tenant. |
| **Canais de Alerta** | Tenant define quais canais estão disponíveis. |
---
---
url: 'https://docs.horusbi.com.br/hec/users-groups/users.md'
---
# Gestão de Usuários
O módulo de Gestão de Usuários centraliza a administração das identidades com acesso ao seu Tenant (Ambiente). A partir desta tela, você visualiza todos os membros ativos, cadastra novos colaboradores e controla o ciclo de vida das contas — incluindo inativação, redefinição de senhas e configurações de segurança.
## 📋 Listagem de Usuários
Ao acessar **Usuários & Grupos > Usuários: Gerenciar Usuários**, você terá uma visão geral de todas as contas vinculadas ao ambiente.
### Filtros Disponíveis
* **Busca Rápida**: Pesquise diretamente pelo Nome ou Login (E-mail) do usuário
* **Filtro por Grupo**: Insira o nome de um grupo de acesso ("Analistas") para exibir apenas os membros atrelados a ele
* **Exibir Clientes (Toggle)**: Controla a visibilidade de perfis com o tipo **"Cliente"** na listagem principal. Ative ou desative a chave para incluir ou ocultar esses usuários
> \[!NOTE]
> **Requisitos para o toggle "Exibir Clientes"**:
>
> * **Contexto White-Label**: Disponível apenas para instâncias configuradas como White-Label.
> * **Tipo de Perfil**: Aplicável somente a usuários registrados sob o tipo **Cliente**.
> * **Nível de Acesso**: Restrito a usuários com permissão de **ADM Final**.
>
> Se a chave não estiver visível para você, verifique se o seu perfil atende a esses requisitos.
## 🔐 Níveis de Acesso
Para compreender as diferenças de privilégios entre um **Admin do Cliente** (nível global) e um **Admin do Tenant** (nível de ambiente), consulte a documentação sobre a [Hierarquia de Entidades](../concepts/hierarchy.md).
## ➕ Criando um Novo Usuário
O processo de convite foi desenhado para evitar a duplicidade de contas na plataforma. Para adicionar uma pessoa ao seu ambiente:
1. Clique no botão **+ Novo Usuário**.
2. Preencha o **Login** (E-mail corporativo ou pessoal do usuário).
3. O sistema fará uma verificação automática na base global da Horus:
* **Usuário Existente**: Se o e-mail já possuir cadastro em outro Tenant, o sistema emitirá um aviso. Ao salvar, você estará apenas *autorizando* essa identidade já existente a acessar o seu ambiente — não é necessário criar uma nova senha
* **Usuário Novo**: Se o e-mail for inédito, os campos complementares de cadastro serão exibidos
4. **Definição de Senha**:
* Você pode definir uma senha provisória marcando *"Definir uma senha agora?"*
* Alternativamente, deixe em branco e use *"Enviar Senha por E-mail"* após salvar — o próprio usuário criará sua credencial
5. **Grupos**: Opcionalmente, vincule o usuário aos grupos de permissões adequados já durante a criação.
> \[!NOTE]
> **Recadastro de usuário inativado**: Se o e-mail informado pertence a um usuário previamente **inativado neste Tenant**, ao salvar você está reativando o cadastro existente. A confirmação de sucesso indicará "Usuário reativado". As permissões anteriores não são restauradas — apenas os grupos selecionados neste formulário serão aplicados.
> \[!WARNING]
> O cadastro de novos usuários está sujeito ao limite de licenças do seu contrato. O sistema exibirá um alerta caso o limite de assentos do seu plano seja atingido.
## ✏️ Editando um Usuário
Para modificar uma conta, clique no ícone de lápis correspondente na listagem. A tela de edição é organizada em abas:
### Aba Geral
Gerenciamento básico e estado da conta.
* **Dados Cadastrais**: Atualização do nome de exibição do usuário
* **Grupos**: Inclusão ou remoção do usuário em diferentes Grupos de Acesso
* **Tenants**: *(Exclusivo para Super Admins)* Gerenciamento de quais ambientes este usuário pode acessar
#### ⚠️ Ações de Conta (Área de Risco)
Ações operacionais críticas para a administração do acesso:
* **Inativar Usuário**: Revoga imediatamente o acesso ao sistema, removendo todos os grupos e permissões individuais do usuário neste Tenant e encerrando suas sessões ativas. Ideal para desligamentos ou auditorias
> \[!WARNING]
> A inativação **não preserva** as configurações de acesso do usuário. Para reativar uma conta inativa, utilize o fluxo de **+ Novo Usuário** com o mesmo e-mail — o sistema reconhece o cadastro existente e reativa o vínculo, mas as permissões precisarão ser **configuradas novamente** do zero.
* **Enviar E-mail de Senha**: Dispara um link seguro para que o usuário redefina sua própria senha
* **Ver Código Telegram**: Exibe o token numérico de pareamento para vincular a conta ao Telegram, permitindo o recebimento de notificações e interações via chat
* **Resetar 2FA**: Remove a configuração atual de Autenticação de Dois Fatores (2FA). Necessário quando o usuário perde acesso ao dispositivo móvel ou aplicativo autenticador. Se o Tenant exigir 2FA, o usuário será obrigado a reconfigurá-lo no próximo login
> \[!WARNING]
> A redefinição do 2FA reduz temporariamente a segurança da conta. Como boa prática, confirme a identidade do usuário por um canal oficial (e-mail corporativo ou chamado interno) antes de executar o reset.
### Outras Abas
* **[Acesso aos Dados](./permissions.md#acesso-aos-dados)**: Audite ou configure quais Mesas e Tabelas o usuário pode visualizar
* **[Funções do Sistema](./permissions.md#funcoes-do-sistema)**: Veja as ações permitidas (permissão para Criar, Editar ou Publicar recursos)
---
---
url: 'https://docs.horusbi.com.br/etl/processors/inputs/google-sheets.md'
---
# Google Sheet (Leitura)
O nó **Google Sheet** conecta-se à API do Google Sheets para ler dados diretamente de uma planilha na nuvem.
***
## 📋 Pré-requisitos
Para usar este nó, você precisa de uma **Service Account** do Google Cloud Platform (GCP) com a API do Google Sheets habilitada.
***
## ⚙️ Parâmetros de Configuração
### Credenciais
* **Descrição** — O conteúdo do arquivo JSON da Service Account (chave privada)
* **Tipo** — JSON String (recomendado usar uma Variável Protegida `var-GOOGLE_JSON`)
### ID da Planilha
* **Descrição** — O identificador único da planilha, encontrado na URL
* **Exemplo** — Na URL `https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKbBdB_.../edit`, o ID é `1BxiMVs0XRA5nFMdKbBdB_...`
### Nome da Aba
* **Descrição** — O nome exato da aba (Worksheet) que você deseja ler
* **Exemplo** — `Página1` ou `Vendas 2024`
### Intervalo
* **Descrição** — (Opcional) O intervalo de células a ser lido, na notação A1. Se vazio, lê a planilha inteira
* **Exemplo** — `A1:F100`
### Cabeçalho
* **Descrição** — Indica se a primeira linha do intervalo contém os nomes das colunas
* **Padrão** — Sim (`true`)
***
## 🔑 Permissões
Certifique-se de compartilhar a planilha (botão "Compartilhar" no Google Sheets) com o e-mail da Service Account (`...@...iam.gserviceaccount.com`).
***
## 🔗 Próximo Passo
Após configurar a leitura do Google Sheets, conecte a saída ao [nó Datawarehouse](/etl/processors/outputs/datawarehouse) para carregar os dados no DW.
Para um tutorial completo — da leitura do Google Sheets até o Dashboard publicado — veja a receita [Google Sheets → Dashboard](/guia/receitas/google-sheets-dashboard).
---
---
url: 'https://docs.horusbi.com.br/dataviz/04-features/apresentacoes/grafico-bi.md'
---
# Gráfico BI
O **Gráfico BI** é o que diferencia um Deck de uma apresentação comum: em vez de colar um print, você insere um gráfico **real**, vindo direto de um dashboard da sua plataforma, com **dados de verdade** — e que continua atualizando conforme os dados mudam.
> \[!IMPORTANT]
> Gráfico BI faz parte de Slides Desenhados, em **fase beta**.
***
## ➕ Inserindo um Gráfico BI
1. No editor, clique no botão **"Gráfico BI"**
2. Um seletor abre com a árvore da sua plataforma: **Mesas → Aplicações → Dashboards → Gráficos**
3. Use a **busca** para encontrar o gráfico pelo nome, sem precisar navegar a árvore inteira
4. Ao selecionar um gráfico, uma **pré-visualização ao vivo** mostra como ele está agora, com os dados reais
5. Confirme a escolha — o gráfico entra no slide, já com os dados carregados
> \[!TIP]
> Você pode inserir mais de um Gráfico BI no mesmo slide — por exemplo, dois indicadores lado a lado para comparação.
***
## 🎛️ Opções do gráfico
Com o Gráfico BI selecionado no slide, um painel lateral abre com as opções de personalização:
| Opção | O que faz |
|---|---|
| **Trocar gráfico** | Abre o seletor novamente para escolher outro gráfico, mantendo posição e tamanho no slide |
| **Filtros** | Filtros aplicados **só naquele gráfico, naquele slide** — não alteram o dashboard original |
| **Título** | Escolha entre o título original do painel, um título personalizado, ou nenhum título |
| **Fundo** | Cartão (com borda/sombra), transparente, ou uma cor sólida |
| **Tema** | Claro ou escuro — **cada gráfico pode ter seu próprio tema**, independente dos outros |
| **Escala do conteúdo** | Zoom do conteúdo interno do gráfico (fonte, eixos, rótulos), para caber melhor no slide |
| **Dados** | **Ao vivo** ou **congelado** (snapshot) |
### Tema por gráfico
Como o tema é escolhido gráfico a gráfico, dá pra montar um Deck com **seções claras e escuras** convivendo — por exemplo, um slide com fundo escuro e gráficos no tema escuro para destaque, seguido de slides no tema claro para o restante da apresentação.
### Dados ao vivo vs. congelado
* **Ao vivo**: o gráfico consulta os dados reais toda vez que é exibido. Se o número mudou desde a última vez, o gráfico mostra o valor atualizado.
* **Congelado (snapshot)**: os dados ficam travados no momento em que você congelou o gráfico. Útil para **apresentar sem depender de conexão** (ex.: sala sem rede estável) ou quando você quer garantir que o número exibido numa reunião não mude no meio da apresentação.
> \[!TIP]
> Vai apresentar em um lugar sem internet confiável? Congele os gráficos antes. Vai deixar rodando numa TV que atualiza sozinha? Deixe ao vivo.
***
## ↔️ Redimensionando e posicionando
O Gráfico BI se comporta como qualquer outro elemento do slide: arraste para posicionar, puxe as bordas para redimensionar. Não há tamanho fixo — o gráfico se adapta ao espaço que você der a ele.
***
## 🔄 Atualização automática
Quando configurado como **ao vivo**, o Gráfico BI usa o mesmo motor de consulta dos dashboards normais: se os dados por trás dele mudam (uma nova carga, um valor atualizado), o gráfico reflete isso automaticamente da próxima vez que for exibido — sem precisar editar o slide de novo.
***
## 🗺️ Próximos passos
* **[Editor de Slides Desenhados](./editor.md)** — outros elementos do slide (texto, imagem, forma, tabela) e como exportar o Deck.
* **[Modo Kiosk](./kiosk.md)** — deixar um Deck com Gráfico BI rodando sozinho numa TV.
* **[Gerar Apresentação com IA](./ia.md)** — a IA escolhe e insere os Gráfico BI relevantes automaticamente.
---
---
url: 'https://docs.horusbi.com.br/dataviz/03-widgets/bar-line.md'
---
# Gráficos de Barra, Linha e Área (Combo)
Este é o Widget mais poderoso e versátil do Horus DataViz. Ele não é apenas um gráfico simples, mas uma *"engine"* de visualização capaz de criar desde gráficos de barras básicos até displays complexos com eixos múltiplos (Combo Charts) e drill-down hierárquico.
## 📊 Modos de Operação (Dados)
O comportamento do gráfico muda drasticamente dependendo de como a aba **Dados** é configurada. O Widget detecta e se adapta a 3 modos principais:
### 1. Comparação Simples (1 Dimensão + N Valores)
* **Configuração**: 1 Dimensão `(Vendedor)` e 1 ou mais métricas `(Venda, Meta)`
* **Resultado**: Cria barras/linhas lado a lado para cada vendedor. Se houverem múltiplas métricas, elas serão agrupadas
> \[!TIP]
> O cenário de uso mais comum para esta visualização é a criação de rankings e a comparação direta entre diferentes categorias ou indicadores.
### 2. Agrupamento Automático (2 Dimensões + 1 Valor)
* **Configuração**: 2 Dimensões `(Ano)` e `(Estado)` e 1 Métrica `(Venda)`
* **Resultado**: O sistema faz um "Pivot" automático
* O Eixo X representará os Anos
* Cada Estado se tornará uma série individual (representada por cores diferentes)
> \[!TIP]
> Ideal para analisar a evolução da composição (Vendas por Estado ao longo dos Anos).
### 3. Multi-KPI (0 Dimensões + N Valores)
* **Configuração**: Nenhuma dimensão, apenas várias métricas `(Total Vendas`, `Total Custos`, `Lucro)`
* **Resultado**: Cria uma barra/ponto único para cada métrica global
> \[!TIP]
> Ideal para comparação de métricas que não possuem relação direta de categoria, como um "Resumo Executivo" visual.
***
## 🔀 Combo Charts e Eixos Múltiplos
Uma das funções mais avançadas é a capacidade de "misturar" tipos de gráficos e escalas no mesmo visual.
### Configuração por Série (Métrica)
Ao adicionar métricas na aba Dados, clique no ícone de **Engrenagem** ao lado de cada medida para abrir o painel avançado:
1. **Tipo de Gráfico Individual**: É possível forçar uma métrica específica para se comportar como **Linha** enquanto os demais dados do gráfico se mantêm como **Barra**
* **Exemplo**: Vendas em Colunas (Volume) e Margem `%` em Linha (Tendência)
2. **Múltiplos Eixos (Y)**: O Horus DataViz suporta até **3 eixos verticais** independentes
* **Configuração**: Na engrenagem da métrica, mude a propriedade **Eixo** para `2` ou `3`
3. **Acumular Valores**: Transforma a série em um **"Total Acumulado"** (Running Total)
> \[!TIP]
> Essencial quando se comparam grandezas diferentes (Faturamento em Milhões `R$` no Eixo 1 e Margem Percentual `%` no Eixo 2)
> \[!WARNING]
> A acumulação soma os valores sequencialmente. Não utilize isso com métricas de porcentagem ou médias, pois a soma matemática (10% + 20% = 30%) raramente faz sentido estatístico.
***
## 🔍 Interatividade e Drill-down
O Widget oferece caminhos poderosos para o usuário explorar os dados:
### 1. Drill-down Hierárquico
Permite "mergulhar" nos dados `(Ano > Mês > Dia)`
* **Como configurar**:
1. Adicione a dimensão principal `(Ano)`
2. Na área **Dimensões Alternativas**, adicione os próximos níveis `(Mês)` e `(Dia)`
3. Altere o **Comportamento** para `Drilldown Hierárquico`
* **Funcionamento**: Quando o Dashboard recebe um filtro da dimensão principal (usuário clica na barra de "2024" ou filtra 2024 no topo), o gráfico automaticamente atualiza para mostrar os **Meses** de 2024
### 2. Escolha do Usuário (Alternância)
Permite que o próprio usuário decida como quer visualizar o gráfico.
* **Como configurar**: Mesmos passos acima, mas altere o **Comportamento** para `Escolha do Usuário`
* **Funcionamento**: Botões aparecem sobre o gráfico permitindo alterar o eixo X instantaneamente (análise por "Loja", depois alternar para análise por "Vendedor")
***
## 🕸️ Radar (Spiderweb)
O tipo **Radar** projeta os dados em eixos radiais que partem do centro.
* **Distinção**: Ao selecionar "Radar/Spiderweb" no tipo global, todas as séries obedecem a este formato, impossibilitando a mistura entre Radar e Barras convencionais
> \[!TIP]
> Excelente para análise de "Perfil" ou multiparâmetros, como em uma avaliação de um funcionário em 5 competências diferentes.
***
## 🎨 Configuração Visual
### Cores e Temas
* **Coloração por Eixo (Dimensão)**: Cada barra tem uma cor baseada no seu nome ("Aprovado" verde, "Reprovado" vermelho)
* **Coloração por Valor (Métrica)**: A cor muda baseada na magnitude ou em regras de negócio (Expressão)
### Ajustes Finos
* **Largura da Barra**: `Automático` ou `Personalizado` (para evitar barras largas em gráficos com poucos itens)
* **Empilhamento**:
* `Normal`: Soma os valores (bom para volume total)
* `100% (Relativo)`: Estica tudo para o topo (bom para ver share/participação sem se importar com volume absoluto)
* **Preencher Lacunas de Data**: Se o eixo X for temporal, essa opção força o aparecimento de dias/meses que não houveram vendas (valor zero), garantindo que a linha do tempo seja contínua e proporcional
***
## 📋 Guia Completo de Configuração
Abaixo listamos todos os parâmetros disponíveis no painel de configuração do Widget.
### 1. Aba Dados
#### Configuração de Séries (Engrenagem no Valor)
Ao clicar na engrenagem ao lado de uma métrica, são exibidas opções granulares:
* **Tipo de Coloração**:
* `Cor Fixa`: Uma única cor sólida para toda a série
* `Cor por Expressão`: Coloração dinâmica via JavaScript
* **Escala (Eixo)**: Define em qual eixo vertical Y a métrica será plotada *(1, 2 ou 3)*. Múltiplos eixos são cruciais para comparar grandezas diferentes *(R$ vs %)*
* **Tipo de Gráfico**: Sobrescreve o tipo global apenas para essa série (O gráfico é de Barras, porém essa métrica em específico será uma Linha)
* **Mostrar valores sobre os dados**: Força a exibição (ou ocultação) dos rótulos de dados apenas para essa série
* **Formato do Número**: Força a formatação visual `(Padrão, Número, Moeda, Porcentagem, Reduzido 1k, Inteiro)`
* **Acumular Valores**: Soma progressiva dos valores (Running Total). Ideal para análises de evolução acumulada
#### Configuração de Eixos (Engrenagem na Dimensão)
Disponível apenas em gráficos coloridos por dimensão.
* **Mapeamento Manual**: Lista os valores encontrados ("SP", "RJ") e permite escolher uma cor fixa para cada um
* **Botão "Carregar Valores"**: Busca os primeiros 50 registros do banco para popular a lista de cores
* **Cor por Expressão**: Regra JavaScript para colorir o eixo X
#### Outros Campos
* **Valores Alternativos**: Funciona da mesma forma que as Dimensões Alternativas, mas para métricas. Permite ao usuário trocar *o que* está vendo (trocar "Vendas" por "Lucro" no mesmo gráfico)
* **Ordenação**: É possível ordenar por campos que **não** estão visíveis no gráfico
* **Exemplo**: Mostrar os "Nomes dos Meses" (Jan, Fev) no eixo X, mas ordenar pelo "Número do Mês" logicamente para garantir a sequência temporal correta
***
### 2. Aba Visual
#### Aparência Geral
* **Estilo**:
* `Padrão`: Fundo sólido (cor configurável em **Cor do Fundo**)
* `Transparente`: Remove o fundo para integrar ao Dashboard
* `Destacado`: Adiciona sombras e bordas
* **Cor do valor sobre os dados**: Define a cor global dos textos numéricos (Data Labels)
#### Layout e Orientação
* **Orientação**: `Vertical` (Colunas) ou `Horizontal` (Barras)
* **Barras/Áreas Empilhadas**: Ativa o modo Stacked
* `Empilhamento Relativo`: Normaliza as barras para 100% de altura
* `Mostrar valores sobre o total`: Exibe o somatório no topo da pilha
* `Mostrar valores para cada barra`: Exibe os valores individuais dentro de cada segmento da pilha
* **Largura da Barra**:
* `Automático`: O sistema calcula a largura baseada no número de itens
* `Personalizado`: Permite definir uma largura máxima em pixels (Ideal para estéticas minimalistas)
#### Legendas e Eixos
* **Mostrar Legenda**: Habilita/Desabilita a legenda das séries
* **Posição**: `Topo` ou `Base`
* **Linhas de Grade**: Controle independente para grade Vertical (X) e Horizontal (Y)
* **Pontos de marcações (Markers)**: Exibe "bolinhas" em cima de cada ponto das linhas. Ideal para destacar a precisão dos dados
* **Mostrar Escala**: Exibe ou oculta os números do eixo Y lateral
* **Não mostrar valores zerados**: Remove do gráfico pontos que tenham valor 0 (ao invés de desenhar uma linha no chão do gráfico)
* **Limitar Registros**: Corta o gráfico nos Top N itens (Top 10 Clientes), ordenando pelos valores – máximo de 500
***
### 3. Aba Comportamento
* **Comportamento Dimensões Alternativas**:
* `Escolha do Usuário`: Botões para troca manual
* `Drilldown Hierárquico`: Navegação por clique (Nível 1 > Nível 2)
* **Preencher lacunas de data**: Se houver dimensão de data, caso haja registros fantasmas, serão exibidos com valor zero para datas ausentes
* **Mostrar itens sem valores**: Força o banco de dados a trazer registros nulos. Ideal para visualizar cadastros que não tiveram movimentação
* **Campos para Relatório**: Define quais colunas aparecerão na tabela de detalhes "Drill-through" ao exportar ou visualizar os dados brutos deste Widget
***
## 💡 Dicas de DataViz
Para criar Dashboards profissionais, escolha o tipo adequado para cada dado:
> \[!TIP]
>
> * **Linhas**: Use **exclusivamente** para dados contínuos/temporais (Dias, Meses, Anos). Linhas indicam conectividade e tendência. Usar linhas para dados categóricos (Produtos) é um erro conceitual, pois não existe "transição" entre um produto e outro.
> * **Barras (Horizontais)**: Melhores para rankings com muitos itens ou quando os nomes (rótulos) são longos e difíceis de serem interpretados na vertical.
> * **Colunas (Verticais)**: Ótimas para comparação de poucos itens (fácil comparação de altura) ou séries temporais curtas.
> * **Eixos Duplos**: Utilize-os com moderação. Se as linhas se cruzam demais, podem gerar confusão. Tente manter no máximo 2 eixos para clareza.
---
---
url: 'https://docs.horusbi.com.br/guia.md'
---
# Guia da Plataforma HorusBI
Bem-vindo ao **Guia da Plataforma**. Este é o ponto de partida para entender como os módulos da HorusBI trabalham juntos — desde a configuração inicial até a publicação de Dashboards interativos para toda a organização.
Aqui você encontra a visão integrada de todos os módulos e receitas práticas para atingir seus objetivos.
## 🔄 Pipeline Completo
O fluxo típico de dados na plataforma segue este caminho:
```mermaid
graph LR
HEC["HEC (Setup)"] --> ETL["ETL (Extrair)"]
HEC --> DW_UP["DW (Upload Excel)"]
ETL --> DW["DW (Armazenar)"]
DW_UP --> DW
DW --> DataViz["DataViz (Visualizar)"]
DataViz --> PUB["Publicar + Dar Acesso (HEC)"]
```
***
## 🧭 Quando Usar Cada Módulo
| Objetivo | Módulo | Link |
|----------|--------|------|
| Criar tenant, usuários, Mesas e permissões | **HEC** | [Visão Geral](/hec/intro/overview) |
| Extrair dados de bancos, APIs ou Google Sheets | **HorusETL** | [Primeiros Passos](/etl/getting-started/) |
| Carregar planilha Excel manualmente | **HorusDW** | [Upload de Dados](/dw/getting-started/upload) |
| Armazenar e organizar dados estruturados | **HorusDW** | [Conceitos Fundamentais](/dw/intro/) |
| Criar Dashboards e Relatórios interativos | **DataViz** | [Criando Aplicações](/dataviz/01-getting-started/create-app) |
| Integrar via API REST | **API** | [Documentação da API](/api/) |
***
## 🗂️ Minha Mesa em Cada Módulo
Cada módulo possui o conceito de **Minha Mesa** — um espaço privado de trabalho onde você cria e edita conteúdo antes de publicá-lo:
* **ETL**: Minha Mesa contém seus **Dataflows** (pipelines de dados). [Saiba mais](/etl/guides/mesas-publicacao)
* **DW**: Minha Mesa contém suas **Tabelas** (dados carregados ou gerados por ETL). [Saiba mais](/dw/intro/)
* **DataViz**: Minha Mesa contém suas **Aplicações** (Dashboards e Relatórios). [Saiba mais](/dataviz/01-getting-started/create-app)
> \[!IMPORTANT]
> Tudo que está em "Minha Mesa" é invisível para outros usuários. Para que outros acessem seu conteúdo, é preciso **publicar** e **dar acesso** explicitamente via HEC.
***
## 📥 Duas Formas de Carregar Dados
### Trilha A: Excel Direto no DW (sem Agente)
Ideal para cargas manuais e pontuais. Você faz upload de um arquivo Excel diretamente no HorusDW.
**Caminho**: Upload Excel → DW (Minha Mesa) → Publicar → Mesa Oficial
[Ver receita completa](/guia/receitas/excel-dashboard)
### Trilha B: Pipeline ETL (com Agente)
Ideal para automação e fontes externas (bancos de dados, Google Sheets, APIs). Requer a instalação de um Agente ETL.
**Caminho**: Agente → ETL (Dataflow) → DW (Minha Mesa) → Publicar → Mesa Oficial
[Ver receita Google Sheets](/guia/receitas/google-sheets-dashboard) | [Ver receita Banco de Dados](/guia/receitas/banco-dashboard)
***
## 📤 Publicar e Compartilhar
O ciclo completo para que outros usuários vejam seus dados e Dashboards:
1. **Criar Mesas no HEC** — Mesas de Dados (para tabelas) e Mesas de Aplicações (para apps). [Como criar Mesas](/hec/desks/mesas)
2. **Publicar conteúdo** — Publicar tabelas no DW ou Dataflows no ETL para Mesas de Dados; publicar Aplicações para Mesas de Aplicações via HEC. [Gerenciar conteúdo](/hec/resources/01-content)
3. **Dar acesso via permissões** — No HEC, configurar acesso de usuários/grupos às Mesas. [Configurar permissões](/hec/users-groups/permissions)
> \[!WARNING]
> Criar uma Mesa não dá acesso automático. Cada usuário ou grupo precisa ter acesso explícito à Mesa nas Permissões.
***
## ➡️ Próximos Passos
* [Jornada do Usuário: Do Dado ao Dashboard](/guia/jornada) — Passo a passo completo
* [Receitas Práticas](/guia/receitas/) — Tutoriais por objetivo
---
---
url: 'https://docs.horusbi.com.br/etl/guides.md'
---
# Guias de Operação e Gerenciamento
Esta seção reúne instruções passo a passo para gerenciar a infraestrutura e a operação diária dos seus fluxos de dados no HorusETL. Os guias são voltados para Engenheiros de Dados e Administradores do sistema.
***
## 📚 Tópicos
* ⚙️ [**Gerenciamento de Agentes**](./agentes.md) - Instalação, configuração de tokens, atualizações e solução de problemas de conectividade
* 🔤 [**Variáveis Globais**](./variaveis-globais.md) - Como criar e usar variáveis compartilhadas entre múltiplos fluxos
* 🔗 [**Conexões de Banco de Dados**](./conexoes-banco.md) - Como cadastrar credenciais de acesso para bancos relacionais e outros drivers
* 📦 [**Fluxo de Trabalho: Mesas e Publicação**](./mesas-publicacao.md) - Diferença entre o ambiente de rascunho ("Minha Mesa") e produção, e como publicar fluxos
* 🔄 [**Tipos de Carga**](./tipos-de-carga.md) - Total, Temporal e Incremental: o que cada um apaga antes de inserir, e como escolher
* ⏰ [**Execução e Agendamento**](./execucao-agendamentos.md) - Como criar rotinas automáticas (cron) e disparar cargas manuais
* 📊 [**Logs e Monitoramento**](./logs-monitoramento.md) - Como interpretar os logs de execução para debugar erros e performance
---
---
url: 'https://docs.horusbi.com.br/dw/architecture/hierarchy.md'
---
# Hierarquia da Informação
O HorusDW organiza os dados de maneira hierárquica para equilibrar **liberdade de criação** (ambiente privado) com **governança corporativa** (dados oficiais). Entender essa hierarquia é fundamental para saber onde seus dados estão e quem pode acessá-los.
***
## 🏛️ Níveis de Organização
### 1. 🏢 Tenant (Sua Empresa)
É o ambiente global da organização. Tudo que acontece no Horus — usuários, dados, acessos — está contido dentro do Tenant da sua empresa. Os dados de um Tenant são completamente isolados dos demais.
### 2. 🗄️ Mesas (Desks)
São os containers onde as tabelas são armazenadas fisicamente.
* **Função** — Organizar o armazenamento físico dos dados
* **Regra** — Uma tabela **só pode** estar em uma única Mesa
* **Tipos disponíveis:**
| Tipo | Descrição |
|------|-----------|
| **Minha Mesa** | Seu espaço privado de trabalho — visível apenas para você |
| **Mesas Públicas** | Espaços compartilhados e governados do departamento (Financeiro, RH) |
### 3. 📄 Tabelas (Tables)
São os objetos de dados propriamente ditos. Cada tabela tem um "dono" (quem a criou) e um endereço fixo (a Mesa onde ela está armazenada).
***
## 🔀 Armazenamento vs. Visualização
O grande diferencial do Horus está na separação entre **onde o dado está guardado** e **onde ele é exibido**.
### Datamarts (Vitrines de Negócio)
Enquanto as Mesas guardam os dados, os **Datamarts** funcionam como vitrines que exibem esses dados para os usuários por área temática:
* Uma tabela **está armazenada** na Mesa "Financeiro"
* Mas ela **aparece** no Datamart "Diretoria" e também no Datamart "Vendas"
> \[!TIP]
> **Analogia:** Pense no Spotify. A música (Tabela) está gravada no álbum do artista (Mesa), mas você pode adicioná-la em várias playlists diferentes (Datamarts).
### Por que isso é importante?
Essa separação permite que a equipe de TI organize os dados tecnicamente (por sistema de origem), enquanto os usuários de negócio acessam os dados por tema (por assunto) — tudo isso sem necessidade de duplicar a informação.
---
---
url: 'https://docs.horusbi.com.br/hec/concepts/hierarchy.md'
---
# Hierarquia de Entidades
A plataforma HorusBI é organizada em uma estrutura hierárquica projetada para garantir **governança, segurança e escalabilidade**. Antes de configurar seu ambiente, é fundamental entender como as peças se encaixam.
> \[!TIP]
> Para uma visão rápida de Cliente, Tenant e Usuário, consulte a [Visão Geral do HEC](../intro/overview.md#️-hierarquia-de-entidades). Esta página aprofunda os conceitos de **Mesas**, **Aplicações** e **Tabelas**, que são a base da organização interna de cada Tenant.
```mermaid
erDiagram
CLIENTE ||--o{ TENANT : "gerencia"
TENANT ||--o{ MESA_DE_DADOS : "contém"
TENANT ||--o{ MESA_DE_APLICACAO : "contém"
MESA_DE_DADOS ||--o{ TABELA : "armazena"
MESA_DE_APLICACAO ||--o{ APLICACAO : "armazena"
```
## 🏢 1. Cliente (Parceiro)
No topo da hierarquia está o **Cliente** (também chamado de Parceiro). Esta entidade representa a sua organização — por exemplo, uma empresa revendedora, consultoria de dados ou holding.
* **Função**: Gerenciamento global de múltiplos ambientes (Tenants)
* **Administradores**: Usuários neste nível são chamados de "Admins do Cliente". Eles podem criar novos Tenants e gerenciar configurações globais como SMTP, domínio e identidade visual (branding)
## 🏠 2. Tenant (Ambiente)
O **Tenant** é a unidade de isolamento da plataforma. Cada Tenant representa um cliente final, um projeto ou um ambiente segregado ("Empresa X — Produção", "Empresa X — Homologação").
* **Isolamento total**: Dados, usuários e configurações de um Tenant são completamente invisíveis para os demais
* **Governança**: Recomenda-se criar um Tenant para cada cliente final. Isso facilita atualizações via **Templates** e garante que um cliente não impacte o outro
* **Administradores**: O "Admin do Tenant" possui controle total apenas sobre o seu ambiente específico
## 📂 3. Mesas (Workspaces)
Dentro de cada Tenant, a organização dos ativos é feita por **Mesas**. Pense nelas como pastas de trabalho que separam o conteúdo por finalidade. Existem dois tipos:
### 🗄️ Mesa de Dados (Data Workspace)
Voltada para a organização **técnica e lógica** dos dados.
* **Exemplos de nomes**: `RAW` (dados brutos), `Refined` (dados tratados), `Sandbox` (área de testes)
* **Conteúdo**: Armazena as **Tabelas** e os **Dataflows** (fluxos de integração)
* **Na prática**: É a camada onde a engenharia de dados organiza os diferentes estágios de qualidade do dado
### 📊 Mesa de Aplicação (Business Workspace)
Voltada para a organização **de negócio e entregas ao usuário final**.
* **Exemplos de nomes**: `Comercial`, `Financeiro`, `Logística`, `Estoque`, `Suprimentos`
* **Conteúdo**: Armazena os **Dashboards**, **Aplicações** e **Visualizações**
* **Reuso de dados**: Uma tabela técnica (`Produtos` da mesa `Refined`) pode ser consumida por múltiplas mesas de aplicação. Por exemplo, a mesma tabela `Produtos` serve simultaneamente para `Comercial`, `Estoque` e `Suprimentos`
> \[!IMPORTANT]
> **Mesas e Templates**: As mesas são a unidade levada quando você publica um template. Isso significa que a estrutura organizacional definida pelas mesas é o que será replicado para outros tenants.
## 📦 4. Aplicações e Tabelas
* **Tabelas**: Representam a estrutura física/lógica dos dados (colunas, tipos, relacionamentos). São criadas e mantidas nas Mesas de Dados
* **Aplicações**: São o agrupamento visual de Dashboards que consomem as tabelas. Residem nas Mesas de Aplicação e são o produto final entregue ao usuário de negócio
***
## 🔐 Níveis de Acesso
A hierarquia de entidades define também os perfis de usuário na plataforma:
| Nível | Escopo | O que pode fazer |
|:------|:-------|:-----------------|
| **Admin do Cliente** | Todos os Tenants do parceiro | Criar ambientes, configurar branding, gerenciar a plataforma globalmente |
| **Admin do Tenant** | Um ambiente específico | Controle total sobre usuários, mesas, permissões e configurações daquele Tenant |
| **Usuário** | Conforme permissões | Acesso restrito via Grupos e Permissões dentro de um ou mais Tenants |
> \[!TIP]
> Para detalhes sobre como configurar cada nível de acesso, consulte a documentação de [Permissões e Segurança](../users-groups/permissions.md).
---
---
url: 'https://docs.horusbi.com.br/hec.md'
---
# Horus Enterprise Control (HEC)
O **Horus Enterprise Control (HEC)** é o painel central de administração e governança da plataforma Horus. É por meio dele que você controla quem acessa o sistema, quais dados cada perfil pode visualizar e quais ferramentas estão disponíveis para cada equipe.
Enquanto os módulos de [ETL](/etl/intro/) e [Data Warehouse](/dw/intro/) cuidam do processamento e armazenamento de dados, o **HEC** garante que toda a operação funcione de forma segura, organizada e rastreável.
## 🧭 Funcionalidades Principais
### 👥 Identidade e Acesso
Gerencie o ciclo de vida dos usuários e reforce a segurança do ambiente com o Controle de Acesso Baseado em Funções (RBAC).
* **[Gestão de Usuários](users-groups/users.md)**: Convide novos membros e administre o acesso de forma centralizada
* **[Grupos de Acesso](users-groups/groups.md)**: Crie perfis padronizados (Administrador, Analista, Visualizador) para escalar a gestão de permissões
* **[Matriz de Permissões](users-groups/permissions.md)**: Defina permissões granulares para garantir a política de privilégio mínimo — cada pessoa acessa apenas o que precisa
* **[Autenticação 2FA](users-groups/security-2fa.md)**: Adicione uma camada extra de proteção às contas com a verificação em duas etapas
### 📂 Organização e Estrutura
Organize seu ambiente de trabalho para manter projetos estruturados e fáceis de localizar.
* **[Mesas (Desks)](desks/mesas.md)**: Agrupe Dashboards e fluxos de trabalho em diretórios lógicos e categorizados
* **[Clientes e Tenants](intro/overview.md#1-cliente-contratante)**: Compreenda e gerencie a hierarquia multilocatário (multi-tenant) da plataforma
### ⚙️ Administração e Infraestrutura
Recursos avançados para o controle técnico e operacional do ambiente.
* **[Conexões de Dados](resources/02-infrastructure.md)**: Centralize e gerencie de forma segura as credenciais de acesso aos bancos de dados
* **[Gestão de Conteúdo](resources/01-content.md)**: Visualize e administre todos os ativos do ambiente — Tabelas, Fluxos (Flows) e Aplicações
* **[Gestão de Tenants](resources/tenants.md)**: Ajuste configurações globais, personalize a interface (White-label) e defina limites de consumo de recursos
## 🚀 Por Onde Começar?
Se esta é a primeira vez que você está configurando o seu ambiente Horus, siga este roteiro:
1. Leia a **[Visão Geral](intro/overview.md)** para entender a hierarquia entre Cliente e Tenant.
2. Configure de forma segura as suas **[Conexões de Dados](resources/02-infrastructure.md)**.
3. Crie **[Grupos de Acesso](users-groups/groups.md)** que reflitam a estrutura do seu time.
4. Convide seus **[Usuários](users-groups/users.md)** e atribua-os aos grupos correspondentes.
---
---
url: 'https://docs.horusbi.com.br/dw.md'
---
# HorusDW
O **HorusDW (Data Warehouse)** é o módulo responsável pelo armazenamento, organização e governança dos dados estruturados da suíte HorusBI. Focado em *Self-Service BI*, permite que usuários de negócio realizem carga, tratamento e modelagem de dados com autonomia — desde o upload de uma planilha até a publicação para consumo em Dashboards.
> \[!NOTE]
> O DW é alimentado por [Upload Excel](/dw/getting-started/upload) ou por pipelines do [HorusETL](/etl/getting-started/). Os dados armazenados são consumidos pelo [DataViz](/dataviz/01-getting-started/create-app) para criação de Dashboards. Veja o [Guia da Plataforma](/guia/) para o pipeline completo.
***
## 📚 Estrutura da Documentação
* 📖 **[Introdução](/dw/intro/)** — Visão geral, conceitos de Mesas, Datamarts e Fluxo de Trabalho
* 🚀 **[Primeiros Passos](/dw/getting-started/)** — Tutoriais para carregar e configurar seus primeiros dados
* 🗄️ **[Mesas (Desks)](/dw/desks/)** — Gestão de dados oficiais e governança
* 📄 **[Tabelas](/dw/tables/)** — Guia detalhado sobre tipos de tabelas, edição e ferramentas de IA
* 🏷️ **[Datamarts](/dw/datamarts/)** — Criação de visualizações de negócio para facilitar o acesso
* 🏗️ **[Arquitetura](/dw/architecture/)** — Conceitos lógicos de hierarquia, motor e segurança
***
## 🚀 Começar Rápido
1. Entenda os [Conceitos Fundamentais](/dw/intro/)
2. Aprenda a [Carregar Dados](/dw/getting-started/upload)
3. Organize suas [Mesas](/dw/desks/) e publique informações
4. Crie [Datamarts](/dw/datamarts/) para distribuir o conhecimento
---
---
url: 'https://docs.horusbi.com.br/etl.md'
---
# HorusETL
O **HorusETL** é a ferramenta de integração de dados *low-code* da suíte HorusBI. Permite criar pipelines de extração, transformação e carga visualmente, com execução na nuvem ou em infraestrutura local (On-Premise).
> \[!NOTE]
> O pipeline típico é: **ETL** (extrair e transformar) → **[DW](/dw/intro/)** (armazenar) → **[DataViz](/dataviz/01-getting-started/create-app)** (visualizar). Veja o [Guia da Plataforma](/guia/) para entender como os módulos se conectam.
***
## 📚 Estrutura da Documentação
* 📖 **[Introdução](/etl/intro/)** — Conceitos fundamentais (Dataflow, Nós, Conexões, Arquitetura)
* 🚀 **[Primeiros Passos](/etl/getting-started/)** — Tutorial para criar e executar seu primeiro fluxo
* 🧩 **[Processadores (Nós)](/etl/processors/)** — Referência detalhada de cada processador
* 📋 **[Guias de Operação](/etl/guides/)** — Como usar agentes, conexões, agendamentos e logs
* 🏗️ **[Arquitetura (Avançado)](/etl/architecture/)** — Detalhes técnicos de comunicação, segurança e execução
***
## 🚀 Começar Rápido
1. Leia os [Conceitos Principais](/etl/intro/) para entender o básico
2. Siga o tutorial [Primeiros Passos](/etl/getting-started/)
3. Explore os [Processadores Disponíveis](/etl/processors/)
4. Configure e monitore com os [Guias de Operação](/etl/guides/)
---
---
url: 'https://docs.horusbi.com.br/ia.md'
description: >-
Mapa da IA na plataforma HorusBI: onde cada peça vive no produto e para quem
ela serve.
---
# IA na plataforma
A IA da HorusBI está espalhada pelo produto de propósito: ela aparece onde o
trabalho acontece. Você conversa com ela no DataViz, delega a escrita de um
alerta a ela, classifica linhas com ela dentro de um fluxo de ETL, arruma os
rótulos de uma tabela do DW com ela, e a chama de fora, do assistente que você
já usa.
Esta página é o índice dessa camada. Cada linha do mapa aponta para a
documentação de sempre; nada aqui substitui essas páginas.
::: tip Se você só quer perguntar sobre os seus dados
Comece pelo [Chat com Dados](./chat/). Se as respostas mudam de uma conversa
para outra, o próximo passo é [Indicadores](./chat/fatos.md).
:::
## O mapa
| Peça | O que faz | Onde vive no produto | Para quem |
|---|---|---|---|
| [Chat com Dados](./chat/) | Responde perguntas em linguagem natural executando consulta real nos seus dados. | Sidebar de Insights e janela flutuante do DataViz. | Quem consome dashboard e precisa perguntar algo que não está montado. |
| [Indicadores](./chat/fatos.md) | Fixam qual coluna é a métrica de cada conceito do negócio, qual é a coluna de data e quais filtros valem por padrão. | Aba Conceitos IA de cada Aplicação. | Quem modela a Aplicação. |
| [Cockpit de Indicadores](/dataviz/04-features/indicadores/) | Mostra os indicadores curados como cards ao vivo e explica por IA o porquê de cada variação. | Módulo Indicadores. | Quem acompanha número no dia a dia. |
| [Agentes de IA](./agentes.md) | Persona de investigação reutilizável, com instruções próprias, escopo de até 3 Aplicações e histórico de execuções. | Módulo Agentes de IA. | Quem quer a mesma investigação repetida, no chat e nos alertas. |
| [Conectar seu assistente (MCP)](/ia/mcp) | Leva os seus indicadores para dentro do Claude, do Claude Code e do Cursor, com acesso somente de leitura. | Card "Use no MCP", dentro do módulo Agentes de IA. | Quem já trabalha dentro de um assistente e não quer trocar de janela. |
| [Conteúdo de IA no alerta](/dataviz/04-features/alerts/content-ai) | A partir de um briefing, a IA investiga e escreve a mensagem que o alerta entrega. | Bloco de IA do Alerta Inteligente. | Quem recebe relatório recorrente por e-mail, WhatsApp, Telegram ou webhook. |
| [Gerar apresentação com IA](/dataviz/04-features/apresentacoes/ia) | Monta o deck slide a slide e insere gráficos reais dos dashboards a que você tem acesso. | Botão de IA no Editor de Slides Desenhados. | Quem apresenta resultado em reunião ou em TV. |
| [Classificação por IA (AIExtract)](/etl/processors/transforms/ai-extract) | Nó que enriquece cada linha do DataFrame com campos extraídos ou classificados por um LLM, sobre um schema tipado. | Nó de transformação do HorusETL. | Quem constrói o pipeline. |
| [Assistente do nó Python](/etl/processors/transforms/python#ai-assistant) | Escreve o código do nó a partir do que você pede, já conhecendo as bibliotecas e as regras do nó. | Botão de IA no editor do nó Python. | Quem constrói o pipeline. |
| [FixLabels](/dw/tables/) | Transforma código técnico de coluna em nome legível e sugere máscara e comportamento por coluna. | Botão de estrelas na tela de edição de tabela do HorusDW. | Quem prepara a tabela antes de publicar. |
| [Lumo CLI](/lumo/) | Espelha o tenant em arquivos YAML para um agente de IA criar fluxo, modelar o DW e montar dashboard por conta própria. | Linha de comando, fora da interface. | Administrador e desenvolvedor. |
| [API REST de Chat IA](/api/ai-chat) | Envia mensagem ao motor de chat em nome de um usuário e devolve a resposta por streaming. | Integração servidor a servidor, autenticada por token de tenant. | Quem integra o próprio sistema. |
## Por onde começar, pelo que você faz
### Você pergunta sobre os dados
O [Chat com Dados](./chat/) é a porta. Vale ler
[Como o chat responde](./chat/como-funciona.md) para entender por que a resposta
vem como insight em vez de tabela bruta, e quando trocar o modo Rápido pelo modo
Raciocínio.
Se você quer a mesma investigação sempre com o mesmo recorte, crie um
[agente](./agentes.md) e escolha ele no seletor no topo do chat.
### Você modela a Aplicação
A qualidade da resposta depende de como a Aplicação está organizada. A peça
central é o catálogo de [Indicadores](./chat/fatos.md): o conceito e os
anti-padrões estão ali, e a [aba Conceitos IA](/dataviz/02-apps/ai-concepts) é a
referência da tela de cadastro. Os
[Exemplos de Indicadores](./chat/exemplos-de-fatos.md) mostram os casos lado a
lado.
Para decidir o que a IA enxerga de cada tabela, veja a seção **Colunas Visíveis**
em [Funcionalidades do DataViz](/dataviz/04-features/).
### Você constrói o pipeline
Dentro do HorusETL a IA entra em dois pontos: o nó
[AIExtract](/etl/processors/transforms/ai-extract), que classifica ou extrai
campos de texto livre linha a linha, e o
[assistente do nó Python](/etl/processors/transforms/python#ai-assistant), que
escreve o código do nó. Na chegada, no HorusDW, o **FixLabels** normaliza os
nomes das colunas antes de a tabela ir para o dashboard: veja
[Gerenciamento de Tabelas](/dw/tables/).
### Você chama a IA de fora da plataforma
Três caminhos, com públicos distintos:
* [MCP](/ia/mcp) conecta um assistente de terceiros, autorizado por OAuth ou por
token pessoal. Somente leitura, com as suas permissões.
* [API REST de Chat IA](/api/ai-chat) integra o seu próprio sistema ao motor de
chat, com token de tenant.
* [Lumo CLI](/lumo/) entrega o tenant inteiro como YAML versionável, para o
agente construir e publicar o BI.
## O catálogo de indicadores é a base compartilhada
Os indicadores são cadastrados uma vez na Aplicação e servem a mais de um
consumidor. A mesma definição alimenta o card do cockpit, a resposta do chat
interno e a do assistente conectado por MCP, e a curadoria de colunas vale para o
chat e para os agentes usados em alerta.
---
---
url: 'https://docs.horusbi.com.br/dw/tables/cadastros/importar-exportar.md'
---
# Importar e exportar
> **Caminho**: Cadastro > Ações > Importar
Além de digitar registro a registro, você pode **importar** dados de uma planilha Excel de uma vez — e **exportar** o conteúdo do Cadastro de volta para Excel quando precisar.
***
## 📥 Importar (XLSX)
A importação lê um arquivo **XLSX** e o traz para o Cadastro. Há dois modos:
* **Adicionar** — acrescenta as linhas do arquivo aos registros existentes.
* **Substituir** — troca o conteúdo: a importação **supersede** (substitui) a base anterior. É **reversível** — dá para desfazer (veja o rollback abaixo).
Durante a importação, cada linha passa por **validações de estrutura** (tipos compatíveis, campos obrigatórios preenchidos). O critério é **tudo ou nada**: se **uma linha** apresentar erro, o **import inteiro é rejeitado** e **nada entra**. Isso evita deixar o Cadastro num estado meio-importado.
::: warning A importação exige acesso irrestrito
Para importar é preciso ter acesso de importação **irrestrito** ao Cadastro — **sem filtros nem colunas ocultas**. Como a importação mexe na base inteira, ela só faz sentido para quem enxerga tudo.
:::
> \[!NOTE]
> As **validações em JavaScript** (regras de negócio) **não** rodam na importação — só as checagens de estrutura. Garanta que a planilha já vem com o dado **limpo**. Veja **[Validações e expressões](./validacoes-expressoes)**.
***
## ↩️ Rollback de importação
Toda importação fica registrada, e você pode **desfazer uma importação específica** — um **rollback**. Isso reverte o efeito daquela carga, devolvendo o Cadastro ao estado anterior.
É a rede de segurança para o modo **Substituir**: se a planilha nova veio errada, o rollback traz a base anterior de volta.
***
## 📤 Exportar
A qualquer momento você pode **exportar** o conteúdo do Cadastro para **Excel** — útil para conferir, fazer ajustes em massa fora da plataforma e reimportar, ou apenas compartilhar.
::: info Permissão de exportar
Exportar usa a **permissão de exportação genérica** da plataforma (a mesma de outras telas), não um flag específico de Cadastro. Veja **[Permissões](/hec/resources/cadastros-permissoes)**.
:::
***
::: warning Limite de linhas pode bloquear a importação
Se uma importação ultrapassaria o **limite de linhas do tenant**, ela é **bloqueada inteira** — nenhuma linha entra. O limite é por tenant e sempre bloqueante. Veja **[Cobrança e limites](/hec/resources/cadastros-cobranca)**.
:::
---
---
url: 'https://docs.horusbi.com.br/dataviz/04-features/indicadores.md'
---
# Indicadores
O módulo **Indicadores** é o cockpit de métricas de negócio ao vivo da plataforma. Cada indicador configurado vira um card que pulsa com o valor real, mostra variação vs o ciclo anterior e sinaliza o estado com cores (verde / amarelo / vermelho).
O módulo tem um **papel duplo**: ele é ao mesmo tempo um painel de acompanhamento para os usuários e a raiz de contexto que o [Chat com Dados](/ia/chat/) lê para entender o negócio. Cuidar bem dos indicadores melhora ao mesmo tempo o cockpit e as respostas do chat.
::: tip Em uma frase
Um indicador curado aparece no cockpit para o usuário E entra no contexto da IA nas conversas. Dois benefícios, uma configuração.
:::
Para entender o que é um indicador e como defini-lo, veja **[Indicadores: Ensinando seu Negócio](/ia/chat/fatos.md)**. Para entender como o Lumo aprende o comportamento normal de cada indicador e avisa quando ele sai do padrão, veja **[Monitoramento: o vigia](./monitoramento.md)**.
***
## O Cockpit
A rota `/indicadores` é o ponto central do módulo. Ao abrir você encontra um grid de cards, organizados em duas faixas:
* **Indicadores curados** — todos os indicadores curados dos apps que você tem acesso, reunidos em um único painel cross-app.
* **Meus Indicadores** — os indicadores que você marcou com estrela (seguiu), na ordem que você definiu.
Cada card mostra:
| Elemento | O que representa |
|---|---|
| **Valor** | O número atual do indicador (calculado ao vivo) |
| **Variação** | Diferença vs o ciclo anterior (ex.: semana passada, mês anterior) |
| **Status RAG** | Verde = dentro do esperado; Amarelo = atenção; Vermelho = crítico |
| **Mini-série** | Sparkline com a tendência recente |
| **Meta** | Linha de referência, quando configurada |
| **Ícone** | Símbolo visual associado ao indicador |
### Meus Indicadores
Você monta sua visão pessoal com dois gestos:
* **Seguir** — clique na estrela do card. O indicador aparece na faixa "Meus Indicadores".
* **Reordenar** — arraste os cards seguidos para a ordem de importância que faz sentido para você.
Há um teto de **5 indicadores seguidos**: a faixa mostra "Seguindo · N de 5". Ao tentar seguir o sexto, aparece o aviso **"Giro cheio"** pedindo para deixar de seguir um antes. O limite mantém o acompanhamento como um radar: poucos números, olhados de verdade.
Seguir um indicador também é o que liga o [monitoramento automático](./monitoramento.md) dele para você: mudanças de faixa geram aviso no sino e o indicador entra na sua Home Giro.
::: info
Seguir um indicador curado não cria uma cópia — você está se inscrevendo no indicador existente do app. Se o indicador for atualizado ou removido pelo admin, o seu card reflete a mudança.
:::
### Personalizando o card
Cada card tem um editor de apresentação (ícone de engrenagem) onde você ajusta **como** o indicador é exibido — sem mudar a definição da métrica:
* **Formato do número** — moeda, porcentagem, casas decimais, etc.
* **Ícone** e **cor** — o símbolo do card e a cor de destaque.
O semáforo (verde/amarelo/vermelho) tem casa própria: por padrão ele é automático, aprendido pelo Lumo a partir do histórico, com a opção de faixas manuais. O editor de apresentação tem um atalho direto para esses controles. Veja **[Monitoramento: o vigia](./monitoramento.md)**.
***
## A Home Giro (beta)
Para quem participa do beta, a página inicial da plataforma ganha um formato novo, o **Giro**: uma home pensada para responder em segundos "como está o negócio hoje e o que mudou". Quem já tinha uma home personalizada não perde nada: as duas convivem em abas, **"Giro"** e **"Minha página (clássica)"**, e a plataforma lembra a última aba que você usou.
O Giro abre com uma saudação e a data do dia, seguido de três blocos:
### Meus indicadores
Os até 5 indicadores que você segue, como cards ao vivo (valor, variação, estado). Se você ainda não segue nenhum, o bloco convida: "Escolha até 5 indicadores para acompanhar aqui", com um botão que leva ao cockpit.
### Hoje
As **exceções das últimas 24 horas**: os indicadores seguidos que mudaram de estado. Cada card mostra a transição (de qual cor para qual cor), o valor atual com a variação e o horário ("Saiu da faixa às 09:40" ou "Voltou ao normal às 14:15"). Clicar no card abre o detalhe do indicador já com o "Por quê?" para você investigar.
Num dia calmo, o bloco diz exatamente isso: **"Nada fora do esperado. Bom trabalho."** A ausência de exceções é a notícia.
### Aplicações recentes
Todas as suas aplicações, com busca e três ordenações: **Mais usados** (padrão, as que você mais abre vêm primeiro), **Nome** e **Recência**. Cada card traz a contagem de acessos e há quanto tempo foi o último ("12× · há 2 d").
::: info O que qualifica uma exceção
O bloco "Hoje" lista mudanças reais de estado do [monitoramento](./monitoramento.md) (piorou ou se recuperou), calculadas de forma determinística sobre os indicadores que você segue. Sem indicadores seguidos, não há o que vigiar, e o bloco avisa isso em vez de mostrar um falso "tudo certo".
:::
***
## Pessoal vs Curado
Todo indicador tem um tipo que determina quem o vê e se ele entra no contexto da IA.
| | Pessoal | Curado |
|---|---|---|
| **Quem criou** | O próprio usuário, no cockpit | Administrador (ou promovido) |
| **Visível para** | Só o dono | Todos que acessam o app |
| **Alimenta a IA de outros?** | Não | Sim |
| **Aparece na curadoria do app?** | Não | Sim |
**Indicador pessoal** é um radar privado. O usuário cria para acompanhar algo que importa para ele sem impactar o contexto dos outros. Ele não vaza para a IA nas conversas de terceiros.
**Indicador curado** é a definição oficial do conceito de negócio naquele app. Ele entra no contexto da IA para todos que usam o app e aparece na tela de curadoria do admin.
### Como um indicador torna-se curado
Um administrador pode **promover** um indicador pessoal para curado — diretamente na tela de curadoria. A partir daí o indicador passa a alimentar a IA e a aparecer para os outros usuários do app.
::: info Indicadores criados pelo admin já nascem curados
Criações pelo editor do app (botão "Novo Indicador", "Gerar com IA", clone, template) são curadas por padrão. Só a criação pelo próprio usuário no cockpit nasce como pessoal.
:::
***
## Criando um indicador
Qualquer usuário pode criar um indicador pessoal direto no cockpit, sem depender de um admin. O botão **Criar indicador** abre a janela "Novo indicador", organizada em três abas, da mais rápida para a mais detalhada:
### Sugestões (aba inicial)
A plataforma sugere indicadores prontos a partir de duas fontes:
* **Dos seus painéis**: os KPIs que já existem nos seus dashboards, prontos para virar indicador com um clique.
* **Populares no seu time**: os KPIs mais adotados pelas pessoas do seu time.
Em cada sugestão você escolhe **Adicionar** (cria direto, com nome e métrica do widget) ou **Ajustar antes** (leva a configuração para a aba Manual, para refinar).
### Com IA
Você escolhe a aplicação e descreve o que quer acompanhar em uma frase, como "Faturamento confirmado por mês" ou "Ticket médio dos pedidos". A IA monta um rascunho e mostra a **revisão**: o nome (editável), a métrica escolhida, o período e os filtros. Você pode **Criar**, pedir para **Gerar de novo** ou **Ajustar no manual**.
A IA trabalha só com as **colunas reais da aplicação escolhida**: ela não inventa métrica e nada é criado sem a sua confirmação. Se ela não conseguir montar um indicador para a sua descrição, o aviso sugere descrever de outro jeito ou usar a aba Manual.
### Manual
O caminho campo a campo:
1. Escolha a **aplicação** e dê um **nome** claro (o termo que sua empresa usa no dia a dia).
2. Selecione a **coluna principal**: a medida que o indicador acompanha, com a função de agregação (soma, média, contagem…).
3. Em **Opções avançadas**, se precisar: a **coluna de data** (usada no ciclo e na série histórica), o **período padrão** (semana, mês, trimestre…), os **filtros** que fixam o contexto do conceito (ex.: `Status = "Confirmado"`) e uma **descrição** de uma frase.
A coluna de data é opcional. Sem ela, o indicador vira uma "foto do momento" e o Lumo constrói o histórico registrando o valor diariamente; veja [Indicador sem coluna de data](./monitoramento.md#indicador-sem-coluna-de-data-indicador-foto).
::: tip
Use os filtros para fixar o contexto. Um indicador "Faturamento" sem `Status = "Confirmado"` vai incluir vendas canceladas em todas as visualizações.
:::
### Aviso de duplicado
Nas três abas, se já existe um indicador idêntico (mesmo nome, aplicação, data de controle e filtros), a plataforma pergunta antes de criar: "Já existe um indicador idêntico. Criar mesmo assim?". É um aviso, você pode prosseguir; mas na maioria dos casos o melhor é seguir o indicador que já existe.
O indicador criado no cockpit nasce como **pessoal**. Quando um admin considera que ele representa um conceito relevante para o app, ele pode promovê-lo para curado.
***
## Curadoria — "Indicadores por app"
Administradores têm acesso à tela de curadoria, que lista os indicadores curados organizados por app. Nela é possível:
* **Criar** um novo indicador curado diretamente.
* **Editar** ou **excluir** um indicador existente.
* **Promover** um indicador pessoal de um usuário para curado.
* Controlar as **Colunas IA** — quais colunas do app a IA pode enxergar nas conversas.
::: warning
Remover um indicador curado o desvincula de todos os usuários que o seguiam. Use com cuidado; prefira desabilitar antes de excluir caso haja dúvida.
:::
### Possíveis duplicados
A tela de curadoria destaca com o selo **"Duplicado"** os indicadores que têm outros com o **mesmo nome, aplicação, data de controle e filtros**. O selo é apenas uma dica: cabe ao admin decidir se são de fato o mesmo conceito.
### Unificar indicadores
Quando a duplicação é real, o admin seleciona 2 ou mais indicadores do mesmo contexto (mesma aplicação, mesma data de controle, mesmos filtros) e usa **Unificar selecionados**. Na janela de unificação:
1. Escolha **qual indicador manter** (a sugestão inicial é o que tem mais métricas, mas você pode escolher qualquer um).
2. Revise a prévia: as **métricas dos selecionados são reunidas** no indicador mantido, e os demais serão arquivados.
3. Confirme. **Quem seguia os indicadores arquivados passa a seguir o mantido** automaticamente.
O indicador mantido preserva seu histórico de monitoramento e suas análises. Os arquivados saem do catálogo (e do contexto da IA), mas não são apagados de forma irreversível.
A curadoria é onde o administrador garante que o catálogo de indicadores do app seja enxuto, bem definido e alinhado com o vocabulário da empresa — o que resulta em respostas mais consistentes do chat e um cockpit mais útil.
***
## Detalhe do indicador
Clique em qualquer card para abrir a tela de detalhe (`/indicadores/[id]`).
O topo da tela concentra o essencial: o **valor atual** com a variação, o seletor de **métrica** (quando o indicador tem valores alternativos), a barra de **filtros**, o botão **"Por quê?"** (a explicação por IA, veja a seção abaixo) e duas ações:
* **Abrir no Explorer**: o caminho para a tabela bruta. Abre o Explorer da aplicação já com o recorte do indicador (métrica, período e filtros), para quem quer ver as linhas que compõem o número.
* **De onde vem**: um cartão de proveniência com a tabela de origem em destaque, a métrica e agregação, a coluna de tempo, os filtros fixos, o período aplicado e quando os dados foram carregados. É a resposta rápida para "posso confiar neste número?".
O corpo da tela é organizado em três abas:
* **Análise**: a série completa do indicador, com controle de período e granularidade e um **overlay do ciclo anterior** sobreposto para comparação visual; ao lado, o **breakdown**: escolha na hora qual dimensão quer usar para decompor o valor (filial, produto, canal, etc.) e veja os maiores contribuintes (top‑N + "Outros"). Clicar numa categoria **isola** aquela fatia, um filtro indicado por um chip "Filtrado por" no topo (com botão para limpar).
* **Monitoramento**: o trabalho do vigia, com o veredito do período atual, o gráfico com a faixa esperada, os desvios recentes e o padrão do seu negócio. Detalhes em [Monitoramento: o vigia](./monitoramento.md).
* **Configuração**: as definições do indicador em três sub-abas, **Definição** (valor principal, valores alternativos, coluna de data, filtros, período), **Apresentação** (formato, ícone, cores) e **Monitoramento** (grão, janela, direção, faixas manuais e o aviso no sino).
::: info
O breakdown usa as colunas disponíveis no app — você escolhe na hora qual dimensão quer ver. A dimensão **não** é parte da definição do indicador; ela pertence à pergunta. (Os "valores alternativos" do indicador, ao contrário, são **métricas** do mesmo conceito — ex.: Faturamento → Valor Total, Ticket Médio, Margem.)
:::
***
## "Por quê?" — explicação por IA
No detalhe do indicador, o botão **"Por quê?"** abre um painel lateral com uma análise gerada pela IA sob demanda. Enquanto ela roda, o painel mostra o progresso em três etapas:
1. **Consultando o período**: a IA levanta o panorama de métricas do app, o valor atual de cada uma vs o período de comparação.
2. **Analisando com IA**: o raciocínio sobre o que explica a variação do indicador.
3. **Estruturando os fatores**: a extração dos **drivers**, os fatores que mais contribuíram, exibidos como destaques junto da explicação.
O resultado vem em linguagem natural, acompanhado da tabela de panorama (métrica, valor atual, anterior, variação %).
A análise fica guardada por indicador e período: abrir o "Por quê?" de novo no mesmo período retorna a mesma análise na hora, sem novo custo. Para forçar uma análise nova (por exemplo, depois de uma carga de dados), use o link **"Atualizar"** no rodapé do painel. A atualização tem um intervalo mínimo de **10 minutos** entre pedidos; antes disso, a plataforma pede para aguardar.
::: tip
Use "Por quê?" quando o status virou vermelho e você precisa entender rapidamente o que aconteceu, sem montar um dashboard nem formular uma pergunta no chat.
:::
***
## Notificações
Quando um indicador que você segue piora de status, a plataforma envia uma notificação no **sino** (canto superior direito):
* **Amarelo** — o indicador entrou em zona de atenção.
* **Vermelho** — o indicador está em estado crítico.
* **Recuperação** — o indicador voltou para verde após um período amarelo ou vermelho.
Clicar na notificação abre o detalhe do indicador já com o **"Por quê?"**, para que você tenha contexto imediato sobre o que mudou.
O que conta como "piorar" ou "recuperar" é decidido pelo [monitoramento automático](./monitoramento.md), que reavalia os indicadores de hora em hora.
::: info
Só indicadores que você segue (estrela) geram notificações. Indicadores curados que você não segue não disparam alertas. E o aviso é opcional por indicador: em Configuração → Monitoramento, a chave **"Avisar no sino quando mudar de faixa"** (ligada por padrão) controla só as suas notificações, sem afetar os outros seguidores.
:::
***
## Indicadores e a IA
Os indicadores curados são a raiz que a Lumia lê para entender o negócio. Quando alguém abre uma conversa no chat sobre uma aplicação, o catálogo de indicadores curados daquele app entra como contexto — e a IA usa esses indicadores para resolver ambiguidades e ancorar as respostas em definições explícitas.
Curar bem os indicadores tem efeito duplo:
* O **cockpit** fica mais relevante — cards bem definidos mostram o que realmente importa.
* O **chat** fica mais consistente — definições claras eliminam adivinhações.
O inverso também vale: um catálogo com muitos indicadores parecidos ou mal definidos piora tanto o cockpit (sinal diluído) quanto o chat (ambiguidade aumentada).
::: tip
Se o chat está dando respostas inconsistentes, o primeiro lugar para olhar é o catálogo de indicadores. Veja **[Indicadores: Ensinando seu Negócio](/ia/chat/fatos.md)** para o guia de boas práticas e os anti-padrões a evitar.
:::
---
---
url: 'https://docs.horusbi.com.br/ia/chat/fatos.md'
---
# Indicadores: Ensinando seu Negócio
Um **indicador** é a forma de explicar para o chat o que cada conceito do seu negócio significa **na sua aplicação**. É a peça que transforma uma resposta razoável em uma resposta **consistente**.
::: tip Em uma frase
Um indicador é um exemplo de pergunta resolvida que você grava para o chat — você nomeia um conceito ("Faturamento") e mostra **qual coluna é a métrica**, **em qual coluna o tempo é medido** e **que filtros padrão se aplicam**.
:::
Os indicadores têm um papel duplo: além de alimentar a IA com contexto curado, eles aparecem no **cockpit de Indicadores** — um painel que exibe cada métrica ao vivo com status RAG, variação vs ciclo anterior e série histórica. Cuidar bem dos indicadores melhora ao mesmo tempo a qualidade das respostas e a inteligência do cockpit.
***
## A analogia do analista novo
Imagine que você está sentado no primeiro dia com um analista recém-contratado. Ele tem acesso ao seu modelo de dados, sabe ler tabelas e sabe escrever SQL. Em algum momento alguém entra na sala e pergunta:
> "Qual o faturamento de ontem?"
O analista olha pra você confuso — no modelo tem três colunas que poderiam ser "faturamento": `valor_bruto`, `valor_liquido` e `valor_pago`. Tem duas colunas de data: `data_emissao` e `data_competencia`. E ele percebeu que vendas têm um status — "Confirmado", "Cancelado", "Pendente".
Você responde:
> "Quando alguém aqui fala em **Faturamento**, é o **Valor Líquido** da tabela de vendas, considerando apenas o status **Confirmado**, com o tempo medido pela **Data de Emissão**."
Pronto. O analista agora sabe. Para a próxima pergunta — "faturamento do trimestre", "faturamento por filial", "faturamento ontem vs hoje" — ele aplica essa mesma definição variando apenas o que a pergunta variou.
**É exatamente isso que um indicador faz com o chat.**
***
## Por que o chat precisa de indicadores
O chat **consegue ver toda a estrutura da sua aplicação** — tabelas, colunas, expressões, dashboards. Ele pode responder perguntas mesmo sem nenhum indicador configurado.
**O problema é a ambiguidade.** Em modelos de dados reais quase sempre existe mais de uma resposta razoável:
* Múltiplas colunas que poderiam ser "vendas" (`valor_bruto`, `valor_liquido`, `valor_pago`)
* Múltiplas datas que poderiam ser "tempo" (emissão, competência, vencimento)
* Filtros sutis que mudam o resultado (`status = "Confirmado"`, `tipo = "Comercial"`)
Sem indicadores, o chat **faz uma escolha razoável a cada conversa** — mas a escolha não é determinística. Ele pode acertar hoje e errar amanhã na mesma pergunta, porque mais de um caminho parecia válido.
Um indicador resolve isso sendo um **exemplo curado**: para *este* conceito de negócio na *esta* aplicação, a definição certa envolve *estas* colunas, *esta* data e *estes* filtros padrão.
::: info Indicador é definição, não relatório
Um indicador grava uma **definição** que o chat usa como ponto de partida toda vez que aquele conceito aparece numa pergunta — independente das variações. "Faturamento do trimestre", "faturamento por filial" e "faturamento ontem vs hoje" são todas a **mesma definição** com agrupamentos e recortes diferentes. Você define uma vez; o chat aplica em todas as perguntas que tocam o conceito.
:::
***
## Como criar um indicador
Os indicadores vivem dentro do editor da aplicação, na aba **Conceitos IA**.
1. Abra a aplicação no modo de edição (ícone de lápis no menu superior).
2. Acesse a aba **Conceitos IA**.
3. Há dois caminhos:
### Manualmente — botão "Novo Indicador"
Você preenche os campos diretamente (Nome, Valor Principal, Coluna de Data, etc.). Use quando já sabe exatamente o que quer cadastrar.
### Automaticamente — botão "Gerar com IA"
Você descreve em linguagem natural ("faturamento líquido considerando apenas vendas confirmadas, com tempo medido pela data de emissão"). A IA sugere a configuração, você revisa, ajusta se precisar e salva.
::: tip
O modo automático é ótimo para popular os primeiros indicadores da aplicação rapidamente. Mas **sempre revise** — em modelos ambíguos, a sugestão pode escolher uma coluna parecida em vez da que você quer.
:::
Para a referência detalhada da UI (formulário, duplicar, expandir, excluir), veja [Aba Conceitos IA](/dataviz/02-apps/ai-concepts.md).
***
## Os campos em detalhe
Cada indicador é descrito por um pequeno conjunto de campos. Entender o propósito de cada um é o que separa um indicador bom de uma definição vaga.
### Nome *(obrigatório)*
O termo que **as pessoas da sua empresa de fato usam** quando falam desse conceito. Em português, sem jargão técnico.
```
✅ "Faturamento", "Ticket Médio", "Inadimplência", "Pedidos em Aberto"
❌ "SUM_VLR_LIQ", "FAT_2024", "Vendas_Mensal_Por_Filial"
```
O nome é o sinal mais forte que o chat usa para identificar **quando aplicar o indicador**. Se você o nomear "Vendas" mas a sua empresa fala "Faturamento", o indicador perde valor.
### Descrição *(opcional)*
Texto livre com a **nuance** do conceito. Use para diferenciar conceitos parecidos ou registrar uma regra de negócio sutil que o nome sozinho não carrega:
```
Descrição: "Faturamento comercial. Considera apenas vendas confirmadas
do canal direto. Não inclui receita financeira (juros, multas)."
```
::: warning A descrição não substitui um indicador novo
Se a descrição está crescendo a ponto de "explicar dois conceitos diferentes em um só indicador", isso é sinal de que você precisa de **dois indicadores**, não de uma descrição maior.
:::
### Valor Principal *(obrigatório)*
A **coluna ou expressão que representa a métrica** desse conceito. Tem que ser uma medida — algo que se soma, conta ou agrega.
```
Valor Principal: [Fato Vendas]."VALOR_LIQUIDO" (soma)
```
### Valores Alternativos *(opcional, mas muito importante)*
Outras métricas **que compartilham o mesmo contexto** desse indicador — mesma tabela, mesma data, mesmos filtros. É aqui que você evita criar indicadores redundantes.
Exemplo: o indicador "Faturamento" tem Valor Líquido como principal, e Lucro, Margem (%), Ticket Médio e Quantidade Vendida como alternativos. Quando alguém pergunta "qual o ticket médio do mês?", o chat usa o **mesmo indicador Faturamento**, só troca a métrica.
::: info Alternativos são métricas, não dimensões
Valores Alternativos são outras **medidas** do mesmo contexto (Lucro, Margem, Ticket Médio). Dimensões de quebra — como Filial, Produto, Cliente — **não pertencem ao indicador**: elas vêm das colunas da aplicação e o usuário escolhe na hora de fazer a pergunta.
:::
### Coluna de Data *(opcional, mas quase sempre presente)*
A coluna usada quando a pergunta envolve tempo. Sem ela, "faturamento do mês passado" não tem ancoragem temporal e o chat precisa adivinhar entre as datas disponíveis.
```
Coluna de Data: [Calendário]."DATA" (mapeada para Data de Emissão)
```
### Filtros Padrão *(recomendado)*
Contexto fixo que **sempre** se aplica a esse conceito. Use quando o conceito tem uma regra de negócio que não pode ser esquecida.
```
Filtros Padrão: Status = "Confirmado"
```
Sem esse filtro, "faturamento" passaria a incluir vendas canceladas e pendentes — distorcendo todas as respostas. Filtros padrão são a forma de garantir que o conceito **chega íntegro** em qualquer pergunta.
***
## Comportamento que você precisa entender
Esse é o ponto onde a maioria das pessoas se enrosca, então vale entender devagar:
::: warning Todos os indicadores da aplicação são carregados em cada conversa
Quando alguém abre uma conversa com o chat sobre uma aplicação, **o catálogo inteiro de indicadores daquela aplicação entra junto** como contexto. O chat olha pra esse catálogo e escolhe os indicadores relevantes pra responder.
:::
Esse comportamento tem uma consequência direta:
* **Poucos indicadores bem definidos** → o chat olha 10 ou 20 cartas na mesa, identifica rapidamente o conceito certo, responde com clareza.
* **Muitos indicadores parecidos** → o chat precisa filtrar entre centenas de opções similares. Ele gasta mais tempo decidindo, **pode misturar definições** de indicadores próximos e **tende a alucinar** mais.
Em outras palavras: **excesso de indicadores piora o chat**. A qualidade sobe com indicadores curados e cai quando o catálogo vira um inventário pulverizado.
::: info Custo cresce junto com o catálogo
Esse mesmo catálogo entra no processamento **a cada mensagem da conversa** — incluindo respostas triviais como "oi, bom dia" ou "obrigado". Em uma aplicação com centenas de indicadores cadastrados, mesmo um simples cumprimento puxa o catálogo inteiro pra dentro do turno, o que se traduz em consumo de tokens (e custo) muito maior por interação. Catálogos enxutos não só respondem melhor — respondem **mais barato**.
:::
***
## O anti-padrão "dicionário gigante"
A intuição errada mais comum é: "**quanto mais indicadores eu cadastrar, melhor o chat fica**". É o oposto. Quando o catálogo passa de algumas dezenas, o chat **perde foco** — passa a hesitar entre opções parecidas e mistura definições.
::: danger Padrões a evitar
| Padrão errado | Por quê é ruim |
|---|---|
| Criar um indicador para cada **variação de pergunta** ("Vendas", "Vendas Mensais", "Vendas por Filial", "Vendas Ontem"…) | Todas são a mesma definição de "Vendas". Agrupamento, período e recorte são **como o chat usa** o indicador, não definições novas. |
| Criar um indicador por **dimensão de quebra** ("Receita por Cliente", "Receita por Produto", "Receita por Cidade"…) | A dimensão é decidida pela pergunta. Um indicador "Receita" cobre todas. |
| Replicar o mesmo indicador com **filtros temporais diferentes** ("Faturamento de 2024", "Faturamento Mensal", "Faturamento Diário") | Filtro de tempo o chat aplica sozinho. O recorte temporal pertence à pergunta, não à definição. |
| Criar um indicador para cada **métrica relacionada** quando elas compartilham o mesmo contexto (mesma data, mesmos filtros) | Use o campo **Valores Alternativos** dentro de um indicador. Um indicador "Faturamento" pode listar Lucro, Margem, Ticket Médio, Quantidade como alternativos. |
:::
O nome "dicionário gigante" é literal: cada possível pergunta vira um verbete. O catálogo cresce sem parar, fica difícil de manter, e o chat fica **pior**, não melhor.
***
## Sinais de mau dimensionamento
Alguns sinais práticos de que o catálogo está virando dicionário gigante:
* **Mais de 50 indicadores numa única aplicação.** Tipicamente um app de negócio tem entre 5 e 25 conceitos genuinamente diferentes. Acima de 50 quase sempre indica pulverização.
* **Você consegue agrupar indicadores em "famílias" óbvias.** Se cinco indicadores da lista são "Vendas X", "Vendas Y", "Vendas Z" — provavelmente é um único indicador "Vendas" com Valores Alternativos e a dimensão variando na pergunta.
* **Dois indicadores têm o mesmo Valor Principal, mesma Coluna de Data, mesmos Filtros Padrão.** São o mesmo indicador com nomes diferentes. Consolide.
* **Existem indicadores cujo nome só faz sentido para quem cadastrou.** Se ninguém na empresa fala assim no dia a dia, ninguém vai perguntar assim — e o indicador vira ruído.
* **As respostas do chat ficaram inconsistentes depois que o catálogo cresceu.** É sintoma de excesso de indicadores parecidos competindo entre si.
::: tip Heurística
Se você tem **mais de 50 indicadores numa única aplicação**, provavelmente está virando catálogo telefônico. Vale auditar.
:::
***
## Boas práticas
### Pergunte antes: "quantos conceitos genuinamente diferentes existem aqui?"
Tipicamente são **5 a 25 indicadores por aplicação**. Faça a lista no papel primeiro, depois cadastre. Comece pelos conceitos que **as pessoas perguntam**, não pelos que **existem no banco**.
### Um indicador = um conceito de negócio
A pergunta-teste: *se duas pessoas diferentes da empresa, ao ouvir o nome do indicador, descrevem **a mesma métrica**, é um conceito*. Se elas descrevem coisas ligeiramente diferentes, talvez sejam dois indicadores genuínos — ou talvez seja um conceito mal nomeado.
### Agrupe métricas relacionadas em "Valores Alternativos"
Se cinco métricas (Receita, Lucro, Margem, Ticket Médio, Quantidade) compartilham **mesma data, mesma tabela, mesmos filtros padrão**, são um único indicador com cinco valores. Não cinco indicadores.
### Use o campo Descrição para nuance, não para criar outro indicador
Se a diferença entre dois indicadores é uma frase ("este desconsidera estornos"), a Descrição do indicador resolve. Se a diferença é genuína (tabelas diferentes, datas diferentes, filtros diferentes), então sim, são dois indicadores.
### Cuide do nome
Linguagem que sua empresa **de fato usa**, em português, sem siglas técnicas. O nome do indicador é o canal pelo qual a IA reconhece "esta pergunta é sobre este conceito" — se o nome não bate com o vocabulário da empresa, o indicador passa despercebido.
### Revise periodicamente
* Se um indicador **nunca é usado**, remova ou consolide.
* Se dois indicadores **sempre são chamados juntos** na mesma pergunta, consolide num só com Valores Alternativos.
* Se o chat **erra consistentemente** em algum conceito, o indicador relacionado pode estar mal definido (coluna errada, filtro faltando).
### Quando estiver na dúvida, **menos é mais**
Você sempre pode adicionar um indicador novo depois. É muito mais difícil descobrir que o catálogo cresceu demais e ter que limpar 400 indicadores.
***
## Próximos passos
* Veja **[Exemplos de Indicadores](./exemplos-de-fatos.md)** para casos práticos lado a lado: indicadores bem feitos vs anti-exemplos do "dicionário gigante".
* Entenda **[Como o chat responde](./como-funciona.md)** para ver onde os indicadores entram no processo de uma pergunta.
* Consulte a **[Aba Conceitos IA](/dataviz/02-apps/ai-concepts.md)** para a referência detalhada da UI (formulário, duplicar, expandir, excluir).
* Explore o **[Cockpit de Indicadores](/dataviz/04-features/indicadores/)** para ver como os indicadores curados aparecem como cards ao vivo para os usuários.
---
---
url: 'https://docs.horusbi.com.br/hec/resources/02-infrastructure.md'
---
# Infraestrutura
O painel de **Infraestrutura** centraliza a gestão dos recursos de conectividade que alimentam toda a plataforma. É aqui que você cadastra, compartilha e administra as credenciais que permitem ao Horus se conectar às fontes de dados da sua organização.
## 🔌 Conexões de Dados
As **Conexões de Dados** (ou Credenciais) são os dados de acesso que permitem ao Horus conectar-se aos bancos de dados da sua organização — como PostgreSQL, MySQL, SQL Server, Oracle, entre outros.
### Gerenciamento
* **Listagem**: Visualize todas as conexões cadastradas no ambiente, com status e informações de uso
* **Criação**: Para criar uma nova conexão, o HEC redirecionará você para a interface segura do módulo **ETL**. Isso garante que os dados sensíveis de conexão sejam criptografados corretamente na origem
* **Exclusão**: Permite revogar uma credencial que não é mais necessária
> \[!WARNING]
> Ao excluir uma credencial, **todos os Dataflows que dependem dela pararão de funcionar imediatamente**. Certifique-se de que nenhum fluxo ativo utiliza essa conexão antes de excluí-la.
### 🤝 Compartilhamento
Por padrão, uma credencial é **privada** — visível apenas para quem a criou. Para que outros desenvolvedores possam usá-la em seus fluxos:
1. Clique na coluna **Usuários** da credencial desejada.
2. Adicione os usuários que precisam de acesso.
3. Eles passarão a ver esta conexão na lista de "Bancos de Dados" ao configurar os nós de leitura no ETL.
> \[!TIP]
> **Boas práticas de segurança**: Compartilhe credenciais apenas com os usuários que realmente precisam acessar aquela fonte de dados. Cada conexão deve seguir o princípio de privilégio mínimo.
---
---
url: 'https://docs.horusbi.com.br/etl/processors/inputs.md'
---
# Inputs (Leitura de Dados)
Os processadores de Input são responsáveis por iniciar o fluxo de dados. Eles se conectam a uma fonte externa (Banco de Dados, Arquivo, API) e trazem os dados para dentro do Dataflow.
***
## 🗄️ Bancos de Dados Relacionais
Estes nós permitem executar consultas SQL diretamente na fonte.
* **[Consulta SQL Server](./sql-server.md)** — Conecta ao Microsoft SQL Server
* **[Consulta PostgreSQL](./postgresql.md)** — Conecta ao PostgreSQL
* **[Consulta MySQL](./mysql.md)** — Conecta ao MySQL / MariaDB
* **[Consulta OracleDB](./oracle.md)** — Conecta ao Oracle Database
* **[Consulta Firebird](./firebird.md)** — Conecta ao Firebird SQL
* **[Consulta ODBC](./odbc.md)** — Conexão genérica via driver ODBC
* **[Consulta InterSystems IRIS](./intersystems-iris.md)** — Conecta ao InterSystems IRIS
***
## 📂 Arquivos e Planilhas
Nós para leitura de arquivos estruturados.
* **[Arquivo CSV](./csv.md)** — Lê arquivos `.csv`, `.txt` ou `.tsv` enviados pelo editor
* **[Arquivo Excel](./excel.md)** — Lê arquivos `.xlsx` ou `.xls`
* **[Arquivo Parquet](./parquet.md)** — Lê arquivos no formato Parquet (altamente performático)
* **[Google Sheet](./google-sheets.md)** — Lê dados diretamente de uma planilha do Google Sheets
***
## 🌐 Web e Serviços
* **[Requisição HTTP (API)](./http-request.md)** — Faz chamadas REST (GET, POST, etc.) para APIs externas
* **[Extrair Datalake](./datalake.md)** — Lê dados armazenados no Data Lake (S3/MinIO)
* **[Consultar Lakehouse](./lakehouse.md)** — Lê dados de uma tabela do Datawarehouse dentro do ETL
---
---
url: 'https://docs.horusbi.com.br/dw/tables/cadastros/dados.md'
---
# Inserir e editar dados
Com a estrutura pronta, é hora de preencher. Você tem **duas formas** de trabalhar os registros: a **grade** (estilo planilha, para edição rápida em massa) e o **formulário** (um drawer lateral, para focar num registro).
***
## 📋 A grade
A grade é uma planilha de verdade — quem está acostumado com Excel se sente em casa:
* **Célula selecionável** — clique para selecionar; navegue com as **setas**.
* **Editar** — **Enter** confirma a edição da célula, **Esc** cancela.
* **Multi-seleção** — selecione várias células e veja **soma** e **média** no rodapé.
* **Copiar** — copia a seleção como **TSV**, pronto para colar em planilhas.
* **Redimensionar coluna** — ajuste a largura arrastando a borda do cabeçalho.
A grade também respeita os tipos de campo:
* **Lista de valores** aparece como **pílula colorida** (a cor que você definiu na opção).
* **Chave externa** mostra o **rótulo** do registro referenciado; um vínculo órfão aparece como **`#chave`**.
* **Imagem** abre em **lightbox** ao clicar.
::: info No celular vira cards
Em telas pequenas, a grade se transforma em **cards** — cada registro vira um cartão, mais confortável de ler e editar no mobile.
:::
***
## 🗂️ O formulário (drawer)
Para criar ou editar um registro com calma, abra o **formulário** — ele desliza como um **drawer** lateral, mostrando todos os campos (agrupados pelas **seções** que você definiu na estrutura).
No formulário você pode:
* **Criar** um registro do zero ou **editar** um existente
* **Anexar imagens** — exibidas com **lightbox** para ampliar
* **Clonar** um registro — útil para duplicar e ajustar só o que muda
* **Excluir** um registro
::: info Ações do registro
Os **três pontinhos** (`⋯`) do registro abrem as ações — **excluir** e **histórico** — direto no drawer.
:::
***
## 🕜 Histórico do registro
Cada registro guarda seu próprio **histórico**: você vê **quem** alterou, **quando** (com o **nome do autor**) e **o que** mudou ao longo do tempo.
Isso dá rastreabilidade ao dado mantido à mão — quando um número não bate, dá para olhar o histórico e entender quando e por quem ele mudou.
> \[!NOTE]
> Esses metadados de autoria e data também podem ser **expostos no BI** como colunas (`# Criado em`, `# Autor`, etc.). Veja **[Cadastros no BI](/dataviz/04-features/cadastros/no-bi)**.
---
---
url: 'https://docs.horusbi.com.br/etl/processors/outputs/google-sheets.md'
---
# Inserir Google Sheet
O nó **Inserir Google Sheet** escreve os dados do fluxo em uma planilha do Google Sheets.
## Funcionalidades
* **Criar Planilha/Aba**: Se a aba ou planilha não existir, o nó tentará criá-la
* **Modos de Escrita**:
* `Overwrite`: Apaga todo o conteúdo da aba e escreve os novos dados
* `Append`: Adiciona os novos dados ao final da planilha existente
## Parâmetros de Configuração
### Credenciais
* **Descrição**: O JSON da Service Account com permissão de escrita na planilha
* **Recomendação**: Use uma variável `{GOOGLE_JSON}`
### ID da Planilha
* **Descrição**: O ID único da planilha (encontrado na URL)
### Nome da Aba
* **Descrição**: Nome da aba onde os dados serão escritos
### Modo de Escrita
* **Opções**:
* `Overwrite`: Sobrescreve tudo (cuidado!)
* `Append`: Adiciona linhas
## Validações
* No modo `Append`, o nó verifica se o cabeçalho das colunas do fluxo bate com o cabeçalho existente na planilha. Se houver divergência, o processo falhará para evitar corrupção de dados
---
---
url: 'https://docs.horusbi.com.br/etl/processors/outputs/datawarehouse.md'
---
# Inserir no Datawarehouse
O processador **Inserir no Datawarehouse** é o nó mais importante e central de escrita do HorusETL. Ele é o responsável por persistir os dados processados no ambiente do Horus, tornando-os disponíveis para consumo imediato em Dashboards (DataViz) ou armazenando-os para processamento em camadas posteriores.
Este processador oferece flexibilidade total para definir se o dado é um "Produto Final" ou uma "Etapa Intermediária" através de seus modos de operação.
## Modos de Operação
O processador possui dois modos distintos, selecionados na configuração:
### 1. Datawarehouse (DataViz / Produto Final)
* **Objetivo**: Disponibilizar o dado para consumo imediato nos painéis e Dashboards do Horus DataViz
* **Comportamento**: Os dados são processados e otimizados para consulta rápida
* **Uso Típico**: A última etapa de um fluxo ETL, onde o dado já está tratado, limpo e pronto para o usuário final
### 2. Datalake (Cloud / Camadas)
* **Objetivo**: Armazenar o dado no Data Lake do Horus (Cloud Storage) para processamento posterior ou arquivamento
* **Comportamento**: Os dados são salvos em formatos otimizados para leitura em lote (Parquet), permitindo controle de versionamento, particionamento e upserts (atualização diferencial)
* **Uso Típico**: Construção de arquiteturas em camadas (Bronze -> Silver -> Gold). Este modo escreve dados que serão lidos posteriormente por outros fluxos usando o processador **Leitura de Datalake**
***
## Configuração
### Parâmetros Principais
| Nome | Descrição |
| :--- | :--- |
| **Modo** | Define o destino do dado: **Datawarehouse** (Final) ou **Datalake** (Camadas). |
| **Tabela** | Seleciona a tabela de destino. Permite escolher uma tabela existente ou **criar uma nova tabela** diretamente pelo fluxo. |
| **Nome da Tabela** | (Apenas ao criar) Nome da nova tabela a ser criada. O sistema valida se o nome já existe. |
| **Recriar Tabela** | Se a estrutura dos dados (colunas/tipos) do fluxo for diferente da tabela existente, o sistema alerta e oferece um botão para recriar a tabela automaticamente ajustada aos novos dados. |
### Opções Específicas do Modo Datawarehouse
Quando o modo **Datawarehouse** está ativo, o comportamento de *Upsert* (inserir ou atualizar) é controlado pela definição da tabela, e não pelo processador.
> \[!IMPORTANT]
> **Cargas Incrementais e Upsert no Datawarehouse**
> Para garantir a unicidade dos registros e habilitar o comportamento de Upsert (manter apenas o registro mais recente) no modo Datawarehouse:
>
> 1. Após criar a tabela, clique no link **(Editar tabela em nova janela)** exibido no processador.
> 2. Na tela de modelagem da tabela, altere o "Modelo da Tabela" para **Chave Única**.
> 3. Selecione as colunas que compõem a chave única (`ID`, `CPF`).
>
> Isso garante que, se o fluxo tentar inserir um registro com um ID já existente, o sistema atualizará o registro antigo com os dados novos, mantendo a integridade sem duplicatas.
> \[!TIP]
> **Performance do Upsert**: O Horus Lakehouse faz upsert nativo sem custo adicional de performance. Ou seja, uma carga com upsert não é mais lenta do que uma carga comum. Exemplo prático: se um registro teve uma coluna alterada na origem, o SELECT vai trazer esse registro, mas como ele já existe no Lakehouse, o sistema automaticamente atualiza ao invés de duplicar usando as chaves escolhidas.
### Opções Específicas do Modo Datalake
Quando o modo **Datalake** está ativo, opções avançadas de gerenciamento de dados são exibidas:
#### Carga Diferencial (Use Delta)
Habilita o recurso de *Upsert* (Update + Insert). O sistema atualizará registros existentes e inserirá novos, evitando duplicidade.
* **Chaves Primárias (Primary Keys)**: Quando a Carga Diferencial está ativa, você deve definir quais colunas formam a chave única do registro (`ID`, `Codigo`, `Data`). O sistema usa essas chaves para identificar se um registro deve ser atualizado ou inserido
#### Particionamento (Se não usar Delta)
Se a carga não for diferencial (apenas *Append* ou carga completa), é possível particionar fisicamente os arquivos para otimizar leituras futuras. Disponível quando o fluxo possui varáveis de carga temporal/incremental.
* **Arquivo Único (NONE)**: Salva tudo em um bloco
* **Por Ano (YEAR)**: Separa os dados em pastas por ano
* **Por Mês (MONTH)**: Separa os dados em pastas por ano e mês
***
## Detalhes Técnicos
* **Validação de Estrutura**: O processador compara automaticamente os tipos de dados e nomes de colunas do fluxo (Metadata) com a tabela de destino. Se houver divergência (coluna nova, tipo alterado), ele bloqueia a execução segura e solicita a recriação ou ajuste
* **Performance (DW)**: No modo Datawarehouse, o sistema utiliza o motor de ingestão `HorusParquet`, otimizado para alta volumetria e indexação automática para o DataViz
* **Performance (Datalake)**: No modo Datalake, utiliza o engine `DeltaStore`. O uso de *Delta Lake* garante transações ACID e histórico de versões
* **Integração**: Tabelas criadas no modo **Datalake** ("cloud tables") são visíveis apenas para processadores de leitura técnica (como o *ExtractDatalake*), enquanto tabelas **Datawarehouse** aparecem publicamente no módulo de DataViz
## Exemplos de Uso
### Cenário 1: Arquitetura em Camadas (Bronze -> Silver -> Gold)
1. **Fluxo 1 (Ingestão)**: Lê dados crus de uma API e usa o **Inserir no Datawarehouse (Modo Datalake)** para salvar na tabela `vendas_bronze`.
2. **Fluxo 2 (Tratamento)**: Usa o *Leitura de Datalake* para ler `vendas_bronze`, limpa os dados, remove duplicatas e salva usando **Inserir no Datawarehouse (Modo Datalake)** na tabela `vendas_silver`.
3. **Fluxo 3 (Entrega)**: Lê `vendas_silver`, agrega os totais por mês e salva usando **Inserir no Datawarehouse (Modo Datawarehouse)** na tabela `vendas_gold_bi`.
* *Resultado*: O usuário final acessa apenas `vendas_gold_bi` no Dashboard, mas o time de dados tem todo o histórico rastreável nas camadas anteriores
### Cenário 2: Carga Incremental com Atualização (CDC)
* **Configuração**: Modo Datalake, Checkbox "Usar carga diferencial" ativado, Chave Primária = `id_pedido`
* **Fluxo**: O sistema recebe uma lista de pedidos do dia
* **Resultado**: Pedidos com `id_pedido` que já existiam são atualizados (status mudou de 'Pendente' para 'Pago'). Pedidos novos são inseridos. Isso mantém o Datalake sempre fiel ao estado atual sem duplicar dados
---
---
url: 'https://docs.horusbi.com.br/lumo/integracoes/manual.md'
---
# Instalação Manual (outros agentes)
As skills do Lumo são apenas arquivos `SKILL.md` (com uma pasta `references/` ao lado). Qualquer agente de IA que carregue skills nesse formato pode usá-las — basta copiá-las para a pasta de skills do agente.
> \[!NOTE]
> Este guia é para agentes **sem** instalador automático. Para Claude Code e Codex, prefira os guias dedicados — eles detectam o agente e instalam sozinhos: [Claude Code](/lumo/integracoes/claude-code) · [Codex](/lumo/integracoes/codex).
## O que você recebe
Baixe **[`lumo-cli-skill.zip`](https://storage.horusbi.com.br/download/lumo/lumo-cli-skill.zip)** e extraia. A estrutura é:
```
skills/
lumo-cli/
SKILL.md
references/...
lumo-design/
SKILL.md
references/...
instalar.sh
instalar.bat
LEIA-ME.txt
```
As duas skills:
* **`lumo-cli`** — operação geral da CLI.
* **`lumo-design`** — design de widgets/dashboards.
## Como instalar
Copie as pastas `lumo-cli` e `lumo-design` (de dentro de `skills/`) para o diretório de skills do seu agente. As pastas conhecidas hoje são:
| Agente | Pasta de skills |
|---|---|
| Claude Code | `~/.claude/skills/` |
| Codex | `~/.agents/skills/` |
| Outro agente | consulte a documentação do agente |
Exemplo genérico (ajuste o destino):
```bash
mkdir -p
cp -r skills/lumo-cli /
cp -r skills/lumo-design /
```
> \[!TIP]
> O `instalar.sh` aceita um destino explícito via `--skill-target`. Valores: `auto` (detecta), `all` (Claude + Codex), `claude`, `codex`, ou combinações (`claude,codex`). Para agentes fora dessa lista, faça a cópia manual acima.
## Pré-requisito que vale para qualquer agente
A skill **não** instala a CLI — ela assume que o `lumo` já está disponível no `PATH` e autenticado. Antes de usar:
```bash
lumo --help # CLI instalada?
lumo auth login # sessão ativa?
```
Veja [Primeiros Passos](/lumo/getting-started/) para instalar e autenticar o CLI.
---
---
url: 'https://docs.horusbi.com.br/lumo/integracoes.md'
---
# Integrações com IA
O Lumo CLI foi feito para ser operado tanto por você quanto por **agentes de IA**. Ele publica, a cada release, um pacote de *skills* que ensina um agente a usar a CLI corretamente: criar flows, editar tabelas do DW, montar dashboards, gerenciar credenciais e schedules, exportar imagens, etc.
Com a skill instalada, você pode pedir em linguagem natural — *"crie um flow que extrai vendas do Postgres e carrega na tabela fato\_vendas"* — e o agente traduz isso para os comandos e YAMLs corretos do Lumo.
## O que é distribuído
Cada release publica dois pacotes prontos:
| Pacote | Para quê |
|---|---|
| [`lumo-cli-skill.zip`](https://storage.horusbi.com.br/download/lumo/lumo-cli-skill.zip) | Instalador standalone com duplo-clique (`instalar.bat` no Windows, `instalar.sh` no macOS/Linux). **Detecta automaticamente** Claude Code, Codex, Cursor e OpenCode e instala nas pastas certas. É o caminho mais simples. |
| [`lumo-cli-plugin.tar.gz`](https://storage.horusbi.com.br/download/lumo/lumo-cli-plugin.tar.gz) | Pacote no formato de plugin (com `.claude-plugin/` e `.codex-plugin/`), para quem prefere gerenciar via marketplace/plugin do agente. |
Ambos embarcam **cinco skills**:
* **`lumo-cli`** — operação geral da CLI (flows, tabelas, apps, credenciais, schedules, push/pull, etc.).
* **`lumo-design`** — criação e estilização de widgets/dashboards (layout, temas, design dos componentes).
* **`lumo-decks`** — decks de slides e apresentações de telão com gráficos vivos.
* **`lumo-scope`** — escopo de projeto de BI, termo de abertura e proposta de pré-venda.
* **`lumo-mockup`** — mockup visual do dashboard antes de existir dado real.
## Agentes suportados
| Agente | Status | Guia |
|---|---|---|
| **Claude Code** | ✅ Instalador automático | [Claude Code](/lumo/integracoes/claude-code) |
| **Codex** | ✅ Instalador automático | [Codex](/lumo/integracoes/codex) |
| **Cursor** | ✅ Instalador automático | [Cursor](/lumo/integracoes/cursor) |
| **Outros** (qualquer agente que leia skills no formato `SKILL.md`) | ⚙️ Manual | [Instalação Manual](/lumo/integracoes/manual) |
> \[!IMPORTANT]
> A skill ensina o agente a **usar** o `lumo`, mas não instala a CLI. Garanta que o binário `lumo` já esteja instalado e autenticado antes (veja [Primeiros Passos](/lumo/getting-started/)). Um teste rápido: peça ao agente para rodar `lumo auth status`.
## Pré-requisitos
1. **Lumo CLI instalado** e no `PATH` — confira com `lumo --help`.
2. **Sessão autenticada** — `lumo auth login` (a skill assume que já existe sessão).
3. **Permissão "Uso do Lumo CLI"** concedida ao seu usuário pelo administrador do tenant.
---
---
url: 'https://docs.horusbi.com.br/hec/intro/overview.md'
---
# Introdução ao HEC
O **Horus Enterprise Control (HEC)** é o painel central de administração da plataforma Horus. É por meio dele que os administradores gerenciam identidades, níveis de acesso e recursos, além de monitorar o consumo de toda a infraestrutura de dados.
Enquanto módulos como ([ETL](/etl/), [Data Warehouse](/dw/), [DataViz](/dataviz/)) focam no processamento e na visualização das informações, o HEC atua como o orquestrador administrativo, centralizando configurações globais que refletem em toda a plataforma.
## 🏛️ Hierarquia de Entidades
A arquitetura da plataforma Horus é desenhada para suportar múltiplos níveis de isolamento estrutural. Esse modelo é ideal tanto para consultorias de dados (que gerenciam diversos clientes) quanto para corporações com múltiplas filiais ou unidades de negócio.
### 1. Cliente (Contratante)
Representa a entidade de nível superior (Root). Geralmente, é a organização detentora do licenciamento da plataforma Horus (uma Consultoria de BI ou uma Empresa de Software).
* **Papel**: Agrupador lógico e financeiro
* **Personalização (White-label)**: A definição da identidade visual (logotipo, cores e domínio personalizado) é configurada neste nível
* **Usuários de Cliente**: Perfis com privilégios administrativos globais, capazes de acessar e gerenciar todos os Tenants vinculados a este Cliente
### 2. Tenant (Ambiente)
Atua como a unidade principal para o isolamento de dados e recursos. Cada Tenant representa um ambiente dedicado ou um projeto específico sob a governança do Cliente.
* **Exemplo Prático**: Se o Cliente é uma consultoria de dados, os Tenants são os seus clientes finais ("Projeto Varejo", "Projeto Saúde")
* **Isolamento de Dados**: Tabelas, Dashboards e fluxos de trabalho de um Tenant são estritamente segregados e invisíveis para outros Tenants
* **Consumo**: O uso de processamento, armazenamento (storage) e a quantidade de usuários ativos são contabilizados individualmente por Tenant
### 3. Usuário
Representa a identidade global de acesso à plataforma. Com uma única credencial (e-mail e senha/2FA), um usuário pode ser autorizado a acessar múltiplos Tenants ou até mesmo múltiplos Clientes.
* **Vínculo e Acesso**: A permissão de entrada em um ambiente é estabelecida pela relação Usuário -> Tenant, sendo sempre governada pelos Grupos de Permissão (Roles)
> **Resumo Visual**:
>
> `Cliente (Consultoria)`
> ├── `Tenant A (Projeto Vendas)` -> `Usuários (João, Maria, Admin)`
> └── `Tenant B (Projeto RH)` -> `Usuários (Carlos, Admin)`
## 🧭 Visão Geral do Menu
O menu lateral do HEC estrutura as funcionalidades em quatro grandes pilares, acompanhando o fluxo de trabalho da administração do sistema:
### 1. 🏠 Home (Dashboard de Consumo)
Painel executivo focado na saúde do ambiente e na gestão de custos.
* **Monitoramento**: Gráficos detalhados sobre o consumo de Armazenamento (GB), Processamento (GB) e Tokens de Inteligência Artificial
* **Atividade**: Registro de usuários ativos e histórico de logins recentes (Auditoria)
* **Alertas**: Central de notificações sobre manutenções e o status geral do sistema
### 2. 👤 Usuários e Grupos
Dedicado à gestão de identidades e ao controle de acesso (RBAC).
* **Usuários**: Envio de convites, cadastro e gestão dos vínculos das contas
* **Grupos**: Criação de perfis de acesso padronizados (Roles) com atribuição de permissões granulares
* **Permissões**: Matriz de segurança detalhada para definir o nível de privilégio de cada grupo
### 3. 🖥️ Mesas (Desks)
Dedicado à organização lógica do espaço de trabalho. As Mesas funcionam como diretórios ou pastas corporativas para estruturar as entregas.
* **Mesas de BI**: Espaços voltados para a organização e o compartilhamento de Dashboards e visões de negócios
* **Mesas de Dados**: Espaços técnicos para a organização de Tabelas e Fluxos de integração (ETL)
### 4. 📦 Recursos (Resources)
Painel administrativo avançado para a gestão técnica da plataforma e sua infraestrutura.
* **Gestão de Ativos**: Inventário centralizado de Tabelas, Fluxos e Aplicações ativas no ambiente
* **Infraestrutura**: Gerenciamento seguro de credenciais e conexões de Banco de Dados
* **Admin**: Gestão global de Clientes e Tenants (exclusivo para superadministradores)
---
---
url: 'https://docs.horusbi.com.br/dataviz/00-intro.md'
---
# Introdução ao Horus DataViz
O **Horus DataViz** é o módulo de visualização e exploração de dados da plataforma. Sua função é permitir a criação de **Aplicações Analíticas** — ambientes completos de análise — a partir dos dados gerenciados no HorusDW.
Enquanto módulos como o [ETL](/etl/) e o [Data Warehouse](/dw/) focam na ingestão e no armazenamento dos dados, o DataViz é responsável por transformá-los em Dashboards interativos, Relatórios e insights acessíveis para toda a organização.
## 🧩 Principais Conceitos
### 1. Aplicação
A **Aplicação** é a unidade principal de trabalho no DataViz. Funciona como um "portal analítico" dedicado a um tema de negócio.
* **Estrutura**: Agrupa **Fontes de Dados** (Tabelas), **Regras de Negócio** (Relacionamentos e Expressões) e **Visualizações** (Dashboards) em um único pacote
* **Navegação**: Uma Aplicação pode ter múltiplas páginas (Dashboards) e filtros que persistem durante a navegação do usuário
* **Exemplo Prático**: Uma Aplicação chamada "Análise Comercial" pode conter Dashboards de "Visão Geral", "Detalhes por Vendedor" e "Análise Geográfica", todos consumindo as mesmas Tabelas e Expressões
### 2. Dashboard
É uma página dentro da Aplicação, onde os elementos visuais são organizados.
* **Layout**: Utiliza um grid (grade) responsivo para posicionar os componentes visuais (Widgets)
* **Múltiplas Páginas**: Uma Aplicação pode ter N Dashboards, cada um focado em um aspecto diferente da análise
### 3. Widget
É o componente visual que exibe os dados dentro de um Dashboard.
* **Variedade**: Gráficos de barras, pizza, mapas, KPIs, tabelas pivô, diagramas e muito mais
* **Interatividade**: Cada Widget pode vir de uma Tabela diferente, mas todos "conversam" entre si por meio dos filtros da Aplicação (recurso conhecido como *cross-filtering*)
### 4. Mesa
As **Mesas** organizam onde as Aplicações são salvas e quem tem acesso a elas. Funcionam como diretórios corporativos com controle de permissão.
* **Minha Mesa**: Espaço privado para rascunhos e Aplicações em desenvolvimento
* **Mesas Compartilhadas**: Espaços oficiais onde as Aplicações são publicadas para a organização ("Mesa Diretoria", "Mesa Vendas")
***
## 🔄 Fluxo de Criação
O processo de criação no DataViz segue uma lógica de construção em camadas, desde a conexão com os dados até a publicação para os usuários finais:
```mermaid
graph TD
Data[(HorusDW)] -->|1. Conectar| App[Aplicação]
subgraph DataModeling [Modelagem]
App -->|Importar| Tables(Tabelas)
Tables -->|Ligar| Rels(Relacionamentos)
end
subgraph Design [Visualização]
App -->|Criar| Dash1(Dashboard: Visão Geral)
App -->|Criar| Dash2(Dashboard: Detalhes)
Dash1 -->|Adicionar| W1[Widget: Gráfico Vendas]
Dash1 -->|Adicionar| W2[Widget: KPI Faturamento]
end
App -->|Publicar| Users((Usuários Finais))
```
1. **Criação**: Crie uma nova Aplicação na opção de "Minha Mesa".
2. **Modelagem**: Selecione quais Tabelas do DW farão parte da análise e defina como elas se relacionam (ligar `Vendas` com `Clientes` pela coluna `ID do Cliente`).
3. **Design**: Crie Dashboards e arraste Widgets para a tela, configurando Dimensões (Eixo X) e Métricas (Eixo Y) para melhor visualização dos dados.
4. **Publicação**: Publique a Aplicação em uma Mesa Compartilhada para que outros usuários possam acessar via **[HEC](/hec/resources/01-content)**.
---
---
url: 'https://docs.horusbi.com.br/dw/intro.md'
---
# Introdução ao HorusDW
O **HorusDW (Data Warehouse)** é o módulo responsável pelo armazenamento, organização e governança dos dados estruturados. Ele funciona como a base de dados central da plataforma, organizando as informações em três camadas distintas que facilitam a gestão do ciclo de vida do dado.
***
## 🧩 Conceitos Fundamentais
### 1. 🏠 Minha Mesa
É o ambiente privado de trabalho de cada usuário — um espaço seguro para importar, testar e ajustar dados antes de torná-los oficiais.
* **Função** — Serve como um "laboratório" onde você pode subir planilhas, criar tabelas manuais, testar fórmulas e ajustar metadados livremente
* **Privacidade** — O que está na "Minha Mesa" é visível **apenas para você**, até que decida publicar ou compartilhar com outros usuários via HEC
* **Ação Principal** — Botão **"Carregar Dados"** (Upload de Excel)
> \[!TIP]
> Para cargas avançadas lendo de diversas fontes de dados, use o [HorusETL](/etl/getting-started/). É necessário um [Agente ETL](/etl/guides/agentes) instalado. A tabela resultante aparecerá em sua Minha Mesa no HorusDW. Veja o [Guia da Plataforma](/guia/) para entender o pipeline completo.
### 2. 🗄️ Mesas
São os ambientes compartilhados e governados da organização, onde ficam armazenados os dados oficiais.
* **Função** — Armazenar os dados validados e prontos para consumo
* **Organização** — Geralmente divididas por camadas técnicas (*Raw*, *Trusted*, *Gold*) ou por sistemas de origem (*Sistema A*, *Sistema B*)
* **Governança** — Tabelas publicadas em Mesas não podem ser editadas livremente. Elas resultam de uma **Publicação** vinda da "Minha Mesa" ou de processos automáticos (ETL). A criação e manutenção de Mesas é realizada pelo módulo administrativo **HEC**
### 3. 🏷️ Datamarts
São visualizações de negócio que agrupam tabelas de diferentes Mesas, facilitando o acesso por área temática.
* **Função** — Enquanto as Mesas organizam onde o dado *está guardado*, os Datamarts organizam como o dado *é encontrado*. A definição de Datamarts também é gerenciada via **HEC**
* **Exemplo** — O Datamart "Vendas 360" pode conter a tabela de `Faturamento` (Mesa Financeira) e `Clientes` (Mesa CRM)
***
## 📦 Tipos de Tabelas
Dentro do HorusDW, existem dois tipos principais de tabelas:
### 💾 Tabela de Dados (Física)
São tabelas cujos dados são armazenados fisicamente no banco de dados do Horus.
* **Origem** — Upload manual (Excel) ou Processos de Carga (ETL)
* **Edição** — Permite edição linha a linha, criação de Colunas Calculadas (Fórmulas) e uso da IA (FixLabels) para ajustes de rótulos
### ☁️ Tabela Cloud (Datalake)
São tabelas virtuais que apontam para arquivos Parquet armazenados em Data Lakes.
* **Origem** — Mapeamento de arquivos externos
* **Edição** — **Apenas leitura**. Você visualiza a estrutura e as partições, mas não edita os dados diretamente. Ideal para grandes volumes de dados (Big Data)
***
## 🔄 Fluxo de Trabalho
O ciclo de vida comum de um dado no HorusDW segue o fluxo de "Importação → Preparação → Publicação":
```mermaid
graph LR
Input[📄 Arquivo Excel] -->|Carregar Dados| MyDesk(🏠 Minha Mesa)
subgraph Sandbox [Ambiente Privado]
MyDesk -->|1. Ajustar Tipos| MyDesk
MyDesk -->|2. Criar Fórmulas| MyDesk
MyDesk -->|3. Validar| MyDesk
end
MyDesk -->|🚀 Publicar| PublicDesk(🗄️ Mesa Oficial)
subgraph Governance [Ambiente Compartilhado]
PublicDesk
end
PublicDesk -->|Consumo| Dashboards[📊 DataViz / Dashboards]
```
1. **Importação** — O usuário sobe um arquivo Excel para sua **Minha Mesa**
2. **Preparação** — Nesta etapa, o usuário refina a tabela: renomeia colunas técnicas (`nm_cli` → `Nome Cliente`) usando **FixLabels (IA)**, configura tipos de dados, define chaves primárias e particionamento
3. **Publicação** — Estando tudo validado, o usuário **Publica** a tabela para uma **Mesa** (*Comercial*), tornando-a disponível para outros usuários criarem Dashboards
> \[!NOTE]
> Após publicar suas tabelas em uma Mesa Oficial, o próximo passo é criar Dashboards no [DataViz](/dataviz/01-getting-started/create-app). Veja o [Guia: Do Dado ao Dashboard](/guia/jornada) para o fluxo completo.
---
---
url: 'https://docs.horusbi.com.br/etl/intro.md'
---
# Introdução ao HorusETL
O **HorusETL** é a ferramenta de integração de dados *low-code* da suíte HorusBI. Ele permite criar fluxos de extração, transformação e carga (ETL) de dados visualmente, com execução na nuvem ou em infraestrutura local (On-Premise) através de Agentes.
***
## 🧩 Como Funciona?
No HorusETL, os pipelines de dados são chamados de **Dataflows**. Cada etapa do pipeline é um **Nó** (Processador) que realiza uma tarefa específica, como:
* Conectar a bancos de dados
* Ler e processar arquivos
* Realizar requisições HTTP
* Transformar dados
O processamento é realizado pelo **Agente**, um serviço que pode rodar no servidor do cliente para garantir performance e segurança dos dados locais.
***
## 📖 Conceitos Principais
### Dataflow (Fluxo)
Pipeline de dados desenhado visualmente. Define a sequência de execução e transformação dos dados.
### Nó / Processador
Bloco de construção do fluxo. Cada nó executa uma ação única (Input, Processamento, Output).
### Conexão
Liga dois nós, definindo o caminho dos dados. O output de um nó serve como input para o próximo.
### Agente (Engine)
Motor de execução (Serviço Windows ou Docker/Linux).
* **Função** — Recebe o fluxo do Backend e o executa
* **Local** — Pode rodar na nuvem ou On-Premise (na rede do cliente)
### Token
Chave de autenticação que vincula um Agente (instalação) ao Tenant (conta).
***
## 🏗️ Arquitetura
O sistema é composto por três módulos que se comunicam:
```mermaid
graph TD
User((Usuário)) -->|Desenha Dataflow| Frontend[Frontend - Interface Web]
Frontend -->|Salva Configuração| Backend[Backend - API/Orquestração]
Backend -->|Envia Ordem de Serviço| Agent[Engine - Agente Windows]
Agent -->|Executa Processamento| Sources[(Fontes de Dados)]
Agent -->|Envia Logs/Status| Backend
```
### 🎨 Frontend (Design)
* **Interface Web** — Onde o usuário desenha o fluxo e configura parâmetros
* **Responsabilidade** — Configuração e monitoramento
### 🧠 Backend (Orquestração)
* **API/Cloud** — Gerencia permissões, armazenamento e agendamentos
* **Responsabilidade** — Gestão e controle
### ⚙️ Engine (Execução)
* **Serviço** — O executor dos processos
* **Responsabilidade** — Processamento de dados e conexões
---
---
url: 'https://docs.horusbi.com.br/etl/processors/transforms/join.md'
---
# Join (Junção)
O nó **Join** combina dados de dois fluxos de entrada em um único fluxo de saída, baseando-se em uma ou mais chaves de correspondência. Funciona de forma similar ao `JOIN` do SQL ou `VLOOKUP` do Excel.
## Funcionalidades
* **Hash Join em Memória**: O Horus indexa o input secundário (lado direito) em memória hash para execução extremamente rápida
* **Chaves Compostas**: Suporta união por até 4 colunas chave simultaneamente
* **Tipos de Join**: `INNER` (apenas correspondências) e `LEFT` (mantém todos do lado esquerdo, preenchendo com nulo se não houver correspondência)
## Parâmetros de Configuração
### Colunas a Trazer
* **Descrição**: Lista das colunas do input secundário que você deseja adicionar ao fluxo principal
* **Seleção**: Você pode escolher quais campos "carregar" para o fluxo resultante
### Chaves de Ligação
* **Chaves Primário**: Coluna(s) do fluxo principal (Input 1) usada(s) para o match
* **Chaves Secundário**: Coluna(s) do fluxo secundário (Input 2) correspondente(s)
## Exemplo de Uso
1. **Input 1**: Lista de Vendas (contém `id_cliente`).
2. **Input 2**: Cadastro de Clientes (contém `id`, `nome`, `cidade`).
3. **Join**:
* Chave Primária: `id_cliente`
* Chave Secundária: `id`
* Colunas a Trazer: `nome`, `cidade`
4. **Resultado**: Cada linha de venda agora terá as colunas `nome` e `cidade` do cliente.
---
---
url: 'https://docs.horusbi.com.br/guia/jornada.md'
---
# Jornada do Usuário: Do Dado ao Dashboard
Este guia apresenta o pipeline completo — desde a configuração inicial até a visualização por outros usuários — incluindo as etapas de publicação e controle de acesso.
## 🗺️ Visão Geral do Pipeline
```mermaid
graph LR
A["HEC (Setup)"] --> B["ETL ou DW Upload"]
B --> C["DW (Minha Mesa)"]
C --> D["Publicar Tabela/Flow"]
D --> E["Mesa Oficial (DW)"]
E --> F["DataViz (Minha Mesa)"]
F --> G["Publicar App"]
G --> H["Mesa de Aplicação"]
H --> I["Dar Acesso (HEC)"]
```
***
## ⚙️ Etapa 1: Configurar Ambiente (HEC)
Antes de trabalhar com dados, configure a estrutura básica no HEC:
1. **Criar tenant** (se ainda não existir) e **criar usuários** que terão acesso à plataforma.
2. **Criar Mesas de Dados** — onde tabelas e Dataflows serão publicados.
3. **Criar Mesas de Aplicações** — onde Aplicações do DataViz serão publicadas.
> \[!TIP]
> Crie as Mesas antes de começar a carregar dados. Assim, no momento de publicar, o destino já estará pronto.
**Links**:
* [Visão Geral do HEC](/hec/intro/overview)
* [Criar e gerenciar Mesas](/hec/desks/mesas)
* [Gerenciar Usuários](/hec/users-groups/users)
***
## 📥 Etapa 2: Carregar Dados
Você tem duas opções para levar dados ao HorusDW:
### Opção A: Upload de Excel (sem Agente)
A forma mais simples. Faça upload direto de uma planilha Excel no HorusDW.
* [Tutorial de Upload](/dw/getting-started/upload)
### Opção B: Pipeline ETL (com Agente)
Para fontes externas como bancos de dados, Google Sheets ou APIs. Requer um **Agente ETL instalado**.
1. Instale e configure o Agente ETL. [Gerenciamento de Agentes](/etl/guides/agentes)
2. Crie um Dataflow com nós de input e output. [Primeiros Passos do ETL](/etl/getting-started/)
> \[!IMPORTANT]
> O Agente ETL precisa estar online e acessível antes de criar Dataflows. Verifique o status na tela de Agentes.
***
## 📤 Etapa 3: Publicar Dados para Produção
Após carregar os dados, eles estarão em **Minha Mesa** (ambiente privado). Para que outros usuários acessem, é preciso publicar.
### Tabelas de Upload Excel
Publique manualmente pelo HorusDW: abra a tabela em Minha Mesa, clique em **Publicar** e escolha a Mesa de Dados de destino.
* [Tabelas Físicas — Publicação](/dw/tables/physical)
### Tabelas de Dataflow (ETL)
Publique o **Dataflow** no HorusETL. A tabela de saída é publicada automaticamente junto com o Dataflow.
* [Mesas e Publicação no ETL](/etl/guides/mesas-publicacao)
> \[!IMPORTANT]
> Publicar o Dataflow no ETL publica a tabela junto. Você **não** publica a tabela separadamente pelo DW.
***
## 📊 Etapa 4: Criar Dashboards no DataViz
Com os dados publicados em uma Mesa Oficial do DW, você pode criar visualizações:
1. Crie uma nova **Aplicação** em Minha Mesa do DataViz.
2. Vincule as tabelas publicadas na aba **Tabelas**.
3. Crie **Dashboards** arrastando Widgets e configurando dimensões e métricas.
* [Criando sua Primeira Aplicação](/dataviz/01-getting-started/create-app)
***
## 🔑 Etapa 5: Publicar Aplicação e Dar Acesso
### Publicar a Aplicação
Publique a Aplicação de Minha Mesa para uma **Mesa de Aplicações**. Isso é feito pelo HEC, em **Recursos > Conteúdo > Publicar**.
Publicar exige a permissão **Publicar** — que é **concedível** e **não** é exclusiva de administrador. Quem a tem publica **nas Mesas às quais tem acesso**.
* [Gerenciar Conteúdo (Publicar)](/hec/resources/01-content)
* [Controle de Acesso às Mesas](/dw/desks/controle-de-acesso)
### Dar Acesso aos Usuários
No HEC, vá em **Usuários** ou **Grupos > Permissões > Acesso aos Dados** e marque as Mesas que cada usuário/grupo deve acessar.
* [Configurar Permissões](/hec/users-groups/permissions)
> \[!WARNING]
> Criar uma Mesa não dá acesso automático. Cada usuário ou grupo precisa ter acesso explícito à Mesa nas Permissões.
### (Opcional) Governança com Datamarts
Para controle granular de quais tabelas cada grupo pode ver, configure Datamarts no DW.
* [Datamarts](/dw/datamarts/)
***
## 📋 Resumo Visual
| Etapa | Ação | Módulo |
|-------|------|--------|
| 1 | Criar tenant, usuários e Mesas | HEC |
| 2 | Carregar dados (Excel ou ETL) | DW / ETL |
| 3 | Publicar dados para Mesa Oficial | DW / ETL |
| 4 | Criar Dashboards | DataViz |
| 5 | Publicar Aplicação e dar acesso | HEC |
---
---
url: 'https://docs.horusbi.com.br/dataviz/03-widgets/kpi.md'
---
# KPI (Indicador-Chave de Desempenho)
O Widget **KPI** (Key Performance Indicator) é utilizado para destacar números únicos e importantes, como faturamento total, número de vendas ou tickets abertos. Ele suporta até dois valores (primário e secundário), comparação histórica e indicadores de variação.
## 🏗️ Estrutura do Widget
O KPI é composto por:
1. **Valor Primário**: O número principal em destaque
2. **Título Primário**: Rótulo do valor principal
3. **Valor/Título Secundário** (Opcional): Um segundo dado de apoio (meta ou média)
4. **Variação** (Opcional): Indicador percentual ou absoluto de mudança em relação a um período anterior
5. **Gráfico de Evolução** (Opcional): Uma *sparkline* (minigráfico de área) ao fundo mostrando a tendência histórica
***
## ⚙️ Configuração
### 1. Aba Dados
Configure as fontes de dados para o valor principal e secundário.
#### Dados Primários
* **Coluna de dados**: Selecione a métrica (número) que será agregada
* **Título**: O texto que aparece acima ou abaixo do número. Suporta **Expressão** para textos dinâmicos
* **Filtros**: Define o contexto de dados específico para este número (filtrar apenas por "Status = Fechado")
#### Dados Secundários
* **Coluna de dados**: Selecione a segunda métrica
* **Título**: Rótulo para o dado secundário. Suporta **Expressão**
* **Filtros**: Contexto específico para o dado secundário
> \[!TIP]
> Se os filtros forem idênticos aos dados primários, o sistema fará apenas uma consulta ao banco de dados para otimizar a performance
***
### 2. Aba Visual
Personalize a aparência, cores e formatação.
#### 🎨 Aparência Geral
* **Estilo**:
* **Padrão**: Fundo sólido (configurável)
* **Transparente**: Sem fundo, ideal para sobrepor em imagens ou mapas
* **Destacado**: Estilo visual com sombras ou bordas enfáticas
* **Customizado**: Destrava a galeria de **Aparência do Card** (borda, sombra, destaque lateral e preenchimento) — ver seção abaixo
* **Cor do Fundo**: (Disponível no estilo Padrão). Suporta **Expressão** para mudar a cor condicionalmente (vermelho se a meta não for batida)
#### Dados Primários / Secundários
Para cada conjunto de dados, é possível configurar independentemente:
* **Tamanho do Valor/Título**: Pequeno (12px), Médio (24/16px), Grande (36/24px)
* **Cor do Número**: Cor estática ou **Expressão** (verde se positivo, vermelho se negativo)
* **Cor do Título**: Cor estática ou **Expressão**
* **Formato do Número**:
* **Padrão do Campo**: Usa o formato definido na modelagem do DW
* **Número**: Decimal padrão (2 casas)
* **Moeda**: Formato monetário (R$)
* **Porcentagem**: Multiplica por 100 e adiciona %
* **Reduzido**: Abrevia números grandes (1K, 1M, 1B, 1T)
* **Inteiro**: Sem casas decimais
* **Alinhamento**: Esquerda, Centro ou Direita
***
### 3. Aba Clique (Interação)
Define o que acontece ao clicar no Widget.
* **Link no Indicador**: Se ativado, transforma o Widget em um botão clicável
### 🎨 Configuração Visual e Layout
#### Aparência Geral
* **Estilo**: `Padrão` (Card com sombra), `Transparente` (Clean), `Destacado` (Borda forte)
* **Alinhamento**: Alinhe o conteúdo à esquerda, centro ou direita do card
* **Tamanhos**: Controle individual do tamanho da fonte para o Título e para o Número (`Pequeno`, `Médio`, `Grande`)
#### Cores Avançadas
Quase tudo no KPI pode ser colorido dinamicamente via Expressão (JavaScript):
* **Fundo do Widget**
* **Cor do Número** (Primário e Secundário)
* **Cor do Título**
* **Cor da Linha de Evolução**
> \[!TIP]
> **Use Expressões**: É possível pintar o fundo de vermelho se a meta não for batida:
>
> ```javascript
> if (this.value < 1000) return "#fee2e2"; // Vermelho claro
> return "#ffffff";
> ```
***
## 📈 Evolução Histórica (Sparkline)
O grande diferencial do KPI é mostrar não apenas a "foto" atual, mas o "filme" da evolução ao longo do tempo.
### Configuração
* **Período Padrão**: Define o intervalo histórico ("Último Ano", "Últimos 30 dias")
* **Gráfico de Linha**: Exibe uma *sparkline* (minigráfico) no fundo do card mostrando a tendência
### Variação (Delta)
Calcula automaticamente a diferença em relação ao período anterior.
* **Tipos de Comparação**:
* `Mesmo período ano anterior` (YoY)
* `Mês anterior` (MoM)
* `Valor Primário vs Secundário` (Realizado vs Meta)
* **Ícones e Cores**:
* Setas `▲` / `▼` automáticas
* **Cores Semânticas**: "Maior (Verde), Menor (Vermelho)" para receitas, ou invertido para custos/despesas
***
***
## 🖼️ Layouts e Recursos Visuais (Novidades)
> \[!NOTE]
> **Retrocompatibilidade total.** KPIs já criados continuam funcionando exatamente como antes — nenhuma configuração existente é alterada. Os recursos descritos nesta seção são **opt-in**: só são ativados ao escolher um layout diferente de **Clássico**.
### Galeria de Layouts
Na aba **Visual**, o campo **Layout** exibe uma galeria com 8 predefinições visuais. Cada layout define uma composição padrão (posição do ícone, destaque do valor, presença de barra de progresso etc.) que pode ser ajustada livremente após a seleção.
| Layout | Característica principal |
|---|---|
| **Clássico** | Comportamento atual do KPI — sem alterações |
| **Valor em destaque** | Número central em tipografia maior, sem elementos laterais |
| **Ícone à esquerda** | Ícone alinhado à esquerda do valor e título |
| **Ícone no topo** | Ícone centralizado acima do valor |
| **Valor + variação** | Valor e delta lado a lado em destaque |
| **Valor + tendência** | Valor acompanhado de sparkline de tendência |
| **Progresso / Meta** | Barra ou anel de progresso como elemento principal |
| **Compacto** | Layout reduzido para cards pequenos em grids densos |
> \[!TIP]
> Selecionar um layout aplica padrões sensatos automaticamente, mas **todos os campos permanecem editáveis**. Use o layout como ponto de partida, não como restrição.
***
### 🏷️ Ícone
Disponível quando o layout escolhido inclui posição de ícone (ou ao ativar manualmente):
* **Ícone**: Código Iconify no formato `prefixo:nome` — ex: `mdi:currency-usd`, `carbon:chart-line`, `heroicons:arrow-trending-up`. Use o seletor visual (**IconPicker**) para navegar pela biblioteca
* **Posição**: `Esquerda` (ao lado do valor) ou `Topo` (acima do valor)
* **Cor do Ícone**: Cor estática ou **Expressão** para cor condicional (ver Cheat Sheet)
* **Tamanho**: Tamanho em `px` (ex: `32`)
* **Fundo do ícone** (opcional): exibe uma **pílula** ou **círculo** atrás do ícone. Escolha a forma, a cor de fundo (vazia = tom suave derivado da cor do ícone) e a opacidade
***
### ✍️ Tipografia
Controle de estilo tipográfico **por elemento e por contexto**. O KPI usa sempre a fonte do tema do dashboard — não há seleção de família de fonte.
* **Dados Primários** — para o **Número** e o **Título** primários, ative individualmente **Negrito** e **Itálico**
* **Dados Secundários** — o **Número** e o **Título** secundários têm controle **independente** do primário (ex.: primário em negrito, secundário normal)
* **Textos adicionais** — quando ativados, **Overline**, **Subtítulo** e **Helper** oferecem **Negrito**, **Itálico**, **Cor** (estática ou Expressão) e **Tamanho** (px) próprios
***
### 📝 Textos Adicionais
Campos de texto extras para enriquecer o contexto do indicador, todos com suporte a **Expressão**:
* **Overline**: Texto pequeno exibido *acima* do valor — útil para categoria ou período (ex: `"Jan/2025"`)
* **Subtítulo**: Texto exibido *abaixo* do valor — complemento ou contexto adicional
* **Helper**: Legenda discreta no rodapé do card — ideal para notas metodológicas curtas
* **Prefixo**: Texto inline *antes* do número — ex: `"R$ "`, `"US$ "`
* **Sufixo**: Texto inline *depois* do número — ex: `" un"`, `" h"`, `"%"`
> \[!TIP]
> Prefixo e sufixo são renderizados junto ao número, sem espaço extra. Se precisar de espaço, inclua-o na string: `"R$ "` (com espaço no final).
***
### 📉 Variação (Delta)
A variação continua sendo alimentada pela configuração de **Evolução Histórica** (período de comparação). Os novos controles são de apresentação:
* **Posição**: `Abaixo do valor` (padrão) ou `À direita do valor`
* **Modo de exibição**:
* `Percentual`: Diferença relativa ao período anterior (ex: `+12,3%`)
* `Diferença absoluta`: Valor numérico da diferença (ex: `+1.450`)
* `Valor`: Exibe o valor do período comparado, sem cálculo de diferença
* `% da meta`: Razão entre o **Valor Primário** e o valor da **Meta** configurada (campo Meta, fixo ou por expressão) — ex: `87% da meta`
* **Selo (badge)**: quando ativado, a variação aparece dentro de uma **pílula tingida** com a cor semântica, em vez de texto simples
***
### 🎯 Meta (Progresso)
Indicador visual de atingimento de meta, comparando o **Valor Primário** com o valor da **Meta** configurada:
* **Origem da Meta**:
* `Valor fixo`: um número fixo ou por **Expressão**
* `Dado secundário`: reaproveita o valor secundário já carregado (sem nova consulta)
* `Coluna`: agrega uma métrica do modelo como denominador da meta
* **Tipo de indicador**:
* `Barra`: Barra horizontal de progresso abaixo do valor
* `Anel`: Anel circular de progresso (semelhante a um mini-gauge)
* A barra/anel usa as mesmas cores semânticas configuradas na seção de Variação
> \[!NOTE]
> O progresso compara o **Valor Primário** com o valor do campo **Meta** (configurável como valor fixo ou por expressão na própria seção Meta). O denominador é esse valor de Meta, **não** o Dado Secundário.
***
### ℹ️ Descrição (ⓘ)
Permite ao construtor do dashboard adicionar uma explicação rica sobre o indicador, visível ao consumidor final:
* **Conteúdo**: Editor rich-text — suporta negrito, itálico, listas e links
* **Acesso**: O consumidor clica no botão **ⓘ** ao lado do título do KPI para abrir um modal com a descrição
* **Uso recomendado**: Explicar o que o indicador mede, como é calculado, quais exclusões foram aplicadas ou como interpretar o valor
> \[!TIP]
> Use a Descrição para reduzir dúvidas recorrentes em reuniões: explique a fórmula, o período de referência e as principais regras de negócio do indicador.
***
### 📊 Sparkline como Fundo
Além da sparkline tradicional (minigráfico abaixo do valor), é possível renderizar a **tendência histórica esmaecida atrás do número** — o valor fica em primeiro plano e o histórico vira um fundo discreto. Requer a **Evolução Histórica** configurada.
***
### 🃏 Aparência do Card
Quando o **Estilo** (aba Visual → Aparência Geral) é definido como **Customizado**, o KPI desenha a própria moldura, com uma galeria de estilos prontos e controles finos.
#### Galeria de Estilos de Card
Um seletor com **pré-visualização** (igual ao de Layouts) oferece estilos prontos:
| Estilo | Característica |
|---|---|
| **Plano** | Sem borda nem sombra |
| **Contornado** | Borda fina ao redor |
| **Elevado** | Sombra (efeito flutuante) |
| **Destaque à esquerda** | Barra de cor na lateral esquerda |
| **Destaque no topo** | Barra de cor no topo |
| **Sólido** | Fundo preenchido com cor sólida |
| **Suave** | Fundo com um leve banho da cor (*wash*) |
| **Gradiente** | Fundo com gradiente entre duas cores |
| **Bloco à esquerda** | Barra lateral larga colorida |
> \[!TIP]
> Assim como nos Layouts, escolher um estilo é um **ponto de partida** — todos os controles finos abaixo continuam editáveis.
#### Controles Finos
* **Borda**: Espessura (Nenhuma, Fina, Média, Grossa) e cor
* **Cantos (raio)**: Pequeno, Médio ou Grande
* **Sombra**: Nenhuma, Suave ou Média
* **Espaçamento interno** (padding)
* **Borda de destaque (accent)**: Uma faixa colorida em **uma única lateral** (Esquerda, Topo ou Base), com cor e espessura próprias — ótima para dar identidade sem fundo pesado
* **Preenchimento**: `Sólido`, `Suave` (banho leve da cor) ou `Gradiente` (entre duas cores)
> \[!NOTE]
> Em fundos escuros (Sólido/Gradiente), defina a **Cor do Número** e a **Cor do Título** em um tom claro (ex.: branco) para manter o contraste. Ao escolher um estilo de fundo escuro na galeria, o editor já ajusta esse contraste automaticamente.
***
## 💻 Cheat Sheet: Expressões Comuns
O KPI suporta expressões "Low-Code" (JavaScript) para cores e textos dinâmicos. O objeto `this` contém o contexto do Widget.
**Variáveis Disponíveis:**
* `this.primaryValue`: Valor numérico principal
* `this.secondaryValue`: Valor numérico secundário
* `this.variation`: Valor da variação calculada (0.15 para 15%)
### 1. Cor Condicional (Semáforo)
Use no campo **Cor do Número** ou **Cor do Fundo**.
```javascript
// Verde se positivo, Vermelho se negativo
if (this.primaryValue >= 0) return '#22c55e';
return '#dc2626';
```
### 2. Atingimento de Meta (Cor)
Use quando tiver Valor (Vendas) e Secundário (Meta).
```javascript
// Calcula atingimento
const atingimento = this.primaryValue / this.secondaryValue;
if (atingimento >= 1) return '#22c55e'; // Meta batida (Verde)
if (atingimento >= 0.8) return '#f59e0b'; // Quase lá (Laranja)
return '#dc2626'; // Ruim (Vermelho)
```
### 3. Título Dinâmico
Use no campo **Título**.
```javascript
// Exibe a variação no título
const diff = this.primaryValue - this.secondaryValue;
return `Vendas (Diferença: ${diff})`;
```
### 4. Cor do Ícone por Expressão
Use no campo **Cor do Ícone** (layout com ícone ativo). As expressões do KPI expõem apenas `this.primaryValue` e `this.secondaryValue` — para comparar com uma meta numa expressão, use um limite fixo:
```javascript
// Ícone verde se meta atingida, vermelho se não
const meta = 1000;
if (this.primaryValue >= meta) return '#22c55e';
return '#dc2626';
```
### 5. Modo de Exibição do Delta: % da Meta
Configure o **Modo de exibição** como `% da meta` e preencha o campo **Meta**.
O sistema calcula `(Valor Primário / Meta) * 100` automaticamente — não é necessária expressão adicional.
---
---
url: 'https://docs.horusbi.com.br/dataviz/03-widgets/listbox.md'
---
# Lista de Seleção (ListBox)
O Widget **Lista de Seleção** é um facilitador visual para a interatividade. Embora o Dashboard já possua sua área de filtros nativa, este Widget serve como um menu de acesso rápido dentro da própria tela de visualização.
## 🔧 Funcionalidades Principais
* **Multiseleção**: Permite selecionar um ou múltiplos itens (controlado por configuração)
* **Busca Rápida**: Campo de pesquisa integrado para encontrar itens em listas grandes
* **Visualização de Dados Inline**: Pode exibir uma barra de gráfico ao fundo de cada item ("gráfico inline") representando um valor numérico (barra maior para vendedores com mais vendas)
* **Layout Flexível**: Suporta layouts verticais, horizontais ou em grade (grid)
***
## ⚙️ Configuração
### 1. Aba Dados
* **Campo de Dimensão**: A coluna de texto ou categoria que será listada (Nome do Cliente)
* **Ordenação**: Alfabética (A-Z ou Z-A) ou Numérica (por valor da métrica)
* **Limite de resultados**: Opcionalmente, limita a lista aos top N itens ("Top 50 Clientes")
* **Filtro**: Filtros pré-aplicados nativos desta lista
> \[!NOTE]
> Para ordenar numericamente **(Vendedores por Vendas)**, é necessário ativar o "Gráfico Inline" e configurar a métrica de valor
***
### 📊 Gráfico Inline
Transforma a lista em um gráfico de barras horizontal interativo.
* **Mostrar gráfico inline**: Ativa a barra de fundo em cada item
* **Campo de valor**: Definie a métrica que determina o comprimento da barra (Total Vendas)
* **Cor do preenchimento**: Define a cor da barra (Fixa ou via Expressão)
* **Opacidade**: Ajusta a transparência da barra para garantir a legibilidade do texto
***
### 🎨 Estilo e Cores
Extensas opções para personalizar a aparência dos itens:
* **Formatação de Texto**: Tamanho, alinhamento (Esquerda/Centro/Direita), peso (Negrito) e estilo (Itálico)
* **Bordas e Arredondamento**: Controle fino sobre o contorno de cada "botão" da lista
* **Esquema de Cores**: Defina cores distintas para os estados:
* **Normal**: Item não selecionado
* **Selecionado**: Item ativo (indica o filtro aplicado)
* **Hover**: Ao passar o mouse
> \[!TIP]
> Use **Coloração baseada em expressão** para formatar itens específicos condicionalmente (pintar de vermelho um departamento específico)
***
### 🔧 Aba Avançado
* **Formatação condicional**: Permite injetar CSS customizado via expressão JavaScript para itens específicos
* **Botões Selecionar Todos/Limpar**: Exibe atalhos úteis no topo da lista (apenas para seleção múltipla)
* **Mostrar itens sem valores**: Se o gráfico inline estiver ativo, decide se mostra ou esconde itens que tenham valor zero na métrica
---
---
url: 'https://docs.horusbi.com.br/etl/guides/logs-monitoramento.md'
---
# Logs e Monitoramento
A transparência é crucial em processos de ETL. O Horus fornece logs detalhados para cada execução, permitindo rastrear exatamente o que aconteceu, linha a linha.
***
## 📋 Acessando os Logs
Existem duas formas principais de acessar os logs:
1. **Pelos Agendamentos** — Na tela "Agendamentos", cada item mostra o status da última execução. Clicando no status (data/hora), abre-se um resumo. Deste resumo, clique em **Ver Logs Completos**
2. **Pelo Menu Lateral** — Acesse **Logs > Flows** para ver uma lista cronológica de tudo o que rodou no ambiente
***
## 📊 Entendendo a Tabela de Logs
Ao abrir os logs de uma execução específica (Schedule ID), você verá uma tabela onde cada linha representa uma etapa ou sub-processo.
### Colunas Importantes
| Coluna | Descrição |
|--------|-----------|
| **Nome Flow** | Qual fluxo foi executado |
| **Contexto** | O diferencial da execução |
| **Duração** | Tempo de processamento — fique atento a tempos excessivamente longos que podem indicar gargalos de banco ou rede |
| **Mensagem** | Detalhes técnicos. Em caso de erro, aqui aparecerá o stack trace ou a mensagem de rejeição do banco |
***
## 🎨 Status de Execução
| Status | Descrição |
|--------|-----------|
| 🟢 **Sucesso** | Tudo ocorreu conforme o esperado |
| 🟡 **Aviso / Erro Parcial** | O fluxo terminou, mas alguns itens podem ter sido rejeitados (erro de validação em linhas específicas), ou o usuário cancelou o processo manualmente |
| 🔴 **Erro** | Falha crítica — o processo parou abruptamente (senha de banco incorreta, arquivo não encontrado, erro de memória) |
---
---
url: 'https://docs.horusbi.com.br/lumo.md'
---
# Lumo CLI
O **Lumo CLI** é a interface de linha de comando oficial da plataforma HorusBI. Ele transforma qualquer diretório local em um **workspace declarativo** de um tenant — uma cópia espelho do tenant em arquivos YAML que você lê, edita e sincroniza com o servidor.
> \[!NOTE]
> O Lumo é voltado para administradores e desenvolvedores que gerenciam recursos de um tenant HorusBI (flows, tabelas DW, aplicações BI, agendamentos, credenciais, agentes e mesas) de forma programática, versionada e auditável.
***
## 🤖 Feito para agentes de IA
O Lumo nasceu como o **modo headless do HorusBI** — uma forma de **agentes de IA** (Claude Code, Codex e afins) operarem a plataforma por conta própria: criar flows, modelar o DW, montar dashboards e exportar resultados, tudo via comandos e YAML.
Você descreve a intenção em linguagem natural e o agente traduz para os comandos certos do `lumo`. Para isso, publicamos uma **skill** pronta a cada release.
➡️ **[Integrações com IA](/lumo/integracoes/)** — instale a skill no Claude Code, Codex ou outro agente.
➡️ **[Trabalhando com a IA](/lumo/trabalhando-com-ia/)** — o que é seu, o que é da IA, e como pedir as coisas.
***
## ⚙️ Como funciona
Você trabalha com **arquivos YAML** que representam os recursos do seu tenant. O ciclo é direto: edite os arquivos localmente, envie ao servidor com `push`, e traga o que está no servidor com `pull`.
```bash
lumo status # ver o que mudou localmente
# editar YAML em flows/, tables/, apps/, ...
lumo push # aplicar as mudanças no servidor
```
Depois do `push`, seus arquivos são atualizados para refletir exatamente o que ficou salvo no servidor.
***
## 🗂️ Recursos Gerenciados
O Lumo gerencia os seguintes tipos de recursos de um tenant HorusBI:
| Recurso | Pasta | Descrição |
|---|---|---|
| `flow` | `flows/` | Pipelines ETL |
| `table` | `tables/` | Tabelas do Data Warehouse |
| `app` | `apps/` | Aplicações BI e dashboards |
| `credential` | `credentials/` | Conexões e credenciais |
| `schedule` | `schedules/` | Agendamentos |
| `agent` | `agents/` | Agentes de execução |
| `dw-desk` | `dw-desks/` | Mesas de dados (flows + tabelas) |
| `bi-desk` | `bi-desks/` | Mesas de aplicação (apps) |
| `variables` | `variables/` | Variáveis do tenant |
> \[!NOTE]
> **O que o Lumo CLI não cobre:** gestão de usuários e grupos, criação e provisionamento de tenants e demais configurações administrativas do tenant continuam sendo feitas pela **interface HEC** ou pela **API**. O Lumo CLI foca nos recursos de dados e BI listados acima.
***
## 🧭 Por que um CLI (e não um MCP)?
Quando desenhamos o modo headless, a escolha natural seria expor um **servidor MCP**. Optamos por um **CLI + YAML** de propósito — e por bons motivos:
* **Acurácia.** LLMs já viram milhões de exemplos de uso de CLIs e de arquivos YAML no treino. É um terreno conhecido: o agente erra menos seguindo `lumo push` e editando YAML do que aprendendo um conjunto de ferramentas custom do zero.
* **Economia de tokens.** Um MCP tende a injetar o schema de **todas** as ferramentas no contexto a cada sessão. Com o CLI, o agente lê só o `--help` do comando que precisa, quando precisa — e a saída é texto compacto. Menos contexto gasto, mais espaço para o raciocínio.
* **Componibilidade.** O agente combina o `lumo` com o resto do shell — `grep`, `git`, pipes, scripts. Todo o ecossistema de linha de comando vira ferramenta, sem integração extra.
* **Versionável e auditável.** Os YAMLs vivem em Git, exatamente como um humano faria. Diffs revisáveis, histórico, rollback — o trabalho do agente fica rastreável.
* **Sem processo extra.** Não há servidor MCP para subir e manter vivo: o `lumo` é invocado sob demanda e termina. Mais simples de operar em CI e em máquinas efêmeras.
Em resumo: ao falar a língua que os modelos já dominam (CLI e YAML), ganhamos precisão e economia sem abrir mão de auditabilidade.
***
## 📚 Estrutura da Documentação
* 🧩 **[Conceitos](/lumo/conceitos/)** — O que é flow, tabela DW, mesa, tipo de carga e como tudo se conecta
* 🚀 **[Primeiros Passos](/lumo/getting-started/)** — Instalar, autenticar e deixar pronto para a IA operar
* 🤖 **[Integrações com IA](/lumo/integracoes/)** — Instale a skill no Claude Code, Codex e outros agentes
* 💬 **[Trabalhando com a IA](/lumo/trabalhando-com-ia/)** — O que é seu vs. da IA e dicas de prompt
* 🔍 **[Revisando o trabalho da IA](/lumo/revisar/)** — Como conferir o que a IA produziu
* 📖 **[Referência de Comandos](/lumo/comandos/)** — Todos os comandos, flags e grupos
***
## 🚀 Começar Rápido
```bash
# 1. Instalar (macOS/Linux)
curl -fsSL https://storage.horusbi.com.br/download/lumo/install.sh | bash
# 2. Autenticar
lumo auth login
# 3. Inicializar workspace
lumo init
# 4. Ver status e editar
lumo status
# edite os YAMLs em flows/, tables/, apps/, ...
# 5. Enviar mudanças
lumo push
```
Veja o guia completo em [Primeiros Passos](/lumo/getting-started/).
---
---
url: 'https://docs.horusbi.com.br/lumo/integracoes/claude-code.md'
---
# Lumo CLI no Claude Code
Este guia instala as skills do Lumo no [Claude Code](https://claude.com/claude-code) para que ele saiba operar a CLI por você.
> \[!NOTE]
> Pré-requisitos: o binário `lumo` já instalado e autenticado (`lumo auth login`). A skill ensina o agente a **usar** o `lumo` — ela não instala a CLI. Veja [Primeiros Passos](/lumo/getting-started/).
## Opção A: Instalador automático (recomendado)
1. Baixe **[`lumo-cli-skill.zip`](https://storage.horusbi.com.br/download/lumo/lumo-cli-skill.zip)** e extraia.
2. Rode o instalador da sua plataforma:
* **Windows** — duplo-clique em `instalar.bat`
* **macOS / Linux** — `./instalar.sh`
3. O instalador detecta o Claude Code e copia as skills para `~/.claude/skills/`.
Para forçar a instalação no Claude Code mesmo sem detecção automática (macOS/Linux):
```bash
./instalar.sh --skill-target claude
```
Ao final, você terá:
```
~/.claude/skills/lumo-cli/SKILL.md
~/.claude/skills/lumo-design/SKILL.md
```
## Opção B: Instalação manual
Se preferir copiar à mão, extraia o `.zip` e copie as duas pastas de skill para `~/.claude/skills/`:
::: code-group
```bash [macOS / Linux]
mkdir -p ~/.claude/skills
cp -r skills/lumo-cli ~/.claude/skills/
cp -r skills/lumo-design ~/.claude/skills/
```
```powershell [Windows (PowerShell)]
New-Item -ItemType Directory -Force "$env:USERPROFILE\.claude\skills" | Out-Null
Copy-Item -Recurse -Force .\skills\lumo-cli "$env:USERPROFILE\.claude\skills\"
Copy-Item -Recurse -Force .\skills\lumo-design "$env:USERPROFILE\.claude\skills\"
```
:::
## Verificando
Abra o Claude Code no diretório do seu workspace Lumo e peça algo simples:
> *Verifique o status da minha sessão do Lumo e liste meus flows.*
O Claude deve reconhecer a skill `lumo` e rodar `lumo auth status` / `lumo list flow`. Se ele não souber o que é o `lumo`, confirme que os arquivos estão em `~/.claude/skills/lumo-cli/SKILL.md` e reinicie a sessão.
## Atualizando
A cada nova versão do CLI, baixe o `.zip` novamente e rode o instalador — ele **substitui** as skills existentes pela versão nova. Vale repetir a cada `lumo update` relevante para manter a skill alinhada com os comandos da CLI.
---
---
url: 'https://docs.horusbi.com.br/lumo/integracoes/codex.md'
---
# Lumo CLI no Codex
Este guia instala as skills do Lumo no Codex para que ele saiba operar a CLI por você.
> \[!NOTE]
> Pré-requisitos: o binário `lumo` já instalado e autenticado (`lumo auth login`). A skill ensina o agente a **usar** o `lumo` — ela não instala a CLI. Veja [Primeiros Passos](/lumo/getting-started/).
## Opção A: Instalador automático (recomendado)
1. Baixe **[`lumo-cli-skill.zip`](https://storage.horusbi.com.br/download/lumo/lumo-cli-skill.zip)** e extraia.
2. Rode o instalador da sua plataforma:
* **Windows** — duplo-clique em `instalar.bat`
* **macOS / Linux** — `./instalar.sh`
3. O instalador detecta o Codex e copia as skills para `~/.agents/skills/`.
Para forçar a instalação no Codex mesmo sem detecção automática (macOS/Linux):
```bash
./instalar.sh --skill-target codex
```
Ao final, você terá:
```
~/.agents/skills/lumo-cli/SKILL.md
~/.agents/skills/lumo-design/SKILL.md
```
> \[!TIP]
> Para instalar nos dois agentes de uma vez (Claude Code **e** Codex), use `./instalar.sh --skill-target all`.
## Opção B: Instalação manual
Extraia o `.zip` e copie as duas pastas de skill para a pasta de skills do Codex (`~/.agents/skills/`):
::: code-group
```bash [macOS / Linux]
mkdir -p ~/.agents/skills
cp -r skills/lumo-cli ~/.agents/skills/
cp -r skills/lumo-design ~/.agents/skills/
```
```powershell [Windows (PowerShell)]
New-Item -ItemType Directory -Force "$env:USERPROFILE\.agents\skills" | Out-Null
Copy-Item -Recurse -Force .\skills\lumo-cli "$env:USERPROFILE\.agents\skills\"
Copy-Item -Recurse -Force .\skills\lumo-design "$env:USERPROFILE\.agents\skills\"
```
:::
## Verificando
Abra o Codex no diretório do seu workspace Lumo e peça algo simples:
> *Verifique o status da minha sessão do Lumo e liste meus flows.*
O agente deve rodar `lumo auth status` / `lumo list flow`. Se ele não reconhecer o `lumo`, confirme que os arquivos estão em `~/.agents/skills/lumo-cli/SKILL.md` e reinicie a sessão.
## Atualizando
A cada nova versão do CLI, baixe o `.zip` novamente e rode o instalador — ele **substitui** as skills existentes pela versão nova.
---
---
url: 'https://docs.horusbi.com.br/lumo/integracoes/cursor.md'
---
# Lumo CLI no Cursor
Este guia instala as skills do Lumo no Cursor para que ele saiba operar a CLI por você.
> \[!NOTE]
> Pré-requisitos: o binário `lumo` já instalado e autenticado (`lumo auth login`). A skill ensina o agente a **usar** o `lumo`, sem instalar a CLI. Veja [Primeiros Passos](/lumo/getting-started/).
## Você talvez já tenha
O Cursor lê skills de várias pastas, incluindo as do Claude Code e do Codex:
| Pasta | Escopo |
|---|---|
| `~/.cursor/skills/` | global, nativa do Cursor |
| `~/.agents/skills/` | global, compartilhada com o Codex |
| `~/.claude/skills/` | global, do Claude Code |
| `~/.codex/skills/` | global, do Codex |
| `.cursor/skills/` | do projeto |
| `.agents/skills/` | do projeto |
| `.claude/skills/` | do projeto |
| `.codex/skills/` | do projeto |
Se você já instalou as skills do Lumo para o **Claude Code** ou para o **Codex** nesta máquina, o Cursor já enxerga todas elas. Não há nada a fazer: abra o Cursor e peça algo (veja [Verificando](#verificando)). Por isso o instalador só cria `~/.cursor/skills/` quando nenhuma dessas outras pastas recebeu skill, evitando que o Cursor carregue a mesma skill duas vezes.
## Opção A: Instalador automático (recomendado)
1. Baixe **[`lumo-cli-skill.zip`](https://storage.horusbi.com.br/download/lumo/lumo-cli-skill.zip)** e extraia.
2. Rode o instalador da sua plataforma:
* **Windows** — duplo-clique em `instalar.bat`
* **macOS / Linux** — `./instalar.sh`
3. O instalador detecta o Cursor e copia as skills para `~/.cursor/skills/`.
Para forçar a instalação no Cursor mesmo que o Claude Code ou o Codex já estejam na máquina (macOS/Linux):
```bash
./instalar.sh --skill-target cursor
```
Ao final, você terá:
```
~/.cursor/skills/lumo-cli/SKILL.md
~/.cursor/skills/lumo-design/SKILL.md
~/.cursor/skills/lumo-decks/SKILL.md
~/.cursor/skills/lumo-scope/SKILL.md
~/.cursor/skills/lumo-mockup/SKILL.md
```
> \[!TIP]
> Para instalar em todos os agentes de uma vez, use `./instalar.sh --skill-target all`.
## Opção B: Instalação manual
Extraia o `.zip` e copie as pastas de skill para a pasta de skills do Cursor:
::: code-group
```bash [macOS / Linux]
mkdir -p ~/.cursor/skills
cp -r skills/lumo-cli ~/.cursor/skills/
cp -r skills/lumo-design ~/.cursor/skills/
cp -r skills/lumo-decks ~/.cursor/skills/
cp -r skills/lumo-scope ~/.cursor/skills/
cp -r skills/lumo-mockup ~/.cursor/skills/
```
```powershell [Windows (PowerShell)]
New-Item -ItemType Directory -Force "$env:USERPROFILE\.cursor\skills" | Out-Null
Copy-Item -Recurse -Force .\skills\lumo-cli "$env:USERPROFILE\.cursor\skills\"
Copy-Item -Recurse -Force .\skills\lumo-design "$env:USERPROFILE\.cursor\skills\"
Copy-Item -Recurse -Force .\skills\lumo-decks "$env:USERPROFILE\.cursor\skills\"
Copy-Item -Recurse -Force .\skills\lumo-scope "$env:USERPROFILE\.cursor\skills\"
Copy-Item -Recurse -Force .\skills\lumo-mockup "$env:USERPROFILE\.cursor\skills\"
```
:::
## Opção C: Por projeto, versionado no repositório
Para um time inteiro pegar as skills junto com o repositório, coloque-as em `.cursor/skills/` na raiz do projeto e commite:
```bash
mkdir -p .cursor/skills
cp -r skills/lumo-cli .cursor/skills/
git add .cursor/skills && git commit -m "chore: skill do Lumo no repositório"
```
O Cursor varre essa pasta recursivamente e carrega tudo que encontrar.
## Invocando
O agente do Cursor decide sozinho quando usar cada skill, a partir da descrição dela. Para chamar uma na mão, digite `/` no chat do Agent e busque pelo nome (`lumo-cli`, `lumo-design`, `lumo-decks`, `lumo-scope`, `lumo-mockup`).
## Verificando
Abra o Cursor no diretório do seu workspace Lumo e peça algo simples:
> *Verifique o status da minha sessão do Lumo e liste meus flows.*
O agente deve rodar `lumo auth status` / `lumo list flow`. Se ele não reconhecer o `lumo`, confirme que os arquivos estão em uma das pastas da tabela acima e comece uma conversa nova.
## Atualizando
A cada nova versão do CLI, baixe o `.zip` novamente e rode o instalador, que **substitui** as skills existentes pela versão nova. O `lumo update` também atualiza as skills nas pastas dos agentes detectados.
---
---
url: 'https://docs.horusbi.com.br/etl/processors/transforms/map.md'
---
# Map (Script C#)
O processador **Map** é um nó de scripting avançado que permite escrever código C# puro para manipular, transformar, filtrar ou gerar dados. Ele oferece controle total sobre o fluxo de dados, permitindo lógicas que não são possíveis com nós padrão.
## Como Funciona
O Horus compila seu código C# em tempo de execução (usando Roslyn) e o injeta dentro de um método executável no pipeline de dados.
### Assinatura do Método
O código que você escreve é o corpo do seguinte método:
```csharp
public DataList Run(DataList input, ETLLogger logger, DataList input0, ...)
{
// SEU CÓDIGO AQUI
}
```
## API de Scripting
Para usar este nó efetivamente, você interage com as seguintes classes do motor ETL:
### 1. DataList (Entrada e Saída)
Representa o fluxo de dados (tabela).
* **Comportamento**: É um `IEnumerable`. Você pode iterar sobre ele com `foreach`
* **Schema**: Possui metadados das colunas (`input.GetSchema()`)
### 2. DataRow (Linha)
Representa uma linha de dados.
* **Acesso por Coluna**: Use o indexador de string para ler ou escrever valores:
```csharp
var valor = linha["NOME_DA_COLUNA"];
linha["OUTRA_COLUNA"] = 123;
```
* **Tipagem**: Os valores são objetos (`object`). Use a classe `Helpers` para conversão segura (`Helpers.ToDouble()`, `Helpers.ToDate()`)
### 3. Logger
Permite escrever mensagens no log de execução, visíveis na tela.
* `logger.Log("Mensagem"):` Escreve uma linha no log
***
## Exemplos de Código
### Exemplo 1: Transformação Simples (Update)
Itera sobre as linhas recebidas e altera o valor de uma coluna existente. Ideal para correções pontuais.
```csharp
// Itera sobre cada linha do input
foreach(var linha in input)
{
// Lê coluna "NUMERO", multiplica por 2 e salva na coluna "RESULTADO"
// Nota: A coluna "RESULTADO" deve existir previamente no fluxo
linha["RESULTADO"] = Helpers.ToDouble(linha["NUMERO"]) * 2;
}
// Retorna o mesmo DataList modificado
return input;
```
### Exemplo 2: Criação de Novos Dados (Novo Schema)
Cria uma estrutura de dados totalmente nova e gera linhas programaticamente. Ideal para normalização, explosão de linhas ou criação de dados sintéticos.
```csharp
// 1. Defina o schema (colunas e tipos) da saída
var schema = DataSchema.Create(@"
ID NUMBER
NOME STRING
DATA_CRIACAO DATE
");
// 2. Crie uma função geradora (yield return) para performance (Lazy Loading)
// Isso evita carregar tudo na memória de uma vez
IEnumerable GerarDados()
{
int i = 0;
foreach(var linhaOriginal in input)
{
i++;
// Log a cada 10.000 linhas para acompanhar progresso
if (i % 10000 == 0) logger.Log($"Processado {i:N0} linhas...");
// Cria uma nova linha vazia com o schema definido
var dr = new DataRow(schema);
// Preenche os valores
dr["ID"] = i;
dr["NOME"] = linhaOriginal["NOME_COMPLETO"].ToString().ToUpper();
dr["DATA_CRIACAO"] = DateTime.Now;
// Entrega a linha para o próximo passo do fluxo
yield return dr;
}
}
// 3. Retorna um novo DataList usando o gerador e o schema
return new DataList(GerarDados(), schema);
```
> \[!WARNING]
> **Use com Moderação**
> O nó Map é poderoso, mas requer conhecimento de C#.
>
> * Para transformações simples (filtros, cálculos matemáticos, renomear colunas), prefira usar o nó **SQL (DuckDB)** ou **Python**, que são mais concisos e fáceis de manter.
> * Erros no script C# podem parar todo o fluxo de dados.
---
---
url: 'https://docs.horusbi.com.br/dataviz/03-widgets/map.md'
---
# Mapa
O Widget de **Mapa** é uma ferramenta geoespacial completa que permite visualizar dados através de múltiplas camadas sobrepostas. Ele suporta desde mapas simples de vendas por estado até rotas de logística complexas ou mapas de calor baseados em coordenadas precisas.
## 🌍 Funcionalidades Principais
* **Multicamadas**: É possível adicionar várias camadas de dados diferentes no mesmo mapa (uma camada de calor para densidade populacional e uma camada de pontos para lojas)
* **Motor de Renderização**: Utiliza **Leaflet** (OpenSource), leve e rápido. Suporta camadas base de Mapa e Satélite
* **Clusterização e Calor**: Agrupa automaticamente pontos muito próximos ou gera mapas de calor para alta densidade
***
## 🗂️ Tipos de Camada
### 1. UF / Cidade
Colore estados ou cidades geográficas com base em um valor.
* **Dados**: Requer uma coluna com a sigla do estado (UF) e/ou nome da Cidade
* **Visualização**: O mapa é colorido gradualmente (Escala de Cor) baseada no valor da métrica
* **Drill-down Automático**: Se configurado com UF e Cidade, ao clicar em um estado, o mapa foca e mostra as cidades daquela região selecionada
### 2. Coordenadas (Lat/Long)
Plota pontos exatos no mapa usando latitude e longitude.
* **Dados**: Requer uma coluna com coordenadas (formato `lat, long` ou colunas separadas, dependendo da fonte)
* **Modos de Exibição**:
* **Pontos**: Marcadores individuais
* **Cluster**: Agrupa pontos próximos (evita poluição visual)
* **Mapa de Calor**: Cria manchas de calor baseadas na densidade de pontos
### 3. Rotas
Desenha caminhos sequenciais entre pontos. Ideal para logística e rastreamento.
* **Dados Necessários**:
* **Lat/Long**: Posição dos pontos
* **Sequência**: Coluna de ordem dos pontos para traçar a linha corretamente (Textual, Numérica ou Data e Hora)
* **Grupo de Rota**: Identificador da rota (Placa do Caminhão), para separar linhas diferentes
* **Marcadores**: Opcionalmente, exibe "alfinetes" em cada parada da rota
### 4. GeoJSON
Renderiza formas geométricas personalizadas a partir de uma URL ou arquivo GeoJSON.
* **Uso**: Mapas customizados (planta de um shopping, divisões de territórios de vendas não-padrão, bairros específicos)
* **Vinculação**: Permite colorir as formas baseando-se em dados da Aplicação (pelo ID da forma)
***
## 🎨 Configuração Visual
#### Aparência
* **Aparência**: `Padrão`, `Transparente`, `Destacado`
* **Camadas Base**: Escolha entre visualização `Mapa` (vetorial/ruas) ou `Satélite` (fotos reais)
* **Controles**: Opção para mostrar/ocultar botões de Zoom
#### Configuração por Camada (Layer)
Cada camada adicionada tem configurações independentes na aba Visual:
* **Limitação de Registros**: Define um teto de pontos (1000) para garantir performance em hardwares mais modestos
* **Cores**:
* **UF/Cidade**: Gradiente de cor (Início -> Fim) e sem cor para "Sem Dados"
* **Pontos/GeoJSON**: Cor única ou coloração dinâmica baseada em uma coluna de dimensão
#### Opções Avançadas de Coordenadas
Para camadas de Lat/Long, é possível escolher o modo de exibição:
* **Pontos**: Marcadores individuais
* **Cluster**: Agrupa pontos próximos em um círculo numérico que se expande ao clicar. Ideal para milhares de pontos
* **Mapa de Calor (Heatmap)**: Manchas de densidade baseadas na concentração de pontos
***
## 💡 Dicas de Otimização
> \[!TIP]
> **Performance e Otimização Automática**:
> Para garantir a fluidez do Dashboard, o Widget aplica regras automáticas baseadas no volume de dados na camada de **Coordenadas**:
>
> * **Acima de 10.000 pontos**: O modo é alterado automaticamente para **Cluster** para evitar travamentos.
> * **Acima de 100.000 pontos**: O modo é alterado automaticamente para **Mapa de Calor**.
---
---
url: 'https://docs.horusbi.com.br/dataviz/03-widgets/matrix.md'
---
# Matriz (Pivot Table)
O Widget **Matriz** é a ferramenta mais poderosa para análise tabular no Horus DataViz. Diferente de uma tabela simples, a Matriz permite agrupar dados em níveis hierárquicos (Drill-Down), transpor colunas (Pivot) e realizar análises financeiras avançadas (Vertical e Horizontal) nativamente.
É o componente ideal para Balancetes, Análises de Vendas por Categoria e qualquer cenário que exija exploração profunda de dados agrupados.
> \[!TIP]
> **Demonstrativo com linhas calculadas é outro Widget.** Na Matriz as linhas saem do dado: são os valores de uma dimensão. Por isso ela não expressa "Receita Líquida", que não existe como valor de coluna nenhuma — é Receita Bruta menos Deduções.
>
> Se o seu demonstrativo tem contas calculadas (Receita Líquida, EBITDA, margens) ou mistura fatos que não se relacionam entre si, use a [Matriz Composta](composed-matrix.md).
>
> Se for um balancete puro sobre um plano de contas que já tem Código e Código Pai, fique aqui: o modo Hierárquico resolve em **uma consulta**, e é muito mais barato.
## 🏗️ Modos de Estrutura
A Matriz opera em dois modos distintos de estruturação de linhas:
### 1. Por Níveis (Drill-down)
É o modo clássico de *Pivot Table*. Define-se uma ordem de Dimensões `(País > Estado > Cidade)`.
* **Comportamento**: O usuário clica em `País` para expandir e visualizar os `Estados`, e assim por diante
> \[!TIP]
> Ideal para análises comerciais, geográficas ou de produtos.
### 2. Hierárquico (Pai/Filho)
Muitas estruturas de dados (como planos de contas contábeis ou organogramas) são armazenadas no formato "Auto-relacionamento" (ID e ID Pai). A Matriz entende essa estrutura nativamente.
* **Configuração**:
* **Campo Código**: O ID único da linha `(ContaContabilID)`
* **Campo Código Pai**: O ID que aponta para o nível superior `(ContaPaiID)`
* **Campo Rótulo**: O nome a ser exibido `(Nome da Conta)`
> \[!TIP]
> Ideal para Balancetes, Balanços e Organogramas — estruturas em que a árvore inteira já existe no dado. Quando o demonstrativo precisa de contas que o plano não tem, veja a [Matriz Composta](composed-matrix.md).
***
## 🔀 Configuração de Colunas (Pivot)
Além das linhas hierárquicas, é possível adicionar uma **Dimensão de Coluna** para "pivotar" os dados.
* **Campo de Coluna**: Selecione uma dimensão `(Meses, Ano, Lojas)` para que cada valor único dessa dimensão se torne uma coluna na matriz
* **Total Lateral**: Quando uma coluna é definida, o sistema pode calcular automaticamente uma coluna extra de "Total" que soma horizontalmente os valores da linha
***
## 💰 Análises Financeiras (DRE)
A Matriz possui um motor de cálculo embutido para análises financeiras comuns, dispensando a necessidade de criar medidas complexas no banco de dados.
### 1. Análise Vertical (AV%)
Calcula a representatividade de uma linha em relação a um total.
* **Padrão**: Calcula a % da linha em relação ao **Total da Coluna**
* **Referência Avançada**: Permite configurar uma linha específica da hierarquia em 100% como métrica para cálculos diversos
* ***Exemplo***: Em um DRE, é possível configurar a "Receita Bruta" como dado de referência. Assim, todas as despesas mostrarão quantos % representam da Receita Bruta, e não do total geral
* ***Acesso***: Clique no botão de engrenagem na aba **Comportamento** para abrir o menu de seleção de referência
### 2. Análise Horizontal (AH%)
Calcula a variação (crescimento ou queda) entre colunas adjacentes, sendo ideal para comparar a evolução temporal `(Fev vs Jan)` ou `(2024 vs 2023)`.
* **Visualização**: Pode ser exibida como percentual `(+15.5%)` ou através de setas indicativas `(▲/▼)` para uma leitura mais limpa e compacta
> \[!TIP]
> **Cores Inteligentes**: As análises podem ter suas cores invertidas.
>
> * Para receitas, crescimento (cor verde) é bom.
> * Para despesas/custos, crescimento (cor vermelha) pode ser ruim. Use a opção **Inverter Cores** na aba Comportamento conforme a natureza do dado.
***
## 🎨 Customização Visual
A Matriz oferece extenso controle sobre a aparência para personalização de relatórios densos ou Dashboards executivos.
### Aparência Geral
* **Estilo**:
* `Padrão`: Visual clássico com fundo branco/escuro
* `Transparente`: Remove fundos e bordas externas
* `Destacado`: Adiciona sombras e bordas para ênfase
* **Linhas Zebradas**: Alterna a cor de fundo das linhas para facilitar a leitura em tabelas longas
* **Espaçamento (Padding)**: Controla a densidade da informação
* `Pequeno`: Máxima densidade de dados `(estilo Excel)`
* `Grande`: Mais respiro, ideal para apresentações
* **Tamanho do Texto**: Ajuste global da fonte `(Pequeno, Médio, Grande)`
### Totais e Subtotais
* **Mostrar Totalizador Geral**: Exibe a linha de rodapé com a soma total dos valores das colunas
* **Mostrar Totalizador Lateral**: (Apenas quando há Dimensão de Coluna) Exibe a última coluna com a soma da linha
* **Níveis Abertos**: Define quantos níveis da hierarquia já vêm expandidos por padrão ao carregar o Widget
### Alinhamento e Layout
* **Alinhamento de Cabeçalho/Conteúdo**: Controle independente `(Esquerda, Centro, Direita)` para títulos e dados
* **Quebra de Texto**: Controla se textos longos na primeira coluna serão cortados `(...)` ou terão quebras de linhas `(Wrap)`
* **Bordas de Célula**: Configuração fina de bordas `(Horizontais, Verticais ou Todas)`, incluindo espessura e cor
* **Delimitadores de Coluna**: Adiciona linhas verticais mais espessas para separar grupos de colunas, sendo útil quando existem muitas métricas por mês
### Cores Customizadas
Habilitando a opção "Customizar Cores", é possível sobrescrever a paleta do tema:
* Cabeçalho, Corpo e Rodapé (Texto e Fundo)
* Linhas Zebradas
* Subtotais (Linhas de nível hierárquico)
***
## 📝 Formatação de Células (Dados)
Cada métrica (coluna de valor) pode ser customizada individualmente clicando na engrenagem ao lado do campo na aba **Dados**.
### Cores Condicionais
É possível colorir o fundo, o texto ou adicionar um indicador (bolinha) na célula:
1. **Por Escala**: Define faixas de valores (0 a 50 = Vermelho, 50 a 100 = Verde)
2. **Por Valor**: Define cores para valores exatos de texto ("Lucro" = Verde, "Prejuízo" = Vermelho)
3. **Por Expressão (JavaScript)**: Lógica total
```javascript
// Exemplo: Pintar de vermelho se for negativo
if (this.value < 0) {
return '#ef4444';
}
return ''; // Sem cor
```
### Formatação Numérica
Além dos formatos padrões (Moeda, %...), é possível forçar formatações específicas para uma coluna se ela divergir do padrão do banco de dados (exibir como Inteiro ou Reduzido `1k`).
### Lógica do Totalizador
Por padrão, os totais dos nós pais são a soma dos filhos. Porém, é possível alterar esse comportamento se a métrica exigir:
* **Média**: O pai será a média dos filhos
* **Máximo/Mínimo**: O pai mostrará o maior/menor valor encontrado nos filhos
* **Expressão**: Mantém o cálculo original proveniente do banco (útil para métricas não-aditivas, como `Margem %`, que não podem ser somadas)
***
## ➕ Subtotalizadores (Linhas Calculadas)
A Matriz permite a criação de linhas de subtotal "artificiais" que são injetadas na tabela em posições específicas. Diferente dos totais nativos (que somam os filhos), esses subtotalizadores são calculados com base em **filtros customizados**.
Isso é essencial para DREs e relatórios contábeis onde certas linhas são somas de outras contas que não necessariamente compartilham o mesmo pai hierárquico ("EBITDA" ou "Margem de Contribuição").
### Configuração
Acesse a aba **Subtotais** no configurador:
* **Nome**: Rótulo que aparecerá na linha ("Receita Líquida")
* **Filtros**: Defina quais dados devem ser somados nesta linha
> \[!NOTE]
> Os filtros do subtotal são somados aos filtros globais do Widget.
* **Posicionamento**:
* **Tipo**: Se a linha deve aparecer "Antes" ou "Depois" de um nó de referência
* **Prefixos**: Uma lista de textos (separados por vírgula) para encontrar onde inserir a linha. O sistema buscará a primeira linha visível cujo rótulo comece com um desses prefixos
* **Comportamento "Depois"**: Se o nó de referência tiver filhos (drill-down), o subtotal será inserido após **todos** os filhos e netos, respeitando a hierarquia visual.
***
## ⚡ Performance e Limites
Para garantir performance, mesmo com milhões de linhas, a Matriz utiliza **Virtual Scrolling** (Renderização Virtual).
* **Renderização Inteligente**: O componente desenha na tela apenas as linhas que estão visíveis. É possível ter uma árvore com 10.000 linhas abertas, e o navegador continuará leve
* **Limitação por Nível**: Se um nível tiver um número elevado de filhos (50.000 produtos dentro de uma categoria), é possível usar a opção **Limitar registros por nível** na aba Visual para carregar apenas os "Top N" (os 50 primeiros), evitando sobrecarga de rede e do navegador
* **Ocultar sem valores**: Opção para esconder automaticamente linhas que estejam zeradas ou nulas, resultando em uma visualização mais limpa
---
---
url: 'https://docs.horusbi.com.br/dataviz/03-widgets/composed-matrix.md'
---
# Matriz Composta (Demonstrativo)
O Widget **Matriz Composta** é a ferramenta para demonstrativos financeiros: DRE, demonstrativo gerencial, orçado × realizado, fluxo gerencial. A diferença para a [Matriz](matrix.md) está em de onde vêm as linhas.
Na Matriz, as linhas são **descobertas no dado**: elas são os valores de uma dimensão. Isso resolve "Faturamento por Região × Mês", e não resolve DRE, porque "Receita Líquida" não existe como valor de coluna nenhuma. Ela é Receita Bruta menos Deduções.
Na Matriz Composta, cada linha é **declarada por quem monta**, e as colunas são sempre tempo. Apesar do nome, ela não serve só a DRE: qualquer relatório em que as linhas são um roteiro fixo (indicadores operacionais, quadro de pessoal, painel de safra) se monta do mesmo jeito.
## Qual dos dois usar
| Seu demonstrativo | Onde montar |
|---|---|
| Balancete puro sobre um plano de contas que já tem Código e Código Pai | [Matriz](matrix.md), modo Hierárquico (resolve em **uma consulta**) |
| Tem linhas calculadas (Receita Líquida, EBITDA, margens) | **Matriz Composta** |
| Mistura fatos que não se relacionam entre si (venda, financeiro, folha) | **Matriz Composta** |
| Precisa de coluna de categoria (Região, Produto) | [Matriz](matrix.md) |
> \[!TIP]
> Se o plano de contas do cliente já está bem modelado com Pai/Filho, comece pela Matriz Hierárquica. Ela é muito mais barata. A Matriz Composta ganha quando o demonstrativo precisa de contas que o plano não tem.
***
## 🏗️ Montando a estrutura
O botão **Montar matriz**, no fim da barra lateral, abre a tela em que o demonstrativo é construído: a lista de linhas à esquerda, e à direita tudo o que a linha selecionada tem.
Toda linha é de uma destas três espécies, e os três botões de **Adicionar** ficam no topo da lista:
* **Grupo**: totaliza as linhas de dentro, cada uma com o sinal dela. Não lê dado
* **Medida**: lê dado. Escolhe um campo da Aplicação e, se precisar, filtros próprios
* **Conta**: aritmética sobre **outras linhas** (`Receita Bruta − Deduções`)
### A conta se monta clicando
Em **Como esta linha é calculada**, cada parcela é uma ficha: escolha a operação (somar, subtrair, multiplicar, dividir) e a linha que entra. Também dá para acrescentar um número fixo (`Lucro antes do IR × 0,34`) ou abrir um grupo de parênteses, que nasce e morre fechado. Logo abaixo, a conta aparece escrita por extenso, com os nomes das linhas, para conferência.
Uma conta só oferece as linhas que já **terminaram acima** dela. É isso que torna impossível escrever uma referência circular.
### O sinal de cada linha
Em **No total do grupo acima**, cada linha diz como entra no total do grupo que a contém: **Soma**, **Subtrai** ou **Não entra**. Quem subtrai aparece no demonstrativo com um `(−)` antes do nome, para a leitura não depender de abrir a configuração.
O "Não entra" existe para linha de memória: número de colaboradores, toneladas, ticket médio, uma margem no meio de uma seção. Sem ele, o grupo somaria pessoas com dinheiro.
> \[!WARNING]
> O sinal vale **só** para a soma do grupo. Contas, totalizadores e análise vertical leem sempre o valor sem sinal.
>
> Ou seja: se "Deduções" está marcada como Subtrai dentro do grupo dela, a conta `Receita Bruta − Deduções` continua subtraindo **uma vez**, não duas.
Se o dado já vem com sinal invertido no banco (despesa gravada negativa, ou débito e crédito em coluna separada), isso se resolve onde é o lugar: numa expressão da tabela ou da Aplicação. É a causa número um de um demonstrativo que parece certo e não está.
### Formato de cada linha
O campo **Formato** aceita `Do próprio campo` (o padrão, que respeita a máscara definida na modelagem), `Número`, `Moeda`, `Percentual`, `Reduzido (mil, mi)` e `Inteiro`. Grupos e contas herdam o formato de quem eles resumem, então um grupo não sai com `R$` em cima de filhas sem `R$`.
### Arrastar para reordenar
Cada linha da lista tem uma alça. Arrastando:
* Soltar no **terço de cima** ou no **terço de baixo** de outra linha põe a linha antes ou depois dela
* Soltar no **meio de um grupo** move a linha para dentro dele
* Soltar na **área vazia** abaixo da árvore manda para o fim
Um grupo viaja com tudo o que está dentro dele. Mirar no próprio conteúdo não faz nada, de propósito: seria a forma de o ramo sumir do demonstrativo sem aviso.
Cada linha também tem, ao passar o mouse, os botões **Subir**, **Descer** e **Excluir**, e os grupos ganham um **Adicionar dentro** para criar uma linha já no lugar certo.
> \[!IMPORTANT]
> Não existe linha de total automática no rodapé, e isso é de propósito. Um rodapé automático somaria um grupo que contém uma linha percentual e imprimiria um número sem significado. A última linha do demonstrativo é uma **conta** que você escreve, chamada Lucro Bruto ou Resultado Líquido.
***
## 🗓️ O eixo de tempo
A Matriz Composta exige uma **Tabela Calendário**. O motivo é direto: as linhas de um DRE costumam ler fatos diferentes, que não se relacionam entre si, e o Calendário é a única coisa que faz um filtro de data da Dashboard alcançar todos eles.
Na seção **Tempo**, escolha a **Coluna do Calendário** e o **Agrupar por**: `Dia`, `Semana`, `Mês` ou `Ano`.
> \[!WARNING]
> Não existe agrupamento por trimestre. Para uma visão trimestral, agrupe por mês e leia os grupos, ou agrupe por ano.
Se uma linha precisa de outra data (despesa por competência, financeiro por caixa), isso se resolve na modelagem, com [Relacionamento Fantasma](../02-apps/data-modeling#relacionamento-fantasma-multiplas-datas-no-mesmo-fato) e uma expressão `use()`. Veja a receita [Calendário e Filtros Dinâmicos](../../guia/receitas/calendario-filtros).
### Períodos exibidos
O demonstrativo **sempre** segue o filtro de data da Dashboard. O que se configura aqui é só o quanto esse filtro é expandido:
* **Exatamente o período filtrado**: as colunas cobrem o que foi selecionado, arredondado para períodos inteiros. Selecionar do dia 15 até o dia 10 do mês seguinte mostra os dois meses completos, e não meio mês em cada ponta com cara de mês fechado
* **Sempre anos completos**: filtrou um trimestre, aparece o ano inteiro. É o DRE que se lê de janeiro a dezembro independentemente do recorte da tela. Continua respondendo ao filtro: trocar o ano troca as colunas
* **Últimos N períodos**: conta para trás a partir do fim do que foi filtrado. O campo **Quantos períodos** diz o N
No **ano corrente**, "Sempre anos completos" não desenha doze colunas vazias esperando o futuro: ele vai até o mês de hoje, e passa dele quando houver valor lançado à frente, como um previsto ou um orçamento já aprovado. Assim o ano abre em janeiro sem terminar em cinco colunas em branco.
Nos dois primeiros modos há um **Mínimo de colunas**, porque filtrar um mês só deixaria o demonstrativo com uma coluna. Em "Últimos N períodos" ele não aparece: o N já diz quantas.
### Filtros padrão do widget
A seção **Filtros padrão do widget**, na barra lateral, vale para **todas as linhas** do demonstrativo: restringir tudo a uma filial, a um centro de custo, ou sobrescrever o período que veio da Dashboard.
Filtro de linha compõe por cima deste, do mais geral para o mais específico: primeiro o do Widget, depois o da linha.
> \[!TIP]
> Para o demonstrativo ler um recorte diferente do resto da Dashboard, use os **Filtros padrão do widget**, e não o seletor de períodos. É a mesma mecânica dos outros Widgets, e ela aparece na barra de filtros como qualquer outro filtro, em vez de ficar escondida numa configuração interna.
> \[!NOTE]
> Uma linha não pode filtrar a mesma data que define as colunas. Se tentar, o demonstrativo avisa e pede para ajustar o período na seção de Tempo, que é quem manda no eixo horizontal.
***
## 🔀 Colunas dentro do período
Por padrão, cada período é uma coluna. Declarando duas ou mais em **Colunas dentro do período**, cada período passa a ter várias: é assim que se monta **orçado × realizado**, ou a papeleta de consolidação `Empresa A | Empresa B | Eliminações | Consolidado`.
Uma coluna é só um **nome**. Não tem filtro nem configuração própria. O que muda entre elas é o **campo** que cada linha lê: com duas colunas declaradas, toda linha de medida passa a escolher dois campos, um para cada, em **Campo de cada coluna**.
Isso é o suficiente porque orçado e realizado costumam morar em tabelas diferentes. Quando moram na mesma, separados por um indicador de cenário, a diferença vira um campo de [Expressão](../02-apps/expressions) da Aplicação, e a coluna simplesmente aponta para ele. Uma coluna de variação percentual funciona do mesmo jeito.
> \[!TIP]
> Ao adicionar a primeira, já nascem duas ("Realizado" e "Orçado"). Uma coluna sozinha não significa nada: cada período já é uma.
***
## 💰 A coluna de Acumulado
A **Coluna de acumulado** fica ligada por padrão, em **Totalizadores e análises**, e o rótulo dela é editável. O acumulado é uma **consulta própria**, sem agrupamento por período. Não é a soma das células.
Isso não é detalhe técnico, é o que faz o número certo aparecer:
* Uma linha de **ticket médio** mostra a média do período inteiro, e não a soma das médias mensais
* Uma linha de **contagem distinta** mostra os distintos do período, e não a soma dos distintos de cada mês
* Uma **conta** recalcula: a margem do ano é o lucro do ano sobre a receita do ano, nunca a média das margens mensais
***
## 📊 Análises
### Vertical (AV%)
Cada linha como percentual de uma **linha base** que você escolhe, tipicamente a Receita Líquida.
* Por padrão sai numa **coluna só, no fim**, calculada sobre o acumulado
* Ligando **Uma coluna em cada período**, ela aparece dentro de cada mês, sobre a base daquele mês
### Horizontal (variação)
Compara o período com outro. Ao ligar, escolha com o que ela compara:
* `Contra o período anterior` (fevereiro contra janeiro)
* `Contra o mesmo período do ano anterior` (fev/26 contra fev/25)
E escolha onde ela aparece:
* `Numa sub-linha abaixo de cada linha`: cabe em demonstrativo largo, com muitos períodos
* `Numa coluna dentro de cada período`: cabe em demonstrativo alto, com muitas linhas
São o mesmo número desenhado em dois lugares.
> \[!NOTE]
> Numa linha de percentual a variação sai em **pontos percentuais**. Uma margem que foi de 10% para 12% aparece como `+2,0 p.p.`, e não como `+20%`, porque é assim que se lê demonstrativo.
***
## 🌿 Abrir uma linha por dimensão
Uma linha de medida pode se abrir em filhas, uma por valor de uma dimensão: despesas comerciais abertas por centro de custo, por exemplo. Configure em **Quebrar por**, dentro da linha.
As filhas saem dos valores da dimensão, então **um centro de custo criado no mês que vem aparece sozinho**, sem ninguém editar o demonstrativo.
* **Máximo de itens**: quantas filhas mostrar, escolhidas pelas maiores
* **Agrupar o resto**: mantenha ligado. Sem ele, as filhas exibidas não somam o valor do pai, e um demonstrativo em que os filhos não fecham com o pai está errado aos olhos de quem lê
Até três níveis. Acima disso a quantidade de linhas explode e o demonstrativo fica ilegível.
Com colunas declaradas, a lista de filhas é a mesma em todas: quem define o conjunto é a primeira coluna. Sem isso, o orçado teria centros de custo que o realizado não tem e a matriz deixaria de ser uma matriz.
***
## 🎨 Aparência
A seção **Aparência**, na barra lateral, tem a galeria de modelos. Escolher um resolve o caso comum sem abrir mais nada:
| Modelo | Característica |
|---|---|
| **Limpo** | Sem grade. A hierarquia vem do peso do texto. É o padrão |
| **Contábil** | Negativo entre parênteses, sem grade nenhuma, traço acima dos subtotais |
| **Compacto** | Grade completa, linhas alternadas e texto menor. Cabe mais linha na tela |
| **Executivo** | Texto maior e faixa colorida nos grupos. Para telão e apresentação |
| **Zebrado** | Linhas alternadas com grade horizontal. Ajuda a percorrer muitos períodos |
> \[!TIP]
> **Trocar de modelo não apaga os ajustes que você já fez.** O modelo é o ponto de partida, e o que você mexeu continua por cima. Dá para experimentar os cinco sem perder trabalho.
### A tela de aparência
O botão **Ajuste fino**, logo abaixo da galeria, abre uma tela maior com **prévia ao vivo** grudada no canto: mexeu num controle, o exemplo ao lado muda na hora. Ela vem em seções recolhíveis.
**Modelo**
A mesma galeria, em cartões maiores, com a explicação de cada um ao lado do nome.
**Geral**
* **Espaçamento**: `Apertado`, `Normal` ou `Espaçoso`
* **Grade**: `Nenhuma`, `Só entre linhas` ou `Completa`, com cor própria
* **Cabeçalho**: `Simples`, `Com traço` ou `Com fundo`
* **Recuo por nível**: quanto cada nível da hierarquia entra para a direita
* **Tamanho do texto**: contínuo, entre 80% e 140% do corpo. A mesma matriz num telão e num monitor de 24" quer tamanhos diferentes
* **Listrar linhas alternadas**
* **Cor de destaque**: alimenta a coluna de acumulado e as faixas dos modelos que usam cor
**Ajuste fino**, marcado como *o que quase ninguém precisa*
Largura da coluna de rótulos (plano de contas costuma ter nome comprido), fonte dos números (`A da interface` ou `Monoespaçada`, que alinha dígito a dígito em conferência), caixa e alinhamento do cabeçalho, traço abaixo do cabeçalho com largura, tipo e cor, cores do cabeçalho, e **Esconder o cabeçalho** para quando o Widget já tem título logo acima.
**Período corrente**
**Destacar o período em que estamos** marca a coluna do mês de hoje, em negrito e com cor de fundo e de texto configuráveis. Numa matriz de doze colunas, achar o mês corrente no meio delas é um trabalho que a tela pode fazer.
**Por espécie de linha**
Ajusta uma espécie inteira de uma vez: `Todas as linhas`, `Grupos e seções`, `Medidas`, `Contas`, `Itens de quebra` e `Linha de variação`. Cada uma tem um "voltar ao modelo" próprio.
Note que a régua é visual, e não o tipo declarado: uma medida quebrada por dimensão conta como **Grupos e seções**, porque para quem lê ela é uma seção que se desdobra.
As duas últimas seções, **Regras da matriz** e **Colunas especiais**, são formatação condicional e estão descritas mais abaixo.
### Aparência de uma linha específica
Dentro de **Montar matriz**, cada linha tem sua própria seção de aparência, e o que estiver ali vence o da espécie. Ela fica onde a linha se edita de propósito: procurar "onde eu mudo a cor do Lucro Líquido" numa tela à parte é o tipo de caça que faz gente desistir de customizar.
O vocabulário é o mesmo dos dois lados: negrito, itálico, caixa alta, tamanho relativo (`Menor`, `Normal`, `Maior`), cor de texto e de fundo, e o **Traço**, que carrega as convenções contábeis prontas: `Traço acima` significa "aqui fecha uma conta", `Traço duplo abaixo` significa "aqui fecha o resultado". Atrás do "Ajuste fino" da própria linha ficam largura, estilo e cor do traço, respiro acima e abaixo, sublinhado, riscado, alinhamento do rótulo, um ícone fixo e uma segunda cor de fundo, que transforma o fundo da linha em degradê.
Cada controle mostra um ponto ao lado quando o valor foi declarado **ali**, e não herdado. É o que separa "não mexi nisto" de "quero isto assim", e é o que permite tirar o negrito que o modelo põe em toda conta.
### Números negativos
Em **Geral**:
* **Números negativos**: `Com sinal de menos` ou `Entre parênteses`, a convenção contábil
* **Negativo em vermelho**: desligado por padrão nos cinco modelos
O padrão é desligado porque num demonstrativo a seção de custos é negativa por natureza: pintar todo negativo de vermelho pinta metade da página e o resultado se lê como relatório de erro. Vermelho aqui funciona melhor como sinal de exceção, e exceção é o que a formatação condicional marca. Quando você liga, qualquer regra condicional sobre negativos continua vencendo, inclusive para escolher outra cor.
***
## 🚦 Formatação condicional
Existe em dois lugares, com a mesma tela:
* **Regras da matriz**, no Ajuste fino da aparência: valem para todas as células
* **Formatação condicional**, dentro de cada linha: valem só para ela, e vencem as da matriz
A régua é o escopo: o que fala de **uma** linha vence o que fala de todas. É assim que se isenta uma linha de uma regra geral sem inventar exceção.
### A regra se lê como uma frase
Cada regra tem duas metades lado a lado: **Quando a célula** (o teste) e **Ela fica assim** (o efeito).
Do lado esquerdo, o teste:
* `For negativa`, `For positiva`, `For zero`, `Não tiver valor`
* `Comparar com um número` (`>`, `>=`, `<`, `<=`, `=`, `!=`)
* `Estiver numa faixa` (de / até)
* `Regra em código`
Do lado direito, o efeito. Comece pelos estilos prontos, que dizem a **leitura** em vez de pedir uma escolha entre dois vermelhos: `Ruim`, `Bom`, `Atenção`, `Destaque`, as três versões `com fundo`, e `Apagado`. Cada botão mostra um número de verdade, então dá para ver se o texto continua legível. Além deles: negrito, itálico, sublinhado, riscado, um **ícone** de um catálogo curto (semáforo, direção, situação, marcação) que pode ficar antes ou depois do número, e as cores à mão.
> \[!TIP]
> **A ordem importa, e ela está na tela.** As regras valem de cima para baixo, e as de baixo refinam as de cima: uma que pinta o fundo depois de outra que pinta o texto mantém as duas coisas. Use as setas para reordenar, e o olho para desligar uma regra sem apagá-la.
### Regra em código
Para o que os testes prontos não alcançam ("marque o melhor mês desta linha", "pinte numa escala contínua"), o teste `Regra em código` abre o editor do produto.
O código devolve `true`, e aí a célula recebe o efeito escolhido ao lado, ou devolve o próprio efeito como objeto, quando ele depende do valor. O painel **O que dá para usar aqui** lista o que o código recebe e traz exemplos prontos:
* `this.value`: o valor da célula, ou `null` quando não deu para calcular
* `this.row`: a linha (nome e tipo)
* `this.period`: o período da coluna, e se ela é a do acumulado
* `this.column`: a coluna dentro do período, quando há mais de uma
* `this.rowValues`: todos os valores daquela linha, na ordem dos períodos
**1. Usar o efeito escolhido ao lado**
```javascript
// Marca o melhor mês desta linha.
const v = this.rowValues.filter(x => x !== null);
return this.value !== null && this.value === Math.max(...v);
```
**2. Decidir o efeito pelo valor**
```javascript
if (this.value === null) return false;
if (this.value < -1e6) return { color: "#dc2626", bold: true, icon: "mdi:alert-octagon" };
if (this.value < 0) return { color: "#dc2626", icon: "mdi:triangle-down" };
return false;
```
**3. Escala de cor contínua**
```javascript
// A cor aceita qualquer notação de cor, então o degradê sai da conta.
const v = this.rowValues.filter(x => x !== null);
if (this.value === null || !v.length) return false;
const min = Math.min(...v), max = Math.max(...v);
const t = max === min ? 0.5 : (this.value - min) / (max - min);
return { background: `hsl(${Math.round(120 * t)} 70% 92%)` };
```
Enquanto escreve, o editor testa a regra contra **células reais do seu próprio demonstrativo** e mostra como o número ficaria. É o que transforma "não funcionou" em "devolveu isto".
> \[!NOTE]
> Só cor, fundo, marcas de texto e ícone atravessam. Tamanho, espaçamento e borda ficam de fora porque mudariam a métrica da tabela inteira a partir de uma célula, e o alinhamento entre linhas é o que faz um demonstrativo se ler.
> \[!WARNING]
> Uma regra em código que dá erro se desliga sozinha e o demonstrativo mostra o aviso na tela, com o nome da linha. Ele some quando a regra é corrigida.
### Colunas especiais
A coluna de acumulado, a de AV% e a de variação têm aparência própria, tanto no nível do demonstrativo (**Colunas especiais**, no Ajuste fino) quanto dentro de uma linha (**Colunas especiais nesta linha**). Serve para deixar o acumulado de uma linha em destaque sem mexer nas outras.
A ordem de precedência, do mais fraco para o mais forte, é sempre a mesma:
```
modelo < espécie de linha < a linha < regra condicional
```
***
## 🖱️ Clicar num número
Clicar numa célula (com o botão esquerdo mesmo) abre o menu de ações daquele número:
* **Copiar Valor**: o número como está na tela, já formatado
* **Copiar valor sem formatação**: o número cru, para colar numa conta
* **Ver relatório deste número**: abre o Relatório tabular com exatamente o recorte que produziu a célula (o campo da linha, o período da coluna e a pilha de filtros)
* **De onde vem este número**: abre a decomposição
* **Copiar linha**: a série inteira daquela linha, com cabeçalho
* **Copiar matriz**: o demonstrativo inteiro
> \[!TIP]
> "Copiar linha" e "Copiar matriz" colam direto numa planilha, já em colunas, sem passar por arquivo. As colunas saem na mesma ordem em que estão na tela, incluindo acumulado e análises.
### De onde vem este número
**Ver relatório** aparece nas linhas de medida, porque ali existe uma consulta que devolve aquele número. Numa linha de **grupo** ou de **conta** não existe: "Lucro Bruto" não é uma consulta, é aritmética. Para essas, a ação é **De onde vem este número**, que abre a decomposição:
* Cada parcela aparece com o **operador** pelo qual entrou (`+`, `−`, `×`, `÷`, ou `não entra`), e o rodapé repete o total, então dá para conferir a conta na hora
* A parcela marcada como "não entra" também é listada. Escondê-la deixaria uma lista que não fecha com o total
* **Decompor** desce numa parcela que também é composta, e o caminho percorrido fica clicável no topo
* Ao chegar numa medida, a parcela ganha o botão **Relatório** e a descida acaba ali, porque é ali que o dado existe
Células de AV% e de variação não têm nenhuma das duas ações: elas são derivadas de outras duas células, que já as têm.
***
## 🔽 Abrir e fechar níveis
Toda linha que se desdobra (um grupo, ou uma medida quebrada por dimensão) ganha uma seta à esquerda do nome. Clicar abre e fecha.
O estado inicial se define em dois lugares:
* **Ao abrir a dashboard**, no Ajuste fino da aparência: `Tudo aberto`, `Só o primeiro nível aberto` ou `Tudo fechado`
* **Começar fechado**, dentro de um grupo específico, para aquele grupo nascer fechado independentemente do padrão
> \[!NOTE]
> Refiltrar a Dashboard **não** desfaz o que você abriu ou fechou à mão. As linhas vêm da configuração, não do dado, então trocar o mês não é motivo para colapsar o demonstrativo de volta.
***
## ⏳ Enquanto carrega, e o custo
Este é o Widget mais caro do produto. Cada linha de medida faz uma consulta por coluna, mais uma para o acumulado. Um demonstrativo de 30 linhas de medida com duas colunas e acumulado ligado são 120 consultas.
Duas consequências práticas:
* Prefira **poucas linhas largas abertas por dimensão** a muitas linhas irmãs escritas à mão. Uma linha "Despesas comerciais" aberta por centro de custo é uma consulta; quinze linhas de centro de custo são quinze
* Grupos e contas **não custam nada**: são aritmética sobre linhas já buscadas
Enquanto carrega, a tela mostra o progresso real (a porcentagem dos dados já carregados, não uma animação genérica). A matriz não desenha pela metade, de propósito: meio demonstrativo preenchido parece um demonstrativo pronto.
Depois de montado, editar nome, sinal, conta, cor ou análise **não** volta ao banco: só o que muda o recorte dos dados (campo, filtro, período, colunas, quebra) refaz as consultas.
***
## ⚠️ Quando o demonstrativo se recusa a desenhar
Se a montagem tiver erro (uma conta referenciando uma linha que não existe, um grupo vazio, uma linha de medida sem campo), a Matriz Composta **não desenha** e lista o que está errado, com o nome da linha. A barra lateral também mostra quantos ajustes estão pendentes, e clicar nela abre a tela de montagem.
Isso é diferente dos outros Widgets, e é deliberado. Num demonstrativo, uma linha que some deixa um resultado completo, internamente coerente e errado, e alguém toma decisão em cima dele. Erro visível é melhor que número plausível.
### Quando aparece um traço
Uma célula com `—` no lugar do número significa **não foi possível calcular**, e nunca zero.
O caso mais comum é uma margem num mês sem movimento: a receita é zero e a divisão não existe. Mostrar `0%` ali seria pior, porque `0%` de margem se lê como prejuízo operacional, enquanto o traço se lê como ausência de dado, que é o que de fato houve. Nesses meses o Acumulado continua mostrando a margem real do período inteiro.
O traço também aparece quando a linha depende de um campo que o seu acesso não alcança. Nesse caso só ela e quem depende dela ficam vazios; o resto do demonstrativo continua desenhado.
***
## 🚫 O que fica fora
* **Saldo acumulado** (saldo inicial e final, caixa acumulado). Uma linha não consegue enxergar o período anterior. Isso é modelagem: materialize o saldo no ETL com função de janela. Lembre que o Acumulado é uma consulta sem agrupamento, então o saldo precisa de uma agregação que sobreviva a isso
* **Rateio de custo indireto**. É política contábil, muda por exercício e precisa ser auditável: o lugar é o ETL
* **Subtotal de período no meio** (`Jan | Fev | Mar | 1º Tri | Abr…`)
* **Barra de dados e escala de calor prontas**. Numa matriz cujas linhas são grandezas diferentes (receita, número de colaboradores, margem), uma barra comparando células de linhas distintas não significa nada. A escala de calor, quando faz sentido dentro de uma linha, sai de uma regra em código de poucas linhas (exemplo 3 acima)
* **Colunas por categoria**. O eixo horizontal é sempre tempo. Para categoria, use a [Matriz](matrix.md)
---
---
url: 'https://docs.horusbi.com.br/hec/desks/mesas.md'
---
# Mesas
> **Caminho**: Menu Lateral > Mesas
As **Mesas** são os containers organizacionais do Horus. Pense nelas como "pastas raiz" onde os objetos do seu ambiente — Aplicações, Dashboards, Tabelas e Dataflows — são armazenados e organizados de forma lógica.
O sistema divide as mesas em dois tipos, cada um com suas características e regras de negócio:
### 📊 1. Mesas de Aplicações
Onde ficam os **Dashboards** e **Aplicações de BI** — tudo o que o usuário final visualiza.
* **Gestão**: Menu "Mesas"
* **Objetivo**: Organizar as entregas de visualização de dados por área de negócio (Comercial, Financeiro, RH)
### 🗄️ 2. Mesas de Dados
Onde ficam os **Dataflows** (fluxos de integração) e **Tabelas** do Data Warehouse — a camada técnica por trás dos Dashboards.
* **Gestão**: Menu "Mesas de Dados"
* **Objetivo**: Organizar a engenharia e o armazenamento dos dados por estágio (Raw, Refined, Sandbox)
***
## ⚙️ Gerenciamento de Mesas
O processo de criação e edição é o mesmo para ambos os tipos:
* **Criar Nova Mesa**: Clique no botão de adicionar (+) no topo da lista
* **Editar**: Clique em "Gerenciar" na linha da mesa desejada
### Metadados
* **Nome**: Identificação da mesa ("Financeiro", "RH", "Raw")
* **Ícone e Cor**: Personalização visual para facilitar a identificação rápida no menu lateral
***
## 🗑️ Regras de Exclusão
Esta é a principal diferença técnica entre os dois tipos de mesa:
### Mesas de Aplicações — Exclusão Destrutiva
* Ao excluir uma Mesa de Aplicação, **todos os Dashboards e aplicações** contidos nela são removidos permanentemente
* **Permissão necessária**: Funções do Sistema > Aplicações > Deletar
> \[!WARNING]
> Antes de excluir uma Mesa de Aplicação, certifique-se de que nenhum Dashboard ativo depende dela. A exclusão é irreversível.
### Mesas de Dados — Exclusão Protegida
A exclusão é **protegida** para garantir a integridade do Data Warehouse.
* O sistema **impede** a exclusão direta se a mesa contiver Tabelas ou Dataflows ativos
* **Assistente de Migração**: Caso haja conteúdo, ao tentar excluir, uma janela solicitará que você escolha uma **Nova Mesa de Dados** como destino
* **Migração Automática**: O sistema move todos os objetos para a nova mesa e só então exclui a original — evitando perda acidental de dados
### Mesas de Template
Certas mesas podem ser marcadas como "Template" nas configurações do Tenant.
* Essas mesas ficam **protegidas contra edição e exclusão**
* **Objetivo**: Garantir que estruturas padrões distribuídas por uma consultoria não sejam alteradas pelos usuários finais
***
## 🔐 Permissões de Acesso
O menu **Usuários > Permissões** controla a visibilidade e as ações sobre as mesas:
1. **Funções do Sistema (O que posso fazer)**:
* Para criar mesas, o usuário precisa da permissão "Criar" nos módulos **Aplicações** ou **Mesas de Dados**
2. **Acesso aos Dados (O que posso ver)**:
* O modelo **depende da natureza da mesa** — e os dois tipos partem de estados opostos:
* **Mesa de Aplicação = lista de permissão (allow-list)**: nasce **trancada**. Ninguém vê até receber acesso explícito (por usuário ou grupo) na aba **Acesso aos Dados**.
* **Mesa de Dados = lista de bloqueio (deny-list)**: nasce **aberta** — visível a todos por padrão. Só fica restrita quando você adiciona um **bloqueio explícito** (por usuário ou grupo) na aba **Acesso aos Dados**.
> \[!IMPORTANT]
> **Admin não é bypass universal.** O **Admin do Tenant** faz bypass do allow-list das **Mesas de Aplicação** — enxerga todas as Aplicações. Mas **honra o bloqueio de Mesa de Dados**: se foi bloqueado de uma Mesa de Dados, não a vê, como qualquer outro usuário. O bloqueio de dados é uma parede dura para todos os papéis. Detalhes completos em **[Controle de Acesso às Mesas](/dw/desks/controle-de-acesso)**.
> \[!IMPORTANT]
> **Datamarts vs Mesas de Dados**: As **Mesas de Dados** são a estrutura física de armazenamento. Os **[Datamarts](../../dw/datamarts/)** (Menu Recursos) são a estrutura lógica para distribuição de acesso e governança das tabelas. São conceitos complementares, mas distintos.
---
---
url: 'https://docs.horusbi.com.br/dw/desks.md'
---
# Mesas e Gestão de Tabelas
No HorusDW, a organização dos dados segue uma hierarquia que separa o armazenamento físico (Mesas) da visualização de negócio (Datamarts). Esta página foca na gestão das Mesas e no gerenciamento das tabelas que você é responsável.
***
## 🗄️ Mesa (Desk) — Armazenamento Físico
A Mesa é o **container governado** onde as tabelas são armazenadas fisicamente. Ela representa a camada técnica ou o estágio do dado no ciclo de vida.
* **Exemplos** — *Raw* (Dados brutos), *Trusted* (Dados limpos), *Gold* (Dados agregados), *API* (Dados externos)
* **Regra** — Uma tabela só pode existir fisicamente em **uma única Mesa**
* **Gerenciamento** — A criação e manutenção de Mesas (quem pode ver, quem pode editar) é realizada através do módulo administrativo **HEC**
***
## 🏷️ Datamart — Visualização de Negócio
O Datamart é uma forma de agrupar tabelas que fazem sentido para uma determinada área da empresa, independente de onde estão fisicamente armazenadas.
* **Exemplos** — *Comercial*, *Financeiro*, *Recursos Humanos*
* **Regra** — Uma mesma tabela pode aparecer em **múltiplos Datamarts**
> \[!TIP]
> A tabela `Filiais` (que está fisicamente na Mesa *Gold*) pode ser visualizada tanto no Datamart *Comercial* quanto no *Financeiro* — sem duplicação de dados.
***
## 📋 Minhas Tabelas (Gestão)
Para usuários que são **Donos (Owners)** de Datamarts ou Tabelas, o HorusDW oferece uma visão administrativa chamada **Minhas Tabelas**. Diferente da "Minha Mesa" (que é um ambiente de trabalho privado), esta tela serve para **gerenciar** os dados oficiais que você publicou ou pelos quais é responsável.
### O que você pode ver e fazer
Na tela de listagem, as seguintes informações estão disponíveis:
| Coluna | Descrição |
|--------|-----------|
| **Datamarts** | A quais áreas de negócio a tabela pertence |
| **Tabela** | O nome da tabela |
| **Pessoas com Acesso** | Contador de usuários com acesso — clicando, você pode ver a lista completa |
| **Solicitações Abertas** | Alerta se há usuários pedindo permissão para acessar seus dados |
> \[!NOTE]
> Esta é a central de comando para garantir que seus dados estejam sendo acessados pelas pessoas certas e com as permissões adequadas.
---
---
url: 'https://docs.horusbi.com.br/dataviz/02-apps/data-modeling.md'
---
# Modelagem de Dados
Este guia explica os conceitos fundamentais de modelagem de dados no Horus DataViz:
* Como conectar Tabelas corretamente
* Como evitar problemas comuns
* Como escolher a melhor abordagem para cada cenário
* Como reaproveitar uma mesma Dimensão Calendário para várias datas do mesmo Fato (Relacionamento Fantasma)
> \[!TIP]
> Para o passo a passo de como criar Relacionamentos na interface, consulte [Aba Tabelas (Modelo de Dados)](./tables).
>
> Para montar o fluxo completo de ponta a ponta (filtro da dashboard, propagação e datas diferentes por widget), siga a receita [Calendário e Filtros Dinâmicos](/guia/receitas/calendario-filtros).
***
## Explosão Cartesiana
Conectar duas Tabelas Fato diretamente **causa explosão cartesiana**, um problema técnico sério que deve ser evitado.
### O que é?
Todo Relacionamento no DataViz é **um-para-muitos** (1:N), pois a Tabela de origem deve ter chaves únicas. Tabelas Fato (Vendas, Metas, Pedidos) contêm eventos repetidos: nenhuma delas possui chaves únicas para a outra. Ao tentar conectar duas Tabelas Fato diretamente, o sistema bloqueará no processo de validação — mas mesmo que fosse possível, o resultado seria um **produto cartesiano**: cada registro de uma Tabela combinado com cada registro da outra. Com 1.000 vendas e 500 metas, o resultado teria 500.000 linhas — e os valores seriam multiplicados incorretamente.
**Exemplo do problema:**
| Vendas | Metas | Resultado Incorreto |
|--------|-------|---------------------|
| 3 registros (R$ 300) | 2 registros (R$ 100) | 6 registros (R$ 600 de vendas, R$ 300 de metas) |
Para visualizar o problema na prática, execute as consultas abaixo e compare os resultados:
***
## Dimensões Compartilhadas
A solução é conectar as Tabelas Fato através de **Dimensões Compartilhadas** — Tabelas às quais ambas as Tabelas Fato fazem referência. Cada Tabela Fato se relaciona de forma independente com a Tabela Dimensão, evitando a multiplicação dos dados.
```mermaid
flowchart LR
subgraph "❌ Errado"
V1[Vendas] <--> M1[Metas]
end
subgraph "✅ Correto"
D[Dimensão] --> V2[Vendas]
D --> M2[Metas]
end
```
Esse padrão funciona para **qualquer Dimensão que as Tabelas Fato tenham em comum**:
| Dimensão | Exemplo de Uso |
|----------|----------------|
| **Calendário** | Vendas por data ↔ Metas por data |
| **Filial** | Vendas por filial ↔ Estoque por filial |
| **Produto** | Vendas por produto ↔ Compras por produto |
| **Vendedor** | Vendas por vendedor ↔ Metas por vendedor |
| **Cliente** | Vendas por cliente ↔ Devoluções por cliente |
> \[!TIP]
> O **Calendário** é a Dimensão mais comum porque praticamente todo dado corporativo possui alguma referência temporal (data da venda, data da meta, data do pedido, etc.). Por isso, ter uma Tabela Calendário bem estruturada é fundamental para qualquer modelo analítico.
### Por que usar uma Tabela Calendário?
Quando existem Tabelas Fato como **Vendas** e **Metas**, cada uma possui sua própria coluna de data:
* Vendas: `DATA_VENDA`
* Metas: `DATA_META`
O problema surge ao criar um gráfico comparando as duas:
```mermaid
flowchart LR
subgraph "❌ Conexão Direta"
V1[Vendas] <--> M1[Metas]
end
```
Ao tentar configurar a coluna `DATA_VENDA` no eixo X de um gráfico de "% de Meta Atingida", os dados de Metas não conseguem se referenciar a essa coluna — ela pertence apenas à Tabela Vendas. O resultado apresentará dados incorretos ou um erro.
A solução é criar uma **Tabela Calendário** como ponte. Tanto Vendas quanto Metas se conectam ao Calendário através de suas respectivas colunas de data:
```mermaid
erDiagram
CALENDARIO ||--o{ VENDAS : "DATA = DATA_VENDA"
CALENDARIO ||--o{ METAS : "DATA = DATA_META"
VENDEDOR ||--o{ VENDAS : "ID_VENDEDOR"
VENDEDOR ||--o{ METAS : "ID_VENDEDOR"
```
Agora, ao utilizar `Calendário.DATA` no eixo X:
* O sistema filtra Vendas pelo Relacionamento `Calendário.DATA = Vendas.DATA_VENDA`
* O sistema filtra Metas pelo Relacionamento `Calendário.DATA = Metas.DATA_META`
* Ambos os valores aparecem corretamente alinhados por data
> \[!IMPORTANT]
> **Regra de ouro**: Todas as **Dimensões em comum** entre Tabelas Fato devem ser conectadas através de Tabelas Dimensão. No exemplo acima, tanto `Calendário` quanto `Vendedor` conectam as duas Tabelas Fato, permitindo análises cruzadas por data e por vendedor.
### Exemplo Prático: Meta vs. Realizado
Considere este modelo para análise de metas de vendas:
```mermaid
erDiagram
CALENDARIO {
date DATA PK
int ANO
int MES
string DIA_SEMANA
string TRIMESTRE
}
VENDEDOR {
int ID_VENDEDOR PK
string NOME
string REGIONAL
}
VENDAS {
int ID_VENDA PK
date DATA_VENDA FK
int ID_VENDEDOR FK
decimal VALOR
}
METAS {
int ID_META PK
date DATA_META FK
int ID_VENDEDOR FK
decimal VALOR_META
}
CALENDARIO ||--o{ VENDAS : "DATA = DATA_VENDA"
CALENDARIO ||--o{ METAS : "DATA = DATA_META"
VENDEDOR ||--o{ VENDAS : "ID_VENDEDOR"
VENDEDOR ||--o{ METAS : "ID_VENDEDOR"
```
Com este modelo, é possível criar Expressões como:
```sql
-- Percentual de meta atingida
SUM([Vendas].VALOR) / SUM([Metas].VALOR_META) * 100
```
E utilizar no eixo X:
* ✅ `Calendário.DATA` — funciona para ambas as Tabelas Fato
* ✅ `Calendário.MES` — agrupamento mensal
* ✅ `Vendedor.NOME` — por vendedor
* ❌ `Vendas.DATA_VENDA` — funciona apenas para a Tabela Vendas; a Tabela Metas ficaria sem dados
Com Dimensões Compartilhadas, tanto o agrupamento por data quanto por vendedor funcionam corretamente — sem duplicação de valores.
***
## Relacionamento Fantasma (múltiplas datas no mesmo Fato)
Um mesmo Fato costuma ter mais de uma coluna de data. Uma nota fiscal tem **data de emissão** e **data de entrega**; uma oportunidade tem data de abertura, de ganho e de perda. As perguntas "faturamento por emissão" e "faturamento por entrega" usam o mesmo Fato, porém datas diferentes.
A solução é manter **uma única Tabela Calendário** e ligá-la ao Fato por mais de uma chave. A primeira ligação é a padrão; as demais ficam disponíveis sob demanda. Esse padrão é chamado de **Relacionamento Fantasma** (ou Relacionamento Secundário).
```mermaid
erDiagram
CALENDARIO ||--o{ FATURAMENTO : "DATA = DATA_EMISSAO (padrão)"
CALENDARIO ||--o{ FATURAMENTO : "DATA = DATA_ENTREGA (use)"
```
Com as duas chaves cadastradas:
* O filtro e o agrupamento por `Calendário.DATA` usam **DATA\_EMISSAO** automaticamente (a chave padrão).
* Um Widget ou Expressão que precise da entrega chama a chave alternativa com `use()`:
```sql
use([Calendário], [Faturamento].DATA_ENTREGA) SUM([Faturamento].VALOR)
```
> \[!TIP]
> O cadastro das chaves alternativas é feito na [Aba Tabelas](./tables#relacionamentos-secundarios). A sintaxe `use()` e o vídeo tutorial estão em [Expressões](./expressions#relacionamentos-secundarios-com-use).
Para o passo a passo completo desse cenário (incluindo o filtro da dashboard e a granularidade por Widget), veja a receita [Calendário e Filtros Dinâmicos](/guia/receitas/calendario-filtros).
***
## Dimensões Exclusivas e Linhas Duplicadas
Ao combinar métricas de duas ou mais Tabelas Fato no mesmo Relatório, é importante que todas as Dimensões (colunas de agrupamento) utilizadas existam em **todas** as Tabelas Fato envolvidas. Quando uma Dimensão existe em apenas uma das Tabelas Fato, o resultado pode apresentar linhas aparentemente duplicadas para o mesmo registro.
Para entender o motivo, é importante saber como o sistema combina duas Tabelas Fato: ele gera uma consulta independente para cada uma e depois une os resultados. Cada consulta só consegue preencher as Dimensões das Tabelas que estão conectadas a ela.
**Exemplo prático:**
Imagine o modelo abaixo. A Tabela Vendas se conecta a Vendedor, Calendário e Produto. A Tabela Metas se conecta apenas a Vendedor e Calendário. Note que **não existe conexão entre Metas e Produto**, porque as metas são definidas por vendedor/mês, sem granularidade de produto.
No diagrama abaixo, Produto se conecta apenas a Vendas. A ausência de uma ligação entre Produto e Metas é intencional e é a causa do comportamento descrito a seguir.
```mermaid
erDiagram
VENDEDOR ||--o{ VENDAS : ""
VENDEDOR ||--o{ METAS : ""
CALENDARIO ||--o{ VENDAS : ""
CALENDARIO ||--o{ METAS : ""
PRODUTO ||--o{ VENDAS : ""
```
Se o Relatório solicita `Vendedor`, `Produto`, `Total Vendas` e `Valor Meta`, o sistema gera duas consultas:
1. **Consulta de Vendas:** preenche Vendedor e Produto normalmente, porque ambas as Dimensões estão conectadas à Tabela Vendas
2. **Consulta de Metas:** preenche Vendedor, mas **não tem acesso a Produto** porque não existe essa conexão no modelo
Ao unir os resultados, o sistema agrupa por todas as Dimensões do Relatório (Vendedor + Produto). Como a consulta de Metas não consegue preencher Produto, esse campo fica vazio nessa parte dos dados:
| Vendedor | Produto | Total Vendas | Valor Meta |
|----------|---------|-------------|------------|
| João | Notebook | R$ 5.000 | R$ 0 |
| João | Monitor | R$ 3.200 | R$ 0 |
| João | - | R$ 0 | R$ 8.000 |
João aparece em **três linhas**: duas com Produto preenchido (proveniente de Vendas, uma para cada produto) e uma terceira sem Produto (proveniente de Metas). Como `Notebook`, `Monitor` e `-` são valores distintos na coluna Produto, o agrupamento não consegue consolidá-los em uma única linha.
Se o Relatório solicitasse apenas `Vendedor`, `Total Vendas` e `Valor Meta` (sem Produto), o resultado seria uma única linha por vendedor, com os valores consolidados:
| Vendedor | Total Vendas | Valor Meta |
|----------|-------------|------------|
| João | R$ 8.200 | R$ 8.000 |
**Regra geral:** ao montar Relatórios que cruzam métricas de duas Tabelas Fato, utilize apenas Dimensões que existam em ambas. No diagrama acima, as Dimensões seguras seriam Vendedor e Calendário (conectadas a Vendas e Metas). Produto causaria a duplicação porque só existe na Tabela Vendas.
> \[!NOTE]
> Esse comportamento é inerente à diferença de granularidade entre as Tabelas Fato, não sendo um bug do sistema. Se a Tabela Metas não possui a Dimensão Produto, nenhuma abordagem conseguirá realizar o cruzamento de metas com produtos de forma precisa. Na abordagem de [Tabela Fato Única](#fato-unica-abordagem-alternativa), o comportamento se manifesta de outra forma: as linhas de Metas teriam `Produto = vazio`, e ao filtrar por um produto específico, as metas seriam excluídas do resultado. A recomendação é a montagem de Relatórios que cruzam múltiplas Tabelas Fato, utilizando apenas as Dimensões compartilhadas entre elas.
Execute o exemplo abaixo para ver como e por que as linhas duplicam — e como evitar:
***
## Gerando uma Tabela Calendário
É possível criar uma Tabela Calendário no **HorusETL** utilizando o nó [Python (Pandas)](/etl/processors/transforms/python). O script abaixo é um exemplo de como gerar um Calendário completo dos últimos 5 anos:
```python
import pandas as pd
from datetime import datetime, timedelta
# Configuração: últimos 5 anos até hoje
end_date = datetime.now().date()
start_date = end_date - timedelta(days=5*365)
# Gera range de datas
dates = pd.date_range(start=start_date, end=end_date, freq='D')
# Cria DataFrame do calendário
df = pd.DataFrame({'DATA': dates})
# Adiciona colunas úteis para análise
df['ANO'] = df['DATA'].dt.year
df['MES'] = df['DATA'].dt.month
df['DIA'] = df['DATA'].dt.day
df['DIA_SEMANA'] = df['DATA'].dt.day_name()
df['DIA_SEMANA_NUM'] = df['DATA'].dt.dayofweek + 1 # 1=Segunda, 7=Domingo
df['SEMANA_ANO'] = df['DATA'].dt.isocalendar().week.astype(int)
df['TRIMESTRE'] = df['DATA'].dt.quarter
df['SEMESTRE'] = ((df['DATA'].dt.month - 1) // 6) + 1
df['ANO_MES'] = df['DATA'].dt.to_period('M').astype(str)
df['FIM_SEMANA'] = df['DIA_SEMANA_NUM'].isin([6, 7])
# Formata a coluna DATA para o padrão do DW
df['DATA'] = df['DATA'].dt.strftime('%Y-%m-%d')
# Converte nomes dos dias para português
dias_pt = {
'Monday': 'Segunda-feira',
'Tuesday': 'Terça-feira',
'Wednesday': 'Quarta-feira',
'Thursday': 'Quinta-feira',
'Friday': 'Sexta-feira',
'Saturday': 'Sábado',
'Sunday': 'Domingo'
}
df['DIA_SEMANA'] = df['DIA_SEMANA'].map(dias_pt)
# Converte colunas para MAIÚSCULO (convenção HorusDW)
df.columns = [col.upper() for col in df.columns]
print(f"Calendário gerado: {len(df)} dias ({start_date} até {end_date})")
```
> \[!TIP]
> O script pode ser adaptado para incluir feriados, anos fiscais personalizados, ou qualquer outra lógica específica do negócio.
***
## Tabela Fato Única (Abordagem Alternativa)
Outra estratégia para evitar explosão cartesiana é **unificar múltiplas Tabelas Fato em uma única Tabela**. Por exemplo, ao invés de relacionar Vendas e Metas através de Dimensões, os dados são centralizados em uma Fato Única.
Essa abordagem é especialmente útil quando:
* As Tabelas possuem **granularidades diferentes** (vendas diárias vs. metas mensais)
* O objetivo é simplificar o modelo, eliminando Relacionamentos complexos
* As Tabelas têm estruturas muito semelhantes
**Como funciona:**
Alinhe as colunas em comum, e então preencha as colunas específicas de cada origem com valores neutros (`0` ou `null`):
```sql
SELECT 'Venda' AS FATO, DATA, PRODUTO, NUMERO_NF, VALOR AS VALOR_VENDA, 0 AS VALOR_META
FROM VENDAS
UNION ALL
SELECT 'Meta' AS FATO, DATA, PRODUTO, NULL AS NUMERO_NF, 0 AS VALOR_VENDA, META AS VALOR_META
FROM METAS
```
O resultado é uma Tabela unificada:
| FATO | DATA | PRODUTO | NUMERO\_NF | VALOR\_VENDA | VALOR\_META |
|------|------|---------|-----------|-------------|------------|
| Venda | 2024-01-15 | Notebook | NF-001 | 3500 | 0 |
| Venda | 2024-01-15 | Monitor | NF-002 | 800 | 0 |
| Meta | 2024-01-01 | Notebook | *null* | 0 | 5000 |
| Meta | 2024-01-01 | Monitor | *null* | 0 | 1000 |
Com essa estrutura, é possível criar Expressões simples:
```sql
-- Total de vendas
SUM([FatoUnificada].VALOR_VENDA)
-- Total de metas
SUM([FatoUnificada].VALOR_META)
-- % de meta atingida
SUM([FatoUnificada].VALOR_VENDA) / SUM([FatoUnificada].VALOR_META) * 100
```
> \[!TIP]
> No **HorusETL**, utilize o nó [Concatenar (Union)](/etl/processors/transforms/union) para unir os fluxos. Ele alinha automaticamente as colunas por nome e preenche com `null` as colunas que existem apenas em um dos inputs. Para transformações SQL mais avançadas, utilize o nó [SQL (DuckDB)](/etl/processors/transforms/sql-duckdb).
Teste a abordagem completa — do `UNION ALL` até os cálculos de meta:
### Quando usar cada abordagem?
| Abordagem | Vantagens | Desvantagens |
|-----------|-----------|--------------|
| **Dimensões Compartilhadas** | Modelo mais flexível e extensível; cada Tabela Fato mantém suas colunas específicas | Requer criar e manter Tabelas Dimensão (Calendário, etc.) |
| **Tabela Fato Única** | Modelo mais simples; facilita cálculos entre as origens | Menos flexível para adicionar novas fontes; colunas específicas ficam esparsas |
---
---
url: 'https://docs.horusbi.com.br/dataviz/04-features/apresentacoes/kiosk.md'
---
# Modo Kiosk (Apresentação Pública)
Uma Apresentação publicada gera um **link público**, ideal para deixar rodando sozinha numa TV de fábrica, escritório ou sala de reunião — sem precisar de ninguém logado controlando.
***
## 🔗 Publicando
1. Abra a Apresentação
2. Clique em **Publicar**
3. Um **link público** é gerado — abra esse link na TV (Chromecast, Smart TV, notebook conectado) em modo tela cheia
> \[!TIP]
> Deixe o navegador da TV em modo tela cheia (`F11`) apontando para o link publicado. A playlist roda sozinha e se atualiza conforme os dados mudam.
***
## 🧩 Misturando Dashboards e Decks
A mesma playlist pode combinar os dois tipos de item — Dashboards e Decks (Slides Desenhados) — na mesma rotação:
| Item na playlist | Comportamento na rotação |
|---|---|
| **Dashboard** | Fica exibido pelo tempo configurado, depois passa para o próximo item da playlist |
| **Deck (Slides Desenhados)** | Avança **automaticamente** pelos próprios slides internos; ao terminar o último slide, a playlist passa para o próximo item |
Isso permite, por exemplo, uma TV que mostra três dashboards operacionais e, na sequência, um Deck com o resumo do mês antes de voltar ao início da playlist.
> \[!IMPORTANT]
> Misturar Decks na playlist depende de Slides Desenhados, que está em **fase beta**.
***
## ⏱️ Tempo de exibição e resolução
Ao montar a Apresentação:
1. Adicione os itens (Dashboards e/ou Decks) e defina a **ordem**
2. Defina o **tempo de exibição** de cada Dashboard (ex.: 60 segundos) — Decks usam o próprio ritmo interno dos slides
3. Escolha a **resolução base** (Full HD, 4K) para a escala ficar correta na tela grande
***
## ▶️ Modos de exibição
* **Automático** — a playlist roda sozinha, trocando de item conforme o tempo configurado (e, nos Decks, conforme os slides avançam)
* **Manual** — quem está apresentando controla a troca, útil em reuniões onde a discussão em um item pode demorar mais que o previsto
***
## 🔌 Dados ao vivo numa TV sem supervisão
Como o Kiosk fica rodando sem ninguém acompanhando, vale pensar na configuração de dados de cada Gráfico BI dentro dos Decks:
* **Ao vivo** — a TV sempre mostra o número mais recente, ideal para monitoramento contínuo
* **Congelado** — o número fica fixo desde quando foi congelado, útil se a rede da TV for instável ou se você quiser garantir consistência num período específico (ex.: fechamento do mês)
Veja o detalhe em **[Gráfico BI — Dados ao vivo vs. congelado](./grafico-bi.md#dados-ao-vivo-vs-congelado)**.
***
## 🗺️ Próximos passos
* **[Editor de Slides Desenhados](./editor.md)** — monte o Deck que vai entrar na playlist.
* **[Gráfico BI](./grafico-bi.md)** — configure ao vivo/congelado antes de publicar.
* **[Gerar Apresentação com IA](./ia.md)** — monte o Deck rapidamente antes de publicar na TV.
---
---
url: 'https://docs.horusbi.com.br/dataviz/04-features/indicadores/monitoramento.md'
---
# Monitoramento: o vigia
Cada indicador tem um vigia: o Lumo observa o histórico, aprende sozinho o que é o comportamento normal daquele número e avisa quando ele sai do padrão. Você não precisa configurar limites, metas ou regras para isso funcionar.
::: tip Em uma frase
O vigia aprende a faixa de valores esperada do seu indicador a partir do próprio histórico e acende amarelo ou vermelho quando o valor real foge dela.
:::
***
## Como o Lumo aprende o "normal"
O vigia constrói uma **faixa esperada** de forma estatística, olhando o histórico do indicador:
* Ele identifica o **valor típico** de cada período e o quanto o número **costuma oscilar** em torno dele. A faixa esperada cobre essa oscilação natural: variação do dia a dia fica verde, um desvio real dispara o aviso.
* Dias atípicos isolados (um pico ou um buraco pontual) **não distorcem a faixa**. O cálculo é feito para resistir a valores extremos.
* O período ainda em andamento (o dia de hoje, a hora atual) **fica de fora do aprendizado**. Um total parcial nunca contamina a régua que vai julgá-lo.
### Perfil do negócio
Quando há dados suficientes, a faixa deixa de ser uma régua única e passa a respeitar o **ritmo do seu negócio**:
* No grão diário, cada dia da semana tem a sua própria faixa: a terça-feira de hoje é comparada com as terças passadas, não com o domingo.
* No grão por hora, o vigia combina dia da semana e horário (madrugada, manhã, tarde, noite; com bastante histórico, hora a hora).
Se algum recorte ainda não tem histórico suficiente, o vigia usa a faixa geral para aquele recorte, sem inventar precisão que os dados não sustentam.
### Quanto histórico é preciso
No grão diário, o vigia precisa de **cerca de quatro semanas de dias completos** para publicar a primeira faixa (o mínimo para separar o efeito de dia de semana e fim de semana). Os outros grãos têm mínimos equivalentes. Antes disso, o indicador fica no estado **"aprendendo"**.
***
## Os estados
| Estado | O que significa |
|---|---|
| **Verde (ok)** | O valor está dentro da faixa esperada. Estar fora da faixa **para o lado bom** (por exemplo, vendas acima do normal) também é verde: um resultado excepcionalmente bom não é um problema. |
| **Amarelo (atenção)** | O valor saiu da faixa para o lado ruim, por pouco. |
| **Vermelho (crítico)** | O valor saiu da faixa para o lado ruim, com folga. |
| **Aprendendo** | Ainda não há faixa. Acontece em duas situações: histórico insuficiente, ou um valor tão estável que não há oscilação mensurável para aprender. |
O que é "lado bom" e "lado ruim" depende da **direção** configurada no indicador (veja [Ajustando o monitoramento](#ajustando-o-monitoramento) abaixo).
O estado oficial de cada indicador é reavaliado **de hora em hora**. É essa reavaliação que registra as mudanças de estado e dispara os avisos no sino.
***
## A aba Monitoramento no detalhe
No detalhe do indicador, a aba **Monitoramento** mostra o trabalho do vigia:
* **Veredito**: uma frase direta sobre o período atual. Exemplo: "Hoje (terça), R$ 48.200, dentro do normal (R$ 41.000 a R$ 55.000)". Abaixo, o contexto do aprendizado ("Observando dia a dia, aprendendo com 12 meses") e a última mudança de estado, quando houve.
* **O gráfico do vigia**: a série do indicador com a **faixa esperada sombreada** ponto a ponto. Cada ponto usa a faixa do seu próprio recorte (a faixa de sábado é diferente da de segunda), pontos fora da faixa aparecem em amarelo ou vermelho, e períodos ainda em andamento ficam esmaecidos, sem julgamento.
* **Desvios recentes**: a lista dos pontos que saíram da faixa, do mais recente para o mais antigo, com o tamanho do desvio ("18% acima do normal") e a faixa esperada. Clicar em um desvio destaca o ponto no gráfico. Quando não há desvios: "Nenhum desvio no período observado. Bom sinal."
* **O padrão do seu negócio**: um gráfico de barras com o valor típico de cada recorte (dia da semana, horário), com um resumo do tipo "Sexta costuma ser o ponto mais alto; domingo, o mais fraco".
::: info Se o indicador usa faixas manuais
A análise automática continua rodando e aparece nesta aba como contexto, com um aviso de que o semáforo oficial segue as regras manuais.
:::
***
## Ajustando o monitoramento
O botão **"Ajustar monitoramento"** (ou a aba **Configuração → Monitoramento**) abre os controles do vigia.
### Direção: subir é bom ou ruim?
O controle "Quando este indicador sobe, isso é…" aceita três respostas:
* **Bom**: quanto maior, melhor (vendas, margem).
* **Ruim**: quanto menor, melhor (custo, inadimplência, cancelamentos).
* **Neutro**: sem lado bom ou ruim (qualquer desvio merece atenção).
A direção orienta tanto o vigia automático (desvio para o lado bom continua verde) quanto a cor da variação nos cards.
### Grão e janela
No modo automático, o link **"ajustar"** revela dois controles:
* **Grão**: hora, **dia (padrão)**, semana ou mês. Define o ritmo em que o vigia observa o indicador.
* **Janela**: quanto histórico entra no aprendizado. A opção **"Automática"** usa o padrão recomendado para o grão; as demais opções vão de 2 semanas a 3 anos, conforme o grão.
Na dúvida, deixe os dois no padrão. Grão diário com janela automática cobre a grande maioria dos indicadores de negócio.
### Faixas manuais (régua manual)
A régua manual é opcional. Se você prefere definir os limites em vez de deixar o Lumo aprender, troque o modo de **"Automático"** (recomendado) para **"Faixas manuais"**. Nesse modo você define dois limiares de **variação percentual** vs o ciclo anterior:
* **Limiar verde (%)**: a variação mínima para o indicador ficar verde.
* **Limiar âmbar (%)**: até onde a variação ainda é amarela. Abaixo (ou acima, na direção "Ruim") disso, vermelho.
Com faixas manuais ativas, o semáforo oficial segue as suas regras. O vigia automático continua aprendendo em segundo plano e a aba Monitoramento mostra a análise dele como contexto.
***
## Avisos no sino
Cada indicador tem, na aba Configuração → Monitoramento, o bloco **"Para você"** com a chave **"Avisar no sino quando mudar de faixa"**:
* A preferência é **sua**, por indicador: desligar o aviso para você não afeta os outros seguidores.
* Vem **ligada por padrão** para quem segue o indicador. Quem não segue não recebe avisos (e a chave só aparece para seguidores).
* Os avisos cobrem as três transições que importam: entrou em **atenção** (amarelo), entrou em estado **crítico** (vermelho) e **voltou ao normal** (verde).
* O sino não repete o mesmo aviso em sequência: se o indicador continua vermelho hora após hora, você recebe um único aviso e a plataforma espaça as repetições.
Clicar no aviso abre o detalhe do indicador com o "Por quê?" para você entender o que mudou.
***
## Indicador sem coluna de data (indicador-foto)
Alguns indicadores medem um **estado atual**, sem linha do tempo na fonte: saldo em carteira, posições em aberto, itens em estoque. Ao criar um indicador sem coluna de data, o Lumo trata ele como uma **foto do momento**:
> "A fonte deste indicador guarda apenas o valor de agora. O Lumo vai registrar esse valor todos os dias e montar o histórico aos poucos, até liberar o acompanhamento automático."
Na prática:
* O Lumo **registra o valor uma vez por dia** e vai acumulando as fotos. A aba Monitoramento mostra o progresso: "Registrando o valor diariamente desde 12/06. 29 fotos coletadas."
* O **gráfico nasce das fotos**: com poucas fotos você já vê a linha se formando, e a análise visual melhora conforme elas acumulam.
* O **acompanhamento automático** (faixa esperada, estados, avisos no sino) libera quando o histórico coletado atinge o mínimo do vigia, cerca de quatro semanas de fotos no grão diário.
::: info Avisos automáticos chegam depois
Enquanto o histórico é coletado, a análise desse tipo de indicador é visual: você acompanha a linha das fotos no gráfico. As notificações automáticas passam a valer quando o vigia tiver histórico suficiente para aprender a faixa.
:::
::: tip Siga para começar a coletar
O registro diário acontece para indicadores que têm seguidores. Se o histórico ainda não começou, siga o indicador: é isso que coloca ele na rota do vigia.
:::
***
## Próximos passos
* **[Visão geral dos Indicadores](./index.md)**: o cockpit, criação, curadoria e o papel dos indicadores na IA.
* **["Por quê?"](./index.md#por-que-explicacao-por-ia)**: a explicação por IA quando um indicador muda de estado.
---
---
url: 'https://docs.horusbi.com.br/dw/architecture/engine.md'
---
# O Motor do Horus
O HorusDW opera com um **motor híbrido** projetado para equilibrar velocidade de consulta com custo de armazenamento. Abaixo, os dois componentes principais que trabalham nos bastidores para transformar dados brutos em informações prontas para consumo.
***
## ❄️ Cold Storage (Datalake & Camadas)
O "Lago de Dados" é a fundação de todo o armazenamento — é onde a Engenharia de Dados acontece.
* **O que é** — Repositório de arquivos (Parquet) na nuvem, com custo baixo e capacidade de armazenamento escalável
* **Tratamento em Camadas** — O Datalake é ideal para organizar os dados em estágios de qualidade (*Bronze* para dados brutos, *Prata* para dados limpos)
* **Acesso** — No Horus, você visualiza essas pastas como "Tabelas Cloud". A estrutura é visível, mas os dados permanecem frios até serem solicitados para consulta
***
## 🔥 Hot Storage (Modern Lakehouse)
Quando o dado está pronto para o usuário final, ele é trazido para o Hot Storage — a camada de alta performance.
* **O que é** — Uma arquitetura de **Lakehouse Moderno** que combina a performance de bancos de dados analíticos com a flexibilidade do lago de dados
* **Origem Principal** — Alimentado automaticamente pelo **HorusETL**, que lê do Datalake, aplica as regras de negócio mais recentes e entrega o dado "quente" para consumo
* **Outras Origens** — Arquivos Excel pequenos (uploads manuais) também entram diretamente nesta camada
* **Vantagem** — Velocidade de resposta para Dashboards e consultas complexas
---
---
url: 'https://docs.horusbi.com.br/etl/processors/outputs.md'
---
# Outputs (Destinos e Escrita)
Os processadores de Output finalizam o fluxo (ou uma ramificação dele), enviando os dados processados para um local de armazenamento permanente ou para visualização.
***
## 💾 Armazenamento
* **[Datawarehouse](./datawarehouse.md)** — Escreve os dados processados nas tabelas do Data Warehouse do Horus. **Este é o destino mais comum para BI**
* **[Google Sheets](./google-sheets.md)** — Escreve os dados em uma planilha do Google
* **[Parquet](./parquet.md)** — Salva os dados em disco local no formato Parquet
---
---
url: 'https://docs.horusbi.com.br/hec/resources/cadastros-permissoes.md'
---
# Permissões de Cadastro
> **Caminho**: HEC > Usuários/Grupos (editar) · ou DW > Minhas Tabelas > Gerenciar
O acesso aos **dados** de um Cadastro é controlado por **cinco permissões** independentes — você decide, para cada usuário ou grupo, o que ele pode **ler, criar, atualizar, excluir e importar**. Assim, a mesma tabela pode ser só-leitura para uns e totalmente editável para outros.
## 🔐 As 5 permissões
Cada permissão libera uma ação específica sobre os **dados** (as linhas) do Cadastro:
| Permissão | O que libera |
|-----------|--------------|
| **Ler** | Visualizar os dados — abrir a grade e os registros do Cadastro. É a base: sem **Ler**, o usuário nem enxerga o Cadastro. |
| **Criar** | Adicionar novos registros (novas linhas), pela grade ou pelo formulário. |
| **Atualizar** | Editar registros existentes — alterar valores de campos nas linhas já cadastradas. |
| **Excluir** | Remover registros do Cadastro. |
| **Importar** | Carregar dados em massa via importação de planilha (XLSX), nos modos Adicionar ou Substituir. |
> \[!NOTE]
> **Exportar** não é um flag específico de Cadastro: usa a **permissão de exportação genérica** já existente na plataforma (a mesma que controla a exportação em outras telas). Por isso ela não aparece na lista acima.
Essas permissões são cumulativas e independentes — você pode, por exemplo, dar **Ler** e **Atualizar** sem dar **Criar** ou **Excluir**, formando perfis de acesso sob medida.
## 🖥️ Onde conceder
As permissões de Cadastro podem ser concedidas em **duas telas**, conforme quem está no comando:
| Tela | Quem usa | Como funciona |
|------|----------|---------------|
| **DW > Minhas Tabelas > Gerenciar** | O **dono** do Cadastro | O dono libera o acesso diretamente ou aprova uma solicitação de acesso feita por outro usuário, marcando quais das cinco permissões cada um recebe. |
| **HEC > Usuários/Grupos (editar)** | O **administrador** do tenant | Ao editar um usuário ou um grupo, o admin define as permissões de Cadastro junto das demais permissões de acesso a dados. |
> \[!IMPORTANT]
> Os toggles das cinco permissões (Ler, Criar, Atualizar, Excluir, Importar) **só aparecem quando a tabela é um Cadastro e o acesso a ela já está liberado** para aquele usuário ou grupo. Em tabelas comuns — ou enquanto o acesso ao Cadastro não foi concedido — os toggles não são exibidos.
## 🧱 Dado é diferente de estrutura
::: info
Permissão de **dado** (as cinco da tabela acima) é independente da permissão de **estrutura**.
* **Dado** = as linhas: ler, criar, atualizar, excluir e importar registros. É o que você concede aqui.
* **Estrutura** = a definição do Cadastro: criar/alterar campos, tipos, validações, expressões e unicidade. Isso é feito **apenas no DW** e exige ser **dono do Cadastro ou administrador** — não é liberado por estes toggles.
Ou seja: dar a alguém permissão para **editar os dados** não dá a ele permissão para **mudar a estrutura** do Cadastro.
:::
## 🔗 Veja também
* **[Cobrança e limites](/hec/resources/cadastros-cobranca)** — quanto cada Cadastro pode crescer (limite de linhas por tenant).
* **[Inserir e editar dados](/dw/tables/cadastros/dados)** — a grade e o formulário que essas permissões liberam.
* **[Importar e exportar](/dw/tables/cadastros/importar-exportar)** — o que a permissão **Importar** habilita.
* **[Cadastros no DataViz](/dataviz/04-features/cadastros/)** — editar dados conforme as permissões, sem acesso ao DW.
---
---
url: 'https://docs.horusbi.com.br/dataviz/04-features/alerts/permissions.md'
---
# Permissões e Compartilhamento
O acesso e o controle dos alertas variam conforme o **papel do usuário** no sistema e a **relação com o alerta criado**. Esta página detalha quem pode fazer o quê.
***
## Papéis
### Criador do Alerta (Dono)
Quem criou o alerta.
| Pode | Não pode |
|---|---|
| ✅ Editar todos os campos | |
| ✅ Excluir | |
| ✅ Forçar envio manual | |
| ✅ Ver histórico de entregas | |
| ✅ Adicionar/remover destinatários | |
O controle é **permanente**: mesmo que um Administrador edite o alerta posteriormente, o criador **não perde a autoria** nem o controle.
### Administrador do Tenant
Usuários com perfil de Admin (ver **HEC > Grupos > Permissões**).
| Pode | Não pode |
|---|---|
| ✅ Editar qualquer alerta do tenant | ❌ Transferir autoria para outro usuário |
| ✅ Excluir qualquer alerta | |
| ✅ Forçar envio | |
| ✅ Ver histórico de qualquer alerta | |
> \[!NOTE]
> Administradores **podem** editar alertas que não criaram. A autoria não muda, quem criou continua dono. O Admin é uma autoridade adicional, não substitutiva.
### Destinatário (recebe mas não criou)
Usuário que está na lista de destinatários mas não é dono nem admin.
| Pode | Não pode |
|---|---|
| ✅ Ver o alerta na lista | ❌ Editar |
| ✅ Receber as entregas | ❌ Excluir |
| ✅ Pedir pra ser removido (via dono ou admin) | ❌ Forçar envio |
### Outros usuários do tenant
Nem dono, nem admin, nem destinatário.
| Pode | Não pode |
|---|---|
| | ❌ Ver |
| | ❌ Editar |
| | ❌ Receber |
Alertas são **privados** por padrão. Não há "alertas públicos do tenant".
***
## Função "Alerta" (Permissão de Criação)
A criação de alertas é controlada por uma função no sistema de permissões. Em **HEC > Grupos > Permissões**, a função "Alerta" tem 4 sub-permissões:
| Sub-permissão | Significado |
|---|---|
| **Criar** | Criar novos alertas |
| **Ler** | Listar e ver alertas próprios + onde é destinatário |
| **Editar** | Editar alertas próprios (admin pode tudo) |
| **Excluir** | Excluir alertas próprios (admin pode tudo) |
> \[!TIP]
> Para um perfil "consumidor de alertas" (recebe mas não cria), dê apenas "Ler". O usuário aparece como destinatário válido em alertas criados por outros, mas não pode criar os próprios.
***
## Destinatários
### Quem pode ser destinatário?
* **Usuários ativos do tenant** (do mesmo tenant)
* **Grupos do tenant** (todos os membros ativos do grupo viram destinatários)
* **Emails externos** (apenas para canal Email, e somente se a opção estiver habilitada nas configurações do tenant)
Os canais WhatsApp/Telegram/Webhook exigem cadastro do contato no perfil do usuário Lumo, não há "destinatário externo" para eles.
### Compartilhamento de Alertas
Por padrão, usuários comuns só podem criar **alertas privados** (só pra eles mesmos como destinatário). Para liberar o compartilhamento com outros usuários e grupos:
**Caminho:** `HEC > Tenants > Avançado` → ativar **"Permitir usuários não administradores de compartilhar alertas"**.
Sem isso ativo, apenas Administradores podem criar alertas com múltiplos destinatários.
### Grupos vs Usuários Diretos
Adicionar um **grupo** como destinatário expande dinamicamente: se você adiciona "Grupo Vendas" e amanhã chega um novo vendedor no grupo, ele recebe automaticamente sem editar o alerta. Adicionar **usuários diretos** é estático.
### Limite de Destinatários (IA)
Alertas com **bloco de IA** têm limite de **50 destinatários únicos** (somando usuários diretos + membros de grupos). Para volumes maiores, considere:
* Quebrar em alertas separados por segmento
* Usar [agrupamento condicional](./conditions.md#agrupamento-uma-entrega-por-grupo), que faz uma entrega por grupo
* Trocar IA por bloco de [Texto + variáveis](./content-text.md) (sem limite)
***
## Acesso a Aplicações Referenciadas
Um alerta pode referenciar várias aplicações nos seus blocos de conteúdo. A autoria do alerta funciona assim:
* **No momento da criação/edição**: o criador precisa ter acesso às aplicações referenciadas.
* **No momento do disparo**: o Lumo **revalida** os acessos do criador. Aplicações que o criador perdeu acesso (revogação, mudança de grupo) são removidas silenciosamente do contexto da IA, não entram nos filtros do relatório.
> \[!WARNING]
> Se o criador perder acesso a **todas** as aplicações de um bloco de IA, esse bloco entrega uma mensagem de erro em vez da análise. Verifique permissões depois de mudanças no HEC.
### Destinatário não precisa de acesso às aplicações
O destinatário **recebe o conteúdo gerado** mesmo sem ter acesso às aplicações. A geração roda no contexto do **criador**. Isso permite cenários como "diretor recebe insights de vendas sem ter login no app de vendas".
***
## Transferência de Autoria
Não há interface para transferir autoria. Se o criador sai da empresa e o alerta precisa continuar:
1. **Admin edita o alerta** (a autoria continua com o antigo criador).
2. Se o antigo criador for desativado, o alerta entra em estado degradado (não há revalidação possível das aplicações). **Recomendação**: recriar o alerta com o novo dono e arquivar/desativar o anterior.
***
## Auditoria
Toda ação relevante gera log:
* **Criação** com data e dono
* **Edição** registrada (admin pode ver "última edição por X em Y")
* **Disparo** com status, canal, timestamp, erro se houver
* **Envio forçado** registrado com flag específica
Veja em **Alertas > Histórico** (modal do alerta).
***
## Próximos Passos
* Voltar para [Visão Geral](./index.md)
* Configurar [canais](./channels.md) considerando os destinatários
* Revisar [função Alerta no HEC](../../../hec/users-groups/permissions.md) se precisar ajustar quem cria/edita
---
---
url: 'https://docs.horusbi.com.br/hec/users-groups/permissions.md'
---
# Permissões e Segurança
> **Caminho**: Usuários > Permissões
O controle de segurança do HEC é dividido em três camadas complementares:
| Camada | Pergunta que responde |
|:-------|:----------------------|
| **Funções de Sistema** | O que eu posso **fazer**? (criar, editar, deletar) |
| **Acesso a Dados** | O que eu posso **ver**? (mesas, tabelas) |
| **Restrições** | Quais **linhas** de uma tabela eu posso visualizar? |
Esta página detalha as abas encontradas tanto na edição de **Usuários** quanto na de **Grupos**.
***
## 👁️ Acesso aos Dados
Esta aba define o escopo de visualização dos ativos de inteligência. É aqui que você controla **o que** cada pessoa ou grupo pode enxergar.
### 1. Mesas
Aqui os dois tipos de mesa se comportam de forma **oposta**:
* **Mesa de Aplicação — liberar acesso (allow-list)**: a mesa nasce **trancada**. Use **Adicionar Mesa** para que o usuário veja a Mesa de Aplicação e seus Dashboards. Sem essa liberação, ele não enxerga nada.
* **Mesa de Dados — bloquear acesso (deny-list)**: a Mesa de Dados nasce **aberta** (visível a todos). Aqui você não "libera" — você **bloqueia**: adicione a mesa à lista de bloqueio do usuário/grupo nesta mesma aba para que ele **deixe** de ver aquelas tabelas e dataflows.
* **Herdado**: Se uma mesa aparece com a observação `(Grupo X)`, significa que o acesso (ou o bloqueio) veio via grupo e não pode ser removido individualmente — para remover, edite o grupo ou desvincule o usuário dele
> \[!IMPORTANT]
> O **Admin do Tenant** faz bypass do allow-list das Mesas de Aplicação (vê todas), mas **honra o bloqueio explícito** — tanto a **Aplicação Bloqueada** quanto o bloqueio de uma **Mesa de Dados**. O bloqueio de dados vale para todos os papéis, sem exceção de admin. Modelo completo em **[Controle de Acesso às Mesas](/dw/desks/controle-de-acesso)**.
### 2. Restrições de Tabela
Crie regras granulares de segurança que controlam quais **linhas** de uma tabela o usuário pode ver.
* **Exemplo prático**: Um gerente regional deve ver a tabela "Vendas", mas apenas onde `Região == "Sul"`
* **Como Configurar**:
1. Clique em "Restringir Tabela".
2. Selecione a Tabela Alvo.
3. Configure o filtro (Campo `Filial` Igual a `Matriz`).
> \[!NOTE]
> As restrições são aplicadas diretamente no motor de dados (Data Warehouse). O usuário **não conseguirá** contorná-las via API ou exportação.
### 3. Aplicações Bloqueadas
Serve para criar exceções. Se você concedeu acesso a uma Mesa inteira, mas precisa esconder *um único Dashboard* específico dentro dela, use esta opção.
***
## ⚙️ Funções do Sistema
Define as **capacidades operacionais** do usuário na plataforma. É uma matriz onde você habilita ou desabilita verbos de ação (**Visualizar, Criar, Editar, Deletar, Publicar, Bloquear**) para cada módulo do sistema.
### Principais Módulos
| Nome na Interface | Descrição |
| :--- | :--- |
| **Aplicações** | Gestão de Dashboards e apps de BI. |
| **Tabelas** | Criação e edição de estruturas no Data Warehouse. |
| **Mesas de Dados** | Criação de novas áreas no DW. |
| **Mesas Publicadas** | Gestão de áreas de visualização no BI. |
| **Dataflows (ETL)** | Criação de fluxos de dados no ETL. |
| **Ger. Dataflows (HEC)** | Administração avançada de fluxos. |
| **Ger. Usuários** | Permissão para criar/editar outros usuários. |
| **Ger. Grupos** | Permissão para criar grupos de acesso. |
| **Credenciais** | Acesso para configurar conexões de banco de dados. |
| **Datamarts** | Gestão de Datamarts. |
| **Armazenamento** | Gestão global de arquivos do tenant (tela Arquivos): listar, tornar público/privado, excluir, fazer upload. |
| **Alertas** | Configuração de envios automáticos. |
| **Templates** | Gestão de modelos de sistema. |
| **Admin Tenant** | Configurações globais da organização. |
| **Usar IA** | Permissão para utilizar recursos de Inteligência Artificial. |
> \[!NOTE]
> **A coluna "Publicar" é uma permissão concedível** — não um privilégio exclusivo de administrador. Quem a recebe pode publicar Aplicações/Tabelas, mas **só nas mesas às quais tem acesso**: o verbo libera a ação, e o acesso à mesa define o destino. Um analista sem papel administrativo pode receber "Publicar" e passar a publicar suas entregas dentro do seu escopo. Ver **[Controle de Acesso às Mesas](/dw/desks/controle-de-acesso)**.
> \[!TIP]
> **Atenção à coluna "Bloquear"**: Ela tem **precedência sobre todas as outras**. Se você marcar "Bloquear" no módulo **Ger. Usuários**, o usuário perderá acesso à tela de usuários — mesmo que faça parte de um grupo de "Admins". Use com cautela.
> \[!NOTE]
> **Arquivos de ETL** (CSV/Excel estáticos usados em Dataflows) **não** são controlados por "Armazenamento" — eles são recurso do workspace de ETL e seguem a função **Dataflows (ETL)** (`flow`): quem cria/edita flow vê e usa esses arquivos. A função **Armazenamento** governa a tela global de Arquivos (todos os tipos), não o uso dentro do editor de ETL.
---
---
url: >-
https://docs.horusbi.com.br/dataviz/04-features/apresentacoes/personalizar-grafico.md
---
# Personalizando Gráficos no Slide
Um Gráfico BI no slide normalmente **espelha** um gráfico que já existe numa dashboard: você escolhe qual, e ele aparece ali, vivo.
Mas às vezes o gráfico da dashboard não é bem o que a apresentação pede. A barra deveria ser linha. A cor deveria ser a da marca do cliente. Falta uma série. Ou o gráfico que você quer **simplesmente não existe ainda**.
Para isso existem dois caminhos: **personalizar** um gráfico existente, e **criar um do zero** — os dois direto no slide, sem sair da apresentação e sem mexer na dashboard.
::: tip Nada disso altera a dashboard original
Ao personalizar, o slide passa a ter uma **cópia própria** do gráfico. A dashboard de onde ele veio continua exatamente como estava, e ninguém que a use vai notar diferença.
:::
## 🎨 Personalizar um gráfico existente
1. Clique no Gráfico BI do slide.
2. No painel lateral, clique em **Personalizar gráfico…**.
Abre um editor em duas colunas:
* **À esquerda**, o gráfico — **na mesma proporção que ele tem no slide**. É o que você vai ver na apresentação, não uma aproximação.
* **À direita**, os controles: tipo de gráfico, séries, dimensões, cores, formatação de número.
Mexeu num controle, o gráfico à esquerda **muda na hora**. Quando estiver bom, **Aplicar**.
O elemento passa a exibir um selo **Personalizado** no painel — o lembrete de que aquele gráfico agora vive no slide, não na dashboard.
### Voltar atrás
**Voltar ao original** descarta a personalização e traz de volta o gráfico como está na dashboard. Nada se perde de forma irreversível.
## ✨ Criar um gráfico do zero
Não precisa existir um gráfico pronto em lugar nenhum.
1. **Inserir → Gráfico BI**.
2. Escolha a aba **Criar do zero**.
3. Escolha a **aplicação** (de onde vêm os dados) e o **tipo de gráfico** (barra, linha, pizza, KPI, tabela…).
O gráfico nasce vazio no slide e o editor abre direto — é só desenhar: escolher os campos, agrupar, colorir.
::: info Se você cancelar
O gráfico **fica no slide**, vazio, do tipo que você escolheu. É só clicar em **Personalizar gráfico…** quando quiser voltar a configurá-lo. Nada é apagado sem você mandar.
:::
## 🧩 O que continua sendo do slide
Uma distinção que vale entender, porque evita confusão:
| O **editor de gráfico** cuida de… | O **painel do slide** cuida de… |
| --- | --- |
| tipo de gráfico, séries, dimensões | **título** (do painel, personalizado ou nenhum) |
| cores das séries, formatação de números | **fundo** (cartão, transparente ou cor) |
| agrupamentos, ordenação | **tema** (claro/escuro), **escala** |
| — | **filtros** aplicados àquele gráfico |
Ou seja: o **conteúdo** do gráfico é do editor; a **aparência dele dentro do slide** continua sendo do painel lateral, como em qualquer Gráfico BI.
Isso é o que permite, por exemplo, um gráfico totalmente personalizado ainda respeitar o **fundo transparente** do slide.
## ⚠️ Limitações conhecidas
* **O preview do editor é sempre claro.** Se o elemento estiver com tema escuro, o slide vai renderizar escuro corretamente — mas o preview dentro do editor aparece claro. É uma limitação técnica (o tema é global à janela do editor).
* **Congelar não funciona em gráfico personalizado.** Um gráfico personalizado não existe como gráfico salvo, e o congelamento depende disso. Ele continua funcionando **ao vivo**, normalmente.
## 🗺️ Próximos passos
* [Gráfico BI](./grafico-bi) — as opções do elemento no slide (título, fundo, tema, filtros)
* [Editor de slides](./editor) — o editor de apresentações
* [Modo Kiosk](./kiosk) — colocar a apresentação no telão
---
---
url: 'https://docs.horusbi.com.br/dataviz/03-widgets/pie.md'
---
# Pizza, Rosca e Funil
O Widget de **Pizza** (e suas variações, Rosca e Funil) é utilizado para mostrar a proporção de partes em relação a um todo. Ele é ideal para exibir como um valor total (Vendas Totais) é dividido entre categorias (por Região, por Categoria de Produto).
## 📊 Tipos de Visualização
Este Widget suporta três modos visuais distintos:
1. **Pizza (Pie)**: O clássico gráfico circular
* *Uso recomendado*: Para poucas categorias (2 a 5 fatias)
2. **Rosca (Donut)**: Similar à pizza, mas com o centro vazado
* *Uso recomendado*: Esteticamente mais moderno, ideal quando se quer destacar o todo
3. **Funil (Funnel)**: Forma triangular invertida
* *Uso recomendado*: Para etapas de processos sequenciais (Leads > Propostas > Vendas) onde há redução natural de volume em cada etapa
***
## ⚙️ Configuração de Dados
### Estrutura
* **Eixo (Dimensão)**: A categoria que dividirá as fatias (Marca)
* **Valor (Métrica)**: O número que define o tamanho da fatia (Valor Total)
* **Ordenação**: É possível ordenar as fatias (do maior para o menor ou por etapa, utilizando um campo extra)
### 🎨 Configuração Visual (Aba Visual)
#### Aparência Geral
* **Aparência**: `Padrão`, `Transparente` (sem fundo/borda) ou `Destacado` (com sombra)
* **Cor do Fundo**: Cor fixa ou dinâmica via expressão (na aparência Padrão)
* **Tipo de Gráfico**: Alternância rápida entre Pizza, Rosca e Funil
#### Rótulos de Dados (Data Labels)
Controle total do texto que será apresentado sobre cada fatia:
* **Mostrar Valor**: Exibe o número absoluto
* **Mostrar Percentual**: Exibe a representatividade da fatia
* **Mostrar Nome**: Exibe o nome da categoria (Eixo)
* **Posição**:
* `Interno`: Dentro da fatia (ideal para poucas fatias grandes)
* `Externo`: Fora do gráfico com linhas de chamada (ideal para fatias menores)
* **Formatação Numérica**: Forçar formato (Moeda, %, Inteiro, Reduzido) independente do banco de dados
#### Legenda
* **Mostrar Legenda**: Toggle para exibir ou ocultar a informação
* **Posição**: `Superior` ou `Inferior`
#### 🔢 Limitação de Resultados
Essencial para evitar gráficos de pizza ilegíveis com centenas de fatias finas.
* **Limitar em**: Define o número máximo de fatias (Top 10)
* **Mostrar fatia ("Outros")**: Agrupa todo o restante em uma única fatia cinza no final, garantindo que o total 100% seja preservado
#### Opções Específicas de Funil
* **Tamanho do Funil**: `Muito Pequeno`, `Pequeno`, `Médio`, `Grande`. Afeta a largura visual do cone
***
## 🎨 Cores e Estilos
É possível configurar as cores de duas formas principais:
1. **Cor por Eixo (Categoria)**:
* Use para fixar cores específicas para marcas ou status ("Aprovado" sempre verde, "Reprovado" sempre vermelho)
* É possível configurar manualmente a cor para cada item
2. **Cor por Expressão**:
* Permite lógica avançada via JavaScript
* **Exemplo**: `if (this.value > 1000) return 'green'; else return 'red';`
Para personalizar, clique no ícone de engrenagem ao lado da coluna selecionada.
***
## 🔻 Funil (Opções Específicas)
Quando o tipo **Funil** é selecionado:
* **Tamanho**: Controla a largura e o estreitamento do funil (Muito Pequeno a Grande)
* **Posição dos Rótulos**:
* `Externo`: Mostra linhas conectando o nome da etapa ao funil
* `Interno`: Tenta posicionar o texto dentro da área colorida
***
## 💻 Expressões Comuns
### Contexto de Coloração
Ao usar **Cor por Expressão**, acessam-se os dados da fatia específica que está sendo colorida.
| Variável | Descrição | Exemplo |
| :--- | :--- | :--- |
| `this.value` | Valor numérico da fatia (Métrica) | `if (this.value < 0) return 'red';` |
| `this.axis` | Nome da categoria da fatia (Dimensão) | `if (this.axis == 'Crítico') return 'red';` |
| `this.result` | Lista completa de dados do gráfico | `const total = this.result.reduce(...);` |
#### Exemplos Práticos:
**1. Colorir apenas uma categoria específica:**
```javascript
// Pinta de verde se for "Lucro", o resto fica cinza
if (this.axis == 'Lucro') {
return '#22c55e'; // Verde
}
return '#e5e7eb'; // Cinza
```
**2. Colorir baseado em meta (Valor):**
```javascript
// Vermelho se vendas forem menores que 1000
if (this.value < 1000) {
return '#ef4444';
}
return '#3b82f6'; // Azul
```
---
---
url: 'https://docs.horusbi.com.br/hec/resources/03-platform.md'
---
# Plataforma e Administração
O painel de **Plataforma e Administração** reúne os recursos avançados para gestão da estrutura hierárquica, limites de consumo e padronização de ambientes. Aqui você configura Clientes, Tenants, Templates e cotas de recursos.
## 🏢 Clientes
O **Cliente** é a entidade de nível mais alto na hierarquia — por exemplo, a sua empresa, parceiro revendedor ou holding.
* **Configurações Globais**: Aqui são definidas configurações visuais e técnicas que serão herdadas por todos os Tenants deste cliente
### Personalização (White Label)
Personalize a aparência da plataforma para que ela reflita a sua marca ou a do seu cliente.
* **Nome do Produto**: Altera o nome exibido na aba do navegador e em notificações (de "Horus" para "Lumo Analytics")
* **Logotipo**: Upload da imagem da sua marca
* **Favicon**: Ícone exibido na aba do navegador
* **Suítes**: Defina um branding personalizado para cada módulo
### 📧 Infraestrutura de E-mail (SMTP)
Por padrão, o sistema utiliza o gateway de e-mail da Horus. Para usar seu próprio remetente (`nao-responda@suaempresa.com.br`), configure o SMTP:
1. Acesse a aba **Avançado**.
2. Preencha os dados do servidor SMTP (Host, Porta, Usuário, Senha).
3. O sistema passará a enviar por este servidor:
* E-mails de "Esqueci minha senha"
* Convites de novos usuários
* Relatórios e alertas por e-mail
***
## 🏠 Tenants
**Tenants** são os ambientes isolados onde residem seus dados, usuários e configurações. Um Cliente pode ter múltiplos Tenants ("Produção", "Homologação", e tenants para clientes finais).
Para a documentação completa sobre todas as configurações disponíveis, consulte a página dedicada de **[Gestão de Tenants](tenants.md)**.
* **Status do Tenant**:
* **Ativo**: Operação normal
* **Trial**: Em período de degustação (com data de expiração)
* **Suspenso**: Acesso bloqueado (geralmente por questões administrativas ou de pagamento)
* **Herança**: Tenants herdam automaticamente as configurações de Logo e SMTP do Cliente pai, a menos que sejam sobrescritas nas configurações do próprio Tenant
***
## 📦 Templates
**Templates** permitem padronizar e distribuir conteúdo (Aplicações, Dataflows, Tabelas, etc.) para múltiplos tenants, criando **produtos de BI autocontidos** e escaláveis.
Para a documentação detalhada, consulte **[Templates e Linhas de Produto](./templates.md)**.
### Linhas de Produto
Agrupa templates relacionados ("Pacote Financeiro", "Pacote RH").
* Use para organizar e versionar seus produtos de dados
* Permite visualizar rapidamente quais tenants estão com versões desatualizadas
### Templates Órfãos
Templates que ainda não foram associados a uma Linha de Produto.
* Use esta tela para organizar templates soltos e vinculá-los a grupos
* **Instalações**: Visualize em quais tenants o template foi instalado, a data e o responsável pela instalação
***
## 📊 Recursos do Tenant (Cotas)
O painel de controle de recursos computacionais de um tenant. Aqui você define os limites de consumo que cada ambiente pode utilizar.
### CNPJs
Cadastro das empresas fiscais associadas ao tenant.
* O número de CNPJs cadastrados **multiplica** a franquia base de recursos do tenant
* **Exemplo**: Se a franquia base é de 10 usuários e você cadastra 2 CNPJs, a franquia total passa a ser **20 usuários**
### Recursos Extras
Além da franquia base (multiplicada pelos CNPJs), é possível contratar recursos avulsos:
* **Usuários Extras**: Vagas adicionais para cadastro de usuários
* **Tokens de IA (10k)**: Pacotes de consumo para funcionalidades de Inteligência Artificial
* **Processamento/Storage (GB)**: Capacidade de processamento mensal e armazenamento para ETLs
> \[!TIP]
> **Visualização de impacto**: Ao editar os recursos, o sistema exibirá uma barra de "Impacto" projetando os novos limites. Ela alerta automaticamente caso os novos valores entrem em conflito com o uso atual (tentar reduzir o limite de usuários para um número menor do que os ativos).
---
---
url: 'https://docs.horusbi.com.br/dataviz/03-widgets/forecast.md'
---
# Previsão (Forecast)
O Widget **Previsão** utiliza inteligência artificial para analisar dados históricos e projetar tendências futuras. Ele é baseado na tecnologia **Prophet** (desenvolvida pelo Facebook/Meta), combinando estatística robusta com uma abordagem Bayesiana para gerar estimativas confiáveis de séries temporais.
## 🧠 Como Funciona
O sistema analisa automaticamente o histórico para identificar três componentes principais:
1. **Tendência**: A direção geral dos dados (crescimento ou queda) a longo prazo
2. **Sazonalidade**: Padrões que se repetem (vendas maiores no Natal, picos de acesso às segundas-feiras)
3. **Outliers**: O modelo é capaz de ignorar pontos fora da curva (dados atípicos) para não distorcer a previsão
> \[!TIP]
> O Widget gera também um **intervalo de confiança** (área sombreada), mostrando a margem de incerteza da previsão. Quanto mais estável for o histórico, menor será essa margem.
***
## ⚙️ Configuração
### 1. Aba Dados
* **Coluna de Período**: O campo de data que guiará a linha do tempo
* **Coluna de Valor**: A métrica que se deseja prever (Faturamento, Tickets)
* **Dias para Prever**: Quantos dias no futuro o modelo deve projetar (Máximo: 500)
* **Período a Buscar**: Quanto do histórico deve ser considerado para "treinar" a IA
* *Opções*: Padrão do Dashboard, Todos os Períodos ou janelas fixas (12/24 meses)
* *Recomendação*: Para capturar sazonalidade anual (Natal), use pelo menos **2 anos** de histórico
* **Filtros**: Contexto de dados específico para o Widget
***
### 🎨 Configuração Visual (Aba Visual)
Personalize a apresentação do gráfico de linha.
#### Aparência e Cores
* **Aparência**: `Padrão`, `Transparente` ou `Destacado`
* **Cores Personalizadas**:
* Linha de "Realizado" (passado)
* Linha de "Previsão" (futuro)
* Fundo do gráfico
* **Textos**: É possível renomear as legendas padrão de "Realizado" e "Previsão" para termos do negócio ("Vendas" e "Meta")
#### Eixos e Grade
* **Linhas de Grade**: Habilitar/desabilitar linhas verticais (X) e horizontais (Y) para limpar o visual
* **Escala Y**: Opção para mostrar ou ocultar os valores do eixo Y
* **Zeros**: Opção "Não mostrar valores zerados" para evitar linhas retas no chão do gráfico em dias sem operação
#### Rótulos e Legenda
* **Rótulos de Dados**: Mostrar valores diretamente sobre os pontos. Suporta formatação de número e moeda
* **Legenda**: Mostrar/Ocultar e posicionar (Topo ou Base)
#### 🧭 Navegação no Tempo
Escolha como o usuário interage com o zoom/tempo:
* **Barra de Navegação Inferior**: Um mini-gráfico interativo abaixo do principal para "pan & zoom" (estilo Stocks)
* **Botões de Período Superior**: Botões pré-definidos (1M, 6M, YTD, 1Y, Todas) no topo
* **Ambos**: Combina botões e barra de navegação
* **Apenas Zoom**: Permite arrastar e usar scroll do mouse para zoom
***
## 🔬 Detalhes Avançados (Modal)
Ao clicar no botão **Detalhes** no canto do Widget, uma janela se abre revelando a "caixa preta" do modelo. Isso ajuda a entender *por que* a previsão é essa.
1. **Tendência Detalhada**: Isola apenas o crescimento/queda, removendo o ruído diário
2. **Sazonalidade Semanal**: Mostra quais dias da semana performam melhor ("Sexta-feira tende a ser 20% acima da média")
3. **Sazonalidade Anual**: Mostra os picos e vales ao longo do ano ("Setembro é historicamente um mês fraco")
> \[!NOTE]
> Esses gráficos (Semanal/Anual) só aparecem se o modelo detectar evidências estatísticas suficientes desses padrões nos dados.
***
## ❓ FAQ Rápido
* **O sistema funciona com falhas nos dados?**
* Sim, o Prophet lida bem com dias sem informação
* **Por que a previsão suaviza meus picos?**
* O modelo tenta separar o que é padrão do que é ruído aleatório. Picos extremos não recorrentes são tratados como exceções para não viciar a previsão futura
* **O que muda com a abordagem Bayesiana?**
* Ela permite que o modelo incorpore incertezas. Ao invés de dizer "vai vender 100", ele diz "provavelmente entre 90 e 110"
---
---
url: 'https://docs.horusbi.com.br/dw/getting-started.md'
---
# Primeiros Passos
Comece sua jornada no HorusDW aprendendo a carregar e configurar seus dados. Os tutoriais abaixo cobrem desde o upload de uma planilha até a configuração de metadados e fórmulas.
1. 📤 **[Carregando Dados (Upload)](./upload)** — Aprenda a subir arquivos Excel, validar requisitos e gerenciar tabelas com múltiplos arquivos (Unificação)
2. ⚙️ **[Configurando a Tabela](./configuration)** — Entenda como ajustar metadados, definir chaves (Primary Keys) e criar Fórmulas (Expressões) para enriquecer sua análise
---
---
url: 'https://docs.horusbi.com.br/dataviz/01-getting-started.md'
---
# Primeiros Passos (Consumidor)
Bem-vindo ao **Horus DataViz**! Este guia é voltado para quem vai **consumir** dados e Dashboards existentes para apoiar decisões no dia a dia.
Aqui será possível aprender a navegar pela plataforma, encontrar as informações necessárias e interagir com as análises da organização.
***
## 🏠 1. A Tela Inicial (Home)
Ao acessar o DataViz, a plataforma direciona para a **Home** — o ponto de partida rápido:
* **Aplicações Recentes**: Os Dashboards mais acessados aparecem no topo para acesso imediato
* **Acesso Rápido**: Atalhos diretos para Mesas e conteúdos favoritos

***
## 🔍 2. Encontrando Aplicações
Se o Relatório procurado não está na Home, utilize as **Mesas** para localizá-lo.
1. No menu lateral ou na Home, clique em **Mesas**.
2. Serão exibidas todas as áreas de trabalho (Mesas) disponíveis ("Vendas", "Financeiro", "Logística").
3. Entre na Mesa desejada para ver a lista de **Aplicações** (conjuntos de Relatórios) disponíveis.
4. Clique na Aplicação para abri-la.

***
## 🧭 3. Navegando em uma Aplicação
Uma Aplicação é composta por visualizações organizadas em páginas (Dashboards) com ferramentas de interação integradas.
### Dashboards (Abas)
Uma Aplicação pode ter vários painéis. Eles ficam listados como **abas** no topo da tela. Clique nas abas para alternar entre os diferentes assuntos da análise (de "Visão Geral" para "Detalhes por Vendedor").


### Usando Filtros
A barra de filtros é a principal ferramenta de interação. Localizada no topo do Dashboard (ícone de funil), ela permite refinar os dados exibidos.
* **Filtros Globais**: Ao alterar um filtro (selecionar um "Ano" ou "Região"), **todos** os gráficos da tela são atualizados automaticamente para refletir a seleção
* **Adicionar Filtro**: Caso o filtro necessário não esteja visível, clique no botão de filtro (ícone de funil) para adicionar novos critérios à análise

### Interagindo com Gráficos
* Passe o mouse sobre os gráficos para ver **Tooltips**, caixas informativas com os números exatos de cada ponto
* A maioria dos gráficos possibilita **cross-filtering** (filtragem cruzada): ao clicar em uma barra, fatia ou ponto, o valor selecionado é adicionado como filtro, atualizando todos os demais widgets da tela

***
## ➡️ Próximos Passos
Agora que a navegação básica está clara, explore as Mesas da sua equipe!
Para **criar** novos Relatórios ou painéis, consulte o guia para criadores:
[Criando sua Primeira Aplicação](./create-app.md)
---
---
url: 'https://docs.horusbi.com.br/etl/getting-started.md'
---
# Primeiros Passos com HorusETL
Este guia levará você do zero à execução do seu primeiro fluxo de dados no HorusETL. Você aprenderá a configurar um **Agente**, conectá-lo à plataforma e criar um pipeline simples.
***
## 1. 📋 Pré-requisitos
O HorusETL utiliza um **Agente** (Engine) que roda na sua infraestrutura para processar os dados. Escolha o ambiente onde deseja instalá-lo:
### Ambiente Windows
* Sistema Operacional: Windows 10/11 ou Server 2016+ (x64)
* **Runtime**: [.NET 8 Runtime](https://dotnet.microsoft.com/en-us/download/dotnet/8.0) instalado
### Ambiente Linux (Docker)
* **Docker** instalado e rodando
* Conectividade de saída para a internet (HTTPS)
> \[!TIP]
> **Quanto de CPU e RAM?** Para a maioria dos cenários, **2 vCPU / 4 GB RAM** é o suficiente. Consulte [Requisitos de Hardware do Agente](../guides/requisitos-hardware) para dimensionar conforme paralelismo, volume e uso de Python.
***
## 2. 🔑 Criando um Agente (Token)
Antes de instalar o software, você precisa gerar um **Token** na plataforma. Esse token serve como a chave de identidade do seu Agente.
1. No menu lateral, navegue até **Agentes**
2. Clique no botão **Novo Agente**
3. Preencha as configurações iniciais:
* **Descrição** — Um nome para identificar onde este agente está rodando (`Servidor-Producao` ou `Meu-PC-Local`)
* **Agendamentos Simultâneos** — Define quantos fluxos podem rodar ao mesmo tempo (Padrão: 4)
4. Clique em **Salvar**
5. Após salvar, um campo **Token do Agente** será exibido
6. **Copie este Token** e guarde-o em local seguro (você precisará dele no próximo passo)
***
## 3. 💻 Instalação e Conexão
Agora vamos colocar o Agente para rodar.
### Opção A: Windows (Instalador MSI)
1. Faça o download do instalador **HorusETL** (o link geralmente está disponível na tela de detalhes do Agente)
2. Execute o instalador e siga os passos (Next, Next, Finish)
3. Após a instalação, abra o **Configurador do HorusETL** (atalho criado no Desktop ou Menu Iniciar)
4. Cole o **Token** copiado anteriormente no campo solicitado
5. Inicie o serviço
### Opção B: Linux (Docker)
Execute o seguinte comando no seu terminal, substituindo `SEU_TOKEN_AQUI` pelo token copiado:
```bash
docker run -d \
--name horus-etl \
--restart=always \
-e TOKEN=SEU_TOKEN_AQUI \
horusbi/etl:latest
```
> \[!TIP]
> **Verificação**: Volte para a tela de **Agentes** no navegador. Em alguns instantes, o indicador de status do seu agente deve ficar **Verde (Online)**.
***
## 4. 🎨 Criando seu Primeiro Dataflow
Com o agente online, vamos criar um fluxo simples que consulta uma API pública e salva o resultado.
1. Navegue até **Dataflows**
2. Clique em **Novo Flow**
3. Dê um nome ao seu fluxo (`Teste API GitHub`)
4. Certifique-se de que o **Agente** correto está selecionado (caso tenha mais de um)
### Desenhando o Fluxo
O editor funciona com "arrastar e soltar". Vamos usar dois nós:
1. **Origem**:
* No menu de ferramentas à esquerda, abra a categoria **Conexões**
* Arraste o nó **Requisição HTTP** para a área de desenho
* Clique no nó para configurá-lo
* **URL**: Digite `https://api.github.com/zen` (uma API simples que retorna uma frase de texto)
2. **Destino**:
* Abra a categoria **Destinos**
* Arraste o nó **Inserir Datawarehouse**
* Conecte a **saída** (bolinha direita) do nó *Requisição HTTP* na **entrada** (bolinha esquerda) do nó *Inserir Datawarehouse*
* Dependendo da configuração do seu ambiente, pode ser necessário selecionar uma tabela de destino válida nas propriedades deste nó
3. **Salvar**: Clique no botão **Salvar Flow** (ícone de disquete) na barra superior
***
## 5. ▶️ Executando e Monitorando
1. Na barra superior, clique no botão **Executar Tudo**
2. Aguarde a execução — você verá indicadores de status nos nós (rodando, sucesso ou erro)
3. **Verificando o Resultado**:
* Clique no nó **Inserir Datawarehouse**
* Verifique os logs na aba inferior (ou lateral) para confirmar se os dados foram processados
* Se houver erro (tabela não configurada), o nó ficará vermelho. Clique nele para ver a mensagem de erro detalhada
***
## 🔗 Próximos Passos
Agora que você já sabe o básico, explore a documentação completa dos [Processadores](../processors/) para criar fluxos mais complexos com Banco de Dados, Excel e transformações Python.
---
---
url: 'https://docs.horusbi.com.br/lumo/getting-started.md'
---
# Primeiros Passos com o Lumo CLI
O Lumo foi feito para ser operado por um **agente de IA** (Claude Code, Codex…). Seu papel é **preparar o terreno** — três passos que você faz uma vez, por fora — e a partir daí é só **conversar com a IA**, que comanda o `lumo` por você.
**O que você faz (uma vez):**
1. 💻 **Instalar** o `lumo`
2. 🔑 **Autenticar** (`lumo auth login`) — é a sua sessão
3. 🤖 **Instalar a skill** no seu agente
Depois disso: **[converse com a IA](/lumo/trabalhando-com-ia/)**. Ela inicializa o workspace, cria e edita os recursos, roda os flows e monta os dashboards.
> \[!NOTE]
> O acesso ao Lumo CLI é controlado por permissão. Um administrador precisa conceder a função **"Uso do Lumo CLI"** ao seu usuário antes que você consiga autenticar. Entre em contato com o administrador do tenant caso receba um erro de acesso negado.
> \[!TIP]
> Não conhece os termos do HorusBI (flow, tabela DW, mesa, tipo de carga)? Comece pela página de **[Conceitos](/lumo/conceitos/)** — ela liga cada recurso do Lumo à explicação completa do conceito.
***
## 1. 💻 Instalação
### Instalação rápida (recomendado)
O caminho recomendado em todas as plataformas é o **instalador remoto**: um comando que detecta seu sistema e arquitetura (amd64 ou arm64), baixa o binário, verifica o checksum e instala o `lumo` no seu `PATH`.
::: code-group
```bash [macOS / Linux]
curl -fsSL https://storage.horusbi.com.br/download/lumo/install.sh | bash
```
```powershell [Windows (PowerShell)]
irm https://storage.horusbi.com.br/download/lumo/install.ps1 | iex
```
:::
### Alternativa para Windows: instalador gráfico (.exe)
Se você prefere um instalador com duplo-clique em vez da linha de comando, baixe o **[LumoCLI-Setup.exe](https://storage.horusbi.com.br/download/lumo/LumoCLI-Setup.exe)** — ele coloca o `lumo` no `PATH` automaticamente.
> \[!NOTE]
> O instalador `.exe` é compilado **apenas para Windows x64**. Em **Windows ARM**, use a instalação rápida acima ou o pacote `LumoCLI-windows-arm64.zip` do download manual — ambos entregam o binário nativo arm64. Não use o instalador x64 no ARM: ele rodaria só sob emulação.
### Download manual
Prefere baixar o pacote e instalar à mão? Pegue o arquivo do seu sistema:
| Sistema | Download |
|---|---|
| **Linux** (amd64) | [LumoCLI-linux-amd64.tar.gz](https://storage.horusbi.com.br/download/lumo/LumoCLI-linux-amd64.tar.gz) |
| **Linux** (arm64) | [LumoCLI-linux-arm64.tar.gz](https://storage.horusbi.com.br/download/lumo/LumoCLI-linux-arm64.tar.gz) |
| **macOS** (Intel) | [LumoCLI-darwin-amd64.tar.gz](https://storage.horusbi.com.br/download/lumo/LumoCLI-darwin-amd64.tar.gz) |
| **macOS** (Apple Silicon) | [LumoCLI-darwin-arm64.tar.gz](https://storage.horusbi.com.br/download/lumo/LumoCLI-darwin-arm64.tar.gz) |
| **Windows** (amd64) | [LumoCLI-windows-amd64.zip](https://storage.horusbi.com.br/download/lumo/LumoCLI-windows-amd64.zip) |
| **Windows** (arm64) | [LumoCLI-windows-arm64.zip](https://storage.horusbi.com.br/download/lumo/LumoCLI-windows-arm64.zip) |
Extraia o pacote e coloque o binário `lumo` em um diretório presente no seu `PATH`. Para conferir a integridade, compare com o [`checksums.txt`](https://storage.horusbi.com.br/download/lumo/checksums.txt).
### Verificar a instalação
```bash
lumo --help
```
***
## 2. 🔑 Autenticação
Faça login com suas credenciais HorusBI:
```bash
lumo auth login
```
O CLI solicitará seu e-mail e senha interativamente. A sessão fica armazenada localmente.
> \[!IMPORTANT]
> **O login é seu e fica por fora da IA.** A IA não tem suas credenciais — ela apenas usa a sessão que você abriu aqui. Faça este passo você mesmo no terminal.
> \[!TIP]
> Em máquinas compartilhadas ou pipelines de CI/CD, defina a variável de ambiente `LUMO_PASSWORD` em vez de usar `--password` na linha de comando. Isso evita que a senha apareça no histórico do shell ou em logs de processo.
Para verificar o status da sessão:
```bash
lumo auth status
```
Para encerrar a sessão:
```bash
lumo auth logout
```
***
## 3. 🤖 Instale a skill no seu agente
A skill ensina o agente a operar o `lumo` corretamente. É o que permite pedir as coisas em linguagem natural.
Baixe o instalador de skills, rode, e ele detecta seu agente automaticamente — o passo a passo completo (Claude Code, Codex e instalação manual) está em **[Integrações com IA](/lumo/integracoes/)**.
> \[!TIP]
> Antes de instalar a skill, garanta que o `lumo` já está instalado e autenticado (passos 1 e 2). A skill **usa** o `lumo`; ela não o instala.
***
## 4. 💬 A partir daqui, é com a IA
Com CLI instalado, sessão ativa e skill no agente, abra o seu agente na pasta onde quer o workspace e **converse**. Você não precisa rodar comandos: a IA inicializa o workspace, cria e edita recursos, roda flows e monta dashboards.
Um bom primeiro pedido para confirmar que tudo está conectado:
> *Verifique minha sessão do Lumo, inicialize o workspace do tenant `` e liste meus flows.*
A partir daí, aprenda a colaborar bem com ela: **[Trabalhando com a IA](/lumo/trabalhando-com-ia/)** (o que é seu vs. da IA, dicas de prompt) e **[Revisando o trabalho da IA](/lumo/revisar/)** (como conferir o que ela fez).
***
## 5. ⚙️ Operação manual (referência)
Você normalmente **não** precisa desta seção — a IA roda estes comandos por você. Ela serve para quem quer operar à mão, automatizar em CI, ou apenas entender o que acontece nos bastidores.
Um workspace é um diretório local que espelha um tenant. O ciclo é:
```bash
lumo tenants list # ver tenants disponíveis
lumo init ./meu-tenant # criar o workspace (cria flows/, tables/, apps/…)
cd ./meu-tenant
lumo status # o que mudou localmente
# editar YAML em flows/, tables/, apps/, …
lumo lint flows/meu-flow.yaml # validar antes de enviar
lumo push # enviar ao servidor (reescreve o YAML com a resposta canônica)
lumo fetch # ver mudanças do servidor sem alterar arquivos
lumo pull # trazer mudanças do servidor para os arquivos
```
> \[!TIP]
> Use Git para versionar o workspace — é a rede de segurança para revisar e desfazer o que a IA faz. O CLI já ignora os arquivos internos; só os seus YAMLs e o `workspace.json` ficam versionados.
>
> ```bash
> cd ./meu-tenant
> git init && git add . && git commit -m "checkpoint: init tenant "
> ```
A referência completa de comandos e flags está em [Referência de Comandos](/lumo/comandos/).
***
## 6. ⬆️ Mantendo o CLI Atualizado
O Lumo verifica periodicamente se há uma versão nova disponível e exibe um aviso no stderr quando há:
```
⬆ lumo vX.Y.Z disponível (atual: vA.B.C) — rode: lumo update
```
Para atualizar:
```bash
lumo update
```
Para verificar se há atualização sem instalar:
```bash
lumo update --check
```
Se o diretório do binário exigir permissão elevada, o comando para e instrui a ação exata (ex.: `sudo lumo update`) com exit code 20.
Para silenciar o aviso de atualização, defina a variável de ambiente:
```bash
export LUMO_NO_UPDATE_NOTIFIER=1
```
***
## 🔗 Próximos Passos
* 💬 **[Trabalhando com a IA](/lumo/trabalhando-com-ia/)** — o que é seu vs. da IA, e como pedir as coisas.
* 🔍 **[Revisando o trabalho da IA](/lumo/revisar/)** — como conferir o que a IA produziu.
* 🧩 **[Conceitos](/lumo/conceitos/)** — flow, tabela DW, mesa, tipo de carga.
* 📖 **[Referência de Comandos](/lumo/comandos/)** — lista completa de comandos e flags.
---
---
url: 'https://docs.horusbi.com.br/etl/processors.md'
---
# Processadores (Nós)
Esta seção documenta todos os **Nós (Nodes)** disponíveis no editor de Dataflow do HorusETL. Os nós são os blocos de construção dos seus fluxos de integração — cada nó executa uma função específica, como ler um arquivo, transformar dados ou carregar informações em um banco.
***
## 📂 Categorias
Os processadores estão divididos em três categorias principais:
### 1. 📥 [Inputs (Leitura)](./inputs/)
Processadores responsáveis por **trazer dados** para o fluxo. São sempre o ponto de partida de um Dataflow.
* Exemplos: Consulta SQL, Requisição HTTP, Arquivo Excel, Google Sheets
### 2. 🔄 [Transformações (Lógica)](./transforms/)
Processadores que **modificam, enriquecem ou filtram** os dados que estão passando pelo fluxo.
* Exemplos: Join (Junção), Union, Python, SQL (DuckDB), Mapeamento
### 3. 📤 [Outputs (Escrita e Destinos)](./outputs/)
Processadores que **enviam os dados processados** para um destino final.
* Exemplos: Inserir no Datawarehouse, Salvar Parquet, Google Sheets
> \[!TIP]
> No editor, você pode arrastar e soltar esses nós da biblioteca lateral para a área de desenho (canvas). Clique em um nó para configurar suas propriedades.
---
---
url: 'https://docs.horusbi.com.br/dataviz/03-widgets/punchcard.md'
---
# Punchcard
O Widget **Punchcard** (ou Gráfico de Dispersão Temporal) é uma ferramenta poderosa para visualizar padrões de comportamento e densidade de eventos ao longo do tempo. Ele utiliza círculos de tamanhos variados para indicar "onde" e "quando" ocorre a maior concentração de uma determinada métrica.
## 📊 Comportamento
O gráfico cruza duas dimensões temporais (Hora do Dia x Dia da Semana) e plota um círculo na interseção.
* **Tamanho do Círculo**: Proporcional ao valor da métrica. Quanto maior o círculo, maior o valor (mais vendas, mais acessos)
* **Eixos**:
* **Eixo Y (Vertical)**: Sempre representa os **Dias da Semana** (Domingo a Sábado)
* **Eixo X (Horizontal)**: Varia conforme o tipo de gráfico (Horas ou Semanas)
É excelente para responder perguntas como: *"Qual o horário de pico da minha loja às segundas-feiras?"* ou *"Em quais semanas do ano tivemos mais chamados de suporte?"*.
***
## ⚙️ Configuração
### 1. Aba Dados
* **Tipo do Gráfico**:
* **Diário**: O Eixo X mostra as **Semanas do Ano**. Útil para ver sazonalidade ao longo de um ano inteiro, dia a dia
* **Horário**: O Eixo X mostra as **Horas do Dia** (0h - 23h). Útil para analisar a distribuição de carga horária ou picos intradia
* **Coluna de Data**: O campo temporal base para a extração do dia, semana e hora
* **Coluna de Valor**: A métrica que definirá o **tamanho** dos círculos (Quantidade de Vendas)
* **Filtros**: Define o contexto de dados específico para o Widget
***
### 🔍 Interação
* **Tooltip**: Ao passar o mouse sobre um círculo, uma caixa de detalhes exibe a data/hora exata e o valor da métrica naquele ponto
---
---
url: 'https://docs.horusbi.com.br/etl/processors/transforms/python.md'
---
# Python (Pandas)
O nó **Python** executa scripts de análise de dados em um ambiente isolado, robusto e preparado para engenharia de dados. Ele é ideal para manipulações complexas, uso de bibliotecas científicas (`scipy`, `scikit-learn`) ou integrações via API que seriam difíceis de fazer apenas com SQL.
## Ambiente de Execução
O ambiente é **Headless** (sem interface gráfica), projetado para processamento em lote.
* **Status e Logs**: Use `print()` apenas para mensagens de status e progresso. O sistema exibe apenas a última mensagem impressa na interface do usuário
* **Erros**: Para abortar o processo com erro, use `raise Exception("Mensagem")`
* **Bibliotecas Pré-Instaladas**:
* **Core**: `pandas`, `numpy`
* **Formatos**: `fastparquet`, `pyarrow`, `openpyxl`
* **Ciência de Dados**: `scipy`, `scikit-learn`
* **Web/API**: `requests`, `urllib3`, `beautifulsoup4`, `scrapy`
* **Banco de Dados**: `psycopg2`, `mysql-connector-python`, `pymssql`, `pymongo`
## Estrutura do Script
Para garantir compatibilidade com o motor de execução do Horus, seu script deve seguir estas regras:
1. **Inputs**: Os inputs do fluxo estão disponíveis como DataFrames globais chamados `input_0`, `input_1`, etc.
2. **Output Obrigatório**: O resultado final do processamento deve ser atribuído a uma variável chamada DataFrame `df`.
3. **Colunas**: Por convenção e compatibilidade, mantenha todos os nomes de colunas em **MAIÚSCULO**.
4. **Estilo Notebook**: Prefira um código linear, passo-a-passo, declarando variáveis descritivas (`raw_data`, `clean_data`). Evite encapsular tudo em funções complexas desnecessariamente.
## Exemplo de Código
```python
# 1. Carregamento e Verificação
print("Iniciando processamento...")
# input_0 é injetado automaticamente pelo sistema
if input_0.empty:
raise Exception("O input_0 está vazio!")
# 2. Transformação (Estilo Linear)
clean_data = input_0.dropna(subset=['VALOR'])
# Exemplo: Enriquecimento usando cálculo vetorizado (rápido)
print("Calculando impostos...")
clean_data['IMPOSTO'] = clean_data['VALOR'] * 0.15
clean_data['TOTAL_LIQUIDO'] = clean_data['VALOR'] - clean_data['IMPOSTO']
# Conversão de tipos (Pandas)
clean_data['DATA_VENDA'] = pd.to_datetime(clean_data['DATA_VENDA'])
# 3. Resultado Final
print("Finalizando...")
# A variável 'df' será lida pelo Horus como saída do nó
df = clean_data
# Nota: Não use print(df) para ver dados, pois isso não é visível no log final de forma estruturada.
```
## AI Assistant
O editor do nó Python conta com um assistente de IA especializado. Você pode pedir para ele gerar o código clicando no botão de IA. Ele já conhece todas as bibliotecas e regras acima.
> \[!TIP]
> **Performance**: O Python envolve carregar dados do disco para a memória RAM.
>
> * Dê preferência a operações vetorizadas do Pandas/Numpy.
> * Evite loops `for` iterando linhas (`iterrows`).
> * Para transformações puramente relacionais (JOIN, GROUP BY), o nó **SQL (DuckDB)** costuma ser mais eficiente pois evita a serialização desnecessária.
***
## Configurador Python
O nó **Configurador Python** é uma variação especial que **executa sempre no início do fluxo**, antes de qualquer outro processador. Ele é ideal para criar variáveis dinâmicas que serão usadas pelos nós subsequentes.
### Características
* **Entradas**: Os nós de entrada para o configurador python serão executados antes de todos os outros nós
* **Propósito**: Ler variáveis existentes e criar novas variáveis para o fluxo
* **Uso Típico**: Montar SELECTs dinâmicos com UNION ALL para múltiplas organizações/filiais ou montar connection string dinamicamente
### Exemplo: SELECT Dinâmico com UNION ALL
```python
# Lê a lista de IDs de organizações (variável global ou fixa)
organization_ids = [101, 102, 103, 104]
# Monta um SELECT com UNION ALL para cada organização
selects = []
for org_id in organization_ids:
selects.append(f"SELECT * FROM vendas WHERE organization_id = {org_id}")
query_final = " UNION ALL ".join(selects)
# Salva a query numa variável que será usada pelo nó SQL
variables = {"QUERY_VENDAS": query_final}
```
Após executar o Configurador Python, a variável `QUERY_VENDAS` estará disponível para uso no nó de consulta SQL (substitua `{QUERY_VENDAS}`).
---
---
url: 'https://docs.horusbi.com.br/guia/receitas/banco-dashboard.md'
---
# Receita: Banco de Dados → Dashboard
Conecte a um banco de dados via ETL, carregue os dados no DW e publique um Dashboard para sua equipe.
## ✅ Pré-requisitos
* Agente ETL instalado **na mesma rede** do banco de dados ([Gerenciamento de Agentes](/etl/guides/agentes))
* Conexão de banco configurada no ETL ([Conexões de Banco](/etl/guides/conexoes-banco))
* Mesas criadas no HEC: uma Mesa de Dados e uma Mesa de Aplicações ([Criar Mesas](/hec/desks/mesas))
***
## 📋 Passo a Passo
### 1. 🏗️ Criar Mesas no HEC
Se ainda não existem, crie uma **Mesa de Dados** (destino das tabelas) e uma **Mesa de Aplicações** (destino dos Dashboards) no HEC.
* [Como criar Mesas](/hec/desks/mesas)
### 2. 🤖 Instalar e Verificar o Agente ETL
Certifique-se de que o Agente ETL está instalado na mesma rede do banco de dados, está online e visível na plataforma.
* [Primeiros Passos do ETL](/etl/getting-started/)
* [Gerenciamento de Agentes](/etl/guides/agentes)
### 3. 🔗 Configurar Conexão de Banco
No ETL, crie uma conexão apontando para o banco de dados de origem.
* [Conexões de Banco](/etl/guides/conexoes-banco)
### 4. 📄 Criar Dataflow com Nó de Banco de Dados
Crie um novo Dataflow e adicione o nó de input correspondente ao seu banco (SQL Server, PostgreSQL, MySQL, Oracle, Firebird, ODBC ou InterSystems IRIS).
* [Processadores de Input](/etl/processors/inputs/)
### 5. 💾 Configurar Saída para o DW
Adicione um nó de output **Datawarehouse** ao Dataflow. Configure o nome da tabela de destino.
* [Output Datawarehouse](/etl/processors/outputs/datawarehouse)
### 6. ▶️ Executar e Publicar o Dataflow
Execute o Dataflow para validar. Depois, **publique o Dataflow** para a Mesa de Dados criada na Etapa 1.
* [Mesas e Publicação no ETL](/etl/guides/mesas-publicacao)
> \[!IMPORTANT]
> Publicar o Dataflow no ETL publica a tabela junto. Você não precisa publicar a tabela separadamente no DW.
### 7. 📊 Criar Aplicação no DataViz
Crie uma nova Aplicação em Minha Mesa do DataViz. Vincule a tabela publicada e monte seus Dashboards.
* [Criando sua Primeira Aplicação](/dataviz/01-getting-started/create-app)
### 8. 🌐 Publicar Aplicação para Mesa de Aplicação
No HEC, vá em **Recursos > Conteúdo** e publique a Aplicação de Minha Mesa para a Mesa de Aplicações.
* [Gerenciar Conteúdo (Publicar)](/hec/resources/01-content)
### 9. 🔑 Dar Acesso aos Usuários/Grupos
No HEC, vá em **Usuários** ou **Grupos > Permissões** e conceda acesso às Mesas de Dados e de Aplicações.
* [Configurar Permissões](/hec/users-groups/permissions)
> \[!WARNING]
> Criar uma Mesa não dá acesso automático. Cada usuário ou grupo precisa ter acesso explícito à Mesa nas Permissões.
***
## ⏰ (Opcional) Agendar Atualização Automática
Configure um agendamento no ETL para que o Dataflow execute periodicamente e mantenha os dados atualizados.
* [Execução e Agendamento](/etl/guides/execucao-agendamentos)
---
---
url: 'https://docs.horusbi.com.br/guia/receitas/calendario-filtros.md'
---
# Receita: Calendário e Filtros Dinâmicos
Esta receita resolve um fluxo recorrente: fazer o filtro de data da Dashboard chegar aos Widgets, usar **datas diferentes em Widgets diferentes** (emissão em um, entrega em outro) e fazer cada Widget responder na **sua própria granularidade** (um por dia, outro pelo mês inteiro da data escolhida).
O exemplo condutor é um Fato **Faturamento** com duas datas: `DATA_EMISSAO` e `DATA_ENTREGA`.
## Pré-requisitos
* Uma Aplicação criada no Horus DataViz.
* O Fato Faturamento já carregado no HorusDW, com as colunas `DATA_EMISSAO`, `DATA_ENTREGA` e `VALOR`.
***
## Passo 1: Um único Fato e uma única data
Se a análise usa **um Fato e uma só data**, não é preciso Tabela Calendário. Basta definir um **Filtro Padrão** na Aplicação para que ela já abra na data desejada.
1. Abra a edição da Aplicação e vá na [Aba Geral](../../dataviz/02-apps/general#filtros-padrao).
2. Em **Filtros Padrão**, adicione um filtro sobre a coluna de data do Fato (por exemplo `DATA_EMISSAO`).
3. Salve. A Aplicação passa a abrir já filtrada, e o usuário ainda pode alterar o período durante a navegação.
> \[!TIP]
> O Filtro Padrão é reaplicado toda vez que a Aplicação é reaberta.
***
## Passo 2: Fazer o filtro da Dashboard chegar a todos os Widgets
Quando há mais de um Widget (ou mais de um Fato), o filtro precisa de uma ponte: a **Tabela Calendário**. Ao filtrar pela Dimensão Calendário, o filtro **se propaga** para todos os Fatos conectados a ela.
1. Crie a Tabela Calendário (veja o [Passo 5](#passo-5-criar-a-tabela-calendario-no-etl)).
2. Na [Aba Tabelas](../../dataviz/02-apps/tables), ligue `Calendário.DATA` à coluna de data do Fato (`Faturamento.DATA_EMISSAO`).
3. Nos Widgets e na Barra de Filtros da Dashboard, use **`Calendário.DATA`** (não a coluna de data do Fato). Assim um único filtro alimenta todos os Widgets.
### Por que um KPI pode "ignorar" o filtro da Dashboard
Se um Widget tem um **Filtro Pré-Aplicado** de data próprio, esse filtro vale só para aquele Widget, [independentemente do filtro global da Dashboard](../../dataviz/02-apps/tables#filtros-pre-aplicados). É a causa mais comum de "mudei o filtro da Dashboard e o KPI não respondeu".
Para o KPI responder à Dashboard:
* Remova o filtro de data fixo do próprio Widget, e
* Garanta que ele consulta dados ligados ao `Calendário`, deixando o filtro global cuidar do período.
Se o Widget precisa mesmo de um período próprio derivado do filtro global, use a abordagem do [Passo 4](#passo-4-cada-widget-na-sua-granularidade).
***
## Passo 3: Datas diferentes em Widgets diferentes (Relacionamento Fantasma)
Para um Widget analisar por **emissão** e outro por **entrega**, mantenha uma única Tabela Calendário e cadastre uma **chave de Relacionamento secundária**. Veja o conceito em [Relacionamento Fantasma](../../dataviz/02-apps/data-modeling#relacionamento-fantasma-multiplas-datas-no-mesmo-fato).
1. Na [Aba Tabelas](../../dataviz/02-apps/tables#relacionamentos-secundarios), selecione o Relacionamento entre Calendário e Faturamento.
2. Clique em **"+ Adicionar Chave"** e inclua `Calendário.DATA = Faturamento.DATA_ENTREGA`. A primeira chave (`DATA_EMISSAO`) continua sendo a padrão.
3. No Widget que deve usar a entrega, escreva a métrica com [`use()`](../../dataviz/02-apps/expressions#relacionamentos-secundarios-com-use):
```sql
use([Calendário], [Faturamento].DATA_ENTREGA) SUM([Faturamento].VALOR)
```
Agora o filtro da Dashboard (sobre `Calendário.DATA`) alimenta os dois Widgets, cada um pela sua data.
***
## Passo 4: Cada Widget na sua granularidade
Cenário do "quem for mês mantém mês": o usuário escolhe **um dia** no filtro da Dashboard, e um Widget configurado por mês mostra o **mês inteiro** daquela data.
Um filtro é um valor só. Filtrar `Calendário.DATA` em um dia restringe tudo àquele dia. Para um Widget reinterpretar essa seleção na sua própria granularidade, use duas peças de [Expressões](../../dataviz/02-apps/expressions#expressoes-analiticas-com):
1. **Ler a data escolhida** na Dashboard com a sintaxe `<...>`:
`<[Calendário]."DATA":START, CURRENT_DATE>` devolve o início do período filtrado (ou `CURRENT_DATE` se não houver filtro).
2. **Reescrever o período** com a Expressão Analítica `${ EXPRESSÃO, FILTRO }` e as funções de data.
Exemplo (Widget que sempre mostra o **mês** da data selecionada):
```sql
${
SUM([Faturamento]."VALOR"),
[Calendário]."DATA" BETWEEN
MONTHSTART(<[Calendário]."DATA":START, CURRENT_DATE>)
AND
MONTHEND(<[Calendário]."DATA":START, CURRENT_DATE>)
}
```
### Mapa de granularidade
Trocando as funções de data, o mesmo padrão cobre qualquer granularidade. Abreviando `<[Calendário]."DATA":START, CURRENT_DATE>` como ``:
| Comportamento desejado no Widget | Filtro dentro do `${...}` |
|----------------------------------|----------------------------|
| Sempre o mês da data selecionada | `[Calendário]."DATA" BETWEEN MONTHSTART() AND MONTHEND()` |
| Sempre o ano da data selecionada | `[Calendário]."DATA" BETWEEN YEARSTART() AND YEAREND()` |
| Sempre a semana da data selecionada | `[Calendário]."DATA" BETWEEN WEEKSTART() AND WEEKEND()` |
| Últimos 3 meses a partir da seleção | `[Calendário]."DATA" BETWEEN SUBMONTHS(, 3) AND ` |
| Últimos 12 meses a partir da seleção | `[Calendário]."DATA" BETWEEN SUBMONTHS(, 12) AND ` |
Para combinar granularidade própria **com** uma data alternativa (entrega), use as duas técnicas juntas: `use()` para a chave e `${...}` para o período.
> \[!TIP]
> A lista completa de funções (`MONTHSTART`, `YEARSTART`, `SUBMONTHS`, `ADDYEARS`, etc.) está em [Funções de Data](../../dataviz/02-apps/expressions#funcoes-de-data-disponiveis).
***
## Passo 5: Criar a Tabela Calendário no ETL
A Tabela Calendário é gerada uma vez no HorusETL com um nó Python (Pandas). O script pronto, que cria um calendário dos últimos 5 anos com colunas de ano, mês, trimestre e dia da semana, está documentado no hub de modelagem:
➡️ [Gerando uma Tabela Calendário](../../dataviz/02-apps/data-modeling#gerando-uma-tabela-calendario)
Depois de gerar e publicar a Tabela no DW, volte ao Passo 2 para ligá-la ao Fato.
***
## Resumo
| Necessidade | Recurso |
|-------------|---------|
| Abrir já filtrado por uma data | Filtro Padrão na Aba Geral |
| Filtro da Dashboard chega aos Widgets | Tabela Calendário + filtrar por `Calendário.DATA` |
| Widget ignora o filtro global | Remover Filtro Pré-Aplicado fixo do Widget |
| Datas diferentes por Widget | Relacionamento Secundário + `use()` |
| Granularidade própria por Widget | `<...>` + `${...}` + funções de data |
---
---
url: 'https://docs.horusbi.com.br/guia/receitas/excel-dashboard.md'
---
# Receita: Excel → Dashboard (sem ETL)
Carregue uma planilha Excel diretamente no DW e publique um Dashboard — sem necessidade de Agente ou pipeline ETL.
## ✅ Pré-requisitos
* Acesso ao HorusDW e ao DataViz
* Mesas criadas no HEC: uma Mesa de Dados e uma Mesa de Aplicações ([Criar Mesas](/hec/desks/mesas))
* Arquivo Excel com os dados que deseja visualizar
***
## 📋 Passo a Passo
### 1. 🏗️ Criar Mesas no HEC
Se ainda não existem, crie uma **Mesa de Dados** (destino das tabelas) e uma **Mesa de Aplicações** (destino dos Dashboards) no HEC.
* [Como criar Mesas](/hec/desks/mesas)
### 2. 📤 Upload do Excel no DW
No HorusDW, acesse **Minha Mesa** e clique em **Carregar Dados**. Selecione seu arquivo Excel.
* [Tutorial de Upload](/dw/getting-started/upload)
### 3. 📦 Publicar Tabela para Mesa de Dados
Na sua Minha Mesa do DW, abra a tabela carregada e clique em **Publicar**. Escolha a Mesa de Dados criada na Etapa 1.
* [Tabelas Físicas — Publicação](/dw/tables/physical)
### 4. 📊 Criar Aplicação no DataViz
Crie uma nova Aplicação em Minha Mesa do DataViz. Vincule a tabela publicada e monte seus Dashboards.
* [Criando sua Primeira Aplicação](/dataviz/01-getting-started/create-app)
### 5. 🌐 Publicar Aplicação para Mesa de Aplicação
No HEC, vá em **Recursos > Conteúdo** e publique a Aplicação de Minha Mesa para a Mesa de Aplicações.
* [Gerenciar Conteúdo (Publicar)](/hec/resources/01-content)
### 6. 🔑 Dar Acesso aos Usuários
No HEC, vá em **Usuários** ou **Grupos > Permissões** e conceda acesso às Mesas de Dados e de Aplicações.
* [Configurar Permissões](/hec/users-groups/permissions)
> \[!WARNING]
> Criar uma Mesa não dá acesso automático. Cada usuário ou grupo precisa ter acesso explícito à Mesa nas Permissões.
***
## 💡 Quando Migrar para ETL?
A trilha Excel funciona bem para:
* Cargas pontuais e manuais
* Prototipação rápida
* Dados que não precisam de atualização automática
Considere migrar para um **pipeline ETL** quando:
* Os dados precisam ser atualizados periodicamente (agendamento)
* A fonte de dados é um banco de dados, API ou Google Sheets
* Você precisa aplicar transformações complexas antes de carregar
Veja as receitas com ETL: [Google Sheets → Dashboard](/guia/receitas/google-sheets-dashboard) | [Banco de Dados → Dashboard](/guia/receitas/banco-dashboard)
---
---
url: 'https://docs.horusbi.com.br/guia/receitas/google-sheets-dashboard.md'
---
# Receita: Google Sheets → Dashboard
Leia dados de uma planilha Google Sheets, carregue no DW via ETL e publique um Dashboard para sua equipe.
## ✅ Pré-requisitos
* Agente ETL instalado e online ([Gerenciamento de Agentes](/etl/guides/agentes))
* Service Account do Google Cloud Platform com API do Google Sheets habilitada
* Mesas criadas no HEC: uma Mesa de Dados e uma Mesa de Aplicações ([Criar Mesas](/hec/desks/mesas))
***
## 📋 Passo a Passo
### 1. 🏗️ Criar Mesas no HEC
Se ainda não existem, crie uma **Mesa de Dados** (destino das tabelas) e uma **Mesa de Aplicações** (destino dos Dashboards) no HEC.
* [Como criar Mesas](/hec/desks/mesas)
### 2. 🤖 Instalar e Verificar o Agente ETL
Certifique-se de que o Agente ETL está instalado, online e visível na plataforma.
* [Primeiros Passos do ETL](/etl/getting-started/)
* [Gerenciamento de Agentes](/etl/guides/agentes)
### 3. 📄 Criar Dataflow com Nó Google Sheets
Crie um novo Dataflow e adicione o nó **Google Sheet** como input. Configure as credenciais da Service Account, o ID da planilha e a aba desejada.
* [Referência do nó Google Sheets](/etl/processors/inputs/google-sheets)
### 4. 💾 Configurar Saída para o DW
Adicione um nó de output **Datawarehouse** ao Dataflow. Configure o nome da tabela de destino.
* [Output Datawarehouse](/etl/processors/outputs/datawarehouse)
### 5. ▶️ Executar e Publicar o Dataflow
Execute o Dataflow para validar. Depois, **publique o Dataflow** para a Mesa de Dados criada na Etapa 1.
* [Mesas e Publicação no ETL](/etl/guides/mesas-publicacao)
> \[!IMPORTANT]
> Publicar o Dataflow no ETL publica a tabela junto. Você não precisa publicar a tabela separadamente no DW.
### 6. 📊 Criar Aplicação no DataViz
Crie uma nova Aplicação em Minha Mesa do DataViz. Vincule a tabela publicada e monte seus Dashboards.
* [Criando sua Primeira Aplicação](/dataviz/01-getting-started/create-app)
### 7. 🌐 Publicar Aplicação para Mesa de Aplicação
No HEC, vá em **Recursos > Conteúdo** e publique a Aplicação de Minha Mesa para a Mesa de Aplicações.
* [Gerenciar Conteúdo (Publicar)](/hec/resources/01-content)
### 8. 🔑 Dar Acesso aos Usuários/Grupos
No HEC, vá em **Usuários** ou **Grupos > Permissões** e conceda acesso às Mesas de Dados e de Aplicações.
* [Configurar Permissões](/hec/users-groups/permissions)
> \[!WARNING]
> Criar uma Mesa não dá acesso automático. Cada usuário ou grupo precisa ter acesso explícito à Mesa nas Permissões.
***
## ⏰ (Opcional) Agendar Atualização Automática
Configure um agendamento no ETL para que o Dataflow execute periodicamente e mantenha os dados atualizados.
* [Execução e Agendamento](/etl/guides/execucao-agendamentos)
---
---
url: 'https://docs.horusbi.com.br/guia/receitas.md'
---
# Receitas Práticas
**Receitas** são tutoriais passo a passo que cobrem cenários completos — da origem do dado até o Dashboard publicado. Cada passo aponta para a documentação detalhada do módulo correspondente.
## 📚 Receitas Disponíveis
| Receita | Descrição | Módulos Envolvidos |
|---------|-----------|-------------------|
| [Excel → Dashboard](/guia/receitas/excel-dashboard) | Carregue uma planilha Excel no DW e crie um Dashboard (sem ETL) | HEC, DW, DataViz |
| [Google Sheets → Dashboard](/guia/receitas/google-sheets-dashboard) | Leia dados do Google Sheets via ETL e crie um Dashboard publicado | HEC, ETL, DW, DataViz |
| [Banco de Dados → Dashboard](/guia/receitas/banco-dashboard) | Conecte a um banco de dados via ETL e crie um Dashboard publicado | HEC, ETL, DW, DataViz |
| [Calendário e Filtros Dinâmicos](/guia/receitas/calendario-filtros) | Faça o filtro da Dashboard chegar aos Widgets, use datas diferentes por Widget (emissão vs entrega) e granularidade própria por Widget | DataViz, ETL |
***
> \[!TIP]
> Não sabe qual receita seguir? Se você tem um arquivo Excel, comece pela receita **Excel → Dashboard**. Se seus dados estão em um banco ou serviço externo, use uma das receitas com ETL.
---
---
url: 'https://docs.horusbi.com.br/lumo/comandos.md'
---
# Referência de Comandos
Referência completa dos comandos do Lumo CLI. Use `lumo --help` para a ajuda detalhada de qualquer um: a tabela abaixo lista o que existe, o `--help` explica cada flag em detalhe.
> \[!TIP]
> Termos como **tipo de carga** (`--context`), **mesa**, **rascunho/publicado** e **star schema** estão explicados na página de [Conceitos](/lumo/conceitos/).
> \[!TIP]
> A maioria dos comandos aceita referências no formato `tipo:id` - por exemplo: `flow:42`, `table:23913`, `app:7`, `credential:539`. Nunca passe um número avulso; sempre inclua o prefixo do tipo.
***
## Por onde começar
A tabela de comandos é exaustiva e ordenada alfabeticamente, o que é ótimo para procurar e péssimo para aprender. Esta seção dá a ordem em que os comandos costumam aparecer no trabalho real. Ela não repete o que cada comando faz nem quais flags ele aceita: isso está na tabela, gerada do próprio código.
**1. Entrar.** `lumo auth login`, depois `lumo tenants list` para achar o tenant e `lumo init ` para criar o workspace no diretório atual. `lumo auth status` confirma a sessão.
**2. Olhar antes de mexer.** `lumo status` mostra o que mudou localmente. `lumo list ` enumera recursos, `lumo info :` abre um, `lumo columns table:` mostra as colunas de uma tabela DW e `lumo diff :` mostra a mudança pendente. Todos são de leitura e seguros para repetir.
**3. Sincronizar.** `lumo fetch` verifica o servidor sem tocar nos seus arquivos; `lumo pull` traz as mudanças para eles; `lumo push` envia as suas. `lumo lint` valida o YAML contra o schema, e o `push` já roda o lint sozinho antes de enviar.
**4. Criar e remover.** `lumo new ` cria um recurso novo; `lumo rm :` remove.
**5. Clonar, editar, publicar.** Recurso publicado é somente leitura. O ciclo é sempre `clone` (vira rascunho seu) → editar → `publish` (volta a ser compartilhado). Vale para flows (`lumo flow clone` / `lumo flow publish`, numa mesa de dados) e para apps (`lumo app clone` / `lumo app publish`, numa mesa de aplicação).
> \[!WARNING]
> **Publicar é uma decisão humana.** Um recurso publicado fica somente leitura e é compartilhado com outros usuários. Construa e itere como rascunho; publique apenas quando o trabalho estiver pronto.
**6. Executar.** `lumo flow run` dispara um flow publicado; `lumo flow run-node` executa um nó isolado de um rascunho. `lumo flow status` e `lumo flow logs` acompanham, `lumo flow cancel` interrompe. Do outro lado, `lumo schedule run` / `pause` / `resume` controla o agendamento, e `lumo agent restart` o agente.
**7. Variáveis.** `lumo variables list` / `get` / `set` / `unset` mexem no workspace local; `lumo push variables` envia ao servidor.
***
## Todos os comandos
Tabela completa, gerada a partir do `Use`, do `Short` e das flags de cada comando cobra em `cmd/lumo/*.go`, na main do lumo-cli. É a única lista de comandos desta documentação: não existe uma segunda tabela escrita à mão para divergir dela.
| Comando | Descrição | Flags |
|---------|-----------|-------|
| `lumo agent` | Agent management & investigation: link, restart, migrate, events, health | - |
| `lumo agent events ` | List problem schedule-events for an agent (missed/late/error/crashed) | `--status` |
| `lumo agent health ` | Show the agent's 24h health pulse (uptime, memory, jobs, queue, timeline) | `--range` `--full` `--mac` |
| `lumo agent link` | Link an external agent to this tenant via its token | `--token` `--name` |
| `lumo agent migrate` | Bulk-rebind flows and credentials to another agent | `--flows` `--credentials` `--to-agent` `--force` |
| `lumo agent restart ` | Force a remote agent to restart | - |
| `lumo app` | BI app operations: query, export, open | - |
| `lumo app change-table ` | Swap a table reference inside an app to a compatible one | `--from-table` `--to-table` `--dry-run` `--force` |
| `lumo app clone ` | Clone an app under a new name | `--name` |
| `lumo app dashboard-preview ` | Run each widget's query and return data + visual JSON (no PNG render) | `--dashboard` `--widget` `--filter` `--limit` `--sql-only` `--debug-sql` |
| `lumo app export ` | Export a dashboard as PNG/PDF/SVG | `--dashboard` `--image-format` `--output` `--dark-theme` `--landscape` |
| `lumo app export-widget ` | Export a single widget as PNG | `--dashboard` `--widget` `--image-format` `--output` `--dark-theme` |
| `lumo app open ` | Open the app in the browser (or print the URL) | `--no-browser` |
| `lumo app publish ` | Publish an app to a desk (drift-protected via If-Match) | `--desk` `--new` |
| `lumo app query ` | Run a BI engine query against an app | `--fields` `--filter` `--order-by` `--limit` `--sql-only` |
| `lumo auth` | Authentication commands | - |
| `lumo auth login` | Log in to Horus BI | `--email` `--password` `--otp` `--local` |
| `lumo auth logout` | Log out and clear stored credentials | - |
| `lumo auth status` | Show current authentication status | - |
| `lumo backup` | Snapshot the local workspace as a tar.gz | `--output` `--exclude-state` |
| `lumo columns ` | List the columns of a DW table | - |
| `lumo credential` | Credential operations: test, query, schema, transfer | - |
| `lumo credential query ` | Run an ad-hoc SQL query against the source | `--sql` `--limit` `--timeout` |
| `lumo credential schema ` | List schemas / tables / columns visible through this credential | `--schema` `--timeout` |
| `lumo credential test ` | Test connectivity to the source via its agent | - |
| `lumo credential transfer ` | Rebind the credential to a different agent | `--to-agent` |
| `lumo diff ` | Show diff between WORKING / STATE / SERVER | `--include-remote` `--field` |
| `lumo fetch [resource...]` | Refresh STATE from the server (does not modify WORKING) | `--parallel` `--full` |
| `lumo flow` | Imperative flow operations: clone, publish, run, node-level execution | - |
| `lumo flow cancel` | Cancel a running flow execution | `--execution` `--force` |
| `lumo flow clone ` | Clone a flow under a new name | `--name` `--dry-run` |
| `lumo flow export-node ` | Export a node's output as a downloadable artifact | `--node` |
| `lumo flow force-batch` | Dispatch a batch of flows in dependency order (a template apply's reloadPlan) | `--from-reload-plan` |
| `lumo flow logs ` | Show execution history for a flow | - |
| `lumo flow nodes ` | List nodes of a flow (internalId + kind + description) for run-node lookup | `--type` |
| `lumo flow preview-node ` | Preview a node's output rows without writing | `--node` |
| `lumo flow publish ` | Publish a flow to a desk (drift-protected via If-Match) | `--desk` `--new` `--copy-data` `--dry-run` |
| `lumo flow run ` | Trigger a flow execution | `--wait` `--context` `--temporal-days` |
| `lumo flow run-node ` | Execute a single node (write side-effects) | `--node` `--wait` |
| `lumo flow status ` | Show runtime status for a flow (running / idle / queued) | - |
| `lumo flow sync-table ` | Create or sync a flow's DW table from its InsertDatawarehouse node | `--node` `--name` |
| `lumo info :` | Show read-only metadata and runtime status for a resource | `--full` `--dependents` `--field` |
| `lumo init []` | Bootstrap a workspace for a Horus BI tenant | `--recover` `--fetch-only` |
| `lumo lint [file...]` | Validate dual-document YAML files against the v2 schemas | `--strict` |
| `lumo list ` | List resources of a kind (flow, table, app, credential, dw-desk, bi-desk, schedule, agent, variables) | `--search` `--desk` `--remote` `--limit` `--offset` |
| `lumo log [resource]` | Show audit log of sync operations | `--count` `--limit` `--since` `--operation` `--errors` |
| `lumo new ` | Create a new resource on the server (eager: server-first → STATE → WORKING) | - |
| `lumo new app` | Create a new BI application | `--name` `--table` |
| `lumo new bi-desk` | Create a new application desk (publish target for apps) | `--name` |
| `lumo new credential` | Create a new HEC credential | `--name` `--type` |
| `lumo new dw-desk` | Create a new data desk (publish target for flows / tables) | `--name` |
| `lumo new flow` | Create a new flow | `--name` `--load-type` |
| `lumo new schedule` | Create a new schedule | `--name` `--cron` |
| `lumo new table` | Create a new DW table - must combine --from-flow + --node (workflow C) | `--name` `--from-flow` `--node` `--from-node` |
| `lumo pull [resource...]` | Refresh STATE then overwrite WORKING with the cached payload | `--discard-local` `--no-fetch` `--parallel` `--dry-run` |
| `lumo push [resource...]` | Save local edits to the server | `--dry-run` `--no-lint` `--parallel` `--continue-on-error` `--force-ours` `--i-know-what-im-doing` `--force-recreate` |
| `lumo rm :` | Delete a resource on the server and locally | `--force` `--yes` `--cascade` `--dry-run` |
| `lumo scaffold [ []]` | Print embedded YAML reference + examples for any Lumo kind | `--variant` |
| `lumo scaffold ls` | Flat list of every embedded scaffold (kind.variant - tagline) | - |
| `lumo schedule` | Schedule operations: run, pause, resume, logs | - |
| `lumo schedule logs ` | List recent trigger executions for a schedule | `--since` `--tail` |
| `lumo schedule pause ` | Pause a schedule (sets ativo=false) | - |
| `lumo schedule resume ` | Resume a schedule (sets ativo=true) | - |
| `lumo schedule run ` | Trigger a schedule one-shot | - |
| `lumo status` | Show local-modified, deleted, and untracked resources | `--summary` `--filter` |
| `lumo table` | Table operations: truncate, preview | - |
| `lumo table clone ` | (not supported) - clone the FLOW that owns the table instead | - |
| `lumo table preview ` | Preview rows from the DW table | `--limit` |
| `lumo table truncate ` | Remove all rows from the DW table (schema preserved) | `--force` `--yes` `--dry-run` |
| `lumo tenants` | Tenant discovery | - |
| `lumo tenants list` | List tenants available to the authenticated user | - |
| `lumo update` | Atualiza o lumo para a última versão (binário + skills) | `--check` |
| `lumo variables` | Tenant variable operations: list, get, set, unset | - |
| `lumo variables get ` | Read one variable by key | `--remote` |
| `lumo variables list` | List all tenant variables | `--remote` |
| `lumo variables set =` | Create or update a tenant variable | `--from-stdin` |
| `lumo variables unset ` | Remove a tenant variable | - |
| `lumo version` | Print build information | - |
***
## Códigos de Saída
Retirados de `cmd/lumo/errors.go` e dos pontos onde o CLI de fato os retorna.
| Exit | Significado | Primeira ação |
|---:|---|---|
| 0 | Sucesso (inclui "nada a fazer" e "sem diff") | - |
| 1 | Erro genérico. O `lumo diff` também usa 1 no sentido Unix: "há diff" | Leia a mensagem em stderr. Vindo do `diff`, é resultado, não erro |
| 2 | Erro de uso | Leia `lumo --help` |
| 3 | Servidor mudou desde sua última sincronização | `lumo diff ` e escolha pull / merge / `--force-ours` |
| 4 | Conflito local + servidor | Mesclagem manual |
| 5 | Recurso não encontrado | Verifique `tipo:id`; talvez `lumo fetch` |
| 6 | Problema de auth/acesso | `lumo auth login`; se o acesso mudou, re-execute `lumo init ` |
| 7 | Rede/timeout, **retriável** | Execute novamente o mesmo comando |
| 8 | Lint/schema falhou | Leia o caminho; corrija o YAML |
| 9 | Validação do servidor falhou | Leia a mensagem; comum: `FLOW_READONLY` (clone primeiro), `SCHEDULE_UNPUBLISHED_FLOW` |
| 10 | Servidor 5xx | Transitório. Tente uma vez; se persistir, pare e reporte |
| 11 | Workspace já inicializado no diretório, ou vinculado a outro tenant. Só o `lumo init` retorna este código | `lumo init --recover` para reaproveitar o `.lumo/` que já está lá, ou escolha outro diretório |
| 12 | Recurso ainda não sincronizado localmente | `lumo fetch ` primeiro |
| 13 | `rm` bloqueado por dependências | Execute com `--cascade` ou remova os dependentes primeiro |
| 14 | Pré-condição (If-Match) falhou | `lumo fetch`, re-edite e faça push novamente |
| 15 | Aplicação parcial: o servidor ignorou ao menos 1 campo | Workspace já auto-corrigido; leia o diff no stdout e corrija a causa raiz |
| 20 | `lumo update` precisa de privilégio | Execute a ação que o comando imprime (ex.: `sudo lumo update`) |
| 21 | `lumo update` falhou (rede/checksum/IO) | Tente novamente; se persistir, verifique a conectividade |
> \[!NOTE]
> São **seguros para retry**: comandos de leitura (`status`, `info`, `list`, `columns`, `diff`, `log`, `fetch`).
> **Não são seguros** para retry cego: imperativos (`new`, `push`, `clone`, `publish`, `run`, `rm`, `truncate`). O estado pode ter mudado, resolva antes de tentar novamente.
***
## Flags Globais
Valem para qualquer comando. Geradas de `root.PersistentFlags()` em `cmd/lumo/root.go`.
| Flag | Descrição |
|------|-----------|
| `--debug` | verbose debug output to stderr |
| `--format` | output format: text | json | yaml (yaml is honored by `info` and `diff`; other commands fall back to text or json) |
| `--no-color` | disable colored output |
| `--no-input` | disable interactive prompts |
| `--server` | API base URL (overrides workspace.json) |
| `--workspace` | workspace path (default: walk up from PWD) |
Além destas, o cobra adiciona `--help` (`-h`) a todo comando.
---
---
url: 'https://docs.horusbi.com.br/lumo/referencia.md'
description: >-
Referência do YAML do Lumo: um kind por página, com propriedades, tipos,
restrições e exemplos.
---
# Referência de YAML
O workspace do Lumo é uma cópia espelho do tenant em arquivos YAML. Esta seção é o contrato desses arquivos: uma página por tipo de recurso, com todas as propriedades, os tipos, o que é obrigatório e o que cada restrição quebra quando você a ignora.
Toda chave documentada aqui existe nos schemas v2 que o próprio CLI usa para validar. O que não está no schema não aparece nestas páginas.
| Recurso | Onde vive | Página |
|---|---|---|
| Flow | `flows/--.yaml` | [Flow](/lumo/referencia/flow) |
| Tabela do DW | `tables/--.yaml` | [Table](/lumo/referencia/table) |
| App de BI | `apps/--.yaml` | [App](/lumo/referencia/app) |
| Credencial | `credentials/--.yaml` | [Credential](/lumo/referencia/credential) |
| Agendamento | `schedules/--.yaml` | [Schedule](/lumo/referencia/schedule) |
| Desk | `dw-desks/` e `bi-desks/` | [Desk](/lumo/referencia/desk) |
| Variáveis do tenant | `variables.yaml` | [Variables](/lumo/referencia/variables) |
## O formato do arquivo
Todo recurso é um arquivo com **dois documentos YAML** separados por `---`.
```yaml
id: 44531 # ─┐
kind: flow # │ header: identidade. imutável depois da criação.
lumo: v2 # │
tenantId: 853 # ─┘
---
nome: Fato Vendas # ─┐ body: o que você edita.
load_type: Total # ─┘
```
O header identifica o recurso e nunca muda. O body é o conteúdo, e é o que você edita e sincroniza.
O nome do arquivo segue `--.yaml`. O `` no nome é o mesmo `id` do header. A exceção é `variables.yaml`, que é único no tenant e não tem id.
## Autocomplete e validação no editor
Cole a linha do schema no topo do arquivo. Editores com YAML Language Server, como VS Code, passam a completar as chaves, mostrar os tipos e sublinhar o que estiver errado antes de você dar `push`.
```yaml
# yaml-language-server: $schema=https://docs.horusbi.com.br/schemas/v2/flow.schema.json
```
Troque `flow` pelo kind do arquivo. Os schemas servidos:
* [flow.schema.json](https://docs.horusbi.com.br/schemas/v2/flow.schema.json)
* [table.schema.json](https://docs.horusbi.com.br/schemas/v2/table.schema.json)
* [app.schema.json](https://docs.horusbi.com.br/schemas/v2/app.schema.json)
* [app-dashboard.schema.json](https://docs.horusbi.com.br/schemas/v2/app-dashboard.schema.json)
* [credential.schema.json](https://docs.horusbi.com.br/schemas/v2/credential.schema.json)
* [schedule.schema.json](https://docs.horusbi.com.br/schemas/v2/schedule.schema.json)
* [dw-desk.schema.json](https://docs.horusbi.com.br/schemas/v2/dw-desk.schema.json)
* [bi-desk.schema.json](https://docs.horusbi.com.br/schemas/v2/bi-desk.schema.json)
* [variables.schema.json](https://docs.horusbi.com.br/schemas/v2/variables.schema.json)
O mesmo contrato roda localmente:
```bash
lumo lint # valida o workspace inteiro
lumo lint flows/fato-vendas--44531.yaml
```
## Como ler as páginas
Cada página tem três camadas.
1. **Modelo de configuração.** O esqueleto com todas as chaves, o tipo de cada uma e a restrição ao lado dela.
2. **Configuração completa.** Um exemplo que funciona, com o nome do arquivo declarado e as pegadinhas comentadas onde elas acontecem.
3. **Especificação.** Uma seção por propriedade, com tipo, obrigatoriedade, default e exemplo.
## Descobrir o que já existe
O CLI imprime o modelo de qualquer recurso, direto do binário:
```bash
lumo scaffold flow # flow genérico
lumo scaffold flow postgresql # flow de extração PostgreSQL
lumo scaffold node Join # fragmento de um nó, com as options comentadas
lumo scaffold credential --variant postgres
```
E lista o que o tenant já tem, com os ids que você vai precisar referenciar:
```bash
lumo list flow
lumo list table
lumo list credential # a chave da credencial, usada nos nós de extração
lumo columns table:44940 # os ids de coluna, usados em relationships e facts
```
---
---
url: 'https://docs.horusbi.com.br/dataviz/04-features/alerts/webhook/payload.md'
---
# Referência do Payload
Esta é a referência completa do que o Lumo envia quando um alerta com canal **Webhook** ativo dispara. Use esta página como contrato técnico: aqui estão todos os campos, formatos e variações possíveis.
> \[!INFO]
> Tudo aqui é descrito a partir do que o Lumo realmente envia hoje. Quando algo é opcional ou condicional, está marcado. Quando algo está no roadmap (ainda não disponível), está em [Avançado → Limitações](./advanced.md#limitacoes-conhecidas).
***
## Requisição HTTP
Toda entrega de alerta para webhook é **uma requisição HTTP por destinatário por entrega**:
| Característica | Valor |
|---|---|
| **Método** | `POST` (fixo) |
| **URL** | A URL configurada no tenant (mesma para todos os alertas do tenant) |
| **Content-Type** | `application/json; charset=utf-8` (fixo, gerenciado pelo sistema) |
| **User-Agent** | `Horus-Alert-Webhook/1.0` (fixo, gerenciado pelo sistema) |
| **Corpo** | JSON do envelope (descrito abaixo) |
| **Timeout** | 10 segundos |
| **Tamanho máx. do corpo** | 4 MB |
> \[!WARNING]
> Se o seu endpoint não responder em **10 segundos**, a requisição é abortada e contabilizada como falha (com retry posterior). Endpoints lentos devem **responder imediatamente** (HTTP 2xx) e processar de forma assíncrona em background.
### Headers HTTP que sua URL recebe
O Lumo envia dois grupos de headers em cada POST:
**1. Headers do sistema (fixos, não configuráveis):**
| Header | Valor |
|---|---|
| `Content-Type` | `application/json` |
| `User-Agent` | `Horus-Alert-Webhook/1.0` |
**2. Headers personalizados (configuráveis pelo cliente):**
Configurados em **HEC → Tenants → Editar Tenant → Canais de Alerta Permitidos → Webhook → Headers HTTP Customizados**. Os mesmos headers podem ser configurados a nível de cliente (template), e cada tenant herda os do cliente até definir os próprios.
Exemplos comuns:
```http
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
X-Custom-Source: lumo-alerts
X-Tenant-Identifier: cliente-acme
```
Casos de uso típicos:
* **Autenticação** do request no seu endpoint (token bearer, chave de API).
* **Roteamento** em proxies/middlewares (`X-Forwarded-Target`, headers de tenant).
* **Identificação de origem** em serviços compartilhados que recebem webhooks de várias fontes.
**Limites e regras:**
| Regra | Valor |
|---|---|
| Máximo de headers | 20 por tenant |
| Tamanho do nome | até 100 caracteres |
| Tamanho do valor | até 500 caracteres |
| Caracteres permitidos no nome | letras, dígitos e `!#$%&'*+-.^_\`|~`(token RFC 7230) |
| Headers reservados (não podem ser sobrescritos) |`Content-Type`, `User-Agent`, `Host`, `Content-Length`, `Connection\` |
> \[!INFO]
> Os headers reservados acima são gerenciados pelo sistema/HTTP stack. Mesmo que sejam configurados na UI por engano, eles são bloqueados na validação antes de salvar — e, em caso de conflito, os valores do sistema sempre prevalecem.
### Sucesso vs Falha
* **Sucesso**: o seu endpoint retornou um status HTTP entre `200` e `299` (inclusive).
* **Falha**: qualquer outro status, timeout, erro de DNS, recusa de conexão, certificado inválido, ou corpo da requisição maior que 4 MB.
A resposta do seu endpoint é **lida e armazenada** (até 10 KB; conteúdo além disso é truncado). Isso ajuda a investigar falhas no histórico do alerta.
***
## Envelope (raiz do JSON)
Todo payload do Lumo segue o mesmo envelope:
```json
{
"alert": { "id": 123, "nome": "Margem Negativa Diária", "type": "condition", "app_id": 456 },
"user": { "id": 789, "nome": "Maria Souza", "email": "maria@empresa.com" },
"tenantId": 1,
"timestamp": "2026-05-27T10:30:00.000Z",
"content": [ /* blocos de conteúdo, descritos abaixo */ ]
}
```
### Campos da raiz
| Campo | Tipo | Descrição |
|---|---|---|
| `alert.id` | número | Identificador único do alerta no Lumo |
| `alert.nome` | string | Nome do alerta como cadastrado pelo usuário |
| `alert.type` | `"condition" \| "trigger"` | Metadado de origem: condicional (dispara por regra) ou agendado (dispara por horário). **Não muda o formato do payload.** |
| `alert.app_id` | número | `null` | Aplicação associada ao alerta. Pode ser `null` para alertas globais (sem app vinculado) |
| `user.id` | número | Identificador do destinatário no Lumo. **Sempre presente.** |
| `user.nome` | string | Nome do destinatário. Pode estar ausente se o registro tiver sido removido |
| `user.email` | string | E-mail do destinatário (usado como login). Pode estar ausente se o registro tiver sido removido |
| `tenantId` | número | Identificador do tenant que disparou o alerta |
| `timestamp` | string (ISO 8601) | Momento em que a entrega foi enfileirada para envio, em UTC |
| `content` | array | Blocos de conteúdo do alerta, em ordem de exibição (igual à ordem definida no editor) |
> \[!TIP]
> Use `tenantId` para roteamento se o seu endpoint atende **vários tenants** do Lumo (ex.: um endpoint compartilhado entre filiais). Use `alert.id` + `timestamp` como chave de idempotência (veja [Avançado → Idempotência](./advanced.md#idempotencia)).
***
## Bloco de Conteúdo (`content[]`)
`content` é um array. Cada item descreve **um bloco de conteúdo** do alerta. A ordem do array é a mesma da mensagem entregue (espelha o que o usuário vê no editor).
Todos os itens têm o mesmo esqueleto:
```json
{
"id": 11,
"type": "text",
"nome": "Cabeçalho",
"generated": { /* varia por type */ }
}
```
| Campo | Tipo | Descrição |
|---|---|---|
| `id` | número | Identificador único do bloco no alerta |
| `type` | `"text" \| "ai" \| "chart" \| "report"` | Tipo do bloco. Define o formato de `generated`. |
| `nome` | string | Nome do bloco, definido pelo usuário no editor (ex.: "Cabeçalho", "Análise IA") |
| `generated` | objeto | Resultado da geração do bloco. **Pode estar vazio (`{}`)** se a geração falhou. |
> \[!INFO]
> **Comportamento de falha por bloco:** chaves vazias **não aparecem** em `generated`. Não há `null` nem `undefined` explícitos — basta verificar presença da chave. Se a geração de um bloco inteiro falhou, `generated` pode vir como `{}`.
***
## Detalhamento por `type`
### A. `type: "text"`
Texto fixo configurado pelo usuário, com **variáveis renderizadas** (`{{venda_total}}` etc. já substituídas pelos valores do alerta).
```json
{
"id": 11,
"type": "text",
"nome": "Cabeçalho",
"generated": {
"text": "3 vendas com margem negativa em Loja Centro"
}
}
```
| Campo | Tipo | Descrição |
|---|---|---|
| `generated.text` | string | Texto final, com variáveis já substituídas |
***
### B. `type: "ai"`
Análise gerada pelo agente IA. Inclui o **texto final** e a **trilha de passos** (passos de pensamento que o agente executou). A trilha permite que o seu receptor referencie buscas e gráficos gerados durante a análise.
```json
{
"id": 12,
"type": "ai",
"nome": "Análise IA",
"generated": {
"text": "Resumo: 3 vendas com prejuízo total de R$ 1.230 em Loja Centro nas últimas 24h. Recomendo revisar política de desconto manual.",
"steps": [
{
"kind": "query",
"title": "Vendas com margem negativa nas últimas 24h",
"chartUrl": "https://storage.horusbi.com.br/download/a1b2c3.png",
"permalink": "https://lumo.exemplo.com/app/12/report?filter=...",
"rowCount": 3,
"summary": "3 linhas retornadas"
},
{
"kind": "search",
"searchText": "filial centro",
"appName": "Vendas",
"columnLabel": "Filial",
"resultCount": 1
}
]
}
}
```
| Campo | Tipo | Descrição |
|---|---|---|
| `generated.text` | string | Texto final da análise (resposta consolidada do agente) |
| `generated.steps` | array | Passos intermediários do agente. **Pode ser vazio (`[]`)** |
#### Variantes de `steps[]`
Cada passo tem um campo `kind` que define o formato. Hoje existem duas variantes:
**B.1 — `kind: "query"`** (consulta a uma aplicação):
| Campo | Tipo | Descrição |
|---|---|---|
| `kind` | `"query"` | Tipo do passo |
| `title` | string | Título descritivo da consulta (gerado pela IA) |
| `chartUrl` | string | ausente | URL do gráfico PNG gerado para esta consulta. Pode estar ausente se a IA não gerou gráfico |
| `permalink` | string | Link para abrir esta mesma consulta no Lumo (com filtros aplicados) |
| `rowCount` | número | Quantidade de linhas retornadas pela consulta |
| `summary` | string | Resumo textual do resultado |
**B.2 — `kind: "search"`** (validação de dimensão / valor):
| Campo | Tipo | Descrição |
|---|---|---|
| `kind` | `"search"` | Tipo do passo |
| `searchText` | string | Texto que a IA buscou |
| `appName` | string | Nome da aplicação onde a IA buscou |
| `columnLabel` | string | Coluna em que a busca foi feita |
| `resultCount` | número | Quantos valores foram encontrados |
> \[!TIP]
> Novos `kind` podem ser adicionados no futuro. Trate `kind` como **enum aberto**: se receber um valor desconhecido, ignore o passo e siga em frente — o `generated.text` continua válido.
***
### C. `type: "report"`
Relatório exportado. `pdf` e `xlsx` são **independentemente opcionais** — o bloco pode conter só PDF, só XLSX, os dois, ou nenhum (se a geração falhou).
```json
{
"id": 13,
"type": "report",
"nome": "Detalhamento",
"generated": {
"pdf": "https://storage.horusbi.com.br/download/a1b2c3.pdf",
"xlsx": "https://storage.horusbi.com.br/download/a1b2c3.xlsx"
}
}
```
| Campo | Tipo | Descrição |
|---|---|---|
| `generated.pdf` | string | ausente | URL do PDF |
| `generated.xlsx` | string | ausente | URL do XLSX |
> \[!WARNING]
> **Sempre verifique presença das chaves antes de usar.** Um relatório com apenas Excel terá `generated: { "xlsx": "..." }` (sem `pdf`). Um relatório que falhou na geração de ambos terá `generated: {}`.
***
### D. `type: "chart"`
O tipo `chart` tem **duas variantes** muito diferentes. Você precisa olhar o conteúdo de `generated` para distinguir.
#### D.1 — Widget único (PNG)
Snapshot de um widget isolado. Sempre PNG.
```json
{
"id": 14,
"type": "chart",
"nome": "Gráfico de Margem",
"generated": {
"chart": "https://storage.horusbi.com.br/download/a1b2c3.png"
}
}
```
| Campo | Tipo | Descrição |
|---|---|---|
| `generated.chart` | string | URL do PNG |
#### D.2 — Dashboard inteiro
Snapshot de um dashboard completo. Pode ser PDF ou outro formato.
```json
{
"id": 15,
"type": "chart",
"nome": "Dashboard Operacional",
"generated": {
"kind": "dashboard",
"format": "pdf",
"url": "https://storage.horusbi.com.br/download/a1b2c3.pdf"
}
}
```
| Campo | Tipo | Descrição |
|---|---|---|
| `generated.kind` | `"dashboard"` | Marca explícita de variante |
| `generated.format` | string | Formato do arquivo (ex.: `"pdf"`) |
| `generated.url` | string | ausente | URL do arquivo. **Pode estar ausente em caso de falha** |
| `generated.error` | string | ausente | Mensagem de erro, presente quando a geração falhou |
**Em falha:**
```json
{
"id": 15,
"type": "chart",
"nome": "Dashboard Operacional",
"generated": {
"kind": "dashboard",
"format": "pdf",
"error": "Tempo limite excedido ao gerar o dashboard"
}
}
```
> \[!TIP]
> **Como diferenciar as variantes de `chart`:**
>
> ```ts
> if ("kind" in item.generated && item.generated.kind === "dashboard") {
> // D.2 — Dashboard
> } else if ("chart" in item.generated) {
> // D.1 — Widget único
> }
> ```
***
## URLs de Download
Todas as URLs de arquivos (PDF, XLSX, PNG) apontam para `https://storage.horusbi.com.br/download/...`.
> \[!DANGER]
> **Essas URLs NÃO são permanentes.** Não expiram em tempo fixo, mas há **limpeza periódica** do storage. Um arquivo gerado pode deixar de existir após algumas semanas.
>
> **Recomendação:** ao receber o payload, **baixe e armazene imediatamente** os arquivos no seu próprio storage. Não persista a URL do Lumo no seu banco de dados como se fosse permanente.
Boas práticas para download:
* Use HTTP GET; nenhum header de autenticação é necessário.
* Tamanhos típicos: PNG até alguns MB, PDF/XLSX podem chegar a dezenas de MB.
* Em caso de 404, considere a entrega "URL expirada" e marque como degradada no seu lado (mas a entrega original do Lumo ainda foi um sucesso).
***
## Garantias e Não-Garantias
### O que o Lumo garante
* **Entrega pelo menos uma vez (at-least-once)** por destinatário: a mesma entrega pode chegar várias vezes em caso de retry com resposta lenta. Implemente idempotência ([detalhes](./advanced.md#idempotencia)).
* **Ordem dentro de `content[]`**: a ordem dos blocos é estável e espelha o editor.
* **Encoding UTF-8** no corpo JSON.
### O que o Lumo NÃO garante
* **Ordem entre entregas**: se dois alertas dispararem quase ao mesmo tempo, eles podem chegar ao seu endpoint fora da ordem cronológica de disparo.
* **Unicidade por destinatário**: em retry agressivo, o mesmo payload (mesmo `alert.id` + `timestamp`) pode chegar 2+ vezes.
* **Permanência das URLs de arquivos**: como dito acima, baixe assim que receber.
***
## JSON Schema completo
Esquema formal (JSON Schema Draft 2020-12) para validação no seu receptor:
```json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["alert", "user", "tenantId", "timestamp", "content"],
"properties": {
"alert": {
"type": "object",
"required": ["id", "nome", "type"],
"properties": {
"id": { "type": "integer" },
"nome": { "type": "string" },
"type": { "type": "string", "enum": ["condition", "trigger"] },
"app_id": { "type": ["integer", "null"] }
}
},
"user": {
"type": "object",
"required": ["id"],
"properties": {
"id": { "type": "integer" },
"nome": { "type": "string" },
"email": { "type": "string" }
}
},
"tenantId": { "type": "integer" },
"timestamp": { "type": "string", "format": "date-time" },
"content": {
"type": "array",
"items": {
"type": "object",
"required": ["id", "type", "nome", "generated"],
"properties": {
"id": { "type": "integer" },
"type": { "type": "string", "enum": ["text", "ai", "chart", "report"] },
"nome": { "type": "string" },
"generated": {
"type": "object",
"oneOf": [
{
"title": "text",
"properties": { "text": { "type": "string" } }
},
{
"title": "ai",
"properties": {
"text": { "type": "string" },
"steps": {
"type": "array",
"items": {
"type": "object",
"required": ["kind"],
"properties": {
"kind": { "type": "string" }
}
}
}
}
},
{
"title": "report",
"properties": {
"pdf": { "type": "string", "format": "uri" },
"xlsx": { "type": "string", "format": "uri" }
}
},
{
"title": "chart-widget",
"properties": {
"chart": { "type": "string", "format": "uri" }
}
},
{
"title": "chart-dashboard",
"required": ["kind", "format"],
"properties": {
"kind": { "const": "dashboard" },
"format": { "type": "string" },
"url": { "type": "string", "format": "uri" },
"error": { "type": "string" }
}
}
]
}
}
}
}
}
}
```
***
## Tipos TypeScript
Para uso direto no seu receptor em Node.js, Deno ou ambiente TypeScript:
```typescript
// ---------- Envelope ----------
export interface AlertWebhookPayload {
alert: {
id: number;
nome: string;
type: "condition" | "trigger";
app_id: number | null;
};
user: {
id: number;
nome?: string;
email?: string;
};
tenantId: number;
/** ISO 8601 UTC, ex.: "2026-05-27T10:30:00.000Z" */
timestamp: string;
content: ContentBlock[];
}
// ---------- Blocos de conteúdo ----------
export type ContentBlock =
| TextBlock
| AIBlock
| ReportBlock
| ChartWidgetBlock
| ChartDashboardBlock;
export interface BaseBlock {
id: number;
nome: string;
}
export interface TextBlock extends BaseBlock {
type: "text";
generated: { text: string } | {};
}
export interface AIBlock extends BaseBlock {
type: "ai";
generated:
| {
text: string;
steps: AIStep[];
}
| {};
}
export type AIStep = AIQueryStep | AISearchStep;
export interface AIQueryStep {
kind: "query";
title: string;
chartUrl?: string;
permalink: string;
rowCount: number;
summary: string;
}
export interface AISearchStep {
kind: "search";
searchText: string;
appName: string;
columnLabel: string;
resultCount: number;
}
export interface ReportBlock extends BaseBlock {
type: "report";
generated: {
pdf?: string;
xlsx?: string;
};
}
export interface ChartWidgetBlock extends BaseBlock {
type: "chart";
generated: { chart: string };
}
export interface ChartDashboardBlock extends BaseBlock {
type: "chart";
generated: {
kind: "dashboard";
format: string;
url?: string;
error?: string;
};
}
// Helper para distinguir as 2 variantes de chart
export function isDashboardChart(
b: ChartWidgetBlock | ChartDashboardBlock
): b is ChartDashboardBlock {
return "kind" in b.generated && b.generated.kind === "dashboard";
}
```
***
## Próximos Passos
* [Como Receber e Testar](./receivers.md) — exemplos práticos em webhook.site, curl, n8n, Make, Zapier, Node.js, Python.
* [Avançado](./advanced.md) — idempotência, validação de origem, integração WhatsApp via Meta, limitações.
* Voltar para a [Visão Geral do Webhook](./index.md).
---
---
url: 'https://docs.horusbi.com.br/api/files.md'
---
# Repositório de Arquivos — API REST
Esta página documenta a API REST de gestão de arquivos da plataforma HorusBI, disponível para integrações server-to-server (headless). Com ela, você pode fazer upload, listar, consultar metadados, controlar visibilidade, gerar links temporários e excluir arquivos do repositório de um tenant.
## Envelope de resposta
Todos os endpoints de `/v1/tenant/files*` retornam respostas no **envelope padrão da plataforma**:
**Sucesso:**
```json
{ "status": "success", "data": }
```
**Erro:**
```json
{ "status": "error", "message": "Descrição do erro" }
```
Os exemplos de resposta ao longo desta página já mostram o envelope completo.
## Autenticação
Todos os endpoints ficam sob `/v1/tenant` e usam o **Tenant Bearer Token** (`sys_tenant_api_token`):
```http
Authorization: Bearer
```
O token identifica o tenant. Sem ele, todas as rotas retornam `401 Unauthorized`.
> **Escopo:** os endpoints REST de Arquivos operam no tenant do token bearer e **não** são filtrados por usuário (sem owner-scoping). O controle por função (`files`) e por kind (ETL via `flow`) aplica-se às telas do HEC/ETL, não à API headless.
## Origens dos arquivos
Cada arquivo tem uma `origin` que indica como foi criado:
| Origin | Quem cria | Padrão de visibilidade |
|--------|-----------|----------------------|
| `api` | REST API (este endpoint) | Privado |
| `hec` | Upload manual via HEC, ou via presign+confirm na REST | Privado |
| `dw` | Sistema (writeback de Cadastros, exportações) | Público |
| `etl` | Pipeline ETL | Privado |
## Fluxo de upload grande
Para arquivos grandes (ex.: parquets de 700 MB), use o fluxo presigned que evita passar bytes pelo backend:
```
1. POST /v1/tenant/files/presign → { id, key, uploadUrl }
2. PUT → (direto no S3, Content-Type no header)
3. POST /v1/tenant/files/confirm → FileDTO completo
```
Para arquivos pequenos (até ~1 GB), `/v1/tenant/files/upload` com `multipart/form-data` também funciona.
> **Limite por arquivo:** o backend lê o tamanho real via `HeadObject` no S3 ao confirmar o upload (campo `file_size` não é aceito no body do `/files/confirm`). O teto é 2 147 483 647 bytes (~2 GB, limite da coluna `int4`). O `/files/upload` (multipart) é limitado a ~1 GB pelo middleware de streaming.
> **Content-Type no PUT do presign (footgun):** ao fazer o `PUT` diretamente na `uploadUrl` retornada pelo `/files/presign`, o cliente **deve** enviar o header `Content-Type` com o valor **exatamente igual** ao `mime` informado na chamada ao `/files/presign`. O S3 inclui o `Content-Type` na assinatura da URL; qualquer divergência resulta em `SignatureDoesNotMatch` (HTTP 403).
***
## Shape: FileDTO
Todos os endpoints de leitura retornam objetos no formato `FileDTO`:
| Campo | Tipo | Descrição |
|-------|------|-----------|
| `id` | string (UUID) | Identificador único do arquivo |
| `file_name` | string | Nome do arquivo |
| `file_size` | integer | null | Tamanho em bytes (null enquanto o presign não é confirmado) |
| `mime` | string | null | MIME type (ex.: `application/parquet`, `image/png`) |
| `origin` | string | Origem: `api`, `hec`, `dw` ou `etl` |
| `public` | boolean | `true` = URL pública sem autenticação; `false` = requer token assinado |
| `criado_por` | integer | null | ID do usuário que fez o upload; `null` quando não há contexto de usuário |
| `criado_em` | string (ISO 8601) | Data/hora de criação |
| `url` | string | URL canônica do arquivo: `https://storage.horusbi.com.br/f/{id}/{file_name}` |
| `uploader` | string | Rótulo legível de quem enviou: nome do usuário (quando `criado_por != null`), ou `"API"` / `"ETL"` / `"Sistema"` (quando `criado_por` é null, derivado da `origin`) |
> **`uploader` — lógica de resolução:** se `criado_por` está preenchido, é o `nome` do usuário em `sys_user`; se não está, o rótulo vem da `origin`: `api` → `"API"`, `etl` → `"ETL"`, `dw` ou `hec` → `"Sistema"`.
***
## Referência dos endpoints
### `GET /v1/tenant/files`
Lista os arquivos do tenant, paginado, ordenado por data de criação decrescente. Exclui arquivos marcados como excluídos (`excluido=true`).
> **Nota:** uploads presigned abandonados (reservas criadas pelo `/presign` mas nunca confirmadas) **não aparecem** nesta listagem. Apenas arquivos com upload concluído são retornados.
**Query params:**
| Parâmetro | Tipo | Padrão | Descrição |
|-----------|------|--------|-----------|
| `limit` | integer | `50` | Máximo de registros por página (teto: `200`) |
| `offset` | integer | `0` | Número de registros a pular (paginação) |
| `origin` | string | — | Filtro de origem: `api`, `hec`, `dw` ou `etl` |
**Resposta (200):**
```json
{
"status": "success",
"data": {
"rows": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"file_name": "relatorio-junho.parquet",
"file_size": 734003200,
"mime": "application/octet-stream",
"origin": "api",
"public": false,
"criado_por": null,
"criado_em": "2026-06-26T14:30:00.000Z",
"url": "https://storage.horusbi.com.br/f/a1b2c3d4-e5f6-7890-abcd-ef1234567890/relatorio-junho.parquet",
"uploader": "API"
}
],
"total": 42
}
}
```
***
### `GET /v1/tenant/files/:id`
Retorna os metadados de um único arquivo. Retorna `404` se o arquivo não existir, pertencer a outro tenant, ou estiver excluído.
**Path params:** `:id` — UUID do arquivo
**Resposta (200):**
```json
{ "status": "success", "data": { /* FileDTO — ver shape acima */ } }
```
**Resposta (404):**
```json
{ "status": "error", "message": "Arquivo não encontrado" }
```
***
### `POST /v1/tenant/files/upload`
Upload multipart de um arquivo (caminho compatível, adequado para arquivos até ~1 GB). O arquivo é transmitido em streaming direto para o S3 — sem bufferizar em memória.
**Tipo de conteúdo:** `multipart/form-data`
| Campo | Tipo | Obrigatório | Descrição |
|-------|------|-------------|-----------|
| `file` | file | sim | O arquivo a fazer upload (nome do campo deve ser `"file"`) |
| `filename` | string | não | Nome desejado para o arquivo; se omitido, usa o nome original do upload |
> O limite de tamanho é ~1 GB. Para arquivos maiores, use o fluxo presign → PUT → confirm.
**Resposta (200):**
```json
{
"status": "success",
"data": {
"url": "https://storage.horusbi.com.br/f/a1b2c3d4-e5f6-7890-abcd-ef1234567890/relatorio-junho.parquet",
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}
```
> **Nota:** esta rota cria o arquivo com `origin: "api"` e `criado_por: null`. Arquivos com o mesmo `file_name` no tenant são reutilizados (idempotente por nome): se já existe um arquivo ativo com esse nome, o upload substitui o conteúdo no S3 e atualiza os metadados.
**Respostas de erro:**
| Código | Situação |
|--------|----------|
| `400` | Nenhum arquivo enviado, ou campo não se chama `"file"` |
| `500` | Erro interno (ex.: falha no S3) |
***
### `POST /v1/tenant/files/presign`
**Passo 1 do fluxo de upload grande.** Cria a linha no banco (origin=`hec`, public=`false`) e retorna uma URL pré-assinada para `PUT` direto no S3. A URL expira em **15 minutos** (900 segundos).
**Body (JSON):**
| Campo | Tipo | Obrigatório | Descrição |
|-------|------|-------------|-----------|
| `file_name` | string | sim | Nome do arquivo a criar |
| `mime` | string | não | MIME type (ex.: `application/parquet`); padrão: `application/octet-stream` |
**Resposta (200):**
```json
{
"status": "success",
"data": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"key": "42/a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"uploadUrl": "https://horusbi-uploads.s3.amazonaws.com/42/a1b2c3d4-...?X-Amz-Signature=..."
}
}
```
**Respostas de erro:**
| Código | Situação |
|--------|----------|
| `400` | `file_name` ausente |
***
### `POST /v1/tenant/files/confirm`
**Passo 3 do fluxo de upload grande.** Após o `PUT` direto no S3, confirme o upload para preencher `file_size`, `mime` e `file_path` na linha criada pelo `/presign`. Retorna o `FileDTO` completo.
> **Tamanho lido do S3:** o backend consulta o S3 via `HeadObject` para obter o tamanho real do objeto — o campo `file_size` **não é aceito** no body. O confirm falha se o `PUT` não tiver sido concluído (objeto ausente no S3). O teto de ~2 GB é aplicado contra o tamanho real lido.
**Body (JSON):**
| Campo | Tipo | Obrigatório | Descrição |
|-------|------|-------------|-----------|
| `id` | string | sim | UUID retornado pelo `/presign` |
| `mime` | string | não | MIME type efetivo do arquivo |
**Resposta (200):**
```json
{ "status": "success", "data": { /* FileDTO completo — ver shape acima */ } }
```
**Respostas de erro:**
| Código | Situação |
|--------|----------|
| `400` | `id` ausente, ou arquivo não encontrado / já excluído |
| `400` | `"Arquivo não encontrado no storage (upload não concluído)"` — PUT nunca foi feito ou falhou |
| `400` | `"Arquivo excede o limite de ~2 GB por arquivo"` — tamanho real lido do S3 supera o teto `int4` |
***
### `PATCH /v1/tenant/files/:id`
Alterna a visibilidade pública/privada do arquivo.
**Path params:** `:id` — UUID do arquivo
**Body (JSON):**
| Campo | Tipo | Obrigatório | Descrição |
|-------|------|-------------|-----------|
| `public` | boolean | sim | `true` = público (URL sem autenticação); `false` = privado (requer token assinado) |
**Resposta (200):**
```json
{ "status": "success", "data": { "ok": true } }
```
**Respostas de erro:**
| Código | Situação |
|--------|----------|
| `400` | Arquivo não encontrado |
***
### `POST /v1/tenant/files/:id/sign`
Gera um **link temporário assinado** para um arquivo privado. O token é armazenado no Redis com TTL; a URL resultante pode ser compartilhada com qualquer pessoa durante a validade.
**Path params:** `:id` — UUID do arquivo
**Body (JSON):**
| Campo | Tipo | Obrigatório | Descrição |
|-------|------|-------------|-----------|
| `ttl` | integer | não | Validade em segundos. Intervalo aceito: **60 – 604800** (1 min a 7 dias). Padrão: `3600` (1h). Valores fora do intervalo são clamped para o mínimo ou máximo; ausente/inválido → padrão. |
**Resposta (200):**
```json
{
"status": "success",
"data": {
"token": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
"url": "https://storage.horusbi.com.br/f/a1b2c3d4-e5f6-7890-abcd-ef1234567890/relatorio.parquet?token=a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4"
}
}
```
> **Acesso por visibilidade:**
>
> * **Público** (`public: true`): a URL canônica `https://storage.horusbi.com.br/f/{id}/{file_name}` funciona sem autenticação. O `/sign` ainda funciona para gerar tokens, mas não é necessário.
> * **Privado** (`public: false`): a URL sem `?token=` retorna erro. Use `/sign` para gerar um token temporário e acessar com `?token=`. O token é reutilizável até expirar.
**Respostas de erro:**
| Código | Situação |
|--------|----------|
| `400` | Arquivo não encontrado |
***
### `DELETE /v1/tenant/files/:id`
Soft-delete do arquivo: marca como excluído (`excluido=true`) sem remover do S3 imediatamente. O arquivo deixa de aparecer nas listagens e não é mais acessível. O storage para de ser contado no billing a partir da exclusão.
**Restrição:** arquivos de origem `dw` que sejam imagens de Cadastro (writeback) **e que ainda estejam referenciados** em registros ativos ou histórico são **bloqueados**. O sistema verifica a URL pública antes de excluir.
**Path params:** `:id` — UUID do arquivo
**Resposta (200):**
```json
{ "status": "success", "data": { "ok": true } }
```
**Respostas de erro:**
| Código | Mensagem | Situação |
|--------|----------|----------|
| `400` | `"Arquivo não encontrado"` | ID inexistente, de outro tenant, ou já excluído |
| `400` | `"Arquivo ainda referenciado por um Cadastro (writeback)"` | Imagem de writeback (`origin: "dw"`) com referências ativas em `wb_record` ou `wb_record_history` |
***
## Ciclo de vida e limpeza automática
### Soft-delete e reaper diário
A exclusão via `DELETE /files/:id` é um **soft-delete**: o arquivo é marcado como excluído e some imediatamente das listagens e do billing (não entra mais nos snapshots diários de storage). O conteúdo físico no S3 é removido posteriomente pelo **reaper diário**, que varre os arquivos soft-deletados e os purga do storage — reclamando os bytes e encerrando qualquer cobrança residual.
### Reservas de presign abandonadas
Chamadas a `POST /files/presign` que nunca forem seguidas de um `PUT` + `POST /files/confirm` criam entradas "fantasma" no banco (sem `file_path`). Essas entradas **não aparecem nas listagens** e são limpas automaticamente pelo mesmo processo de reaper, sem nenhuma ação do integrador.
***
## Fluxo de upload grande (presign → PUT → confirm)
Exemplo em JavaScript para upload de um arquivo grande sem passar pelo backend:
```js
const BASE = "https://api.horusbi.com.br/v1/tenant";
const headers = {
Authorization: `Bearer ${TENANT_TOKEN}`,
"Content-Type": "application/json",
};
// 1. Solicitar URL pré-assinada
const presignRes = await fetch(`${BASE}/files/presign`, {
method: "POST",
headers,
body: JSON.stringify({
file_name: "dados-junho.parquet",
mime: "application/octet-stream",
}),
});
const { data: { id, uploadUrl } } = await presignRes.json();
// 2. PUT direto no S3 (sem passar pelo backend)
const fileBuffer = await fs.promises.readFile("./dados-junho.parquet");
await fetch(uploadUrl, {
method: "PUT",
headers: { "Content-Type": "application/octet-stream" },
body: fileBuffer,
});
// 3. Confirmar o upload — file_size NÃO é enviado; o backend lê o tamanho do S3
const confirmRes = await fetch(`${BASE}/files/confirm`, {
method: "POST",
headers,
body: JSON.stringify({
id,
mime: "application/octet-stream",
}),
});
const { data: fileDTO } = await confirmRes.json();
console.log("Arquivo criado:", fileDTO.url);
```
***
## Erros comuns
| Código | Situação |
|--------|----------|
| `400` | Validação do body falhou, arquivo não encontrado, ou guard de writeback ativo |
| `401` | Token ausente ou inválido |
| `404` | Arquivo não encontrado (em `GET /files/:id`) |
| `500` | Erro interno (ex.: falha no S3 ou banco) |
---
---
url: 'https://docs.horusbi.com.br/etl/processors/inputs/http-request.md'
---
# Requisição HTTP (API)
O nó **Requisição HTTP** é um dos mais poderosos do HorusETL. Ele permite conectar a qualquer API REST/JSON externa para buscar ou enviar dados.
***
## ✨ Funcionalidades Principais
* **Métodos Suportados** — GET, POST, PUT, DELETE, PATCH
* **Autenticação** — Suporte a Basic Auth, Bearer Token, API Key (Header/Query) e OAuth2 (via token)
* **Paginação Automática** — Capaz de percorrer múltiplas páginas de resultados automaticamente
* **Loop de Input (Enriquecimento)** — Pode executar uma requisição **para cada linha** que vem do nó anterior (consultar detalhes de um cliente para cada ID de uma lista)
***
## ⚙️ Parâmetros de Configuração
### Geral
* **URL** — Endereço do endpoint (`https://api.exemplo.com/v1/pedidos`)
* **Método** — Verbo HTTP a ser utilizado
### Autenticação
Define como o Horus deve se autenticar na API:
| Tipo | Descrição |
|------|-----------|
| **None** | Sem autenticação |
| **Basic** | Usuário e Senha (codificados em Base64) |
| **Bearer** | Token JWT ou Opaque Token no header `Authorization: Bearer ` |
| **API Key** | Chave inserida no Header ou na Query String |
### Carga (Payload/Body)
Usado principalmente em métodos POST/PUT:
* **Raw Content** — Corpo da requisição em texto puro (geralmente JSON ou XML)
* **Form Data** — Envio de dados como formulário (`application/x-www-form-urlencoded`)
### Paginação
Permite que o Horus percorra todas as páginas de dados:
| Tipo | Comportamento |
|------|---------------|
| **Limit/Offset** | Incrementa um contador de offset |
| **Page Number** | Incrementa o número da página |
| **Cursor / Next Link** | Busca o link da próxima página na resposta atual (campo `next_page_url` no JSON) |
### Configurações de Output
* **Data Path** — Caminho JSON para encontrar a lista de dados (array) na resposta. Exemplo: se a API retorna `{ "status": "ok", "data": [ ...itens... ] }`, o Data Path deve ser `$.data`. Se a API já retornar o Array diretamente na raiz, pode deixar vazio
* **Return Full Response** — Se marcado, retorna o status code e headers junto com o corpo
***
## 🔄 Loop de Input (Iterador)
Esta funcionalidade permite usar dados do nó anterior como parâmetros na requisição:
1. Habilite o **Iterator**
2. Use a sintaxe `${NOME_COLUNA}` na URL ou no Body
3. O Horus fará uma requisição para cada linha recebida
* Exemplo: `https://api.crm.com/clientes/${cliente_id}/compras`
> \[!WARNING]
> O Loop de Input pode ser lento se houver muitos registros. Use com cautela ou habilite a execução paralela (Configurações Avançadas).
***
## 📅 Interpolação de Variáveis (Datas e Incremental)
Além de usar colunas do input (`${COLUNA}`), você pode usar as **Variáveis Globais do Fluxo** para criar cargas dinâmicas e incrementais. As variáveis devem ser usadas no formato `{NomeVar:Formato}` diretamente na URL ou Parâmetros.
### Filtros de Data
Útil para buscar apenas dados de um período específico. As variáveis `StartDate` e `EndDate` são configuradas na execução do fluxo:
| Padrão | Formato | Exemplo de Resultado |
|--------|---------|---------------------|
|**Padrão ISO**| `{StartDate:yyyy-MM-dd}` | `2024-01-01` |
|**Brasileiro**| `{StartDate:dd/MM/yyyy}` | `01/01/2024` |
|**Com Hora**| `{StartDate:yyyy-MM-ddTHH:mm:ss}` | `2024-01-01T12:00:00` |
|**Unix Milissegundos**| `{StartDate:unixms}` | `1704067200000` (Milissegundos) |
**Exemplo de URL**:
`https://api.exemplo.com/vendas?inicio={StartDate:yyyy-MM-dd}&fim={EndDate:yyyy-MM-dd}`
### Carga Incremental
Para buscar apenas dados novos desde a última execução bem-sucedida, use a variável `{LastDataPoint}`. O Horus gerencia automaticamente o valor dessa variável (salvando a maior data/ID processado na execução anterior).
**Exemplo de URL**:
`https://api.crm.com/vendas?updated_at_gt={LastDataPoint:yyyy-MM-dd HH:mm:ss}`
> \[!NOTE]
> Para cargas incrementais, é necessário configurar a tabela de Datawarehouse ou o nó de inserção ao Datalake com as chaves dos registros, permitindo o upsert correto sem duplicar registros.
---
---
url: 'https://docs.horusbi.com.br/etl/guides/requisitos-hardware.md'
---
# Requisitos de Hardware do Agente
O **Agente** do HorusETL roda na sua infraestrutura e é leve por natureza. Ainda assim, o consumo de recursos varia conforme o tipo de fluxo, o paralelismo e o volume de dados. Esta página dá um **número recomendado direto** para repassar à sua equipe de TI e, logo abaixo, explica as nuances para quem precisa dimensionar com mais precisão.
> \[!TIP]
> **Não sabe por onde começar?** Use o perfil **Recomendado** abaixo (**2 vCPU / 4 GB RAM**). Atende a grande maioria dos cenários. Se for rodar transformações **Python/pandas** pesadas, comece direto no perfil de **Alto Desempenho**.
***
## 📐 Perfis Recomendados
| Perfil | vCPU | RAM | Disco livre | Quando usar |
|--------|:----:|:---:|:-----------:|-------------|
| **Mínimo (piso)** | 1 | 2 GB | 10 GB | Baixo volume, **sem** paralelismo, fluxos apenas de banco → Data Warehouse. Roda, mas sem folga. |
| **Recomendado** ✅ | 2 | 4 GB | 10 GB | Uso geral: paralelismo de 2 a 4 fluxos, mix de fontes, volumes moderados. **É este o número padrão.** |
| **Alto Desempenho** | 4+ | 8 GB+ | 20 GB | Python/pandas pesado, alto paralelismo (8+ fluxos simultâneos) ou grandes volumes carregados em memória. |
Esses valores valem tanto para **Windows** quanto para **Linux (Docker)**. Veja também os [pré-requisitos de sistema operacional e runtime](../getting-started/#_1-📋-pre-requisitos).
***
## 🤔 Por que "depende"?
O agente não tem um consumo fixo como o de um banco de dados. O que ele usa varia conforme o que você roda. Dois fatores definem o tamanho da máquina.
### 1. Tipo de fluxo: streaming x carga em memória
* **Banco → Data Warehouse (ODBC, SQL Server, Oracle, etc.):** é **streamado**. O agente lê em lotes pequenos (cerca de 5.000 linhas por vez) e grava direto, mantendo o uso de memória **praticamente constante**, independente do tamanho da tabela. Uma tabela de 10 mil ou de 10 milhões de linhas consome quase a mesma RAM. É o cenário mais leve.
* **Python / pandas:** cada execução de um nó Python sobe um processo separado. Se o script carrega os dados inteiros em memória (por exemplo, `pd.concat` ou `merge` de DataFrames grandes), o consumo sobe rápido, de centenas de MB a vários GB por execução. É o cenário mais pesado.
### 2. Paralelismo e tenants
Cada agendamento que roda ao mesmo tempo é um **processo independente**, então RAM e CPU somam. Mas o que pode rodar em paralelo depende dos tenants, e não só do número configurado em **Agendamentos Simultâneos**:
* **Agendamentos do mesmo tenant nunca rodam em paralelo.** Eles são serializados, executados um de cada vez, em fila. Isso é proposital: protege o banco de origem e o Data Warehouse daquele cliente contra várias extrações simultâneas.
* **O paralelismo ocorre entre tenants diferentes.** Em um agente compartilhado (whitelabel, atendendo vários tenants), o limite de Agendamentos Simultâneos define quantos **tenants distintos** podem rodar ao mesmo tempo.
> \[!IMPORTANT]
> **Consequência prática para o dimensionamento:**
>
> * **Agente de um cliente só (um tenant):** a concorrência real é de cerca de **1 agendamento por vez**, independente do valor de Agendamentos Simultâneos. A RAM é ditada pelo **fluxo mais pesado**, e não pela multiplicação por paralelismo.
> * **Agente compartilhado (whitelabel, vários tenants):** a concorrência real é o **número de tenants rodando juntos no pico** (até o teto configurado). É aqui que a RAM cresce de forma aproximadamente linear. Dimensione por quantos tenants disparam ao mesmo tempo.
O paralelismo é configurado por agente (veja [Gerenciamento de Agentes](./agentes#🔑-criando-um-novo-agente)).
> \[!NOTE]
> **CPU raramente é o gargalo.** Na prática, o consumo de CPU dos agentes fica baixo na média e atinge picos de cerca de **1,5 núcleo** mesmo sob carga e paralelismo. O fator que mais determina o tamanho da máquina é a **memória RAM**.
***
## 🔁 Fila, Paralelismo e Encavalamento
O agente **nunca estoura o limite de paralelismo configurado**. Mesmo que centenas de agendamentos sejam disparados de uma vez, ele executa no máximo o teto definido e **enfileira o restante**, processando conforme os slots liberam. Isso protege a máquina de sobrecarga e mantém o uso de RAM com um teto previsível.
A regra-chave, como visto acima: **agendamentos do mesmo tenant são serializados** (1 por vez) e o **paralelismo só atua entre tenants diferentes**.
### ⚠️ O risco de encavalamento (backlog)
O ponto de atenção é o **acúmulo de fila**. Se o tempo total de execução dos agendamentos de um tenant em um período for **maior que o intervalo de agendamento**, a fila cresce e as execuções vão atrasando progressivamente.
> **Exemplo:** um cliente (tenant) com 10 agendamentos a cada 10 minutos, cada um levando mais de 1 minuto.
>
> * Como são do **mesmo tenant**, rodam **um de cada vez**: 10 × (>1 min) dá **mais de 10 minutos** para drenar. No intervalo seguinte eles já dispararam de novo, então **a fila nunca esvazia** e o atraso só cresce. Esse é o encavalamento.
> * **Aumentar o paralelismo NÃO resolve esse caso**, porque agendamentos do mesmo tenant não rodam em paralelo de qualquer forma.
### Como resolver o encavalamento
A solução depende do cenário:
| Cenário | Como resolver |
|---------|---------------|
| **Um cliente (tenant) com agendamentos se acumulando** | **Espace os agendamentos** (intervalo maior), **reduza o tempo de cada fluxo** (filtros, cargas incrementais) ou **agrupe** fluxos em um único agendamento. Aumentar o paralelismo não ajuda, porque eles serializam. |
| **Agente compartilhado (whitelabel) com muitos tenants** | **Aumente o paralelismo** para permitir mais tenants simultâneos e **aumente a RAM proporcionalmente** (o consumo cresce de forma aproximadamente linear com o nº de tenants rodando juntos). |
> \[!TIP]
> Regra prática: garanta que o **tempo total de execução dos agendamentos de um tenant** caiba dentro do **intervalo de agendamento**. Em agente de cliente único, isso se resolve no desenho dos fluxos e no espaçamento. Em agente whitelabel, também subindo o paralelismo (e a RAM).
***
## 📊 O que observamos na prática
Medições reais de agentes em produção, ao longo de 30 dias, mostram o seguinte padrão:
| Cenário | RAM típica (média) | RAM em pico (p95) | CPU em pico |
|---------|:------------------:|:-----------------:|:-----------:|
| Agente de cliente único (um tenant) | ~1,3 GB | ~1,7 GB | ~1,5 núcleo |
| Agente compartilhado (vários tenants) | ~1,2 GB | ~3 GB | ~1,5 núcleo |
| Agente com Python/pandas pesado | ~2,6 GB | ~4 GB (picos pontuais bem acima) | ~1,5 núcleo |
> \[!TIP]
> Um agente "típico" cabe confortavelmente em **4 GB**. Já cargas Python pesadas podem ultrapassar 8 GB em execuções específicas. Nesses casos, **aumente a RAM** (em agente whitelabel, reduzir o paralelismo também ajuda a limitar quantos tenants rodam juntos).
***
## 💾 Disco e Rede
* **Disco:** reserve espaço para a imagem do agente e para os **arquivos temporários** (Parquet) gerados durante as execuções. Esses temporários são limpos automaticamente, mas existe um pico de uso durante fluxos grandes. **10 GB livres** é um piso seguro; **20 GB** para cargas pesadas.
* **Rede:** apenas **conectividade de saída HTTPS (porta 443)** para a plataforma. O agente é *firewall-friendly* e não precisa de portas abertas para entrada. Detalhes em [Comunicação e Redes](../architecture/communication).
***
## ✅ Resumo
* **Não sabe dimensionar?** Vá de **2 vCPU / 4 GB RAM**, que cobre a maioria dos casos.
* **Só fluxos de banco → DW, volume baixo, sem paralelismo?** **1 vCPU / 2 GB RAM** já roda.
* **Python/pandas pesado ou agente whitelabel com muitos tenants?** **4+ vCPU / 8 GB+ RAM.**
* **CPU dificilmente é o limite, então dimensione pela RAM.** Lembre que agendamentos do mesmo tenant serializam, e o consumo somado de RAM só cresce com o nº de **tenants** rodando juntos.
---
---
url: 'https://docs.horusbi.com.br/lumo/revisar.md'
---
# Revisando o trabalho da IA
A IA opera o `lumo`, mas o resultado é **seu**. A boa notícia: tudo o que ela faz vira **arquivos YAML** num diretório local — então dá para revisar com calma, com as mesmas ferramentas que você já usa para código (Git, diff).
Você não precisa escrever YAML à mão — isso é trabalho da IA. Esta página é para você **reconhecer e conferir** o que ela produziu.
## Onde o trabalho fica
Cada recurso vira um arquivo na pasta correspondente do workspace (`flows/`, `tables/`, `apps/`, `credentials/`…). Para ver o que mudou, peça à IA — ou olhe você mesmo:
* *"o que você mudou?"* — a IA resume e pode rodar `lumo status` / `git diff`.
* Se o workspace está em Git, `git diff` mostra exatamente o que entrou, linha a linha.
> \[!TIP]
> Manter o workspace em **Git** é a melhor rede de segurança: você revisa as mudanças da IA como um *code review* e desfaz com `git checkout` se não gostar.
## Anatomia de um arquivo (para reconhecer)
Todo arquivo do Lumo tem duas partes separadas por `---`: um **cabeçalho** gerenciado pelo CLI e o **corpo** com a configuração de fato.
```yaml
lumo: v2 # versão do schema — gerenciado pelo CLI
kind: flow # tipo do recurso
id: 42866 # id no servidor — gerenciado pelo CLI
tenantId: 522 # checagem de segurança — gerenciado pelo CLI
---
nome: Carga Vendas # ↓ daqui pra baixo é a configuração (o que a IA monta)
load_type: Temporal
# nós do flow, dashboards, colunas, etc.
```
* **Cabeçalho** (acima do `---`): não mexa. O CLI preenche `id`/`tenantId` ao criar o recurso.
* **Corpo** (abaixo do `---`): é a configuração do recurso. É o que você lê para entender o que a IA fez.
> \[!NOTE]
> Recursos grandes (flows e apps) podem aparecer como uma **pasta** em vez de um arquivo único — com `_flow.yaml`, `nodes/`, `dashboards/` etc. É o mesmo conteúdo, só fatiado para facilitar o diff. A IA cuida disso.
## O que conferir antes de publicar
Antes de dar o ok para publicar, vale checar — e dá para pedir tudo isso à IA:
* **Dashboard:** peça a **imagem exportada** e confira o visual (layout, legendas, rótulos). Os números podem estar certos e o visual quebrado.
* **Tabela:** confira que tem as **colunas e as linhas esperadas** (peça a prévia / a contagem). "Enfileirado" não é "carregado".
* **Nomes e destino:** os nomes dos recursos e a **mesa** de destino estão certos?
> \[!TIP]
> Depois de cada `push`, o CLI **reescreve o YAML local com a resposta do servidor**. Ou seja: o que está no arquivo é exatamente o que está no servidor — sem divergência silenciosa.
## O que não mexer
Alguns pontos são gerenciados pelo sistema. Se você (ou a IA) editar à mão, a mudança é ignorada ou causa inconsistência:
* A pasta interna **`.lumo/state/`** — é cache do CLI.
* **`id` / `tenantId`** no cabeçalho — preenchidos pelo servidor.
* **`deskId`** — quem controla é a publicação, não a edição do arquivo.
* **Colunas estruturais de tabela** (nome da coluna, tipo, chave) — pertencem ao flow que produz a tabela, não à tabela.
Você não precisa decorar isso: a IA conhece essas regras. É só para você não se assustar se vir um campo desses "voltar ao normal" sozinho.
## Publicar é o ponto de virada
Lembre-se: **publicar é decisão sua e tem peso**. Um recurso publicado fica **somente-leitura**, é **compartilhado** com outros usuários, e republicar **sobrescreve** a versão anterior (só reversível por clonar → editar → republicar). Por isso a IA constrói tudo como rascunho e espera seu ok.
Construa e itere à vontade como rascunho; quando estiver realmente pronto e revisado, aí sim diga para publicar.
## Veja também
* [Trabalhando com a IA](/lumo/trabalhando-com-ia/) — como pedir as coisas para a IA.
* [Conceitos](/lumo/conceitos/) — rascunho vs publicado, mesas, tipos de carga.
---
---
url: 'https://docs.horusbi.com.br/etl/processors/outputs/parquet.md'
---
# Salvar Parquet
O nó **Salvar Parquet** exporta os dados do fluxo para um arquivo `.parquet` no disco local do Agente.
## Parâmetros de Configuração
### Caminho do Arquivo
* **Descrição**: O caminho completo onde o arquivo será salvo
* **Exemplo**: `/data/output/resultado_final.parquet`
## Uso Comum
* Salvar resultados intermediários para debugging
* Gerar arquivos para serem consumidos por outros sistemas ou processos do Agente
* Criar backups locais de dados extraídos
---
---
url: 'https://docs.horusbi.com.br/lumo/referencia/schedule.md'
description: >-
Referência do YAML de agendamento no Lumo: flows, gatilhos, contextos de carga
e um exemplo completo.
---
# Schedule
Um agendamento executa flows de forma recorrente. Ele vive em `schedules/--.yaml`.
```bash
lumo new schedule --name "Carga Diária"
```
Ligue o autocomplete no editor colando esta linha no topo do arquivo:
```yaml
# yaml-language-server: $schema=https://docs.horusbi.com.br/schemas/v2/schedule.schema.json
```
Antes de agendar: todos os flows precisam estar publicados num dw-desk, e todos precisam estar no mesmo agente. O agendamento herda o agente dos flows e não tem campo de agente próprio.
## Modelo de configuração
```yaml
# ── header ────────────────────────────────────────────────
id: integer # obrigatório, >= 1
kind: schedule # obrigatório, literal "schedule"
lumo: v2 # obrigatório, literal "v2"
tenantId: integer # obrigatório, >= 1
---
# ── body ──────────────────────────────────────────────────
nome: string # obrigatório, mínimo 1 caractere
ativo: boolean # obrigatório
timezone: string # obrigatório, nome IANA (ex: America/Sao_Paulo)
tags: [string]
flows: # obrigatório. executados em sequência
- flow_etl_id: integer # obrigatório
context: string # obrigatório: Total | Incremental | Temporal | Temporal:Days:
triggers: # obrigatório. quando o agendamento dispara
# Hour / Minute: a cada N unidades, dentro de uma janela diária
- type: Hour # Hour | Minute. Minute exige schedulingGranularity=minutes no tenant
period: integer # obrigatório, >= 1
start_when: string # obrigatório, ISO 8601 UTC (ex: 2026-01-01T07:00:00.000Z)
start_time: "HH:MM" # obrigatório, início da janela
end_time: "HH:MM" # obrigatório, fim da janela
# Day: uma vez por dia
- type: Day
start_when: "HH:MM" # obrigatório, hora do dia
# Week: num dia da semana
- type: Week
period: integer # obrigatório, 0=domingo até 6=sábado
start_when: "HH:MM" # obrigatório
# Month: num dia do mês
- type: Month
period: integer # obrigatório, 1 a 31 (dia do mês, não intervalo)
start_when: "HH:MM" # obrigatório
```
`start_when` muda de formato conforme o `type`. Em `Hour` e `Minute`, é um timestamp ISO completo em UTC. Em `Day`, `Week` e `Month`, é apenas a hora do dia, no formato `HH:MM`.
Em `Month`, `period` é o dia do mês, e não um intervalo de meses. O dia 31 é ajustado para o último dia válido nos meses mais curtos.
## Configuração completa
```yaml
# schedules/carga-diaria--2478.yaml
# yaml-language-server: $schema=https://docs.horusbi.com.br/schemas/v2/schedule.schema.json
id: 2478
kind: schedule
lumo: v2
tenantId: 853
---
nome: Carga Diária
ativo: true
# Nome IANA, obrigatório. Sem ele, o horário do gatilho fica ambíguo entre
# UTC e local.
timezone: America/Sao_Paulo
# Executados em sequência: o segundo começa quando o primeiro termina.
# Carregue as dimensões antes dos fatos.
flows:
- flow_etl_id: 44547
context: Total # dimensão pequena: apaga tudo e recarrega
- flow_etl_id: 44531
# Recarrega os últimos 31 dias. A tabela precisa ser key_type: unique,
# senão a janela recarregada duplica as linhas a cada execução.
context: Temporal:Days:31
triggers:
# A cada 2 horas, entre 07:00 e 19:00, no fuso acima.
- type: Hour
period: 2
start_when: "2026-01-01T07:00:00.000Z"
start_time: "07:00"
end_time: "19:00"
tags: [producao]
```
## Especificação: header
### `id`
**Tipo:** integer (>= 1) · **Obrigatório:** sim
### `kind`
**Tipo:** string · **Obrigatório:** sim · **Valor:** `schedule`
### `lumo`
**Tipo:** string · **Obrigatório:** sim · **Valor:** `v2`
### `tenantId`
**Tipo:** integer (>= 1) · **Obrigatório:** sim
## Especificação: body
### `nome`
**Tipo:** string (mínimo 1 caractere) · **Obrigatório:** sim
Nome do agendamento. Descreva o que ele faz e como carrega, por exemplo `Vendas Diário Temporal 7d`.
### `ativo`
**Tipo:** boolean · **Obrigatório:** sim
`false` desliga o agendamento sem apagá-lo. Equivale a `lumo schedule pause`.
### `timezone`
**Tipo:** string (mínimo 1 caractere) · **Obrigatório:** sim
Nome de fuso IANA, como `America/Sao_Paulo`. Define quando os gatilhos disparam. É obrigatório para não deixar o horário ambíguo entre UTC e local.
### `flows`
**Tipo:** array de objetos · **Obrigatório:** sim
Flows executados, em sequência. Cada item exige `flow_etl_id` e `context`. Veja [Especificação: flow do agendamento](#especificacao-flow).
### `triggers`
**Tipo:** array de objetos · **Obrigatório:** sim
Quando o agendamento dispara. Veja [Especificação: trigger](#especificacao-trigger).
### `tags`
**Tipo:** array de string · **Obrigatório:** não · **Default:** `[]`
## Especificação: flow do agendamento {#especificacao-flow}
Cada item de `flows`.
### `flow_etl_id`
**Tipo:** integer (>= 1) · **Obrigatório:** sim
Id do flow. O flow precisa estar publicado num dw-desk.
### `context`
**Tipo:** string · **Obrigatório:** sim
Como o flow carrega nesta execução.
| Valor | Comportamento |
|---|---|
| `Total` | Apaga tudo e recarrega tudo. |
| `Incremental` | Acrescenta ou atualiza, sem apagar. |
| `Temporal` | Recarrega a janela padrão. |
| `Temporal:Days:` | Recarrega os últimos `` dias. |
```yaml
context: Temporal:Days:31
```
Uma janela `Temporal` recarregada sobre uma tabela `key_type: duplicate` duplica as linhas a cada execução. Use `unique`.
## Especificação: trigger {#especificacao-trigger}
Cada item de `triggers`. As chaves exigidas mudam conforme `type`.
### `type`
**Tipo:** string · **Obrigatório:** sim · **Valores:** `Hour`, `Minute`, `Day`, `Week`, `Month`
| Valor | Dispara | Exige |
|---|---|---|
| `Hour` | A cada `period` horas, dentro da janela. | `period`, `start_when`, `start_time`, `end_time` |
| `Minute` | A cada `period` minutos, dentro da janela. | `period`, `start_when`, `start_time`, `end_time` |
| `Day` | Uma vez por dia. | `start_when` |
| `Week` | Num dia da semana. | `period`, `start_when` |
| `Month` | Num dia do mês. | `period`, `start_when` |
`Minute` só funciona em tenant com `schedulingGranularity` igual a `minutes`.
### `period`
**Tipo:** integer · **Obrigatório:** em `Hour`, `Minute`, `Week` e `Month`
O significado muda conforme o `type`.
| `type` | Significado de `period` | Faixa |
|---|---|---|
| `Hour` | Intervalo em horas. | >= 1 |
| `Minute` | Intervalo em minutos. | >= 1 |
| `Week` | Dia da semana, com 0 igual a domingo. | 0 a 6 |
| `Month` | Dia do mês. | 1 a 31 |
### `start_when`
**Tipo:** string · **Obrigatório:** sim
Em `Hour` e `Minute`, é um timestamp ISO 8601 em UTC, como `2026-01-01T07:00:00.000Z`, que marca quando o agendamento passa a valer.
Em `Day`, `Week` e `Month`, é apenas a hora do dia, no formato `HH:MM`.
### `start_time`
**Tipo:** string `HH:MM` · **Obrigatório:** em `Hour` e `Minute`
Início da janela diária em que o gatilho pode disparar.
### `end_time`
**Tipo:** string `HH:MM` · **Obrigatório:** em `Hour` e `Minute`
Fim da janela diária.
## Campos gerenciados pelo servidor
| Campo | O que é |
|---|---|
| `version` | Versão do recurso. |
| `criado_em`, `criado_por`, `publicado_em`, `publicado_por` | Auditoria. |
---
---
url: 'https://docs.horusbi.com.br/dw/architecture/security.md'
---
# Segurança e Governança
No HorusDW, a segurança segue uma hierarquia clara de "quem vê o quê", baseada em um modelo de **delegação** — onde a responsabilidade pelo acesso é distribuída entre a equipe de TI e os donos dos dados.
***
## 🔑 Modelo de Acesso por Delegação
Diferente de sistemas onde a TI precisa aprovar cada acesso individualmente, o Horus distribui a responsabilidade:
### Como Funciona
1. **TI (HEC)** — Define quem são os **Donos dos Datamarts** e quais tabelas pertencem a esses Datamarts
2. **Dono do Datamart (DW)** — Tem autonomia total para conceder acesso às suas tabelas dentro do HorusDW
### Camada acima: bloqueio de Mesa de Dados
Antes de chegar ao grant tabela a tabela, existe uma camada mais ampla, no nível da **Mesa de Dados**, que funciona por **lista de bloqueio (deny-list)**:
* A **Mesa de Dados nasce aberta** — visível a todos os usuários do tenant por padrão. Não há "liberação" a fazer
* Ela só fica restrita quando um administrador adiciona um **bloqueio explícito** (por usuário ou grupo) na aba **Acesso aos Dados**
* Um usuário **bloqueado** na mesa **não vê nenhuma** de suas tabelas/dataflows — o bloqueio vem **antes** de qualquer grant de datamart na ordem de decisão
> \[!WARNING]
> O bloqueio de Mesa de Dados é uma **parede dura para todos os papéis**: o **Admin do Tenant** **honra** o bloqueio — se foi bloqueado de uma mesa, não a vê, sem exceção de administrador. (No lado das Mesas de Aplicação de BI, ao contrário, o Admin do Tenant faz bypass do allow-list.) Modelo completo em **[Controle de Acesso às Mesas](/dw/desks/controle-de-acesso)**.
### Acesso Tabela a Tabela
> \[!IMPORTANT]
> Dentro de uma mesa que o usuário **enxerga**, o acesso ao dado ainda é concedido **tabela por tabela** dentro de cada Datamart. Isso é proposital para permitir segurança granular.
Essa abordagem permite cenários como:
* Liberar a tabela `Vendas` para o estagiário, mas **não** liberar a tabela `Custos`, mesmo que ambas estejam no mesmo Datamart
* Liberar a tabela `Clientes` para todos, mas ocultar a coluna `Telefone` (LGPD)
### Ferramentas de Facilitação
Embora o acesso seja tabela a tabela, o Horus oferece a **Liberação em Massa** para agilizar o processo:
* **Concessão em lote** — Selecione todas as tabelas do seu Datamart e conceda acesso a um novo Grupo com um único clique
* **Remoção em lote** — Ao remover acesso, você também pode fazer isso de forma agrupada
***
## 👥 Níveis de Permissão (Roles)
Dentro do DW, as permissões se dividem em três perfis:
| Perfil | Permissões |
|--------|-----------|
| 👑 **Admin / Criador (HEC)** | Pode criar Mesas e Datamarts; define quem acessa o quê |
| ✏️ **Editor (Analista)** | Tem sua própria "Minha Mesa"; pode subir dados, criar fórmulas e, se tiver a permissão **Publicar**, publicar para as Mesas oficiais às quais tem acesso |
| 👁️ **Leitor (Consumidor)** | Acessa os Datamarts para consumir dados em Dashboards; não pode alterar estruturas, deletar tabelas ou mudar fórmulas |
> \[!NOTE]
> **Publicar** não é um atributo fixo do perfil de Editor: é uma **permissão concedível** na matriz de Funções do Sistema. Qualquer usuário ou grupo pode recebê-la e passar a publicar — sempre restrito às mesas às quais tem acesso. Ver **[Controle de Acesso às Mesas](/dw/desks/controle-de-acesso)**.
---
---
url: 'https://docs.horusbi.com.br/etl/architecture/security.md'
---
# Segurança e Integridade
A segurança no HorusETL é baseada em dois conceitos fundamentais: **Isolamento de Dados** e **Autenticação Segura**.
***
## 🔑 Autenticação (Tokens)
Cada Agente instalado possui uma identidade única garantida por um **Token de Acesso**.
* O Token funciona como um identificador — ele diz ao sistema quem é aquele agente e a qual conta (Tenant) ele pertence
* O Token é gerado no momento da criação do agente no Frontend e deve ser inserido na instalação do serviço
* Se um Token for revogado ou excluído no painel, o agente perde imediatamente o acesso, interrompendo qualquer comunicação
***
## 🔒 Privacidade dos Dados (Isolamento)
Para clientes que executam o agente On-Premise (na própria infraestrutura), o Horus garante o conceito de **Zero Data Storage** no lado da plataforma.
### Como funciona?
1. **Processamento Local** — Quando o agente lê um banco de dados ou um arquivo, os dados são carregados na memória RAM do servidor **onde o agente está rodando**
2. **Transformação Local** — Joins, filtros e cálculos acontecem nessa memória local
3. **Destino** — O resultado é escrito diretamente no destino (outro banco, arquivo, etc.) a partir do agente
> \[!IMPORTANT]
> **Seus dados não passam pelos servidores da HorusBI.** O Backend recebe apenas **metadados** (logs de sucesso/erro, quantidade de linhas processadas e status), garantindo que informações sensíveis de negócio permaneçam sob seu controle.
### Exceções
A única situação onde dados de negócio saem da sua rede é se você explicitamente configurar um nó de destino para enviar dados para a nuvem (Google Sheets, Nuvem da HorusDW). Mesmo nesses casos, o transporte é feito utilizando **protocolos seguros (HTTPS/SSL)**, garantindo a integridade e confidencialidade no trânsito.
---
---
url: 'https://docs.horusbi.com.br/etl/processors/transforms/sql-duckdb.md'
---
# SQL (DuckDB)
O nó **SQL** é a ferramenta mais poderosa e recomendada para transformação de dados no HorusETL. Ele utiliza o motor **DuckDB** embutido para executar consultas analíticas (OLAP) de altíssima performance diretamente em memória.
> \[!TIP]
> **Por que usar DuckDB?** Tudo que você conseguir fazer via SQL no DuckDB será o mais rápido possível. Para lógicas que SQL não resolve bem (expressões regulares complexas, integrações com APIs, machine learning), use o nó **Python**. Para operações puramente SQL (JOINs, GROUP BY, Window Functions), o DuckDB supera o Python em performance.
## Ambiente de Execução
* **Motor**: DuckDB (Altamente compatível com o dialeto PostgreSQL)
* **Abstração**: Os dados de entrada são convertidos automaticamente em **Tabelas Temporárias** disponíveis para consulta imediata
* **Case Sensitivity**: Para garantir consistência no ambiente de dados do Horus, todas as referências a colunas devem ser feitas em **MAIÚSCULAS**
## Configuração
### 1. Alias de Entrada
Ao conectar múltiplos fluxos a este nó, você deve identificar cada um:
* **Padrão**: Se não configurado, os inputs recebem nomes sequenciais: `input_0`, `input_1`, `input_2`
* **Customizado**: Você pode (e deve) dar apelidos semânticos na aba de conexões (mudar `input_0` para `VENDAS` e `input_1` para `CLIENTES`)
### 2. Consulta SQL
Escreva qualquer consulta SQL válida. O resultado do `SELECT` final será o output do nó.
## Melhores Práticas e Exemplos
O editor conta com um assistente de IA que segue princípios rígidos de engenharia de dados. Abaixo estão padrões recomendados:
### Exemplo 1: Join e Agregação
```sql
-- Seleciona vendas e cruza com metas
SELECT
v.ID_VENDEDOR,
v.NOME,
SUM(v.VALOR) as TOTAL_VENDAS,
MAX(m.META) as META_ALVO,
-- Lógica de negócio via CASE
CASE
WHEN SUM(v.VALOR) >= MAX(m.META) THEN 'ATINGIU'
ELSE 'NAO ATINGIU'
END as STATUS_META
FROM VENDAS v
LEFT JOIN METAS m ON v.ID_VENDEDOR = m.ID_VENDEDOR
-- Sempre filtre antes de agrupar para performance
WHERE v.DATA_VENDA >= '2024-01-01'
GROUP BY v.ID_VENDEDOR, v.NOME
```
> \[!WARNING]
> **Cuidado ao unir duas Tabelas Fato.** O JOIN acima só está seguro porque cada vendedor tem no máximo uma linha em `METAS`. Se um vendedor tivesse mais de uma venda **e** mais de uma meta, o `SUM(v.VALOR)` multiplicaria pelo número de linhas do outro lado do JOIN antes do `GROUP BY` conseguir agrupar (explosão cartesiana): o resultado ficaria errado mesmo com a agregação correta. Prefira agregar cada fato separadamente em CTEs antes de unir, como no Exemplo 2. Veja o problema e a correção rodando ao vivo no DuckDB do navegador:
### Exemplo 2: Consultas Complexas (CTE)
Para lógicas complexas, prefira usar CTEs (`WITH`) ao invés de sub-queries aninhadas. Isso melhora a legibilidade e a manutenção.
```sql
-- 1. Calcula o total por vendedor (Tabela Temporária Lógica)
WITH VendasAgregadas AS (
SELECT
ID_VENDEDOR,
SUM(VALOR) as VALOR_TOTAL
FROM input_0
GROUP BY ID_VENDEDOR
),
-- 2. Cria um ranking baseado no valor total
Ranking AS (
SELECT
ID_VENDEDOR,
VALOR_TOTAL,
-- Window Function do DuckDB
ROW_NUMBER() OVER (ORDER BY VALOR_TOTAL DESC) as POSICAO
FROM VendasAgregadas
)
-- 3. Seleciona apenas o TOP 10
SELECT *
FROM Ranking
WHERE POSICAO <= 10
ORDER BY POSICAO ASC
```
### Exemplo 3: Extração de JSON (Sintaxe Simplificada)
Use o operador `->>` para extrair texto e `->` para extrair objetos/arrays.
```sql
SELECT
ID,
-- Sintaxe de seta (similar ao Postgres)
RESPOSTA_API->>'$.status' as STATUS_API,
-- Casting direto
(RESPOSTA_API->>'$.data.metrics.total')::DOUBLE as TOTAL
FROM input_0
WHERE json_valid(RESPOSTA_API) -- Garante que é um JSON válido
```
### Exemplo 4: Explodir Array JSON (Lateral Join)
Se a API retorna uma lista de itens dentro de um único registro e você quer transformar isso em várias linhas (uma por item), use a função `json_each` combinada com `LATERAL`.
```sql
SELECT
t.ID,
-- Extrai campos de dentro do objeto do array (que está na coluna 'value' do json_each)
item.value->>'$.produto_id' as PRODUTO_ID,
item.value->>'$.quantidade' as QTD
FROM input_0 t,
-- Explode o array "itens" do JSON em novas linhas
LATERAL (SELECT value FROM json_each(t.RESPOSTA_API->'$.itens')) as item
```
> \[!TIP]
> **Performance**:
>
> * O DuckDB é colunar e vetorizado. Diferente do Python, ele não precisa serializar dados para uma linguagem externa.
> * Sempre que possível, prefira resolver transformações via SQL (Joins, Filtros, Agregações, Window Functions) antes de recorrer a scripts Python ou C#.
---
---
url: 'https://docs.horusbi.com.br/dataviz/03-widgets/table.md'
---
# Tabela
O Widget de **Tabela** é uma ferramenta de alta performance projetada para exibir grandes volumes de dados detalhados dentro dos Dashboards.
## 🔍 Visões e Navegação (Drill-through)
Uma das funcionalidades mais poderosas da Tabela é o sistema de **Visões Alternativas**. Ele permite criar múltiplas "abas" ou estados diferentes para os mesmos dados, facilitando a exploração progressiva.
### 1. Visão Principal vs Alternativas
* **Visão Principal**: É a configuração padrão exibida quando o Widget carrega
* **Visões Alternativas**: São configurações adicionais (com colunas, filtros e ordenações próprias) que podem ser acessadas via menu dropdown ou gatilhos automáticos
### 2. Gatilhos Automáticos (Disparar por Filtro)
É possível configurar uma Visão para ser ativada automaticamente quando o usuário realiza um filtro específico no Dashboard.
* **Cenário**: Imagine uma tabela de "Vendas por Estado"
* **Configuração**: Crie uma Visão Alternativa "Detalhe por Cidade" e configure o gatilho na coluna "Estado"
* **Comportamento**: Quando o usuário clicar em uma barra de outro gráfico filtrando "SP", a tabela detecta o filtro e alterna automaticamente para a Visão de "Cidades de SP". Isso cria uma experiência fluida de Drill-through sem necessidade de botões ou links
***
## 📐 Layout e Colunas
### Largura das Colunas
Há controle total sobre o comportamento na tela:
* **Automático (Padrão)**: O navegador decide a largura baseada no conteúdo. Se houver muitas colunas, uma barra de rolagem horizontal aparecerá automaticamente
* **Manual**: Permite definir a largura exata de cada coluna em % (porcentagem)
* **Comportamento**: O modo manual força a tabela a ocupar exatos 100% da largura disponível, desabilitando a rolagem horizontal. É ideal para criação de tabelas limpas e fixas
* **Texto Excedente**: É necessário decidir o que fazer quando o texto for maior que a coluna:
* `Automático`: Adapta o texto na coluna
* `Quebrar Linha`: Aumenta a altura da linha para melhor encaixe do texto
* `Reticências (...)`: Corta o texto e adiciona "..." ao final
### Congelamento de Colunas
* **Congelar Colunas**: Fixa as colunas iniciais selecionadas à esquerda, mantendo-as sempre visíveis durante a rolagem horizontal
* **Quantidade de Colunas**: Configure quantas colunas congelar (mínimo 1, máximo = total de colunas - 1)
> \[!TIP]
> Essencial para tabelas largas onde a coluna *"Nome"* ou *"Código"* precisa estar sempre visível durante a navegação horizontal.
### Contador de Linhas (#)
* Adiciona uma coluna à esquerda numerando as linhas (1, 2, 3...)
> \[!NOTE]
> Este número é visual e fixo. Se a tabela for reordenada, o primeiro registro continuará sendo o número 1.
### Alinhamento Padrão
Configure o alinhamento padrão para toda a tabela:
* **Alinhamento do Cabeçalho**: Define como os títulos das colunas são alinhados. Opções: `Padrão`, `Esquerda`, `Centralizado`, `Direita`
* **Alinhamento do Corpo**: Define como os dados nas células são alinhados. Opções: `Padrão`, `Esquerda`, `Centralizado`, `Direita`
> \[!TIP]
> No modo "Padrão", os números são automaticamente alinhados à direita e textos à esquerda. Use as opções "Forçar" para sobrescrever esse comportamento.
***
## 📊 Totalizadores
A tabela pode exibir uma linha de rodapé com totais. Existem dois modos de cálculo, configuráveis por coluna (Engrenagem > Lógica do Totalizador):
### 1. Nativo (Expressão)
* **O que é**: O valor vem calculado diretamente do banco de dados/backend
* **Uso**: Obrigatório para médias ponderadas, tickets médios ou razões (`% Margem`). O backend sabe a fórmula correta (`Sum(Lucro) / Sum(Venda)`)
### 2. Manual (Soma/Média/Min/Max)
* **O que é**: O frontend pega os valores visíveis na tabela e aplica uma operação matemática simples
* **Uso**: Útil quando o usuário deseja apenas somar o que "está vendo"
> \[!WARNING]
> Em tabelas filtradas, isso pode gerar confusão se o conceito matemático da métrica não permitir soma simples (somar porcentagens).
***
## 🎨 Formatação Avançada
Cada coluna possui um menu de configuração detalhado (ícone de Engrenagem ou clique no cabeçalho na edição).
### 1. Estética da Célula
* **Rótulo Customizado**: Renomeie a coluna apenas para esta visualização
* **Alinhamento**: Esquerda, Centro ou Direita (pode sobrescrever o padrão numérico)
* **Tooltip**: Adicione explicações que são reveladas ao passar o mouse sobre o cabeçalho (útil para dicionário de dados)
### 2. Formatação Numérica
* **Listas**: Formatos pré-definidos (Moeda, Porcentagem, Inteiro)
### 3. Coloração Condicional
Existem três estratégias para colorir células ou textos (Vermelho/Verde):
* **Por Escala (Heatmap)**: Define faixas numéricas (`0 a 50` = Vermelho, `50 a 100` = Verde)
* **Por Valor**: Define cores para textos exatos (exemplo: Status "Atrasado" = Vermelho)
* **Por Expressão (JavaScript)**: Regra livre para lógica complexa
* Exemplo: `if (this.value > 1000) return '#00ff00'` (Retorna em coloração verde se venda for alta)
* **Estilo**: É possível colorir a **Célula Inteira** (fundo) ou apenas adicionar um **Indicador** (bolinha colorida "traffic light") ao lado do valor
### 4. Formatação de Linha Inteira (Row Formatter)
Na configuração global da tabela, é possível definir uma expressão que reflete na linha inteira, não apenas em uma célula.
* Colore o fundo da linha de cinza se o produto estiver "Inativo"
* Para configurar, marque a opção "Formatação avançada de linha", na seção "Avançado" da aba Visual
* Utilize expressões JavaScript para retornar estilos CSS baseados nos valores da linha
***
## 🖥️ Aparência Global
### Estilo Visual
* **Aparência**: Escolha entre *Padrão*, *Transparente* ou *Destacado* para alterar a moldura visual do Widget
* **Densidade (Espaçamento)**: *Pequeno*, *Médio* ou *Grande*. Reflete na altura das linhas
* **Zebrado**: Alterna cores de fundo das linhas para facilitar a leitura em tabelas largas
* **Fonte**: Configura o tamanho das fontes em *Pequeno*, *Médio* ou *Grande*
### Quebra de Linha
Configure se os textos realizam quebra de linha automaticamente:
* **Quebra no Cabeçalho**: Títulos longos organizados em múltiplas linhas
* **Quebra no Corpo**: Conteúdo nas células organizado em múltiplas linhas
> \[!NOTE]
> Útil para tabelas com pouco espaço horizontal onde as colunas têm nomes ou valores extensos.
### Cores Personalizadas
Marque a opção ***"Customizar Cores"*** na aba ***Visual*** para ter controle total sobre as cores da tabela:
* **Cor do Texto do Cabeçalho**: Cor da fonte nos títulos das colunas
* **Cor de Fundo do Cabeçalho**: Cor de fundo da linha de títulos
* **Cor do Texto do Corpo**: Cor da fonte nas células de dados
* **Cor de Fundo do Corpo**: Cor de fundo das linhas de dados
* **Cor da Linha Ímpar (Zebrado)**: Cor de fundo para linhas ímpares quando o zebrado está ativo
* **Cor do Texto do Rodapé**: Cor da fonte na linha de totais
* **Cor de Fundo do Rodapé**: Cor de fundo da linha de totais
### Bordas das Células
Controle fino sobre as bordas internas das células:
* **Estilo**: *Nenhuma*, *Horizontal*, *Vertical* ou *Todas*
* **Espessura**: *Fina*, *Média* ou *Grossa*
* **Cor**: Coloração da borda (disponível quando cores personalizadas estão ativas)
***
## ⚙️ Comportamento
### Campos para Relatório (Drill-through)
* Defina quais colunas serão exibidas na visualização detalhada ao clicar em uma linha
* Permite criar Relatórios exploratórios a partir dos dados da tabela
### Mostrar Itens Sem Valores
* Quando ativado, exibe linhas mesmo que não haja dados para algumas colunas
> \[!TIP]
> Útil para garantir que todas as dimensões apareçam, mesmo sem métricas associadas.
### Limite de Linhas (Top N)
* Define um número máximo de linhas a serem exibidas
* Valor `0` significa sem limite
> \[!TIP]
> Útil para criar rankings ou listas "Top 10".
---
---
url: 'https://docs.horusbi.com.br/dw/tables/datalake.md'
---
# Tabelas Cloud (Datalake)
As Tabelas Cloud são representações virtuais de dados armazenados no Datalake (arquivos Parquet). Ao contrário das tabelas físicas, elas não armazenam dados no banco do Horus — apenas apontam para os arquivos externos, sendo geralmente utilizadas para reuso em processos de engenharia de dados.
***
## 📋 Características
| Característica | Detalhe |
|---------------|---------|
| **Modo de Acesso** | Somente leitura — a estrutura é inferida diretamente dos arquivos Parquet |
| **Edição** | Não é possível editar tipos de dados ou colunas manualmente |
| **Particionamento** | Geralmente organizadas em pastas por data e agente |
***
## 📂 Ver Arquivos Datalake
Esta é a funcionalidade exclusiva mais importante para este tipo de tabela — permite navegar na estrutura física dos arquivos armazenados.
Ao clicar em **Ver Arquivos Datalake**:
1. O sistema lista as **Partições** encontradas (Agente, Data)
2. Você pode ver estatísticas como **Total de Linhas** por arquivo
3. É possível **baixar** o arquivo `.parquet` individual para análise local
***
## ⚠️ Truncar Tabela Cloud
> \[!CAUTION]
> **Ação Destrutiva no Datalake** — Para tabelas Cloud, a ação de **Truncar** não apenas limpa a "tabela" visual no Horus, mas **apaga fisicamente os arquivos** da pasta correspondente no Datalake. Use com extrema cautela.
---
---
url: 'https://docs.horusbi.com.br/dw/tables/physical.md'
---
# Tabelas Físicas (Excel e Dataflow)
As tabelas físicas são o principal tipo de tabela no HorusDW — nelas, os dados são armazenados diretamente no banco e ficam prontos para consulta em Dashboards e Relatórios. A seguir, as ações disponíveis para gerenciá-las.
***
## 👁️ Visualizar Dados
Ao entrar na edição de uma tabela, você pode visualizar as primeiras linhas para validar o conteúdo, formatação e máscaras aplicadas.
***
## 🚀 Publicar (Mover para Produção)
A ação de **Publicar** move uma tabela da sua "Minha Mesa" para uma Mesa Governada (Gold, Silver), tornando-a disponível para outros usuários.
> \[!IMPORTANT]
> **Regra de Publicação:**
>
> * **Tabelas Excel** — Você publica manualmente pelo botão "Publicar" na listagem.
> * **Tabelas de Dataflow** — Você **não** publica a tabela diretamente. Publique o **Dataflow** no módulo de ETL e a tabela será publicada automaticamente como consequência.
Ao publicar, se a tabela já existir no destino, o sistema perguntará se deseja **Substituir** a versão existente.
***
## 📂 Gerenciar Arquivos (Apenas Excel)
Para tabelas criadas via upload de Excel, é possível **unificar** múltiplos arquivos em uma única tabela (*Vendas Jan*, *Vendas Fev*).
1. Acesse **Gerenciar Arquivos** na edição da tabela
2. Faça upload de novos arquivos com a **mesma estrutura** (colunas)
3. O sistema empilhará os dados automaticamente
***
## 🗑️ Ciclo de Vida: Truncar e Excluir
### Truncar (Limpar Dados)
Remove **todos os registros** (linhas) da tabela, mas mantém a estrutura (colunas, metadados, fórmulas).
> \[!TIP]
> Útil quando você quer recarregar os dados do zero sem perder as configurações da tabela.
### Excluir
Remove a tabela inteira, incluindo estrutura e metadados.
> \[!CAUTION]
> Esta ação quebrará qualquer Datamart ou Dashboard que dependa desta tabela. Certifique-se de que nenhum recurso depende dela antes de excluir.
---
---
url: 'https://docs.horusbi.com.br/lumo/referencia/table.md'
description: >-
Referência do YAML de tabela do DW no Lumo: propriedades, tipos, restrições e
um exemplo completo.
---
# Table
Uma tabela é o destino de um flow no Data Warehouse. Ela vive em `tables/--.yaml`.
Quem manda no esquema físico (nomes de coluna, tipos) é o flow que alimenta a tabela. O YAML da tabela controla o **modelo de chaves** do Doris e a **camada cosmética**: rótulo, máscara e descrição de cada coluna.
Crie a tabela a partir de um nó do flow, que é o caminho que preenche as colunas para você:
```bash
lumo new table --from-flow flow:44531 --node join_itens --name "Fato Vendas"
```
Ligue o autocomplete no editor colando esta linha no topo do arquivo:
```yaml
# yaml-language-server: $schema=https://docs.horusbi.com.br/schemas/v2/table.schema.json
```
## Modelo de configuração
```yaml
# ── header ────────────────────────────────────────────────
id: integer # obrigatório, >= 1
kind: table # obrigatório, literal "table"
lumo: v2 # obrigatório, literal "v2"
tenantId: integer # obrigatório, >= 1
---
# ── body ──────────────────────────────────────────────────
nome: string # obrigatório, mínimo 1 caractere
table_type: string # obrigatório: table | file | cloud
key_type: string # duplicate | unique. ESTRUTURAL. proibido quando table_type=cloud
key_columns: [string] # colunas que formam a chave. ESTRUTURAL. proibido quando table_type=cloud
partition_column: string | null # coluna de partição. ESTRUTURAL. proibido quando table_type=cloud
icon: string | null
tags: [string]
columns: # obrigatório. objeto, chaveado por "NOME_DA_COLUNA (TipoDeDado)"
NOME_DA_COLUNA (DataType): # chave somente-leitura: o esquema é do flow
label: string # rótulo exibido
mask: string | null # máscara de formatação
human_description: string | null
defaultBehavior: string
expressions: # colunas calculadas
- column_name: string # obrigatório
label: string # obrigatório
column_type: string # obrigatório: column | expression
data_type: string # obrigatório: Number | String | Date | Boolean | DateTime | Time | Json
display_order: integer # obrigatório, >= 0
expression: string | null
is_key_column: boolean
is_partition_column: boolean
indexed: boolean
human_description: string | null
mask: string | null
```
`key_type`, `key_columns` e `partition_column` são **estruturais**. Mudar qualquer um deles força um DROP e CREATE da tabela física no Doris, e as linhas são perdidas. O `lumo push` recusa a mudança numa tabela que já tem dados, a menos que você passe `--force-recreate`. Depois disso, rode o flow de novo para recarregar.
## Configuração completa
```yaml
# tables/fato-vendas--44940.yaml
# yaml-language-server: $schema=https://docs.horusbi.com.br/schemas/v2/table.schema.json
id: 44940
kind: table
lumo: v2
tenantId: 853
---
nome: fato_vendas
table_type: table
# unique deduplica linhas com a mesma key_columns (a última carga vence).
# duplicate acumula linhas a cada carga. Numa carga Temporal que recarrega
# uma janela, duplicate multiplica os registros: use unique.
key_type: unique
key_columns: [PEDIDO_ID, PRODUTO_ID]
partition_column: DATA_EMISSAO
# A chave de cada coluna é "NOME (Tipo)" e é somente-leitura: quem define o
# esquema é o flow. Só os valores abaixo dela são editáveis.
columns:
PEDIDO_ID (Number):
label: Pedido
mask: N0
PRODUTO_ID (Number):
label: Produto
mask: N0
CLIENTE_ID (Number):
label: Cliente
mask: N0
DATA_EMISSAO (Date):
label: Data de Emissão
mask: DATE
QUANTIDADE (Number):
label: Quantidade
mask: N2
VALOR_ITEM (Number):
label: Valor do Item
mask: C2
human_description: Valor líquido do item, já com desconto aplicado.
expressions:
- column_name: TICKET_MEDIO
label: Ticket Médio
column_type: expression
data_type: Number
display_order: 6
expression: VALOR_ITEM / NULLIF(QUANTIDADE, 0)
mask: C2
human_description: Valor médio por unidade vendida.
tags: [vendas]
```
## Especificação: header
### `id`
**Tipo:** integer (>= 1) · **Obrigatório:** sim
Id da tabela no servidor.
### `kind`
**Tipo:** string · **Obrigatório:** sim · **Valor:** `table`
### `lumo`
**Tipo:** string · **Obrigatório:** sim · **Valor:** `v2`
### `tenantId`
**Tipo:** integer (>= 1) · **Obrigatório:** sim
## Especificação: body
### `nome`
**Tipo:** string (mínimo 1 caractere) · **Obrigatório:** sim
Nome da tabela no DW.
### `table_type`
**Tipo:** string · **Obrigatório:** sim · **Valores:** `table`, `file`, `cloud`
| Valor | O que é | Usável em apps |
|---|---|---|
| `table` | Tabela normal do DW, alimentada por um `InsertDatawarehouse` em modo `Datawarehouse`. | Sim |
| `file` | Tabela estática, alimentada por upload de Excel pela interface do DW. Não tem flow. | Sim |
| `cloud` | Saída em parquet no datalake, de um `InsertDatawarehouse` em modo `Datalake`. Só serve como entrada de `ExtractLakehouse` em outro flow. | Não |
Uma tabela `cloud` não aceita `key_type`, `key_columns` nem `partition_column`: o modelo de chaves é do Doris, e o datalake não tem Doris.
### `key_type`
**Tipo:** string · **Obrigatório:** não · **Valores:** `duplicate`, `unique` · **Restrição:** proibido quando `table_type` é `cloud` · **Estrutural**
Modelo de chaves do Doris.
* `duplicate`: cada carga acumula linhas (append).
* `unique`: linhas com a mesma `key_columns` são deduplicadas, e a última escrita vence (upsert).
Não confunda com `PrimaryKeys` do nó `InsertDatawarehouse`. Aquele deduplica em memória durante uma carga. Este é o modelo físico da tabela e vale entre cargas.
### `key_columns`
**Tipo:** array de string · **Obrigatório:** não · **Restrição:** proibido quando `table_type` é `cloud` · **Estrutural**
Colunas que formam a chave do Doris. Com `key_type: unique`, é a chave de deduplicação. Com `duplicate`, é a chave de ordenação. Cada nome precisa bater com uma coluna física.
### `partition_column`
**Tipo:** string ou null · **Obrigatório:** não · **Default:** `null` · **Restrição:** proibido quando `table_type` é `cloud` · **Estrutural**
Coluna usada como partição do Doris. Precisa bater com uma coluna física.
### `columns`
**Tipo:** object · **Obrigatório:** sim
Camada cosmética sobre as colunas físicas, chaveada por `NOME_DA_COLUNA (TipoDeDado)`. A chave é somente-leitura, porque o esquema pertence ao flow. Para mudar nome, tipo ou esquema de uma coluna, altere o flow que alimenta a tabela.
Cada valor aceita:
| Campo | Tipo | O que faz |
|---|---|---|
| `label` | string | Rótulo exibido da coluna. |
| `mask` | string ou null | Máscara de formatação, como `N0`, `C2`, `DATE`. |
| `human_description` | string ou null | Descrição de negócio da coluna. Alimenta a IA. |
| `defaultBehavior` | string | Comportamento padrão da coluna. |
```yaml
columns:
VALOR_ITEM (Number):
label: Valor do Item
mask: C2
```
### `expressions`
**Tipo:** array de objetos · **Obrigatório:** não · **Default:** `[]`
Colunas calculadas. Cada item exige `column_name`, `label`, `column_type`, `data_type` e `display_order`.
### `icon`
**Tipo:** string ou null · **Obrigatório:** não
Ícone da tabela na interface.
### `tags`
**Tipo:** array de string · **Obrigatório:** não · **Default:** `[]`
## Especificação: expression
Cada item de `expressions`.
### `column_name`
**Tipo:** string (mínimo 1 caractere) · **Obrigatório:** sim
Nome da coluna calculada.
### `label`
**Tipo:** string · **Obrigatório:** sim
Rótulo exibido.
### `column_type`
**Tipo:** string · **Obrigatório:** sim · **Valores:** `column`, `expression`
Use `expression` para uma coluna calculada.
### `data_type`
**Tipo:** string · **Obrigatório:** sim · **Valores:** `Number`, `String`, `Date`, `Boolean`, `DateTime`, `Time`, `Json`
### `display_order`
**Tipo:** integer (>= 0) · **Obrigatório:** sim
Posição da coluna na listagem.
### `expression`
**Tipo:** string ou null · **Obrigatório:** não
A fórmula. Divisões precisam se proteger de zero, com `NULLIF` no denominador.
```yaml
expression: VALOR_ITEM / NULLIF(QUANTIDADE, 0)
```
### `is_key_column`
**Tipo:** boolean · **Obrigatório:** não · **Default:** `false`
### `is_partition_column`
**Tipo:** boolean · **Obrigatório:** não · **Default:** `false`
### `indexed`
**Tipo:** boolean · **Obrigatório:** não · **Default:** `false`
### `human_description`
**Tipo:** string ou null · **Obrigatório:** não
Descrição de negócio. A IA usa este texto para escolher a coluna certa.
### `mask`
**Tipo:** string ou null · **Obrigatório:** não
Máscara de formatação.
## Campos gerenciados pelo servidor
| Campo | O que é |
|---|---|
| `_state` | `draft`, `published` ou `inconsistent`. |
| `deskId` | Desk em que a tabela foi publicada. |
| `originFlowId` | Flow que alimenta a tabela. |
| `version` | Versão do recurso. |
| `criado_em`, `criado_por`, `publicado_em`, `publicado_por` | Auditoria. |
---
---
url: 'https://docs.horusbi.com.br/hec/resources/templates.md'
---
# Templates e Linhas de Produto
Templates são a forma de criar **produtos de BI auto-contidos** que podem ser replicados para múltiplos tenants. Eles permitem padronizar, distribuir e atualizar soluções completas de Business Intelligence.
> \[!TIP]
> Templates são ideais para clientes whitelabel que precisam escalar um BI embarcado para dezenas ou centenas de clientes finais.
## 📦 O que é um Template?
Um Template é um "snapshot" completo de um ambiente de BI. Ao criar um template, você seleciona as **Mesas de Aplicações** que deseja empacotar.
### Empacotamento Inteligente
O sistema identifica automaticamente todas as dependências a partir das aplicações selecionadas:
1. **Aplicações** → O ponto de partida: Dashboards, Widgets, filtros, expressões e configurações de IA
2. **Tabelas** → Identificadas a partir das aplicações que as utilizam
3. **Dataflows** → Derivados das tabelas necessárias, incluindo relacionamentos entre fluxos para tratamento em camadas
4. **Agendamentos** → Gatilhos de execução vinculados aos Dataflows
5. **Variáveis Globais** → Parâmetros configuráveis usados pelos Dataflows
6. **Credenciais** → Referências a conexões de banco (sem dados sensíveis)
> \[!NOTE]
> Tabelas que não são utilizadas por nenhuma aplicação **não entram no template**. O sistema empacota apenas o que é efetivamente necessário para o funcionamento das aplicações selecionadas.
> \[!WARNING]
> **Dataflows seguem as tabelas.** Um Dataflow só é empacotado se produzir uma tabela que é usada por alguma aplicação de uma mesa selecionada. Um Dataflow novo "solto" (que ainda não alimenta uma tabela em uso por um dashboard) **não entra no template**, mesmo que tenha sido criado e validado.
>
> Se um Dataflow precisa ir junto, garanta que a tabela que ele gera seja efetivamente consumida por uma aplicação dentro de uma das mesas selecionadas.
> \[!NOTE]
> **Cadastros viajam no template — estrutura, sem dados.** Quando um Cadastro de uma mesa selecionada faz parte do template, vão junto seus campos, validações e referências (chaves externas). Os registros (dados) **não** são empacotados: cada tenant começa com o Cadastro vazio, pronto para ser preenchido.
***
## 🔄 Ciclo de Vida do Template
### 1. Criação
Templates são criados a partir de um **tenant de desenvolvimento** (também chamado de "tenant base"):
1. Desenvolva suas aplicações, Dataflows e tabelas no tenant de desenvolvimento
2. Teste e valide todo o conteúdo
3. Acesse **HEC → Templates** e clique em **Criar novo Template**
4. Selecione as mesas que farão parte do template
5. Dê um nome e descrição ao template
> \[!NOTE]
> O template captura o estado atual do conteúdo. Alterações posteriores no tenant de desenvolvimento não afetam templates já criados.
### 2. Instalação
Templates podem ser instalados em novos tenants de duas formas:
**Durante a criação do tenant:**
* Ao criar um novo tenant, você pode selecionar um template
* As variáveis globais do template serão solicitadas neste momento
* Isso permite criar templates parametrizáveis (URL de API, chave de integração)
**Em tenants existentes:**
* Acesse a edição do tenant
* Na aba **HEC → Templates**, clique em **Instalar Template**
* Selecione o template e configure as variáveis
### 3. Atualização
Quando você precisa atualizar um template:
1. Faça as alterações necessárias no tenant de desenvolvimento
2. Em **HEC → Templates**, localize o template existente
3. Clique em **Atualizar** para criar uma nova versão
4. A nova versão pode ser aplicada aos tenants instalados
***
### 4. Espelhar Template (Estado Canônico)
Por padrão, aplicar um template (instalar ou atualizar) é uma operação **aditiva**: cria e atualiza o que o template define, mas nunca remove nada do tenant. Com o tempo isso faz o tenant acumular conteúdo que não pertence mais ao template — linhas de produto descontinuadas, forks órfãos, restos de templates antigos.
A opção **Espelhar template**, disponível na hora de aplicar, resolve isso: além do apply aditivo de sempre, ela executa uma fase de **poda**, removendo do tenant o conteúdo de produto que não existe no template — deixando o tenant em **estado canônico**, um espelho exato do que o template define.
> \[!NOTE]
> Espelhar é **desativado por padrão**. Sem marcar a opção, o apply continua exatamente aditivo, como sempre foi.
#### O que é espelhado
Estes recursos são **conteúdo de produto**: o que casa por nome (e Mesa) com o template é mantido/atualizado normalmente; o que não casa é removido.
| Recurso | Quando é removido |
|---|---|
| **Aplicações** | Não existe Aplicação de mesmo nome, na mesma Mesa, no template |
| **Tabelas** (exceto Cadastros) | Não existe Tabela de mesmo nome, na mesma Mesa, no template |
| **Dataflows** | Não existe Dataflow de mesmo nome, na mesma Mesa, no template |
| **Mesas** (de Aplicação e de Dados) | Ficou **vazia** depois que os itens acima foram removidos |
#### O que é sempre preservado
O **estado pessoal/operacional do tenant** nunca é alvo da poda — só desaparece em cascata, se o recurso ao qual pertence for removido:
* Credenciais de conexão
* Variáveis do tenant
* **Cadastros** — a estrutura (campos, validações) pode vir do template, mas os **dados digitados** pelo tenant nunca são tocados
* Permissões e grants (grupos de usuário, acesso a Mesas)
* Alertas configurados pelos usuários (com uma exceção — veja abaixo)
* Bookmarks
* Dashboards pessoais
#### Cascata: Agendamentos e Datamarts órfãos
Agendamentos e Datamarts não são avaliados por nome — eles seguem o destino do que referenciam:
* Um **Agendamento** só é removido quando **todos** os Dataflows que ele dispara foram podados. Se restar pelo menos um Dataflow vivo, o agendamento continua valendo.
* Um **Datamart** só é removido quando **nenhuma** das Tabelas que ele expõe sobreviveu. Basta uma Tabela sobrevivente para o Datamart ser mantido.
> \[!TIP]
> Tags de Dataflow seguem o Dataflow (somem se ele for podado); a definição da tag em si permanece disponível para uso futuro.
#### Exceção: Alertas de uma Aplicação removida
Alertas são, em regra, estado pessoal preservado. A exceção: quando uma **Aplicação inteira é podada** (por não existir no template), os Alertas vinculados a ela são removidos junto — um Alerta é um job ativo, e deixá-lo apontando para uma Aplicação inexistente geraria disparos com erro.
Bookmarks e Dashboards pessoais da mesma Aplicação **não** seguem essa regra: ficam preservados como estado inerte e voltam a funcionar normalmente se a Aplicação for restaurada do Arquivo.
#### Preview obrigatório
Antes de confirmar um apply com Espelhar ativado, o sistema sempre mostra um preview com o que será removido (por tipo e nome), para revisão antes de confirmar.
> \[!CAUTION]
> O preview é uma estimativa **conservadora**. Ele pode listar Mesas, Agendamentos ou Datamarts como candidatos à remoção mesmo quando, depois que o próprio apply recriar o conteúdo do template, esses recursos acabem sobrevivendo de fato. Ou seja: o preview pode superestimar o que será removido, mas **nunca esconde** uma remoção real — revise sempre antes de confirmar.
#### Recuperação
Toda remoção feita por Espelhar é uma exclusão reversível (soft-delete), seguindo as mesmas regras de qualquer exclusão no Horus — sem apagamento físico dos dados. Aplicações, Dataflows e Tabelas removidos aparecem no [Arquivo](../archive.md) e podem ser restaurados em até **90 dias**.
#### Interação com Fork e Customizações
O fluxo de [Fork](#2-a-solução-fork-clonar-e-renomear) cria os ativos customizados em uma Mesa "Customizações" separada, propositalmente fora do template. Por não pertencer ao template, essa Mesa **é podada** quando você espelha — é o mesmo "preço da segurança" que o fluxo de Fork já assume: o template nunca toca uma customização para atualizá-la, mas também não a reconhece como própria. Por isso o preview e a recuperação via Arquivo existem: revise o que será removido antes de confirmar.
#### Mesas protegidas e tenants com múltiplos templates
A opção **"Proteger mesas criadas via templates"** bloqueia apenas a **edição manual** do usuário — ela não isenta uma Mesa da poda do Espelhar. Mesas do template continuam casando por nome e sobrevivem normalmente; uma Mesa protegida que não pertence ao template sendo aplicado (por exemplo, de uma linha de produto antiga, ou de outro template instalado no mesmo tenant) é podada como qualquer outra Mesa fora do template.
Se o tenant tem mais de um template instalado, Espelhar remove o conteúdo de produto que não pertence **ao template que está sendo aplicado**, mesmo que pertença a outra linha de produto também instalada ali. É um comportamento proposital — e, como qualquer remoção, aparece no preview antes de confirmar.
***
## 🛡️ Governança e Customização
Um dos maiores desafios em produtos de dados em escala é: **como entregar um produto padrão (Template) e ainda permitir customizações para clientes específicos?**
A solução do HEC combina dois conceitos: **Mesas Protegidas** (Segurança) e **Fork** (Operação).
### 1. Mesas Protegidas
O sistema permite proteger os ativos instalados pelo template para que ninguém os edite acidentalmente.
* **Aviso**: Se a opção "**Proteger mesas criadas via templates**" estiver ativa, as mesas do template ficam bloqueadas para edição/exclusão
* **Objetivo**: Garantir que o cliente final não "quebre" o produto padrão e que futuras atualizações funcionem sem conflitos
### 2. A Solução: Fork (Clonar e Renomear)
Como as mesas oficiais estão bloqueadas (ou devem ser tratadas como tal), a única forma de customizar é clonar o ativo.
Sempre que precisar customizar um Dashboard, tabela ou ativo padrão do produto, siga este fluxo:
1. Acesse o ambiente do cliente.
2. Localize o ativo padrão (Dataflow `Fato_Vendas` ou Aplicação `Sales Dashboard`).
3. Utilize a função **Clonar para Minha Mesa**.
4. **Renomeie** o clone para algo único (`Fato_Vendas_Custom` ou `Sales Dashboard (Custom)`).
5. Publique este novo ativo em uma **Mesa Customizada** (uma mesa que não pertence ao template, "Customizações").
> \[!TIP]
> **Por que isso funciona?**
>
> * O Template gerencia os ativos originais (protegidos). Quando você lançar a V2, o `Fato_Vendas` será atualizado automaticamente.
> * O seu `Fato_Vendas_Custom` é um ativo independente ("órfão") e **não será tocado** pela atualização, preservando a customização do cliente.
### Exemplo Prático: Adicionar campo novo
Cenário: Você precisa adicionar uma coluna `Regra_X` na tabela de Vendas apenas para o Cliente A.
1. **Clone o Dataflow**: Clone o `Fato_Vendas` original para sua mesa.
2. **Edite**: Adicione a coluna `Regra_X` no novo flow.
3. **Renomeie**: Salve como `Fato_Vendas_Custom`.
4. **Publique**: Publique na mesa "Customizações".
5. **Clone a Aplicação**: Se precisar mostrar esse dado, clone também a Aplicação de Vendas.
6. **Re-vincule**: Na nova aplicação, aponte os gráficos para usarem a tabela gerada pelo `Fato_Vendas_Custom`.
Isso cria uma redundância deliberada (o dado original e o customizado coexistem), que é o preço da segurança de atualização.
***
## ⚙️ Variáveis Globais
Templates podem ser **parametrizáveis** através de variáveis globais. Isso permite criar templates genéricos que se adaptam a diferentes clientes.
### Exemplos de uso
| Variável | Uso |
|----------|-----|
| `URL_API` | Endpoint da API do cliente |
| `CHAVE_INTEGRACAO` | Token de autenticação |
| `CNPJ_EMPRESA` | Identificador para filtro de select |
### Como configurar
1. No tenant de desenvolvimento, acesse **ETL → Edição de Dataflows → Variáveis Globais**
2. Crie as variáveis necessárias (valores não serão levados para o template)
3. Ao criar o template, as variáveis são incluídas automaticamente
4. Durante a instalação em um novo tenant, o usuário informará os valores específicos
***
## 📊 Linhas de Produto
Conforme você cria múltiplas versões de templates, fica difícil gerenciar qual é a versão vigente e quais tenants estão desatualizados. **Linhas de Produto** resolvem esse problema.
### O que são Linhas de Produto
Uma Linha de Produto agrupa templates relacionados, permitindo:
* **Versionamento**: Definir qual versão é a "vigente" (atual)
* **Histórico**: Manter versões anteriores como "descontinuadas"
* **Changelog**: Documentar mudanças entre versões
* **Visualização**: Ver rapidamente quais tenants estão desatualizados
* **Migração em lote**: Atualizar múltiplos tenants de uma vez
### Criando uma Linha de Produto
1. Acesse **HEC → Templates**
2. Clique em **Criar Linha de Produto**
3. Dê um nome ("Pacote Financeiro", "Módulo RH")
4. Vincule templates existentes à linha
### Gerenciando Versões
Na tela de detalhes da Linha de Produto:
* **Template Vigente**: A versão recomendada para novas instalações
* **Templates Descontinuados**: Versões antigas mantidas para referência
* **Tenants Desatualizados**: Lista de tenants usando versões antigas
### Migração em Lote
Para atualizar múltiplos tenants para a versão vigente:
1. Acesse a Linha de Produto
2. Selecione os tenants desatualizados
3. Clique em **Migrar Selecionados**
4. Acompanhe o progresso da migração
***
## 💡 Casos de Uso
### BI Embarcado para Whitelabel
Uma software-house que oferece BI embarcado pode:
1. Desenvolver um template completo com Dashboards padrão
2. Criar variáveis para personalização (API do cliente, cnpj)
3. Instalar o template em cada novo cliente
4. Usar Linhas de Produto para gerenciar atualizações
5. Ativar proteção de mesas para preservar customizações
***
## ✅ Boas Práticas
> \[!IMPORTANT]
>
> * Sempre teste templates em um tenant de homologação antes de distribuir
> * Use variáveis globais para evitar hardcode de configurações
> * Documente as mudanças entre versões no changelog
> * Ative a proteção de mesas para clientes que não devem editar o conteúdo base
---
---
url: 'https://docs.horusbi.com.br/dataviz/03-widgets/text.md'
---
# Texto e Imagens
O Widget de **Texto** é uma área livre para inclusão de conteúdo rico na Dashboard. Diferente dos outros Widgets que exibem dados, este componente serve para adicionar títulos, descrições, narrativas, imagens ou logotipos estáticos.
## ✏️ Funcionalidades
* **Editor Rico (WYSIWYG)**: Permite formatação completa (Negrito, Itálico, Listas, Alinhamento) diretamente na tela, sem necessidade de códigos
* **Edição Direta**: Ao ativar o "Modo Edição" da Dashboard, basta clicar e digitar diretamente dentro do Widget
## ⚙️ Configuração
A configuração deste Widget é extremamente simples, pois o conteúdo é editado diretamente na área visual.
### Opções Visuais
A única configuração técnica disponível é:
* **Gráfico com fundo transparente**: Remove o fundo branco padrão do Widget, permitindo que o texto/imagem flutue sobre a cor de fundo da Dashboard. Ideal para logotipos ou títulos de seção soltos
***
## 💡 Casos de Uso Comuns
1. **Cabeçalhos de Seção**: Usar texto grande para separar áreas da Dashboard ("Vendas Regionais", "Indicadores de RH")
2. **Instruções de Uso**: Adicionar um pequeno guia explicando como usar os filtros da tela
3. **Narrativas e Insights**: Escrever observações estáticas sobre os dados ("Nota: A queda em Fev/23 deve-se ao feriado de Carnaval".)
4. **Logotipos**: Inserir a imagem do logo da empresa ou do cliente
---
---
url: 'https://docs.horusbi.com.br/etl/guides/tipos-de-carga.md'
description: >-
Tipos de carga do HorusETL: Total, Temporal e Incremental. Como cada um apaga
e insere no Data Warehouse, o que exige da tabela de destino e como escolher.
---
# Tipos de Carga
Todo fluxo que grava no Data Warehouse tem um **tipo de carga** (`load_type`). Ele responde a uma única pergunta: *a cada execução, o que o fluxo apaga antes de inserir os dados novos?*
São três valores, e não existem outros:
| Tipo | O que apaga antes de inserir | Coluna de controle |
|---|---|---|
| **Total** | Tudo. A tabela é esvaziada e recarregada | Não usa |
| **Temporal** | Só as linhas dentro de uma janela de datas | Coluna de data (partição) |
| **Incremental** | Nada. Só insere ou atualiza | Coluna de rastreio (ex.: `updated_at`) |
O tipo de carga é uma propriedade do **fluxo**, não da tabela. Quem executa a regra é o nó `InsertDatawarehouse`: ele monta o `DELETE` conforme o tipo e depois insere o resultado do fluxo.
> \[!IMPORTANT]
> A escolha errada aqui não quebra a execução: ela duplica ou perde linhas em silêncio, e o erro só aparece semanas depois num total que não fecha. Vale gastar tempo nesta decisão.
***
## 🔄 Total (recarga completa) {#total}
Esvazia a tabela e recarrega tudo, a cada execução.
* **Condição de DELETE:** `1=1`, ou seja, apaga todas as linhas.
* **Exige:** nada. Nenhuma coluna de controle.
* **Quando usar:** tabelas pequenas, tipicamente dimensões e tabelas de referência (cidades, categorias, plano de contas), ou qualquer origem que simplesmente não tenha como ser rastreada de forma incremental.
É o tipo mais simples e o único que não tem risco de duplicar registros: o estado final depende só da última execução. O custo é reprocessar a origem inteira toda vez, o que fica inviável em tabelas fato grandes.
***
## 📅 Temporal (recarga por janela de datas) {#temporal}
Recarrega apenas um intervalo de datas, delimitado por uma coluna de data da tabela.
* **Condição de DELETE:** `TRUNC("COLUNA") BETWEEN 'StartDate' AND 'EndDate'`.
* **Exige:** `load_type_column` no fluxo apontando para a coluna de data, e a mesma coluna definida como `partition_column` na tabela do DW.
* **Variáveis injetadas:** `{StartDate}` e `{EndDate}`, disponíveis nos nós de extração. Os nós precisam usá-las, senão o fluxo traz a base inteira e a janela não filtra nada na origem.
```sql
SELECT * FROM vendas
WHERE DATA_EMISSAO BETWEEN '{StartDate}' AND '{EndDate}'
```
Este é o tipo padrão para tabelas fato grandes: roda todo dia recarregando os últimos N dias, o que absorve correções retroativas na origem sem reprocessar o histórico.
> \[!WARNING]
> **A coluna de partição precisa ser imutável.** Use `data_emissao`, `data_venda`, `created_at`. Nunca `data_vencimento` ou qualquer data que possa ser alterada depois. Se o valor mudar, o `DELETE` limpa a janela antiga, o `INSERT` grava o registro na janela nova, e a linha antiga permanece em outra janela que ninguém mais vai apagar. Resultado: duplicata. Para datas mutáveis, use Incremental com chave única.
***
## ➕ Incremental (append ou upsert) {#incremental}
Não apaga nada. Insere só o que é novo desde a última execução.
* **Condição de DELETE:** nenhuma.
* **Exige:** `load_type_column` no fluxo apontando para a coluna de rastreio (`updated_at`, `created_at` ou um ID sequencial).
* **Variável injetada:** `{LastDataPoint}`, o maior valor da coluna de rastreio visto na execução anterior.
* **Primeira execução:** carrega tudo, porque ainda não existe `LastDataPoint`.
```sql
SELECT * FROM pedidos
WHERE UPDATED_AT > '{LastDataPoint}'
```
O que acontece com um registro que já existe na tabela depende do `key_type` da tabela de destino:
* **`key_type: unique`** com `key_columns` preenchido: o Doris faz **upsert**. Linhas com a mesma chave são substituídas, a última carga vence.
* **`key_type: duplicate`**: **append** puro. A linha nova é acrescentada e a antiga fica lá.
Ou seja, rastrear por `updated_at` sem `key_type: unique` produz uma cópia do pedido a cada alteração dele. Rastreando por `created_at` num log append-only, `duplicate` é o correto.
***
## 🔗 O lado da tabela {#tabela}
O tipo de carga é do fluxo, mas quem cumpre metade do contrato é a tabela do DW. Três propriedades importam:
| Propriedade da tabela | Para que serve |
|---|---|
| `key_type` | `unique` deduplica linhas com a mesma `key_columns` (upsert). `duplicate` acumula. |
| `key_columns` | As colunas que formam a chave. Só faz sentido com `key_type: unique`. |
| `partition_column` | A coluna de data usada pelo `DELETE` da carga Temporal. Só uma por tabela. |
As três são **estruturais**: mudar qualquer uma força um DROP e CREATE da tabela física no Doris e as linhas são perdidas. Detalhes e restrições em [Referência: table](/lumo/referencia/table).
***
## 🧭 Escolhendo o tipo {#escolhendo}
| Cenário | Tipo | Coluna de controle | `key_type` |
|---|---|---|---|
| Tabela de referência pequena (dimensão, cadastro) | Total | (nenhuma) | `duplicate` |
| Fato grande com data imutável (vendas, faturamento) | Temporal | `data_emissao` (Date) | `unique` |
| Log ou evento append-only | Incremental | `created_at` | `duplicate` |
| Registro mutável (pedido, chamado, contas a receber) | Incremental | `updated_at` | `unique` + `key_columns` |
> \[!TIP]
> Numa carga Temporal que recarrega uma janela, prefira `key_type: unique`. Se o `DELETE` da janela falhar por qualquer motivo, `duplicate` multiplica os registros; `unique` os deduplica pela chave.
***
## 📄 Exemplo: fato de vendas com carga Temporal {#exemplo}
Um fluxo que recarrega diariamente os últimos dias de vendas, particionado pela data de emissão do pedido.
O fluxo declara o tipo de carga e a coluna de partição:
```yaml
# flows/carga-vendas--42866.yaml
# yaml-language-server: $schema=https://docs.horusbi.com.br/schemas/v2/flow.schema.json
id: 42866
kind: flow
lumo: v2
tenantId: 853
---
nome: Carga Vendas
load_type: Temporal
load_type_column: DATA_EMISSAO # obrigatório em Temporal e Incremental
nodes:
Processors:
- internalId: ext_vendas
kind: ExtractPostgreSQL
inputs: []
options:
Credential: erp-producao
# {StartDate} e {EndDate} são injetadas pelo tipo de carga Temporal.
SQL: |
SELECT
p.id AS PEDIDO_ID,
i.produto_id AS PRODUTO_ID,
p.data_emissao AS DATA_EMISSAO,
i.valor_item AS VALOR_ITEM
FROM public.pedidos p
JOIN public.pedido_itens i ON i.pedido_id = p.id
WHERE p.data_emissao >= '{StartDate}'
AND p.data_emissao <= '{EndDate}'
- internalId: load_dw
kind: InsertDatawarehouse
inputs: [ext_vendas]
options:
TableID: 44940
Mode: Datawarehouse
Connections:
- from: ext_vendas
to: load_dw
```
E a tabela de destino declara a partição e a chave:
```yaml
# tables/fato-vendas--44940.yaml
# yaml-language-server: $schema=https://docs.horusbi.com.br/schemas/v2/table.schema.json
id: 44940
kind: table
lumo: v2
tenantId: 853
---
nome: fato_vendas
table_type: table
key_type: unique
key_columns: [PEDIDO_ID, PRODUTO_ID]
partition_column: DATA_EMISSAO # a mesma coluna do load_type_column do flow
```
A cada execução, o `InsertDatawarehouse` apaga as linhas de `fato_vendas` cuja `DATA_EMISSAO` cai na janela pedida e insere as que o fluxo trouxe. Fora da janela, o histórico não é tocado.
***
## ▶️ Escolhendo a janela na execução {#execucao}
O `load_type` define a regra; a janela concreta é escolhida na hora de executar. Na execução manual, o Horus mostra o seletor de modo (Total, Temporal com *Dias Passados* / *Mês e Ano* / *Ano*, ou Incremental). Pela CLI, o mesmo controle está em `--context`:
```bash
lumo flow run flow:42866 --context Temporal --wait # recarrega a janela temporal
lumo flow run flow:42866 --context Total --wait # recarga completa pontual
```
Dá para forçar uma carga Total pontual num fluxo Temporal, tipicamente para a carga histórica inicial ou para um reprocessamento. Veja [Execução e Agendamento](./execucao-agendamentos.md).
***
## 📚 Relacionados {#relacionados}
* [Execução e Agendamento](./execucao-agendamentos.md): disparar cargas manuais e agendar rotinas
* [Referência: flow](/lumo/referencia/flow): `load_type` e `load_type_column` no YAML
* [Referência: table](/lumo/referencia/table): `key_type`, `key_columns` e `partition_column`
* [Tabelas do DW](/dw/tables/): tipos de coluna, chaves e partições no Data Warehouse
---
---
url: 'https://docs.horusbi.com.br/lumo/trabalhando-com-ia.md'
---
# Trabalhando com a IA
O Lumo foi desenhado para ser operado por um **agente de IA** (Claude Code, Codex…), não digitado comando a comando. Na prática, você **descreve o que quer** e a IA traduz isso para os comandos e YAMLs do `lumo`.
Esta página não é um passo a passo de comandos — é um guia de **como colaborar com a IA**: o que cabe a você, o que a IA faz sozinha, e como pedir as coisas de um jeito que dá certo.
## O que é seu, o que é da IA
Há uma fronteira simples: algumas coisas você faz **uma vez, por fora** (no terminal); o resto acontece **dentro da conversa**, comandado pela IA.
| Você faz (por fora, uma vez) | A IA faz (por dentro, na conversa) |
|---|---|
| Instalar o `lumo` ([Primeiros Passos](/lumo/getting-started/)) | Inicializar e sincronizar o workspace |
| **Autenticar** com `lumo auth login` — é a **sua** sessão | Criar e editar flows, tabelas, apps, dashboards |
| Instalar a skill no agente ([Integrações](/lumo/integracoes/)) | Rodar flows, carregar dados, exportar imagens |
| Apontar o agente para a pasta do workspace | Validar, dar `push`, conferir resultados |
> \[!IMPORTANT]
> **O login é seu e fica por fora.** Você roda `lumo auth login` no terminal — a IA não tem suas credenciais, ela apenas usa a sessão que você já abriu. Se a IA disser que não está autenticada, é você quem faz o login.
E há uma coisa que é **sempre sua, nunca da IA**: decidir **publicar**. Veja abaixo.
## Sendo claro com a IA
Quanto mais claro o pedido, melhor o resultado. Algumas dicas:
* **Fale o objetivo, não o comando.** Diga *"quero um dashboard de vendas por região"*, não *"rode `lumo app query`"*. A IA escolhe os comandos.
* **Diga a fonte.** *"extrai do nosso Postgres de produção"*, *"a partir desta planilha do Google"* — assim ela sabe qual credencial/entrada usar.
* **Peça pra ver antes de fechar.** *"me mostra o dashboard antes"* — a IA exporta uma imagem para você conferir o visual, não só os números.
* **Publicar é decisão sua — e explícita.** A IA **constrói tudo como rascunho** e só publica quando você disser *"publica"*. Quando for a hora, diga **onde**: *"publica o flow na mesa Operação"*, *"publica o app na mesa Comercial"*.
* **Confirme por evidência.** *"confirma que carregou"* — peça a contagem de linhas ou a prévia da tabela. "Enfileirado" não é "pronto".
### Exemplos de pedido
| Em vez de… | Prefira… |
|---|---|
| "roda o lumo aí" | "carrega as vendas de 2024 e me mostra quantas linhas entraram" |
| "cria um flow" | "cria um flow que extrai pedidos do Postgres e carrega no DW" |
| "publica tudo" | "está bom — publica o flow na mesa Operação e o dashboard na mesa Comercial" |
| "faz um dashboard" | "monta um dashboard de faturamento mensal por filial, e me mostra a imagem" |
## A IA vai parar e te perguntar — e isso é bom
A skill orienta a IA a **não agir sozinha** em decisões que são suas ou em estados ambíguos. Espere que ela pare e pergunte quando:
* For **publicar** algo (sempre pede confirmação e o destino).
* Houver **ambiguidade** (duas mesas com nomes parecidos, qual fonte usar).
* O servidor estiver num **estado inconsistente** que não se resolve insistindo.
Isso é proteção, não lentidão. Um "deixa como rascunho que eu reviso" é uma resposta perfeitamente válida.
## Rede de segurança
* **Use Git no workspace.** A IA trabalha em arquivos YAML versionáveis; com Git você tem histórico e rollback do que ela fez. Veja como conferir em [Revisando o trabalho da IA](/lumo/revisar/).
* **Mantenha o CLI atualizado.** O `lumo` avisa quando há versão nova; a IA costuma te alertar e pode rodar `lumo update` com seu ok — vale manter atualizado para a skill acompanhar os comandos.
## Próximo passo
Depois que a IA fizer o trabalho, aprenda a conferir: **[Revisando o trabalho da IA](/lumo/revisar/)**.
---
---
url: 'https://docs.horusbi.com.br/dataviz/02-apps.md'
---
# Trabalhando com Aplicações
Uma **Aplicação** no Horus DataViz é muito mais do que um simples conjunto de Dashboards. Ela funciona como um contêiner semântico que agrupa dados, lógica de negócios, visualizações e inteligência artificial em um único pacote.
Ao criar uma Aplicação, não se está apenas desenhando telas — está se construindo um **sistema de análise** completo que permite:
1. **Visualização Guiada**: Dashboards interativos com gráficos, KPIs e mapas.
2. **Exploração Self-Service**: Modo **Explorer** (Relatórios Nativos), onde o próprio usuário monta seus Relatórios.
3. **Inteligência Artificial**: A Aplicação fornece o contexto (tabelas, relacionamentos e exemplos) para que a IA da Horus responda perguntas em linguagem natural com precisão.
***
## 🏗️ Anatomia de uma Aplicação
Uma Aplicação é composta por várias camadas que trabalham em conjunto:
### 1. Dashboards (Analytics)
São as "páginas" visuais da Aplicação.
* Compostos por **Widgets** (gráficos, KPIs, mapas, tabelas).
* Focados em responder perguntas conhecidas e monitorar métricas definidas.
* Podem conter filtros locais ou globais que se propagam entre os Widgets.
### 2. Explorer (Relatórios Nativos)
Toda Aplicação possui nativamente uma aba **Explorer** (ícone Tabela).
* Permite a criação de Relatórios tabulares *ad-hoc* (sob demanda).
* O usuário final pode selecionar colunas, aplicar filtros e ordenações livremente, sem depender do criador do Dashboard.
* Ideal para exportação de dados granulares, auditorias e análises exploratórias.
### 3. Visões Salvas
Permite que os usuários salvem "estados" da Aplicação para reutilização futura.
* A Visão salva os **Filtros** aplicados no momento.
* No modo Explorer, salva também as **Colunas** selecionadas e a **Ordenação** configurada.
* Podem ser pessoais (visíveis apenas para o próprio usuário) ou compartilhadas com o time.
### 4. Expressões (Cálculos)
Permite criar métricas avançadas combinando dados de diferentes tabelas.
* Diferente das colunas simples, as Expressões podem cruzar dados de tabelas distintas (`% Atingimento = Vendas / Meta`, onde "Vendas" e "Meta" vêm de origens diferentes).
> \[!TIP]
> **Boas Práticas**: Se o cálculo depende de apenas uma tabela (`Preço Médio = ValorTotal / Quantidade`), prefira criar essa coluna calculada diretamente no **HorusDW**. Dessa forma, a métrica ficará disponível para *todas* as Aplicações que utilizarem essa tabela, garantindo consistência e evitando retrabalho.
### 5. Modelo de Dados
É o "cérebro" da Aplicação. Define quais tabelas estão disponíveis e como elas se relacionam entre si. Esse modelo permite, por exemplo, que um filtro aplicado em "Clientes" reflita automaticamente em um gráfico de "Vendas".
***
## ⚙️ Gerenciando a Aplicação
Para acessar as configurações, clique no botão **Editar** (ícone de lápis) no menu superior.
> \[!NOTE]
> A edição completa (incluindo modelagem de dados) só está disponível enquanto a Aplicação está em **Minha Mesa**. Aplicações publicadas em Mesas Compartilhadas são protegidas contra alterações estruturais.
### Configurações Gerais
* **Nome e Descrição**: Identificação da Aplicação na Home e nas buscas.
* **Ícone e Cor**: Facilita a organização visual e a identidade da Aplicação.
* **Filtros Padrão**: Define filtros pré-aplicados sempre que a Aplicação é aberta (`Ano = Ano Atual`).
### Gerenciando Dashboards
Na tela de edição, é possível gerenciar a listagem de Dashboards existentes:
* **Reordenar**: Arraste e solte para alterar a ordem das abas.
* **Opções de Dashboard**: Clique na engrenagem de um Dashboard para:
* Renomear.
* Alterar a grade (12, 24, 36 ou 60 colunas) para maior precisão no posicionamento.
* Habilitar **Widgets Flutuantes** (posicionamento vertical livre).
***
## 🧭 Navegação e Consumo
Ao abrir uma Aplicação, a barra de navegação no topo apresenta os seguintes recursos:
### Modos de Visualização
* **Analytics** (Ícone Dashboard): Onde ficam os gráficos e painéis visuais.
* **Explorer** (Ícone Tabela): Área para criação de listas, Relatórios tabulares e extrações de dados.
### Barra de Filtros
Localizada logo abaixo do topo, exibe todos os filtros ativos da Aplicação.
* **Adicionar Filtro**: Clique no `ícone de funil` ou interaja com os gráficos (cross-filtering).
* **Sintaxe de Filtro**: A barra exibe o filtro de forma legível (`Data entre 01/01 e 31/01`).
* **Remover**: Clique no ícone `'X'` para excluir o filtro completamente do painel.
* **Limpar**: Apague o valor selecionado (ou desmarque as opções) para esvaziar o filtro sem removê-lo da tela.
### Usando Visões Salvas
No canto superior direito, o ícone de **Fita/Favoritos** abre o gerenciador de Visões.
1. **Salvar Visão Atual**: Cria um novo favorito com o estado atual da tela.
* Permite definir a Visão como **Padrão** para que a Aplicação sempre abra nesse estado.
* Permite torná-la **Pública** para que outros usuários da Aplicação a visualizem.
2. **Meus Favoritos**: Lista de Visões criadas pelo usuário.
3. **Compartilhados Comigo**: Visões criadas por outros usuários do time.

### 🤖 Chat com Dados (IA)
No menu lateral esquerdo, é possível conversar com a plataforma fazendo uso de linguagem natural.
* **Contexto Inteligente**: A IA não se limita à Aplicação aberta. Ela analisa a pergunta e decide automaticamente qual Aplicação possui os dados necessários para a resposta.
* **Fatos**: Durante a edição da Aplicação, é possível cadastrar **fatos** — definições explícitas dos conceitos do seu negócio (qual coluna é "faturamento", em qual data o tempo é medido, que filtros padrão se aplicam) que o chat usa como ponto de partida para responder com consistência.
📖 **Documentação canônica do Chat com Dados**: veja **[Chat com Dados](/ia/chat/)** para visão geral, como o chat responde, e a peça central — **[Fatos: Ensinando seu Negócio](/ia/chat/fatos)**.

***
## 📖 Guias de Edição
Para informações detalhadas sobre cada aba de edição, consulte:
| Aba | Descrição | Link |
|-----|-----------|------|
| **Tabelas** | Modelo de dados e relacionamentos | [Ver guia](./tables.md) |
| **Modelagem de Dados** | Dimensões compartilhadas, explosão cartesiana e Fato Única | [Ver guia](./data-modeling.md) |
| **Expressões** | Fórmulas e métricas calculadas | [Ver guia](./expressions.md) |
| **Dashboards** | Criação e edição de Dashboards | [Ver guia](./dashboards.md) |
| **Conceitos IA** | Cadastro de fatos da aplicação | [Ver guia](./ai-concepts.md) |
| **Geral** | Identidade visual e configurações | [Ver guia](./general.md) |
| **Explorer** | Relatórios nativos e análise *ad-hoc* | [Ver guia](./explorer.md) |
---
---
url: 'https://docs.horusbi.com.br/etl/processors/transforms.md'
---
# Transformações (Lógica de Dados)
Os processadores de Transformação permitem manipular, limpar e enriquecer os dados que já estão no fluxo.
***
## 🔀 Manipulação de Dados
* **[Join (Junção)](./join.md)** — Combina dados de dois fluxos diferentes baseados em uma chave comum (similar ao JOIN do SQL)
* **[Concatenar (Union)](./union.md)** — Une dois fluxos verticalmente (empilha as linhas)
* **[Map (Mapeamento)](./map.md)** — Scripting avançado em C# para manipulação total dos dados
***
## 💻 Scripting e Lógica Avançada
* **[Python (Pandas)](./python.md)** — Permite escrever scripts Python customizados para manipular o DataFrame usando a biblioteca Pandas
* **[SQL (DuckDB)](./sql-duckdb.md)** — Permite fazer transformações nos dados em memória usando sintaxe SQL (Powered by DuckDB)
***
## 🤖 Inteligência Artificial
* **[Classificação por IA (AIExtract)](./ai-extract.md)** — Enriquece o DataFrame com campos extraídos ou classificados por LLM. Define um schema de saída e a IA preenche os campos para cada registro.
---
---
url: 'https://docs.horusbi.com.br/api.md'
---
## Autenticação
A API utiliza autenticação via **Bearer Token**. Existem dois tipos de tokens:
### Client Token
Para operações multi-tenant (gestão de tenants). Use este token quando precisar gerenciar múltiplos tenants.
### Tenant Token
Para operações dentro de um tenant específico. Use este token para operações de usuários, grupos, flows, etc.
### Exemplo de Uso
Inclua o token no header de cada requisição:
```http
Authorization: Bearer SEU_TOKEN_AQUI
```
## Recursos Disponíveis
A API possui endpoints para gerenciar:
| Recurso | Descrição |
|---------|-----------|
| **Tenants** | Criação e gerenciamento de tenants |
| **Usuários** | Criação, edição e gerenciamento de usuários |
| **Grupos** | Criação e gerenciamento de grupos com permissões |
| **Flows ETL** | Listagem e execução de flows |
| **Agendamentos** | Gerenciamento de agendamentos ETL |
| **Datamarts** | Criação e gerenciamento de datamarts |
| **Templates** | Aplicação de templates em tenants |
| [**Chat IA (Lumia)**](/api/ai-chat) | Integração com o motor de chat IA via REST + SSE |
| [**Repositório de Arquivos**](/api/files) | Upload, listagem, links assinados e exclusão de arquivos do tenant |
## Próximos Passos
* Use a sidebar para navegar diretamente para uma operação específica
---
---
url: 'https://docs.horusbi.com.br/dw/tables/cadastros/validacoes-expressoes.md'
---
# Validações e expressões
Duas ferramentas para deixar seu Cadastro mais robusto: **validações** garantem a **qualidade do dado** na hora de salvar, e **expressões** **enriquecem o BI** com colunas calculadas.
***
## ✅ Validações
Uma validação é uma **regra** que decide se um registro pode ou não ser salvo. As regras são escritas em **JavaScript** e avaliadas **no servidor**, num ambiente isolado e seguro.
Elas rodam **ao salvar um registro** — pelo formulário ou pela grade. Se a regra falhar, o registro **não é gravado** e o usuário recebe a mensagem que você definiu.
Exemplos típicos:
* **Valor positivo** — uma meta ou quantidade tem que ser maior que zero.
* **Data não futura** — uma data de competência não pode estar no futuro.
* **Coerência entre campos** — a data final precisa ser maior ou igual à inicial.
* **Faixa válida** — um percentual tem que ficar entre 0 e 100.
::: tip IA no editor de validações
O editor tem **auxílio de IA**: descreva a regra em português ("o valor não pode ser negativo") e ela gera o código; ou peça para **explicar** uma regra existente. Você sempre revisa o resultado antes de salvar.
:::
***
## 🧮 Expressões
Uma expressão é uma **coluna calculada** que você define no Cadastro. Ela **não é digitada** na grade — o valor é **derivado** de outros campos — e aparece como **coluna no BI**, pronta para usar em widgets e dashboards.
Use expressões quando o número é sempre o **resultado de uma conta** e não faz sentido alguém digitar à mão:
* **Margem** = (receita − custo) / receita
* **Atingimento** = realizado / meta
* **Faixa** classificada a partir de um valor numérico
Como o valor é calculado, ele se mantém **coerente** automaticamente: mude um campo de origem e a expressão acompanha.
> \[!NOTE]
> Expressões são **só para o BI** — elas não são editáveis na grade nem no formulário. Para um valor que as pessoas precisam **digitar**, use um campo normal (veja **[Criar e estruturar](./criar)**).
***
::: warning Validações não rodam na importação
As validações são aplicadas quando alguém **salva um registro** na plataforma — **não** durante a **importação** de planilha. A importação assume que o dado já vem **limpo**; ela faz apenas as checagens de **estrutura** (tipos, campos obrigatórios). Confira os dados antes de importar — veja **[Importar e exportar](./importar-exportar)**.
:::
---
---
url: 'https://docs.horusbi.com.br/lumo/referencia/variables.md'
description: >-
Referência do YAML de variáveis do tenant no Lumo: chaves, segredos e um
exemplo completo.
---
# Variables
As variáveis do tenant guardam valores reutilizáveis e segredos. Ao contrário dos outros recursos, elas vivem num arquivo único e fixo, `variables.yaml`, e não têm `id` no header: o tenant já é a identidade.
Ligue o autocomplete no editor colando esta linha no topo do arquivo:
```yaml
# yaml-language-server: $schema=https://docs.horusbi.com.br/schemas/v2/variables.schema.json
```
## Modelo de configuração
```yaml
# ── header ────────────────────────────────────────────────
kind: variables # obrigatório, literal "variables"
lumo: v2 # obrigatório, literal "v2"
tenantId: integer # obrigatório, >= 1
# sem id: o arquivo é único no tenant
---
# ── body ──────────────────────────────────────────────────
items: # obrigatório
- key: STRING # obrigatório. só MAIÚSCULAS, dígitos e _; não pode começar com dígito
is_secret: boolean # obrigatório
value: string # o valor. some da leitura quando is_secret é true
description: string | null
```
`key` é validado pelo padrão `^[A-Z_][A-Z0-9_]*$`. Um nome em minúsculas, com hífen ou começando por dígito é rejeitado.
## Configuração completa
```yaml
# variables.yaml
# yaml-language-server: $schema=https://docs.horusbi.com.br/schemas/v2/variables.schema.json
kind: variables
lumo: v2
tenantId: 853
---
items:
- key: JANELA_CARGA_DIAS
value: "31"
is_secret: false
description: Dias recarregados pelas cargas Temporal.
- key: EMAIL_ALERTAS
value: dados@exemplo.com.br
is_secret: false
description: Destino das notificações de falha de carga.
# Em variável secreta, o value não volta na leitura: depois de um
# lumo pull ele aparece vazio ou com placeholder. Isso é esperado.
- key: API_TOKEN_ERP
value: "{{ token }}"
is_secret: true
description: Token da API do ERP.
```
## Especificação: header
### `kind`
**Tipo:** string · **Obrigatório:** sim · **Valor:** `variables`
### `lumo`
**Tipo:** string · **Obrigatório:** sim · **Valor:** `v2`
### `tenantId`
**Tipo:** integer (>= 1) · **Obrigatório:** sim
### `id`
**Proibido.** O header de `variables` não aceita `id`. O arquivo é único no tenant, e o schema rejeita o campo.
## Especificação: body
### `items`
**Tipo:** array de objetos · **Obrigatório:** sim
As variáveis. Cada item exige `key` e `is_secret`.
## Especificação: item
Cada item de `items`.
### `key`
**Tipo:** string · **Obrigatório:** sim · **Padrão:** `^[A-Z_][A-Z0-9_]*$`
Nome da variável. Aceita apenas letras maiúsculas, dígitos e sublinhado, e não pode começar com dígito.
```yaml
key: JANELA_CARGA_DIAS # válido
```
### `value`
**Tipo:** string · **Obrigatório:** não
Valor da variável. Quando `is_secret` é `true`, o servidor não devolve o valor na leitura: depois de um `lumo pull`, o campo vem vazio ou com um placeholder. Preencha na criação ou para rotacionar o segredo.
### `is_secret`
**Tipo:** boolean · **Obrigatório:** sim
`true` marca a variável como segredo. O valor deixa de ser devolvido em leituras.
### `description`
**Tipo:** string ou null · **Obrigatório:** não
Para que serve a variável.
---
---
url: 'https://docs.horusbi.com.br/etl/guides/variaveis-globais.md'
---
# Variáveis Globais
As **Variáveis Globais** são valores compartilhados entre todos os fluxos de um tenant. Elas permitem centralizar configurações e parâmetros que são usados em múltiplos Dataflows.
***
## ✨ Vantagens
* **Manutenção Centralizada** — Alteração em um único lugar atualiza todos os fluxos que a utilizam
* **Consistência** — Garante que todos os fluxos usem os mesmos valores de referência
* **Sem Duplicação** — Evita a necessidade de editar fluxo por fluxo quando algo muda
***
## 📝 Exemplos de Uso
### Lista de Organization IDs
Se você tem múltiplas organizações/filiais para processar:
```
ORGANIZATION_IDS = '101, 102, 103, 104, 105'
```
No seu SELECT SQL, use:
```sql
SELECT * FROM vendas WHERE organization_id IN (${ORGANIZATION_IDS})
```
### Parâmetros de Conexão
```
API_BASE_URL = 'https://api.empresa.com/v2'
MAX_RETRIES = 3
```
### Filtros de Negócio
```
TIPOS_DOCUMENTO_VALIDOS = "'NFE', 'NFCE', 'SAT'"
DATA_CORTE = '2020-01-01'
```
***
## ➕ Como Criar
1. Acesse o menu **Variáveis Globais** no editor de DataFlows do HorusETL
2. Clique em **Nova Variável**
3. Preencha o **Nome** (em MAIÚSCULAS, sem espaços) e o **Valor**
4. Salve a variável
***
## 🔗 Como Usar nos Fluxos
Em qualquer nó que aceite parâmetros (SQL, HTTP Request, etc.), referencie a variável com a sintaxe:
```
{NOME_DA_VARIAVEL}
```
> \[!TIP]
> **Mudança Global**: Se você adicionar uma nova organização, basta alterar a variável `ORGANIZATION_IDS` uma única vez. Todos os fluxos que a referenciam passarão a incluir a nova organização automaticamente na próxima execução.
---
---
url: 'https://docs.horusbi.com.br/dataviz/03-widgets/gauge.md'
---
# Velocímetro (Gauge)
O Widget **Velocímetro** é ideal para visualizar o desempenho em relação a uma meta ou limite definido. Ele exibe um valor realizado posicionado em uma escala colorida, com faixas que indicam a qualidade do resultado (Ruim, Médio, Bom).
## 📊 Tipos de Gauge
O Widget oferece dois tipos visuais distintos:
### 1. Velocímetro (Speedometer)
O tipo padrão. Exibe um arco colorido com ponteiro indicando o valor realizado em relação ao máximo da escala. Ideal para exibir uma única métrica com visual impactante — ou um grid de métricas lado a lado via **Repetir por Dimensão**.
### 2. Solid Gauge
Exibe múltiplos anéis circulares empilhados, cada um representando um item de uma dimensão. Ideal para comparar o desempenho de múltiplos itens simultaneamente.
**Configurações específicas do Solid Gauge:**
* **Coluna de Dimensão**: Define qual dimensão será usada para separar os anéis (Vendedor, Produto, Região)
* **Limite de Itens**: Quantidade máxima de itens a exibir (1 a 20)
* **Ordenação**: Exibir os itens com maior ou menor percentual primeiro
***
## ⚙️ Configuração de Dados
### Aba Dados
Defina as colunas e o modelo de valor do gauge:
* **Valor Realizado**: Coluna de métrica que alimenta o ponteiro. Obrigatório.
* **Máscara do Valor**: Formato de exibição do valor realizado — `Reduzido` (1K, 1M), `Padrão do Campo`, `Inteiro`, `Moeda`, `Porcentagem`.
* **Objetivo / Máximo da Escala**: Define o teto da escala. Pode ser:
* **Constante**: Um número fixo (ex: `100`). Padrão quando nenhum campo é selecionado.
* **Campo**: Uma coluna da tabela — o máximo é lido de cada linha, permitindo que cada card do grid use seu próprio máximo.
* **Filtros**: Define o contexto de dados para o cálculo.
> \[!NOTE]
> O ponteiro é posicionado na proporção `Realizado / Máximo`. As faixas (bandas de cor) são relativas ao máximo, então se o máximo vier de um campo, cada card do grid terá suas faixas ajustadas ao seu próprio teto.
### Marcador de Meta (Velocímetro)
O **Marcador de Meta** é uma linha opcional que corta o arco indicando um valor de referência secundário (diferente do máximo da escala). Fica desligado por padrão — você o ativa quando precisa.
* **Habilitar Marcador de Meta**: Ativa a linha no arco.
* **Fonte do valor**: Constante ou campo (igual ao Objetivo acima).
***
## 🌈 Faixas de Cor (Bandas)
Defina "zonas de temperatura" no arco para indicar visualmente o status. Clique em **Gerenciar Faixas** para abrir o editor:
* **Nome da Faixa**: Texto opcional para a zona (ex: "Ótimo", "Atenção"). Quando preenchido, habilita o **Status** no layout de texto e a legenda.
* **De / Até**: Intervalo percentual da faixa (0 a 100, relativo ao máximo da escala).
* **Cor de Fundo**: Cor do arco nesta zona.
A barra visual no topo do editor mostra a proporção de cada faixa em tempo real, e você pode arrastar os divisores para ajustar os limites.
**Faixas padrão** (quando nenhuma faixa é configurada):
* 0% – 60%: Vermelho (Crítico)
* 60% – 85%: Laranja (Atenção)
* 85% – 100%: Verde (Ótimo)
***
## 🎨 Configuração Visual
### Estilo (Presets)
Uma galeria de presets visuais permite mudar rapidamente a aparência geral do velocímetro — combinando forma do arco, espessura, tipo de ponteiro e coloração. Clique no preset desejado para aplicá-lo ao gauge atual.
### Tipo de Ponteiro (Velocímetro)
Selecione o indicador de valor em um seletor visual com pré-visualização ao vivo:
* **Gota**: Ponteiro em forma de gota partindo do centro. Padrão.
* **Ponteiro**: Agulha tradicional partindo do centro.
* **Interno**: Indicador no lado interno do arco.
* **Externo**: Indicador no lado externo do arco.
* **Sem Ponteiro**: Esconde o ponteiro (útil quando apenas o valor central importa).
Opções complementares:
* **Cor do Velocímetro**: Cor do ponteiro/agulha.
* **Cor do Ponto Central**: Cor do círculo no eixo (apenas para o tipo Ponteiro).
* **Contorno do Ponteiro (Halo)**: Borda que faz o ponteiro se destacar sobre o arco. Ativo por padrão; desative para um visual mais limpo.
### Estilo do Marcador de Meta (Velocímetro)
Visível apenas quando o Marcador de Meta está habilitado na aba Dados:
* **Estilo da linha**: `Linha sólida`, `Tracejada`, `Triângulo`.
* **Cor**: Cor da linha de meta.
* **Texto do Marcador**: Rótulo opcional exibido ao lado da linha. Suporta cor, tamanho, negrito e itálico independentes.
### Coloração do Arco
Quatro modos de coloração do arco, com seletor visual:
* **Faixas**: O arco é colorido segundo as faixas definidas em Gerenciar Faixas. Padrão.
* **Gradiente**: Transição suave de cor do início ao fim do arco.
* **Barra (cor fixa)**: O arco inteiro em uma única cor configurável.
* **Barra (cor da faixa)**: O arco inteiro assume a cor da faixa onde o ponteiro está posicionado.
### Legenda
Exibe uma legenda das faixas nomeadas fora do arco (requer que ao menos uma faixa tenha nome preenchido):
* `Nenhuma` — sem legenda.
* `Topo` — legenda acima do gauge.
* `Baixo` — legenda abaixo do gauge.
No modo grid (Repetir por Dimensão), a legenda é única para todos os cards.
### Layout de Texto (Velocímetro)
Define quais informações aparecem dentro do arco e onde. Uma galeria de presets (Limpo, KPI, Completo) oferece atalhos rápidos. Cada elemento também pode ser posicionado individualmente:
| Elemento | Conteúdo | Posição padrão |
| :--- | :--- | :--- |
| **Valor** | Valor realizado formatado | Topo |
| **Razão** | Percentual (realizado/máximo) | Oculto |
| **Status** | Nome da faixa atual | Oculto — requer faixa com nome |
Posições disponíveis por elemento: `Oculto`, `Topo`, `Centro`, `Baixo`.
**Tratamento do centro/topo** (quando algum elemento está em Topo ou Centro):
* **Contorno (Halo)**: Contorno suave que contrasta o texto com o arco ao fundo.
* **Pill**: Caixinha com cor e borda configuráveis, raio de arredondamento (Nenhum / Pequeno / Médio / Grande) e opacidade.
### Aparência
* **Padrão**: Fundo sólido.
* **Transparente**: Remove o fundo para integrar ao Dashboard.
* **Destacado**: Adiciona sombra e borda.
### Rótulos de Dimensão (Solid Gauge)
* **Tamanho da Fonte**: Tamanho do texto dos rótulos de cada anel (10px a 24px).
* **Largura Máxima do Rótulo**: Limita a largura dos rótulos. Textos maiores são truncados com `...` (60px a 300px). Passe o mouse sobre o rótulo para ver o texto completo.
***
## 🔧 Avançado (Velocímetro)
Controles detalhados disponíveis em painéis recolhidos na aba Visual.
### Ângulos
* **Personalizar Ângulos Manualmente**: Ativa os controles de ângulo.
* **Ângulo Inicial**: Onde o arco começa (em graus, relativo ao centro).
* **Ângulo Final**: Onde o arco termina.
> \[!WARNING]
> Evite arcos com amplitude menor que 90° — dificulta a leitura do valor.
### Rotação e Escala
* **Rotação Global**: Gira todo o gauge em torno do seu centro (-180° a 180°). Útil para posicionamento personalizado.
* **Marcações (Ticks)**:
* `Nenhuma` — sem marcações no arco.
* `Pontas` — apenas os valores de início e fim. Padrão.
* `Graduado` — marcações ao longo de todo o arco, com controle de **Densidade** (10px a 100px; valores menores = mais marcações).
* **Máscara dos Ticks**: Formato dos valores nas marcações — `Reduzido`, `Padrão`, `Inteiro`, `Moeda`, `Porcentagem`, `Decimal`.
* **Raio Interno**: Espessura do arco. Valores maiores criam um arco mais fino.
### Textos e Labels
Controles finos por elemento textual (Valor, Razão/%, Status, Marcações):
* **Cor**: Cor do texto do elemento.
* **Tamanho**: Pequeno, Médio ou Grande.
* **Negrito / Itálico**: Estilo tipográfico individual por elemento.
***
## 🔁 Repetir por Dimensão (Velocímetro)
> Disponível apenas no tipo **Velocímetro**. O Solid Gauge não é afetado.
Quando uma **Dimensão de Repetição** é configurada, o widget exibe um **grid responsivo de velocímetros** — um card por valor da dimensão (ex: um por região, um por vendedor). É a forma de transformar um único medidor em um painel de "small multiples".
### Configuração (seção "Repetir por Dimensão" na aba Dados)
* **Dimensão de Repetição**: Coluna de dimensão que gera os cards. Ao limpar este campo, o widget volta ao modo de medidor único.
* **Largura Mínima do Card**: Largura mínima de cada card (em pixels). O grid encaixa automaticamente quantas colunas couberem na largura do widget — a quebra de linha é automática, e a célula rola na vertical quando há muitos cards.
* **Limite de Cards**: Número máximo de cards exibidos. Aplicado após a ordenação.
* **Ordenar Por**: Ordena os cards por qualquer campo — inclusive um campo que não aparece no gauge. Suporta múltiplos campos, com sentido crescente/decrescente por coluna.
> \[!WARNING]
> Se o campo de ordenação tiver granularidade diferente da dimensão repetida (ex: ordenar por uma métrica diária quando a dimensão é mensal), os cards podem **duplicar**. Um aviso é exibido automaticamente quando isso é detectado.
### Comportamento do Máximo no Grid
* **Máximo constante**: Todos os cards compartilham a mesma escala — facilita a comparação visual direta entre eles.
* **Máximo por campo**: Cada card usa o máximo da sua própria linha. As faixas se ajustam automaticamente ao teto de cada card, e o marcador de meta (quando vem de um campo) também varia por card.
***
## 🃏 Aparência do Card (modo grid)
Quando o grid está ativo, cada card já adota a estética padrão dos cards do sistema (fundo claro — escuro no tema escuro —, sombra suave e cantos arredondados). As opções abaixo personalizam a apresentação:
> As configurações de aparência do card ficam dentro da seção **"Repetir por Dimensão"** na aba Dados, logo abaixo da ordenação.
### Fundo do Card
* **Fundo do Card**: Cor personalizada para o fundo de cada card. O contorno do ponteiro se ajusta automaticamente a essa cor para manter o contraste.
### Título do Card
O valor da dimensão (ex: "Norte", "SP") é exibido como título no topo de cada card. Opções de formatação:
* **Cor do Título**: Cor do texto do título.
* **Fundo do Título**: Cor de fundo do bloco de título (opcional; cria um destaque visual).
* **Tamanho**: `Pequeno`, `Médio`, `Grande`.
* **Alinhamento**: `Centro` ou `Esquerda`.
* **Negrito**: Engrossa o texto do título.
* **Quebrar Linha**: Quando desligado (padrão), nomes longos são truncados com `...` (passe o mouse para ver o nome completo). Ative para exibir o nome inteiro em várias linhas.
### Legenda em Card
* **Legenda em Card**: Coloca a legenda das faixas dentro de um cartão (com fundo e contorno do tema), em vez de flutuando solta sobre o gráfico.
***
## 🔍 Comportamento
### Relatório (Drill-through)
* **Campos para Relatório**: Define quais colunas serão mostradas na tabela detalhada ao clicar para visualizar o Relatório no Widget. Permite explorar os dados que compõem o cálculo do gauge.
---
---
url: 'https://docs.horusbi.com.br/dw/tables/cadastros/versoes-publicar.md'
---
# Versões e publicação
A estrutura de um Cadastro evolui — campos entram, regras mudam. Cada mudança vira uma **versão**, e quando estiver pronto você **publica** o Cadastro para que o BI e outras pessoas possam usá-lo.
***
## 🧬 Versões da estrutura
Toda vez que você altera a **definição** do Cadastro (adiciona um campo, muda um tipo, ajusta uma validação), isso vira uma nova **versão** da estrutura. O histórico de versões fica registrado.
Entre duas versões você pode ver um **diff** — exatamente **o que mudou** em campos e validações de uma versão para a outra. É a forma de auditar como a estrutura chegou ao estado atual.
::: info Renomear campo é não-destrutivo
Trocar o **rótulo** (ou o nome interno) de um campo é uma operação **não-destrutiva**: os dados já preenchidos **não se perdem**. O campo passa a se chamar de outro jeito, mas continua apontando para os mesmos valores.
:::
***
## 🚀 Publicar
Um Cadastro começa como **rascunho na Minha Mesa** — só você o vê, e é onde você estrutura e testa. Quando estiver pronto, você o **publica numa Mesa**.
```mermaid
flowchart LR
A["Rascunho (Minha Mesa)"] --> B["Publicar"]
B --> C["Publicado (numa Mesa)"]
C --> D["Consumível no BI + visível a outros"]
```
O que muda depois de publicar:
* **Consumível no BI** — o Cadastro vira uma fonte de dados como qualquer tabela; dá para montar widgets e dashboards em cima dele.
* **Visível a outros** — outras pessoas passam a ver e editar os **dados** conforme a **permissão** que tiverem.
* **Identidade fixa** — depois de publicado, a identidade da tabela passa a ser **fixa** e estável, para que dashboards e processos que dependem dela não quebrem.
> \[!NOTE]
> Publicar expõe o Cadastro, mas **não** libera ninguém automaticamente. Quem vê e quem edita os dados depende das permissões — veja **[Permissões](/hec/resources/cadastros-permissoes)**.
---
---
url: 'https://docs.horusbi.com.br/dataviz/04-features/alerts/webhook.md'
---
# Webhook (Visão Geral)
O canal **Webhook** entrega o conteúdo de cada alerta como **payload JSON** numa URL que você controla. É a porta de integração entre o Lumo e o restante do seu ecossistema: sistemas internos, automações (n8n, Make, Zapier), bots de WhatsApp/Slack, gateways de mensageria customizados, lakes de auditoria, etc.
> \[!TIP]
> **Caso real comum:** o cliente quer "uma integração customizada de WhatsApp via Meta Cloud API". A forma certa de resolver é: configurar o canal Webhook, ter um middleware (servidor próprio ou n8n) que recebe o payload do Lumo e chama a API oficial da Meta. Veja [exemplo completo em Avançado](./advanced.md#integracao-whatsapp-via-meta-cloud-api).
***
## Quando usar Webhook
* **Integrações customizadas com sistemas internos** (helpdesk, CRM, ERP, ITSM).
* **Automação no-code/low-code** via n8n, Make, Zapier — recebe o webhook e dispara fluxos arbitrários.
* **Gateways de mensageria próprios** (instância de WhatsApp Business API com a Meta diretamente, Slack via Incoming Webhook, Microsoft Teams, Discord, etc.).
* **Logs externos de auditoria** (envia toda entrega para um lake/Elastic/Splunk seu).
* **Roteamento condicional** que depende de lógica que não cabe no Lumo (ex.: priorizar pager de plantão pelo escalonamento atual).
***
## Como funciona
```mermaid
flowchart TD
A[Alerta dispara] --> B[Lumo monta payload JSON]
B --> C[POST para a URL do tenant]
C --> D{Endpoint respondeu 2xx?}
D -->|Sim| E[Sucesso]
D -->|Não / timeout| F[Fila de retentativa]
F -->|backoff até 8x| C
F -->|esgotou| G[Falha permanente]
E --> H[Histórico do alerta]
G --> H
```
Cada entrega bem-sucedida ou falha aparece no **histórico do alerta** com timestamps, status HTTP recebido e mensagem de erro (quando houver).
***
## Configuração em 3 passos
### 1. Configurar a URL do Webhook
A URL pode ser definida em dois níveis, com **herança automática**:
| Onde configurar | Quando usar |
|---|---|
| **HEC → Tenants → \[seu tenant] → Canais de Alerta Permitidos → URL do Webhook** | Quando cada tenant tem destino próprio (mais comum) |
| **HEC → Clientes → \[seu cliente] → Canais de Alerta Permitidos → URL do Webhook** | Quando todos os tenants do cliente devem cair no mesmo endpoint |
> \[!TIP] Herança Cliente → Tenant
> Se o tenant **não tiver URL preenchida**, o Lumo usa automaticamente a URL configurada no **cliente pai**. Configurar no nível do cliente é a forma mais econômica quando todos os tenants daquele cliente compartilham o mesmo middleware/integração. Para um tenant específico precisar de URL diferente, basta preencher a URL dele — ela sobrepõe a do cliente.
Requisitos da URL:
* **HTTPS obrigatório.** URLs `http://` são rejeitadas antes de qualquer tentativa de envio.
* Aceita qualquer endpoint público (n8n, Make, Zapier, seu servidor, Cloudflare Worker, AWS Lambda + API Gateway, etc.).
> \[!INFO]
> Uma única URL atende **todos os alertas do tenant** (ou de todos os tenants, se configurada no cliente). Para rotear por alerta no seu lado, use o campo `alert.id` ou `alert.nome` do payload. Para rotear por destinatário, use `user.id` ou `user.email`. Para rotear por tenant quando a URL é compartilhada, use `tenantId`.
### 2. Habilitar o canal "Webhook" no alerta
No editor do alerta, aba **Destinatários e Canais**, marque **Webhook** entre os canais de entrega. Isso pode ser combinado com Email, WhatsApp e Telegram — todos recebem o mesmo conteúdo, em formatos apropriados a cada canal.
### 3. Testar antes de ativar
* Aponte temporariamente a URL para um endpoint de teste como [webhook.site](https://webhook.site) (veja [Receivers → webhook.site](./receivers.md#webhook-site-inspecao-rapida)).
* Use o botão **Forçar envio agora** no alerta para disparar uma entrega real e ver o payload chegando no endpoint de teste. (O botão **Pré-visualizar** apenas renderiza o conteúdo na tela — **não dispara POST** para a URL do webhook.)
* Confira no \[Histórico do alerta] o status, a resposta HTTP recebida e qualquer erro.
***
## Contrato resumido
| Aspecto | Valor |
|---|---|
| **Método** | `POST` (fixo) |
| **Headers fixos (sistema)** | `Content-Type: application/json`, `User-Agent: Horus-Alert-Webhook/1.0` |
| **Headers customizados** | Configuráveis por tenant (até 20, máx 100 chars na chave, 500 no valor). Veja [Headers HTTP que sua URL recebe](./payload.md#headers-http-que-sua-url-recebe) |
| **Corpo** | JSON (envelope com `alert`, `user`, `tenantId`, `timestamp`, `content[]`) |
| **Timeout** | 10 segundos por tentativa |
| **Tamanho máx. do payload** | 4 MB |
| **Critério de sucesso** | Status HTTP entre 200 e 299 |
| **Tentativas** | Até 8 (backoff exponencial: ~2s, 4s, 8s, 16s, 32s, 1min, 2min, 4min, teto de 15min) |
| **Jitter** | ±20% no intervalo de retry |
| **Resposta armazenada** | Sim, até 10 KB (truncada acima disso) |
| **HTTP** | Rejeitado (somente HTTPS) |
Para a especificação completa do payload (todos os campos, JSON Schema, tipos TypeScript), veja **[Referência do Payload](./payload.md)**.
***
## Preview do payload
Exemplo enxuto do que chega ao seu endpoint. Veja a [referência completa](./payload.md) para todos os campos:
```json
{
"alert": {
"id": 123,
"nome": "Margem Negativa Diária",
"type": "condition",
"app_id": 456
},
"user": {
"id": 789,
"nome": "Maria Souza",
"email": "maria@empresa.com"
},
"tenantId": 1,
"timestamp": "2026-05-27T10:30:00.000Z",
"content": [
{
"id": 11,
"type": "text",
"nome": "Cabeçalho",
"generated": { "text": "3 vendas com margem negativa em Loja Centro" }
},
{
"id": 12,
"type": "ai",
"nome": "Análise IA",
"generated": {
"text": "Resumo: 3 vendas com prejuízo total de R$ 1.230...",
"steps": []
}
},
{
"id": 13,
"type": "report",
"nome": "Detalhamento",
"generated": {
"pdf": "https://storage.horusbi.com.br/download/a1b2c3.pdf",
"xlsx": "https://storage.horusbi.com.br/download/a1b2c3.xlsx"
}
}
]
}
```
***
## Confiabilidade e Reentrega
O Lumo trata seu endpoint como **eventually available** — falhas transitórias (timeouts, 5xx, indisponibilidade momentânea) são absorvidas pela política de retry. Mas há um teto:
* **Até 8 tentativas** por entrega. Após esgotar, a entrega é marcada como falha permanente.
* **Backoff exponencial com jitter**: ~2s → ~4s → ~8s → ~16s → ~32s → ~1min → ~2min → ~4min (com ±20% de variação). Teto de 15 minutos por retry, mesmo em casos extremos.
* **Garantia at-least-once**: o mesmo payload pode chegar **mais de uma vez** se a sua resposta de sucesso (2xx) chegou tarde demais. Implemente idempotência ([detalhes](./advanced.md#idempotencia)).
* **Falha permanente notifica o criador**: quando uma entrega esgota todas as tentativas, o criador do alerta e os admins do tenant recebem uma notificação dentro do Lumo informando o problema.
> \[!WARNING]
> Falha no webhook **não derruba outros canais.** Se a mesma entrega tem Email + Webhook e o Webhook falhar permanentemente, o Email ainda foi enviado. Mas a entrega inteira aparece com status de erro no histórico (filosofia "fail loud").
***
## Limitações conhecidas (hoje)
Para o cliente decidir antes de integrar:
* **Método fixo em `POST`**. Não há `PUT`/`PATCH`/etc.
* **URL única por tenant.** Não há URL diferente por alerta nem por destinatário.
* **Sem configuração de retry pelo usuário** (8 tentativas e backoff são fixos).
Detalhes em [Avançado → Limitações conhecidas](./advanced.md#limitacoes-conhecidas).
> \[!INFO]
> Para autenticar o request no seu endpoint, combine **caminho secreto na URL** + **headers HTTP personalizados** (`Authorization: Bearer ...`, etc.). Veja [Validação de Origem](./advanced.md#validacao-de-origem).
***
## Próximos Passos
* [Referência do Payload](./payload.md) — contrato técnico completo, JSON Schema, tipos TypeScript.
* [Como Receber e Testar](./receivers.md) — webhook.site, curl, n8n, Make, Zapier, Node.js, Python, serverless.
* [Avançado](./advanced.md) — idempotência, validação de origem, integração WhatsApp via Meta, Slack, troubleshooting, limitações.
---
---
url: 'https://docs.horusbi.com.br/dataviz/03-widgets/svelte-custom.md'
---
# Widget Svelte (Código Avançado)
O **Widget Svelte** é a ferramenta definitiva para flexibilidade no Horus DataViz. Ele permite que desenvolvedores criem **qualquer tipo de visualização ou interatividade** utilizando código padrão Svelte, JavaScript e CSS (Tailwind).
> \[!WARNING]
> **Versão do Svelte**: O ambiente suporta **Svelte 4**.
> Funcionalidades do Svelte 5 (como *Runes*) **não** são suportadas. Utilize a sintaxe clássica de reatividade (`let`, `$:`, etc.).
***
## 🖥️ 1. Configuração Visual (UI)
Antes de mergulhar no código, o Widget possui configurações visuais que controlam seu contêiner e comportamento no Dashboard.
### Aba Geral
* **Gráfico com fundo transparente**: Remove o fundo branco e bordas padrão do card. Ideal para criar KPIs flutuantes, formas personalizadas ou integrar visualmente com o fundo do Dashboard.
* **Filtros Pré-aplicados**: Define filtros que afetam **apenas este Widget**, independentemente dos filtros globais do Dashboard.
### Aba Comportamento
* **Colocar link no Indicador**: Adiciona funcionalidade de clique/link baseada no contexto do Widget.
* **Campos para Relatório**: Seleciona colunas que serão passadas para o contexto de "Exportação" ou "Drill-through" se o Widget for usado como ponto de partida para um Relatório detalhado.
* **Filtros do Relatório**: Filtros adicionais aplicados apenas quando o usuário navega para o Relatório detalhado a partir deste Widget.
***
## 🛠️ 2. Ambiente de Desenvolvimento
Ao clicar em "Editar Código", há acesso a um editor completo.
### Bibliotecas Disponíveis
O ambiente já vem com bibliotecas essenciais pré-instaladas. Não é necessário (nem possível) fazer `npm install`.
* **Svelte**: Sintaxe padrão (`{#if}`, `{#each}`, `on:click`).
* **Highcharts** (Recomendado): Via wrapper oficial `@highcharts/svelte`.
* **Lodash** (`_`): Utilitários para manipulação de arrays/objetos.
* **Moment** (`moment`): Manipulação de datas e horas.
* **Chroma-js** (`chroma`): Manipulação de cores.
* **XLSX** (`xlsx`): Geração de planilhas Excel.
* **TailwindCSS**: Todas as classes utilitárias estão disponíveis globalmente.
***
## 🔌 3. API do Objeto `app`
A variável `app` (`export let app;`) é sua ponte com os dados e o motor do HorusBI.
### Buscando Dados (`fetchMatrix`)
Use `app.fetchMatrix` para executar queries no motor de dados (Hyper-c).
```javascript
/* Exemplo de uso dentro de uma função async ou onMount */
const data = await app.fetchMatrix({
columns: [
'[Vendas]."VENDEDOR":AGP', // Dimensão (Agrupador)
'[Vendas]."VALOR":SUM' // Métrica (Soma)
],
filters: [], // Opcional: filtros adicionais
orderBy: ['[Vendas]."VALOR":SUM:DESC'], // Ordenação
limit: 100
});
// A resposta "data" contém:
// data.Columns: Array com metadados (rótulos, tipos)
// data.Result: Matriz de dados [[Vendedor A, 1000], [Vendedor B, 500]]
```
> \[!IMPORTANT]
> **Expressões são Obrigatórias**: Ao definir colunas, é necessário sempre incluir a expressão de agregação (`:SUM`, `:AGP`, `:DATE`, etc.). Sem isso, o motor não sabe como processar a query.
### Gerenciamento de Filtros
É possível fazer o Widget interagir com o resto do Dashboard aplicando filtros globais.
```javascript
// Criar referência à coluna (sempre com expressão!)
const colunaVendedor = app.columnFromString('[Vendas]."VENDEDOR":AGP');
// Aplicar filtro (Ex: Filtrar onde Vendedor é 'João')
app.addFilter(colunaVendedor, "=", "João");
// Remover filtro
app.addFilter(colunaVendedor, "=", false);
```
### Formatação
Use `app.formatValue` para garantir que números e datas sigam o padrão do sistema (moeda, casas decimais, locale).
```javascript
// row[1] é o valor numérico cru (ex: 1540.5)
// colunaValor contém os metadados de formatação
const textoFormatado = app.formatValue(row[1], colunaValor);
// Resultado: "R$ 1.540,50" (dependendo do locale)
```
***
## 📋 4. Exemplo Completo
Abaixo, um exemplo de Widget que lista vendedores e filtra o Dashboard ao clicar.
```html
{#if loading}
Carregando...
{:else}
Top Vendedores
{#each vendedores as v}
filtrar(v.nome)}
>
{v.nome}{v.valorFormatado}
{/each}
{/if}
```
***
## 🔐 5. Autenticação JWT para Integrações Externas
O método `app.generateJWTToken()` permite criar tokens de autenticação para chamar APIs externas (ERP, CRM, etc.) de forma **segura**, sem expor credenciais no frontend.
### Por que usar JWT?
Armazenar tokens de API ou senhas no código JavaScript é uma **falha de segurança** — qualquer usuário pode inspecionar o código do browser. O JWT resolve isso criando um **mecanismo de confiança** entre o Horus e seu sistema:
1. O **backend do Horus** gera um token assinado com uma chave secreta
2. O Widget envia esse token para sua API
3. A API valida o token com a **mesma chave** (configurada na aba Avançado do Tenant/Cliente)
4. Se válido → a API confia que a requisição veio de um usuário autenticado
### Configuração Prévia
> \[!IMPORTANT]
> Para usar `generateJWTToken()`, é necessário primeiro habilitar e configurar a **Chave JWT** na aba **Avançado** do Tenant ou Cliente. [Veja a documentação](./../../hec/resources/tenants.md#gerador-de-jwt).
### API do Método
```javascript
const result = await app.generateJWTToken();
// result = {
// token: "eyJhbGciOiJIUzI1NiIs...", // Token JWT assinado
// payload: {
// userId: 123,
// tenantId: 456,
// data: "2024-01-15T10:30:00.000Z" // ISO timestamp UTC
// }
// }
```
### Exemplo Completo: Botão "Aprovar Pedido"
Um Widget que lista pedidos pendentes e permite aprová-los diretamente no Dashboard:
```html
Pedidos Pendentes de Aprovação
{#if loading}
Carregando...
{:else if pedidos.length === 0}
Nenhum pedido pendente
{:else}
ID
Cliente
Valor
Ação
{#each pedidos as pedido}
{pedido.id}
{pedido.cliente}
{pedido.valor}
{/each}
{/if}
```
### Validando o Token no seu Backend
No lado do servidor (ERP, API, etc.), é necessário validar o token JWT usando a mesma chave configurada no Horus.
**Exemplo em Node.js:**
```javascript
const jwt = require('jsonwebtoken');
const CHAVE_JWT = 'sua-chave-configurada-no-horus'; // Mesma chave do Tenant
app.post('/pedidos/aprovar', (req, res) => {
const token = req.headers.authorization?.replace('Bearer ', '');
try {
const payload = jwt.verify(token, CHAVE_JWT);
// payload = { userId: 123, tenantId: 456, data: "2024-01-15T10:30:00.000Z" }
console.log(`Usuário ${payload.userId} do tenant ${payload.tenantId} está aprovando pedido`);
// Seu código de aprovação aqui...
res.json({ success: true });
} catch (error) {
res.status(401).json({ error: 'Token inválido' });
}
});
```
**Exemplo em Python:**
```python
import jwt
CHAVE_JWT = 'sua-chave-configurada-no-horus'
@app.route('/pedidos/aprovar', methods=['POST'])
def aprovar_pedido():
token = request.headers.get('Authorization', '').replace('Bearer ', '')
try:
payload = jwt.decode(token, CHAVE_JWT, algorithms=['HS256'])
# payload = { 'userId': 123, 'tenantId': 456, 'data': '2024-01-15T10:30:00.000Z' }
print(f"Usuário {payload['userId']} aprovando pedido")
# Seu código aqui...
return jsonify({'success': True})
except jwt.InvalidTokenError:
return jsonify({'error': 'Token inválido'}), 401
```
> \[!CAUTION]
> **Nunca exponha a Chave JWT** no código frontend. Ela deve existir apenas:
>
> * No backend do Horus (configurada na aba Avançado)
> * No backend do seu sistema externo
---
---
url: 'https://docs.horusbi.com.br/dataviz/03-widgets.md'
---
# Widgets e Visualizações
Os **Widgets** são os blocos de construção dos Dashboards no Horus DataViz, onde cada Widget é um componente visual especializado em representar dados em diversos contextos, como KPIs, mapas, gráficos e tabelas pivô.
## 🔄 Interatividade
A maioria dos Widgets oferecem recursos nativos de interatividade:
* **Drill-down**: Clique em uma barra ou fatia para "mergulhar" nos detalhes daquela categoria, revelando os dados granulares por trás do valor
* **Cross-filtering** (Filtragem cruzada): Ao filtrar um Widget, todos os outros Widgets da página que compartilham o mesmo contexto de dados são filtrados automaticamente
* **Maximizar**: Qualquer Widget pode ser expandido para tela cheia para melhor visualização
* **Ir para o Relatório**: A maioria dos Widgets possui um botão que permite navegar para o Relatório tabular original que gerou aquela visualização
## 📚 Tipos de Widgets
Abaixo encontra-se a documentação específica para cada tipo de visualização disponível:
* [Cartão / KPI](kpi.md)
* [Tabela](table.md)
* [Matriz (Pivot)](matrix.md)
* [Matriz Composta (Demonstrativo)](composed-matrix.md)
* [Gráficos de Barra e Linha](bar-line.md)
* [Gráfico de Pizza](pie.md)
* [Mapas](map.md)
* [Velocímetro (Gauge)](gauge.md)
* [Filtro de Lista (ListBox)](listbox.md)
* [Texto Rico](text.md)
* [Calendário](calendar.md)
* [Diagramas](diagram.md)
* [Previsão (Forecast)](forecast.md)
* [Punchcard](punchcard.md)
* [Widget Svelte (Avançado)](svelte-custom.md)
* [Componentes Reutilizáveis (Svelte Paramétrico)](svelte-parametrico.md)