Skip to content

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 && currentTenant?.role === 'ADMIN'
  • Identificação Backend: @tenantSec.isCurrentTenantAdmin(authentication) nos recursos /a/*. Não alcança nada em /api/admin/** — o SecurityConfig exige SUPER_ADMIN no 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_ADMIN only.

B.1. Tenant Modeler (Modelador do Tenant)

  • Identificação Frontend: currentTenant?.role === 'MODELER', resolvido por reaches() em src/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 é ADMIN menos 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 USER vê. /monitor continua 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) — aceita MANAGER e ADMIN com vínculo (UserTenantMembership) no tenant corrente; SUPER_ADMIN passa 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}/monitor e /monitor/{activityId}) e a escrita da documentação do processo (PUT e DELETE em /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) — aceita MODELER e ADMIN com vínculo (UserTenantMembership) no tenant corrente; SUPER_ADMIN passa 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 é ADMIN menos 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. MODELER não entra em PlatformRole.seesEverythingInTenant(): em execução ele enxerga o que um USER enxerga. Cancelar, suspender ou reativar instância, ver a fila de jobs com erro e acompanhar /monitor continuam 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 / PathMódulo / FunçãoSuper AdminTenant AdminModeladorTenant User
/dashboardVisão geral, gráficos e contadores.❌ (Usa admin panel)
/admin/tenantsListagem e criação global de tenants.
/admin/usersListagem global de usuários.
/admin/ai-configConfiguração de IA, em duas abas: Catálogo de Modelos LLM e Banco de Vetores (RAG).
/definitionsDefiniçõ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-templatesTemplates Globais
- Para SA: Lista tudo, permite criar BPMN/CMMN/DMN e configurar Tipos Documentais, Variáveis e Anexos globais.
/admin/process-rolesCatálogo centralizado de Papéis de Processo.
/admin/global-templates/$keyDetalhes do template e abas de configuração avançada.
/instancesListagem 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)
/tasksCaixa de entrada e execução de tarefas humanas.
/cmsGerenciamento 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-basesBases 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.
/monitorAcompanhamento por etapa: diagrama do processo com a carga parada em cada atividade. Gerente ou superior.
/admin/document-typesCatá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.
/decisionsTabelas 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.
/robotsAutomaçã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-keysChaves de API programáticas por tenant.
/admin/webhooksCallbacks configurados por tenant.
/admin/tenants/$idGestã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-inboxesCaixas 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-deliveriesToda 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

  1. /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_ADMIN only (ver a tabela de administração em API REST).
    • Operam sem tenant selecionado, cross-tenant onde aplicável.
  2. /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 @PreAuthorize de cada controller, e são quatro predicados de tenant: isCurrentTenantAdmin (só ADMIN), isCurrentTenantManager (MANAGER ou ADMIN), isCurrentTenantModeler (MODELER ou ADMIN) e a ausência deliberada de anotação, listada em Rotas abertas de propósito. SUPER_ADMIN passa em todos. Rota nova sem decisão de papel reprova em TenantEndpointDeclaresItsRoleTest.

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.

EndpointMétodoContextoAcessoDescrição
/a/definitionsGETTenantTA, Gerente, UserRetorna o catálogo de processos ativos para o tenant. Basta estar autenticado.
/a/definitions/deployPOSTTenantTA, ModeladorDeploy do arquivo BPMN/CMMN (multipart). Não publica a versão.
/a/definitions/{key}/versionsGETTenantTA, ModeladorHistórico de versões; a publicada vem com published: true.
/a/definitions/{key}/versions/{definitionId}/publishPOSTTenantTA, ModeladorMove a publicação para a versão indicada.
/a/definitions/{key}/configPUTTenantTA, ModeladorConfiguração específica do processo no tenant (Business Key Template e política de anexos).
/a/definitions/{key}/attachment-policyGETTenantTA, ModeladorPolí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-policyGETTenantTA, ModeladorO 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/delegatesGETTenantTA, ModeladorBeans 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/activeGETTenantTAJARs de delegate globais ativos. Já exigia papel antes da #115.
/a/definitions/{key}/monitorGETTenantTA, GerenteCarga parada por etapa do processo, com a quebra por motivo. Aceita ?version=.
/a/definitions/{key}/monitor/{activityId}GETTenantTA, GerenteO que está parado naquela etapa: instância, motivo, responsável ou erro. Aceita ?version=.
/a/definitions/{key}/togglePATCHTenantTA, ModeladorHabilita/Desabilita o processo no catálogo do tenant.
/a/definitions/{key}/suspendPUTTenantTA, ModeladorSuspende a definição: nenhuma instância nova começa. É diferente de desabilitar no catálogo.
/a/definitions/{key}/activatePUTTenantTA, ModeladorReativa a definição suspensa.
/a/instancesPOSTTenantTA, UserInicia uma instância de workflow em nome do tenant logado. Gera o Business Key de acordo com fallback hierarchy.
/a/instances/searchGETTenantTA, UserBusca instâncias por variável. Para usuário comum, procura apenas dentro das instâncias que ele enxerga.
/a/instances/{id}DELETETenantTA, GerenteCancela uma instância. Operação destrutiva — não é oferecida a usuário comum, e o backend recusa.
/a/instances/{id}/suspendPATCHTenantTA, GerenteSuspende a instância.
/a/instances/{id}/activatePATCHTenantTA, GerenteReativa a instância suspensa.
/a/instances/failed-jobsGETTenantTA, GerenteFila de jobs com erro do tenant.
/a/instances/failed-jobs/{jobId}/retryPOSTTenantTA, GerenteReexecuta o job.
/a/instances/failed-jobs/{jobId}/deadletterPOSTTenantTA, GerenteMove o job para a deadletter.
/a/instances/{id}GETTenantTA, UserDetalhe 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/instancesGETTenantTA, UserLista 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.

EndpointMétodoAcessoDescrição
/a/attachmentsPOSTConforme uploadMode + uploadRolesUpload. Recusado em FORM_ONLY para PROCESS_INSTANCE.
/a/attachmentsGETMembro do tenantLista os anexos vigentes da entidade (substituídos ficam de fora).
/a/attachments/capabilitiesGETMembro do tenantO que o usuário atual pode fazer: canUpload, canPreview, canReplace. É o que a UI consulta em vez de replicar a regra.
/a/attachments/{id}GETpreviewRolesMetadados do anexo.
/a/attachments/{id}/downloadGETpreviewRolesConteúdo do arquivo.
/a/attachments/{id}/replacePOSTreplaceRolesSobe a nova versão e marca a anterior como substituída.
/a/attachments/{id}/historyGETpreviewRolesVersões substituídas da entidade.
/a/attachments/{id}DELETENinguém, para PROCESS_INSTANCERecusado 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).

EndpointMétodoAcessoDescrição
/a/cms/collectionsGETSA, TA, Gerente, UserLista apenas as coleções que o solicitante tem clearance para ler.
/a/cms/collectionsPOSTSA, TA, GerenteCria coleção com seus campos e nível de sigilo.
/a/cms/collections/{id}GETSA, TA, Gerente, UserDetalha a coleção.
/a/cms/collections/{id}PUT, DELETESA, TA, GerenteAltera o schema/sigilo ou exclui a coleção.
/a/cms/collections/{id}/recordsGETSA, TA, Gerente, UserLista registros paginados.
/a/cms/collections/{id}/recordsPOSTSA, TA, GerenteCria um registro.
/a/cms/collections/{id}/recordsDELETESA, TA, GerenteApaga todos os registros da coleção (recarga limpa).
/a/cms/collections/{id}/records/bulkPOSTSA, TA, GerenteCarga em lote idempotente de tabelas de referência.
/a/cms/collections/{id}/records/{rid}PUT, DELETESA, TA, GerenteAltera 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.

EndpointMétodoAcessoDescrição
/a/knowledge-basesGETSA, TA, Gerente, ModeladorLista as bases do tenant e as globais visíveis a ele.
/a/knowledge-basesPOSTSA, TA, Gerente, ModeladorCria uma base. Marcar isGlobal exige Super Admin — o serviço recusa os demais.
/a/knowledge-bases/{id}GETSA, TA, Gerente, ModeladorDetalha a base com a contagem de documentos.
/a/knowledge-bases/{id}DELETESA, TA, Gerente, ModeladorExclui a base. Recusa enquanto houver processo vinculado. Base global só pelo Super Admin.
/a/knowledge-bases/{id}/documentsGETSA, TA, Gerente, ModeladorLista os documentos, paginado.
/a/knowledge-bases/{id}/documentsPOSTSA, TA, Gerente, ModeladorSobe PDF, TXT ou MD; a indexação roda em segundo plano.
/a/knowledge-bases/{id}/documents/{docId}DELETESA, TA, Gerente, ModeladorRemove o documento e os vetores dele.
/a/knowledge-bases/process/{key}GETSA, TA, Gerente, ModeladorBases vinculadas àquele processo.
/a/knowledge-bases/process/{key}/link/{id}POSTSA, TA, Gerente, ModeladorVincula a base ao processo.
/a/knowledge-bases/process/{key}/unlink/{id}DELETESA, TA, Gerente, ModeladorDesfaz 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á.

EndpointMétodoAcessoDescrição
/a/definitions/{key}/knowledge/documentsGETSA, TA, Gerente, ModeladorLista os documentos indexados para aquele processo.
/a/definitions/{key}/knowledge/documentsPOSTSA, TA, Gerente, ModeladorSobe documento e dispara a indexação.
/a/definitions/{key}/knowledge/documents/{docId}DELETESA, TA, Gerente, ModeladorRemove o documento e os vetores dele.
/a/definitions/{key}/knowledge/countGETSA, TA, Gerente, ModeladorQuantidade de chunks indexados para o processo.
/a/definitions/{key}/knowledgeDELETESA, TA, Gerente, ModeladorApaga todos os vetores do processo. Operação destrutiva.
/a/definitions/{key}/knowledge/reindexPOSTSA, TA, Gerente, ModeladorLimpa 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.

EndpointMétodoAcessoDescrição
/a/document-typesGETSA, TA, Gerente, ModeladorLista 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-typesPOSTSA, TA, ModeladorCria um tipo do tenant. Com ?isGlobal=true, só SA.
/a/document-types/{id}PUTSA, TA, ModeladorAltera nome, descrição ou situação. Se o tipo for global, só SA.
/a/document-types/{id}DELETESA, TA, ModeladorExclui 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.

EndpointMétodoContextoAcessoDescrição
/a/robots/{key}/configGETTenantSA, TAConfiguração de parâmetros do robô naquele tenant, com os parâmetros declarados pelo JAR.
/a/robots/{key}/configPUTTenantSA, TAGrava 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.

EndpointMétodoContextoAcessoDescrição
/a/instances/{id}/plan-itemsGETTenantTA, Gerente, UserEtapas vivas do caso, cada uma com as ações que aceita. COMPLETE_STAGE só é oferecida a quem gerencia o tenant.
/a/instances/{id}/milestonesGETTenantTA, Gerente, UserMarcos que o caso já atingiu.
/a/instances/{id}/plan-items/{planItemId}/startPOSTTenantTA, Gerente, UserInicia a etapa oferecida.
/a/instances/{id}/plan-items/{planItemId}/disablePOSTTenantTA, Gerente, UserDescarta a etapa opcional.
/a/instances/{id}/plan-items/{planItemId}/enablePOSTTenantTA, Gerente, UserReoferece a etapa descartada.
/a/instances/{id}/plan-items/{planItemId}/triggerPOSTTenantTA, Gerente, UserDispara um evento de usuário aguardando.
/a/instances/{id}/plan-items/{planItemId}/complete-stagePOSTTenantSA, TA, GerenteFecha 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

EndpointMétodoContextoAcessoDescrição
/a/ai-validation/bpmnPOSTTenantSA, TA, ModeladorValidaçã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/streamPOSTTenantSA, TA, ModeladorCopiloto 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.

EndpointMétodoPor que fica abertaOnde está o limite
/a/attachments, /a/attachments/{id}/replace, /a/attachments/{id}POST, DELETEO 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-allGET, PUTTodo 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/dashboardGETÉ 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, /triggerPOSTConduzir 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):

  1. Chave explícita na requisição: um businessKey enviado em POST /a/instances vence 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.
  2. Template do tenant (PublishedDefinition.businessKeyTemplate): O Tenant Admin o grava em /a/definitions/{key}/config.
  3. 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-9O63

Placeholder 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:

json
{
  "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ívelPapel mínimoConteúdo típico
PUBLICTenant UserNCM, CFOP, CEST, centros de custo, catálogo de produtos
SECRETTenant ManagerMargens, política comercial, tabela de preços de fornecedor
TOP_SECRETTenant AdminFolha 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 em PUBLIC de propósito: o CMS é conteúdo do tenant, não desenho de processo, e CmsController exige 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 queryCollectionTool respeita o nível configurado em cada agente (cmsClearance, padrão PUBLIC). 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 (AiPlaygroundController chama CmsClassification.lowest). O copiloto de execução não faz essa conta: AiExecutionCopilotController usa apenas cmsAccessService.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.

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