Papéis, Permissões e Rotas
Esta documentação serve de referência técnica (inclusive para IA) sobre os níveis de acesso na plataforma Flowi Agentic.
A plataforma possui uma arquitetura SaaS Multi-tenant. O controle de acesso baseia-se primordialmente na coluna global_role do usuário — que vale SUPER_ADMIN ou nada — e no papel (role) do usuário no contexto de um Tenant, gravado em user_tenant_memberships.platform_role.
Nenhum papel vem de um provedor externo
A sessão é sempre um JWT próprio, emitido e validado pela própria aplicação (security/JwtTokenProvider.java, service/AuthService.java). Existe login OIDC — as conexões de SSO em /admin/sso, tratadas por SsoConnectionService e SsoCallbackService —, mas ele só autentica: o provisionamento automático grava globalRole = null (SsoCallbackService.provision), nenhum grupo do diretório vira papel, e nenhum vínculo de tenant é criado. Quem procurar um console de identidade para conceder papéis não vai achar: papel se concede pelo banco da própria plataforma, via /admin/users e /admin/tenants/{id}/members.
1. Perfis e Níveis de Acesso
A. Super Admin (Admin Global)
- Identificação Frontend:
roles.includes('SUPER_ADMIN') - Identificação Backend:
hasRole('SUPER_ADMIN'), resolvido a partir da claim de papel global do JWT emitido pela própria plataforma. - Escopo: Todo o sistema. Não está amarrado a um tenant específico na interface de administração global.
- Responsabilidades: Cadastrar tenants, gerenciar definições de processos globais (BPMN/CMMN), templates DMN, gerenciar configurações de IA e todos os usuários.
B. Tenant Admin (Administrador do Tenant)
- Identificação Frontend:
!isSuperAdmin&¤tTenant?.role === 'ADMIN' - Identificação Backend:
@tenantSec.isCurrentTenantAdmin(authentication)nos recursos/a/*. Não alcança nada em/api/admin/**— oSecurityConfigexigeSUPER_ADMINno matcher, e a authority de um Tenant Admin éROLE_ADMIN. - Escopo: Restrito aos dados do seu próprio Tenant.
- Responsabilidades: Configurar o Business Key padrão do tenant, habilitar/desabilitar processos publicados no seu catálogo, gerenciar robôs (AI Agents), API Keys e Webhooks do tenant. Membros do tenant não estão nessa lista:
/api/admin/tenants/{id}/members*éSUPER_ADMINonly.
B.1. Tenant Modeler (Modelador do Tenant)
- Identificação Frontend:
currentTenant?.role === 'MODELER', resolvido porreaches()emsrc/lib/access.ts— o único lugar do frontend onde papel vira alcance. - Identificação Backend:
@tenantSec.isCurrentTenantModeler(authentication). - Escopo: Restrito aos dados do seu próprio Tenant.
- A regra, em uma linha:
MODELERéADMINmenos as chaves do tenant, os robôs, os webhooks e as pessoas. Ele desenha, configura e publica processo, e usa o copiloto do modelador; não administra a organização. - Alcança: catálogo de processos e o modelador (inclusive publicar uma versão), formulários, tabelas de decisão, papéis de processo, variáveis do processo, tipos documentais, bases de conhecimento, agentes de IA, CMS, a regra de gatilho de e-mail (na aba Configurações da definição) e as entregas de e-mail.
- Não alcança: membros do tenant, robôs e runners, API Keys, webhooks e caixas de entrada de e-mail — a caixa guarda credencial de servidor, da mesma natureza de uma API Key.
- Não é uma patente. Ele não herda a visão de tenant inteiro que o Gerente tem: em execução um modelador vê o que um
USERvê./monitorcontinua exigindo Gerente ou Admin.
O copiloto gasta a cota de IA do tenant
Dar o copiloto ao modelador significa que alguém que não é administrador gasta o dinheiro do tenant. É uma troca deliberada — ver specs/core/modeler-role.md. Por isso a linha de uso carrega o id do usuário, e não só o do tenant.
B.2. Tenant Manager (Gerente do Tenant)
Identificação Frontend:
currentTenant?.role === 'MANAGER'Identificação Backend:
@tenantSec.isCurrentTenantManager(authentication)— aceitaMANAGEReADMINcom vínculo (UserTenantMembership) no tenant corrente;SUPER_ADMINpassa sempre.Escopo: Restrito aos dados do seu próprio Tenant.
Responsabilidades: Dois módulos reconhecem o gerente hoje.
- CMS — cria, edita e exclui coleções e registros, carrega tabelas em lote e limpa coleções.
- Definições de processo — o acompanhamento por etapa (
/a/definitions/{key}/monitore/monitor/{activityId}) e a escrita da documentação do processo (PUTeDELETEem/a/definitions/{key}/documentation) aceitam gerente. Ler a documentação basta estar autenticado.
Nos demais módulos ele ainda se comporta como Tenant User. Ao adicionar um novo módulo, decida explicitamente se ele deve reconhecer o gerente.
B.3. Tenant Modeler (Modelador do Tenant)
- Identificação Frontend:
currentTenant?.role === 'MODELER' - Identificação Backend:
@tenantSec.isCurrentTenantModeler(authentication)— aceitaMODELEReADMINcom vínculo (UserTenantMembership) no tenant corrente;SUPER_ADMINpassa sempre. Uma chave de API não tem vínculo e por isso nunca passa. - Escopo: Restrito aos dados do seu próprio Tenant.
- A regra, em uma linha:
MODELERéADMINmenos as chaves do tenant, os robôs, os webhooks e as pessoas. Ele desenha, configura e publica processo; não administra a organização. - Responsabilidades: catálogo de processos, modelador e versões — inclusive publicar uma versão; formulários; tabelas de decisão (DMN); papéis de processo, variáveis do processo e tipos documentais; bases de conhecimento e agentes de IA do processo; o seletor de Java delegates; a documentação do processo; a regra de gatilho de e-mail (
/a/mail-rules), que hoje mora na aba Configurações da definição, e o log de entregas (/a/mail-deliveries) — uma mensagem que não iniciou processo é diagnóstico sobre o desenho. Também dirige o copiloto do modelador e a validação por IA. - O que ele não alcança: membros do tenant (
/a/members), robôs e runners (/a/robots,/a/robot-runners), chaves de API (/a/api-keys), webhooks (/a/webhooks) e caixas de entrada de e-mail (/a/mail-inboxes, que guardam credencial de servidor — mesma natureza de uma chave de API). Para escolher a caixa que uma regra usa ele lê/a/mail-rules/inboxes, uma projeção que devolve apenas id, nome e situação — nunca host, usuário ou referência de senha. - Ele não é um posto acima do Gerente.
MODELERnão entra emPlatformRole.seesEverythingInTenant(): em execução ele enxerga o que umUSERenxerga. Cancelar, suspender ou reativar instância, ver a fila de jobs com erro e acompanhar/monitorcontinuam sendo do Gerente.
Um não-administrador passa a gastar o dinheiro do tenant
Copiloto e validação por IA consomem a cota de tokens do tenant. Dar essas telas ao modelador é uma troca deliberada — a alternativa seria um papel que não usa a ferramenta em torno da qual o produto é construído. Por isso a linha de ai_usage_logs de um turno de copiloto carrega o user_id além do tenant, e não só o tenant: a tela de governança precisa responder quem gastou. Um teto por pessoa existe em user_ai_quotas e é respeitado tanto na checagem prévia quanto no débito (TenantAiQuotaService); hoje ele só é gravado por PUT /api/admin/users/{id}/ai-quota, que é Super Admin.
C. Tenant User (Usuário Padrão)
- Identificação Frontend:
currentTenant?.role === 'USER' - Identificação Backend: Acesso restrito via endpoints operacionais (
/a/*), com o tenant vindo do token da sessão. - Escopo: Restrito à operação do próprio Tenant.
- Responsabilidades: Iniciar instâncias de processo, executar tarefas (
/tasks), preencher formulários e visualizar dashboards operacionais.
2. Rotas do Frontend (React / TanStack Router)
A tabela abaixo cruza as rotas do frontend com a visibilidade de cada perfil — o que a barra lateral (components/Sidebar.tsx) mostra e o que o beforeLoad de cada rota recusa. Não é a matriz de autorização — quem manda é a seção 3 —, mas desde o papel de modelador ela é completa: toda rota restrita passa por requireArea(), e o mapa de rota para área mora em src/lib/access.ts. São quatro áreas: design (Admin e Modelador), content (Admin, Modelador e Gerente), organisation (só Admin) e tenantWideMonitoring (Admin e Gerente).
A recusa deixou de ser silenciosa: requireArea() manda a pessoa ao /dashboard com ?denied=<área>, e o Dashboard explica em texto traduzido por que aquela tela não é dela.
O ❌ da coluna Super Admin quer dizer "não é o caminho dele", não "bloqueado": nenhum beforeLoad recusa um Super Admin, e um Super Admin que seleciona um tenant passa a ver na barra lateral as mesmas entradas de tenant.
| Rota / Path | Módulo / Função | Super Admin | Tenant Admin | Modelador | Tenant User |
|---|---|---|---|---|---|
/dashboard | Visão geral, gráficos e contadores. | ❌ (Usa admin panel) | ✅ | ✅ | ✅ |
/admin/tenants | Listagem e criação global de tenants. | ✅ | ❌ | ❌ | ❌ |
/admin/users | Listagem global de usuários. | ✅ | ❌ | ❌ | ❌ |
/admin/ai-config | Configuração de IA, em duas abas: Catálogo de Modelos LLM e Banco de Vetores (RAG). | ✅ | ❌ | ❌ | ❌ |
/definitions | Definições Locais (Catálogo de Processos) - Para o Admin e o Modelador: visualiza processos publicados, permite ligar/desligar, publicar versão e configurar instâncias, tipos locais e o gatilho por e-mail. | ❌ | ✅ | ✅ | ❌ |
/admin/global-templates | Templates Globais - Para SA: Lista tudo, permite criar BPMN/CMMN/DMN e configurar Tipos Documentais, Variáveis e Anexos globais. | ✅ | ❌ | ❌ | ❌ |
/admin/process-roles | Catálogo centralizado de Papéis de Processo. | ✅ | ❌ | ✅ | ❌ |
/admin/global-templates/$key | Detalhes do template e abas de configuração avançada. | ✅ | ❌ | ❌ | ❌ |
/instances | Listagem de instâncias ativas, histórico e erros. | ❌ (Gestão global por API apenas) | ✅ | ✅ | ✅ |
/instances/start/* | Formulário para iniciar instâncias. | ❌ (Restrito para evitar instâncias sem tenant) | ✅ | ✅ | ✅ |
/tasks | Caixa de entrada e execução de tarefas humanas. | ❌ | ✅ | ✅ | ✅ |
/cms | Gerenciamento de conteúdo dinâmico do tenant. - A tela exige Gerente, Modelador ou Admin; o usuário comum é recusado com o motivo na tela. - Escrita (criar/editar/excluir/carga em lote): Gerente ou superior. - ⚠️ A API de leitura ( GET /a/cms/**) continua aberta a qualquer membro autenticado, limitada pelo sigilo da coleção. Esconder a rota no front não fecha o endpoint. | ❌ | ✅ | ✅ | ❌ |
/admin/knowledge-bases | Bases de conhecimento do tenant, agrupadas com o CMS em Conteúdo. - A tela exige Gerente, Modelador ou Admin, e a API exige o mesmo papel — leitura inclusive. Esconder a rota no front não é o que fecha o endpoint. | ❌ | ✅ | ✅ | ❌ |
/monitor | Acompanhamento por etapa: diagrama do processo com a carga parada em cada atividade. Gerente ou superior. | ❌ | ✅ | ❌ | ❌ |
/admin/document-types | Catálogo de tipos documentais do tenant, em Modelagem. - A tela exige Admin ou Modelador, e a API exige o mesmo para criar, editar e excluir. - A leitura ( GET /a/document-types) desce até Gerente, porque é ela que preenche o seletor de tipo no construtor de formulários; o usuário comum recebe 403. | ❌ | ✅ | ✅ | ❌ |
/decisions | Tabelas de Decisão (DMN), em Modelagem → Tabelas de Decisão. - O beforeLoad (requireArea('design')) recusa quem não desenha processos — Admin, Modelador e Super Admin passam —, porque GET /a/decisions já exigia esse nível: o usuário comum só via uma lista vazia e botões que respondiam 403.- ⚠️ A avaliação ( POST /a/decisions/{chave}/evaluate) continua aberta a qualquer membro autenticado — é por ela que os processos consultam a tabela. | ✅ | ✅ | ✅ | ❌ |
/robots | Automação e bots vinculados ao tenant. - A tela exige Admin do tenant, e a API exige o mesmo em todas as rotas /a/robots/** — inclusive ler e gravar a configuração de parâmetros, que é onde entra a referência de um segredo. | ❌ | ✅ | ❌ | ❌ |
/admin/api-keys | Chaves de API programáticas por tenant. | ❌ | ✅ | ❌ | ❌ |
/admin/webhooks | Callbacks configurados por tenant. | ❌ | ✅ | ❌ | ❌ |
/admin/tenants/$id | Gestão de membros do tenant. Só Super Admin: o beforeLoad redireciona o Tenant Admin ao Dashboard, e todos os endpoints /api/admin/tenants/** exigem SUPER_ADMIN. | ✅ | ❌ | ❌ | ❌ |
/mail-inboxes | Caixas de entrada de e-mail do tenant, em Integração, ao lado de API Keys e Webhooks. - Guarda a conexão IMAP e a referência da senha: é credencial, e por isso fica com o Admin do tenant. - A lista de regras continua ali, em modo leitura, para o administrador ver o que depende daquela conta antes de mudá-la ou apagá-la. | ❌ | ✅ | ❌ | ❌ |
/mail-deliveries | Toda mensagem que a plataforma leu e o que fez com ela. - Uma mensagem recusada é diagnóstico do processo que deveria ter iniciado, então o modelador também vê. | ❌ | ✅ | ✅ | ❌ |
/admin/rag-config não é uma rota do frontend
rag-config existe só como endpoint de backend (/api/admin/rag-config). Navegar para /admin/rag-config na SPA não abre tela nenhuma — a configuração de RAG é a segunda aba de /admin/ai-config. Vale para qualquer link ou documento que aponte para lá.
3. Endpoints de Backend e Permissões (REST API)
As APIs no Spring Boot são divididas por convenção de URL e filtros de segurança.
O catálogo de caminhos, métodos e parâmetros é API REST; o que esta página acrescenta é quem pode chamar cada um e o que o backend faz quando quem chama não pode.
Prefixos Principais
/admin/**: Endpoints de gestão global.- O filtro de segurança exige
SUPER_ADMIN.ADMINé papel de dentro de um tenant e não abre nada em/api/admin/**; cada controller estreita a partir daí, e a maioria das operações éSUPER_ADMINonly (ver a tabela de administração em API REST). - Operam sem tenant selecionado, cross-tenant onde aplicável.
- O filtro de segurança exige
/a/**: Endpoints operacionais multi-tenant.- Exigem um tenant selecionado no token (
POST /api/auth/select-tenant). - O backend valida se o usuário autenticado possui o tenant listado em suas permissões.
- O prefixo só garante que existe um usuário autenticado no tenant. Quem decide o papel é o
@PreAuthorizede cada controller, e são quatro predicados de tenant:isCurrentTenantAdmin(sóADMIN),isCurrentTenantManager(MANAGERouADMIN),isCurrentTenantModeler(MODELERouADMIN) e a ausência deliberada de anotação, listada em Rotas abertas de propósito.SUPER_ADMINpassa em todos. Rota nova sem decisão de papel reprova emTenantEndpointDeclaresItsRoleTest.
- Exigem um tenant selecionado no token (
Endpoints Chave de Controle de Processo
Não existe /admin/definitions
Deploy, versionamento e publicação de BPMN/CMMN ficam em /a/definitions/..., com escopo de tenant vindo do token. Uma integração escrita contra /admin/definitions recebe 404. Que as definições sejam globais não muda o caminho: o que é global é o artefato publicado, não a rota.
| Endpoint | Método | Contexto | Acesso | Descrição |
|---|---|---|---|---|
/a/definitions | GET | Tenant | TA, Gerente, User | Retorna o catálogo de processos ativos para o tenant. Basta estar autenticado. |
/a/definitions/deploy | POST | Tenant | TA, Modelador | Deploy do arquivo BPMN/CMMN (multipart). Não publica a versão. |
/a/definitions/{key}/versions | GET | Tenant | TA, Modelador | Histórico de versões; a publicada vem com published: true. |
/a/definitions/{key}/versions/{definitionId}/publish | POST | Tenant | TA, Modelador | Move a publicação para a versão indicada. |
/a/definitions/{key}/config | PUT | Tenant | TA, Modelador | Configuração específica do processo no tenant (Business Key Template e política de anexos). |
/a/definitions/{key}/attachment-policy | GET | Tenant | TA, Modelador | Política de anexos vigente do processo. Leitura da tela de catálogo, que já exige Admin do tenant. Quem aplica a política em execução é o servidor (AttachmentAccessService), não o formulário: nenhuma tela de runtime chama esta rota, e por isso ela é fechada. |
/a/definitions/{key}/mcp-policy | GET | Tenant | TA, Modelador | O que um assistente conectado por MCP pode fazer no processo. Mesmo desenho da política de anexos: quem a aplica é o ExecutionMcpTools, dentro do servidor. |
/a/delegates | GET | Tenant | TA, Modelador | Beans de delegate carregados, com o nome da classe Java e os parâmetros declarados de cada um — superfície de informação sobre a instalação. Só o seletor do modelador consome, e o modelador já exige Admin do tenant. |
/a/delegates/active | GET | Tenant | TA | JARs de delegate globais ativos. Já exigia papel antes da #115. |
/a/definitions/{key}/monitor | GET | Tenant | TA, Gerente | Carga parada por etapa do processo, com a quebra por motivo. Aceita ?version=. |
/a/definitions/{key}/monitor/{activityId} | GET | Tenant | TA, Gerente | O que está parado naquela etapa: instância, motivo, responsável ou erro. Aceita ?version=. |
/a/definitions/{key}/toggle | PATCH | Tenant | TA, Modelador | Habilita/Desabilita o processo no catálogo do tenant. |
/a/definitions/{key}/suspend | PUT | Tenant | TA, Modelador | Suspende a definição: nenhuma instância nova começa. É diferente de desabilitar no catálogo. |
/a/definitions/{key}/activate | PUT | Tenant | TA, Modelador | Reativa a definição suspensa. |
/a/instances | POST | Tenant | TA, User | Inicia uma instância de workflow em nome do tenant logado. Gera o Business Key de acordo com fallback hierarchy. |
/a/instances/search | GET | Tenant | TA, User | Busca instâncias por variável. Para usuário comum, procura apenas dentro das instâncias que ele enxerga. |
/a/instances/{id} | DELETE | Tenant | TA, Gerente | Cancela uma instância. Operação destrutiva — não é oferecida a usuário comum, e o backend recusa. |
/a/instances/{id}/suspend | PATCH | Tenant | TA, Gerente | Suspende a instância. |
/a/instances/{id}/activate | PATCH | Tenant | TA, Gerente | Reativa a instância suspensa. |
/a/instances/failed-jobs | GET | Tenant | TA, Gerente | Fila de jobs com erro do tenant. |
/a/instances/failed-jobs/{jobId}/retry | POST | Tenant | TA, Gerente | Reexecuta o job. |
/a/instances/failed-jobs/{jobId}/deadletter | POST | Tenant | TA, Gerente | Move o job para a deadletter. |
/a/instances/{id} | GET | Tenant | TA, User | Detalhe da instância. Usuário comum só alcança instância em que entra; instância alheia responde 404, não 403 — 403 confirmaria que ela existe. |
/a/instances | GET | Tenant | TA, User | Lista paginada de instâncias ativas. Aceita page, size (padrão 20, teto 100) e type. Devolve envelope {content, number, size, totalElements, totalPages} — não um array. O índice da página se chama number na resposta e page na requisição, seguindo o formato do Spring Data usado pelas demais rotas paginadas. |
Quem enxerga uma instância
Gestor e administrador do tenant veem todas as instâncias. Usuário comum vê as instâncias em que entra, e são três origens somadas:
- as que ele iniciou;
- as que ele participa — tem tarefa atribuída, é candidato, ou é candidato por um papel de processo dele;
- as que ele participou — teve tarefa e já concluiu.
É de propósito a mesma regra que a tela de Tarefas aplica: duas telas discordando sobre quem participa é pior que qualquer uma das regras sozinha.
"Vê tudo no tenant" tem uma definição só, em PlatformRole.seesEverythingInTenant(), e vale SUPER_ADMIN, ADMIN e MANAGER. MODELER e USER ficam de fora — e o modelador ficou de fora por decisão, não por ordem do enum: ele desenha o processo, mas em execução enxerga o que um usuário comum enxerga. As duas telas a consultam pelo mesmo caminho (UnifiedTaskService.visibilityOf); nenhuma delas carrega lista de papéis própria. O papel do chamador é lido primeiro das authorities do JWT — a única fonte que carrega o SUPER_ADMIN de plataforma, que não tem vínculo no tenant — e depois do platformRole do vínculo.
O filtro vale para a listagem, para o detalhe, para o diagrama e para a busca por variável — uma busca que enxergasse além da lista transformaria o filtro em formalidade.
Endpoints de Anexos
Anexos de instância de processo (entityType = PROCESS_INSTANCE) não são governados pelo papel de plataforma, e sim pelo papel do usuário naquela definição, conforme a política configurada em /a/definitions/{key}/config. Administradores passam por todas as verificações. Demais tipos de anexo — base de conhecimento, knowledge de processo, anexos globais — seguem as regras dos seus próprios módulos e não são afetados.
| Endpoint | Método | Acesso | Descrição |
|---|---|---|---|
/a/attachments | POST | Conforme uploadMode + uploadRoles | Upload. Recusado em FORM_ONLY para PROCESS_INSTANCE. |
/a/attachments | GET | Membro do tenant | Lista os anexos vigentes da entidade (substituídos ficam de fora). |
/a/attachments/capabilities | GET | Membro do tenant | O que o usuário atual pode fazer: canUpload, canPreview, canReplace. É o que a UI consulta em vez de replicar a regra. |
/a/attachments/{id} | GET | previewRoles | Metadados do anexo. |
/a/attachments/{id}/download | GET | previewRoles | Conteúdo do arquivo. |
/a/attachments/{id}/replace | POST | replaceRoles | Sobe a nova versão e marca a anterior como substituída. |
/a/attachments/{id}/history | GET | previewRoles | Versões substituídas da entidade. |
/a/attachments/{id} | DELETE | Ninguém, para PROCESS_INSTANCE | Recusado inclusive para administradores. Continua valendo para os demais tipos. |
Lista de papéis vazia não significa "liberado"
Uma lista vazia (previewRoles, replaceRoles, uploadRoles) significa qualquer usuário que tenha algum papel naquela definição. Quem não tem papel nenhum no processo não acessa os anexos dele. Processo sem política configurada se comporta como AD_HOC com essa mesma regra padrão.
Tarefa dentro do processo age como o sistema
A política acima responde "qual pessoa pode abrir este arquivo". Uma tarefa que roda dentro do processo não tem pessoa: numa service task assíncrona a thread do job executor não carrega SecurityContext nenhum. Por isso a tarefa do processo tem acesso sistêmico a tudo dentro do tenant dela — quem desenha o processo é o administrador, e cada passo herda a autoridade dele em vez de tomar emprestada a identidade de quem iniciou a instância.
O acesso sistêmico exige as duas condições ao mesmo tempo: estar dentro de uma chamada de ferramenta do agente e ter um tenant roteado. Se o agente declarar um tenant diferente do que está roteado na conexão, o acesso é recusado e a divergência vai para o log em nível error.
O limite entre tenants não depende dessa verificação. A tabela attachments vive no schema tenant_<slug> e a conexão é roteada pelo TenantAwareDataSource — um agente não alcança o anexo de outro tenant nem se toda a checagem de permissão fosse removida, porque a conexão não enxerga o outro schema.
A exclusão continua recusada para todo mundo, sistema incluído: em PROCESS_INSTANCE a única forma de escrita é a substituição.
Endpoints do CMS
Leitura é aberta a qualquer membro do tenant; escrita exige Gerente ou superior. Em ambos os casos o acesso ainda depende do nível de sigilo da coleção (ver seção 5).
| Endpoint | Método | Acesso | Descrição |
|---|---|---|---|
/a/cms/collections | GET | SA, TA, Gerente, User | Lista apenas as coleções que o solicitante tem clearance para ler. |
/a/cms/collections | POST | SA, TA, Gerente | Cria coleção com seus campos e nível de sigilo. |
/a/cms/collections/{id} | GET | SA, TA, Gerente, User | Detalha a coleção. |
/a/cms/collections/{id} | PUT, DELETE | SA, TA, Gerente | Altera o schema/sigilo ou exclui a coleção. |
/a/cms/collections/{id}/records | GET | SA, TA, Gerente, User | Lista registros paginados. |
/a/cms/collections/{id}/records | POST | SA, TA, Gerente | Cria um registro. |
/a/cms/collections/{id}/records | DELETE | SA, TA, Gerente | Apaga todos os registros da coleção (recarga limpa). |
/a/cms/collections/{id}/records/bulk | POST | SA, TA, Gerente | Carga em lote idempotente de tabelas de referência. |
/a/cms/collections/{id}/records/{rid} | PUT, DELETE | SA, TA, Gerente | Altera ou exclui um registro. |
Chamadas autenticadas por API Key (X-API-Key) escrevem no CMS — é assim que uma tabela de referência é carregada por script —, porém com clearance fixo em Público: uma chave vazada não alcança dado classificado.
A chave ainda precisa do escopo do método: read para os GET da tabela acima, write para os POST, PUT e DELETE. Sem ele a requisição para no filtro com 403, antes de qualquer papel ser avaliado. Detalhe em API REST → Escopos.
Endpoints das Bases de Conhecimento
Diferente do CMS, leitura também exige Gerente, Modelador ou superior: a listagem das bases e dos documentos mostra o que a IA consulta, e isso é curadoria, não conteúdo de consumo. Um usuário comum recebe 403 em qualquer linha abaixo.
| Endpoint | Método | Acesso | Descrição |
|---|---|---|---|
/a/knowledge-bases | GET | SA, TA, Gerente, Modelador | Lista as bases do tenant e as globais visíveis a ele. |
/a/knowledge-bases | POST | SA, TA, Gerente, Modelador | Cria uma base. Marcar isGlobal exige Super Admin — o serviço recusa os demais. |
/a/knowledge-bases/{id} | GET | SA, TA, Gerente, Modelador | Detalha a base com a contagem de documentos. |
/a/knowledge-bases/{id} | DELETE | SA, TA, Gerente, Modelador | Exclui a base. Recusa enquanto houver processo vinculado. Base global só pelo Super Admin. |
/a/knowledge-bases/{id}/documents | GET | SA, TA, Gerente, Modelador | Lista os documentos, paginado. |
/a/knowledge-bases/{id}/documents | POST | SA, TA, Gerente, Modelador | Sobe PDF, TXT ou MD; a indexação roda em segundo plano. |
/a/knowledge-bases/{id}/documents/{docId} | DELETE | SA, TA, Gerente, Modelador | Remove o documento e os vetores dele. |
/a/knowledge-bases/process/{key} | GET | SA, TA, Gerente, Modelador | Bases vinculadas àquele processo. |
/a/knowledge-bases/process/{key}/link/{id} | POST | SA, TA, Gerente, Modelador | Vincula a base ao processo. |
/a/knowledge-bases/process/{key}/unlink/{id} | DELETE | SA, TA, Gerente, Modelador | Desfaz o vínculo. |
A base de conhecimento por processo — a que a aba RAG do catálogo alimenta — é a mesma capacidade por outro caminho, e por isso exige o mesmo papel. Fechar /a/knowledge-bases sem fechar estas rotas deixaria um usuário comum apagando por aqui o acervo que não pode tocar por lá.
| Endpoint | Método | Acesso | Descrição |
|---|---|---|---|
/a/definitions/{key}/knowledge/documents | GET | SA, TA, Gerente, Modelador | Lista os documentos indexados para aquele processo. |
/a/definitions/{key}/knowledge/documents | POST | SA, TA, Gerente, Modelador | Sobe documento e dispara a indexação. |
/a/definitions/{key}/knowledge/documents/{docId} | DELETE | SA, TA, Gerente, Modelador | Remove o documento e os vetores dele. |
/a/definitions/{key}/knowledge/count | GET | SA, TA, Gerente, Modelador | Quantidade de chunks indexados para o processo. |
/a/definitions/{key}/knowledge | DELETE | SA, TA, Gerente, Modelador | Apaga todos os vetores do processo. Operação destrutiva. |
/a/definitions/{key}/knowledge/reindex | POST | SA, TA, Gerente, Modelador | Limpa e reindexa todos os arquivos anexados. |
Uma chave de API não alcança essas rotas: ela não tem associação de tenant, e a verificação de papel exige uma associação.
Endpoints dos Tipos Documentais
Um tipo documental é referenciado pelo campo Arquivo do formulário, mas quem preenche o formulário nunca chama estas rotas: o documentTypeId já vem gravado no schema publicado e segue direto para /a/attachments. Por isso a leitura fica no Gerente ou no Modelador — é o seletor do construtor de formulários — enquanto manter o catálogo é do Modelador, que é quem desenha o formulário que aponta para o tipo.
| Endpoint | Método | Acesso | Descrição |
|---|---|---|---|
/a/document-types | GET | SA, TA, Gerente, Modelador | Lista os tipos do tenant, ou os globais de um processo com ?isGlobal=true&processKey=. A leitura do global continua aberta ao tenant: são os tipos do processo que ele assina. |
/a/document-types | POST | SA, TA, Modelador | Cria um tipo do tenant. Com ?isGlobal=true, só SA. |
/a/document-types/{id} | PUT | SA, TA, Modelador | Altera nome, descrição ou situação. Se o tipo for global, só SA. |
/a/document-types/{id} | DELETE | SA, TA, Modelador | Exclui o tipo. Formulário publicado que aponte para ele perde a referência. Se o tipo for global, só SA. |
A camada global não é escrita pelo Admin de um tenant
isGlobal=true grava na camada compartilhada: o tipo passa a valer para todos os tenants que assinam o processo. A aba que autora esses tipos vive em Templates Globais, que na interface é Super Admin, mas a API aceitava a flag de qualquer Admin de tenant — e um Admin de tenant só responde pelo próprio. Agora POST ?isGlobal=true, e o PUT/DELETE de um tipo que já é global, respondem 403 para quem não é Super Admin. É o mesmo desenho que já protegia base de conhecimento global (KnowledgeBaseService.createKnowledgeBase) e agente global (AiAgentService.resolveTenant), e os três casos estão no mesmo teste, GlobalLayerIsNotOpenTest.
Endpoints de Robôs
Um robô é código que o tenant publica e a plataforma executa com credenciais do tenant. Por isso todas as rotas de /a/robots/** exigem Admin do tenant — cadastro, envio do JAR, execução, estado, log e também a configuração de parâmetros, que é onde entra o segredo: um parâmetro marcado como secreto guarda a referência (${var.erpToken}) para a variável do tenant que carrega o token. Ler a configuração já nomeia os parâmetros declarados e mostra para onde cada referência aponta, então leitura e escrita ficam no mesmo papel — Gerente e usuário comum recebem 403 nas duas. A tela /robots/$key, única consumidora dessas rotas, já exigia Admin do tenant no beforeLoad; a rota é que estava aberta.
| Endpoint | Método | Contexto | Acesso | Descrição |
|---|---|---|---|---|
/a/robots/{key}/config | GET | Tenant | SA, TA | Configuração de parâmetros do robô naquele tenant, com os parâmetros declarados pelo JAR. |
/a/robots/{key}/config | PUT | Tenant | SA, TA | Grava a configuração de parâmetros. É por aqui que entra a referência de um parâmetro secreto. |
Travado por RobotConfigAuthorizationTest.
Condução de um caso (CMMN)
O plano do caso é conduzido por quem trabalha nele, e fechar o estágio é a única ação de gestão.
| Endpoint | Método | Contexto | Acesso | Descrição |
|---|---|---|---|---|
/a/instances/{id}/plan-items | GET | Tenant | TA, Gerente, User | Etapas vivas do caso, cada uma com as ações que aceita. COMPLETE_STAGE só é oferecida a quem gerencia o tenant. |
/a/instances/{id}/milestones | GET | Tenant | TA, Gerente, User | Marcos que o caso já atingiu. |
/a/instances/{id}/plan-items/{planItemId}/start | POST | Tenant | TA, Gerente, User | Inicia a etapa oferecida. |
/a/instances/{id}/plan-items/{planItemId}/disable | POST | Tenant | TA, Gerente, User | Descarta a etapa opcional. |
/a/instances/{id}/plan-items/{planItemId}/enable | POST | Tenant | TA, Gerente, User | Reoferece a etapa descartada. |
/a/instances/{id}/plan-items/{planItemId}/trigger | POST | Tenant | TA, Gerente, User | Dispara um evento de usuário aguardando. |
/a/instances/{id}/plan-items/{planItemId}/complete-stage | POST | Tenant | SA, TA, Gerente | Fecha o estágio, encerrando as etapas opcionais ainda oferecidas dentro dele. Usuário comum recebe 403. |
As seis primeiras linhas não são "abertas a qualquer autenticado": todas passam por WorkflowInstanceService.requireVisible, o mesmo portão do detalhe da instância, e um caso de que a pessoa não participa responde 404. Travado por CasePlanItemControllerAuthorizationTest e CasePlanItemServiceTest.
Endpoints de IA do modelador
| Endpoint | Método | Contexto | Acesso | Descrição |
|---|---|---|---|---|
/a/ai-validation/bpmn | POST | Tenant | SA, TA, Modelador | Validação semântica do BPMN por LLM. Gerente e usuário comum recebem 403: as telas que disparam a chamada (/definitions, /definitions/{key}, /admin/global-templates/{key}) são do desenho do processo, e cada chamada gasta a cota de tokens do tenant. Cota estourada ou nunca configurada responde 402, não um parecer de validação. |
/a/modeler/copilot/stream | POST | Tenant | SA, TA, Modelador | Copiloto do modelador por SSE. A linha gravada em ai_usage_logs carrega o user_id de quem dirigiu o turno, além do tenant e do executionCorrelationId do turno — sem isso a governança saberia que o tenant gastou, mas não quem. |
Rotas abertas de propósito
Ausência de @PreAuthorize sob /a/** significa "qualquer usuário autenticado do tenant". Nas quatro linhas abaixo isso é decisão, não esquecimento — fechá-las quebraria uma tela que o usuário comum usa. Cada uma tem o limite ao lado da promessa, e cada limite tem teste que o trava.
| Endpoint | Método | Por que fica aberta | Onde está o limite |
|---|---|---|---|
/a/attachments, /a/attachments/{id}/replace, /a/attachments/{id} | POST, DELETE | O usuário comum anexa arquivo pelo formulário da própria tarefa — é o caminho entityType=process, que não é governado. Exigir Admin na rota tiraria o anexo de dentro do processo. | Dentro do serviço, por papel de processo: AttachmentAccessService.requireUpload/requireReplace/requireDeletable, conforme uploadRoles/replaceRoles da política do processo (ver Endpoints de Anexos acima). Travado por AttachmentControllerAuthorizationTest e AttachmentAccessServiceTest. |
/a/notifications, /a/notifications/{id}/read, /a/notifications/read-all | GET, PUT | Todo usuário tem caixa de notificação; papel nenhum se aplica. | O userId vem do token, nunca do corpo ou da URL. NotificationService.markRead recusa quando a notificação é de outro dono, e markAllRead é um UPDATE ... WHERE user_id = :userId. Passar o id de uma notificação alheia responde 403 e não grava nada — recusa de acesso, e não erro de requisição, para que quem chama distinga "id que não existe" (404) de "não é sua" (403). Travado por NotificationControllerAuthorizationTest e NotificationServiceTest. |
/a/dashboard | GET | É a tela inicial de qualquer membro do tenant. | O tenant e a visibilidade de quem olha. Tarefas ativas e processos concluídos passam por UnifiedTaskService.visibilityOf — o mesmo ponto de entrada de Tarefas e Instâncias: quem enxerga o tenant inteiro lê o total, e os demais leem só o que participam. O contador de definições segue sendo do tenant, porque iniciar processo continua aberto ao usuário comum. Travado por DashboardFollowsInstanceVisibilityTest e DashboardCountsOnPostgresTest. |
/a/instances/{id}/plan-items/{planItemId}/start, /disable, /enable, /trigger | POST | Conduzir a própria etapa é o trabalho de quem trabalha no caso, e a tela do caso não tem beforeLoad de papel. Exigir Gerente tiraria do usuário comum o caso que ele mesmo iniciou. | CasePlanItemService chama WorkflowInstanceService.requireVisible antes de qualquer transição: caso de que a pessoa não participa responde 404. O motor recusa a transição que o estado não permite, e availableActions só esconde COMPLETE_STAGE. Travado por CasePlanItemControllerAuthorizationTest e CasePlanItemServiceTest. |
O cartão do dashboard conta o que a pessoa enxerga
GET /a/dashboard já não devolve os mesmos números para todo mundo. Tarefas ativas e processos concluídos são contados pela mesma regra que a listagem aplica: o serviço lê UnifiedTaskService.visibilityOf(tenant, usuário) — o único lugar onde "quem enxerga o tenant inteiro" está escrito, e o mesmo que InstanceVisibilityService consulta. Gerente, Admin e Super Admin continuam lendo o total do tenant; o usuário comum conta só as tarefas em que é responsável ou candidato (direto ou por grupo de papel de processo) e só os processos concluídos em que teve tarefa ou que iniciou.
O escopo é aplicado como predicado da própria contagem, não materializando a lista de ids visíveis: DashboardCountRepository mantém a promessa de duas consultas por carga, quantas instâncias o tenant tenha. O contador de definições segue sendo do tenant inteiro de propósito — é o catálogo do que se pode iniciar, e POST /a/instances continua aberto ao usuário comum.
4. Hierarchy Resolution para Business Keys (Nota para IA)
O backend resolve variáveis de inicialização como o Business Key numa ordem hierárquica (Implementado em WorkflowDefinitionService):
- Chave explícita na requisição: um
businessKeyenviado emPOST /a/instancesvence o template. O template é o padrão para quem não conhece a chave, não uma imposição sobre quem conhece. Valor ausente ou em branco cai para o template. - Template do tenant (
PublishedDefinition.businessKeyTemplate): O Tenant Admin o grava em/a/definitions/{key}/config. - Padrão do Sistema: fallback hardcoded para
DOC-${date:yyyyMMdd}-${random:4}(WorkflowDefinitionService.DEFAULT_BUSINESS_KEY_TEMPLATE).
São só esses dois degraus. Não existe camada global de Business Key: GlobalProcessTemplate não tem campo de template, e resolveBusinessKeyTemplate pula do template do tenant em branco direto para o padrão do sistema.
O template aceita ${nome:-alternativa}, que usa a variável de processo nome quando ela existe e não está em branco, e a alternativa caso contrário. A alternativa pode conter outro placeholder, então um mesmo template serve uma integração que conhece os dados e um formulário que não conhece:
${tipoDoc:-DOC}-${emissao:-${date:yyyyMMdd}}-${cnpjEmissor:-${random:4}}
API, com as variáveis → NFSE-20260305-22060673000170
Tela, sem as variáveis → DOC-20260807-9O63Placeholder sem alternativa e sem variável correspondente recusa o início. A requisição responde 422 Unprocessable Entity, e a instância não chega a ser criada — nada de tarefa, nada de histórico, nada para cancelar depois. O corpo do erro nomeia cada variável que faltou em fieldErrors, então o cliente sabe exatamente o que enviar na segunda tentativa:
{
"status": 422,
"error": "Unprocessable Entity",
"fieldErrors": [
{ "field": "cnpjEmissor", "message": "variável exigida pelo businessKeyTemplate e não informada" }
]
}A mesma recusa vale quando a substituição esvazia a chave por completo — um template que resolveria para NFSE--, só separadores e nenhum conteúdo, é rejeitado em vez de gravado. Chave de negócio é como o documento é encontrado depois; uma chave truncada cria um registro que ninguém acha, e o 422 troca esse problema silencioso por um erro na hora.
Quem depende de iniciar o processo mesmo sem todas as variáveis tem duas saídas: dar uma alternativa ao placeholder (${cnpjEmissor:-${random:4}}) ou mandar o businessKey explícito no corpo do POST /a/instances, que vence o template.
5. Sigilo dos Dados do CMS
Cada coleção do CMS carrega um nível de confidencialidade. O nível exigido para ler ou escrever uma coleção é derivado do papel do usuário no tenant — não existe concessão individual.
| Nível | Papel mínimo | Conteúdo típico |
|---|---|---|
PUBLIC | Tenant User | NCM, CFOP, CEST, centros de custo, catálogo de produtos |
SECRET | Tenant Manager | Margens, política comercial, tabela de preços de fornecedor |
TOP_SECRET | Tenant Admin | Folha de pagamento, jurídico, material de diretoria |
Regras derivadas:
USER → PUBLIC,MODELER → PUBLIC,MANAGER → SECRET,ADMIN → TOP_SECRET,SUPER_ADMIN → TOP_SECRET. O modelador fica emPUBLICde propósito: o CMS é conteúdo do tenant, não desenho de processo, eCmsControllerexige Gerente — dar-lhe outro nível seria uma promessa que rota nenhuma cumpre.- A listagem de coleções omite o que o solicitante não pode ler; o acesso direto responde
403. - Um gerente não consegue classificar uma coleção como
TOP_SECRET— isso o faria criar um dado fora do próprio alcance. - Agentes de IA: a tool
queryCollectionToolrespeita o nível configurado em cada agente (cmsClearance, padrãoPUBLIC). Em processos BPMN não há usuário logado para herdar permissão, por isso o nível é uma configuração explícita do agente. No playground vale o menor entre o nível do usuário e o do agente (AiPlaygroundControllerchamaCmsClassification.lowest). O copiloto de execução não faz essa conta:AiExecutionCopilotControllerusa apenascmsAccessService.currentClearance(), então lá o teto é o papel do usuário, não o do agente. - Robôs (
RobotCmsApi) não são limitados por sigilo: são código publicado por administradores do tenant e alcançam qualquer coleção dele.
Referência de implementação: security/CmsAccessService.java, security/TenantSecurityService.java e a spec specs/content/cms.md.