Webhooks
Webhooks
Receba eventos do Minstrel com assinatura HMAC verificada, gerencie endpoints pela API, rotacione o segredo sem perder evento e reenvie entregas.
Guia de integração · 17 min de leitura
Nesta página
Webhooks sao a forma correta de saber o que aconteceu com uma cobranca. Fazer
polling em GET /v1/payments/{id} funciona, mas desperdica requisicao e
atrasa a sua reacao.
Endpoints, entregas e eventos sao separados por ambiente: uma chave de sandbox
so ve e so recebe eventos de sandbox. Leitura exige webhooks:read; cadastro,
edicao, rotacao, teste e reenvio, webhooks:write; a consulta de eventos,
events:read.
Cadastrar um endpoint
Pelo painel, em Webhooks, ou pela API:
curl -X POST http://localhost:3333/v1/webhooks \
-H "Authorization: Bearer $MINSTREL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://api.sualoja.com.br/webhooks/minstrel",
"description": "Confirma pedidos pagos e avisa recusas",
"enabledEvents": ["payment.captured", "payment.failed", "invoice.*"],
"customHeaders": { "X-Api-Key": "chave-do-seu-servidor" }
}'
A resposta traz o segredo de assinatura em secret (whsec_...). Guarde-o num
gerenciador de segredos: listagem e consulta nunca o devolvem, e mostra-lo de
novo exige uma revelacao auditada.
Cada ambiente aceita ate 20 endpoints, e uma URL so pode ser cadastrada uma
vez por ambiente - duas inscricoes na mesma URL entregariam cada evento em
dobro. Os dois casos respondem 409.
Regras da URL
Quem dispara a requisicao e o servidor do Minstrel, entao a URL passa por regras que impedem usar o webhook para alcancar redes internas:
- https obrigatorio. Usuario e senha na URL sao recusados; para autenticar, use um cabecalho extra.
- Rede interna e recusada no cadastro: IP privado (10/8, 172.16/12,
192.168/16), loopback, link-local (169.254/16), CGNAT (100.64/10),
multicast, IPv6 local, nome sem dominio (
postgres,api) e sufixos de intranet (.local,.internal,.lan,.corpe similares). - E de novo na conexao, pelo IP resolvido. Um dominio publico que passe a resolver para um endereco interno depois do cadastro (DNS rebinding) e barrado na hora da entrega: basta um endereco interno entre os resolvidos para a entrega falhar com "Endereco interno bloqueado".
localhostso em sandbox, numa instalacao local do Minstrel - e aihttp://tambem vale. Num servidor de producao,localhostseria o proprio servidor do Minstrel, nunca a sua maquina, e e recusado mesmo em sandbox: para testar no seu computador, use um tunel (ngrok, Cloudflare Tunnel).
Eventos e curingas
enabledEvents aceita tipos exatos e curingas:
payment.captured- um evento especifico;payment.*- todos os eventos do recurso, inclusive os que forem criados depois;*- todos (util em desenvolvimento, raramente em producao).
Evento desconhecido e recusado com 422: um erro de digitacao
(payment.capture) criaria um endpoint que nunca recebe nada. Tipos ja cobertos
por um curinga da lista sao descartados, e * substitui todo o resto.
GET /v1/webhooks/event-types devolve o catalogo agrupado por recurso, com o
curinga de cada grupo.
Cabecalhos extras
customHeaders vai em toda entrega - use para a chave de API do seu lado, por
exemplo. Ate 10 cabecalhos, valor em texto ASCII de ate 1.024 caracteres.
Cabecalhos que o Minstrel controla (content-type, user-agent, host,
content-length e afins, alem de qualquer minstrel-*) sao recusados: um
cabecalho extra nunca substitui a assinatura. Nas respostas da API, o valor
aparece mascarado em preview, com no maximo os 4 ultimos caracteres.
Gerenciar endpoints
| Endpoint | O que faz |
|---|---|
GET /v1/webhooks | Endpoints do ambiente, com taxa de sucesso, entregas por hora nas ultimas 24 horas e a ultima entrega. |
GET /v1/webhooks/:id | Um endpoint, com os mesmos numeros, o tempo de resposta e a politica de entrega. |
PATCH /v1/webhooks/:id | Altera URL, descricao, eventos, cabecalhos extras ou enabled. |
DELETE /v1/webhooks/:id | Remocao logica: o endpoint para de receber e as entregas na fila sao canceladas. |
GET /v1/webhooks/summary | Endpoints ativos, com falha e desligados; entregas e taxa de sucesso nas ultimas 24 horas contra as 24 anteriores. |
GET /v1/webhooks/event-types | Catalogo de eventos inscreviveis, agrupado por recurso. |
Na edicao, customHeaders e mesclado com o que ja existe: texto cria ou
substitui, null remove, e o que nao for citado continua como esta - o valor
secreto nao precisa voltar. Trocar a URL zera as falhas seguidas, que eram do
endereco antigo.
Desativar ("enabled": false) cancela as entregas na fila; a resposta informa
quantas em canceledDeliveries. Reativar zera as falhas seguidas. Cada
endpoint traz a situacao em status:
status | Significado |
|---|---|
active | Recebendo normalmente. |
failing | Recebendo, mas a tentativa mais recente falhou (consecutiveFailures maior que zero). |
disabled | Desligado por alguem. |
auto_disabled | Desligado pelo Minstrel apos falhas seguidas (veja disabledReason). |
Rotacao e revelacao do segredo
curl -X POST http://localhost:3333/v1/webhooks/whe_01HQ.../rotate-secret \
-H "Authorization: Bearer $MINSTREL_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "gracePeriod": "24h" }'
A rotacao gera um segredo novo, devolvido uma unica vez em secret:
"gracePeriod": "24h"(o padrao, tambem sem corpo): o segredo atual continua assinando por 24 horas junto com o novo. Durante a janela, o cabecalho de assinatura leva doisv1- tempo para trocar a variavel de ambiente do seu servidor sem perder evento.secretRotation.previousSecretExpiresAtdiz quando a janela fecha."gracePeriod": "none": o segredo atual deixa de valer na hora. Use quando ele vazou.
So existe um segredo anterior: rotacionar de novo dentro da janela descarta o mais antigo na hora.
POST /v1/webhooks/:id/reveal-secret mostra o segredo atual. Exige
webhooks:write e fica registrado na trilha de auditoria como
webhook_endpoint.secret_revealed, com quem pediu e de onde.
Evento de teste
curl -X POST http://localhost:3333/v1/webhooks/whe_01HQ.../test \
-H "Authorization: Bearer $MINSTREL_API_KEY"
Cria um evento webhook_endpoint.test e o entrega na hora, assinado como
qualquer outro - e o jeito de testar a sua verificacao de assinatura. A
resposta e a propria entrega, com o status e o corpo devolvidos pelo seu
servidor.
O evento de teste vai so para o endpoint testado, nunca para os demais
inscritos (nem para os inscritos em *), e nao aceita inscricao: incluir
webhook_endpoint.test em enabledEvents responde 422. Se a entrega
falhar, ela segue a mesma politica de retentativa das entregas reais. Endpoint
desativado responde 409: ative-o antes de testar.
Formato do envelope
{
"id": "evt_01HQ8M5X7P2K3N4R5T6V7W8X9Y",
"type": "payment.captured",
"apiVersion": "2026-01-01",
"createdAt": "2026-01-15T14:32:10.123Z",
"environment": "live",
"sequence": 48213,
"data": {
"object": { "object": "payment", "id": "pay_...", "status": "captured" },
"previousAttributes": { "status": "authorized" }
}
}
O campo sequence e monotonico por organizacao. Use-o para ordenar: com
retentativa, entregas chegam fora de ordem, e aplicar um evento antigo depois
de um recente corromperia o seu estado.
Cada entrega e um POST com estes cabecalhos, alem dos seus cabecalhos extras:
| Cabecalho | Conteudo |
|---|---|
Content-Type | application/json |
User-Agent | Minstrel-Webhooks/1.0 |
Minstrel-Signature | Assinatura, descrita abaixo. |
Minstrel-Event-Id | O id do evento. |
Minstrel-Event-Type | O type do evento. |
Minstrel-Delivery-Attempt | Numero da tentativa desta entrega, a partir de 1. |
Minstrel-Version | Versao do contrato do payload (2026-01-01). |
Verificando a assinatura
Minstrel-Signature: t=1736899200,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
te o unix timestamp da assinatura;v1e o HMAC-SHA256 de${t}.${corpo_bruto}com o segredo do endpoint.
Use o segredo inteiro como chave do HMAC, com o prefixo whsec_.
Valide contra o corpo bruto
Este e o erro mais comum ao integrar webhooks. Valide antes de qualquer parse de JSON:
import { createHmac, timingSafeEqual } from 'node:crypto';
export function verifyMinstrelSignature(params: {
rawBody: string; // string ou Buffer exatamente como chegou
header: string;
secret: string;
toleranceSeconds?: number;
}): boolean {
const parts = params.header.split(',').reduce<Record<string, string[]>>((acc, part) => {
const [key, value] = part.trim().split('=', 2);
if (!key || !value) return acc;
(acc[key] ??= []).push(value);
return acc;
}, {});
const timestamp = Number(parts.t?.[0]);
const signatures = parts.v1 ?? [];
if (!Number.isFinite(timestamp) || signatures.length === 0) return false;
// Rejeita assinaturas antigas: impede replay.
const tolerance = params.toleranceSeconds ?? 300;
if (Math.abs(Math.floor(Date.now() / 1000) - timestamp) > tolerance) return false;
const expected = createHmac('sha256', params.secret)
.update(`${timestamp}.${params.rawBody}`, 'utf8')
.digest('hex');
// Comparacao em tempo constante.
return signatures.some((candidate) => {
const a = Buffer.from(candidate, 'utf8');
const b = Buffer.from(expected, 'utf8');
return a.length === b.length && timingSafeEqual(a, b);
});
}
Reserializar o JSON muda bytes - ordem de chaves, espacos, escapes unicode - e
invalida a assinatura. Em Express, use
express.raw({ type: 'application/json' }); em Fastify, configure
rawBody: true.
Varias assinaturas no mesmo header
Durante a janela de rotacao do segredo o header carrega dois v1: o do segredo
novo e o do anterior. Aceite se qualquer um casar - e assim que voce troca o
segredo sem perder evento.
Retentativa
Respondemos ao seu 2xx como confirmacao. Qualquer outra resposta (inclusive
redirecionamento, que nao e seguido), erro de conexao ou ausencia de resposta
em 10 segundos agenda nova tentativa:
| Tentativa | Quando |
|---|---|
| 1 | Imediata |
| 2 | ~30 segundos |
| 3 | ~2 minutos |
| 4 | ~10 minutos |
| 5 | ~1 hora |
| 6 | ~6 horas |
| 7 | ~24 horas |
Cada intervalo conta a partir da tentativa anterior e tem jitter de mais ou menos 20%. Sem isso, uma queda de cinco minutos no seu endpoint faria milhares de entregas retentarem no mesmo instante, derrubando-o justamente quando ele volta.
Apos 7 tentativas a entrega fica exhausted. Cada tentativa sem 2xx soma uma
falha seguida no endpoint, que passa a aparecer como failing, e qualquer 2xx
zera a contagem. O desligamento automatico exige as duas condicoes: 20 falhas
seguidas e 3 dias sem nenhuma entrega bem-sucedida (contados da ultima
entrega com sucesso ou, se nunca houve, do cadastro). Uma queda de minutos,
mesmo com muito volume, nao desliga o endpoint. Desligado, ele fica
auto_disabled, com o motivo em disabledReason, e as entregas na fila sao
canceladas. Corrija o endpoint, reative-o com
PATCH e reenvie o que faltou.
Entregas
Cada evento gera uma entrega por endpoint inscrito:
status | Significado |
|---|---|
pending | Na fila para envio. |
succeeded | O endpoint respondeu 2xx. |
failed | A ultima tentativa falhou; ha nova tentativa agendada (nextRetryAt). |
exhausted | As 7 tentativas falharam. |
disabled | Cancelada porque o endpoint foi desativado ou removido. |
curl "http://localhost:3333/v1/webhooks/whe_01HQ.../deliveries?status=failed,exhausted&eventType=payment.*" \
-H "Authorization: Bearer $MINSTREL_API_KEY"
A listagem aceita status (lista separada por virgula), eventType (exato ou
curinga como payment.*), createdFrom, createdTo, search (id exato da
entrega whd_... ou do evento evt_...), limit (1 a 100, padrao 25) e
startingAfter.
GET /v1/webhooks/:id/deliveries/:deliveryId mostra a requisicao como ela
saiu, com o corpo exato que foi assinado e os cabecalhos (os extras
mascarados), e a resposta do seu endpoint, truncada em 8 KB (truncated avisa
quando cortou). A assinatura nao e guardada: ela muda a cada tentativa, porque
o timestamp faz parte dela.
Reenvio
curl -X POST http://localhost:3333/v1/webhooks/whe_01HQ.../deliveries/whd_01HQ.../retry \
-H "Authorization: Bearer $MINSTREL_API_KEY"
Zera as tentativas e coloca a entrega na fila; ela sai em poucos segundos. Vale
tambem para entrega que ja teve sucesso - e o caminho normal para reprocessar
um evento do seu lado depois de corrigir uma indisponibilidade. Endpoint
desativado responde 409: reative-o antes.
Consultar eventos
GET /v1/events lista os eventos do ambiente, do mais novo para o mais antigo
pela sequence, no mesmo envelope entregue aos webhooks. Serve para depurar
sem endpoint ("o evento foi gerado? com que dados?") e para reconciliar depois
de uma indisponibilidade:
curl "http://localhost:3333/v1/events?objectId=pay_01HQ...&type=payment.*" \
-H "Authorization: Bearer $MINSTREL_API_KEY"
| Parametro | Efeito |
|---|---|
type | Tipo exato ou curinga (payment.*). |
objectType, objectId | Todos os eventos de um recurso (pay_..., sub_...). |
createdFrom, createdTo | Periodo. |
search | Id exato do evento ou do objeto. |
limit, startingAfter | Pagina (1 a 100, padrao 25) e cursor: o id do ultimo evento da pagina anterior. |
GET /v1/events/:id traz o payload completo. Com o escopo webhooks:read, a
lista inclui o resumo das entregas de cada evento e o detalhe traz cada entrega
com o endpoint e o status - inclusive as de endpoints ja removidos
(endpointDeleted: true). Sem esse escopo, as entregas ficam de fora, porque
revelariam os endpoints cadastrados.
Idempotencia do seu lado
Entrega duplicada e esperada - preferimos entregar duas vezes a perder um
evento. Guarde o event.id e ignore os repetidos:
const alreadyProcessed = await db.processedEvents.findUnique({
where: { id: event.id },
});
if (alreadyProcessed) return reply.code(200).send();
Catalogo de eventos
GET /v1/webhooks/event-types devolve o catalogo completo, agrupado por
recurso, com o curinga de cada grupo e a descricao de cada tipo. Os mais
usados:
| Evento | Quando acontece |
|---|---|
payment.captured | Valor capturado. E o que confirma a venda. |
payment.failed | Recusado e encerrado. Inclui a classificacao ABECS em data.object.decline. No checkout transparente so sai quando o pagamento acabou de fato (recusa irreversivel, fim das tentativas de cartao ou prazo); data.object.checkout.endedBy diz qual. Veja Nova tentativa de cartao. |
payment.attempt_declined | Checkout transparente: cartao recusado por motivo reversivel. O pagamento voltou para requires_payment_method e o portador pode informar outro cartao, com o mesmo clientSecret, ate o prazo e o limite de tentativas. Veja Nova tentativa de cartao. |
payment.checkout_not_configured | Checkout transparente: a conta do conector esta mal configurada e a tokenizacao nao abriu. O portador recebeu o erro checkout_not_configured; o motivo completo vem em data.object.error. Veja Checkout sem configuracao. |
payment.refunded | Estornado integralmente. |
payment.authorization_expiring | Pre-autorizacao perto do fim do prazo de captura (Mastercard 7 dias, demais bandeiras 15). Traz o prazo em data.object.authorizationExpiresAt e a acao tomada em data.object.expiryAction: notify (so avisa), capture ou void. |
payment.fraud_review_resolved | Fim da revisao de fraude do adquirente (Cielo). O resultado vem em data.object.fraudReview.status: accepted, rejected ou merchant_decided. Veja Fim da revisao de fraude. |
antifraud.assessment_completed | Analise de antifraude concluida, pelo no Antifraude do fluxo (origin: "flow_node") ou pelo adquirente na autorizacao (origin: "acquirer"). Veja Analise de antifraude concluida. |
payment_method.verified | Cartao validado sem cobranca (Zero Auth). O resultado vem em data.object.verification.status: valid, invalid ou unavailable (servico fora do ar ou nao habilitado, o que nao diz nada sobre o cartao). |
invoice.payment_failed | Cobranca de fatura falhou. Traz a decisao de retentativa. |
subscription.canceled_by_issuer | Emissor determinou a suspensao. Nao retente. |
retry.exhausted | Politica de retentativa esgotada sem aprovacao. |
recipient.updated | Dados de um recebedor de split alterados. |
flow.notification | Emitido por um no "Notificar webhook" do fluxo. |
Alguns tipos do catalogo ainda nao sao emitidos nesta versao: a inscricao e
aceita, mas nenhuma entrega acontece por enquanto. Sao eles
payment.attempt.created, payment_method.expiring,
invoice.finalized, invoice.uncollectible, dispute.*,
chargeback.received, payout.*, flow.published,
connector_account.health_changed e communication.*.
O webhook_endpoint.test fica fora dos grupos de event-types (vem a parte,
em testEventType) e nao aceita inscricao: vai so para o endpoint testado.
Pre-autorizacao perto de vencer
payment.authorization_expiring sai 24 horas antes do prazo de captura de
uma pre-autorizacao (pagamento authorized, sem captura). A acao nesse momento
vem da configuracao Pre-autorizacao perto de vencer da conta do conector
(Plugins, conta, Recursos), e a que foi aplicada chega em
expiryAction:
expiryAction | O que o Minstrel fez |
|---|---|
notify | So avisou (padrao). Capture ou cancele do seu lado, antes do prazo. |
capture | Capturou o valor autorizado. |
void | Cancelou a autorizacao e liberou o limite do portador. |
{
"type": "payment.authorization_expiring",
"data": {
"object": {
"object": "payment",
"id": "pay_01HQ...",
"status": "authorized",
"authorizationExpiresAt": "2026-10-24T14:30:00.000Z",
"expiryAction": "notify"
}
}
}
Com notify, o aviso e o momento de agir: depois do prazo a autorizacao expira
e a captura responde 409 payment_not_capturable
(Transacoes).
Pagamentos com split
Os eventos de um pagamento com split levam as parcelas no
data.object.splits: payment.authorized, payment.captured,
payment.voided, payment.refunded e payment.partially_refunded. Cada parcela
traz o que o adquirente executou:
{
"type": "payment.captured",
"data": {
"object": {
"object": "payment",
"id": "pay_01HQ...",
"status": "captured",
"splits": [
{
"recipientId": "rcp_vendedor",
"amount": 6000,
"type": "percentage",
"liableForChargeback": true,
"commissionRateBps": 500,
"commissionFixedAmount": 30,
"netAmount": 5670,
"feeAmount": 330,
"capturedAmount": 6000,
"refundedAmount": 0
}
]
},
"previousAttributes": { "status": "authorized" }
}
}
amounte a parcela bruta;netAmountefeeAmountsao o liquido e o desconto como o adquirente informou (feeAmount = amount - netAmount),nulle0enquanto ele nao liquida a divisao.capturedAmounterefundedAmountacumulam por parcela: uma captura parcial ou um estorno parcial divide o valor entre os recebedores, e o evento sai com os valores ja gravados.- A divisao e executada no adquirente. Um pagamento que nao encontra conta capaz
de executa-la nao e autorizado: o evento e
payment.failed, comdata.object.error.code = "split_not_supported"no roteamento direto (veja Catalogo de erros).
Nova tentativa de cartao
No checkout transparente,
uma recusa reversivel do emissor nao encerra o pagamento: ele volta para
requires_payment_method e o portador informa outro cartao, com o mesmo
paymentId e clientSecret. Quem avisa isso e payment.attempt_declined, no
lugar de payment.failed:
{
"type": "payment.attempt_declined",
"data": {
"object": {
"object": "payment",
"id": "pay_01HQ...",
"status": "requires_payment_method",
"actionExpiresAt": "2026-10-19T14:02:11.000Z",
"decline": {
"reason": "INSUFFICIENT_FUNDS",
"label": "Saldo insuficiente",
"reversibility": "REVERSIBLE",
"retryAction": "RETRY_LATER",
"consumerMessage": "SALDO INSUFICIENTE. TENTE OUTRO CARTAO.",
"rawCode": "51"
},
"checkout": { "attemptsUsed": 1, "attemptsMax": 3, "attemptsRemaining": 2 }
},
"previousAttributes": { "status": "processing" }
}
}
- Nao cancele o pedido neste evento. O pagamento continua aberto, ate
actionExpiresAt. O fim vem comopayment.captured/payment.authorized(o portador achou um cartao que passa),payment.failedoupayment.canceled. payment.failedso sai quando o pagamento acabou de fato: recusa irreversivel, ultima tentativa de cartao (padrao: 3) ou prazo vencido.data.object.checkout.endedBydiz qual:irreversible,attempt_limit,expiredounot_issuer_decline(falha tecnica, antifraude ou regra do fluxo, que nao reabrem o pagamento).- Recusa irreversivel (regra da ABECS: o emissor nunca aprovara) encerra o pagamento mesmo com tentativas sobrando. Isso nao e configuravel.
- Cada cartao tentado e uma tentativa do pagamento (
attempts), e cada uma roda o fluxo de novo. Pagamento com cartao no corpo, cobranca do lojista, assinatura, Pix e boleto nao usampayment.attempt_declined: a recusa deles continua sendopayment.failed. - Se o seu sistema ja trata
payment.failedcomo "cancele o pedido", ele continua certo: no checkout transparente passa a sair um pouco mais tarde, so quando nao ha mais o que tentar.
Checkout sem configuracao
payment.checkout_not_configured registra, para voce, o motivo que o portador
nao pode ver. Quando a conta do conector esta mal configurada (por exemplo, uma
conta Cielo sem as credenciais do Silent Order Post), a pagina do portador
recebe o erro checkout_not_configured e o pagamento segue
requires_payment_method; o motivo completo vem aqui, e aparece tambem na
linha do tempo do pagamento no painel:
{
"type": "payment.checkout_not_configured",
"data": {
"object": {
"object": "payment",
"id": "pay_01HQ...",
"status": "requires_payment_method",
"error": {
"code": "connector_credentials_invalid",
"message": "Silent Order Post nao configurado nesta conta: informe o ClientId e o ClientSecret ...",
"stage": "tokenization_session",
"connectorAccount": { "id": "ca_01HQ...", "name": "Cielo matriz" }
}
}
}
}
Sai uma vez por motivo e por pagamento (a pagina pode pedir a sessao varias vezes). Corrija a conta e crie um pagamento novo.
Fim da revisao de fraude
payment.fraud_review_resolved sai uma vez por pagamento, quando a
revisao de fraude do adquirente termina.
O data.object e o pagamento, ja com o resultado em fraudReview.status, e
previousAttributes traz o estado anterior (pending):
{
"type": "payment.fraud_review_resolved",
"data": {
"object": {
"object": "payment",
"id": "pay_01HQ...",
"status": "captured",
"fraudReview": { "status": "accepted" }
},
"previousAttributes": { "fraudReview": { "status": "pending" } }
}
}
fraudReview.status | O que aconteceu |
|---|---|
accepted | O provedor aceitou. A venda foi capturada ou, com captura manual, segue authorized para voce capturar. |
rejected | O provedor recusou e a autorizacao foi cancelada (voided). |
merchant_decided | Voce capturou ou cancelou antes de o provedor decidir. |
Quando a revisao termina junto com uma mudanca de estado, o evento dessa mudanca
(payment.captured ou payment.voided) sai primeiro, com a revisao ja
fechada no payload, e este vem logo depois. Com accepted e captura manual nao
ha evento de estado: so este. Use-o para liberar o pedido quando o resultado for
accepted, mas confira o status do pagamento: ele diz se a venda ja foi
capturada.
Analise de antifraude concluida
antifraud.assessment_completed sai a cada analise registrada no pagamento, com
data.object no mesmo formato para as duas origens:
{
"type": "antifraud.assessment_completed",
"data": {
"object": {
"object": "antifraud_assessment",
"id": "afa_01HQ...",
"paymentId": "pay_01HQ...",
"origin": "acquirer",
"provider": "cybersource",
"providerStatus": "review",
"recommendation": "review",
"score": 62,
"triggeredRules": ["B", "D"]
}
}
}
| Campo | Conteudo |
|---|---|
origin | flow_node: o no Antifraude do fluxo, que o Minstrel chama antes de autorizar. acquirer: a analise que o adquirente (Cielo) faz dentro da autorizacao. |
provider | Quem analisou. No flow_node, o conector (konduto, clearsale); no acquirer, o provedor do adquirente (cybersource, clearsale). |
providerStatus | So no acquirer: accept, reject, review, unfinished, provider_error, aborted, unknown ou skipped. null no flow_node. |
recommendation | approve, review, decline ou error. Na analise do adquirente, skipped e os demais sem decisao saem como error. |
score | De 0 a 100, ou null quando o provedor decide sem score. |
triggeredRules | Regras acionadas; na Cybersource, os codigos de fator. |
O evento sai tambem quando a analise do adquirente e dispensada (skipped,
por falta de dado do cliente) e quando o veredito e uma recusa: nesse caso o
pagamento chega separadamente como payment.failed. Antes desta versao o tipo
estava no catalogo mas nunca era entregue.
Cartao validado
payment_method.verified sai a cada validacao sem cobranca (Zero Auth), seja
pelo no Validacao de cartao ou
por verify ao salvar o cartao:
{
"type": "payment_method.verified",
"data": {
"object": {
"object": "payment_method",
"id": "pm_01HQ...",
"verification": { "status": "valid", "verifiedAt": "2026-10-17T14:02:11.000Z" }
}
}
}
unavailable quer dizer que o servico nao validou (fora do ar, bandeira sem
suporte ou recurso desligado na conta), o que nao diz nada sobre o cartao: nao
trate como cartao ruim. So invalid justifica pedir outro cartao.