Duas formas de receber
Endpoints do painel
Cadastre URLs fixas em Integrações → Webhooks e escolha os eventos (e,
se quiser, os produtos). Recebem todos os eventos que casarem.
postbackUrl da cobrança
Ao criar uma cobrança via API, informe um
postbackUrl. Ele recebe os
eventos daquela transação específica — somado aos endpoints do painel.Eventos
Formato do payload
Toda entrega é umPOST com corpo JSON neste formato:
data nos eventos payment.*:
O objeto salesRep
salesRep e salesRefCode aparecem em todo evento payment.* e subscription.* — nunca ficam
ausentes, mesmo sem vendedor (nesse caso salesRep vem null).
Quando
salesRep é null. Venda direta (sem link de vendedor), código de
referência que não resolveu para nenhum vendedor, ou cadastro do vendedor removido —
nesses três casos salesRep é null, e salesRefCode mantém o valor bruto recebido
(ou também null, se não houve código). Um vendedor desativado continua
aparecendo normalmente: desligar não muda a atribuição já gravada. Numa renovação
de assinatura, o Payment do ciclo tem salesRep: null quando não tem atribuição
própria — mesmo que a assinatura tenha vendedor. O subscription.renewed
correspondente sempre traz o vendedor certo, porque vem da própria assinatura.O objeto customer
Todos os eventos payment.* e subscription.* trazem o objeto customer com os dados que
o comprador preencheu no checkout. Campos não coletados naquele fluxo vêm como null.
O
address, quando presente, tem o formato:
Quando cada campo existe. O endereço é coletado no cartão
(Asaas/Pagar.me) e no boleto — em pagamentos PIX o checkout pede só os
dados pessoais, então
customer.address vem null. O telefone é coletado no
cartão (gateways BR) e no PIX Pagar.me. Pagamentos anteriores a esta versão
não têm os campos novos preenchidos (null).data.metadata traz a metadata que você enviou na
criação — use o invoiceId (ou o que for) para dar baixa.
O objeto offer
Todo evento cujo data tenha um offerId — pagamentos de checkout de oferta e todos os
eventos subscription.* — vem com o objeto offer completo embutido, para você reagir aos
dados comerciais (nome, preço, ciclo) sem uma chamada extra à API.
Cobranças avulsas não têm oferta. Eventos de cobranças criadas via
POST /v1/charges (sem uma oferta) não trazem o objeto offer — nesses casos
offerId é null. Use o data.metadata e o chargeId para reconciliar.Vendas de link de pagamento. Um pagamento feito por um link de pagamento do
painel dispara os mesmos eventos
payment.* da cobrança avulsa, com
paymentLinkId preenchido e sem produto nem oferta (productId e offerId
vêm null). Por isso um endpoint filtrado por produto não recebe vendas de
link. Para receber, use um endpoint sem filtro de produto. Com os dados do link
e as tags, assine os eventos de links de pagamento.subscription.* usam um data menor (id, status, offerId, customerEmail,
customer, currentPeriodEnd, salesRepId, salesRep, salesRefCode) — também com os objetos
customer e offer embutidos. Aqui salesRepId/salesRep/salesRefCode vêm do vendedor gravado na PRÓPRIA assinatura
(preenchido uma vez, no 1º ciclo, e nunca muda depois — por isso todo subscription.* traz o
vendedor certo, mesmo quando o Payment de uma renovação não tem atribuição própria):
Eventos da área de membros
enrollment.created, enrollment.revoked, lesson.completed, course.completed,
certificate.issued, enrollment.cohort_changed e enrollment.expired usam o mesmo envelope
{ id, event, createdAt, data }, a mesma assinatura e os mesmos headers dos eventos de pagamento.
O data traz os mesmos objetos das rotas /v1 da área de membros: student,
course (resumo), enrollment, lesson, progress (resumo) e certificate. Um parser só serve
para o evento e para a consulta.
data.producté o produto do curso. O filtro por produto do endpoint usa esse produto. No curso comprado por order bump, vale o produto do próprio curso, e não o do item principal.- Esses eventos não trazem
salesRep,salesRefCode,customernemoffer. - O
dataé o estado do momento do disparo. O retry automático e o reprocessamento manual reenviam o mesmo corpo. Umenrollment.createdreprocessado depois do reembolso continua comstatus: "ACTIVE". - O aluno de pré-visualização do seller nunca gera evento.
Exemplos do data
Regras dos eventos
reactivated. Vemfalsena matrícula nova. Vemtruequando uma compra ou uma liberação manual devolve o acesso a uma matrícula revogada ou comexpiresAtjá passado. A matrícula é a mesma, com o mesmoid. Na reativação por compra,enrollment.cohortcontinua a turma que a matrícula já tinha. Renovação de assinatura, reentrega do mesmo pagamento e nova liberação de um acesso ainda vivo não disparamenrollment.created.lesson.completedsai uma vez. Sai na primeira conclusão da aula pelo aluno, pelo player ou pela aprovação no quiz. Batimentos simultâneos, quiz refeito e aula revista não geram outro. Batimento de progresso que não conclui a aula nunca gera evento.course.completedpode repetir. Sai quando a conclusão de uma aula deixaprogress.completedLessonsigual aprogress.totalLessons, com o certificado do curso ligado ou desligado. Cada conclusão do curso sai uma vez, mesmo quando o aluno conclui as últimas aulas ao mesmo tempo. Se o curso ganhar aulas e o aluno concluir todas de novo, sai outro. Curso sem aula contada nunca dispara.- Regra de
totalLessons. Conta as aulas publicadas, de módulos publicados, que a turma da matrícula vê.percentécompletedLessons / totalLessons × 100arredondado, e 0 quandototalLessonsé 0.completedAté a conclusão mais recente entre as aulas contadas quando todas estão concluídas, enullnos demais casos. É a mesma conta deGET /v1/students/{id}/progress. certificate.issuedsai uma vez por certificado. Com certificado ligado, a conclusão que torna o aluno elegível emite o certificado na hora. Se a emissão falhar nesse momento, ela acontece no acesso seguinte do aluno à página ou ao PDF do certificado.enrollment.cohort_changedsai na troca de turma. Sai quando o seller move a matrícula para outra turma do curso.previousCohorté a turma de antes, eenrollment.cohorté a turma nova. Mover para a turma em que o aluno já está não dispara. A compra que reativa uma matrícula revogada ou vencida não muda a turma: a matrícula volta na turma que já tinha, qualquer que seja a turma da oferta, e dispara sóenrollment.created, semenrollment.cohort_changed.enrollment.expiredsai uma vez por vencimento. Sai quando passa oexpiresAtde uma matrículaACTIVE, numa varredura horária, em até 1 hora. Odatatrazstatus: "ACTIVE"ehasAccess: false, porque o vencimento não muda o status. Se oexpiresAtfor adiado antes da varredura, o evento não sai. Se o aluno voltar a ter acesso e o acesso vencer de novo, sai outro. Vencimentos anteriores à chegada deste evento não geram aviso.- A ordem de chegada não é garantida. Um
lesson.completedem retry pode chegar depois doenrollment.revokedda mesma matrícula. Useenrollment.status,enrollment.revokedAte as datas dodata, e não a ordem das entregas (veja Ordem de chegada).
Eventos de links de pagamento
Os eventospayment_link.* saem só para vendas de um link de pagamento do painel, no mesmo
momento do payment.* par, e usam o mesmo envelope, assinatura, headers e retry. A venda de link
continua saindo também como payment.*: assine um dos dois grupos para não processar a venda
duas vezes.
O data é o mesmo do payment.* (inclusive customer e salesRep) com mais dois objetos:
paymentLink: o link no instante do envio, comtags,urlpública,useCountjá contado esalesRepdo link (nullquando o link não tem vendedor).charge: a cobrança filha criada pelo uso do link (id,status,amountInCents,currency,description,items, parcelas,customerName,customerEmail, datas).
payment_link.expired não tem par em payment.*: sai uma vez, quando o PIX, o boleto ou o cartão
pendente do link vence sem pagamento e o uso volta para o link. O data.status vem EXPIRED. Se o
pagamento vencido for pago depois, sai payment_link.paid normalmente.
payment_link.paid
tags são as do link no momento do envio: editar as tags depois muda os próximos eventos, não
os já entregues. Cada link tem até 20 tags de até 40 caracteres, sem repetição (maiúsculas e
minúsculas contam como a mesma tag).
Validando a assinatura
Cada entrega é assinada com HMAC-SHA256. O header tem o formatot=<unix>,v1=<hmac>, e o HMAC
é calculado sobre `${t}.${body}` usando o secret.
Qual secret usar? - Endpoints do painel: o signing secret exibido ao
criar o endpoint. - postbackUrl: o
postbackSecret retornado na
resposta do POST /v1/charges daquela cobrança. Cada cobrança tem o seu.postbackUrl
Quando você cria uma cobrança compostbackUrl, todos os eventos daquela transação
(payment.paid, payment.failed, payment.refunded) são entregues também nessa URL —
além dos endpoints do painel. É ideal para reconciliar uma fatura sem precisar manter um endpoint
fixo.
O postbackSecret que valida essas entregas vem uma única vez, na resposta da criação da
cobrança. Guarde-o junto com o id da cobrança/fatura.
Idempotência
A mesma entrega pode chegar mais de uma vez (ex.: no retry automático ou ao reprocessar). Use oX-GravityPay-Delivery (= id do payload) como chave de idempotência e ignore duplicatas. Toda
tentativa da mesma entrega, automática ou manual, usa o mesmo id.
Resposta e re-tentativas
Responda 2xx rapidamente (idealmente em até alguns segundos) para confirmar o recebimento. Qualquer outro status, timeout ou erro de conexão é tratado como falha.Retry automático
Quando a falha é temporária, a Gravity Play tenta de novo sozinha. Entram no retry automático:- timeout ou erro de conexão (DNS, conexão recusada);
- status
429; - qualquer status
5xx.
4xx diferente de 429 não é tentado de novo. Ele indica que o seu sistema recusou o payload, e
repetir o mesmo envio não resolve.
Depois da primeira tentativa, são até 6 tentativas automáticas, com estas esperas:
No total são até 7 tentativas, em cerca de 33 horas. Entregas para a mesma URL mantêm um intervalo
mínimo entre si, então um retry pode sair um pouco depois da espera da tabela.
Se a 6ª tentativa automática também falhar, a entrega fica esgotada e não é tentada de novo
sozinha. Ela continua no painel (Integrações → Webhooks → Entregas), com o histórico de cada
tentativa, e você pode reprocessar manualmente a qualquer momento. O reprocessamento manual
também cancela o retry automático que estiver agendado.

