>_aprovaAI Documentação da API
Manual do usuário
Documentação para desenvolvedores

API de Integração

Conecte um ERP ou outro sistema ao aprovaAI: crie solicitações a partir de documentos externos, deixe o motor de aprovação do aprovaAI conduzir a decisão, e consulte o resultado para refletir de volta na origem.

Introdução

A API de integração é agnóstica de sistema de origem — não assume Protheus, SAP, RM ou qualquer ERP específico. Quem fala a língua de um sistema específico é um conector (agente) que você mantém, fora do aprovaAI, traduzindo entre o seu sistema e este contrato.

O aprovaAI nunca inicia conexão com o seu sistema. Ele só recebe chamadas autenticadas e responde — buscar dados na origem e escrever o resultado de volta é responsabilidade do seu conector.

Uma solicitação criada por esta API se comporta como qualquer outra a partir daí: passa pelo motor de workflow configurado no tenant, aparece nas listagens do aprovaAI, gera notificações in-app/push/e-mail para os aprovadores, e tem comentários e histórico.

Base URL
https://aprovaai.cyberpolos.net

Autenticação

Cada conexão de integração recebe uma API key no formato <connectionId>.<secret>, exibida uma única vez no momento em que a credencial é criada — o aprovaAI não guarda nem reexibe o segredo em texto puro depois disso. Envie em todas as chamadas como Bearer token:

Header
Authorization: Bearer cm3xh2j4k0001abcd.k7f9QpX2yT8vR1nZmW3sLd

A credencial enxerga só o próprio tenant, e só pode criar solicitações de tipos habilitados para integração (ver requestTypeKey em Criar solicitação). No piloto, a emissão de credenciais é feita pela equipe aprovaAI — fale com o seu contato para receber a sua.

Idempotência

Toda solicitação criada é identificada pelo par externalSystem + externalRef que você envia. Se o seu conector reenviar a mesma criação — por retry após timeout, ou reprocessamento — o aprovaAI nunca duplica: devolve a solicitação já existente.

Na prática

externalRef é um valor opaco pro aprovaAI — pode ser qualquer JSON que identifique o documento na origem (filial, tipo de documento, chave). Não precisa ser um único campo; ele só existe para o aprovaAI e é devolvido igual nas consultas de decisão.

Erros

Toda resposta de erro (4xx/5xx) segue o mesmo formato, com um código estável para tratamento programático e uma mensagem legível:

Resposta de erro
{
  "error": {
    "code": "requester_not_found",
    "message": "Nenhum usuário com este e-mail neste tenant."
  }
}

Códigos mais comuns, entre os endpoints:

CódigoHTTPSignificado
unauthorized401Header Authorization ausente, malformado, ou credencial inválida/inativa.
invalid_body422Corpo da requisição não bate com o schema esperado.
request_type_not_found404requestTypeKey não existe (ou está inativo) neste tenant.
requester_not_found404Nenhum usuário com o requesterEmail informado neste tenant.
invalid_custom_fields422customFields não bate com os campos obrigatórios do tipo de solicitação.
already_final409A solicitação já está em um status final (aprovada/rejeitada/cancelada).
external_approval_disabled403Esta credencial não tem permissão para aprovar via API — ver Aprovação via API.
invalid_cursor422Parâmetro since inválido em GET /decisions.

Endpoints

POST /api/integrations/requests

Cria uma solicitação a partir de um documento externo. Idempotente — chamar de novo com o mesmo externalSystem + externalRef retorna a solicitação já existente em vez de duplicar.

Corpo da requisição

CampoTipoDescrição
externalSystemstringobrigatórioIdentificador livre da origem, ex: "protheus".
externalRefJSONobrigatórioValor opaco que identifica o documento na origem — usado para idempotência.
requestTypeKeystringobrigatórioAponta para o integrationKey de um tipo de solicitação habilitado.
titlestringobrigatórioTítulo exibido na listagem e nas notificações.
descriptionstringopcionalTexto livre.
amountCentsintegeropcionalValor em centavos (ex: 600000 = R$ 6.000,00) — inteiro, nunca decimal solto: evita ponto flutuante e a validação de tipo já rejeita string/fração. Aciona as regras de valor do workflow.
requesterEmailstringobrigatórioPrecisa corresponder a um usuário já existente no tenant.
customFieldsobjetoopcionalValores dos campos dinâmicos configurados no tipo de solicitação.
cURL
curl -X POST https://aprovaai.cyberpolos.net/api/integrations/requests \
  -H "Authorization: Bearer <connectionId>.<secret>" \
  -H "Content-Type: application/json" \
  -d '{
    "externalSystem": "protheus",
    "externalRef": { "branch": "01", "docType": "SC7", "key": "000123" },
    "requestTypeKey": "pedido_de_compra",
    "title": "Pedido de Compra 000123 — Dell Brasil",
    "amountCents": 600000,
    "requesterEmail": "solicitante@empresa.com",
    "customFields": { "fornecedor": "Dell Brasil", "justificativa": "Notebooks para o time" }
  }'
Resposta — 201 Created (ou 200 se já existia)
{
  "requestId": "cmr1a2b3c4d5",
  "status": "pending",
  "url": "https://aprovaai.cyberpolos.net/requests/cmr1a2b3c4d5"
}
GET /api/integrations/decisions?since=<cursor>

Lista solicitações originadas por esta API cujo status mudou para um estado final desde o cursor informado. Paginado por cursor opaco, não por data — evita perder eventos por empate de timestamp.

cURL
curl "https://aprovaai.cyberpolos.net/api/integrations/decisions?since=$CURSOR" \
  -H "Authorization: Bearer <connectionId>.<secret>"
Resposta
{
  "decisions": [
    {
      "requestId": "cmr1a2b3c4d5",
      "externalSystem": "protheus",
      "externalRef": { "branch": "01", "docType": "SC7", "key": "000123" },
      "status": "approved",
      "decidedAt": "2026-07-19T18:00:00Z",
      "decidedExternally": false,
      "history": [
        { "step": "Aprovação do Gestor", "decision": "approved",
          "approverEmail": "gestor@empresa.com", "comment": null }
      ]
    }
  ],
  "nextCursor": "MjAyNi0wNy0xOVQxODowMDowMC4wMDBafGNtcjFhMmIzYzRkNQ"
}
  • Só reporta estados finais: approved, rejected, cancelled. changes_requested não aparece aqui — fica só visível dentro do aprovaAI.
  • Quando não há nada novo, a resposta é decisions: [] com o mesmo nextCursor recebido — nunca null.
  • decidedExternally: true significa que esta decisão veio do seu próprio POST .../status — não escreva ela de volta na origem, evita um loop.
  • Persista o nextCursor localmente entre chamadas. Sem since, a listagem começa do início.
GET /api/integrations/health

Confirma que a credencial e o tenant estão ativos. Sem efeitos colaterais — livre para o seu conector validar conectividade antes de operar.

Resposta
{ "ok": true, "tenant": "empresa-demo", "connection": "ERP Produção" }
POST /api/integrations/requests/{requestId}/status

Aplica um status decidido fora do workflow interno do aprovaAI — para quando o documento de origem muda de estado no seu sistema depois de já sincronizado. É uma válvula de exceção: o caminho normal continua sendo os aprovadores decidindo dentro do aprovaAI.

CampoTipoDescrição
statusstringobrigatóriocancelled, rejected ou approved.
reasonstringobrigatório**Obrigatório para rejected e approved; opcional para cancelled.
commentstringopcionalSe preenchido, publica uma mensagem no chat da solicitação (visível para quem acompanha).
cURL
curl -X POST https://aprovaai.cyberpolos.net/api/integrations/requests/cmr1a2b3c4d5/status \
  -H "Authorization: Bearer <connectionId>.<secret>" \
  -H "Content-Type: application/json" \
  -d '{
    "status": "cancelled",
    "reason": "Pedido cancelado pelo comprador direto no Protheus"
  }'
Resposta — 200 OK
{ "requestId": "cmr1a2b3c4d5", "status": "cancelled" }

Regras

  • Só se aplica a solicitações originadas por esta API, em status não-final (draft/pending). Já final → 409 already_final.
  • cancelled e rejected são sempre permitidos.
  • approved exige que a credencial tenha permissão explícita — ver Aprovação via API abaixo.

Aprovação via API

Aprovar uma solicitação sem passar pelos aprovadores nomeados do aprovaAI é uma decisão de governança, não só técnica — por padrão, essa credencial não pode fazer isso. allowExternalApproval = false

Quando habilitada explicitamente para uma credencial, uma aprovação enviada por POST .../status continua contando como aprovada em todos os fluxos e relatórios, mas fica marcada como decidedExternally: true e visualmente destacada na interface do aprovaAI — distinta de uma aprovação que passou pelos aprovadores do workflow.

Quando usar

Use para refletir uma aprovação que já aconteceu de fato no sistema de origem (ex: alguém com alçada aprovou direto no ERP), não como atalho para pular o workflow do aprovaAI. Se a sua credencial precisa desse comportamento, peça para habilitá-lo junto da equipe aprovaAI.

Fluxo de integração recomendado

  1. Guarde a API key recebida com segurança — ela não é reexibida.
  2. Traduza cada documento do seu sistema para o payload de POST /requests (a forma de mapear campos é específica do seu sistema de origem).
  3. Persista localmente a relação entre a chave do documento na origem e o requestId retornado.
  4. Faça polling periódico de GET /decisions, persistindo o nextCursor a cada chamada.
  5. Aplique a decisão de volta no seu sistema, com sua própria lógica de retry — o aprovaAI não sabe se essa escrita teve sucesso.
  6. Se o documento de origem for cancelado, negado ou aprovado fora do aprovaAI depois de sincronizado, chame POST .../status para refletir isso — sem esse passo, a solicitação fica pendente indefinidamente.