Especificação · Fluxo de Aprovação

Fluxo de Aprovação

Um motor só, para os dez processos que pedem uma segunda pessoa — da requisição ao pedido, do cadastro de fornecedor à alteração bancária, do exercício orçamentário ao remanejamento de verba. A versão anterior tinha configuração sem execução — fluxo, nível e delegação gravados, e nenhuma instância, fila, decisão ou notificação. Esta descreve o motor inteiro, e é escrita antes de existir documento para aprovar, que é a única hora em que ela custa barato.

substitui NEXIO_FluxoAprovacao_*_v1.0 (jun/2026) consome Centro de Custo, Empresa e Estabelecimento e Local de Entrega · decide por Fornecedor, Item e Orçamento 8 agregados de configuração · 4 de execução v2.20 · 09/09/2026

Ponto de partida

Oito decisões

Decisão 1 · Camadas

Duas camadas de regra: base e complementar. A base é a alçada financeira — uma só vence, escolhida por prioridade. As complementares empilham: todas que casam entram na cadeia. Controles de compra são ortogonais — alçada, dono do centro de custo, aprovador de categoria, Jurídico, LGPD, Qualidade — e um motor de regra única força o produto cartesiano dessas dimensões em fluxos configurados à mão.

Decisão 2 · Genérico

O motor não sabe o que é um pedido. Ele conhece um assunto com valor, empresa, requisitante e um conjunto de linhas com nó de centro de custo e nó de categoria. ApprovalProcess é entidade de configuração — mesmo padrão do ItemType — e é o que faz requisição, cotação, contrato e alteração bancária percorrerem o mesmo código. Desde 04/09/2026 vale para toda aprovação humana do Nexio: cadastro e homologação de fornecedor, alteração bancária, curadoria de item, exercício e remanejamento de verba são instâncias deste motor. O motor decide; o módulo dono do documento executa o efeito.

Decisão 3 · Resolução

Cadeia congelada na submissão; pessoas resolvidas na ativação do passo. O snapshot completo deixa a tarefa apontando para quem saiu da empresa; a resolução tardia total impede mostrar a cadeia ao requisitante no momento em que ele submete. O híbrido entrega as duas coisas — e é para onde Oracle e Coupa convergiram.

Decisão 4 · Escopo

O passo tem escopo: documento, linha ou fatia de rateio. É o que permite avaliar a alçada sobre os R$ 40 mil que couberam à área, e não sobre os R$ 400 mil do pedido inteiro. O Protheus dispara pelo total; Coupa e Ariba tratam rateio como dado contábil sem efeito no fluxo.

Decisão 5 · Paralelo

Paralelo é dentro do nível, não entre níveis. Cada nível tem um modo: qualquer um, todos ou quórum de N — e em todos eles uma rejeição encerra a instância. Entre níveis é sempre sequencial. A v1.0 definia o oposto, e nunca leu o campo — trocar a semântica ainda é de graça, e esta é a que o mercado inteiro usa.

Decisão 6 · Silêncio

Nada é aprovado por omissão. SLA vencido notifica ou escalona; nunca aprova. Aprovação automática existe como tipo de nível declarado — "abaixo de R$ 500 com fornecedor homologado" — e jamais como consequência de tempo, nem como chave que deixe o requisitante decidir o próprio pedido. Aprovar por decurso de prazo é a funcionalidade que destrói a validade jurídica de toda a trilha.

Decisão 7 · Alçada

A alçada mora na pessoa, dentro do nó. CostCenterAssignment.approvalLimit. O nível do tipo "sobe até quem tenha alçada" caminha a árvore de centro de custo até encontrar quem cubra o valor. Trezentos centros de custo passam a ser trezentas linhas de vínculo, não trezentos fluxos — e é o modelo que o cliente vindo do Protheus já tem na cabeça.

Decisão 8 · Trilha

Toda decisão é evento, e a instância é reconstruível a partir deles. ApprovalEvent é append-only, com autor, papel pelo qual agiu, delegação em vigor e o passo alcançado. O estado do agregado é projeção, não fonte. Desde v2.17 a decisão tem esquema: tabela de campos, enum fechado de ações, ordenação por sequence e a invariante de imutabilidade escrita — até então esta era a única entidade do acervo declarada fonte da verdade sem nenhum campo definido. Sem isso, "por que este pedido foi aprovado" só tem resposta na cabeça de quem estava lá.

Fundamentos

Sete princípios

1. Configuração e execução são agregados diferentes

Editar uma política não muda documento que não foi reavaliado. A instância guarda o id e a versão de cada política que a formou, e a versão é imutável: alterar uma política publica uma versão nova e aposenta a anterior, que continua respondendo pelas instâncias vivas. É a regra RN-F08 da spec de junho, elevada de detalhe a invariante.

Reescrito em v2.17, e a diferença importa. A formulação antiga — "não pode mudar um documento em andamento" — não convivia com a RN-AP-20, que manda reavaliar quando muda a categoria justamente porque isso "pode acionar controle complementar que não existia". O que a invariante protege é o documento parado: nenhuma edição de política mexe numa cadeia sozinha. A reavaliação é o único caminho pelo qual configuração nova entra numa instância viva, ela é sempre disparada por uma mudança no documento, e recasa contra as políticas vigentes na data da reavaliação (RN-AP-47). Congelar as versões antigas na reavaliação seria pior: a devolução reenviada é o caminho mais comum do produto, e o documento voltaria em regra que o cliente já corrigiu.

2. Quem aprova nunca é quem pede

O requisitante é excluído de toda tarefa da própria instância — inclusive quando é ele o responsável pelo centro de custo. Nesse caso o motor sobe um nível na árvore; se a subida esgotar, cai no aprovador de reserva do processo. Recusar silenciosamente ou deixar o próprio aprovar são os dois erros que auditoria encontra primeiro. Não há parâmetro que desligue esta regra: allowSelfApproval não existe. Tenant em que o requisitante é o único aprovador possível recebe a recusa do princípio 4 — e resolve com um segundo aprovador ou com o aprovador de reserva, não com exceção.

3. Ambiguidade é erro de configuração, nunca desempate silencioso

Duas políticas base com a mesma prioridade e critérios que se sobrepõem são recusadas no momento de publicar, com 409 — e, se ainda assim duas casarem o mesmo documento em execução, a submissão é recusada com 422, nunca desempatada em silêncio. A garantia tem duas camadas: a verificação completa na aplicação, na mesma transação da publicação e serializada por bloqueio de linha na cadeia de ancestrais do nó, e uma guarda parcial no banco, quando o banco a oferecer. O que fica proibido é o que a implementação anterior fazia: varredura em memória, sem lock, que perde a corrida de dois POST simultâneos.

4. Um passo sem aprovador possível é recusa explícita

Não existe passo pulado por falta de gente. Se a resolução devolve vazio depois do rollup, da delegação e do grupo, o motor tenta o aprovador de reserva do processo; se também não houver, a submissão é recusada com 422 nomeando o passo. Pular é como um pedido de R$ 2 milhões passa sem que ninguém perceba.

5. A cadeia é mostrada antes de ser percorrida

O requisitante vê a cadeia inteira ao submeter, e qualquer pessoa com permissão pode simular sem criar documento. Nenhum ERP brasileiro pesquisado tem simulador; no Oracle ele existe atrás de privilégio especial. Aqui a lógica de seleção já é a mesma consulta — expor é quase de graça, e é o que transforma configuração de aprovação de adivinhação em engenharia.

6. O motor consome contratos, não caminha em árvores

Centro de custo publica path, responsáveis efetivos, alçada acumulada e empresa. Catálogo publica path de categoria. Orçamento publica saldo. O motor combina — e por isso pode ser testado sem nenhum dos três.

7. Toda aprovação humana é uma instância do motor

Onde uma segunda pessoa decide sobre o pedido de outra, existe um ApprovalProcess — sem exceção por módulo. O módulo dono do documento submete, guarda o resultado e aplica o efeito (carência bancária, movimento do razão, estado do item); o motor resolve quem, em que ordem, em quanto tempo e com que alçada. O motor decide; o módulo executa. Atos unilaterais com motivo — bloquear fornecedor, promover manualmente, revogar anexo, substituir cotação, forçar duplicata, expurgar arquivo — não são aprovação: quem age é quem decide, e o que os protege é permissão, motivo codificado e trilha. Regra de 04/09/2026, que fecha o achado A8 da contra-análise: até a v2.6 o cadastro e a homologação de fornecedor e a curadoria de item tinham rota de aprovação própria, e a alteração bancária tinha um caminho no motor e outro no módulo.

Modelo

Mapa de entidades

Configuração — o que o cliente parametriza

EntidadeCardinalidadeO que é
ApprovalProcessconfiguração · por tenantQual documento é aprovável. Define o valor avaliado, os critérios disponíveis, as ações permitidas, o aprovador de reserva, o uso de motivo da rejeição e da devolução, as flags que o assunto expõe e se nasce com política padrão semeada. Dez tipos-semente: REQUISITION, QUOTATION, PURCHASE_ORDER, CONTRACT, SUPPLIER_REGISTRATION, SUPPLIER, BANK_ACCOUNT_CHANGE, ITEM, BUDGET, BUDGET_CHANGE.
ApprovalPolicyN por processo · versionadaA regra. kind = Base ou Supplemental, prioridade, critérios, vigência e níveis. Versão imutável.
ApprovalCriteriavalue object da políticaQuinze critérios: faixa de valor, nó de centro de custo, nó de categoria, nó de conta contábil, tipo de item, destinação, empresa, estabelecimento, UF de entrega, fornecedor, flags do assunto, estado do orçamento, desvio do menor preço, faixa de risco do fornecedor e categoria de homologação. Critério ausente casa tudo.
ApprovalLevel1..N por políticaOrdem, tipo de aprovador, alvo, modo dentro do nível, SLA próprio, valor de ativação e rótulo exibido — e o alvo do escalonamento (escalationTargetType · escalationTargetRef), exigido quando o processo escala e este nível não caminha árvore. Novo em v2.10
ApprovalGroupN por tenantPool nomeado de usuários — "Comitê de Investimentos", "Jurídico". Referenciado por nível; o modo do nível decide se basta um.
ApprovalGroupMemberN por grupo · dentro do agregadoQuem está no grupo, com vigência — mesma disciplina do vínculo de centro de custo. A resolução do nível lê os membros vigentes na ativação do passo, e não na submissão: um comitê que muda de composição em outubro decide em outubro com quem está nele. Não é agregado próprio — nasce e morre com o grupo, e por isso a contagem de agregados não muda. Novo em v2.9
CostCenterAssignmentdo Centro de CustoOnde a alçada da pessoa mora (approvalLimit). O motor lê; não escreve.
ApprovalDelegationN por usuário · uma vigente por processoAusência temporária. Uma linha por processo — scope nulo significa "todos" e conflita com qualquer outra —, cada uma com seu delegado, seu teto de valor e seu período, de fim obrigatório. Novo em v2.10
ApprovalProcessSettings1 por tenant × processoOs parâmetros que variam por processo: dedupe, tolerância de reavaliação, SLA padrão, comportamento de escalonamento, aprovador de reserva e uso de motivo. Chamava-se ApprovalSettings até v2.16, e foi renomeada quando nasceu a configuração de tenant — cada nome passou a declarar a própria chave. Não há chave de autoaprovação — ver RN-AP-08.
ApprovalSettings1 por tenantNova em v2.16. Os parâmetros do motor que não variam por processo: o limiar de materialidade do rateio (RN-AP-15), o teto de duração da delegação (RN-AP-36) e o expediente com a lista de dias não úteis (RN-AP-13). Uma linha por tenant, criada no provisionamento com os padrões do produto. Fecha a pendência "configuração por tenant do Nexio" do lado do fluxo, no padrão do CurrencySettings.

Execução — o que o motor escreve

EntidadeCardinalidadeO que é
ApprovalRequestaggregate root · 1 por documento vivoA instância. subjectType + subjectId, empresa, requisitante, valor avaliado, moeda, estado, e o snapshot das políticas que a formaram com suas versões. Mais três colunas em v2.17: roundNumber (int, a rodada corrente — sobe na devolução reenviada e na reabertura, RN-AP-43), version (int, o token de concorrência da raiz por onde toda decisão serializa, RN-AP-44) e budgetRequirementUnmet (RN-AP-42). É dona de anexo desde 09/09/2026 (ownerType = ApprovalRequest): da evidência que o aprovador junta depois da submissão — parecer, cotação de terceiro, autorização por fora —, já que o documento em aprovação não aceita mais alteração; o documento que sustenta a proposta antes da decisão continua sendo do agregado que a propôs. CanWrite é o aprovador pendente da rodada corrente, e mais ninguém. As onze respostas do descritor estão declaradas na página do Anexo, em O dono polimórfico — este módulo implementa, não redecide.
ApprovalStep1..N por instânciaUm nível resolvido. Ordem, origem (qual política e nível), escopo, modo, SLA, estado — e a marca authorityExceeded quando a alçada esgotou.
ApprovalAssignment1..N por passoA tarefa de uma pessoa. Origem da atribuição, estado, decisão, comentário, momento. É a entidade que a spec de junho não tinha — e sem ela não existe fila endereçável, aprovação por papel, delegação registrada nem paralelo dentro do nível.
ApprovalEventappend-only · dentro do agregado da instânciaToda transição, com autor, ação, alvo e metadados. Fonte da verdade da trilha. Ganhou esquema em v2.17: tabela de campos, enum fechado de ações, chave de ordenação (approvalRequestId, sequence) e a invariante de imutabilidade — ver Instância, passo e tarefa (RN-AP-48). É ele que a GET /approvals sustenta, e é dele que sai o relatório de aprovação que o Motivo delega a este módulo.
ApprovalTokenopcional · fase 2Link assinado para aprovar por e-mail sem sessão. De uso único, com expiração, vinculado a um ApprovalAssignment.

Os três níveis da execução não são zelo arquitetural. Instância sem passo não sabe onde está; passo sem tarefa não sabe de quem está esperando. Colapsar tarefa em passo — que é o desenho da spec de junho — obriga a resolver "quem pode agir" em toda leitura de fila, transforma a consulta do aprovador numa varredura, e não deixa onde registrar que Ana agiu como substituta de Bruno na delegação nº 12.

Mecânica

Da submissão à decisão

documento alterado acima da tolerância · reavalia ApprovalRequest submissão PolicyMatcher base + complementares ApprovalStep[] cadeia congelada ApproverResolver na ativação do passo ApprovalAssignment a fila de cada pessoa ApprovalEvent decisão registrada valor · empresa · requisitante path do CC · path da categoria saldo de orçamento critérios effectiveOwners · rollup ApprovalGroup ApprovalDelegation vigente approvalLimit do vínculo quem Approved · Rejected Returned — volta ao requisitante cada um é um evento próximo passo
serviço de domínio entidade persistida entrada consumida de outro domínio resultado
O casador roda uma vez por submissão e uma vez por reavaliação; o resolvedor roda uma vez por passo ativado. Essa separação é a decisão 3 desenhada: o que a política decidiu fica congelado, e só a pergunta "quem, agora" é refeita.

Configuração

Processos aprováveis

ApprovalProcess é o que torna o motor genérico. Cada processo declara o que significa "valor" naquele documento, quais critérios fazem sentido e o que acontece quando a cadeia termina.

Processos-semente
ProcessoValor avaliadoCritérios que fazem sentidoEfeito da aprovaçãoPolítica padrão semeada
REQUISITIONTotal estimado, por fatia de rateiovalor, centro de custo, categoria, conta contábil, tipo de item, destinação, orçamentoLibera para cotação ou para pedido— · o tenant configura (RN-AP-03)
QUOTATIONValor da proposta escolhidavalor, categoria, fornecedor, desvio do menor preçoLibera a emissão do pedido—
PURCHASE_ORDERTotal do pedido, por fatiavalor, centro de custo, categoria, conta contábil, fornecedor, empresa, orçamentoAutoriza o envio ao fornecedor—
CONTRACTValor total comprometidovalor, categoria, fornecedor. Prazo e cláusula de exclusividade: a definir na spec do ContractColoca o contrato em vigência—
SUPPLIER_REGISTRATION—fornecedor (tipo de pessoa é flag do assunto)Cadastro passa a Approved; o relacionamento continua ProspectPermissionHolder(approve:suppliers) · Any
SUPPLIER—categoria de homologação, faixa de risco, fornecedor (sancionado)Grava o resultado da homologação e promove a SpendAuthorizedPermissionHolder(manage:supplier-qualifications) · Any; recomendada: Panel de quatro grupos — jurídico, financeiro, técnico, qualidade
BANK_ACCOUNT_CHANGEMédia mensal de compras ao fornecedor nos últimos 12 meses; zero para fornecedor novovalor, fornecedor, flags do assunto (titular ≠ fornecedor, chave PIX não verificada)Aplica a proposta ao registro vivo e inicia a carênciaPermissionHolder(approve:supplier-banks) · Any, mais um nível Role de controladoria quando a flag titular ≠ fornecedor estiver presente; mais um degrau incondicional de Quorum(2) sobre a mesma permissão, sem activationMinValue — quatro olhos, e um "não" basta. O degrau é incondicional de propósito (v2.17): o valor avaliado deste processo é a média mensal de compras, e enquanto PurchaseOrder e Invoice não existirem — que é o horizonte do MVP — todo fornecedor avalia zero. Uma política recomendada por faixa ("Quorum(2) acima de R$ 100 mil") nunca casaria, toda troca de conta cairia no PermissionHolder · Any — uma assinatura só — e o controle nasceria degradado funcionando, que é o pior modo de falha que existe. O cálculo da média fica como gancho nomeado: quem o produz é o módulo do documento de compra, sobre o valor aprovado, na moeda funcional da empresa, e até ele existir o degrau não depende dele. Não escala e não é delegável (allowEscalation e delegable falsos na semente): o decisor deste processo não muda por decurso de prazo nem por férias
ITEM—categoria, tipo de item, flags do assunto (regulado, dado pessoal, crítico)Item passa a Approved; a rejeição grava o motivo no itemPermissionHolder(approve:catalog-items) · Any
BUDGETTotal orçado do exercíciovalor, empresaPublica o exercício e arquiva a versão substituída—
BUDGET_CHANGEDelta da verbavalor, centro de custo, conta contábilAplica o remanejamento—

Quatro desses processos não têm valor, e é por isso que eles precisam existir na lista. Cadastro e homologação de fornecedor, alteração de dado bancário e curadoria de item são aprovações sem dinheiro — e a alteração bancária é o controle antifraude mais importante de um sistema de compras, porque é o vetor de fraude mais explorado no Brasil. Um motor amarrado a "faixa de valor" não consegue aprová-las, e o produto termina com dois motores. Foi o que aconteceu até a v2.6: Fornecedor e Item tinham rotas de aprovação próprias, e a alteração bancária tinha um caminho aqui e outro lá. O princípio 7 fecha isso.

A alteração bancária tem valor, só não é o da proposta. O valor avaliado é a média mensal de compras ao fornecedor nos últimos doze meses — é o que separa a troca de conta de quem recebe R$ 10 mil por ano da de quem recebe R$ 10 milhões, e é o "valor médio histórico" que a tabela prometia sem ter como ler. Fornecedor novo avalia zero, e é a política do tenant que decide se zero merece um nível a mais.

Política padrão semeada: o dia 1 se comporta como antes. Todo processo sem valor nasce com a política de reserva já publicada — isDefault, critérios vazios, um nível PermissionHolder apontando para a permissão que autorizava o clique na rota antiga (approve:suppliers, manage:supplier-qualifications, approve:supplier-banks, approve:catalog-items), modo Any. Quem aprovava ontem aprova hoje, agora pelo motor, com fila, SLA, delegação e trilha — com uma diferença deliberada: quem cadastrou não aprova (RN-AP-08), que é a separação de funções que auditoria de cadastro de fornecedor exige. Tenant em que o solicitante é o único detentor da permissão cai na RN-AP-09; é configuração de onboarding — segundo detentor ou fallbackApproverId do processo —, não exceção, e o painel de administração avisa "processo com um único aprovador possível". O tenant edita a política quando quiser alçada; nunca fica sem ela. Os processos com dinheiro continuam exigindo configuração para ativar (RN-AP-03), porque alçada financeira não tem padrão sensato.

O que o motor não decide. Bloquear e desbloquear fornecedor, promoção manual a SpendAuthorized, revogar anexo, substituir cotação, forçar item sobre duplicata, expurgar arquivo e desligar legal hold são atos de uma pessoa só, com permissão, motivo codificado e trilha. Não há pedido de um e decisão de outro, logo não há instância. Um destes virar aprovação é decisão de spec, não de política. A importação de itens com autoApprove é carga, não aprovação: cria o item já aprovado sem instância, com a permissão e a trilha da importação.

Configuração

Política e critérios

Campos da política
CampoTipoRegra
processIdGuidA qual processo esta regra se aplica. Obrigatório.
kindenumBase ou Supplemental. Decide se a política concorre ou empilha. Imutável depois de publicada.
priorityintSó tem efeito em Base: menor número vence. Em Supplemental, ordena a posição na cadeia.
isDefaultboolExatamente uma política Base por processo é a de reserva, com critérios vazios. É a RN-CF03 da spec de junho, que o agregado nunca teve.
criteriavalue objectVer abaixo. Critério ausente casa tudo.
levels1..NOrdenados. Uma política sem nível é recusada na publicação.
versionintImutável. Editar publica versão nova e aposenta a anterior, que segue respondendo pelas instâncias vivas.
validFrom / validTodate / date?Vigência da política. Fora dela, a política não é candidata — e o histórico continua legível.
fallbackApproverIdGuid?Usado quando um nível resolve vazio. Se nulo, herda o do processo.

Critérios disponíveis

CritérioOperadorFonte
Faixa de valorintervalo semiaberto [min, max)Valor avaliado do processo, na moeda funcional da empresa do documento
Centro de custonó, valendo para a subárvorePrefixo de path. Sub-áreas criadas depois passam a ser cobertas sem tocar na regra
Categorianó, valendo para a subárvorePrefixo de path do CategoryNode, pela mesma mecânica
Tipo de itemem listaItemType das linhas
DestinaçãoigualdadepredominantUse — CAPEX ou OPEX, sem depender do centro de custo
Empresaem listacompanyId do documento, derivado do estabelecimento
Estabelecimentoem listaestablishmentId do documento. Novo. É o recorte para obra, filial em regime especial ou CD com alçada própria
Fornecedorem lista · ou estadoFornecedor da linha, ou o fato de ele ser Prospect, sancionado ou com documento vencido
Flags do assuntopresençaCada processo declara as flags que o seu assunto expõe (subjectFlags): no catálogo, regulado, com dado pessoal, crítico; na alteração bancária, titular ≠ fornecedor e chave PIX não verificada; no cadastro de fornecedor, o tipo de pessoa. Nunca nós inventados na taxonomia. Até a v2.6 chamava-se "flag do item"
Conta contábilnó, valendo para a subárvorePrefixo de path do GLAccount da fatia — obrigatória na submissão, RN-CC-29 — e, no BUDGET_CHANGE, da linha de destino. Terceira árvore, mesma mecânica. Novo em v2.7
Faixa de risco do fornecedorem listariskTier da homologação em curso (SUPPLIER) ou da vigente para a categoria da linha (demais processos). Novo em v2.7
Categoria de homologaçãonó, valendo para a subárvorescopeCategoryIds da homologação, por prefixo de path do CategoryNode. Só no processo SUPPLIER — o critério "categoria" lê as linhas do documento, e uma homologação não tem linhas. Novo em v2.7
OrçamentoestadoOk, Warning, Exceeded, Uncovered. Os quatro desde v2.17 — são os quatro que evaluate:budget-states devolve, e listar três deixava o motor sem tratamento para o sinal que mais bloqueia (RN-AP-53)
UF de entregaem listadeliveryStateCode das linhas. Novo. Entrega interestadual muda alíquota, DIFAL e substituição tributária — e muitas empresas querem que ela passe por quem responde pelo fiscal antes de virar pedido
Desvio do menor preçopercentualSó no processo de cotação

UF de entrega é critério de linha avaliado sobre o documento. Um documento pode ter linhas entregando em UFs diferentes, então o critério casa quando qualquer linha satisfaz — mesma mecânica já usada em centro de custo e categoria, e pela mesma razão: uma regra que só dispara quando o documento inteiro satisfaz é uma regra que nunca dispara. O recorte de o que aquele nível aprova continua sendo do scopeMode do passo: Document manda o documento inteiro para o fiscal, Line manda só as linhas interestaduais. Sem isso escrito, cada implementação escolhe uma.

Política de empresa é critério, não coluna. Com multiempresa especificada em Empresa e Estabelecimento, a tentação é dar um companyId à própria política. Não é preciso e seria pior: o critério Empresa em lista já faz esse recorte, e faz melhor — uma política Base do grupo inteiro convive com uma Supplemental de uma empresa só, e a cadeia final é a união das duas. Uma coluna de escopo criaria um segundo mecanismo concorrente para a mesma pergunta.

Aprovador precisa de escopo na empresa do documento — e isso é garantido antes, não aqui. Desde 08/09/2026 o vínculo de centro de custo só é criado para quem alcança a empresa do nó (RN-CC-30), de modo que quem o motor resolve já enxerga o documento. Em passo de escopo Slice a empresa que vale é a da fatia (CostAllocation.companyId), não a do documento — na v1 as duas coincidem sempre, e a distinção existe porque na compra centralizada o documento é da matriz e o rateio sai de estabelecimento da filial. Escrito em 09/09/2026, junto com a coluna. Isso vale para os tipos que caminham a árvore; para os que não passam por vínculo nenhum — SpecificUser, Role, PermissionHolder, Panel, ApprovalGroup, a delegação e o próprio reserva — a guarda equivalente é na publicação da política, e está na RN-AP-40. O que sobra para o motor é defesa em profundidade, para o caso em que o escopo foi revogado depois do vínculo: o nível é tratado como resolvido vazio, cai no fallbackApproverId — e, desde a v2.12, não em silêncio. Ver RN-AP-39.

Base concorre; complementar empilha

Uma requisição de R$ 80 mil de software com dado pessoal para o centro de custo /TI/INFRA/ resolve uma política base — a faixa de R$ 50 mil a R$ 100 mil — e duas complementares — "software com dado pessoal exige o DPO" e "categoria TI exige o arquiteto". A cadeia final é a união ordenada e deduplicada das três. Com motor de regra única, essa mesma combinação exigiria uma política escrita à mão para cada cruzamento de faixa × categoria × flag.

Especificidade não desempata — de propósito

Entre duas políticas base que casam, vence a de menor prioridade numérica, e ponto. Desempate por "regra mais específica" parece elegante e produz configurações que ninguém consegue prever: qual é mais específica, a que amarra centro de custo ou a que amarra faixa de valor? A prioridade explícita é feia e é auditável.

Sobreposição entre bases é recusada ao publicar — guarda parcial no banco, verificação completa na aplicação

Duas políticas base com a mesma prioridade e critérios que se cruzam nunca coexistem. Até a v2.5 esta spec dizia que a garantia vivia inteira no banco, numa restrição de exclusão sobre (tenant =, processo =, prioridade =, faixa de valor &&, nó de CC =, nó de categoria =). Não é verdade, por três razões: igualdade não colide quando o nó é nulo — e nulo é o caso normal, o da política de reserva e o de toda regra "só por valor"; /TI/ e /TI/INFRA/ se sobrepõem por rollup, e igualdade não enxerga; e os outros critérios (empresa, estabelecimento, tipo de item, destinação, fornecedor, flags, UF, orçamento, desvio) e a vigência ficam de fora, o que recusa como sobrepostas políticas de empresas diferentes ou de janelas disjuntas.

A regra passa a ter duas camadas. Na aplicação, sempre. Ao publicar uma versão de política base, o serviço carrega as bases ativas do mesmo tenant, processo e prioridade cuja vigência cruza a nova e recusa com 409 APPROVAL_POLICY_OVERLAP_CONFLICT se alguma tiver interseção não vazia em todos os critérios — nulo casa tudo, nó casa por prefixo nos dois sentidos, faixa e vigência por interseção. A verificação corre na mesma transação da publicação, serializada por bloqueio de linha (SELECT … FOR UPDATE ou o equivalente do banco) na cadeia de ancestrais do nó de centro de custo — a mecânica que o Centro de Custo já usa para a cascata —, para que dois POST simultâneos não passem os dois. Publicar versão nova aposenta a anterior na mesma transação, antes de verificar, para não colidir consigo mesma. No banco, quando ele oferecer. Uma restrição de exclusão sobre a mesma tupla, com sentinela no lugar do nulo (COALESCE(costCenterId, '0000…')) para que a reserva também seja protegida, continua valendo como segunda guarda — parcial, e a spec não depende dela.

Em execução, se duas bases de mesma prioridade casarem o mesmo documento — dado que escapou da verificação, ou política publicada antes desta regra —, a submissão é recusada com 422 APPROVAL_POLICY_AMBIGUOUS_UNPROCESSABLE, nomeando as duas políticas. É o princípio 3 aplicado até o fim. E GET /approval-policies/{id}/validate passa a listar sobreposição como inconsistência, para que a tela avise antes de publicar.

Depende do banco. A restrição de exclusão com operador de faixa (EXCLUDE … btree_gist, no Postgres) e a sintaxe exata do bloqueio de linha são específicas do banco, e a arquitetura ainda não o fixou. O que esta spec garante não depende disso: a verificação na aplicação, o erro de execução e o /validate. A guarda no banco entra quando o banco for escolhido, na sintaxe dele — decisão registrada no mapa de domínio. Regra de 03/09/2026.

A cobertura por subárvore vale para os descendentes futuros

A política guarda o nó, nunca a lista expandida de folhas. Uma sub-área criada no mês seguinte já nasce coberta. O override no descendente existe: uma política amarrada em /TI/INFRA/ com prioridade menor vence a de /TI/ para tudo que estiver abaixo de infraestrutura.

Configuração

Níveis e aprovadores

Campos do nível
CampoTipoRegra
orderintSequencial dentro da política. Entre níveis é sempre sequencial.
approverTypeenumVer a tabela seguinte. Determina a estratégia de resolução.
targetGuid? / string?Usuário, papel, grupo, permissão, e-mail externo, distância de subida ou lista de alvos (Panel), conforme o tipo.
modeenumAny · All · Quorum(n). É aqui que vive o paralelo. Em todos, uma rejeição encerra a instância.
labelstring(80)?"Aprovação Gerencial". O que a tela mostra; se ausente, o motor deriva do tipo.
maxApprovalHoursint?SLA do nível. Nulo herda o da política, depois o do processo.
activationMinValuedecimal?Abaixo deste valor o nível não entra na cadeia. É como se declara "diretor só entra acima de R$ 100 mil" sem criar outra política.
scopeModeenumDocument · Line · Slice. Um nível de alçada de centro de custo é normalmente por fatia; um de Jurídico é sempre do documento; um de fiscal é por linha — só as linhas que casam o critério geram passo (RN-AP-46). Line entrou no enum em v2.17: era usado em três lugares do documento e não existia aqui, o que tornava inconfigurável o padrão "interestadual passa pelo fiscal".

Tipos de aprovador

TipoResolve paraFase
SpecificUserUm usuário fixo. Simples e frágil: é o tipo que quebra quando a pessoa sai.MVP
RoleTodos os usuários ativos com o papel, no tenant. O modo do nível decide se basta um.MVP
CostCenterOwnerOs effectiveOwners do nó da fatia, com rollup. O tipo mais usado na prática.MVP
CostCenterOwnerUpToAmountSobe a árvore de centro de custo até encontrar quem tenha approvalLimit que cubra o valor da fatia. É a alçada acumulada da decisão 7.MVP
CostCenterOwnerAtDistance(n)O responsável n níveis acima do nó da fatia. É o "gestor do gestor" sem depender de hierarquia de pessoas.MVP
ApprovalGroupOs membros ativos do grupo. Comitês.MVP
PermissionHolderTodos os usuários ativos com a permissão, no tenant. É o alvo das políticas padrão semeadas: no dia 1, quem aprovava pela rota do módulo continua aprovando, agora pelo motor. Novo em v2.7MVP
PanelUma lista de alvos — papel, grupo ou permissão — e o nível só fecha com uma decisão de cada alvo. É o paralelo dentro do nível que a Decisão 5 permite, na forma que a homologação por área exige: jurídico, financeiro, técnico e qualidade ao mesmo tempo, um de cada. Modo fixo em All sobre os alvos; a primeira rejeição encerra. Novo em v2.7MVP
CategoryOwnerO responsável declarado no nó de categoria, com rollup. Exige um vínculo equivalente no catálogo.fase 2
RequesterManagerO gestor direto do requisitante, na hierarquia de pessoas do GrydAuth.fase 2
ExternalEmailUm e-mail sem conta, via ApprovalToken. Sócio, conselheiro, cliente final em obra.fase 2
AutoAprova sozinho quando a condição do nível é satisfeita. Sempre declarado, nunca por decurso de prazo.fase 2

Modo dentro do nível, com precisão. Any: a primeira decisão fecha o nível — e as demais tarefas viram Obsolete, não desaparecem, porque a trilha precisa mostrar quem mais estava na fila. All: o nível só fecha quando todos decidem; uma rejeição encerra a instância imediatamente. Quorum(n): fecha em n aprovações; qualquer rejeição encerra, como nos outros modos. É o "quatro olhos" — duas pessoas do financeiro numa troca de conta bancária. A semântica de comitê de investimento, em que um voto contrário não derruba a proposta e a instância só encerra quando n fica inalcançável, é modo próprio de fase 2, Vote(n), restrito aos processos que o produto declarar (BUDGET, BUDGET_CHANGE, CONTRACT) — nunca uma releitura do Quorum, nunca em BANK_ACCOUNT_CHANGE. Revisado em v2.8.

Contrato vigente · revisão consolidada

Alçada explícita e autorização por empresa

A alçada financeira é o vínculo vigente de responsabilidade: CostCenterAssignment.approvalLimit com approvalLimitCurrency, ou hasUnlimitedAuthority = true. Limite ausente não cobre valor algum. Não somar limites de pessoas distintas.

  1. Filtrar pelo teto de permissões da plataforma, concessão da operação na empresa do documento, vigência, segregação de funções e escopo do passo.
  2. Converter o valor avaliado para a moeda do limite usando o snapshot da submissão/revisão; ausência de cotação recusa a submissão.
  3. Procurar responsáveis do nó e subir a árvore até encontrar autoridade suficiente. Dedupe que agrupa fatias reavalia a alçada sobre o valor agregado.
  4. Esgotada a árvore, procurar o reserva explicitamente habilitado para a empresa, operação e valor, com limite ou autoridade ilimitada cadastrada. Ser reserva ou Owner da raiz não concede alçada.
  5. Sem cobertura, recusar a submissão com APPROVAL_AUTHORITY_UNAVAILABLE_UNPROCESSABLE. Se a insuficiência surgir ao ativar um passo posterior, preservar votos já válidos e devolver a instância por configuração pendente. Avisar a controladoria por registro durável.

authorityExceeded registra a falha de resolução, sem autorizar sua superação. O simulador mostra a falta de cobertura; não exige autoridade ilimitada quando um limite finito já atende ao escopo. Delegação, reatribuição e escalada passam pelas mesmas verificações de empresa, operação, valor e impedimento de autoaprovação.

Configuração

Grupos e delegação

ApprovalGroup

Pool nomeado de usuários, com membros que entram e saem com vigência — mesma disciplina do vínculo de centro de custo. Um grupo referenciado por política ativa não pode ficar vazio: a remoção do último membro é 409, com a lista de políticas que o usam. É o comitê que aprova sem que ninguém precise saber os nomes ao configurar.

ApprovalDelegation

Herdada da v1.0: delegante, delegado, período, encerramento automático por rotina diária e registro em auditoria com delegante, substituto e referência à delegação. Ganha escopo — scope, um processo por linha, nulo = todos — e teto de valor. A regra "no máximo uma ativa por pessoa" virou uma vigente por delegante e por processo em 08/09/2026 (v2.10): com dez processos, a regra antiga tornava impossível o que a própria spec dava como exemplo — delegar o pedido ao Bruno até R$ 50 mil, o item à Carla e não delegar a alteração bancária. Três linhas, três delegados, três tetos.

Uma delegação vigente por delegante e por processo, e com fim

A chave é (delegante, scope, período) sem sobreposição — scope nulo lido como "todos": verificada na aplicação, na mesma transação, com guarda de exclusão por interseção no banco quando ele existir (o banco ainda não está definido) — a mesma disciplina da sobreposição de políticas e do membro de grupo. Sobreposição é APPROVAL_DELEGATION_OVERLAP_CONFLICT · 409. validTo é obrigatório e não passa de maxDelegationDays (padrão 90, renovável): delegação sem fim é autoridade paralela permanente, que ninguém revisa e toda auditoria de compras encontra.

A tarefa delegada carrega a alçada do titular, nunca a do delegado

O passo já foi resolvido com o approvalLimit de quem responde pelo nó; o delegado apenas age. O teto da delegação só reduz — acima dele a tarefa fica com o titular. Um substituto com alçada maior aprovando o que o titular não poderia é o furo silencioso mais comum deste mecanismo, e aqui ele não existe por construção.

Processo pode não ser delegável

delegable por processo, false na semente do BANK_ACCOUNT_CHANGE: se a troca de conta do fornecedor exige quem responde pelo controle, ela não é coberta por quem está cobrindo férias. Criar delegação que alcance um processo não delegável é APPROVAL_DELEGATION_NOT_ALLOWED_UNPROCESSABLE · 422; a delegação de escopo nulo simplesmente não o alcança. Novo em v2.10

Delegação redireciona a tarefa; não substitui o aprovador

A ApprovalAssignment continua registrando o titular como aprovador de direito e o delegado como quem agiu, com o id da delegação. É por isso que a tarefa precisa ser entidade: não há onde guardar esse par num campo do passo.

Delegação vigente é lida na ativação do passo, não na submissão

Uma delegação que começa amanhã tem de valer para o passo que ativa amanhã, mesmo que a instância tenha sido criada hoje. É consequência direta da decisão 3.

Ativar a delegação transfere também o que já está na fila

Novo em v2.17. A regra acima cobre o passo que ativa amanhã e calava sobre o presente — e o presente é justamente o que a pessoa deixou aberto ao sair de férias. Quando a delegação entra em vigor, as tarefas Pending do titular que caem no seu escopo passam ao delegado: a ApprovalAssignment continua a mesma linha, com o titular como aprovador de direito e o delegado como quem age, e cada transferência grava approval-assignment.delegated com o id da delegação. O SLA não reinicia — o relógio é do passo, e zerá-lo faria a delegação virar o jeito mais fácil de comprar prazo. Acima do teto de valor da delegação a tarefa fica com o titular (RN-AP-37), e o fim da delegação faz o caminho de volta pelas que continuarem abertas. É o que Ariba, Coupa e Oracle fazem, e sem isso a delegação só serve para quem se lembra de criá-la antes de a fila encher (RN-AP-49).

Cadeia de delegação não encadeia

Se A delega para B e B delega para C, a tarefa de A vai para B — e para. Encadear produz caminhos que ninguém consegue prever e é o vetor clássico de escapar da alçada em três saltos. B pode recusar a tarefa (POST …/decline): ela volta para A, com evento e aviso.

Execução

Instância, passo e tarefa

Estados
EntidadeEstadosTransição terminal
ApprovalRequestInApproval · Approved · Rejected · Returned · CancelledRevisto em v2.17. Rejected e Cancelled são finais. Approved é terminal condicional: tem duas saídas declaradas — Approved → InApproval por revaluate e Approved → Cancelled por cancel —, cada uma com evento de reabertura e roundNumber incrementado na instância (RN-AP-43). Não nasce estado novo: Reopened seria um estado que só diz "já esteve aprovado", e isso é o que a rodada conta. Returned volta ao requisitante e a instância continua viva. Não há Draft — o rascunho é do documento, e a instância só nasce quando o módulo dono chama POST /approvals; nem Expired — nada vence por decurso (RN-AP-11) e o escalonamento não encerra passo nenhum. Os dois estavam no enum sem nada que os produzisse. Corrigido em v2.9
ApprovalStepPending · Active · Approved · Rejected · Skipped · ObsoleteSkipped só por activationMinValue ou por dedupe — nunca por falta de aprovador. Obsolete é o que a reavaliação produz.
ApprovalAssignmentPending · Approved · Rejected · Returned · Obsolete · ReassignedObsolete quando outra tarefa do mesmo nível já fechou o passo em modo Any. A linha permanece: a trilha mostra quem mais estava na fila.
Campos do passo e da tarefa
CampoOndeO que guarda
policyId · policyVersion · levelOrderpassoDe onde este passo veio. Permite explicar a cadeia meses depois, com a política já editada três vezes.
scope · scopeRef · scopeAmountpassoDocument, Line ou Slice, com o id do alvo e o valor sobre o qual a alçada foi avaliada.
mode · quorum · slaDueAtpassoModo do nível, quórum exigido e o vencimento calculado na ativação.
authorityExceededpassoMarca resolução sem alçada suficiente. Impede a decisão financeira até cobertura explícita; não autoriza Owners da raiz por omissão.
resolvedOutOfScopepassobool, gravado na ativação. Verdadeiro quando o alvo do nível resolveu para quem não alcança a empresa do documento e o passo caiu no reserva. Escopo revogado depois do vínculo é o único caminho que deveria produzi-lo (RN-AP-39). Novo em v2.12
resolvedEmptyOnActivationpassobool, gravado na ativação. Verdadeiro quando o passo resolveu zero pessoas por passagem do tempo — vigência de membro de grupo vencida, papel sem detentor, Owner que deixou de ser vigente — e caiu no aprovador de reserva. É o terceiro irmão de authorityExceeded e resolvedOutOfScope (RN-AP-45). Novo em v2.17
roundNumberinstância e passoint. A instância guarda a rodada corrente; cada passo guarda a rodada em que nasceu. É o que responde "quantas vezes este pedido rodou e o que mudou entre as rodadas" depois que a reabertura passou a existir (RN-AP-43). Novo em v2.17
versioninstânciaint. Token de concorrência da raiz: toda decisão passa expectedVersion e a divergência é 409. Não existe token no passo nem na tarefa — a serialização é uma só, na raiz (RN-AP-44). Novo em v2.17
approverUserIdtarefaQuem é o aprovador de direito.
actingUserIdtarefaQuem de fato decidiu. Igual ao anterior, salvo delegação ou reatribuição.
origin · originReftarefaDirect, ByRole, ByGroup, ByDelegation, ByEscalation, ByReassignment — com o id da delegação, do grupo ou do papel.
decision · reasonCodeId · comment · decidedAttarefaA decisão, o motivo codificado, o comentário e o momento. Em rejeição e devolução os dois são obrigatórios: o código classifica para o relatório, o comentário explica o caso para quem vai corrigir. A tarefa grava também reasonCode e reasonLabel congelados — ver Motivo.
documentRevisionId · relevantContentHash · authorizedAmount · authorizedCurrency · evidenceRefsdecisão / eventoRevisão e conteúdo autorizado, teto de valor, moeda e versões imutáveis das evidências. Cada preservação referencia a decisão original e registra prova de equivalência.
ApprovalEvent — os campos. Novo em v2.17
CampoTipoO que guarda
id · tenantIdGuid · GuidIdentidade e tenant, como em toda entidade do produto.
approvalRequestIdGuidSempre preenchido, inclusive nos eventos de passo e de tarefa. É a chave de agrupamento da trilha.
sequencelongMonotônico dentro da instância, alocado na mesma transação que incrementa o version da raiz (RN-AP-44). A chave de ordenação é (approvalRequestId, sequence) — nunca occurredAt, que empata em decisão em lote e em fatias paralelas fechando juntas.
roundNumberintA rodada em que o fato aconteceu (RN-AP-43). É o que separa "o comitê aprovou" da primeira rodada do da terceira.
stepId · assignmentIdGuid? · Guid?Nulos nos eventos de instância; o de tarefa carrega os dois.
actionenumEnum fechado — a tabela seguinte. Acrescentar valor é migração, como em todo enum do produto.
outcomestring?O desfecho, onde a ação o admite: Approved · Rejected · Returned · Cancelled · ApprovedWithProviso. É o que permite uma ação só para "decidiu" sem perder o quê.
actorUserId · actorTypeGuid? · enumQuem agiu e por qual tipo de aprovador (Role, CostCenterOwner, PermissionHolder…). actorUserId nulo é o motor — ativação de passo, aprovação pela política, vencimento de SLA —, e é o único caso de evento sem pessoa.
onBehalfOfUserId · delegationIdGuid? · Guid?O aprovador de direito e a delegação em vigor, quando quem agiu não é quem responde (RN-AP-17).
policyId · policyVersion · levelOrderGuid? · int? · int?De onde veio o passo que produziu o evento. É o que explica a cadeia com a política já editada três vezes.
scope · scopeRef · scopeAmount · scopeCurrencyenum? · Guid? · decimal? · char(3)?O escopo sobre o qual se decidiu e o valor avaliado, na moeda em que foi avaliado.
exchangeRate · quoteUnit · rateDatedecimal? · int? · date?A prova da comparação de alçada (RN-AP-52): a taxa congelada na submissão, o fator de cotação com que ela foi publicada e a data dela, gravados no evento da decisão. O fator entrou em 09/09/2026, pela mesma razão que o pôs no movimento do razão: taxa sem fator não reconstitui a conta em moeda cotada por 100 — o iene comparado com o fator errado erra por duas ordens de grandeza, e a trilha guardaria a prova de uma conta que ninguém consegue refazer. Sem isso, "por que este valor coube nesta alçada" não tem resposta depois que a cotação mudou.
reasonCodeId · reasonCode · reasonLabel · commentGuid? · string? · string? · text?O motivo codificado com rótulo congelado e o comentário. Obrigatórios juntos em toda ação que não seja aprovar (RN-AP-19).
occurredAttimestamptzQuando. Serve para medir, não para ordenar.
metadatadocumento semiestruturadoO que é específico da ação — o alvo da reatribuição, o que mudou na reavaliação, o passo que a escalada acrescentou. Nunca o lugar de um campo pelo qual a consulta filtra: o que a GET /approvals filtra é coluna.
As ações — enum fechado, <entidade>.<verbo-no-passado>
AlvoAções
Instânciaapproval-request.submitted · chain_resolved · decided · returned · resubmitted · revaluated · reopened · approved_by_policy · budget_requirement_unmet
Passoapproval-step.activated · closed · skipped · obsoleted · escalated · authority_exceeded · resolved_out_of_scope · resolved_empty_on_activation
Tarefaapproval-assignment.created · decided · info_requested · info_answered · reassigned · declined · delegated · obsoleted

Imutável quer dizer sem rota de escrita. ApprovalEvent é append-only: não tem PUT, não tem DELETE, não tem soft delete — a exclusão lógica que o resto do produto herda da plataforma não se aplica aqui, e essa exceção é declarada, não esquecida. Corrigir um evento é gravar outro que o corrija, com metadata apontando o sequence corrigido. É a mesma disciplina do razão orçamentário, e pela mesma razão: uma trilha que pode ser editada não prova nada (RN-AP-48).

Por que só agora. A decisão 8 declarava o evento fonte da verdade desde a v2.0, e o BudgetEntry foi obrigado a ser "reconstruível" citando este precedente — que não existia por escrito. Era a única entidade do acervo sem tabela de campos.

Uma instância viva por documento. Reenviar um documento devolvido reaproveita a mesma ApprovalRequest, com uma nova rodada de passos e os anteriores marcados como Obsolete. Criar instância nova a cada reenvio parece mais limpo e quebra a pergunta "quantas vezes este pedido rodou e o que mudou entre as rodadas" — que é a métrica que expõe política mal configurada.

Mecânica

Congelar e resolver

MomentoO que é fixadoPor quê
SubmissãoPolíticas aplicáveis e suas versões, valor avaliado, taxa de câmbio, rateio, limiar de materialidade efetivo, sequência de passos com tipo, escopo, modo e SLAEditar a política amanhã não pode mudar a cadeia de um documento que já está circulando. É o que torna a trilha defensável.
Ativação do passoOs aprovadores concretos, a delegação vigente, os membros do grupo e o vencimento do SLAUm responsável que mudou entre a submissão e a chegada do passo tem de ser o novo. Congelar pessoas na submissão produz tarefas para quem já saiu.
NuncaQuem já decidiuDecisão tomada é evento. Nenhuma reavaliação, edição de política ou troca de responsável apaga uma assinatura.

Dedupe: a mesma pessoa não assina duas vezes seguidas

Se Ana é responsável pelo centro de custo e membro do comitê, e os dois passos são consecutivos, ela aprova uma vez e o segundo passo é marcado Skipped com o motivo. Configurável por processo — há empresas que exigem a dupla assinatura de propósito, e há auditoria que a proíbe. O que não pode existir é o comportamento implícito.

O requisitante é removido de toda tarefa da própria instância

Antes de tudo, e sem chave que desligue. Se a remoção esvaziar o passo, o motor sobe um nível nos tipos de árvore e cai no aprovador de reserva nos demais; se esgotar, a submissão é recusada com 422, e a mensagem diz qual passo ficou sem ninguém e que o requisitante era o único aprovador possível — não "erro ao aprovar".

Resolução vazia nunca vira passo pulado

É a diferença entre um sistema que controla e um que parece controlar. Ordem exata: tipo do nível → rollup na árvore → delegação vigente → aprovador de reserva da política → aprovador de reserva do processo → recusa explícita.

Na submissão sem elegível, recusar com erro de configuração. Na ativação posterior, procurar reserva explicitamente habilitado; se também faltar, preservar o voto anterior e devolver a instância com motivo (RN-AP-45).

Cadeia inteira pulada é aprovação pela política, com nome

Quando todos os níveis têm activationMinValue acima do valor avaliado, a instância termina Approved sem tarefa e grava approval-request.approved_by_policy, com a política e a versão. É a única aprovação sem pessoa do MVP, e é declarada: aparece no simulador antes de submeter, e o /validate avisa que a política tem faixa sem decisor humano. Novo em v2.8.

Mecânica

Rateio e escopo do passo

Um documento tem N linhas; cada linha tem de 1 a N fatias de rateio, cada fatia com seu centro de custo e seu valor. O motor avalia as regras contra as linhas e fatias e decide sobre o documento inteiro — que é o padrão SAP, onde documentos são "aprovados na íntegra".

#EtapaDetalhe
1Agrega as fatias por centro de custoCinco linhas apontando para /TI/INFRA/ viram uma fatia agregada. Sem isso, o responsável recebe cinco tarefas do mesmo pedido.
2Aplica a materialidadeFatia abaixo de allocationMaterialityPercent e de allocationMaterialityAmount — o que for maior — é absorvida pela maior fatia para efeito de roteamento, e só. Continua contabilizada integralmente. O limiar é lido uma vez aqui e congela com a cadeia (RN-AP-06). Materialidade não é regra de orçamento: o orçamento sempre vê a decomposição integral, e Block numa fatia absorvida recusa a submissão do mesmo jeito (RN-AP-15).
3Gera passos por escopoNíveis com scopeMode = Slice geram um passo por fatia material, em paralelo entre si. Níveis com scopeMode = Line geram um passo por linha que casa o critério do nível, com scopeRef = id da linha e scopeAmount = valor da linha — a materialidade não se aplica (RN-AP-46). Níveis de documento geram um passo só.
4Deduplica aprovadoresSe a mesma pessoa responde por duas fatias, ela recebe uma tarefa que cobre as duas, com o valor somado no scopeAmount. Em escopo Line a dedupe é por (pessoa, linha), não por (pessoa, fatia): quem responde pelo fiscal de duas linhas interestaduais recebe duas tarefas, porque o que ele examina é a linha.
5OrdenaPassos de fatia do mesmo nível são paralelos entre si; os níveis seguem sequenciais. O documento avança quando todos os passos do nível fecham.

Agrupar fatias pode reduzir tarefas, mas exige reavaliar a alçada sobre o valor agrupado e preservar cada responsável e origem na trilha. Ganho de produtividade e comparação com concorrentes são hipóteses a validar em piloto.

Um dono só, e é o tenant — decidido em 08/09/2026 (v2.15). O limiar vive na configuração de tenant deste documento e não varia por processo. Duas razões, e a primeira é de fato: só dois dos dez processos-semente têm fatia — REQUISITION e PURCHASE_ORDER —, e eles carregam o mesmo rateio em dois momentos, de modo que limiares diferentes fariam o pedido reabrir cadeia para fatias que a requisição já tinha dispensado. Nos outros oito não há o que dispensar: a alteração bancária avalia a média de compras, o exercício avalia o total orçado, e cadastro, homologação e curadoria não têm valor nenhum. A segunda razão é de controle: materialidade é dispensa — subir o número remove aprovadores —, e com herança por processo a tela do tenant mostraria 5% enquanto um processo rodasse a 30%. Com um valor só, a garantia da RN-AP-15 é uma consulta e uma linha de trilha. É a mesma escolha que tirou o allowSelfApproval em vez de lhe dar padrão: aperta-se a regra, não o controle. Se um dia aparecer o caso real de variar por processo, dois campos anuláveis no ApprovalSettings herdando deste valor são adição não-quebrante — o que não se desfaz é um número que já divergiu em produção.

Execução

Ações do aprovador

AçãoEfeitoComentário
AprovarFecha a tarefa. O passo fecha conforme o modo; a instância avança para o próximo nível.opcional
RejeitarEncerra a instância. O documento volta ao requisitante como rejeitado e não pode ser reenviado sem alteração.obrigatório
Devolver para ajusteDevolve ao requisitante sem encerrar. Os passos da rodada viram Obsolete; o reenvio abre uma rodada nova na mesma instância.obrigatório
Solicitar informaçãoPergunta ao requisitante sem tirar o documento da fila. O SLA pausa enquanto aguarda — senão o aprovador é penalizado por uma resposta que não depende dele.obrigatório
ReatribuirPassa a tarefa a outra pessoa elegível, uma vez, sem alterar o passo. Exige permissão própria e gera evento com origem e destino.obrigatório
Aprovar com ressalvaAprova e registra uma condição a cumprir — "mediante apresentação da ART". Não bloqueia o avanço; fica visível no documento e no recebimento.obrigatório

Devolver não é rejeitar, e essa distinção é o que faz o SLA significar algo

Sem "devolver", todo pedido com um erro de digitação é rejeitado e reenviado como novo — e a métrica de aprovação passa a medir qualidade de digitação em vez de decisão. É a ação mais usada em qualquer sistema que a oferece, e a primeira que falta em quem não a modelou.

Aprovação em lote existe; decisão em lote não

A tela pode aprovar dez tarefas numa ação, e o motor grava dez eventos, um por tarefa, cada um com seu timestamp. Um evento de lote pareceria uma decisão só e destruiria a rastreabilidade individual que a auditoria pede.

Contrato vigente · revisão consolidada

Revisões e validade da decisão

AlteraçãoEfeito
Aumento de valorReavaliar além da tolerância. Dentro dela, preservar assinatura somente se o novo valor ainda couber no teto explicitamente autorizado; tolerância não amplia autorização.
Redução de valorPode preservar assinatura se todo o restante do conteúdo relevante e o escopo forem equivalentes.
Fornecedor, oferta, quantidade, unidade, especificação, rateio, empresa, prazo ou condição contratualReavaliar quando muda o conteúdo relevante, mesmo com valor total igual ou menor. Mudança de fornecedor é sempre material.
Política ou evidência usada na decisãoReavaliação usa política vigente; assinatura de outra versão de política não é reaproveitada. Revogação de evidência material exige revisão, preservando histórico.
Correção apenas editorialRegistrar a diferença e demonstrar que o hash do conteúdo relevante permanece igual.

Cada decisão grava documentRevisionId, relevantContentHash, authorizedAmount, moeda, escopo, policyId, policyVersion, nível e referências imutáveis das evidências. Ao preservar uma assinatura, manter sua revisão original e registrar a prova de equivalência com a revisão nova. Existir na cadeia nova não basta. A nova decisão exige autorização vigente; revogação de acesso não apaga assinatura histórica.

Pedido emitido mantém versão efetiva. A alteração é uma revisão candidata na mesma trilha de rodadas; enquanto circula, entregas e obrigações continuam ligadas à versão efetiva. Aprovação e emissão da alteração aplicam apenas o delta financeiro e quantitativo, após nova avaliação sob trava. Rejeitar a alteração mantém o pedido anterior. Cancelamento é ato de negócio separado, respeitando parcelas recebidas e obrigações exigíveis.

Antes da primeira emissão, uma devolução de requisição pode liberar ou recompor a reserva conforme seu desfecho documentado. Essa regra não se aplica indiscriminadamente a um pedido já emitido. roundNumber identifica rodadas da instância; documentRevisionId identifica conteúdo, não apenas tentativas.

Execução

SLA e escalonamento

Como o prazo é contado

O vencimento é calculado na ativação do passo, em horas úteis: o relógio só corre nos dias da semana marcados como úteis, dentro do expediente do tenant, e pula as datas da lista de dias não úteis. Contar em horas corridas faz todo passo ativado na sexta vencer no domingo, e o escalonamento vira ruído semanal.

O calendário é uma lista, não uma entidade

Decisão de 08/09/2026 (v2.14). Não existe BusinessCalendar. O expediente e a lista nonWorkingDates são parâmetros do tenant, e o produto semeia a lista com os feriados nacionais — que são algoritmo, não cadastro: as datas fixas mais as derivadas da Páscoa, geráveis anos à frente sem ninguém digitar. O tenant acrescenta o feriado municipal e o recesso, ou tira o que não vale ali. Entidade com ciclo de vida próprio só se pagaria com um segundo consumidor, e não há: prazo de cotação e prazo de entrega contam em dias corridos, como o resto do produto. Três limites, registrados agora para não voltarem como defeito: a lista é do tenant, não do estabelecimento — filial em outro município acrescenta a data à lista de todos ou convive com ela —, não existe meio expediente, e errar uma data adianta um lembrete, nunca uma decisão (RN-AP-11).

O que acontece no vencimento

Definido por processo, em escalationBehavior: Notify (lembrete ao aprovador e ao requisitante), Escalate (acrescenta uma tarefa, mantendo a original viva) e None. Não existe AutoApprove. O processo diz se escala; o nível diz para quem — só ele sabe a própria forma de resolução. NotifyManager saiu do enum em v2.9: avisar "o gestor" exige a árvore de subordinação do GrydAuth, que é fase 2 — a mesma dependência que mantém RequesterManager fora do MVP. Quem precisa do gestor hoje escala pela árvore de centro de custo, que é a hierarquia que o produto tem.

Escalar não inventa aprovador

Decisão de 08/09/2026 (v2.10). Escalate não tem mais destino implícito. Em nível que caminha árvore (CostCenterOwner, CostCenterOwnerUpToAmount, CostCenterOwnerAtDistance(n)) continua sendo o responsável do nó pai — a hierarquia existe e o simulador a desenha. Em Role, SpecificUser, ApprovalGroup, PermissionHolder e Panel não existe "nível acima", e o nível precisa declarar escalationTarget, no mesmo vocabulário do próprio nível. Sem alvo, a política não publica: APPROVAL_ESCALATION_TARGET_REQUIRED_UNPROCESSABLE. O motor não inventa aprovador em nenhum outro ponto desta spec, e não passa a inventar no vencimento — cair no aprovador de reserva transformaria um nome só no aprovador universal de tudo o que atrasa, e bastaria deixar o relógio correr para chegar nele.

Processo de controle não escala

allowEscalation por processo, false na semente do BANK_ACCOUNT_CHANGE: no vencimento, só lembrete. Escalonar é ganho de produtividade; num controle antifraude é superfície de ataque — quem quisesse outro decisor bastaria não decidir até o prazo virar. Mesmo raciocínio do gate 9, que tirou a dissidência do Quorum em vez de afrouxar a RN-AP-10. Publicar política com Escalate em processo que não escala é APPROVAL_ESCALATION_NOT_ALLOWED_UNPROCESSABLE. Novo em v2.10

Escalonar não remove ninguém

Revisto em v2.17. Em Any, a tarefa original continua pendente e a escalada acrescenta um caminho: as duas fecham o passo, e o evento diz qual chegou primeiro. Em All, Quorum(n) e Panel a escalada substitui a tarefa do titular — que vai a Obsolete —, porque acrescentar um caminho ali é reduzir o número de pessoas distintas que o modo exige: num Quorum(2) de quatro olhos, o titular atrasa, a escalada entra, e o quórum fecha com uma assinatura de gente que decidiu e outra de quem entrou por decurso de prazo. O número de pessoas distintas exigido pelo modo não muda nunca — é o que o modo significa (RN-AP-50).

E o alvo da escalada passa pelas mesmas guardas do alvo original: RN-AP-08 (o requisitante nunca decide a própria instância), alçada — o escalado precisa cobrir o scopeAmount do passo, ou o passo herda a marca authorityExceeded — e escopo de empresa (RN-AP-39/40). Um alvo de escalonamento que escapasse dessas três seria a porta que a RN-AP-08 fecha na entrada e deixaria aberta no vencimento.

O relógio pausa

A pergunta avisa o requisitante (nexio.approval-assignment.info_requested) e pausa o SLA do passo; a resposta entra por POST /approvals/{id}/answer-info, rota do requisitante, avisa quem perguntou (nexio.approval-assignment.info_answered) e retoma o relógio. Os dois avisos desde 08/09/2026: até então só a volta tinha slug, e a ida não — o documento parava esperando uma resposta que ninguém tinha sido avisado de dar. O tempo pausado fica registrado, para que o indicador separe "aprovador lento" de "requisitante lento".

Contrato vigente · revisão consolidada

Conclusão financeira na mesma transação

O motor chama explicitamente o contrato do processo. Sob a mesma conexão e transação, o dono do documento revalida orçamento, autorização e revisão, e responde com resultado tipado. Em sucesso, transiciona o documento e grava o efeito financeiro correspondente: Commitment na requisição; Obligation na emissão do pedido ou ativação do mínimo contratual; apenas delta na alteração efetiva.

Insuficiência na última decisão: persistir o voto, retornar documento e instância a Returned e registrar a notificação durável, sem Approved ou novo lançamento financeiro. Retornar ReturnedForBudget com motivos e valores. Falha técnica reverte voto e efeitos; repetição usa a mesma identidade da operação. O registro do voto não é conclusão financeira.

O orçamento devolve NotControlled, Ok, Warning, Exceeded ou Uncovered, origem da política e limites afetados. Block ou Uncovered impedem novo compromisso. RequireApproval exige cobertura Supplemental efetivamente concluída para o escopo excedido; se a avaliação final descobrir exigência nova, devolver para nova rodada. Materialidade dispensa roteamento, nunca consumo ou controle.

Eventos em memória são despachados após o commit produtor; reações obrigatórias, avisos de pendência e integrações têm registro durável. Cancelar uma revisão candidata não cancela a obrigação efetiva. Contratos completos em Contratos transacionais.

Diferencial

Simulador

POST /approvals:simulate recebe um documento hipotético — valor, empresa, linhas com centro de custo e categoria, requisitante — e devolve a cadeia que ele produziria, sem criar nada — passo a passo, com quem resolve em cada um, as marcas de exceção onde a cadeia não sai como o desenho previa — authorityExceeded, resolvedOutOfScope — e o limiar de materialidade efetivo com as fatias que ele dispensou, para que "por que esta área não aparece na cadeia?" tenha resposta antes de alguém perguntar (novo em v2.15). Três usos, e o terceiro é o que ninguém oferece:

Para quem configura

Testar uma política antes de publicar, inclusive numa versão em rascunho. Configuração de aprovação é o lugar do produto onde o erro só aparece quando um pedido trava, semanas depois.

Para quem requisita

A mesma consulta alimenta a prévia mostrada na submissão: "este pedido passará por Ana, depois pelo comitê". Expectativa correta é metade das reclamações de fluxo de aprovação.

Para quem audita

Reexecutar uma instância antiga contra a configuração da época e comparar com o que de fato aconteceu. Como a instância guarda as versões das políticas, a comparação é exata — e é a resposta a "prove que o controle funcionava em março".

Na prática

Exemplos de fluxo

Tudo o que um tenant consegue montar é combinação das peças definidas acima: um ApprovalProcess, a política Base que casa o documento, as Supplemental que empilham, e níveis com tipo, modo, escopo, valor de ativação e prazo. Esta seção existe para o time ver as combinações com números — um exemplo por processo-semente, uma requisição acompanhada do começo ao fim, e os padrões que se repetem entre processos. Nenhum exemplo usa recurso que não esteja nas seções anteriores; onde um parece pedir algo novo, a resposta está em "o que não dá para montar", no fim. Novo em v2.8.

As peças e o que cada uma muda

PeçaOpçõesO que muda no fluxo
PolíticaBase — concorre por prioridade, uma vence · Supplemental — empilha, todas que casam entramA base decide a espinha da cadeia; cada complementar acrescenta níveis na posição que a prioridade dela dá. "OU" entre condições é duas complementares.
CritériosQuinze, em conjunção; ausente casa tudo; nós de centro de custo, categoria e conta contábil valem por subárvoreQuem casa. Faixa de valor e nó são os mais usados; flags do assunto, estado do orçamento, fornecedor e UF de entrega são os que acionam controle extra.
Tipo do nívelSpecificUser · Role · PermissionHolder · ApprovalGroup · Panel · CostCenterOwner · CostCenterOwnerUpToAmount · CostCenterOwnerAtDistance(n)Quem resolve — e se a resolução caminha a árvore de centro de custo ou lê uma lista.
ModoAny · All · Quorum(n); Panel é sempre All sobre os alvosQuantas decisões fecham o nível. Em todos, uma rejeição encerra a instância.
EscopoDocument · Line · SliceSobre o que a alçada é avaliada e quantos passos o nível gera: um por fatia material, em paralelo, ou um só para o documento.
AtivaçãoactivationMinValueAbaixo do valor o nível não entra — "diretor só acima de R$ 100 mil" sem escrever outra política.
AlçadaCostCenterAssignment.approvalLimit, lida por CostCenterOwnerUpToAmountSobe a árvore até quem cubra a fatia; esgotada a subida, aprova o topo com authorityExceeded marcado e aviso à controladoria.
PrazomaxApprovalHours por nível, política ou processo · escalationBehavior só por processo · escalationTarget no nívelO prazo herda de baixo para cima; a resposta ao atraso, não. escalationBehavior tem um dono só — o processo, em ApprovalSettings —, para que o que acontece num vencimento não dependa de qual política casou; o para quem mora no nível, porque depende do tipo dele. Lembrete, tarefa declarada ou nada. Nunca aprovação.
AusênciaApprovalDelegation, uma vigente por processo, com teto de valor e fim obrigatórioPara quem a tarefa vai enquanto o titular está fora — e o que continua com o titular: o que passa do teto, e todo processo marcado delegable = false.
Açõesaprovar · aprovar com ressalva · devolver · pedir informação · reatribuir · rejeitarDevolver abre rodada nova na mesma instância; rejeitar encerra; pedir informação pausa o SLA.
MudançarevaluationTolerancePercent · revaluationToleranceAmountQuando a edição de um documento aprovado refaz a cadeia — e o que é preservado.

Um exemplo por processo

Nomes, valores e centros de custo são inventados; a mecânica é a da spec. "Cadeia" é o que o simulador mostra ao requisitante antes de submeter.

REQUISITION · alçada por fatia, diretor só acima de R$ 100 mil, DPO por complementar

Configuração. Base "até R$ 50 mil" (prioridade 10): nível 1 CostCenterOwner · Any · Slice. Base "de R$ 50 mil a R$ 500 mil" (prioridade 20): nível 1 CostCenterOwnerUpToAmount · Any · Slice; nível 2 Role(Diretor de Compras) · Any · Document com activationMinValue R$ 100 mil. Supplemental "categoria /TI/SOFTWARE/ com a flag dado pessoal": um nível SpecificUser(DPO) · Document. Materialidade do rateio, que é do tenant e vale para todos os processos: 10%.

Documento. Ana, de /TI/INFRA/, requisita R$ 80 mil em licenças que tratam dado pessoal, rateados 75% em /TI/INFRA/ (R$ 60 mil) e 25% em /MARKETING/ (R$ 20 mil). Bruno é Owner de /TI/INFRA/ com limite de R$ 50 mil; Carla é Owner de /TI/ com limite de R$ 150 mil; Diego é Owner de /MARKETING/ com limite de R$ 50 mil.

Cadeia. Casam a base de R$ 50–500 mil e a complementar. Nível 1, por fatia, em paralelo: a fatia de TI sobe de Bruno (R$ 50 mil não cobre R$ 60 mil) para Carla; a fatia de Marketing fica com Diego. Nível 2 não ativa — R$ 80 mil está abaixo de R$ 100 mil — e o passo nasce Skipped. Depois, o passo do documento: o DPO. Ana vê "Carla e Diego em paralelo, depois o DPO" antes de submeter. Com R$ 120 mil, o diretor entraria entre os gestores e o DPO; se Ana fosse ela própria a Owner de /TI/INFRA/, seria removida e a fatia subiria para Carla do mesmo jeito.

QUOTATION · quem explica por que não escolheu a mais barata

Configuração. Base de reserva (critérios vazios): nível 1 Role(Gerente de Compras) · Any · Document. Supplemental "desvio do menor preço acima de 5%": nível Role(Diretor de Suprimentos) · Any. Supplemental "fornecedor da proposta em estado Prospect": nível PermissionHolder(manage:supplier-qualifications) · Any.

Documento. O comprador fecha a cotação escolhendo a proposta da Beta Serviços, 8% acima da menor — e a Beta ainda é Prospect.

Cadeia. Gerente de compras, depois o diretor de suprimentos, depois quem homologa. Escolhendo a menor proposta de um fornecedor SpendAuthorized, só o gerente. A justificativa de "não escolhi a mais barata" vira um nível a mais, não um campo de texto que ninguém lê.

PURCHASE_ORDER · alçada acumulada, verba estourada e comitê por quórum

Configuração. Base "acima de R$ 200 mil" (prioridade 30): nível 1 CostCenterOwnerUpToAmount · Any · Slice; nível 2 ApprovalGroup(Comitê de Compras) · Quorum(2) · Document, quatro membros. Supplemental "estado do orçamento = Exceeded": nível Role(Controller) · Any · Document, com prioridade que o posiciona antes do comitê. Supplemental "fornecedor sancionado": nível Role(Compliance) · Any.

Documento. Pedido de R$ 420 mil, 90% em /OPERACOES/MANUTENCAO/ (R$ 378 mil) e 10% em /OPERACOES/LOGISTICA/ (R$ 42 mil). O Owner de manutenção tem limite de R$ 100 mil; o de /OPERACOES/, R$ 300 mil; Fernanda, CFO, é Owner da raiz com hasUnlimitedAuthority. O orçamento devolve Exceeded para a fatia de manutenção; a overBudgetPolicy do nó é RequireApproval, que é exatamente a complementar acima.

Cadeia. A fatia de manutenção sobe dois níveis e chega a Fernanda num passo só — cada subida não é um passo; a de logística fica com o Owner local. Depois o Controller, depois o comitê: duas aprovações de quatro fecham, um "não" encerra. Variante de alçada estourada: se Fernanda tivesse limite de R$ 250 mil e ninguém na raiz tivesse autoridade ilimitada, a fatia de R$ 378 mil iria a ela mesmo assim, com authorityExceeded marcado na ativação, a tarefa mostrando a marca e nexio.approval-step.authority_exceeded chegando à controladoria enquanto a tarefa está aberta — e o /validate teria apontado a raiz antes de a política ser publicada.

CONTRACT · jurídico sempre, comitê e CEO acima de R$ 1 milhão

Configuração. Base "até R$ 1 milhão" (prioridade 20): Role(Jurídico) · Any · Document, depois Role(Diretor de Suprimentos) · Any. Base "acima de R$ 1 milhão" (prioridade 10): Role(Jurídico) · Any, depois ApprovalGroup(Comitê de Contratos) · Quorum(2) · Document, depois SpecificUser(CEO). Supplemental "categoria /SERVICOS/TI/": SpecificUser(CISO).

Documento. Contrato de 36 meses com a Beta para monitoramento de rede, R$ 1,2 milhão comprometidos, categoria /SERVICOS/TI/MONITORAMENTO/.

Cadeia. Jurídico, CISO (a complementar entra onde a prioridade dela manda), comitê com dois de quatro, CEO. Prazo e cláusula de exclusividade ainda não são critérios — a spec do Contract decide; até lá, contrato longo se recorta pelo valor total comprometido. É um dos três processos em que a votação com dissidência tolerada (Vote(n), fase 2) poderá ser ligada; hoje, no comitê, um voto contrário encerra.

SUPPLIER_REGISTRATION · quem cadastrou não aprova

Configuração. A semente: PermissionHolder(approve:suppliers) · Any · Document. Supplemental "flag tipo de pessoa = física": nível Role(Controladoria) · Any.

Documento. Ana, compradora com approve:suppliers, cadastra a Beta Serviços ME. Bruno e Carla também têm a permissão.

Cadeia. Ana é removida (RN-AP-08); Bruno e Carla recebem a tarefa, e o primeiro que decidir fecha. Faltou certidão? Bruno devolve, com motivo e comentário; Ana completa e reenvia na mesma instância. Fornecedor sancionado? Rejeita, e o motivo vai no uso SupplierReject. Tenant pequeno: se Ana fosse a única com a permissão, a submissão seria recusada com 422 nomeando o passo — é o onboarding que dá a permissão a mais alguém ou define o fallbackApproverId, nunca uma exceção à regra.

SUPPLIER · quatro áreas ao mesmo tempo, num nível só

Configuração. A semente é PermissionHolder(manage:supplier-qualifications) · Any; o tenant a substitui por Panel[Role(Jurídico), ApprovalGroup(Financeiro), Role(Engenharia), Role(Qualidade)] · Document. Supplemental "faixa de risco = alta": nível Role(Diretor de Suprimentos) · Any.

Documento. Homologação da Beta para /SERVICOS/ENGENHARIA/, com riskTier alto.

Cadeia. Um passo com quatro tarefas em paralelo, uma decisão de cada área; a primeira rejeição encerra. Fechado o painel, o diretor. Aprovada, a homologação grava o resultado e promove a Beta a SpendAuthorized na categoria. Quatro áreas ao mesmo tempo é o paralelo dentro do nível da Decisão 5 — não são quatro níveis.

BANK_ACCOUNT_CHANGE · quatro olhos, e um "não" basta

Configuração. A semente é PermissionHolder(approve:supplier-banks) · Any, mais controladoria quando o titular não bate. A recomendada: Base "média mensal a partir de R$ 100 mil": PermissionHolder(approve:supplier-banks) · Quorum(2) · Document. Supplemental "flag chave PIX não verificada": Role(Tesouraria) · Any. Supplemental "flag titular ≠ fornecedor": Role(Controladoria) · Any.

Documento. A Beta, com média de R$ 300 mil por mês nos últimos doze meses, pede troca de conta com chave PIX aleatória ainda não resolvida. Eduardo, de compras, registra a proposta; seis pessoas têm approve:supplier-banks, ele entre elas.

Cadeia. Eduardo é removido; cinco tarefas, duas aprovações fecham, um "não" encerra; depois a Tesouraria. Aprovada, o Fornecedor aplica a proposta e inicia a carência de 24 a 72 horas — e nexio.supplier-bank-account.change_requested já tinha ido ao contato financeiro antigo na abertura. É por este processo que Quorum não tolera dissidência: uma pessoa do financeiro desconfiar tem de bastar. Vote(n) nunca entra aqui.

ITEM · curadoria com controle extra por flag e por categoria

Configuração. A semente: PermissionHolder(approve:catalog-items) · Any. Supplemental "flag regulado": Role(Segurança do Trabalho) · Any. Supplemental "categoria /QUIMICOS/": Role(Engenharia de Processo) · Any.

Documento. Ana cria "Ácido sulfúrico 98% · tambor 200 L" em /QUIMICOS/ACIDOS/, com a flag regulado.

Cadeia. Aprovador do catálogo, Segurança, Engenharia — a ordem entre as complementares é a prioridade delas. Rejeição grava o motivo ItemReject no item e avisa a criadora (nexio.item.rejected, além do decided). Importação em lote com autoApprove não passa por aqui: é carga, com a permissão e a trilha da importação.

BUDGET · o exercício passa pela diretoria inteira

Configuração. Base de reserva: Role(Controller) · Any · Document, depois ApprovalGroup(Diretoria) · All · Document, depois SpecificUser(CEO). Supplemental "empresa = filial em regime especial": SpecificUser(Diretor da filial).

Documento. Exercício 2027 da Empresa Alfa, R$ 48 milhões orçados.

Cadeia. Controller; os cinco diretores, todos; CEO. Um "não" de qualquer diretor encerra — e o exercício volta como rejeitado, com motivo. Aprovado, o exercício é publicado e a versão anterior, arquivada. "Três de cinco bastam mesmo com dois contra" é o Vote(n) de fase 2, e este é um dos processos onde ele será permitido.

BUDGET_CHANGE · remanejamento por valor e por conta de destino

Configuração. Base "delta até R$ 50 mil" (prioridade 10): Role(Analista de Orçamento) · Any · Document. Base "acima de R$ 50 mil" (prioridade 20): Role(Analista de Orçamento) · Any, depois Role(Controller) · Any. Supplemental "conta contábil de destino em /1/ (investimento)": SpecificUser(CFO).

Documento. Remanejar R$ 150 mil de /MARKETING/EVENTOS/ para /MARKETING/DIGITAL/, conta de destino 4.1.2 (despesa).

Cadeia. Analista, depois Controller. Se a conta de destino fosse de investimento, o CFO entraria depois do Controller. O critério de conta contábil lê a linha de destino, que é a que recebe verba — é para isso que ele existe no remanejamento.

Uma requisição do começo ao fim

A requisição de Ana, do primeiro exemplo, acompanhada momento a momento — com o que o motor grava e o que a Notificação envia.

#MomentoO que o motor fazTrilha e aviso
1Ana submeteA Requisição chama POST /approvals no processo REQUISITION. O casador roda uma vez: base de R$ 50–500 mil (versão 3) e a complementar do DPO (versão 1). A cadeia é congelada com valor, taxa e rateio no snapshot; o nível do diretor nasce Skipped.Evento de submissão com as políticas e versões. Nada é enviado ainda.
2Nível 1 ativaO resolvedor lê os Owners vigentes e as delegações de hoje: Carla para a fatia de TI, Diego para a de Marketing. Vencimento calculado em horas úteis.nexio.approval-assignment.assigned para Carla e para Diego.
3Carla aprova; Diego não decide em 48 h úteisA fatia de TI fecha. O SLA de Diego vence; o processo tem escalationBehavior = Notify.nexio.approval-assignment.reminded para Diego e para Ana.
4Diego devolve para ajusteMotivo ApprovalReturn e comentário ("falta a cotação anexa"). A instância vai a Returned, viva; os passos da rodada viram Obsolete, inclusive o que Carla aprovou.nexio.approval-request.returned para Ana.
5Ana anexa e reenviaRodada 2 na mesma ApprovalRequest. O casador roda de novo; a cadeia é a mesma. Carla e Diego recebem tarefas novas — devolver reinicia a rodada, e a assinatura da rodada 1 fica na trilha, não na cadeia.assigned outra vez para os dois.
6Os dois aprovamTodos os passos de fatia do nível 1 fecharam; o passo do documento ativa.assigned para o DPO.
7O DPO aprova com ressalva"Mediante DPA assinado". A instância termina Approved; a ressalva fica visível no documento e no recebimento. O motor emite o evento de empenho com as fatias e os valores; quem grava o Commitment é o Orçamento.nexio.approval-request.decided (outcome Approved) para Ana.
8Semana seguinte, Ana edita para R$ 95 milAumento de 18,75% com tolerância de 10%: reavalia. A cadeia nova é igual — mesmas fatias, mesmas pessoas, diretor ainda abaixo de R$ 100 mil. As aprovações são preservadas; o evento registra o que mudou.Evento de reavaliação. Nenhuma tarefa nova.
9Ana edita para R$ 130 milReavalia: o nível do diretor ativa. Carla, Diego e o DPO continuam válidos com o mesmo escopo e a mesma política; entra uma tarefa nova.assigned para o Diretor de Compras.

Padrões que se repetem

PadrãoComo se montaOnde aparece
Quatro olhosUm nível Quorum(2) sobre PermissionHolder ou Role. Duas pessoas fecham; um "não" encerraAlteração bancária, cadastro de fornecedor pessoa física, item crítico
ComitêApprovalGroup com All ou Quorum(n); os membros mudam sem editar a políticaPedido acima da faixa, contrato, exercício orçamentário
Por área, em paraleloPanel com um alvo por área — uma decisão de cada, no mesmo nívelHomologação de fornecedor, item regulado que exige várias engenharias
Alçada progressivaCostCenterOwnerUpToAmount · Slice; o limite mora no vínculo, não na políticaRequisição e pedido — o padrão de quem vem do Protheus
Gestor do gestorCostCenterOwnerAtDistance(1) depois de CostCenterOwner, um nível por assinatura exigidaEmpresas que querem os dois níveis intermediários assinando
Diretor só acima de XactivationMinValue no nível, dentro da mesma políticaQualquer processo com valor
Controle extra por condiçãoUma Supplemental por condição — flag, categoria, fornecedor, UF, conta — cada uma com o nível que acrescentaDPO, Segurança, fiscal, compliance
Exceção por subárvoreBase amarrada no nó filho com prioridade menor que a do pai/TI/INFRA/ com alçada própria dentro de /TI/
Verba estourada exige o ControllerSupplemental com critério estado do orçamento = Exceeded; é o que RequireApproval significaRequisição, pedido, remanejamento
Interestadual passa pelo fiscalSupplemental com critério UF de entrega e nível Role(Fiscal) · Line — só as linhas de fora da UFRequisição e pedido com entrega em outra UF
Fornecedor ainda não homologadoSupplemental com critério fornecedor em estado ProspectCotação e pedido
Uma política para o grupo, uma por empresaBase sem critério de empresa mais Supplemental com critério empresa ou estabelecimento em listaMultiempresa, filial em regime especial, obra
Férias com teto, por processouma ApprovalDelegation por processo, cada uma com delegado, teto e fim; acima do teto a tarefa fica com o titular, e o vencimento faz o que o processo mandarQualquer aprovador
Compra pequena sem ninguémTodos os níveis da política com activationMinValue acima do valor: a instância termina Approved com o evento approval-request.approved_by_policy; o /validate avisa que a política tem faixa sem decisor humanoRequisição de baixo valor com fornecedor homologado — declarado na política, visível no simulador

O que não dá para montar — e o que usar no lugar

Pedidos frequentes
  • "Dois níveis ao mesmo tempo." Níveis são sequenciais. O paralelo é dentro do nível: Panel para áreas diferentes, Slice para fatias diferentes.
  • "Deixar o requisitante aprovar quando não há mais ninguém." Não existe chave. O tenant dá a permissão a uma segunda pessoa ou define o fallbackApproverId; ausência de aprovador legítima é declarada na política, com activationMinValue hoje e o nível Auto na fase 2.
  • "Aprovar sozinho depois de X dias." SLA vencido lembra, chama o gestor ou cria tarefa no nível acima. Nunca aprova.
  • "Aprovar metade das linhas." A decisão é do documento inteiro. Quem quer separar, separa antes de submeter.
Fase 2, com o padrão já decidido
  • "Comitê onde um voto contrário não derruba." Vote(n) — fecha em n, encerra só quando n fica inalcançável —, restrito a BUDGET, BUDGET_CHANGE e CONTRACT. Nunca em processo de controle antifraude.
  • Políticas mais elaboradas de encaminhamento por falta de autoridade podem evoluir; a v1 já impede conclusão sem autoridade explicitamente concedida.
  • "Condição A ou B numa política só." Critérios são conjunção. Duas complementares, uma por condição, produzem o mesmo nível — e o motor deduplica.
  • "Aprovar pelo e-mail, sem entrar." ApprovalToken está modelado e desligado por parâmetro.

Configuração

Parametrizações

O que o cliente ajusta sem código, e em que nível cada coisa vive. Parâmetro ausente herda o nível acima.

ApprovalSettings · 1 por tenant
ParâmetroTipoPadrãoEfeito
workWeekDayOfWeek[]seg · ter · qua · qui · sexOs dias que contam como úteis. Tirar o fim de semana resolve cinco sétimos do erro de contagem. Novo em v2.14
workdayStart · workdayEndtime · time09:00 · 18:00A janela diária em que o relógio corre. Não existe meio expediente: o dia é útil inteiro ou não é útil. Novo em v2.14
nonWorkingDatesdate[]feriados nacionais, semeadosAs datas que o relógio pula. Semeada pelo produto e editável pelo tenant, que acrescenta feriado municipal e recesso. Substitui o businessCalendarId em v2.14
allocationMaterialityPercentnumeric(9,6)5,00Fatia abaixo disso não gera passo próprio. Um valor por tenant, e o dono é este documento — o Centro de Custo cita, não redeclara (RN-AP-15 · RN-CC-24). Revisto em v2.15
allocationMaterialityAmount · allocationMaterialityCurrencynumeric(21,4) · char(3)1.000,00 · BRLMoeda explícita do limiar, inclusive em tenant multimoeda. Converter usando snapshot e registrar limiar efetivo. Vale o maior entre percentual e valor.
maxDelegationDaysint90Duração máxima de uma delegação. validTo é obrigatório e não passa disso; renovar é criar outra, com trilha. Novo em v2.10

A casa saiu em v2.16, e com ela a pendência do lado do fluxo. Estes parâmetros são a entidade ApprovalSettings — uma linha por tenant, criada no provisionamento com os padrões acima, com rotas e permissão próprias. TenantSettings não existe na plataforma, e Company e Establishment não eram o lugar: são parâmetros do tenant, não da empresa. Padrão do CurrencySettings — entidade nomeada por assunto, nunca um JSON genérico nem tabela-gaveta, que é o que a spec de Empresa recusou por escrito. costCenterMaxDepth saiu desta tabela: nunca foi parâmetro de aprovação — quem valida profundidade de árvore é o Centro de Custo, e é lá que ele mora, como gancho declarado.

ApprovalProcessSettings · 1 por tenant × processo
ParâmetroTipoPadrãoEfeito
fallbackApproverIdGuid—Último recurso quando toda resolução esvazia. Obrigatório para ativar o processo
rejectReasonUsage · returnReasonUsageReasonCodeUsageApprovalReject · ApprovalReturnO uso de motivo que a rejeição e a devolução gravam. ITEM usa ItemReject; SUPPLIER_REGISTRATION e SUPPLIER usam SupplierReject; os demais, o padrão. Do produto, não do tenant
subjectFlagsstring[]por processoAs flags que o assunto expõe ao critério "flags do assunto". Do produto, não do tenant
seededDefaultPolicybooltrue nos processos sem valorA política de reserva vem publicada pelo produto, com um nível PermissionHolder. O tenant edita; nunca fica sem
deduplicateConsecutivebooltrueMesma pessoa em níveis consecutivos assina uma vez
revaluationTolerancePercentnumeric(9,6)0,00Aumento tolerado sem reavaliar a cadeia
revaluationToleranceAmount · revaluationToleranceCurrencynumeric(21,4) · char(3)0,00 · BRLMoeda explícita. Vale o maior limiar após conversão; nunca amplia authorizedAmount nem dispensa mudança material.
defaultSlaHoursint48SLA herdado por política e nível
escalationBehaviorenumNotifyNone · Notify · Escalate. Vive aqui e em nenhum outro nível — política e nível herdam o do processo
allowEscalationbooltruefalse na semente do BANK_ACCOUNT_CHANGE. Desligado, o vencimento só lembra — e política com Escalate não publica. Novo em v2.10
delegablebooltruefalse na semente do BANK_ACCOUNT_CHANGE. Nenhuma delegação alcança um processo não delegável, nem a de escopo nulo. Novo em v2.10
requireCommentOnApproveboolfalseRejeição e devolução já exigem sempre
allowReassignbooltruePermite passar a tarefa a outro elegível, uma vez
allowBatchApprovalbooltrueAprovar várias tarefas numa ação — sempre um evento por tarefa
emailApprovalboolfalseLiga o ApprovalToken. Fase 2
Por política e por nível
ParâmetroNívelEfeito
priority · isDefault · kindpolíticaComo a política concorre ou empilha
criteriapolíticaQuinze critérios; ausência casa tudo
validFrom · validTopolíticaVigência da regra
fallbackApproverIdpolíticaSobrepõe o do processo
approverType · targetnívelEstratégia de resolução
mode · quorumnívelAny · All · Quorum(n)
scopeModenívelDocument, Line ou Slice (RN-AP-46). Revisto em v2.17
activationMinValuenívelAbaixo disso o nível não entra
maxApprovalHoursnívelSLA próprio; nulo herda
escalationTargetType · escalationTargetRefnívelPara quem a escalada vai. Obrigatório quando o processo escala e o nível não caminha árvore; proibido quando caminha — lá o alvo é o nó pai. Novo em v2.10
labelnívelO nome que o aprovador vê

Camada API

Endpoints

Configuração
RotaPermissãoO que faz
GET /approval-processesread:approval-processesProcessos e seus parâmetros
PUT /approval-processes/{id}manage:approval-processesParâmetros do processo e aprovador de reserva
GET /approval-settingsread:approval-settingsNova em v2.16. A linha do tenant: materialidade, teto de delegação, expediente e dias não úteis
PUT /approval-settingsmanage:approval-settingsNova em v2.16. Devolve o recurso, pela convenção de sucesso. Não afeta instância viva: o que a cadeia usou está congelado desde a submissão (RN-AP-06)
GET /approval-policiesread:approval-policiesFiltra por processo, tipo, vigência e nó
POST /approval-policiesmanage:approval-policiesCria em rascunho. Sobreposição entre bases é 409
POST /approval-policies/{id}/publishmanage:approval-policiesPublica versão nova e aposenta a anterior
GET /approval-policies/{id}/validateread:approval-policiesInconsistências: nó sem responsável, grupo vazio, papel sem membro, nível sem alvo, alvo que não alcança nenhuma empresa coberta pela política (RN-AP-39), sobreposição com outra base de mesma prioridade, raiz de centro de custo sem Owner com hasUnlimitedAuthority em política com nível CostCenterOwnerUpToAmount, limite preenchido sem approvalLimitCurrency, faixa em que todos os níveis ficam abaixo do activationMinValue, nível com escalonamento sem alvo declarado, política que escala em processo que não escala e — em política Supplemental com critério estado do orçamento — os nós de centro de custo em RequireApproval que ficariam sem cobertura se ela for publicada com outro critério ou aposentada (RN-AP-41)
GET /approval-groups · POST · PUTmanage:approval-groupsComitês e seus membros com vigência
GET /approval-delegations · POSTmanage:approval-delegations · :selfHerdado da v1.0, mais escopo e teto
POST /approvals:simulateread:approval-policiesA cadeia de um documento hipotético, sem criar nada
Execução
RotaPermissãoO que faz
POST /approvalsinternaSubmete um assunto. Chamada pelo módulo dono do documento, nunca pela tela
GET /approvalsaudit:approvalsNova em v2.17. A consulta transversal. Instâncias de qualquer documento do tenant, com filtro por documento (subjectType + subjectId), processo, estado, período, pessoa (requisitante, aprovador de direito ou quem agiu), rodada e marca de exceção — authorityExceeded, resolvedOutOfScope, resolvedEmptyOnActivation, budgetRequirementUnmet. É a rota que faltava para a permissão audit:approvals significar alguma coisa, e é ela que sustenta as duas promessas sem dono: o relatório de por que os pedidos voltam (motivo × processo × período, que o Motivo delega explicitamente a este módulo) e a pergunta de auditoria que não é sobre um documento conhecido. Paginada e ordenada por submissão; a trilha de cada uma continua em /events
GET /approvals/{id}read:approvalsA instância com passos, tarefas e a cadeia projetada até o fim
GET /approvals/inboxact:approvalsA fila. Tarefas pendentes do usuário, incluindo as recebidas por delegação, com filtro por processo, valor e vencimento
POST /approvals/{id}/assignments/{aid}/approveact:approvalsAprova. Aceita ?withProviso= para ressalva
POST /approvals/{id}/assignments/{aid}/rejectact:approvalsRejeita. Comentário obrigatório
POST /approvals/{id}/assignments/{aid}/returnact:approvalsDevolve para ajuste. Comentário obrigatório
POST /approvals/{id}/assignments/{aid}/request-infoact:approvalsPergunta ao requisitante. Pausa o SLA e avisa quem tem que responder (nexio.approval-assignment.info_requested)
POST /approvals/{id}/answer-inforead:approvals · só o requisitanteA resposta à pergunta aberta. Retoma o SLA do passo, entra na trilha e avisa quem perguntou. Não decide nada e não muda a cadeia
POST /approvals/{id}/assignments/{aid}/declineact:approvalsO delegado recusa a tarefa recebida. Ela volta ao titular — que recebe nexio.approval-assignment.assigned —, o evento registra a recusa com o id da delegação, e o documento continua sem decisão. Não existe para tarefa própria: quem é o aprovador de direito decide ou reatribui
POST /approvals/{id}/assignments/{aid}/reassignreassign:approvalsPassa a outro elegível, uma vez
POST /approvals/batch-approveact:approvalsN tarefas numa chamada, N eventos
POST /approvals/{id}/cancelinternaCancelamento vindo do documento. Libera empenho
POST /approvals/{id}/revaluateinternaDisparado pela alteração do documento. Preserva o que continua válido
GET /approvals/{id}/eventsread:approvalsA trilha completa, ordenada

Contrato

Contrato de erros

Mesma convenção do resto do produto: o status vem do sufixo do código, nunca da mensagem.

CódigoHTTPQuando
APPROVAL_POLICY_OVERLAP_CONFLICT409Duas políticas base com a mesma prioridade e critérios sobrepostos. Verificado na aplicação ao publicar, na mesma transação; guarda parcial no banco quando ele oferecer
APPROVAL_POLICY_IN_USE_CONFLICT409Desativar política com instância viva. A mensagem carrega a contagem
APPROVAL_GROUP_EMPTY_CONFLICT409Remover o último membro de grupo usado por política ativa
APPROVAL_ASSIGNMENT_ALREADY_DECIDED_CONFLICT409Duas decisões simultâneas na mesma tarefa. A segunda perde e é informada
APPROVAL_STEP_NOT_ACTIVE_CONFLICT409Decisão sobre passo que já fechou — tela desatualizada
APPROVAL_REQUEST_VERSION_CONFLICT409Novo em v2.17. expectedVersion diferente do version corrente da instância: outra decisão da mesma instância commitou primeiro. A segunda relê e decide de novo — a resposta traz o estado atual, não um erro genérico (RN-AP-44)
APPROVAL_NO_APPROVER_UNPROCESSABLE422Um passo resolveu vazio depois de todo o encadeamento. A mensagem nomeia o passo
APPROVAL_SELF_APPROVAL_UNPROCESSABLE422Requisitante tentando decidir a própria instância. Defesa em profundidade: a RN-AP-08 já o removeu de toda tarefa; este erro pega reatribuição, delegação ou token que o traga de volta
APPROVAL_NO_POLICY_UNPROCESSABLE422Nenhuma política base casou e o processo não tem política de reserva
APPROVAL_POLICY_AMBIGUOUS_UNPROCESSABLE422Novo em v2.6. Duas políticas base de mesma prioridade casaram o mesmo documento na submissão. A mensagem nomeia as duas. Nunca desempate silencioso
BUDGET_EXCEEDED_UNPROCESSABLE422Repassado do orçamento sem reembrulho, no mesmo padrão dos erros do GrydFiles. Política Block resolvida no nó e valor acima de disponível mais tolerância; traz available e wouldExceedBy por fatia. Substitui o antigo APPROVAL_BUDGET_EXCEEDED_UNPROCESSABLE_UNPROCESSABLE, que existia quando o motor decidia sozinho
BUDGET_UNCOVERED_ALLOCATION_UNPROCESSABLE422Novo em v2.18. Fatia lançada contra dimensão orçamentária que não existe no plano — sinal Uncovered. Também repassado do orçamento sem reembrulho, e é outro código: BUDGET_EXCEEDED_UNPROCESSABLE diz "estourou o disponível" e admite tolerância e política; este diz "não há verba cadastrada para esta conta neste centro de custo" e não admite nenhuma das duas. A mensagem nomeia centro de custo, conta e período, e sugere a linha coringa (RN-AP-53)
APPROVAL_DELEGATION_CHAIN_UNPROCESSABLE422Delegar para quem já delegou — cadeia não encadeia
APPROVAL_ESCALATION_TARGET_REQUIRED_UNPROCESSABLE422Novo em v2.10. Publicar política com escalonamento ligado e nível que não caminha árvore sem escalationTarget
APPROVAL_ESCALATION_NOT_ALLOWED_UNPROCESSABLE422Novo em v2.10. Escalonamento em processo com allowEscalation = false — o antifraude não muda de decisor por decurso
APPROVAL_DELEGATION_OVERLAP_CONFLICT409Novo em v2.10. Duas delegações vigentes do mesmo delegante para o mesmo processo, ou uma de escopo nulo cruzando qualquer outra. A mensagem nomeia a que já existe
APPROVAL_DELEGATION_PERIOD_UNPROCESSABLE422Novo em v2.10. Delegação sem validTo ou acima de maxDelegationDays
APPROVAL_SETTINGS_WORK_WEEK_EMPTY_UNPROCESSABLE422Novo em v2.16. Semana útil sem nenhum dia — o SLA nunca venceria, e nada pode ficar pendente para sempre
APPROVAL_SETTINGS_WORKDAY_WINDOW_UNPROCESSABLE422Novo em v2.16. workdayStart maior ou igual a workdayEnd
APPROVAL_SETTINGS_MATERIALITY_UNPROCESSABLE422Novo em v2.16. Percentual fora de 0–100 ou valor negativo. Zero nos dois é válido e significa "nenhuma fatia é dispensada"
APPROVAL_LEVEL_TARGET_OUT_OF_COMPANY_SCOPE_UNPROCESSABLE422Novo em v2.12. Publicar política com nível cujo alvo resolvido não alcança todas as empresas que ela alcança, ou com fallbackApproverId sem escopo irrestrito (RN-AP-40)
APPROVAL_DELEGATION_OUT_OF_COMPANY_SCOPE_UNPROCESSABLE422Novo em v2.12. Delegado cujo conjunto de empresas não contém o do delegante — a tarefa chegaria a quem toma 404 ao abri-la (RN-AP-40)
APPROVAL_DELEGATION_NOT_ALLOWED_UNPROCESSABLE422Novo em v2.10. Delegação que alcançaria processo com delegable = false
APPROVAL_REASSIGN_TARGET_UNPROCESSABLE422Destino da reatribuição não é elegível para o passo
APPROVAL_POLICY_NO_LEVEL_UNPROCESSABLE422Publicar política sem nível
APPROVAL_PROCESS_NO_FALLBACK_UNPROCESSABLE422Ativar processo sem aprovador de reserva. Não se aplica aos processos com política padrão semeada: neles a reserva é o próprio nível PermissionHolder, e permissão sem ninguém cai na RN-AP-09
APPROVAL_COMMENT_REQUIRED400Rejeição, devolução, ressalva, reatribuição ou pedido de informação sem comentário
APPROVAL_LIMIT_CURRENCY_NOT_CONVERTIBLE_UNPROCESSABLE422Novo em v2.17. Submeter documento cuja moeda não tem par de conversão vigente contra a moeda de um approvalLimit que a cadeia precisa avaliar. Repassado da Moeda sem reembrulho, no mesmo padrão do BUDGET_EXCEEDED_UNPROCESSABLE; a mensagem nomeia o par e o vínculo. Comparar sem par seria comparar números de moedas diferentes no ponto em que a alçada decide (RN-AP-52). Corrigido na validação cruzada de 09/09/2026: este documento nomeava CURRENCY_PAIR_NOT_CONFIGURED_UNPROCESSABLE, que na Moeda significa outra coisa — conversão pedida num sentido sem par cadastrado, em qualquer chamada. Repassar sem reembrulho exige repassar o código, e o desta recusa é o específico
REASON_CODE_REQUIRED_UNPROCESSABLE422Rejeição ou devolução sem reasonCodeId. Repassado do catálogo de Motivo sem reembrulho, como os erros do GrydFiles
APPROVAL_NOT_FOUND404Instância, passo ou tarefa inexistente neste tenant
APPROVAL_AUTHORITY_UNAVAILABLE_UNPROCESSABLE422Submissão sem responsável com autorização e alçada suficientes.
APPROVAL_BUDGET_REQUIREMENT_UNMET_UNPROCESSABLE422RequireApproval sem cobertura complementar aplicável.

Autorização

Permissões

PermissãoPúblicoCobre
read:approval-processes · :manageNexioAdminProcessos e parâmetros por processo
read:approval-settings · :manageAdmin, NexioAdminNova em v2.16. A configuração de tenant do motor. Separada da de processo porque muda o comportamento de todos os processos de uma vez
read:approval-policiesAdmin, ControllerLer políticas, validar e simular
manage:approval-policiesNexioAdminCriar, editar e publicar política
manage:approval-groupsNexioAdminComitês e membros
manage:approval-delegationsNexioAdminQualquer delegação do tenant
manage-own:approval-delegationsAprovadorA própria delegação
read:approvalsRequisitante, Aprovador, AuditoriaInstância, cadeia e trilha dos documentos a que a pessoa tem acesso
act:approvalsAprovadorDecidir sobre tarefas atribuídas a si. Não é guarda-chuva: a tarefa precisa ser sua
reassign:approvalsAprovador sênior, AdminPassar tarefa a outro elegível
audit:approvalsAuditoriaLer qualquer instância do tenant, inclusive de documentos fora do alcance da pessoa. Ganhou rota em v2.17 — GET /approvals, a consulta transversal com filtro. Até então era uma permissão sem endpoint que a exercesse

A separação entre act:approvals e reassign:approvals é deliberada: quem aprova não necessariamente pode escolher quem aprova no lugar dele — que é como uma alçada é contornada sem violar nenhuma permissão.

Rastreabilidade

Regras de negócio

RN-AP-01

Uma política base vence; complementares empilham

A cadeia final é a união ordenada e deduplicada da base com todas as complementares que casaram.

RN-AP-02

Prioridade desempata bases; especificidade não

Menor número vence. Sobreposição com a mesma prioridade é recusada ao publicar — verificada na aplicação, na mesma transação, com guarda parcial no banco quando ele oferecer. Se ainda assim duas casarem em execução, a submissão responde 422 APPROVAL_POLICY_AMBIGUOUS_UNPROCESSABLE.

RN-AP-03Reserva é habilitado, não privilegiado

Processo exige política base de reserva e fallbackApproverId explicitamente habilitado para suas empresas, operações e limites. Configuração sem cobertura não ativa; a execução revalida vigência e autoridade.

RN-AP-04

Critério de árvore guarda o nó e vale por rollup

Centro de custo e categoria, por prefixo de path. Descendentes criados depois já nascem cobertos; override no descendente é uma política de prioridade menor.

RN-AP-05

Política é versionada e a versão é imutável

Editar publica versão nova. Instâncias vivas continuam na versão que as formou.

RN-AP-06

A cadeia é congelada na submissão

Passos, escopos, modos, SLA, valor avaliado, taxa de câmbio, rateio e o limiar de materialidade efetivo. Nada disso muda sem reavaliação explícita — mexer na configuração no meio do caminho não reembaralha aprovadores de instância viva. O limiar entrou na lista em v2.15, pela mesma razão da taxa de câmbio: resolve-se uma vez, na submissão, e o que a cadeia usou fica no snapshot.

RN-AP-07

Os aprovadores são resolvidos na ativação do passo

Responsáveis, membros de grupo e delegação vigente são lidos na hora em que o passo ativa, não na submissão.

RN-AP-08

O requisitante nunca decide a própria instância

Removido de toda tarefa antes de qualquer outra regra, e não há chave que desligue isso — allowSelfApproval não existe. Se o passo esvaziar, sobe um nível nos tipos de árvore ou cai no reserva nos demais; se esgotar, 422 nomeando o passo e dizendo que o requisitante era o único aprovador possível. Vale para todo processo, inclusive os do Fornecedor e do Item: é esta regra — e não uma checagem no domínio — que garante "aprovador ≠ solicitante" na alteração bancária (RN-FOR-12). APPROVAL_SELF_APPROVAL_UNPROCESSABLE fica como defesa em profundidade na decisão. Revisada em v2.8, 06/09/2026.

RN-AP-09

Passo sem aprovador é recusa na submissão e desfecho declarado na ativação

422 nomeando o passo, na submissão. Só activationMinValue e dedupe produzem passo pulado. Revista em v2.17: a guarda de grupo ou vínculo vazio passa a valer também para expiração por vigência — e, quando o esvaziamento acontece na ativação, o desfecho é o da RN-AP-45, não o 422.

RN-AP-10

Paralelo é dentro do nível; entre níveis é sequencial

Any, All, Quorum(n) ou Panel. Rejeição encerra a instância em todos os modos — em Quorum(n), fecha em n aprovações e a primeira rejeição encerra. Votação com dissidência tolerada é modo próprio de fase 2, Vote(n), restrito aos processos que o produto declarar (BUDGET, BUDGET_CHANGE, CONTRACT); nunca uma releitura do Quorum, nunca em BANK_ACCOUNT_CHANGE. Revisada em v2.8, 06/09/2026.

RN-AP-11

Nada é aprovado por decurso de prazo

SLA vencido notifica ou escalona. AutoApprove não existe como comportamento de escalonamento.

RN-AP-12

Escalonar acrescenta caminho, não substitui

Em Any. A tarefa original continua viva, ambas fecham o passo, e o evento registra qual chegou primeiro. Revista em v2.17: em All, Quorum(n) e Panel a escalada substitui a tarefa do titular em vez de somar-se a ela — ver RN-AP-50.

RN-AP-13

SLA conta em horas úteis e pausa em pedido de informação

Revista em v2.14. Hora útil é hora dentro do expediente do tenant, em dia da semana marcado como útil e fora da lista nonWorkingDates, semeada com os feriados nacionais e editável pelo tenant. Não há calendário por estabelecimento: a lista é uma só. O tempo pausado é registrado separadamente.

RN-AP-14

Regras são avaliadas por linha e fatia; a decisão é do documento inteiro

Fatias do mesmo centro de custo são agregadas antes de gerar passo.

RN-AP-15

Alçada é avaliada sobre a fatia, acima do limiar de materialidade

Fatia imaterial é absorvida pela maior para efeito de fluxo e continua contabilizada integralmente. Esta é a regra única do produto para materialidade, e o limiar é um só por tenant — allocationMaterialityPercent e allocationMaterialityAmount, o que for maior —, resolvido na submissão e congelado com a cadeia (RN-AP-06). O Centro de Custo cita esta regra (RN-CC-24) em vez de redeclará-la; nenhuma outra página a redeclara. Revista em v2.15. Revista de novo em v2.17: materialidade é regra de roteamento, e só. O orçamento sempre vê a decomposição integral — a fatia absorvida continua sendo consumida na linha dela —, e Block numa fatia absorvida recusa a submissão do mesmo jeito. Sem isso haveria duas decomposições do mesmo rateio: a alçada nunca veria os 3% de /MARKETING/ e o orçamento devolveria Exceeded naquela fatia, de modo que ou a submissão é recusada por uma fatia que o motor decidiu ignorar, ou a marca da RN-AP-42 dispara no funcionamento normal — e controladoria que vê marca todo dia aprende a ignorá-la.

RN-AP-16

Alçada não soma entre pessoas; acumula subindo a árvore

Esgotada a subida, aprova o topo; o passo marca authorityExceeded na ativação, com evento na trilha e aviso à controladoria antes da decisão (RN-AP-31).

RN-AP-17

Delegação redireciona a tarefa e não encadeia

Titular e substituto ficam ambos na tarefa, com o id da delegação. Delegação de delegação é 422.

RN-AP-18

Rejeitar encerra; devolver mantém a instância viva

O reenvio abre rodada nova na mesma instância, com os passos anteriores marcados como obsoletos.

RN-AP-19

Comentário é obrigatório em toda ação que não seja aprovar

Rejeitar, devolver, pedir informação, reatribuir e aprovar com ressalva.

RN-AP-20Mudança material exige reavaliação

Fornecedor, conteúdo relevante, quantidade, unidade, escopo, condições e evidências materiais exigem reavaliação, mesmo sem aumento de valor. Tolerância numérica não autoriza ultrapassar o valor assinado.

RN-AP-21Assinatura preservada exige prova

Exigir equivalência de conteúdo relevante, escopo, evidências, policyId, policyVersion e nível; novo valor deve caber no authorizedAmount e moeda registrados. Preservar decisão original e registrar a equivalência, sem atribuir ao signatário conteúdo que não autorizou.

RN-AP-22

Aprovação em lote gera um evento por tarefa

Nunca um evento de lote.

RN-AP-23

Toda transição é evento, e o estado é projeção

Append-only, com autor, papel pelo qual agiu, delegação em vigor e passo alcançado.

RN-AP-24

Duas decisões simultâneas na mesma tarefa: a segunda é 409

Concorrência otimista na tarefa. A segunda pessoa é informada de quem decidiu e quando, não de um erro genérico. Revista em v2.17: esta regra continua valendo para a mesma tarefa, mas deixou de ser a única trava — o que decide o avanço do passo é sempre uma leitura agregada de outras tarefas, e essa serializa na raiz pela RN-AP-44.

RN-AP-25

A aprovação final emite evento de empenho; o cancelamento emite o inverso

O motor não conhece verba — quem cria e libera o Commitment é o módulo de orçamento.

RN-AP-26

A cadeia é consultável antes de existir

O simulador roda a mesma seleção, sem criar nada, inclusive contra política em rascunho e contra a configuração de uma data passada.

RN-AP-27

Quem decide bloqueio por verba é o orçamento, não o motor

O motor consulta evaluate:budget-states e obedece à overBudgetPolicy resolvida por nó, com a tolerância que o orçamento aplica. Não existe parâmetro de bloqueio no motor: dois lugares decidindo a mesma coisa divergem no primeiro documento com fatias em centros de custo diferentes — e quem erraria é o motor, que não conhece herança de política nem folga. É o princípio 6 desta spec aplicado a si mesma: o motor consome contratos, não caminha em árvores. Revista em v2.17: os sinais que o motor trata são quatro — Ok, Warning, Exceeded e Uncovered —, e Uncovered bloqueia sempre (RN-AP-53).

RN-AP-28

Toda aprovação humana é uma instância do motor

Onde uma segunda pessoa decide sobre o pedido de outra, o módulo dono do documento chama POST /approvals e aplica o efeito quando a instância termina. Nenhum módulo tem rota própria de aprovar ou rejeitar. Atos unilaterais com motivo (bloqueio, promoção manual, revogação, substituição, escape de duplicata, expurgo) não são aprovação. Princípio 7, 04/09/2026.

RN-AP-29

Processo sem valor nasce com política padrão semeada

A política de reserva vem publicada pelo produto, com um nível PermissionHolder na permissão que autorizava o clique na rota antiga. O tenant edita, nunca apaga a última. Processo com dinheiro continua exigindo configuração para ativar (RN-AP-03). Dia 1 igual ao de ontem, com uma diferença deliberada: quem cadastrou não aprova (RN-AP-08). Tenant em que o solicitante é o único detentor da permissão cai na RN-AP-09 — configuração de onboarding (segundo detentor ou fallbackApproverId do processo), não exceção; o painel de administração avisa "processo com um único aprovador possível". Revisada em v2.8, 06/09/2026.

RN-AP-30

O uso de motivo da rejeição é declarado pelo processo

rejectReasonUsage e returnReasonUsage vêm do produto, por processo. Código e comentário continuam obrigatórios juntos em toda rejeição e devolução (RN-AP-19) — inclusive onde a rota antiga do módulo exigia só o código.

RN-AP-31Falta de autoridade impede decisão financeira

Esgotar a árvore registra authorityExceeded, mas não concede autoridade à raiz. Só reserva com concessão e limite explícitos pode decidir. Sem ele, recusa na submissão ou Returned por configuração na ativação.

RN-AP-32

Cadeia inteira pulada termina aprovada pela política, com evento próprio

Todos os níveis abaixo do activationMinValue: a instância termina Approved sem tarefa e grava approval-request.approved_by_policy com a política e a versão. É a única aprovação sem pessoa do MVP — declarada na política, visível no simulador; /validate avisa a faixa sem decisor humano. Novo em v2.8, 06/09/2026.

RN-AP-33

Membro de grupo entra e sai com vigência, e o nível lê a vigente na ativação

ApprovalGroupMember guarda usuário, grupo e período. Dois períodos do mesmo usuário no mesmo grupo não se sobrepõem — verificado na aplicação, na mesma transação, com guarda de exclusão por interseção no banco quando ele oferecer (o banco ainda não está definido). Grupo referenciado por política ativa não pode ficar sem membro vigente: APPROVAL_GROUP_EMPTY_CONFLICT. Novo em v2.9, 08/09/2026.

RN-AP-34

Toda promessa do motor tem rota: a resposta ao pedido de informação e a recusa do delegado

O SLA que "retoma na resposta" precisa de POST /approvals/{id}/answer-info, e só o requisitante da instância a chama; a recusa que "devolve ao titular" precisa de POST /approvals/{id}/assignments/{aid}/decline, e só o delegado a chama. Nenhuma das duas decide o documento, as duas geram evento. Novo em v2.9, 08/09/2026.

RN-AP-35

Escalonamento tem alvo declarado, e processo de controle não escala

Em nível que caminha árvore, Escalate é o responsável do nó pai. Em qualquer outro tipo, o nível declara escalationTargetType e escalationTargetRef, verificados no /validate e na publicação (APPROVAL_ESCALATION_TARGET_REQUIRED_UNPROCESSABLE). O alvo entra na cadeia projetada pelo simulador: escalar nunca amplia o conjunto de aprovadores além do que foi mostrado antes. Processo com allowEscalation = false — semente do BANK_ACCOUNT_CHANGE — recusa Escalate na publicação e no vencimento apenas lembra. Novo em v2.10, 08/09/2026.

RN-AP-36

Uma delegação vigente por delegante e por processo, com fim obrigatório

Chave (delegante, scope, período) sem sobreposição, com scope nulo lido como "todos os processos"; verificação na aplicação, na mesma transação, e guarda no banco quando ele oferecer exclusão por interseção. validTo obrigatório, limitado a maxDelegationDays (padrão 90). Substitui a regra "no máximo uma ativa por pessoa", que não convivia com dez processos. Novo em v2.10, 08/09/2026.

RN-AP-37

A delegação move a tarefa dentro da alçada do titular, e não alcança processo não delegável

O passo foi resolvido com o approvalLimit de quem responde pelo nó; o delegado age em nome dele e o teto da delegação só reduz. Acima do teto, a tarefa fica com o titular. Processo com delegable = false não é alcançado por nenhuma delegação, nem pela de escopo nulo. A trilha continua gravando aprovador de direito, quem agiu e o id da delegação (RN-AP-17). Novo em v2.10, 08/09/2026.

RN-AP-38Voto e desfecho são atômicos

Serializar a raiz, registrar voto e chamar explicitamente o dono do processo na mesma transação. Sucesso efetiva estado e lançamento. Insuficiência orçamentária esperada devolve ReturnedForBudget, preserva voto e devolve documento/instância sem Approved ou novo compromisso; falha técnica reverte tudo.

RN-AP-39Alvo fora de empresa não decide

Revalidar concessão efetiva por empresa e operação na ativação e na decisão. Alvo revogado não atua; reserva passa pelas mesmas guardas. Sem elegível, recusar submissão ou devolver por configuração, com histórico e aviso durável.

RN-AP-40Permissão local limita cada ação

Exigir teto da plataforma, UserCompanyScope e UserCompanyRole com a permissão da operação na empresa. Publicação valida cobertura em todas as empresas do critério; reserva precisa da mesma cobertura e alçada. Delegação declara empresas e operações, só vale na interseção autorizada e nunca amplia valor ou escopo.

RN-AP-41Configuração incompleta é visível e impeditiva na execução

Validate identifica nós RequireApproval sem Supplemental correspondente, inclusive por expiração ou aposentadoria. Troca de configuração pode gerar pendência administrativa; nenhuma compra conclui financeiramente com a exigência não atendida.

RN-AP-42Exigência não cumprida não vira aviso permissivo

budgetRequirementUnmet registra a falha e gera aviso durável à controladoria. Ausência de cobertura Supplemental válida impede concluir aprovação financeira. Na submissão, APPROVAL_BUDGET_REQUIREMENT_UNMET_UNPROCESSABLE; durante a execução, Returned com motivo e votos preservados.

RN-AP-43Rodada não substitui versão efetiva

Reavaliação incrementa roundNumber e registra a revisão candidata. Pedido emitido mantém effectiveRevisionId e sua obrigação durante a aprovação da alteração. Só a alteração efetiva aplica delta; sua rejeição não libera a obrigação anterior.

RN-AP-44

Toda decisão serializa na raiz da instância

Nova em v2.17, 09/09/2026. ApprovalRequest tem version (int) e toda decisão passa expectedVersion; divergência é APPROVAL_REQUEST_VERSION_CONFLICT · 409, e a resposta traz o estado atual para que a pessoa releia e decida de novo. O fechamento do passo e a ativação do seguinte acontecem dentro da mesma transação da decisão — a mesma em que a RN-AP-38 grava o efeito.

Por que a raiz e não a tarefa. A RN-AP-24 já protegia duas decisões na mesma tarefa, mas o que decide o avanço é sempre uma leitura agregada de outras tarefas: contar aprovações em Quorum(n), fechar All, marcar as demais Obsolete em Any, ver que todas as fatias do nível fecharam. Quorum(2) com três pessoas decidindo ao mesmo tempo: duas transações leem uma aprovação, ambas gravam duas, ambas fecham o passo, ambas invocam o contrato de desfecho — e a verba é empenhada duas vezes. Simétrico: duas fatias paralelas ativam o nível seguinte duas vezes, e uma rejeição concorrente com a última aprovação deixa a instância Rejected com o efeito de aprovação aplicado. Um token por tarefa não pega nenhum dos três, porque em nenhum deles duas transações escrevem a mesma tarefa.

O sequence do ApprovalEvent é alocado nesta mesma transação, que é o que faz a trilha ter ordem total dentro da instância (RN-AP-48). O custo é o esperado: as decisões de uma instância viram fila. É o preço certo — a publicação de política já paga o equivalente com bloqueio de linha, e uma instância recebe unidades de decisão por dia, não milhares.

RN-AP-45Ativação vazia tem desfecho controlado

Registrar resolvedEmptyOnActivation e procurar reserva habilitado para empresa, operação e valor. Sem elegível, devolver a instância e documento por configuração pendente na transação do voto anterior, sem perdê-lo e sem deixar fila sem responsável. Avisos de expiração são duráveis e auxiliares.

RN-AP-46

Escopo Line: um passo por linha que casa, sem materialidade

Nova em v2.17, 09/09/2026. ApprovalScopeMode tem três valores — Document, Line e Slice. Um nível com scopeMode = Line gera um passo por linha do documento que casa o critério do nível, com scopeRef = id da linha e scopeAmount = valor da linha; os passos de linha do mesmo nível são paralelos entre si, como os de fatia. A materialidade não se aplica — ela dispensa fatia, e o que faz uma linha entrar aqui não é o valor dela, é o critério: uma linha interestadual de R$ 300 passa pelo fiscal exatamente como uma de R$ 300 mil. A dedupe é por (pessoa, linha), não por (pessoa, fatia): quem responde pelo fiscal de duas linhas recebe duas tarefas, porque o que ele examina é a linha, uma de cada vez.

Por que o valor já era usado e o enum não o tinha. O campo do passo declarava Document · Line · Slice, o padrão recomendado do documento era Role(Fiscal) · Line — só as linhas de fora da UF — e o campo do nível só oferecia Document · Slice: não havia caminho de configuração que gerasse um passo Line. O efeito era que o padrão "interestadual passa pelo fiscal", um dos diferenciais brasileiros anunciados, era inconfigurável. Escrever a regra custa três linhas; o valor no enum já estava pago.

RN-AP-47Reavaliação usa política vigente e guarda versões

A rodada nova recasa políticas vigentes e registra versões original e corrente. Preservar assinatura exige também policyVersion idêntica, conteúdo relevante equivalente, mesmo nível e escopo e valor dentro do autorizado (RN-AP-21).

RN-AP-48

A trilha é imutável, ordenada por sequence e de ações fechadas

Nova em v2.17, 09/09/2026. ApprovalEvent tem tabela de campos, enum fechado de ações e chave de ordenação (approvalRequestId, sequence), com o sequence monotônico dentro da instância e alocado na transação da decisão (RN-AP-44). Ordenar por occurredAt é proibido: aprovação em lote grava N eventos no mesmo instante, e fatias paralelas fecham juntas — o empate é a regra, não a exceção.

Imutável quer dizer sem rota de escrita. Não há PUT, não há DELETE e não há soft delete — a exclusão lógica que o resto do produto herda da plataforma não se aplica aqui, e a exceção é declarada. Corrigir um evento é gravar outro que o corrija, apontando no metadata o sequence corrigido. Mesma disciplina do razão orçamentário, mesma razão: trilha editável não prova nada, e a promessa "aprovação tem prova quando a auditoria pergunta quem autorizou" precisa ser verificável, não afirmada.

O que é coluna e o que é metadata: tudo por que a GET /approvals filtra é coluna. O metadata guarda o que é específico da ação — o alvo da reatribuição, o que mudou na reavaliação, qual passo a escalada acrescentou. Pôr ali um campo de filtro é como o esquema se perde de novo.

RN-AP-49

Ativar delegação transfere as tarefas já abertas do titular

Nova em v2.17, 09/09/2026. A entrada em vigor de uma ApprovalDelegation passa as tarefas Pending do titular que caem no seu escopo para o delegado: a ApprovalAssignment continua sendo a mesma linha — titular como aprovador de direito, delegado como quem age, id da delegação registrado (RN-AP-17) —, e cada transferência grava approval-assignment.delegated. O SLA não reinicia: o relógio é do passo, e zerá-lo faria da delegação o jeito mais barato de comprar prazo. Tarefa acima do teto de valor da delegação fica com o titular (RN-AP-37), e o encerramento da delegação faz o caminho de volta pelas que ainda estiverem abertas.

Por quê. A regra anterior cobria "o passo que ativa amanhã" e calava sobre o presente — e o presente é exatamente o que a pessoa deixou aberto ao sair de férias, com o SLA correndo. Ariba, Coupa e Oracle transferem a fila aberta na ativação. Sem isso, a delegação só funciona para quem se lembra de criá-la antes de a fila encher, que é a hora em que ninguém se lembra.

RN-AP-50Escalada não cria autoridade ou quórum

Any acrescenta caminho; All, Quorum e Panel substituem a tarefa do titular sem reduzir pessoas distintas exigidas. Escalado precisa da mesma autorização empresarial, alçada e segregação. Falta de elegível segue RN-AP-45.

RN-AP-51Alçada explícita é requisito

approvalLimit nulo não cobre valor; hasUnlimitedAuthority=true concede autoridade ilimitada por ato de cadastro. Esgotada a árvore sem cobertura, reserva também precisa de autoridade suficiente. Não exigir pessoa ilimitada quando limites finitos já atendem ao escopo.

RN-AP-52

A alçada compara na moeda do limite, com a taxa congelada na submissão

Nova em v2.17, 09/09/2026. approvalLimit anda com approvalLimitCurrency, obrigatória quando o limite está preenchido — inclusive no nó corporativo, que não tem empresa. A comparação converte o valor da fatia ou da linha para a moeda do limite, usando a taxa congelada na submissão do documento, e o ApprovalEvent da decisão grava a exchangeRate e a rateDate usadas. Submeter documento cuja moeda não tem par de conversão vigente contra a moeda de um limite que a cadeia precisa avaliar é recusa declarada — APPROVAL_LIMIT_CURRENCY_NOT_CONVERTIBLE_UNPROCESSABLE, repassado da Moeda sem reembrulho —, nunca comparação silenciosa. O código genérico de conversão sem par cadastrado é outro, e não é este.

Multimoeda vale desde a v1 (decisão de 09/09/2026): a functionalCurrency da empresa aceita qualquer ISO 4217, e a restrição "BRL na v1" saiu da spec de Moeda. Com Alfa em BRL e Beta em USD, o limite do nó corporativo comparado com um valor em USD sem conversão declarada é uma comparação entre moedas diferentes exatamente no ponto em que a alçada decide — e quatro páginas discordavam sobre se isso podia acontecer.

A taxa é congelada uma vez, e o razão herda. A conversão acontece na submissão e é o único momento de conversão na vida do documento; todo movimento do razão originado por ele herda exchangeRate e rateDate do documento, e a resolução própria de taxa do Orçamento vale só para movimento sem documento de origem. O Commitment é gravado dias depois da submissão, e as duas specs congelavam em momentos diferentes: com resolução no movimento, a verba consumiria um número diferente do que foi aprovado.

RN-AP-53Sinais distinguem novo compromisso de fato ocorrido

Uncovered e Block impedem novos compromissos; RequireApproval exige cobertura concluída. Recebimento/aceite ocorrido pode ser registrado com pendência, sem concluir nova autorização por esse caminho. Orçamento é a fonte da decisão.

Limites

Fora de escopo

Fora, com gancho previsto
  • Aprovação por e-mail — ApprovalToken está modelado e desligado por parâmetro.
  • Aprovador externo sem conta — depende do token acima.
  • Hierarquia de pessoas — RequesterManager e o escalonamento NotifyManager exigem a árvore de subordinação no GrydAuth. Enquanto ela não existir, nenhum dos dois está no enum: opção que ninguém consegue executar é dívida, não configuração.
  • Responsável por categoria — exige um vínculo no CategoryNode equivalente ao do centro de custo.
  • Condições compostas — hoje os critérios são conjunção. OU se faz com duas políticas complementares.
  • Votação com dissidência tolerada — Vote(n), modo de fase 2 restrito a BUDGET, BUDGET_CHANGE e CONTRACT; nunca em processo de controle antifraude, nunca como releitura do Quorum.
  • Políticas mais elaboradas de encaminhamento por falta de autoridade podem evoluir; a v1 já impede conclusão sem autoridade explicitamente concedida.
Fora, por decisão
  • Editor de fluxo em diagrama. Política com critérios e níveis ordenados cobre o que compras precisa; um motor de BPMN traz desvio, subprocesso e evento temporal — e uma classe de bug que ninguém depura em produção.
  • Aprovação parcial de documento. Aprovar 6 das 10 linhas parece útil e transforma um pedido em dois, quebrando negociação, rateio e recebimento. Se o requisitante quer separar, ele separa antes.
  • Assinatura eletrônica com certificado. Trilha auditável não é assinatura ICP-Brasil. Se o cliente precisar, é integração, não motor.
  • Autoaprovação — por decurso de prazo, por chave ou por exceção. Em nenhum processo, por nenhum parâmetro. O que existe é ausência declarada de aprovador: activationMinValue no MVP e o nível Auto na fase 2, sempre na política, visíveis no simulador e na trilha.
  • Aprovação fora do motor. Nenhum módulo do Nexio tem rota própria de aprovar; o que parece exceção é ato unilateral com motivo, e está listado em "O que o motor não decide". Se um novo ato precisar de segunda pessoa, ele entra na semente — não ganha rota.

Pendência que fica registrada: o processo REQUISITION avalia valor estimado, e o PURCHASE_ORDER avalia o valor negociado, que costuma ser menor. Falta decidir se um pedido cujo valor ficou abaixo do já aprovado na requisição precisa de nova cadeia. A recomendação é não precisar, com um parâmetro por processo para os clientes que exigem a dupla aprovação — mas isso depende de a Requisição existir, e volta como decisão daquela spec.