API REST
Base URL
http://localhost:8080/apiEm 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.htmlO 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:
cd backend
mvn verify -PopenapiO 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)
POST /auth/login
Content-Type: application/json
{
"email": "usuario@empresa.com",
"password": "suaSenha"
}Resposta:
{
"accessToken": "eyJ...",
"refreshToken": "eyJ...",
"user": { "id": "...", "email": "...", "firstName": "..." },
"roles": ["USER"]
}Use o accessToken no header de todas as requisições:
Authorization: Bearer eyJ...Refresh de token
Quando o accessToken expirar (erro 401), use o refreshToken para obter um novo par:
POST /auth/refresh
Content-Type: application/json
{
"refreshToken": "eyJ..."
}API Key (Sistema externo)
X-API-Key: sua-api-keyCada 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:
{ "error": "Too Many Requests", "message": "Rate limit exceeded for this API Key." }Escopos
Existem exatamente dois escopos, definidos em ia.flow.model.ApiKeyScope:
| Escopo | Autoridade | Métodos que abre |
|---|---|---|
read | SCOPE_read | GET, HEAD, OPTIONS |
write | SCOPE_write | POST, 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:
{
"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:
{
"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:
| Ferramenta | O que faz |
|---|---|
flowi_form_list | Lista os formulários de um processo |
flowi_form_get | Retorna um formulário com o schema completo |
flowi_form_field_types | Lista os tipos de campo aceitos e como montar o schema |
flowi_form_create | Cria um formulário (não publica) |
flowi_form_update | Substitui o schema, criando nova versão (não publica) |
flowi_form_versions | Histórico de versões, com autor — é o caminho de desfazer |
flowi_form_publish | Publica uma versão, que é o que a faz chegar aos usuários |
flowi_process_list | Lista as definições do tenant, com a versão publicada |
flowi_process_versions | Histórico de versões de uma definição |
flowi_process_get_xml | Retorna o XML BPMN/CMMN de uma versão |
flowi_process_validate | Confere um BPMN sem implantar: órfãos, becos sem saída, gateway sem fluxo padrão, diagrama ausente |
flowi_process_deploy | Deposita uma nova versão da definição (não publica) |
flowi_ai_model_list | Catá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_create | Agentes de IA do processo |
flowi_agent_attach_to_step | Vincula um agente a uma etapa, com as tools daquela etapa |
flowi_agent_suggest_knowledge | Indica, entre as bases existentes, as relevantes para uma etapa |
flowi_agent_tool_catalog | Ferramentas que uma etapa de agente pode declarar, com o que cada uma faz e do que precisa |
flowi_connector_list | O 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_process | Bases de conhecimento (RAG) |
flowi_cms_field_types | Tipos 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_query | Coleções de CMS |
flowi_process_doc_get | Lê a descrição do processo |
flowi_process_doc_propose | Propõe uma descrição nova — devolve o texto, não grava |
flowi_run_startable_list | Processos que dá para iniciar, cada um com sua política de MCP |
flowi_run_start | Inicia uma instância (só se a política permitir) |
flowi_run_my_tasks | A caixa de tarefas do chamador, como ele a vê na tela |
flowi_run_task_get | A tarefa e o schema do formulário dela, numa resposta só |
flowi_run_task_claim | Assume uma tarefa oferecida a um papel do chamador |
flowi_run_task_preview | Mostra o que a conclusão gravaria e para onde a instância iria; não grava nada |
flowi_run_task_complete | Conclui uma tarefa, exigindo o token do preview |
flowi_run_attach_text | Anexa um documento de texto a uma instância |
flowi_run_instance_get | Estado de uma instância |
flowi_run_why_failed | Falhas 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:
| Valor | Iniciar | Concluir tarefa |
|---|---|---|
| Nada (padrão) | recusado | recusado |
| Iniciar e ler | permitido | recusado |
| Iniciar e concluir | permitido | permitido, 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:
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.
| Prefixo | Escopo | Auth |
|---|---|---|
/auth | Autenticação / sessão | Sem auth em /login, /refresh, /password/forgot, /password/reset e /sso/**; o resto exige token |
/admin/* | Operações de plataforma | JWT (SUPER_ADMIN apenas) |
/a/* | Operações de tenant | JWT com tenant selecionado |
Autenticação
| Método | Endpoint | Descrição |
|---|---|---|
| POST | /auth/login | Login com email/senha |
| POST | /auth/refresh | Renovar token |
| POST | /auth/select-tenant | Trocar o tenant da sessão (devolve um token novo) |
| POST | /auth/select-platform | Só Super Admin: sair da empresa escolhida e voltar a um token sem tenant (devolve um token novo); outro papel recebe 403 |
| GET | /auth/me | Dados do usuário logado |
| GET | /auth/me/tenants | Tenants do usuário |
| GET | /auth/me/session | Dados da sessão corrente |
| POST | /auth/password/forgot | Pedir link de redefinição — responde 202 sempre, cadastrado ou não, e tem limite de tentativas por IP |
| POST | /auth/password/reset | Concluir a redefinição com o token do e-mail |
| POST | /auth/logout | Invalidar sessão |
Resposta do login:
{
"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étodo | Endpoint | Descrição |
|---|---|---|
| GET | /a/definitions | Listar 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}/variables | Variá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}/monitor | Carga 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-metrics | Métricas de SLA do processo nos últimos dias — ?days= (padrão 30) |
| PUT | /a/definitions/{key}/suspend | Suspender 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}/activate | Reativar a definição suspensa — Só BPMN, pelo mesmo motivo do suspend |
| POST | /a/instances | Iniciar instância (BPMN ou CMMN) |
| GET | /a/instances | Listar instâncias ativas, paginado — ?page=&size=&type= |
| GET | /a/instances/search | Buscar 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}/suspend | Suspender instância — só BPMN; um caso responde 400 |
| PATCH | /a/instances/{id}/activate | Reativar instância — só BPMN; um caso responde 400 |
| GET | /a/instances/{id}/diagram | SVG/XML do diagrama com atividades marcadas |
| GET | /a/instances/failed-jobs | Jobs com falha do tenant |
| POST | /a/instances/failed-jobs/{jobId}/retry | Retry de job |
| POST | /a/instances/failed-jobs/{jobId}/deadletter | Move o job para a fila de dead-letter, parando as tentativas |
| GET | /a/ai-task-failures | Etapas de agente de IA que falharam, paginado (size 20 por padrão) |
| POST | /a/ai-task-failures/{id}/retry | Executa de novo a etapa (caso) ou reprocessa o job morto dela (processo) |
| POST | /a/ai-task-failures/{id}/dismiss | Dispensa 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.
{
"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:
| Motivo | Significado |
|---|---|
USER_TASK | tarefa aguardando uma pessoa |
JOB_FAILED | tentativas esgotadas, na fila de dead-letter |
JOB_RETRYING | falhou e ainda tem tentativa |
TIMER | aguardando uma data agendada |
EXECUTING | execuçã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étodo | Endpoint | Descrição |
|---|---|---|
| GET | /a/history/instances | Instâncias concluídas, paginado — ?tenantId=&type=&page=&size= |
| GET | /a/history/instances/{id} | Detalhe da instância concluída |
| GET | /a/history/instances/{id}/activities | Atividades 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étodo | Endpoint | Descrição |
|---|---|---|
| GET | /a/tasks | Listar tarefas do usuário (?page=&size=&mine=) |
| GET | /a/tasks/completed | Listar tarefas já concluídas (histórico) (?page=&size=) |
| GET | /a/tasks/{id} | Detalhe + formulário |
| POST | /a/tasks/{id}/claim | Assumir tarefa |
| POST | /a/tasks/{id}/complete | Concluir tarefa |
| POST | /a/tasks/{id}/unclaim | Devolver tarefa ao grupo |
| POST | /a/tasks/{id}/delegate | Delegar |
| GET | /a/tasks/{id}/variables | Ler as variáveis visíveis da tarefa |
| POST | /a/tasks/{id}/variables | Gravar variáveis sem concluir a tarefa |
| POST | /a/tasks/{id}/ai-suggest | Sugestã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}/comments | Listar comentários |
| POST | /a/tasks/{id}/comments | Adicionar 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étodo | Endpoint | Descrição |
|---|---|---|
| GET | /a/definitions/{processKey}/forms | Listar os formulários do processo |
| POST | /a/definitions/{processKey}/forms | Criar 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}/versions | Listar 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}/publish | Publicar 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étodo | Endpoint | Descrição |
|---|---|---|
| GET | /a/forms/datasources | Listar fontes de dados |
| POST | /a/forms/datasources | Criar 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}/execute | Executar 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:
{ "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étodo | Endpoint | Descrição |
|---|---|---|
| GET | /a/decisions | Listar decisions do tenant |
| POST | /a/decisions | Deploy 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}/evaluate | Testar 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étodo | Endpoint | Permissão | Descrição |
|---|---|---|---|
| GET | /a/cms/collections | Autenticado | Listar as coleções visíveis para o chamador |
| POST | /a/cms/collections | Gerente | Criar coleção |
| GET | /a/cms/collections/{id} | Autenticado | Detalhe da coleção |
| PUT | /a/cms/collections/{id} | Gerente | Atualizar coleção |
| DELETE | /a/cms/collections/{id} | Gerente | Remover coleção |
| GET | /a/cms/collections/{id}/records | Autenticado | Listar registros, paginado (size 20 por padrão) |
| POST | /a/cms/collections/{id}/records | Gerente | Criar um registro (201) |
| POST | /a/cms/collections/{id}/records/bulk | Gerente | Carga em lote |
| PUT | /a/cms/collections/{id}/records/{recordId} | Gerente | Atualizar registro |
| DELETE | /a/cms/collections/{id}/records/{recordId} | Gerente | Remover registro |
| DELETE | /a/cms/collections/{id}/records | Gerente | Esvaziar 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étodo | Endpoint | Permissão | Descrição |
|---|---|---|---|
| GET | /a/knowledge-bases | Admin, Modelador ou Gerente | Listar as bases do tenant e as globais |
| POST | /a/knowledge-bases | Admin, Modelador ou Gerente | Criar base (201); isGlobal: true exige Super Admin |
| GET | /a/knowledge-bases/{id} | Admin, Modelador ou Gerente | Detalhe da base, com a contagem de documentos, ftsLanguage, retrievableChunks, keywordIndexedChunks e keywordIndexStatus (READY/REBUILDING) |
| PATCH | /a/knowledge-bases/{id}/search-settings | Admin, 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 Gerente | Remover base — recusa enquanto houver processo vinculado |
| GET | /a/knowledge-bases/{id}/documents | Admin, Modelador ou Gerente | Listar documentos, paginado (size 20 por padrão) |
| POST | /a/knowledge-bases/{id}/documents | Admin, Modelador ou Gerente | Subir PDF, TXT ou MD (202 — a indexação é assíncrona) |
| DELETE | /a/knowledge-bases/{id}/documents/{docId} | Admin, Modelador ou Gerente | Remover documento e seus vetores |
| GET | /a/knowledge-bases/process/{definitionKey} | Admin, Modelador ou Gerente | Bases vinculadas a um processo |
| POST | /a/knowledge-bases/process/{definitionKey}/link/{id} | Admin, Modelador ou Gerente | Vincular base ao processo (201) |
| DELETE | /a/knowledge-bases/process/{definitionKey}/unlink/{id} | Admin, Modelador ou Gerente | Desvincular |
| GET | /a/knowledge-bases/{id}/reference-questions | Admin, Modelador ou Gerente | Perguntas de referência da base; o nome de um documento acima do seu nível vem escondido |
| POST | /a/knowledge-bases/{id}/reference-questions | Admin, Modelador ou Gerente | Criar 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 Gerente | Editar / apagar (204) |
| POST | /a/knowledge-bases/{id}/retrieval-evaluations/estimate | Admin, 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-evaluations | Admin, Modelador ou Gerente | Iniciar 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-evaluations | Admin, Modelador ou Gerente | Rodadas da base, mais nova primeiro, com stale |
| GET | /a/knowledge-bases/{id}/retrieval-evaluations/{runId} | Admin, Modelador ou Gerente | Resultado: resumo por configuração com a diferença para a rodada anterior, resultado por pergunta e tokensUsed |
| GET | /a/knowledge-bases/{id}/chunks/{chunkId}/preview | Admin, Modelador ou Gerente | Texto 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étodo | Endpoint | Descrição |
|---|---|---|
| GET | /admin/users | Listar usuários, paginado (?search=&page=&size=&sort=) |
| POST | /admin/users | Criar usuário |
| PUT | /admin/users/{id} | Atualizar usuário |
| DELETE | /admin/users/{id} | Desativar usuário |
| PUT | /admin/users/{id}/password | Trocar a senha de um usuário |
| GET | /admin/tenants | Listar tenants, paginado |
| POST | /admin/tenants | Criar tenant |
| PUT | /admin/tenants/{id} | Atualizar tenant |
| PATCH | /admin/tenants/{id}/suspend | Suspender tenant |
| PATCH | /admin/tenants/{id}/reactivate | Reativar tenant |
| GET | /admin/tenants/{id}/members | Listar membros do tenant |
| POST | /admin/tenants/{id}/members | Adicionar membro |
| DELETE | /admin/tenants/{id}/members/{membershipId} | Remover membro |
| PATCH | /admin/tenants/{id}/members/{membershipId}/role | Trocar o papel de um membro |
| GET | /admin/dmn-templates | Listar templates DMN |
| POST | /admin/dmn-templates | Criar 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étodo | Endpoint | Descrição |
|---|---|---|
| POST | /a/definitions/deploy | Deploy de arquivo BPMN/CMMN (multipart) |
| GET | /a/definitions/{key}/versions | Histórico de versões |
| POST | /a/definitions/{key}/versions/{definitionId}/publish | Publicar versão |
| GET | /a/definitions/{key}/versions/{definitionId}/xml | Exportar XML |
| PATCH | /a/definitions/{key}/toggle | Habilitar/desabilitar o processo no catálogo do tenant |
| PUT | /a/definitions/{key}/config | Business 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étodo | Endpoint | Descrição |
|---|---|---|
| GET | /a/webhooks | Listar os webhooks do tenant |
| POST | /a/webhooks | Criar webhook (201) |
| PUT | /a/webhooks/{id} | Atualizar webhook |
| DELETE | /a/webhooks/{id} | Remover webhook (204) |
| GET | /a/webhooks/{id}/deliveries | Histórico de entregas do webhook |
| POST | /a/webhooks/{id}/test | Disparar uma entrega de teste; corpo opcional {"event": "..."} |
| POST | /a/webhooks/{id}/secret | Gerar um novo segredo de assinatura |
| GET | /a/webhooks/egress | Hosts para os quais o tenant pode enviar webhooks |
| PUT | /a/webhooks/egress | Atualizar 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étodo | Endpoint | Descrição |
|---|---|---|
| GET | /a/tenant-variables | Listar 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/backends | Esquemas 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étodo | Endpoint | Descrição |
|---|---|---|
| GET | /a/retention | Janela de retenção de cada tabela, com os limites da instalação e uma prévia |
| GET | /a/retention/{table}/preview | Quantas 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étodo | Endpoint | Permissão | Descrição |
|---|---|---|---|
| GET | /a/ai-models/active | Admin, Modelador ou Gerente | Modelos ativos, para o seletor do editor de agente |
| GET | /a/ai-models/copilot-choices | Autenticado | Modelos que a pessoa pode escolher nos copilotos — só o nome, sem preço nem provedor |
| GET | /a/ai-model-classes | Admin, Modelador ou Gerente | Classes de modelo que um agente pode usar |
| GET | /admin/ai-model-classes | Super Admin | Todas as classes, com os membros em ordem, os agentes vinculados e os avisos |
| POST | /admin/ai-model-classes | Super Admin | Criar classe (201); o código é permanente |
| PUT | /admin/ai-model-classes/{code} | Super Admin | Renomear a classe ou mudar a descrição |
| DELETE | /admin/ai-model-classes/{code} | Super Admin | Remover classe — recusado enquanto houver agente vinculado |
| PUT | /admin/ai-model-classes/{code}/members | Super Admin | Substituir os membros, em ordem; recusa modelo aposentado |
| GET | /admin/ai-model-roles | Super Admin | O modelo do copiloto e o de reserva, quando definidos |
| PUT | /admin/ai-model-roles/{role} | Super Admin | Atribuir 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 Admin | Limpar 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étodo | Endpoint | Descrição |
|---|---|---|
| GET | /a/agents/{agentId}/reference-cases | Listar os casos de referência do agente |
| GET | /a/agents/{agentId}/reference-cases/{caseId} | Detalhe de um caso |
| POST | /a/agents/{agentId}/reference-cases | Criar 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-case | Salvar uma etapa de IA concluída como caso de referência do agente dela (201) |
| POST | /a/agents/{agentId}/evaluations/estimate | Estimar chamadas, tokens e cota de uma avaliação |
| POST | /a/agents/{agentId}/evaluations | Iniciar uma avaliação de modelos (202) |
| GET | /a/agents/{agentId}/evaluations | Listar 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}/costs | Custo por modelo de qualquer avaliação |
| GET | /admin/retrieval-evaluations/{runId}/costs | Tokens 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étodo | Endpoint | Descrição |
|---|---|---|
| GET | /a/robots | Listar os robôs do tenant |
| POST | /a/robots | Criar robô (201). runtime é JAVA (padrão) ou PYTHON; num robô Python, mainClass é o ponto de entrada modulo:funcao |
| GET | /a/robots/runtimes | O 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/python | Baixar 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}/artifact | Subir 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}/jar | Alias de /artifact |
| POST | /a/robots/{key}/execute | Executar o robô agora |
| GET, PUT | /a/robots/{key}/config | Ler 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}/state | Ler, gravar e limpar o estado persistido do robô |
| GET | /a/robots/{key}/executions | Execuçõ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}/log | Baixar o log da execução |
| POST | /a/robots/{key}/executions/{executionId}/cancel | Cancelar uma execução |
| POST | /a/robot-generations | Gerar 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-generations | Geraçõ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}/revisions | Nova 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-run | Enfileira 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}/discard | Descarta o rascunho (204) e apaga o robô criado na aprovação se ele nunca foi ativado. Geração ativada é 409 |
| GET | /a/robot-runners | Listar os runners |
| POST | /a/robot-runners | Cadastrar runner (201) |
| PUT | /a/robot-runners/{id} | Atualizar runner |
| DELETE | /a/robot-runners/{id} | Remover runner |
| GET | /a/robot-runners/{id}/logs | Logs do runner |
| GET | /a/robot-runners/agent-jar | Baixar 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,descResposta paginada:
{
"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:
{
"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
erroremessagenos403de escopo e nos429de limite, semstatus,pathnemtimestamp; - a recusa de
X-Tenant-IDsai 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.
| Status | Significado |
|---|---|
| 400 | Dados inválidos na requisição |
| 401 | Não autenticado (token ausente ou expirado) |
| 403 | Sem permissão |
| 404 | Recurso não encontrado |
| 409 | Conflito (ex.: e-mail já cadastrado; ou uma funcionalidade de IA chamada numa instalação sem modelo ativo — a mensagem diz onde cadastrar) |
| 500 | Erro interno do servidor |