📡 API Reference

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.

● Produção Workflow: 0ooIAhl3lP9bdlw6 Base URL: openrest.evope.com.br/api/v0 Token expira em 30min

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.

POST /evope/processmap (webhook entrada)
└── POST /auth → token Bearer (30min)
└── Set Params → extrai datas + filtros do body
├── POST /applicationreport → apps por colaborador
├── POST /sitereport → sites + activityEmployeeID
└── GET /ClientGroup/ → lista de grupos
└── Merge → une os 3 resultados
└── Construir Grafo → nodes + edges
└── Respond → JSON final ao chamador
Por que não usar o GetProcessMaps do portal?
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.

POST https://openrest.evope.com.br/api/v0/auth

Request Body

JSON
{
  "email":    "seu-email@evope.com.br",
  "password": "sua-senha"
}

Response 200

JSON
{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "expiration": "2026-07-14T15:30:00Z"
}

Como usar nas chamadas seguintes

HTTP Header
Authorization: Bearer <token>
Credenciais: Armazenadas em /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.

POST https://openrest.evope.com.br/api/v0/applicationreport

Request Body

JSON
{
  "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
}
Gotcha crítico: NÃO envie 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

JSON
[
  {
    "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

CampoTipoDescrição
groupNamestringNome do grupo/departamento do colaborador
employeeNamestringNome completo do colaborador
startDateISO 8601Início da janela de atividade monitorada
endDateISO 8601Fim da janela de atividade monitorada
applicationstringNome do aplicativo (ex: chrome, EXCEL, Discord)
totalMinutesintegerMinutos de uso nessa janela
Chave para o grafo: Registros com o mesmo 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).

POST https://openrest.evope.com.br/api/v0/sitereport

Request Body

JSON
{
  "DataIni": "07/07/2026",  // formato DD/MM/YYYY (diferente do applicationreport!)
  "DataFim": "13/07/2026"   // NÃO enviar ClientGroupId nem EmployeeID vazios
}
Gotcha: O sitereport usa 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

JSON
[
  {
    "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

CampoTipoDescrição
groupNamestringGrupo do colaborador
employeeNamestringNome do colaborador
startDateISO 8601Início da sessão
endDateISO 8601Fim da sessão
applicationstringApp utilizado (chrome, msedge, firefox)
sitestringDomínio do site acessado
categorySiteNamestringCategoria do site (ex: "Redes Sociais", "Produtividade")
periodoYYYY-MM-DDData da atividade
totalMinutesintegerMinutos nessa sessão
cNameCorestringDomínio raiz do site
activityEmployeeIDintegerID estável do colaborador — usar para filtros
Por que usar activityEmployeeID? O portal usa 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.

GET https://openrest.evope.com.br/api/v0/ClientGroup/

Response 200 — Array de grupos

JSON
[
  { "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)

clientGroupIDnameparentID
5850Produtoraiz
5522Marketing (MI)raiz
5365RMAds Negócios (RI)raiz
5898Desenvolvimentoraiz
5172Comercialraiz
5774Execsraiz
5798Diraiz
5173Masterraiz
5851Data Science.5850
5523Estrategistas5522
5524Accounts5172
5901DataRank5898
5899Tech5898

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.

POST https://webhook.digital-ai.tech/webhook/evope/processmap

Autenticação do Webhook

Toda chamada ao webhook requer o header X-Auth-Token. Solicite o token ao time Digital AI.

Header
X-Auth-Token: <token-fornecido-pelo-time-digital-ai>

Payload de Entrada

JSON
{
  "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)

JSON
{
  "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

CampoTipoDescriçãoUso no vis-network
idintegerID sequencial único do nóIdentificador de referência
labelstringNome do aplicativo (ex: chrome, EXCEL)Texto exibido no nó
titlestringTooltip: minutos totais + sessõesTooltip no hover
valueintegerTotal de minutos de uso no períodoTamanho do nó (scaling)
topSitesarrayTop sites acessados nesse app: [["github.com", 120], ...]Detalhe no drawer
siteCategoriesarrayCategorias de sites: ["Dev", "Comunicação"]Filtro por categoria

Exemplo de nó

JSON
{
  "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

CampoTipoDescriçãoUso no vis-network
idintegerID sequencial da arestaIdentificador
fromintegerID do nó de origemPonto de partida da aresta
tointegerID do nó de destinoPonto de chegada da aresta
valueintegerFrequência de co-ocorrência dos appsEspessura da aresta (scaling)
titlestringTooltip: descrição textual da conexãoTooltip no hover

Exemplo de aresta

JSON
{
  "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.

1
Agrupar por sessão
Os registros do applicationreport são agrupados por employeeName + startDate. Cada grupo = uma janela de atividade (período monitorado). Apps no mesmo grupo foram usados simultaneamente.
2
Criar nós (apps únicos)
Cada app único vira um nó. O 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.
3
Criar arestas (co-ocorrência)
Para cada par de apps (A, B) que aparecem na mesma sessão, incrementa o peso da aresta A→B. O peso final = número de sessões onde A e B ocorreram juntos. Pares mais frequentes = arestas mais espessas.
4
Enriquecer com dados do sitereport
Os dados do 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).
5
Montar resposta final
Retorna graph (nodes + edges), stats, employees (de activityEmployeeID do sitereport) e groups (do ClientGroup).

Pseudocódigo JavaScript

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âmetroTipoValoresEfeito
DateInistring"2026-07-07"Início do período (obrigatório)
DateEndstring"2026-07-14"Fim do período (obrigatório)
ClientGroupIdstring"" = todos
"5850" = Produto
"5522" = Marketing
Filtra por departamento
SearchSubgroupsbooleantrue / falseInclui subgrupos do grupo selecionado

Grupos disponíveis para filtro

ClientGroupIdGrupoSubgrupos
""Todos os grupos
5850ProdutoData Science.
5522Marketing (MI)Estrategistas, Accounts, Ops, Liderança
5365RMAds Negócios (RI)Atendimento, Negócios, Lideranças
5898DesenvolvimentoDataRank, Tech
5172ComercialAccounts, Finance, People, Estratégia...
5774ExecsBI

8 Exemplo Completo de Chamada

cURL

bash
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)

JavaScript
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)

Python
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.

Python — script standalone
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']}")
Renderizar com vis-network: Passe result["graph"]["nodes"] e result["graph"]["edges"] diretamente para o new vis.Network(). Veja o exemplo de renderização na página do dashboard.