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.
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:
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.
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:
{
"error": {
"code": "requester_not_found",
"message": "Nenhum usuário com este e-mail neste tenant."
}
}
Códigos mais comuns, entre os endpoints:
| Código | HTTP | Significado |
|---|---|---|
| unauthorized | 401 | Header Authorization ausente, malformado, ou credencial inválida/inativa. |
| invalid_body | 422 | Corpo da requisição não bate com o schema esperado. |
| request_type_not_found | 404 | requestTypeKey não existe (ou está inativo) neste tenant. |
| requester_not_found | 404 | Nenhum usuário com o requesterEmail informado neste tenant. |
| invalid_custom_fields | 422 | customFields não bate com os campos obrigatórios do tipo de solicitação. |
| already_final | 409 | A solicitação já está em um status final (aprovada/rejeitada/cancelada). |
| external_approval_disabled | 403 | Esta credencial não tem permissão para aprovar via API — ver Aprovação via API. |
| invalid_cursor | 422 | Parâmetro since inválido em GET /decisions. |
Endpoints
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
| Campo | Tipo | Descrição | |
|---|---|---|---|
| externalSystem | string | obrigatório | Identificador livre da origem, ex: "protheus". |
| externalRef | JSON | obrigatório | Valor opaco que identifica o documento na origem — usado para idempotência. |
| requestTypeKey | string | obrigatório | Aponta para o integrationKey de um tipo de solicitação habilitado. |
| title | string | obrigatório | Título exibido na listagem e nas notificações. |
| description | string | opcional | Texto livre. |
| amountCents | integer | opcional | Valor 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. |
| requesterEmail | string | obrigatório | Precisa corresponder a um usuário já existente no tenant. |
| customFields | objeto | opcional | Valores dos campos dinâmicos configurados no tipo de solicitação. |
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" }
}'
{
"requestId": "cmr1a2b3c4d5",
"status": "pending",
"url": "https://aprovaai.cyberpolos.net/requests/cmr1a2b3c4d5"
}
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 "https://aprovaai.cyberpolos.net/api/integrations/decisions?since=$CURSOR" \
-H "Authorization: Bearer <connectionId>.<secret>"
{
"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_requestednão aparece aqui — fica só visível dentro do aprovaAI. - Quando não há nada novo, a resposta é
decisions: []com o mesmonextCursorrecebido — nuncanull. decidedExternally: truesignifica que esta decisão veio do seu próprioPOST .../status— não escreva ela de volta na origem, evita um loop.- Persista o
nextCursorlocalmente entre chamadas. Semsince, a listagem começa do início.
Confirma que a credencial e o tenant estão ativos. Sem efeitos colaterais — livre para o seu conector validar conectividade antes de operar.
{ "ok": true, "tenant": "empresa-demo", "connection": "ERP Produção" }
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.
| Campo | Tipo | Descrição | |
|---|---|---|---|
| status | string | obrigatório | cancelled, rejected ou approved. |
| reason | string | obrigatório* | *Obrigatório para rejected e approved; opcional para cancelled. |
| comment | string | opcional | Se preenchido, publica uma mensagem no chat da solicitação (visível para quem acompanha). |
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"
}'
{ "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. cancellederejectedsão sempre permitidos.approvedexige 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.
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
- Guarde a API key recebida com segurança — ela não é reexibida.
- Traduza cada documento do seu sistema para o payload de
POST /requests(a forma de mapear campos é específica do seu sistema de origem). - Persista localmente a relação entre a chave do documento na origem e o
requestIdretornado. - Faça polling periódico de
GET /decisions, persistindo onextCursora cada chamada. - 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.
- Se o documento de origem for cancelado, negado ou aprovado fora do aprovaAI depois de sincronizado, chame
POST .../statuspara refletir isso — sem esse passo, a solicitação fica pendente indefinidamente.