Skip to content

API REST ​

Base URL ​

http://localhost:8080/api

Em produção, substitua pelo domínio da sua instância.

Documentação interativa (Swagger) ​

Acesse a documentação OpenAPI 3.1 gerada automaticamente:

http://localhost:8080/swagger-ui.html

O documento por trás dela é servido em /v3/api-docs, pela própria aplicação — é essa a fonte de verdade da API, e é ela que o nginx expõe em produção.

Arquivo estático openapi.json ​

Se você precisar do documento como arquivo (para importar no Postman, versionar um contrato, gerar cliente), peça o profile openapi:

bash
cd backend
mvn verify -Popenapi

O resultado sai em backend/core/target/openapi.json.

Por que é um profile, e não parte do mvn verify

O gerador lê /v3/api-docs de uma aplicação rodando. O profile sobe a aplicação antes e a derruba depois, então precisa de um banco alcançável — as mesmas variáveis do roteiro de execução local.

Fora do profile, mvn verify não gera o arquivo e não tenta subir nada, para que o build continue verde numa máquina que só quer rodar os testes.

Autenticação ​

JWT (Usuário) ​

http
POST /auth/login
Content-Type: application/json

{
  "email": "usuario@empresa.com",
  "password": "suaSenha"
}

Resposta:

json
{
  "accessToken": "eyJ...",
  "refreshToken": "eyJ...",
  "user": { "id": "...", "email": "...", "firstName": "..." },
  "roles": ["USER"]
}

Use o accessToken no header de todas as requisições:

http
Authorization: Bearer eyJ...

Refresh de token ​

Quando o accessToken expirar (erro 401), use o refreshToken para obter um novo par:

http
POST /auth/refresh
Content-Type: application/json

{
  "refreshToken": "eyJ..."
}

API Key (Sistema externo) ​

http
X-API-Key: sua-api-key

Cada API Key tem um limite de requisições por minuto configurável (padrão 100, 0 = ilimitado), definido na criação da chave ou depois em Integrações e automação → API Keys. Ao ultrapassar o limite a API responde 429 Too Many Requests:

json
{ "error": "Too Many Requests", "message": "Rate limit exceeded for this API Key." }

Escopos ​

Existem exatamente dois escopos, definidos em ia.flow.model.ApiKeyScope:

EscopoAutoridadeMétodos que abre
readSCOPE_readGET, HEAD, OPTIONS
writeSCOPE_writePOST, PUT, PATCH, DELETE, e qualquer outro

A regra é uma só e mora num lugar: ApiKeyScope.requiredFor(httpMethod) decide o escopo que a requisição exige, e ApiKeyAuthFilter consulta essa decisão antes de deixar a cadeia seguir. Nenhuma rota nomeia escopo e nenhum controller repete a lista — um endpoint novo sob /api/a/** já nasce coberto.

Os dois escopos não se implicam: uma chave só de write é recusada num GET. A chave que precisa dos dois lados carrega os dois.

Uma requisição sem o escopo necessário é interrompida no filtro — nunca chega ao controller — e responde 403 Forbidden:

json
{
  "error": "Forbidden",
  "message": "Esta chave de API tem os escopos [read] e o método POST exige o escopo 'write'. Marque esse escopo na chave em Integrações e automação → API Keys, ou use uma chave que já o tenha."
}

A leitura da coluna scopes falha fechada em toda ambiguidade: escopo desconhecido é ignorado e nunca promove a chave; lista vazia, null ou JSON malformado não concedem nada, e a chave é recusada em todo método. Antes de 2026-09-03 a lista vazia significava "sem restrição" — hoje significa "sem alcance".

Na criação (POST /api/a/api-keys) a lista de escopos é obrigatória e não pode ser vazia, e cada entrada precisa ser um escopo conhecido: um valor inventado é recusado com 400 e a mensagem lista os que existem. O PATCH aceita scopes opcionalmente — ausente, a chave mantém os que tem; presente, substitui.

A migração V0103__api_key_scopes.sql deu ["read","write"] a toda chave que já existia, inclusive as vazias e as com escopo inventado, pela razão registrada na própria migração (issue #57): o que já roda em produção mantém o alcance que tinha, e o que é criado a partir daqui nasce mínimo.

Servidor MCP (/api/a/mcp) ​

O Flow.IA expõe um servidor MCP (Model Context Protocol) para que um agente de IA externo — Claude Code, um script de provisionamento, um assistente de IDE — configure o processo no tenant: a definição em si, formulários, bases de conhecimento, coleções de CMS, agentes de IA e a documentação do processo.

Autenticação é HTTP Basic, com um usuário da plataforma:

json
{
  "mcpServers": {
    "flowi": {
      "url": "https://<host>/api/a/mcp",
      "headers": {
        "Authorization": "Basic <base64 de email:senha>",
        "X-Tenant-ID": "<slug do tenant>"
      }
    }
  }
}

Esse formato vale para os clientes que aceitam cabeçalho fixo: VS Code e Claude Code (CLI). Claude Desktop, claude.ai e ChatGPT adicionam servidor remoto por URL e negociam OAuth — não têm campo para Basic nem para cabeçalho próprio, então não conseguem falar com este endpoint diretamente. O 401 do endpoint responde WWW-Authenticate: Basic realm="flowi-mcp", que é como um cliente descobre o esquema; OAuth 2.1 ainda não está implementado.

Basic em vez de JWT porque a configuração do cliente MCP é estática e o token expira em 1 hora. Tentativas malsucedidas contam no mesmo limitador do endpoint de login.

O X-Tenant-ID diz qual tenant o cliente quer; quem decide se ele pode é a verificação de associação, feita na autenticação. Um slug em que o usuário não é membro não autentica — o cabeçalho nomeia o candidato, nunca seleciona o tenant. Sem o cabeçalho, o cliente opera sem tenant, que é o caso de um usuário SUPER_ADMIN.

Use um usuário dedicado

Crie um usuário só para isso (ex.: mcp-bot@...) e dê ADMIN apenas nos tenants que ele deve alcançar — a associação do usuário ao tenant é o que concede acesso. Não use a conta de uma pessoa: clientes MCP guardam credenciais em texto plano, e uma senha pessoal vazada dá login na interface, troca de senha e acesso a todos os tenants daquela pessoa. Um usuário dedicado se desativa sem afetar ninguém.

Ferramentas disponíveis:

FerramentaO que faz
flowi_form_listLista os formulários de um processo
flowi_form_getRetorna um formulário com o schema completo
flowi_form_field_typesLista os tipos de campo aceitos e como montar o schema
flowi_form_createCria um formulário (não publica)
flowi_form_updateSubstitui o schema, criando nova versão (não publica)
flowi_form_versionsHistórico de versões, com autor — é o caminho de desfazer
flowi_form_publishPublica uma versão, que é o que a faz chegar aos usuários
flowi_process_listLista as definições do tenant, com a versão publicada
flowi_process_versionsHistórico de versões de uma definição
flowi_process_get_xmlRetorna o XML BPMN/CMMN de uma versão
flowi_process_validateConfere um BPMN sem implantar: órfãos, becos sem saída, gateway sem fluxo padrão, diagrama ausente
flowi_process_deployDeposita uma nova versão da definição (não publica)
flowi_ai_model_listCatálogo de modelos de IA, com o id que flowi_agent_create exige e uma faixa de custo relativa (costTier: LOW, MEDIUM, HIGH), nunca o preço
flowi_agent_list, flowi_agent_createAgentes de IA do processo
flowi_agent_attach_to_stepVincula um agente a uma etapa, com as tools daquela etapa
flowi_agent_suggest_knowledgeIndica, entre as bases existentes, as relevantes para uma etapa
flowi_agent_tool_catalogFerramentas que uma etapa de agente pode declarar, com o que cada uma faz e do que precisa
flowi_connector_listO que uma etapa pode usar além de um agente: tabelas de decisão, delegates Java e caixas de e-mail que iniciam processos. Robôs aparecem marcados como agendados, não como etapa
flowi_kb_list, flowi_kb_create, flowi_kb_add_text, flowi_kb_link_processBases de conhecimento (RAG)
flowi_cms_field_typesTipos de campo e níveis de classificação de uma coleção
flowi_cms_collection_list, flowi_cms_collection_create, flowi_cms_records_load, flowi_cms_records_queryColeções de CMS
flowi_process_doc_getLê a descrição do processo
flowi_process_doc_proposePropõe uma descrição nova — devolve o texto, não grava
flowi_run_startable_listProcessos que dá para iniciar, cada um com sua política de MCP
flowi_run_startInicia uma instância (só se a política permitir)
flowi_run_my_tasksA caixa de tarefas do chamador, como ele a vê na tela
flowi_run_task_getA tarefa e o schema do formulário dela, numa resposta só
flowi_run_task_claimAssume uma tarefa oferecida a um papel do chamador
flowi_run_task_previewMostra o que a conclusão gravaria e para onde a instância iria; não grava nada
flowi_run_task_completeConclui uma tarefa, exigindo o token do preview
flowi_run_attach_textAnexa um documento de texto a uma instância
flowi_run_instance_getEstado de uma instância
flowi_run_why_failedFalhas de agente de IA de uma instância, por business key

Todas as ferramentas exigem papel de administrador do tenant (ou SUPER_ADMIN). Um usuário comum autentica e não alcança ferramenta nenhuma — a verificação está na classe de cada grupo de ferramentas, não no controller, porque o caminho MCP não passa por controller nenhum.

Cada ferramenta se anuncia ao cliente como leitura ou escrita (readOnlyHint, destructiveHint), para que uma listagem não peça a mesma aprovação que uma publicação. Escrita passa pela mesma validação de bean que a rota REST equivalente: um slug com espaço é recusado pela ferramenta como seria pela tela.

Há limites por chamada: 5000 registros em flowi_cms_records_load e 2 milhões de caracteres em flowi_kb_add_text. Divida cargas maiores.

Não há ferramenta de exclusão, nem de fonte de dados. Excluir não tem desfazer, e fonte de dados guarda URL e credencial de saída. Publicar é sempre uma chamada explícita — update_form nunca publica sozinho, e tarefas já abertas mantêm a versão que travaram na criação.

Depositar uma versão não é publicá-la

flowi_process_deploy cria uma versão nova e não existe ferramenta que publique uma definição. Enquanto houver uma versão publicada, a versão depositada não inicia instância nenhuma — ela fica lá para ser conferida e publicada por uma pessoa. A exceção é uma definição que nunca foi publicada: aí o início de instância cai na última versão, e a resposta da ferramenta avisa disso.

Antes de implantar um BPMN escrito por um modelo, chame flowi_process_validate: implantar cria uma versão e versão não se retira. A validação também avisa quando o XML veio sem a seção de diagrama — o processo executa, mas abre em branco no modelador.

Executar o processo é fechado por padrão

As ferramentas flowi_run_* iniciam instâncias e concluem tarefas. Concluir faz o processo andar e nenhuma ferramenta desfaz, então cada processo decide por si, no campo Assistentes de IA (MCP) da aba Configuração:

ValorIniciarConcluir tarefa
Nada (padrão)recusadorecusado
Iniciar e lerpermitidorecusado
Iniciar e concluirpermitidopermitido, sob as regras abaixo

O padrão é fechado, e nenhuma ferramenta muda isso — quem muda é a pessoa dona do processo, na tela.

Concluir exige três coisas ao mesmo tempo: a política do processo em Iniciar e concluir, a tarefa já assumida pelo próprio chamador, e um confirmationToken vindo de flowi_run_task_preview com exatamente aquelas variáveis. O token vale dez minutos e deixa de valer se qualquer campo mudar depois do preview — é assim que a pessoa confirma o payload final, e não um rascunho anterior dele. Nenhuma ferramenta assume tarefa a caminho de concluir, e conclui-se uma tarefa por chamada. A conclusão registra no histórico da tarefa que veio por MCP, para que uma leitura seis meses depois distinga o que a pessoa digitou do que um assistente enviou em nome dela.

O preview lê o diagrama e diz para quais atividades a tarefa pode ir, com a condição de cada caminho quando existe. Ele não avalia as condições — isso seria executar o processo —, então um caminho condicional aparece como condicional em vez de resolvido.

Para desligar o servidor, defina MCP_SERVER_ENABLED=false. Atrás de proxy, o endpoint precisa de proxy_buffering off e leitura longa: o canal de escuta é text/event-stream, e o timeout de 60s das demais rotas de API cortaria a sessão a cada minuto.

O tenant vem do token, não de um header ​

O tenant em que a sessão opera é um claim assinado dentro do access token. Não há header de tenant a enviar: X-Tenant-ID nunca seleciona o tenant. Enviá-lo com o mesmo slug que o token já carrega é aceito e ignorado; enviá-lo com outro slug, ou sem tenant no token, responde 400 com a mensagem dizendo para trocar de contexto em /api/auth/select-tenant. A única credencial que não é token — o HTTP Basic do endpoint MCP — nomeia seu tenant nesse header, e lá a verificação de associação acontece na autenticação: um slug do qual o usuário não é membro não autentica.

Depois do login, escolha o tenant:

http
POST /api/auth/select-tenant
Content-Type: application/json

{ "tenantSlug": "slug-do-tenant" }

A resposta tem o mesmo formato do login, com um accessToken novo carregando o tenant e apenas os papéis daquele tenant. Trocar de tenant é repetir esta chamada e substituir o token guardado — não existe troca em memória.

Selecionar um tenant do qual o usuário não é membro responde 403.

Por que o tenant vive no token

Um header é escolhido pelo cliente, e conferi-lo contra a lista de tenants ativos não impede que um usuário associado a um tenant peça dados de outro trocando o valor. Com o claim assinado, o tenant deixa de ser palpite do cliente: alterá-lo invalida o token inteiro.

Principais endpoints ​

Os endpoints seguem o padrão de prefixo abaixo. Todos os caminhos são relativos à base /api.

PrefixoEscopoAuth
/authAutenticação / sessãoSem auth em /login, /refresh, /password/forgot, /password/reset e /sso/**; o resto exige token
/admin/*Operações de plataformaJWT (SUPER_ADMIN apenas)
/a/*Operações de tenantJWT com tenant selecionado

Autenticação ​

MétodoEndpointDescrição
POST/auth/loginLogin com email/senha
POST/auth/refreshRenovar token
POST/auth/select-tenantTrocar o tenant da sessão (devolve um token novo)
POST/auth/select-platformSó Super Admin: sair da empresa escolhida e voltar a um token sem tenant (devolve um token novo); outro papel recebe 403
GET/auth/meDados do usuário logado
GET/auth/me/tenantsTenants do usuário
GET/auth/me/sessionDados da sessão corrente
POST/auth/password/forgotPedir link de redefinição — responde 202 sempre, cadastrado ou não, e tem limite de tentativas por IP
POST/auth/password/resetConcluir a redefinição com o token do e-mail
POST/auth/logoutInvalidar sessão

Resposta do login:

json
{
  "accessToken": "eyJ...",
  "refreshToken": "eyJ...",
  "user": { "id": "...", "email": "...", "firstName": "..." },
  "roles": ["USER"],
  "tenants": [
    { "id": "uuid", "slug": "empresa-abc", "name": "Empresa ABC" }
  ]
}

O campo tenants lista os tenants aos quais o usuário pertence. Use o slug em POST /api/auth/select-tenant para obter o token daquele tenant.

O que POST /auth/logout encerra. Os dois tokens de um mesmo login pertencem à mesma sessão e carregam o mesmo identificador (sid). Sair apaga a sessão inteira — o access token e o refresh token —, então o refresh token daquele login deixa de renovar. Outras sessões da mesma pessoa, em outro aparelho ou outro navegador, continuam ativas: sair de uma aba não é sair de todos os aparelhos.

O access token em si continua válido até expirar (uma hora): ele não é conferido no banco a cada requisição, e essa é uma decisão de custo registrada em specs/core/tenant-in-token.md. O que o logout fecha é a janela: sem o refresh token daquela sessão, ela não é reaberta.

Processos e instâncias ​

MétodoEndpointDescrição
GET/a/definitionsListar definições do tenant — resposta é a lista inteira, sem paginação
GET/a/definitions/{key}Detalhe + startFormKey — do StartEvent no BPMN, do flowable:formKey do casePlanModel no CMMN
GET/a/definitions/{key}/variablesVariáveis de configuração do processo, não de instância — exige Admin ou Modelador do tenant. GET /a/definitions/{key}/variables/public devolve as não secretas a qualquer autenticado
GET/a/definitions/{key}/monitorCarga parada por etapa, de uma versão do processo — ?version=
GET/a/definitions/{key}/monitor/{activityId}O que está parado naquela etapa — ?version=
GET/a/definitions/{key}/sla-metricsMétricas de SLA do processo nos últimos dias — ?days= (padrão 30)
PUT/a/definitions/{key}/suspendSuspender a definição — nenhuma instância nova começa. Só BPMN; um caso responde 400, porque o motor de CMMN não suspende definição
PUT/a/definitions/{key}/activateReativar a definição suspensa — Só BPMN, pelo mesmo motivo do suspend
POST/a/instancesIniciar instância (BPMN ou CMMN)
GET/a/instancesListar instâncias ativas, paginado — ?page=&size=&type=
GET/a/instances/searchBuscar instâncias por variável de processo, paginado — ?varName=&varValue=&page=&size=
GET/a/instances/{id}Detalhe + variáveis
DELETE/a/instances/{id}Cancelar instância
PATCH/a/instances/{id}/suspendSuspender instância — só BPMN; um caso responde 400
PATCH/a/instances/{id}/activateReativar instância — só BPMN; um caso responde 400
GET/a/instances/{id}/diagramSVG/XML do diagrama com atividades marcadas
GET/a/instances/failed-jobsJobs com falha do tenant
POST/a/instances/failed-jobs/{jobId}/retryRetry de job
POST/a/instances/failed-jobs/{jobId}/deadletterMove o job para a fila de dead-letter, parando as tentativas
GET/a/ai-task-failuresEtapas de agente de IA que falharam, paginado (size 20 por padrão)
POST/a/ai-task-failures/{id}/retryExecuta de novo a etapa (caso) ou reprocessa o job morto dela (processo)
POST/a/ai-task-failures/{id}/dismissDispensa a falha sem executar nada; idempotente

Papéis desta tabela, já que a coluna não os traz: iniciar, listar, detalhar e ver o diagrama de uma instância bastam estar autenticado. Cancelar, suspender e reativar uma instância, e as rotas de failed-jobs, de monitor e de sla-metrics, exigem Gerente ou Admin do tenant. Suspender e reativar a definição exigem Admin ou Modelador do tenant. Abaixo disso a resposta é 403.

Quando um agente de IA falha durante POST /a/instances, a instância não chega a existir: o processo roda de forma síncrona dentro da transação da requisição, então o rollback leva junto a instância, as tarefas e o vínculo do anexo. GET /a/ai-task-failures é onde a tentativa fica registrada — processo, business key, atividade, tipo da falha e motivo, da mais recente para a mais antiga. Aceita ?businessKey= para filtrar por documento, ?processInstanceId= para uma instância e ?open=true para só as falhas em aberto. Gerente e Admin veem todas as falhas do tenant; os demais só as de instâncias que enxergam. retry e dismiss exigem Gerente ou superior; retry responde 409 quando a falha já foi resolvida ou a etapa não pode ser refeita. Cada linha traz open, resolution (RETRIED, DISMISSED, SUPERSEDED, INSTANCE_ENDED), resolvedBy e resolvedAt. A lista de instâncias traz aiFailureOpen em cada linha e o diagrama traz aiFailures[] ao lado de failedJobs.

Numa etapa de IA dentro de um caso a leitura muda: o caso existe e continua aberto, só a etapa falhou, e ela pode ser executada de novo. O campo engine diz qual dos dois é o caso — BPMN ou CMMN — e nas linhas CMMN o campo processKey carrega a chave do caso.

attemptCount conta as retentativas. É uma linha por etapa, não uma por tentativa: quando o motor repete a mesma etapa, a linha existente é atualizada e o contador sobe. Nove tentativas da mesma etapa aparecem como uma linha com attemptCount: 9, o que é uma informação; nove linhas iguais seriam ruído.

Quem enviou o documento recebe uma notificação AI_TASK_FAILED na hora, pelo mesmo canal das demais notificações — uma vez, na primeira falha. As retentativas não notificam de novo: repetir o mesmo aviso nove vezes não acrescenta nada e esconde o primeiro. Início por API key não tem usuário por trás, então a linha é gravada e nenhuma notificação é enviada.

Não há retry nem dead-letter pela API: o registro é um fato, não um item de trabalho. Reprocessar significa enviar o documento de novo.

Acompanhamento por etapa ​

GET /a/definitions/{key}/monitor devolve o diagrama de uma versão e quanto trabalho está parado em cada etapa dela. Exige papel de Gerente ou superior no tenant.

json
{
  "definitionKey": "fiscal-nfe",
  "definitionName": "Auditoria fiscal",
  "version": 3,
  "bpmnXml": "<definitions …>",
  "runningInstances": 12,
  "runningInstancesOtherVersions": 7,
  "versions": [ { "version": 3, "runningInstances": 12 }, { "version": 2, "runningInstances": 7 } ],
  "activities": [
    { "activityId": "parecer", "activityName": "Parecer final", "activityType": "UserTask",
      "count": 11, "byReason": { "USER_TASK": 11 } }
  ]
}

version é opcional. Sem ele, a resposta traz a versão com mais instâncias correndo (empate: a maior) e, se nenhuma instância existir, a última publicada. Um diagrama pertence a uma versão e as instâncias não: contar instâncias de todas as versões sobre um só desenho produz número errado, então runningInstancesOtherVersions diz quanto ficou de fora em vez de omitir.

Etapa sem nada parado não aparece em activities — o diagrama já desenha todas.

byReason classifica o que está ali:

MotivoSignificado
USER_TASKtarefa aguardando uma pessoa
JOB_FAILEDtentativas esgotadas, na fila de dead-letter
JOB_RETRYINGfalhou e ainda tem tentativa
TIMERaguardando uma data agendada
EXECUTINGexecução na etapa, sem job e sem tarefa

GET /a/definitions/{key}/monitor/{activityId} lista os itens daquela etapa — instanceId, businessKey, startTime, reason e, conforme o motivo, taskId, assignee, dueDate ou errorMessage. Passe o mesmo version que o resumo devolveu, senão o detalhe pode descrever um diagrama diferente do que está na tela.

Histórico de instâncias ​

MétodoEndpointDescrição
GET/a/history/instancesInstâncias concluídas, paginado — ?tenantId=&type=&page=&size=
GET/a/history/instances/{id}Detalhe da instância concluída
GET/a/history/instances/{id}/activitiesAtividades da instância

A listagem e a busca por variável respondem com o mesmo envelope de página de /a/instances (content, number, size, totalElements, totalPages). size vale 20 por padrão e é limitado a 100 — pedir mais devolve 100, não um erro. Duas tabelas que só crescem estão por trás delas: ACT_HI_PROCINST e ACT_CMMN_HI_CASE_INST. Sem página, um tenant com dois anos de operação recebia dezenas de milhares de linhas numa resposta só.

Tarefas ​

MétodoEndpointDescrição
GET/a/tasksListar tarefas do usuário (?page=&size=&mine=)
GET/a/tasks/completedListar tarefas já concluídas (histórico) (?page=&size=)
GET/a/tasks/{id}Detalhe + formulário
POST/a/tasks/{id}/claimAssumir tarefa
POST/a/tasks/{id}/completeConcluir tarefa
POST/a/tasks/{id}/unclaimDevolver tarefa ao grupo
POST/a/tasks/{id}/delegateDelegar
GET/a/tasks/{id}/variablesLer as variáveis visíveis da tarefa
POST/a/tasks/{id}/variablesGravar variáveis sem concluir a tarefa
POST/a/tasks/{id}/ai-suggestSugestão de preenchimento para os campos ainda vazios. O corpo {"draft": {"campo": "valor"}} leva o formulário como está na tela — sem ele o servidor só enxerga as variáveis já salvas. Corpo ausente ou draft vazio é válido. Nada é gravado: as sugestões voltam para o formulário, e cada uma traz conflictsWithCurrent (true quando o campo já tem outro valor, e aí o cliente nunca aplica sozinho)
GET/a/tasks/{id}/commentsListar comentários
POST/a/tasks/{id}/commentsAdicionar comentário

Quem pode agir e quem pode ler uma tarefa

Reivindicar, concluir, gravar rascunho, devolver, delegar e comentar exigem ser o responsável, o dono (delegar) ou candidato da tarefa; Gerente, Admin e Super Admin agem em qualquer uma. Ler (GET da tarefa, das variáveis, dos comentários, da proveniência, e ai-suggest) pede a mesma relação ou ter iniciado a instância. Sem relação nenhuma a resposta é 404; com relação de leitura mas sem direito de agir, 403. Uma chave de API recebe 403 com mensagem explícita em todas essas rotas: são atos de uma pessoa, então use o token de um usuário. Variáveis reservadas (aiDecision, escalateToHuman, ai_*, _*, tenantId...) enviadas em qualquer corpo respondem 400.

As respostas de listagem e detalhe de tarefas incluem formVersionId para buscar a versão exata do formulário via GET /a/definitions/{processKey}/forms/{key}/versions/by-id/{versionId}.

POST /a/tasks/{id}/complete valida o formulário no servidor

Se a tarefa tem formKey e o formulário existe na plataforma, os campos obrigatórios são conferidos antes de a tarefa ser concluída. Faltando algum, a resposta é 400 com a mensagem Campos obrigatórios não preenchidos: <campos> e nada é gravado — nem as variáveis, nem o avanço do processo. A conferência usa a mesma versão do formulário travada na criação da tarefa (a de formVersionId), e respeita as regras condicionais: um campo escondido por regra não é cobrado.

Um cliente de API que só enviava o que a tela enviava não muda. Um que enviava menos passa a receber 400 — busque o schema por formVersionId antes de montar o corpo.

Não é validado: POST /a/instances (variáveis do formulário de início) e as validações de formato, tamanho e faixa de cada campo, que continuam só no navegador.

GET /a/tasks e GET /a/tasks/completed devolvem uma janela de página, não a fila inteira. Nas duas, page começa em zero e size vale 100 por padrão, com teto de 200 — pedir mais que isso devolve 200. A resposta continua sendo um array simples, com os mesmos campos; só o número de linhas por chamada é que tem limite. Quem precisa de mais que a primeira página pede ?page=1, e assim por diante.

As duas listas também seguem a mesma regra de visibilidade: Gerente, Administrador e Super Admin veem as tarefas de todo o tenant, abertas e concluídas; os demais veem as suas, aquelas em que são candidatos e as oferecidas a um grupo dos seus papéis de processo.

GET /a/tasks?mine=true estreita a lista às tarefas atribuídas ao chamador, ignorando grupo e papel de processo — inclusive para quem vê o tenant inteiro. O padrão é mine=false, que mantém a lista completa que a visibilidade do chamador permite. Não existe mine em /a/tasks/completed.

Formulários ​

Um formulário sempre pertence a um processo, e o caminho reflete isso: a chave do processo vem antes da chave do formulário. Não existe rota /a/forms/{key} — chamá-la devolve 404.

MétodoEndpointDescrição
GET/a/definitions/{processKey}/formsListar os formulários do processo
POST/a/definitions/{processKey}/formsCriar formulário
GET/a/definitions/{processKey}/forms/{key}Detalhe do formulário
PUT/a/definitions/{processKey}/forms/{key}Salvar formulário (cria nova versão)
DELETE/a/definitions/{processKey}/forms/{key}Remover formulário
GET/a/definitions/{processKey}/forms/{key}/versionsListar versões (traz createdBy, o UUID de quem salvou)
GET/a/definitions/{processKey}/forms/{key}/versions/{num}Versão por número
GET/a/definitions/{processKey}/forms/{key}/versions/by-id/{versionId}Versão por UUID
POST/a/definitions/{processKey}/forms/{key}/publishPublicar uma versão

As fontes de dados são a exceção: elas não pertencem a um processo e ficam direto sob /a/forms.

MétodoEndpointDescrição
GET/a/forms/datasourcesListar fontes de dados
POST/a/forms/datasourcesCriar fonte de dados
GET/a/forms/datasources/{key}Detalhe da fonte de dados
PUT/a/forms/datasources/{key}Atualizar fonte de dados
DELETE/a/forms/datasources/{key}Remover fonte de dados
POST/a/forms/datasources/{key}/executeExecutar fonte de dados

Validação do schema em POST e PUT

O schema enviado precisa ser JSON válido e, quando trouxer um array fields, todo campo tem de declarar um type reconhecido. Um tipo inexistente é rejeitado com 400 e a mensagem nomeia o culpado e lista os tipos aceitos:

json
{ "message": "Tipo de campo desconhecido: 'markdown'. Tipos aceitos: text, textarea, ..." }

Sem essa checagem, um type inventado seria gravado e só quebraria na hora de renderizar. Quem monta o formulário pela tela nunca esbarra nisso, porque a paleta só oferece tipos reais; quem escreve o JSON direto pela API, sim.

Schemas no formato Form.io (com components em vez de fields) são aceitos sem essa validação.

Decision Tables ​

MétodoEndpointDescrição
GET/a/decisionsListar decisions do tenant
POST/a/decisionsDeploy de arquivo .dmn
POST/a/decisions/from-template/{key}Clonar de template global
GET/a/decisions/{key}Detalhes
PUT/a/decisions/{key}Atualizar regras (nova versão)
POST/a/decisions/{key}/evaluateTestar com inputs
DELETE/a/decisions/{key}Remover

Todas as rotas acima exigem Admin ou Modelador do tenant, ou Super Admin, com uma exceção: POST /a/decisions/{key}/evaluate aceita também Gerente e Usuário do tenant — ou seja, Admin, Modelador, Gerente, Usuário ou Super Admin —, porque é o que um formulário chama para testar a regra.

CMS ​

Coleções de conteúdo estruturado e seus registros. Ler basta estar autenticado no tenant, e a resposta já vem cortada pelo nível de sigilo da coleção: o que está acima da sua liberação não aparece. Escrever exige Gerente do tenant ou superior; uma chave de API alcança a escrita com o escopo write.

MétodoEndpointPermissãoDescrição
GET/a/cms/collectionsAutenticadoListar as coleções visíveis para o chamador
POST/a/cms/collectionsGerenteCriar coleção
GET/a/cms/collections/{id}AutenticadoDetalhe da coleção
PUT/a/cms/collections/{id}GerenteAtualizar coleção
DELETE/a/cms/collections/{id}GerenteRemover coleção
GET/a/cms/collections/{id}/recordsAutenticadoListar registros, paginado (size 20 por padrão)
POST/a/cms/collections/{id}/recordsGerenteCriar um registro (201)
POST/a/cms/collections/{id}/records/bulkGerenteCarga em lote
PUT/a/cms/collections/{id}/records/{recordId}GerenteAtualizar registro
DELETE/a/cms/collections/{id}/records/{recordId}GerenteRemover registro
DELETE/a/cms/collections/{id}/recordsGerenteEsvaziar a coleção

A carga em lote recebe { "records": [...], "keyField": "codigo" } e responde { "inserted": n, "updated": n, "total": n }. São no máximo 20 000 registros por requisição — uma lista maior é recusada com 400 —, e keyField tem no máximo 100 caracteres. Com keyField, a carga é idempotente: um registro cujo valor daquele campo já existe é atualizado em vez de duplicado. Sem keyField, tudo é inserido, então uma recarga limpa passa antes por DELETE /a/cms/collections/{id}/records.

A exclusão de registro e de coleção é definitiva: não há lixeira nem desfazer.

Bases de conhecimento (RAG) ​

Os documentos que alimentam a resposta do agente. Todas as rotas abaixo exigem Admin, Modelador ou Gerente do tenant (ou Super Admin) — leitura inclusive, porque a listagem revela o que a IA consulta. Quem chama sem esse papel recebe 403. Criar ou alterar uma base marcada como global continua sendo exclusivo do Super Admin, e a recusa vem do próprio serviço.

MétodoEndpointPermissãoDescrição
GET/a/knowledge-basesAdmin, Modelador ou GerenteListar as bases do tenant e as globais
POST/a/knowledge-basesAdmin, Modelador ou GerenteCriar base (201); isGlobal: true exige Super Admin
GET/a/knowledge-bases/{id}Admin, Modelador ou GerenteDetalhe da base, com a contagem de documentos, ftsLanguage, retrievableChunks, keywordIndexedChunks e keywordIndexStatus (READY/REBUILDING)
PATCH/a/knowledge-bases/{id}/search-settingsAdmin, Modelador ou Gerente{ftsLanguage: SIMPLE|PORTUGUESE|ENGLISH|SPANISH} — idioma da busca por palavra-chave; 202 e recálculo do índice em segundo plano, sem embedding. Base global só pelo Super Admin
DELETE/a/knowledge-bases/{id}Admin, Modelador ou GerenteRemover base — recusa enquanto houver processo vinculado
GET/a/knowledge-bases/{id}/documentsAdmin, Modelador ou GerenteListar documentos, paginado (size 20 por padrão)
POST/a/knowledge-bases/{id}/documentsAdmin, Modelador ou GerenteSubir PDF, TXT ou MD (202 — a indexação é assíncrona)
DELETE/a/knowledge-bases/{id}/documents/{docId}Admin, Modelador ou GerenteRemover documento e seus vetores
GET/a/knowledge-bases/process/{definitionKey}Admin, Modelador ou GerenteBases vinculadas a um processo
POST/a/knowledge-bases/process/{definitionKey}/link/{id}Admin, Modelador ou GerenteVincular base ao processo (201)
DELETE/a/knowledge-bases/process/{definitionKey}/unlink/{id}Admin, Modelador ou GerenteDesvincular
GET/a/knowledge-bases/{id}/reference-questionsAdmin, Modelador ou GerentePerguntas de referência da base; o nome de um documento acima do seu nível vem escondido
POST/a/knowledge-bases/{id}/reference-questionsAdmin, Modelador ou GerenteCriar pergunta (201): {question, targets: [{documentId, page?, snippet?}]}, de 1 a 5 alvos, até 100 perguntas por base; documento de fora da base 400, acima do seu nível 403
PUT, DELETE/a/knowledge-bases/{id}/reference-questions/{questionId}Admin, Modelador ou GerenteEditar / apagar (204)
POST/a/knowledge-bases/{id}/retrieval-evaluations/estimateAdmin, Modelador ou Gerente{configurations: [{mode, topK, minSimilarity?}], questionIds?} — de 1 a 6 configurações distintas; perguntas, tokens aproximados e cota
POST/a/knowledge-bases/{id}/retrieval-evaluationsAdmin, Modelador ou GerenteIniciar a avaliação de busca (202), no nível de acesso de quem pede; 409 com outra rodada da base na fila ou rodando
GET/a/knowledge-bases/{id}/retrieval-evaluationsAdmin, Modelador ou GerenteRodadas da base, mais nova primeiro, com stale
GET/a/knowledge-bases/{id}/retrieval-evaluations/{runId}Admin, Modelador ou GerenteResultado: resumo por configuração com a diferença para a rodada anterior, resultado por pergunta e tokensUsed
GET/a/knowledge-bases/{id}/chunks/{chunkId}/previewAdmin, Modelador ou GerenteTexto de um trecho desta base, até o nível de quem olha; 404 fora dela

Base de outro tenant responde 404 e escrita numa base global por quem é do tenant responde 403, em todas as rotas acima.

O CRUD de agente (do tenant e global) aceita ragSearchMode (VECTOR/HYBRID), ragTopK (1 a 20) e ragMinSimilarity (0 a 1), com 400 fora da faixa. Num PUT, omitir ragSearchMode ou ragTopK mantém o valor do agente, mas omitir ragMinSimilarity limpa o piso — vazio é "sem piso", não "não mexa".

Administração ​

/admin/* inteiro é SUPER_ADMIN

A regra é do filtro de segurança, não de cada controller: todo caminho sob /api/admin/** exige o papel de plataforma SUPER_ADMIN. Um Admin de tenant recebe 403 em qualquer linha da tabela abaixo, inclusive nas de membros — não existe bloco de /admin/* que ele alcance. O que um Admin de tenant administra vive sob /a/*.

MétodoEndpointDescrição
GET/admin/usersListar usuários, paginado (?search=&page=&size=&sort=)
POST/admin/usersCriar usuário
PUT/admin/users/{id}Atualizar usuário
DELETE/admin/users/{id}Desativar usuário
PUT/admin/users/{id}/passwordTrocar a senha de um usuário
GET/admin/tenantsListar tenants, paginado
POST/admin/tenantsCriar tenant
PUT/admin/tenants/{id}Atualizar tenant
PATCH/admin/tenants/{id}/suspendSuspender tenant
PATCH/admin/tenants/{id}/reactivateReativar tenant
GET/admin/tenants/{id}/membersListar membros do tenant
POST/admin/tenants/{id}/membersAdicionar membro
DELETE/admin/tenants/{id}/members/{membershipId}Remover membro
PATCH/admin/tenants/{id}/members/{membershipId}/roleTrocar o papel de um membro
GET/admin/dmn-templatesListar templates DMN
POST/admin/dmn-templatesCriar template DMN
PUT/admin/dmn-templates/{key}Atualizar template DMN
DELETE/admin/dmn-templates/{key}Remover template DMN

Para ler os membros do tenant em que a sessão está, sem ser Super Admin, existe GET /a/members, que exige Admin ou Gerente do tenant.

Definições de processo (BPMN/CMMN) ​

Não existe /admin/definitions. Deploy, versionamento e publicação de definição vivem sob /a/definitions, com escopo de tenant vindo do token. As rotas da tabela exigem Admin ou Modelador do tenant, ou Super Admin; fora dela, listar (GET /a/definitions) e detalhar (GET /a/definitions/{key}) bastam estar autenticado.

MétodoEndpointDescrição
POST/a/definitions/deployDeploy de arquivo BPMN/CMMN (multipart)
GET/a/definitions/{key}/versionsHistórico de versões
POST/a/definitions/{key}/versions/{definitionId}/publishPublicar versão
GET/a/definitions/{key}/versions/{definitionId}/xmlExportar XML
PATCH/a/definitions/{key}/toggleHabilitar/desabilitar o processo no catálogo do tenant
PUT/a/definitions/{key}/configBusiness Key template e política de anexos

Deploy não é publicação

POST /a/definitions/deploy cria uma versão nova, mas não muda a versão que executa. Se o tenant já publicou alguma versão, as instâncias continuam começando naquela — o deploy responde 200, o número da versão sobe, e nada do que você acabou de subir entra em execução.

Para ativar: GET /a/definitions/{key}/versions mostra qual linha está com published: true, e POST /a/definitions/{key}/versions/{definitionId}/publish move a publicação. O deploy também registra um WARN no log quando a versão implantada não é a publicada.

Webhooks ​

Todas as rotas exigem Admin do tenant (ou Super Admin).

MétodoEndpointDescrição
GET/a/webhooksListar os webhooks do tenant
POST/a/webhooksCriar webhook (201)
PUT/a/webhooks/{id}Atualizar webhook
DELETE/a/webhooks/{id}Remover webhook (204)
GET/a/webhooks/{id}/deliveriesHistórico de entregas do webhook
POST/a/webhooks/{id}/testDisparar uma entrega de teste; corpo opcional {"event": "..."}
POST/a/webhooks/{id}/secretGerar um novo segredo de assinatura
GET/a/webhooks/egressHosts para os quais o tenant pode enviar webhooks
PUT/a/webhooks/egressAtualizar a lista de hosts permitidos

Variáveis e segredos da empresa ​

As variáveis do tenant inteiro, fora de processo — a tela Administração → Variáveis e segredos. Todas as rotas exigem Admin do tenant (ou Super Admin), exceto onde a descrição diz outra coisa.

MétodoEndpointDescrição
GET/a/tenant-variablesListar as variáveis; valores secretos voltam mascarados
POST/a/tenant-variables/{key}Criar variável (201) — só cria: chave que já existe responde 409
PUT/a/tenant-variables/{key}Criar ou atualizar; valor em branco mantém o segredo guardado
DELETE/a/tenant-variables/{key}Remover variável
GET/a/secrets/backendsEsquemas de cofre que um valor pode referenciar (ex.: vault) — nunca endereço ou credencial. Aceita também o Modelador

Retenção de dados ​

A tela Administração → Retenção de dados. Todas as rotas exigem Admin do tenant (ou Super Admin).

MétodoEndpointDescrição
GET/a/retentionJanela de retenção de cada tabela, com os limites da instalação e uma prévia
GET/a/retention/{table}/previewQuantas linhas uma janela candidata removeria — ?days=
PUT/a/retention/{table}Escolher o padrão da instalação, desligar, ou um número de dias para uma tabela
GET/a/retention/changesÚltimas alterações das janelas de retenção do tenant

Modelos e classes de modelo de IA ​

MétodoEndpointPermissãoDescrição
GET/a/ai-models/activeAdmin, Modelador ou GerenteModelos ativos, para o seletor do editor de agente
GET/a/ai-models/copilot-choicesAutenticadoModelos que a pessoa pode escolher nos copilotos — só o nome, sem preço nem provedor
GET/a/ai-model-classesAdmin, Modelador ou GerenteClasses de modelo que um agente pode usar
GET/admin/ai-model-classesSuper AdminTodas as classes, com os membros em ordem, os agentes vinculados e os avisos
POST/admin/ai-model-classesSuper AdminCriar classe (201); o código é permanente
PUT/admin/ai-model-classes/{code}Super AdminRenomear a classe ou mudar a descrição
DELETE/admin/ai-model-classes/{code}Super AdminRemover classe — recusado enquanto houver agente vinculado
PUT/admin/ai-model-classes/{code}/membersSuper AdminSubstituir os membros, em ordem; recusa modelo aposentado
GET/admin/ai-model-rolesSuper AdminO modelo do copiloto e o de reserva, quando definidos
PUT/admin/ai-model-roles/{role}Super AdminAtribuir um modelo a COPILOT ou COPILOT_FALLBACK; recusa modelo aposentado ou inativo, e reserva igual ao do copiloto
DELETE/admin/ai-model-roles/{role}Super AdminLimpar o papel

Casos de referência e avaliação de modelos ​

As rotas /a/... exigem Admin ou Modelador do tenant (ou Super Admin); as gêmeas /admin/global-agents/..., para os agentes de um template global, exigem Super Admin.

MétodoEndpointDescrição
GET/a/agents/{agentId}/reference-casesListar os casos de referência do agente
GET/a/agents/{agentId}/reference-cases/{caseId}Detalhe de um caso
POST/a/agents/{agentId}/reference-casesCriar caso (201)
PUT/a/agents/{agentId}/reference-cases/{caseId}Atualizar caso
DELETE/a/agents/{agentId}/reference-cases/{caseId}Apagar caso (204)
POST/a/instances/{instanceId}/activities/{activityId}/reference-caseSalvar uma etapa de IA concluída como caso de referência do agente dela (201)
POST/a/agents/{agentId}/evaluations/estimateEstimar chamadas, tokens e cota de uma avaliação
POST/a/agents/{agentId}/evaluationsIniciar uma avaliação de modelos (202)
GET/a/agents/{agentId}/evaluationsListar as avaliações do agente
GET/a/agents/{agentId}/evaluations/{runId}Resultado, lado a lado por modelo
GET, POST, PUT, DELETE/admin/global-agents/{agentId}/reference-cases/**Os mesmos casos, para um agente global
POST, GET/admin/global-agents/{agentId}/evaluations/**A mesma avaliação (/estimate, iniciar, listar, resultado), para um agente global
GET/admin/model-evaluations/{runId}/costsCusto por modelo de qualquer avaliação
GET/admin/retrieval-evaluations/{runId}/costsTokens e custo por moeda de qualquer avaliação de busca

Robôs e runners ​

Todas as rotas exigem Admin do tenant (ou Super Admin).

MétodoEndpointDescrição
GET/a/robotsListar os robôs do tenant
POST/a/robotsCriar robô (201). runtime é JAVA (padrão) ou PYTHON; num robô Python, mainClass é o ponto de entrada modulo:funcao
GET/a/robots/runtimesO que a instalação roda: pythonEnabled, pythonDisabledProperty (a propriedade que liga Python, quando desligado), minimumPython e maxArtifactSizeMb. Por ocupar esse nome, as chaves de robô runtimes e sdk são recusadas; um robô com a chave runtimes criado antes desta versão perde o GET /a/robots/runtimes de detalhe
GET/a/robots/sdk/pythonBaixar o RobotRunnerPython.zip (SDK flowia_robot e FakeContext; o exemplo de flowia-robot.json está em Robôs), com X-Content-SHA256. Lido de app.robots.python-runner-path
GET/a/robots/{key}Detalhe do robô
PUT/a/robots/{key}Atualizar robô. Campo omitido mantém o valor salvo; um runtime diferente num robô que já tem artefato é 400; em parameters, as chaves extras enviadas substituem as guardadas e uma chave que o JAR declara e o corpo não manda continua como estava
DELETE/a/robots/{key}Remover robô
POST/a/robots/{key}/artifactSubir o artefato do robô (multipart, parte file): um JAR ou, com ROBOTS_PYTHON_ENABLED, um pacote Python. Teto de ROBOTS_MAX_ARTIFACT_SIZE_MB (200 MB), lido em fluxo; acima dele, 413
POST/a/robots/{key}/jarAlias de /artifact
POST/a/robots/{key}/executeExecutar o robô agora
GET, PUT/a/robots/{key}/configLer e gravar a configuração de parâmetros (onde entra a referência a um segredo). O PUT substitui as chaves declaradas pelo JAR — uma declarada que não vier é removida — e mantém as extras que não vierem
GET, PUT, DELETE/a/robots/{key}/stateLer, gravar e limpar o estado persistido do robô
GET/a/robots/{key}/executionsExecuções do robô. Cada uma traz triggerType (MANUAL, SCHEDULED ou TEST) e artifactVersion, a versão do artefato fixada ao enfileirar
GET/a/robots/{key}/executions/{executionId}Detalhe de uma execução
GET/a/robots/{key}/executions/{executionId}/logBaixar o log da execução
POST/a/robots/{key}/executions/{executionId}/cancelCancelar uma execução
POST/a/robot-generationsGerar um robô a partir de uma descrição: {key, name, description, requestId}. Recusa (400) a descrição com segredo antes de chamar o modelo, e a chave tomada é 409; sem cota, 402; resposta do modelo que não serve, falha do provedor ou modelo sem resposta em app.robots.generation.model-timeout-seconds (300 s), 502 (os tokens já foram contados e nada é gravado). O mesmo requestId devolve a revisão já escrita (201) sem chamar o modelo de novo; com o pedido ainda em andamento, 409. Precisa de ROBOTS_PYTHON_ENABLED, e um tenant com robotGeneration.enabled = false recebe 400. A detecção de segredo (no nome, na descrição, na instrução e no código) é heurística: pega as formas comuns, e a pessoa que lê o código antes de aprovar é a barreira final
GET/a/robot-generationsGerações do tenant que não foram descartadas (sem as revisões)
GET/a/robot-generations/{id}Uma geração com a lista das revisões, sem o código
GET/a/robot-generations/{id}/revisions/{n}Uma revisão: código, sourceSha256, parâmetros declarados, capacidades, achados (findings), hosts, aprovação e as execuções de teste dela
POST/a/robot-generations/{id}/revisionsNova revisão: {instruction, requestId} refina com o modelo; {source, declaredParameters, capabilities, requestId} grava uma edição à mão, sem chamar o modelo (código com segredo escrito é 400 e não é gravado). 201
POST/a/robot-generations/{id}/revisions/{n}/approve{sourceSha256}: aprova exatamente o código lido. Hash diferente do guardado é 409; achado bloqueante é 400 nomeando cada um. A primeira aprovação cria o robô desligado e sem versão ativa, e constrói o pacote da revisão
POST/a/robot-generations/{id}/revisions/{n}/test-runEnfileira uma execução TEST fixada no pacote da revisão aprovada (202, RobotExecutionResponse). Revisão não aprovada ou parâmetro obrigatório vazio é 400; outra execução em andamento é 400 com o motivo. O log, o cancelamento e os parâmetros usam as rotas de /a/robots/{key}
POST/a/robot-generations/{id}/revisions/{n}/activate{clearTestState, enable, acceptFailedTest}: torna a revisão a versão ativa do robô. Exige um teste dela terminado (SUCCESS, ou FAILED com acceptFailedTest: true); clearTestState (padrão true) apaga o estado gravado pelos testes, e enable liga o robô com a agenda sugerida
POST/a/robot-generations/{id}/discardDescarta o rascunho (204) e apaga o robô criado na aprovação se ele nunca foi ativado. Geração ativada é 409
GET/a/robot-runnersListar os runners
POST/a/robot-runnersCadastrar runner (201)
PUT/a/robot-runners/{id}Atualizar runner
DELETE/a/robot-runners/{id}Remover runner
GET/a/robot-runners/{id}/logsLogs do runner
GET/a/robot-runners/agent-jarBaixar o JAR do agente de runner, com o SHA-256 num cabeçalho da resposta

As rotas sob /api/internal/runners/** e /api/internal/robot-rpc/** não são para integração: são o canal do agente de runner e do robô em execução, autenticados pelos tokens próprios deles e não pelo JWT de usuário.

Paginação ​

Endpoints de listagem aceitam parâmetros de paginação:

GET /admin/users?page=0&size=20&sort=createdAt,desc

Resposta paginada:

json
{
  "content": [...],
  "totalElements": 45,
  "totalPages": 3,
  "number": 0,
  "size": 20
}

number é o índice da página devolvida, contado a partir de zero — o mesmo valor que você mandou em page. O nome vem do formato de página do Spring Data, e todas as rotas paginadas desta API usam o mesmo formato, inclusive /a/instances.

size é limitado a 100 em /a/instances; pedir mais devolve 100 sem erro em vez de varrer o tenant inteiro.

Erros ​

Todo erro tratado pela aplicação segue o formato:

json
{
  "status": 400,
  "error": "Bad Request",
  "message": "Descrição do problema",
  "path": "/api/a/instances",
  "timestamp": "2026-04-15T10:30:00"
}

Quando o erro é de validação de campo, o mesmo corpo ganha fieldErrors, um array de { "field": "slug", "message": "Slug é obrigatório" }.

Duas famílias de resposta não passam por aí, porque são escritas antes do controller:

  • o filtro de chave de API devolve apenas error e message nos 403 de escopo e nos 429 de limite, sem status, path nem timestamp;
  • a recusa de X-Tenant-ID sai pelo tratador de erro do servidor de aplicação, no formato padrão dele.

Não trate a presença de status ou path como garantida ao ler um erro; leia sempre o código HTTP da resposta.

StatusSignificado
400Dados inválidos na requisição
401Não autenticado (token ausente ou expirado)
403Sem permissão
404Recurso não encontrado
409Conflito (ex.: e-mail já cadastrado; ou uma funcionalidade de IA chamada numa instalação sem modelo ativo — a mensagem diz onde cadastrar)
500Erro interno do servidor

Flowi Agentic — Plataforma de Gestão de Processos com IA