Documentação para desenvolvedores
Webhooks & API
O Proa Pay envia eventos da escola (lead ganho, fatura paga, matrícula) para o sistema que você quiser — CRM, planilha, automação. Cada entrega é um POST JSON assinado, com re-tentativas automáticas. Conecte direto ou via Make, Pluga, Zapier e n8n.
Visão geral
O fluxo é simples: um fato acontece na escola → o Proa Pay enfileira o evento → um worker entrega via HTTPS POST ao seu endpoint, assinado. Você valida a assinatura, responde 2xx e processa. Se o seu endpoint cair, re-tentamos com backoff.
Nesta primeira versão a saída é somente de eventos (webhooks-out). Uma API de leitura (REST) e conectores nativos vêm a seguir.
Configurar um endpoint
No painel admin, em Conta → Integrações → Criar webhook: informe um nome, a URL HTTPS do seu endpoint e marque os eventos desejados. Ao salvar, enviamos um evento ping de verificação — se ele responder 2xx, o endpoint fica ativo.
Você recebe um segredo de assinatura (whsec_…) uma única vez — guarde-o com segurança; é com ele que você valida cada entrega. Pode pausar, re-verificar e ver o histórico de entregas a qualquer momento.
Formato do evento
Toda entrega tem o mesmo envelope, com os campos específicos em data:
POST https://seu-endpoint.com/webhook
Content-Type: application/json
X-AppmaxEdu-Signature: sha256=<hmac-hex>
X-AppmaxEdu-Timestamp: 1781524667000
Idempotency-Key: 3f1c…(= id do envelope)
{
"id": "3f1c2b6e-...", // único por evento — use como chave de dedupe
"event": "invoice.paid",
"api_version": "v1",
"occurred_at": "2026-06-15T13:00:00.000Z",
"tenant_id": "a03bb4a7-...", // a escola (estabelecimento)
"data": { /* payload mínimo do evento — ver catálogo */ }
}Campos fixos do envelope (iguais em todo evento):
id— ID único do envelope (UUID) — repetido no header Idempotency-Keyevent— tipo do evento (ex.: 'invoice.paid')api_version— versão do contrato de saída (ex.: 'v1')occurred_at— quando o evento ocorreu (ISO 8601, UTC)tenant_id— ID opaco do estabelecimento que originou o eventodata— objeto com os campos específicos do evento (detalhados por evento abaixo)
Cabeçalhos HTTP de cada entrega (POST application/json):
content-type— sempre application/jsonX-AppmaxEdu-Signature— sha256=<HMAC-SHA256 de "{timestamp}.{corpo}" usando o segredo do endpoint>X-AppmaxEdu-Timestamp— momento do envio (Unix ms); entra na assinatura — rejeite se diferir mais de 5 min do agoraIdempotency-Key— igual ao id do envelope — use para deduplicar reentregas de um mesmo evento
Verificar a assinatura
A assinatura é HMAC-SHA256 sobre `${timestamp}.${corpo}`, com o timestamp também no cabeçalho. Rejeite requisições fora de uma janela de 5 minutos — isso previne ataques de replay. Compare em tempo constante.
import crypto from 'node:crypto';
// Em cada request, valide assinatura + janela de 5 min ANTES de processar.
function verificar(rawBody, headers, secret) {
const sig = headers['x-appmaxedu-signature'];
const ts = Number(headers['x-appmaxedu-timestamp']);
if (!Number.isFinite(ts) || Math.abs(Date.now() - ts) > 5 * 60 * 1000) {
return false; // fora da janela → possível replay
}
const esperado =
'sha256=' +
crypto.createHmac('sha256', secret).update(`${ts}.${rawBody}`).digest('hex');
const a = Buffer.from(esperado);
const b = Buffer.from(sig ?? '');
return a.length === b.length && crypto.timingSafeEqual(a, b); // tempo constante
}Catálogo de eventos
Cada evento carrega no data apenas os campos abaixo — o mesmo contrato que você marca na tela de criação do webhook. Esta lista é gerada a partir do catálogo do sistema: quando um evento novo entra, ele aparece aqui automaticamente.
lead.wonLead ganho (compra concluída)Um lead converte — a primeira fatura do checkout é paga.
lead_id— ID opaco do leadstage— estágio do lead (sempre 'ganho')invoice_ref— ID da fatura gerada (ou null)offer_code— código da oferta pela qual a venda entrou (8 caracteres, ex.: k8x2m4qp) ou null se a venda não veio de uma oferta
lead.stage_changedLead mudou de estágioO estágio do lead muda no funil (ex.: novo → em contato).
lead_id— ID opaco do leadfrom— estágio anteriorto— novo estágio
lead.offer_dispatchedOferta enviada ao leadUma oferta/link de checkout é disparada para o lead.
lead_id— ID opaco do leadchannel— canal do disparo (ex.: email/whatsapp)plan_slug— slug do produto ofertado
lead.payment_failedPagamento do lead não concluídoUm lead tenta pagar e não conclui (cartão recusado, Pix ou boleto não pago).
lead_id— ID opaco do leadreason— motivo (ex.: cartão recusado, aguardando pagamento)payment_method— método tentado (pix/cartão/boleto)plan_ref— ID do produto de interesse
invoice.paidFatura pagaUma fatura é confirmada como paga (após o re-fetch na Appmax).
invoice_id— ID opaco da faturastatus— status da fatura (ex.: 'paid')amount_cents— valor em centavos (inteiro)due_date— vencimento (YYYY-MM-DD)paid_at— data/hora do pagamento (ISO) ou nullkind— tipo (tuition/extra/agreement/down_payment)installment_number— número da parcela (ou null)installment_total— total de parcelas (ou null)payment_method— meio de pagamento (pix/credit_card/boleto/pix_automatico) ou nullenrollment_ref— ID opaco da vendaenrollment_created_at— quando a venda foi criada (ISO) ou null — no checkout, quando o comprador iniciou a compra; num Link de Pagamento, quando o link foi emitidopayer_ref— ID opaco do pagador (adulto)product_id— ID público do produto vendido (prod_…) ou nullproduct_name— nome do produto vendido ou nulloffer_code— código da oferta pela qual a venda entrou (8 caracteres, ex.: k8x2m4qp) ou null se a venda não veio de uma ofertaoffer_name— nome da oferta pela qual a venda entrou ou nullcoupon_code— código do cupom usado na venda ou null (no combo, só na venda principal)coupon_discount_cents— abatimento TOTAL do cupom na venda, em centavos, carimbado na cobrança que o resgatou (1ª fatura/entrada); null nas demais cobranças e quando não houve cupomutm_source— utm_source capturado no checkout da venda (último toque) ou null — texto vindo da URL do comprador: trate como entrada externautm_medium— utm_medium capturado no checkout da venda (último toque) ou null — texto vindo da URL do comprador: trate como entrada externautm_campaign— utm_campaign capturado no checkout da venda (último toque) ou null — texto vindo da URL do comprador: trate como entrada externautm_content— utm_content capturado no checkout da venda (último toque) ou null — texto vindo da URL do comprador: trate como entrada externautm_term— utm_term capturado no checkout da venda (último toque) ou null — texto vindo da URL do comprador: trate como entrada externaconversion— identificador da ação comercial que o lojista põe na URL do checkout (?conversion=lancamento-out26; só letras, dígitos e _ . : -, até 64) ou null — também vem da URL do comprador: trate como entrada externa
billing_agreement.completedCompra dividida paga por inteiroA última parte de uma compra dividida em partes é confirmada. Sai uma vez por compra; use para liberar acesso (o invoice.paid continua saindo a cada parte).
billing_agreement_id— ID opaco da compra divididastatus— status da compra (sempre 'fulfilled')principal_cents— valor total da compra em centavos (soma das partes)slice_count— em quantas partes a compra foi divididacompleted_at— quando a última parte foi confirmada (ISO)enrollment_ref— ID opaco da venda (ou null)enrollment_created_at— quando a venda foi criada (ISO) ou null — no checkout, quando o comprador iniciou a compra; num Link de Pagamento, quando o link foi emitidopayer_ref— ID opaco do pagador (adulto)invoice_refs— lista de IDs das faturas das partes pagasproduct_id— ID público do produto vendido (prod_…) ou nullproduct_name— nome do produto vendido ou nulloffer_code— código da oferta pela qual a venda entrou (8 caracteres, ex.: k8x2m4qp) ou null se a venda não veio de uma ofertaoffer_name— nome da oferta pela qual a venda entrou ou nullcoupon_code— código do cupom usado na venda ou null (no combo, só na venda principal)coupon_discount_cents— abatimento TOTAL do cupom na venda, em centavos, carimbado na cobrança que o resgatou (1ª fatura/entrada); null nas demais cobranças e quando não houve cupomutm_source— utm_source capturado no checkout da venda (último toque) ou null — texto vindo da URL do comprador: trate como entrada externautm_medium— utm_medium capturado no checkout da venda (último toque) ou null — texto vindo da URL do comprador: trate como entrada externautm_campaign— utm_campaign capturado no checkout da venda (último toque) ou null — texto vindo da URL do comprador: trate como entrada externautm_content— utm_content capturado no checkout da venda (último toque) ou null — texto vindo da URL do comprador: trate como entrada externautm_term— utm_term capturado no checkout da venda (último toque) ou null — texto vindo da URL do comprador: trate como entrada externaconversion— identificador da ação comercial que o lojista põe na URL do checkout (?conversion=lancamento-out26; só letras, dígitos e _ . : -, até 64) ou null — também vem da URL do comprador: trate como entrada externa
invoice.overdueFatura vencidaUma fatura passa do vencimento sem pagamento.
invoice_id— ID opaco da fatura
invoice.refundedFatura estornadaUma fatura paga é estornada.
invoice_id— ID opaco da faturastatus— status da fatura (ex.: 'refunded')amount_cents— valor em centavos (inteiro)due_date— vencimento (YYYY-MM-DD)paid_at— data/hora do pagamento original (ISO) ou nullkind— tipo (tuition/extra/agreement/down_payment)installment_number— número da parcela (ou null)installment_total— total de parcelas (ou null)payment_method— meio de pagamento (pix/credit_card/boleto/pix_automatico) ou nullenrollment_ref— ID opaco da vendaenrollment_created_at— quando a venda foi criada (ISO) ou null — no checkout, quando o comprador iniciou a compra; num Link de Pagamento, quando o link foi emitidopayer_ref— ID opaco do pagador (adulto)product_id— ID público do produto vendido (prod_…) ou nullproduct_name— nome do produto vendido ou nulloffer_code— código da oferta pela qual a venda entrou (8 caracteres, ex.: k8x2m4qp) ou null se a venda não veio de uma ofertaoffer_name— nome da oferta pela qual a venda entrou ou nullcoupon_code— código do cupom usado na venda ou null (no combo, só na venda principal)coupon_discount_cents— abatimento TOTAL do cupom na venda, em centavos, carimbado na cobrança que o resgatou (1ª fatura/entrada); null nas demais cobranças e quando não houve cupomutm_source— utm_source capturado no checkout da venda (último toque) ou null — texto vindo da URL do comprador: trate como entrada externautm_medium— utm_medium capturado no checkout da venda (último toque) ou null — texto vindo da URL do comprador: trate como entrada externautm_campaign— utm_campaign capturado no checkout da venda (último toque) ou null — texto vindo da URL do comprador: trate como entrada externautm_content— utm_content capturado no checkout da venda (último toque) ou null — texto vindo da URL do comprador: trate como entrada externautm_term— utm_term capturado no checkout da venda (último toque) ou null — texto vindo da URL do comprador: trate como entrada externaconversion— identificador da ação comercial que o lojista põe na URL do checkout (?conversion=lancamento-out26; só letras, dígitos e _ . : -, até 64) ou null — também vem da URL do comprador: trate como entrada externarefunded_at— quando a Proa confirmou o estorno (ISO) — igual ao occurred_at do envelope; é o instante do processamento, não o da processadora
invoice.chargeback_openedChargeback abertoA processadora informa uma contestação (chargeback) sobre uma fatura paga. A fatura NÃO muda de status: segue paga até a bandeira decidir; a data é a da primeira notícia.
invoice_id— ID opaco da faturastatus— status da fatura (ex.: 'paid')amount_cents— valor em centavos (inteiro)due_date— vencimento (YYYY-MM-DD)paid_at— data/hora do pagamento original (ISO) ou nullkind— tipo (tuition/extra/agreement/down_payment)installment_number— número da parcela (ou null)installment_total— total de parcelas (ou null)payment_method— meio de pagamento (pix/credit_card/boleto/pix_automatico) ou nullenrollment_ref— ID opaco da vendaenrollment_created_at— quando a venda foi criada (ISO) ou null — no checkout, quando o comprador iniciou a compra; num Link de Pagamento, quando o link foi emitidopayer_ref— ID opaco do pagador (adulto)product_id— ID público do produto vendido (prod_…) ou nullproduct_name— nome do produto vendido ou nulloffer_code— código da oferta pela qual a venda entrou (8 caracteres, ex.: k8x2m4qp) ou null se a venda não veio de uma ofertaoffer_name— nome da oferta pela qual a venda entrou ou nullcoupon_code— código do cupom usado na venda ou null (no combo, só na venda principal)coupon_discount_cents— abatimento TOTAL do cupom na venda, em centavos, carimbado na cobrança que o resgatou (1ª fatura/entrada); null nas demais cobranças e quando não houve cupomutm_source— utm_source capturado no checkout da venda (último toque) ou null — texto vindo da URL do comprador: trate como entrada externautm_medium— utm_medium capturado no checkout da venda (último toque) ou null — texto vindo da URL do comprador: trate como entrada externautm_campaign— utm_campaign capturado no checkout da venda (último toque) ou null — texto vindo da URL do comprador: trate como entrada externautm_content— utm_content capturado no checkout da venda (último toque) ou null — texto vindo da URL do comprador: trate como entrada externautm_term— utm_term capturado no checkout da venda (último toque) ou null — texto vindo da URL do comprador: trate como entrada externaconversion— identificador da ação comercial que o lojista põe na URL do checkout (?conversion=lancamento-out26; só letras, dígitos e _ . : -, até 64) ou null — também vem da URL do comprador: trate como entrada externachargeback_at— quando a contestação foi registrada na fatura (ISO)
enrollment.activatedVenda ativadaUma venda sai do rascunho e passa a gerar faturas.
enrollment_id— ID opaco da vendastatus— status (ex.: 'active')academic_period— referência (rótulo livre da venda)plan_ref— ID opaco do produto/planoenrollment_created_at— quando a venda foi criada (ISO) ou null — no checkout, quando o comprador iniciou a compra; num Link de Pagamento, quando o link foi emitidoinvoice_refs— lista de IDs das faturas ligadas à venda (no combo/order bump, inclui a fatura-pai que cobra o 1º período — confirme o pagamento pelo invoice.paid dela)product_id— ID público do produto vendido (prod_…) ou nullproduct_name— nome do produto vendido ou nulloffer_code— código da oferta pela qual a venda entrou (8 caracteres, ex.: k8x2m4qp) ou null se a venda não veio de uma ofertaoffer_name— nome da oferta pela qual a venda entrou ou null
enrollment.canceledVenda canceladaUma venda é cancelada.
enrollment_id— ID opaco da vendastatus— status (sempre 'canceled')reason— motivo do cancelamento (ou null)canceled_at— quando a venda foi cancelada na Proa (ISO) — igual ao occurred_at do envelopecanceled_invoice_refs— lista de IDs das faturas canceladasenrollment_created_at— quando a venda foi criada (ISO) ou null — no checkout, quando o comprador iniciou a compra; num Link de Pagamento, quando o link foi emitidoproduct_id— ID público do produto vendido (prod_…) ou nullproduct_name— nome do produto vendido ou nulloffer_code— código da oferta pela qual a venda entrou (8 caracteres, ex.: k8x2m4qp) ou null se a venda não veio de uma ofertaoffer_name— nome da oferta pela qual a venda entrou ou nullcoupon_code— código do cupom usado na venda ou null (no combo, só na venda principal)coupon_discount_cents— abatimento TOTAL do cupom na venda, em centavos, carimbado na cobrança que o resgatou (1ª fatura/entrada); null nas demais cobranças e quando não houve cupomutm_source— utm_source capturado no checkout da venda (último toque) ou null — texto vindo da URL do comprador: trate como entrada externautm_medium— utm_medium capturado no checkout da venda (último toque) ou null — texto vindo da URL do comprador: trate como entrada externautm_campaign— utm_campaign capturado no checkout da venda (último toque) ou null — texto vindo da URL do comprador: trate como entrada externautm_content— utm_content capturado no checkout da venda (último toque) ou null — texto vindo da URL do comprador: trate como entrada externautm_term— utm_term capturado no checkout da venda (último toque) ou null — texto vindo da URL do comprador: trate como entrada externaconversion— identificador da ação comercial que o lojista põe na URL do checkout (?conversion=lancamento-out26; só letras, dígitos e _ . : -, até 64) ou null — também vem da URL do comprador: trate como entrada externa
Enriquecimento opcional (dados do pagador). Em invoice.paid, invoice.refunded e invoice.chargeback_opened, o dono da escola pode ativar no endpoint o envio dos dados de contato do pagador (adulto) — mediante aceite de tratamento de dados (DPA). Quando ativo, data traz também:
payer_document— CPF do pagador (adulto), só dígitos — chave de conciliação; só quando o enriquecimento está ativo no endpointpayer_email— e-mail do pagador (adulto) — só quando o enriquecimento está ativo no endpointpayer_name— nome do pagador (adulto) — só quando o enriquecimento está ativo no endpointpayer_phone— telefone do pagador (adulto), só dígitos com DDI (ex.: 5511999999999) ou null — só quando o enriquecimento está ativo no endpoint
Use payer_document (CPF, só dígitos) como chave estável para casar a compra com o cadastro da sua ferramenta. Esses campos nunca incluem dados do aluno/menor.
Entregas e re-tentativas
Consideramos entregue qualquer resposta 2xx em até 10s. Em falha (timeout, 4xx/5xx), re-tentamos com backoff exponencial + jitter (até 16 tentativas, ~72h). Esgotadas as tentativas, a entrega vai para não-entregue e você pode reenviar manualmente pelo painel.
As entregas são at-least-once: o mesmo evento pode chegar mais de uma vez (ex.: você respondeu 2xx mas demorou). Deduplique pelo Idempotency-Key / id do envelope. Cada (evento, endpoint) é entregue com sucesso no máximo uma vez.
Segurança e LGPD
Por padrão os payloads são minimizados: carregam ids, valores em centavos, status, datas, produto/oferta e a origem da venda (UTMs e conversion do checkout). Dados de contato do pagador (adulto) — CPF, e-mail, nome e telefone — só saem quando o dono da escola ativa explicitamente o enriquecimento no endpoint, com aceite de tratamento de dados (DPA). Dados pessoais de alunos menores (nome, CPF, nascimento) nunca são enviados.
Só aceitamos endpoints HTTPS com IP público (bloqueamos redes internas — proteção SSRF), validado a cada envio. Registramos cada entrega (o que saiu, para qual endpoint, quando) para atender pedidos de titular e auditoria.