Guia de Integração: APIs do Kikwiflow para o Monitor de Instâncias
Este guia explica, endpoint por endpoint, como montar as três telas típicas de um monitor de instâncias
(visão geral por processo, detalhe de um processo, detalhe de uma instância) usando as APIs REST de
kikwi-management-rest já descritas nas páginas anteriores desta seção. É o complemento prático do
guia de busca avançada (que aprofunda só o /search).
Todas as chamadas abaixo pressupõem kikwi-management-rest com um QueryRepository no contexto (é o padrão
quando kikwi-runtime-persistence-mongodb-spring-boot-autoconfigure está no classpath — ver
Visão Geral). Ajuste o prefixo de path para o kikwiflow.rest.base-path
configurado na sua aplicação (default /kikwiflow/api/v1).
1. Tela "Visão geral": processo × quantidade de instâncias × incidentes
Objetivo: uma lista de processos, cada um com "quantas instâncias" e "quantos incidentes".
Passo 1 — listar as definições de processo
GET /process-definitions
Devolve todas as versões de todas as definições, ordenadas por version decrescente — não é uma lista
deduplicada por processo. Para montar "um card por processo", agrupe por key no cliente e fique só com a
primeira ocorrência de cada uma (já vem em primeiro por causa da ordenação):
const allVersions = await fetch('/process-definitions').then(r => r.json());
const latestByKey = new Map();
for (const def of allVersions) {
if (!latestByKey.has(def.key)) latestByKey.set(def.key, def); // 1ª ocorrência = versão mais alta
}
Alternativa, se você já sabe as keys que quer mostrar: GET /process-definitions/one-by-key/{key}, uma
chamada por chave, sempre a versão mais recente — sem precisar deduplicar nada.
Passo 2 — métricas macro por processo
Para cada definição (id) obtida no passo 1:
GET /pulse/process-definition/{processDefinitionId}/snapshot
Resposta (KKFProcessStats, campos relevantes para esta tela):
{
"id": "fc451f6f-a944-4dbb-b2ba-b5fb6433f40a",
"key": "intermediacao-veiculos",
"name": "Intermediação de Veículos",
"metrics": { "running": 42, "sla": 100.0, "fail": 3 }
}
metrics.running→ instâncias comstatus: ACTIVEagora (não é o total histórico).metrics.fail→ incidentes comstatus: OPENagora, para esse processo.metrics.sla→ hoje é sempre100.0, hardcoded (StatsService/getProcessMacroMetricsnão calcula SLA real ainda) — não use esse campo para nada além de exibir um placeholder.
Se o que você quer é o total histórico de instâncias (todas, não só as ativas — inclui COMPLETED e
CANCELLED), use em vez disso:
GET /process-instances/count?process-definition-id={processDefinitionId}
→ { "total": 1284 }
Não existe hoje um endpoint que devolva as duas métricas (running + fail) para todos os processos numa
única chamada — é uma requisição por processo (N+1), aceitável para a cardinalidade típica de definições de
processo (dezenas, não milhares). Se isso virar gargalo, é o item 16 do documento de observabilidade
(GET/POST /process-instances/count genérico) mais um "macro metrics em lote" — nenhum dos dois existe ainda.
Alternativa em tempo real (sem polling manual)
Troque o passo 2 por SSE, uma conexão por processo que você está exibindo na tela:
GET /pulse/process-definition/{processDefinitionId}/snapshot/stream
Reenvia o mesmo KKFProcessStats a cada kikwiflow.pulse.sse-endpoints.interval (default 5000ms) enquanto a
conexão estiver aberta — troque polling manual por só manter a conexão viva e atualizar a UI a cada evento.
2. Tela "Detalhe do processo": atividade × quantidade de instâncias × incidentes
Objetivo: o mesmo processo de antes, agora granular por atividade/nó do diagrama.
Mesma chamada do passo 2 acima:
GET /pulse/process-definition/{processDefinitionId}/snapshot
desta vez lendo flowNodes — um mapa nodeId → definição do nó (com métricas embutidas):
{
"flowNodes": {
"AGUARDANDO_CONTATO": {
"type": "EXTERNAL_TASK",
"id": "AGUARDANDO_CONTATO",
"name": "Aguardando contato do vendedor",
"metrics": { "running": 18, "sla": 100.0, "fail": 2 }
},
"AGUARDA_PAGAMENTO": {
"type": "EVENT_CATCHER",
"id": "AGUARDA_PAGAMENTO",
"catchType": "GROUP",
"matchPolicy": "ALL",
"metrics": { "running": 5, "sla": 100.0, "fail": 0 }
},
"GATEWAY_APROVACAO": {
"type": "EXCLUSIVE_GATEWAY",
"id": "GATEWAY_APROVACAO",
"metrics": null
}
}
}
Pontos de atenção:
metricsé preenchido paraEXECUTABLE_TASK,EXTERNAL_TASK,EVENT_CATCHER,CALL_ACTIVITY_COORDINATOReTIMER_TASK— os cinco tipos de nó que são materializados comoExecutableTask/ExternalTaske onde "quantas instâncias estão paradas aqui" faz sentido como conceito (StatsService.buildProcessSnapshot). Os dois últimos só passaram a recebermetricsa partir desta revisão — antes,CALL_ACTIVITY_COORDINATORaparecia sempre commetrics: nullmesmo quando havia dados reais (a tarefa coordenadora já é umaExecutableTasknormal na agregaçãogetMetricsByNodeForProcessDefinition; só não estava sendo repassada). Gateways e boundary events continuam sempre commetrics: null— não é bug, é intencional; não há "fila" nesses tipos de nó da mesma forma.flowNodes[nodeId].metrics.runningcontaExecutableTask/ExternalTaskativas naqueletaskDefinitionId— é o "quantas instâncias estão paradas nesta atividade agora".failconta as que estão com status de erro (ExecutableTaskStatus.ERROR).- O
typede cada entrada deflowNodesé o discriminador polimórfico (KKFFlowNodeDefinition) — use-o para saber que campos extras esperar (KKFExternalTaskDefinition,KKFEventCatcherDefinition,KKFCallActivityDefinition, etc., um por tipo de nó). - Todos os 15 tipos de nó do modelo têm representação aqui, incluindo
EVENT_CATCHEReCALL_ACTIVITY_COORDINATOR(KKFCallActivityDefinitionincluiiterationMode:SEQUENTIAL/PARALLEL) — ver §4 para o porquê disso ser uma garantia nova. - Mesma variante SSE do passo 2 acima serve aqui (
.../snapshot/stream) — é a mesma resposta, só que reenviada periodicamente; um único stream alimenta as telas 1 e 2 ao mesmo tempo se você já tiver os dados carregados.
3. Tela "Detalhe da instância": onde ela está parada
Objetivo: dado um processInstanceId, mostrar em qual(is) nó(s) ela está parada e, quando esse nó for um
EVENT_CATCHER em grupo ou um CALL_ACTIVITY_COORDINATOR (subprocesso), mostrar o progresso — não só "está
parado", mas "3 de 5 eventos recebidos" ou "2 de 3 subprocessos concluídos".
Passo 1 — snapshot completo da instância
GET /process-instances/{id}/snapshot
Resposta (ProcessInstanceSnapshot):
{
"instance": {
"id": "ae8fd836-...",
"businessKey": "100234",
"status": "ACTIVE",
"processDefinitionId": "fc451f6f-...",
"activeNodes": { "AGUARDA_PAGAMENTO": 1 },
"parentInstanceId": null,
"callerTaskId": null,
"callerBranchId": null
},
"executableTasks": [ ... ],
"externalTasks": [ ... ],
"incidents": [ ... ],
"eventCatcherWaitStatus": [
{
"taskDefinitionId": "AGUARDA_PAGAMENTO",
"externalTaskId": "parent-task-id",
"matchPolicy": "ALL",
"totalCorrelationKeys": 5,
"receivedCount": 3,
"pendingCorrelationKeys": ["pedido-104", "pedido-105"]
}
]
}
instance.activeNodes (Map<nodeId, count>) é o ponto de partida: diz em quais nós a instância está
parada agora, e quantos "tokens" ativos há em cada um (relevante para PARALLEL_GATEWAY/loops). Cruze cada
nodeId com o diagrama (flowNodes da tela 2) para saber o tipo do nó e decidir o que mostrar:
| Tipo do nó parado | O que mostrar | Onde vem o dado |
|---|---|---|
EXECUTABLE_TASK / EXTERNAL_TASK | nome da tarefa, assignee (ExternalTask.assignee), há quanto tempo está parada (createdAt), se está em retry/erro (ExecutableTask.status/error/retries) | filtre executableTasks/externalTasks do snapshot por taskDefinitionId == nodeId |
EVENT_CATCHER (GROUP) | "N de M eventos recebidos", quais chaves faltam | eventCatcherWaitStatus — novo campo, ver §3.1 |
CALL_ACTIVITY_COORDINATOR (subprocesso) | quantos filhos, quantos concluíram, status de cada um | POST /process-instances/search com parentInstanceId — novo filtro, ver §3.2 |
BOUNDARY_* (timer/catch event/error handler) | geralmente não aparece em activeNodes isoladamente — é metadado anexado ao nó pai; sua existência aparece como boundaryEvents dentro da ExecutableTask/ExternalTask do nó pai | mesmo lugar da linha do EXECUTABLE_TASK/EXTERNAL_TASK pai |
3.1 Progresso de um EVENT_CATCHER em grupo — eventCatcherWaitStatus (novo)
Antes, para saber "quantos eventos já chegaram" você precisaria filtrar externalTasks manualmente por
type: EVENT_CATCHER_PARENT/EVENT_CATCHER_CHILD e cruzar coordinatorTaskId/status na mão. Agora o
snapshot já entrega isso computado em eventCatcherWaitStatus, um item por EVENT_CATCHER ativo na instância
(GROUP ou STANDALONE):
| Campo | Significado |
|---|---|
taskDefinitionId | id do nó EVENT_CATCHER na definição — cruza com flowNodes da tela 2 |
externalTaskId | id da tarefa coordenadora (GROUP) ou da própria tarefa (STANDALONE) |
matchPolicy | ALL (só conclui com todas as chaves), ANY (primeira chave vence), ou null para STANDALONE |
totalCorrelationKeys | quantas chaves esse nó espera ao todo |
receivedCount | quantas já chegaram |
pendingCorrelationKeys | quais ainda faltam — útil para debug ("por que esse pedido não anda? falta o webhook de pedido-105") |
Renderização sugerida: "${receivedCount} de ${totalCorrelationKeys} eventos recebidos" + lista de
pendingCorrelationKeys como itens pendentes. Um EVENT_CATCHER STANDALONE sempre aparece com
totalCorrelationKeys: 1, receivedCount: 0 — ele só existe na lista enquanto está esperando; quando a chave
chega, a tarefa é removida (não fica um registro com receivedCount: 1).
Detalhe de correção para matchPolicy: ANY: receivedCount é calculado contando as tarefas-filha com
status CORRELATED (não o tamanho de uma lista de "pendentes" da tarefa-mãe) — isso importa porque em ANY a
primeira resposta já conclui o nó inteiro (não há "recebimento parcial"), então você não vai ver
receivedCount subir gradualmente num ANY como sobe num ALL; ele pula de 0 direto para o nó desaparecer
de activeNodes (a instância avançou).
3.2 Subprocessos: filhos e seus status — filtro parentInstanceId (novo)
Quando o nó parado é um CALL_ACTIVITY_COORDINATOR, o snapshot da instância pai não traz o status dos
filhos embutido (cada filho é uma ProcessInstance própria, com seu próprio ciclo de vida). Para listá-los:
POST /process-instances/search
{ "parentInstanceId": "ae8fd836-c6a8-4dd1-af8d-0e6ec3530657", "size": 100 }
{
"content": [
{ "id": "child-1", "businessKey": "100234#0", "status": "COMPLETED", "parentInstanceId": "ae8fd836-...", "callerTaskId": "coord-task-id", "callerBranchId": "coord-task-id:0" },
{ "id": "child-2", "businessKey": "100234#1", "status": "ACTIVE", "activeNodes": { "AGUARDA_ATIVACAO": 1 }, "parentInstanceId": "ae8fd836-...", "callerTaskId": "coord-task-id", "callerBranchId": "coord-task-id:1" }
],
"totalElements": 2
}
Monte "2 de 3 subprocessos concluídos" contando status: COMPLETED sobre totalElements (ou sobre
content.length se totalElements ainda não bateu, ex.: enquanto a coleção de filhos ainda está sendo
populada por um fan-out SEQUENTIAL). Para o filho ainda ativo, activeNodes do próprio item já diz onde
ele está parado — a mesma lógica desta seção 3 se aplica recursivamente (um filho pode, por sua vez, ser
pai de outro subprocesso).
Por que via /search e não um campo embutido no snapshot do pai: o número de filhos de um
CALL_ACTIVITY_COORDINATOR pode ser grande (é dirigido por uma collectionVariable do processo) e cada filho
tem seu próprio ciclo de vida independente — embuti-los cruaria no snapshot do pai duplicaria dados que o
/search já serve, paginado, com todos os filtros existentes (status, tenant, etc.) de graça. Se quiser saber
rapidamente "esta instância é filha de outra?" sem ir ao /search, os três campos já vêm na própria instância:
ProcessInstanceSummary/ProcessInstance têm parentInstanceId/callerTaskId/callerBranchId — null numa
instância raiz, preenchidos numa filha.
3.3 Incidentes da instância
GET /process-instances/{id}/incidents
Lista de Incident (não paginada — normalmente poucos por instância). Combine com o passo 1: se
activeNodes mostra a instância parada num nó mas o incidente aponta para esse mesmo taskDefinitionId, é
sinal de que ela está parada por erro, não por espera normal — distinga os dois casos na UI (ex.: ícone de
alerta vs. ícone de relógio).
4. O que mudou nesta rodada (contexto para quem já integrou antes)
Se seu monitor já consome essas APIs, os pontos novos que valem revisão:
eventCatcherWaitStatusé um campo novo emProcessInstanceSnapshot(GET /process-instances/{id}/snapshot) — aditivo, não quebra nada existente.parentInstanceId/callerTaskId/callerBranchIdsão campos novos emProcessInstanceSummary— aparecem agora emPOST /process-instances/searcheGET /process-instances/summary, além de já existirem emProcessInstance(GET /process-instances/{id}). Também aditivo.parentInstanceIdé um filtro novo emPOST /process-instances/search(§3.2 acima).iterationModeé um campo novo emKKFCallActivityDefinition(dentro deflowNodesna tela 2).- Todos os 15 tipos de nó (incluindo
EVENT_CATCHEReCALL_ACTIVITY_COORDINATOR) são garantidos por compilação a continuar aparecendo emflowNodes— antes, um tipo de nó novo no motor podia quebrar silenciosamenteGET /pulse/process-definition/{id}/snapshotem runtime (já aconteceu antes); agora isso vira erro de build do lado do servidor, não um 500 em produção. sizeem/process-instances/searchagora é limitado a 100 mesmo se você pedir mais — se seu monitor dependia de pedir páginas maiores, ajuste para paginar de verdade.orderByem/process-instances/searchagora só aceitaid,businessKey,status,processDefinitionId,startedAt,endedAt— um valor fora dessa lista agora devolve400em vez de ser ignorado silenciosamente ou quebrar a ordenação no servidor.CALL_ACTIVITY_COORDINATOReTIMER_TASKagora recebemmetricsemflowNodes(tela 2) — se seu monitor já tinha uma regra tipo "semetricsfornull, não desenha o número", ela vai começar a mostrar número de verdade para esses dois tipos a partir de agora (era semprenullantes, mesmo com dados reais).
Nada do que já funcionava foi removido ou renomeado — são só campos/filtros novos e validação mais estrita em dois parâmetros que já existiam.
5. Referência rápida
| Tela | Endpoint principal | Complementos |
|---|---|---|
| 1. Visão geral | GET /process-definitions + GET /pulse/process-definition/{id}/snapshot (×N) | GET /process-instances/count?process-definition-id= para total histórico |
| 2. Detalhe do processo | GET /pulse/process-definition/{id}/snapshot (campo flowNodes) | versão SSE: .../snapshot/stream |
| 3. Detalhe da instância | GET /process-instances/{id}/snapshot | POST /process-instances/search com parentInstanceId para filhos; GET /process-instances/{id}/incidents |