Mapa de Processos
API & Dados
Documentação completa das chamadas REST feitas pelo n8n para reconstruir o mapa de processos da EVOPE — payload de entrada, formato de saída e algoritmo de construção do grafo.
Visão Geral da Arquitetura
O mapa de processos é reconstruído 100% via API REST — sem depender do portal web
nem de cookie de sessão. O n8n faz 3 chamadas em paralelo, merge os dados e constrói o grafo JSON
pronto para renderização com vis-network.js.
O endpoint
/Pages/ProcessMap.aspx/GetProcessMaps usa cookie de sessão ASP.NET (HttpOnly) —
não é compatível com Bearer token da API REST. Nossa abordagem via /applicationreport +
/sitereport produz resultados equivalentes usando a API pública.
1 Autenticação
Toda chamada à API REST requer um Bearer token obtido via login. O token expira em ~30 minutos — o n8n reautentica a cada execução do workflow.
Request Body
{
"email": "seu-email@evope.com.br",
"password": "sua-senha"
}
Response 200
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expiration": "2026-07-14T15:30:00Z"
}
Como usar nas chamadas seguintes
Authorization: Bearer <token>
/cortex/secrets/clients/evope.env.
No n8n, estão codificadas no nó "Autenticar EVOPE" (migrar para n8n Credentials Manager é recomendado).
2 Application Report
Retorna a sequência de aplicativos usados por cada colaborador em janelas de atividade. É a fonte primária para construir os nós (apps) e as arestas (co-uso) do grafo.
Request Body
{
"DateIni": "2026-07-07", // YYYY-MM-DD ou DD/MM/YYYY
"DateEnd": "2026-07-14", // YYYY-MM-DD ou DD/MM/YYYY
"ClientGroupId": "", // "" = todos | "5850" = Produto
"SearchSubgroups": true // incluir subgrupos do grupo
}
EmployeeID nem ClientGroupId com valor vazio string
usando chaves explícitas quando quiser todos os dados — simplesmente omita essas chaves ou envie "" apenas para ClientGroupId.
Para EmployeeID, a chave deve ser omitida quando quiser todos os colaboradores.
Response 200 — Array de registros
[
{
"groupName": "MI. > Estrategistas",
"employeeName": "Joabe Santana de Andrade",
"startDate": "2026-07-07T09:15:00", // janela de atividade início
"endDate": "2026-07-07T09:45:00", // janela de atividade fim
"application": "chrome", // nome do app
"totalMinutes": 28 // minutos nessa janela
},
{
"groupName": "MI. > Estrategistas",
"employeeName": "Joabe Santana de Andrade",
"startDate": "2026-07-07T09:15:00", // mesmo startDate = mesma sessão
"endDate": "2026-07-07T09:45:00",
"application": "Discord",
"totalMinutes": 12
}
]
Schema dos campos
| Campo | Tipo | Descrição |
|---|---|---|
groupName | string | Nome do grupo/departamento do colaborador |
employeeName | string | Nome completo do colaborador |
startDate | ISO 8601 | Início da janela de atividade monitorada |
endDate | ISO 8601 | Fim da janela de atividade monitorada |
application | string | Nome do aplicativo (ex: chrome, EXCEL, Discord) |
totalMinutes | integer | Minutos de uso nessa janela |
employeeName + startDate
foram usados na mesma janela de atividade. Apps que aparecem juntas no mesmo grupo = uma aresta no mapa.
3 Site Report
Retorna os sites acessados por cada colaborador com categorias e tempo. Também fornece o activityEmployeeID — identificador numérico estável dos colaboradores (diferente do encryptEmail do portal).
Request Body
{
"DataIni": "07/07/2026", // formato DD/MM/YYYY (diferente do applicationreport!)
"DataFim": "13/07/2026" // NÃO enviar ClientGroupId nem EmployeeID vazios
}
DataIni/DataFim (não DateIni/DateEnd)
e aceita apenas o formato DD/MM/YYYY. Enviar ClientGroupId: "" ou EmployeeID: ""
retorna 0 resultados — essas chaves devem ser omitidas completamente quando quiser todos os dados.
Response 200 — Array de registros
[
{
"groupName": "Produto > Data Science.",
"employeeName": "Leandro Rosa de Oliveira",
"startDate": "2026-07-07T10:00:00",
"endDate": "2026-07-07T10:30:00",
"application": "chrome",
"site": "github.com",
"categorySiteName": "Programação e Desenvolvimento",
"periodo": "2026-07-07",
"totalMinutes": 18,
"cNameCore": "github.com",
"activityEmployeeID": 77738 // ID estável do colaborador
}
]
Schema dos campos
| Campo | Tipo | Descrição |
|---|---|---|
groupName | string | Grupo do colaborador |
employeeName | string | Nome do colaborador |
startDate | ISO 8601 | Início da sessão |
endDate | ISO 8601 | Fim da sessão |
application | string | App utilizado (chrome, msedge, firefox) |
site | string | Domínio do site acessado |
categorySiteName | string | Categoria do site (ex: "Redes Sociais", "Produtividade") |
periodo | YYYY-MM-DD | Data da atividade |
totalMinutes | integer | Minutos nessa sessão |
cNameCore | string | Domínio raiz do site |
activityEmployeeID | integer | ID estável do colaborador — usar para filtros |
encryptEmail para filtrar colaboradores,
mas esse valor muda por sessão. O activityEmployeeID é estável entre requisições e pode ser armazenado
para uso em filtros persistentes.
4 Grupos (ClientGroup)
Lista todos os departamentos/grupos disponíveis para uso como filtro nas chamadas de relatório.
Response 200 — Array de grupos
[
{ "clientGroupID": 5850, "name": "Produto", "parentID": null },
{ "clientGroupID": 5851, "name": "Data Science.", "parentID": 5850 },
{ "clientGroupID": 5522, "name": "Marketing", "parentID": null }
]
Grupos raiz do tenant RankMyApp (38 grupos no total)
| clientGroupID | name | parentID |
|---|---|---|
5850 | Produto | raiz |
5522 | Marketing (MI) | raiz |
5365 | RMAds Negócios (RI) | raiz |
5898 | Desenvolvimento | raiz |
5172 | Comercial | raiz |
5774 | Execs | raiz |
5798 | Di | raiz |
5173 | Master | raiz |
5851 | Data Science. | 5850 |
5523 | Estrategistas | 5522 |
5524 | Accounts | 5172 |
5901 | DataRank | 5898 |
5899 | Tech | 5898 |
Lista completa em /cortex/clients/evope/processmap-api.md → seção Grupos.
5 Webhook n8n — Endpoint Unificado
O webhook é o ponto de entrada único. Ele autentica, faz as 3 chamadas em paralelo, monta o grafo e retorna JSON pronto para o dashboard.
Autenticação do Webhook
Toda chamada ao webhook requer o header X-Auth-Token. Solicite o token ao time Digital AI.
X-Auth-Token: <token-fornecido-pelo-time-digital-ai>
Payload de Entrada
{
"DateIni": "2026-07-07", // obrigatório — YYYY-MM-DD
"DateEnd": "2026-07-14", // obrigatório — YYYY-MM-DD
"ClientGroupId": "", // opcional — "" = todos grupos
"EmployeeID": "", // opcional — "" = todos colaboradores
"SearchSubgroups": true // opcional — incluir subgrupos
}
Formato de Saída (Response 200)
{
"graph": {
"nodes": [ /* ver schema abaixo */ ],
"edges": [ /* ver schema abaixo */ ]
},
"stats": {
"totalNodes": 40,
"totalEdges": 291,
"totalEmployees": 38,
"totalSessions": 1018,
"totalAppRecords": 3945,
"totalGroups": 38
},
"employees": [
{
"id": 77738,
"name": "Leandro Rosa de Oliveira",
"email": "leandro.rosa@rankmyapp.com.br",
"group": "Produto > Data Science."
}
],
"groups": [
{ "id": 5850, "name": "Produto", "parentId": null }
],
"groupSummary": {
"MI. > Estrategistas": ["Joabe Santana", "Claudio Bianchi"]
}
}
Schema: graph.nodes
| Campo | Tipo | Descrição | Uso no vis-network |
|---|---|---|---|
id | integer | ID sequencial único do nó | Identificador de referência |
label | string | Nome do aplicativo (ex: chrome, EXCEL) | Texto exibido no nó |
title | string | Tooltip: minutos totais + sessões | Tooltip no hover |
value | integer | Total de minutos de uso no período | Tamanho do nó (scaling) |
topSites | array | Top sites acessados nesse app: [["github.com", 120], ...] | Detalhe no drawer |
siteCategories | array | Categorias de sites: ["Dev", "Comunicação"] | Filtro por categoria |
Exemplo de nó
{
"id": 1,
"label": "chrome",
"title": "chrome\n1240 min total | 87 sessões",
"value": 1240,
"topSites": [["github.com", 320], ["google.com", 210]],
"siteCategories": ["Programação", "Pesquisa", "Comunicação"]
}
Schema: graph.edges
| Campo | Tipo | Descrição | Uso no vis-network |
|---|---|---|---|
id | integer | ID sequencial da aresta | Identificador |
from | integer | ID do nó de origem | Ponto de partida da aresta |
to | integer | ID do nó de destino | Ponto de chegada da aresta |
value | integer | Frequência de co-ocorrência dos apps | Espessura da aresta (scaling) |
title | string | Tooltip: descrição textual da conexão | Tooltip no hover |
Exemplo de aresta
{
"id": 1,
"from": 1, // chrome
"to": 3, // Discord
"value": 45, // usados juntos 45x no período
"title": "Usados juntos: 45x"
}
6 Algoritmo de Construção do Grafo
Como o n8n transforma os dados brutos dos relatórios em nós e arestas do grafo.
applicationreport são agrupados por employeeName + startDate. Cada grupo = uma janela de atividade (período monitorado). Apps no mesmo grupo foram usados simultaneamente.value do nó = soma de totalMinutes de todas as ocorrências desse app no período. Quanto maior o value, maior o nó no vis-network.sitereport são cruzados por application para adicionar topSites e siteCategories em cada nó. Apenas apps de browser têm esses dados (chrome, msedge, firefox).graph (nodes + edges), stats, employees (de activityEmployeeID do sitereport) e groups (do ClientGroup).Pseudocódigo JavaScript
// 1. Separar dados por tipo de endpoint const appItems = items.filter(i => i.application && !i.activityEmployeeID); const siteItems = items.filter(i => i.activityEmployeeID); const groups = items.filter(i => i.clientGroupID); // 2. Agrupar appItems por (employeeName + startDate) → sessões const sessions = {}; for (const row of appItems) { const key = `${row.employeeName}|${row.startDate}`; if (!sessions[key]) sessions[key] = []; sessions[key].push(row.application); } // 3. Acumular minutos por app → nós const appMinutes = {}; for (const row of appItems) { appMinutes[row.application] = (appMinutes[row.application] || 0) + row.totalMinutes; } // 4. Contar co-ocorrências → arestas const edgeMap = {}; for (const apps of Object.values(sessions)) { for (let i = 0; i < apps.length; i++) { for (let j = i + 1; j < apps.length; j++) { const key = [apps[i], apps[j]].sort().join('|'); edgeMap[key] = (edgeMap[key] || 0) + 1; } } } // 5. Montar nodes e edges para vis-network const nodeIndex = {}; const nodes = Object.entries(appMinutes).map(([app, mins], idx) => { nodeIndex[app] = idx + 1; return { id: idx + 1, label: app, value: mins }; }); const edges = Object.entries(edgeMap).map(([key, weight], idx) => { const [a, b] = key.split('|'); return { id: idx + 1, from: nodeIndex[a], to: nodeIndex[b], value: weight }; });
7 Filtros Disponíveis
Parâmetros que podem ser passados no webhook para refinar os dados retornados.
| Parâmetro | Tipo | Valores | Efeito |
|---|---|---|---|
DateIni | string | "2026-07-07" | Início do período (obrigatório) |
DateEnd | string | "2026-07-14" | Fim do período (obrigatório) |
ClientGroupId | string | "" = todos"5850" = Produto"5522" = Marketing | Filtra por departamento |
SearchSubgroups | boolean | true / false | Inclui subgrupos do grupo selecionado |
Grupos disponíveis para filtro
| ClientGroupId | Grupo | Subgrupos |
|---|---|---|
"" | Todos os grupos | — |
5850 | Produto | Data Science. |
5522 | Marketing (MI) | Estrategistas, Accounts, Ops, Liderança |
5365 | RMAds Negócios (RI) | Atendimento, Negócios, Lideranças |
5898 | Desenvolvimento | DataRank, Tech |
5172 | Comercial | Accounts, Finance, People, Estratégia... |
5774 | Execs | BI |
8 Exemplo Completo de Chamada
cURL
curl -X POST https://webhook.digital-ai.tech/webhook/evope/processmap \
-H "Content-Type: application/json" \
-H "X-Auth-Token: <seu-token>" \
-d '{
"DateIni": "2026-07-07",
"DateEnd": "2026-07-14",
"ClientGroupId": "",
"SearchSubgroups": true
}'
JavaScript (fetch)
const response = await fetch(
'https://webhook.digital-ai.tech/webhook/evope/processmap',
{
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-Auth-Token': '' },
body: JSON.stringify({
DateIni: '2026-07-07',
DateEnd: '2026-07-14',
ClientGroupId: '',
SearchSubgroups: true
})
}
);
const data = await response.json();
console.log(`Nodes: ${data.stats.totalNodes}`);
console.log(`Edges: ${data.stats.totalEdges}`);
console.log(`Employees: ${data.stats.totalEmployees}`);
Python (requests)
import requests
response = requests.post(
"https://webhook.digital-ai.tech/webhook/evope/processmap",
headers={"X-Auth-Token": ""},
json={
"DateIni": "2026-07-07",
"DateEnd": "2026-07-14",
"ClientGroupId": "",
"SearchSubgroups": True
}
)
data = response.json()
nodes = data["graph"]["nodes"]
edges = data["graph"]["edges"]
print(f"Nodes: {len(nodes)}, Edges: {len(edges)}")
9 Replicar sem n8n
Script Python standalone que replica exatamente o que o workflow n8n faz — útil para integrar em outras ferramentas ou rodar localmente.
import requests
from itertools import combinations
from collections import defaultdict
BASE_URL = "https://openrest.evope.com.br/api/v0"
EMAIL = "seu-email@evope.com.br"
PASSWD = "sua-senha"
def build_processmap(date_ini: str, date_end: str, group_id: str = ""):
# 1. Autenticar
token = requests.post(f"{BASE_URL}/auth", json={
"email": EMAIL, "password": PASSWD
}).json()["token"]
headers = {"Authorization": f"Bearer {token}"}
# 2. Buscar dados em paralelo (aqui sequencial para simplicidade)
app_data = requests.post(f"{BASE_URL}/applicationreport", headers=headers, json={
"DateIni": date_ini, "DateEnd": date_end,
"ClientGroupId": group_id, "SearchSubgroups": True
}).json()
# Sitereport usa DataIni/DataFim e formato DD/MM/YYYY
from datetime import datetime
d1 = datetime.strptime(date_ini, "%Y-%m-%d").strftime("%d/%m/%Y")
d2 = datetime.strptime(date_end, "%Y-%m-%d").strftime("%d/%m/%Y")
site_data = requests.post(f"{BASE_URL}/sitereport", headers=headers, json={
"DataIni": d1, "DataFim": d2
}).json()
groups = requests.get(f"{BASE_URL}/ClientGroup/", headers=headers).json()
# 3. Construir grafo
sessions = defaultdict(list)
app_minutes = defaultdict(int)
for row in app_data:
key = f"{row['employeeName']}|{row['startDate']}"
sessions[key].append(row["application"])
app_minutes[row["application"]] += row.get("totalMinutes", 0)
edge_count = defaultdict(int)
for apps in sessions.values():
for a, b in combinations(sorted(set(apps)), 2):
edge_count[(a, b)] += 1
node_index = {}
nodes = []
for i, (app, mins) in enumerate(app_minutes.items(), 1):
node_index[app] = i
nodes.append({"id": i, "label": app, "value": mins,
"title": f"{app}\n{mins} min total"})
edges = []
for j, ((a, b), weight) in enumerate(edge_count.items(), 1):
if a in node_index and b in node_index:
edges.append({"id": j, "from": node_index[a],
"to": node_index[b], "value": weight,
"title": f"Usados juntos: {weight}x"})
return {
"graph": {"nodes": nodes, "edges": edges},
"stats": {
"totalNodes": len(nodes), "totalEdges": len(edges),
"totalGroups": len(groups)
}
}
# Uso
result = build_processmap("2026-07-07", "2026-07-14")
print(f"Nodes: {result['stats']['totalNodes']}")
print(f"Edges: {result['stats']['totalEdges']}")
result["graph"]["nodes"] e
result["graph"]["edges"] diretamente para o new vis.Network().
Veja o exemplo de renderização na página do dashboard.