Outbound Webhooks

A plataforma Autra emite webhooks outbound pra notificar o BFF do cliente quando algo terminal acontece num evento (pagamento concluído, TED creditada, onboarding aprovado, transação acquiring autorizada etc.).

Esta página explica:

  1. Como o cliente cadastra a URL que vai receber os eventos.
  2. O formato exato do payload que enviamos.
  3. Como validar o X-Autra-Signature (HMAC-SHA256).
  4. Como tratar retries (até 16 tentativas em ~60 min).

Pra cadastrar/atualizar uma subscription, use PUT /v1/banking/webhooks/{domain}. Pra listar / consultar / remover, ver os outros endpoints em "Outbound Webhooks" na referência.


1. Como funciona

+-------------------+        +-------------------+        +------------------+
| Evento terminal   |        |   Autra Service   |        |  BFF do cliente  |
| (Pix completo,    | -----> | Dispatch (sync)   | -----> | (URL cadastrada) |
| TED, onboarding,  |        | + HMAC + Auth     |        |                  |
| acquiring etc.)   |        +-------------------+        +------------------+
+-------------------+                 |
                                      | em caso de falha (timeout, 5xx, conn refused)
                                      v
                              +------------------+
                              | Autra_Webhook_   |
                              | Retry (1/min,    | -----> tenta de novo até 16x
                              | EventBridge)     |        em ~60 min
                              +------------------+
  • Tentativa síncrona = attempt 1 (no momento do evento).
  • Retry automático = attempts 2–16 (worker que roda 1×/min).
  • Esgotado = state = DESPREZADO, não tenta mais.

2. Domínios suportados

Cada domain tem uma subscription independente. Registre uma URL por domínio (podem ser URLs diferentes):

DomainCobre
pix-paymentsPix — pix_payment.completed, pix_payment.failed, pix_payment.rejected, pix_payment.error (Pix enviado) + pix_payment.received (Pix recebido / crédito entrante, status RECEIVED) + pix_payment.held (Pix recebido retido para análise do provedor, status HELD; não é crédito — a liberação chega depois como pix_payment.received)
onboardingConta digital — onboarding.completed, onboarding.declined, onboarding.failed, onboarding.canceled, onboarding.waiting_correction
transfersTED — transfer.in.completed, transfer.in.failed, transfer.out.completed, transfer.out.failed, transfer.out.canceled
acquiringAdquirência (POS + Pix Checkout) — acquiring.financial_transaction, acquiring.payout, acquiring.financial_cycle, acquiring.payment_slip_notification, acquiring.merchant_onboarding_status, acquiring.card_enrollment.completed, acquiring.card_enrollment.failed
cardsCartões (banking) — eventos do ciclo de vida + antifraude + transações. Ver detalhamento abaixo.
ddaDDA (Débito Direto Autorizado) — dda.payer_status_updated (enrollment do pagador) + dda.bill_status_updated (ciclo do boleto). Ver detalhamento abaixo.
pix-keysPortabilidade de chave Pix (claim) — pix_keys.claim.requested, pix_keys.claim.confirmed, pix_keys.claim.completed, pix_keys.claim.denied, pix_keys.claim.canceled. Ver detalhamento abaixo.
paymentsPagamento de conta/boleto/tributo a partir da conta (débito) — payment.completed (status PAID), payment.failed (DENIED/ROLLBACK/ERROR).
boletoBoleto emitido pela conta foi liquidado (crédito entrante) — boleto.paid (status PAID).
subscriptionsAssinaturas (recorrência por cartão) — subscription.* e invoice.*. Ver detalhamento abaixo.
pix-automaticPix Automático — lado recebedor (quem cria a recorrência): pix_automatic.recurrence_* e pix_automatic.billing_*; lado pagador: pix_automatic.authorized, pix_automatic.charge_* etc. Ver detalhamento abaixo.

Devolver um Pix recebido: com o accountUuid e o endToEnd do pix_payment.received, chame POST /v1/banking/pix/reversals com originalEndToEnd = endToEnd — não é preciso consultar nada antes.

Eventos intermediários (PROCESSING, ACCEPTED, FILES_PENDING etc.) não disparam webhook — use os endpoints de GET pra polling quando precisar do estado corrente.

Domain acquiring em detalhe

Os eventos de acquiring são roteados pelo merchant (slugMerchant do payload) → tenant dono. Só são entregues para merchants cadastrados/associados na plataforma Autra.

⚠️

acquiring.merchant_onboarding_status: este evento sinaliza o andamento do cadastro de um EC (ex.: riskAnalysisStatus). Enquanto o merchant ainda não estiver cadastrado/associado na plataforma, o evento não é entregue (não há merchant resolvível para rotear ao tenant). Ele passa a ser entregue assim que o EC estiver vinculado ao seu tenant. Para acompanhar o cadastro antes disso, use o fluxo/endpoints de onboarding de merchant.

Adesão de cartão (acquiring.card_enrollment.*)

Disparados quando termina uma sessão de cartão com purpose=ENROLLMENT (POST /v1/acquiring/card-sessions), para o seu backend não depender do front.

Event typeQuando dispara
acquiring.card_enrollment.completedCartão salvo e série recorrente aberta (venda de valor zero aprovada)
acquiring.card_enrollment.failedEmissor/adquirente recusou a adesão (a página permite tentar outro cartão dentro do prazo)

operationData: sessionId, orderId, channel, status (APPROVED/DENIED), maskedCard e, quando houver, externalRef, origin, paymentId, brandReferenceId, subscriptionId (assinatura ativada por esta adesão), reasonCode/reasonText (motivo da recusa). Nunca PAN nem CVV.

Domain subscriptions em detalhe

Assinaturas de clientes finais com cobrança recorrente no cartão (seção Assinaturas da referência). Quem recebe é o backend do estabelecimento.

Event typeQuando dispara
subscription.createdAssinatura criada (PENDING_ENROLLMENT)
subscription.activatedAdesão do cartão aprovada: a assinatura passa a ACTIVE e as cobranças automáticas começam
subscription.past_due3ª tentativa de cobrança de uma fatura recusada: assinatura PAST_DUE
subscription.reactivatedFatura em atraso paga (ou reativação manual): volta a ACTIVE
subscription.suspendedSuspensa (POST .../suspend): não é cobrada
subscription.canceledCancelada (POST .../cancel, ou no dia do cancelamento agendado, com scheduled=true): faturas ainda não cobradas são canceladas
invoice.createdFatura do período gerada (3 dias antes do vencimento)
invoice.paidCobrança aprovada
invoice.payment_failedTentativa recusada; nova tentativa conforme cardRetryDays do plano
invoice.overdueTentativas esgotadas
invoice.canceledFatura cancelada (assinatura cancelada)
subscription.plan_change_scheduledTroca de plano agendada (POST .../change-plan): fromPlanId, toPlanId, effectiveDate
subscription.plan_changedTroca aplicada pela Autra no vencimento de início: fromPlanId, toPlanId, effectiveDate
subscription.cancel_scheduledCancelamento agendado para o fim do período (POST .../cancel com atPeriodEnd): cancelAt, cancelReason
subscription.scheduled_changes_canceledAgendamentos desfeitos (DELETE .../scheduled-changes): planChange e/ou cancelAt desfeitos
invoice.updatedFatura já gerada e ainda não cobrada passou ao valor do plano novo (troca de plano)

Cobrança avulsa (POST .../charges): invoice.created, invoice.paid ou invoice.payment_failed com invoice.kind=ONE_OFF, invoice.externalRef (a sua chave) e invoice.description. Recusa de avulsa não gera invoice.overdue nem subscription.past_due: é tentativa única e não muda a assinatura.

operationData: subscriptionId, externalRef, planId, merchantId, customer (name, document mascarado), paymentMethod, status e previousStatus (a transição que o evento anuncia — não o estado atual, que pode já ter mudado na entrega), statusReason, nextDueDate e, nos invoice.*, o objeto invoice (id, kind, amount, dueDate, status, periodStart, periodEnd, paymentId, paidAt, attempt, nextAttemptAt, reasonCode, reasonText, requiresReview — presentes quando se aplicam). requiresReview=true indica tentativa sem desfecho confirmado pela adquirente: a Autra não repete a cobrança sozinha até a conciliação. O eventId é estável por evento: use-o para deduplicar. schemaVersion no envelope.

Domain pix-automatic em detalhe

Lado recebedor — você criou a recorrência (POST /v1/banking/pix/automatic/recurrences) e é avisado quando o pagador decide e quando cada cobrança chega a um desfecho, sem precisar consultar o GET. Um aviso por mudança de status; reenvio do mesmo status não gera outro.

Event typeQuando dispara
pix_automatic.recurrence_acceptedRecorrência aceita e enviada ao banco do pagador; aguardando a decisão dele
pix_automatic.recurrence_approvedPagador autorizou
pix_automatic.recurrence_rejectedPagador recusou — operationData.rejectionCode (AP13, AP14, AP15)
pix_automatic.recurrence_canceledCancelada — operationData.canceledBy = RECEIVER ou PAYER; reason = LATE_ACCEPTANCE no aceite fora do prazo
pix_automatic.recurrence_expiredPrazo para o pagador autorizar (ou a vigência) terminou
pix_automatic.billing_completedCobrança paga
pix_automatic.billing_failedCobrança não paga — operationData.failureReason = REJECTED ou EXPIRED; reasonCode quando informado
pix_automatic.billing_canceledCobrança cancelada — operationData.canceledBy = RECEIVER ou PAYER

Envelope: recurrenceId (o mesmo devolvido na criação e usado no GET), txId (só em billing_*), status (novo status, igual ao do GET), amount e schemaVersion: 1. operationData: side = RECEIVER, previousStatus, correlationId, documentNumber (o seu número de contrato), periodicity; em billing_*, billingId e dueDate.

{
  "eventId": "6f0d1c1e-4c2b-4d8e-9a51-1f2b3c4d5e6f",
  "eventType": "pix_automatic.recurrence_approved",
  "schemaVersion": 1,
  "occurredAt": "2026-10-14T13:05:12Z",
  "tenantId": "<seu-tenant>",
  "status": "APPROVED",
  "amount": 96.10,
  "recurrenceId": "<recurrenceId da criação>",
  "operationData": {
    "side": "RECEIVER",
    "previousStatus": "SENT",
    "correlationId": "<idCorrelation>",
    "documentNumber": "APOLICE_123456789012345",
    "periodicity": "MONTHLY"
  }
}

O crédito de cada cobrança paga também chega no domínio pix-payments (pix_payment.received).

Lado pagador — a conta da sua plataforma autoriza recorrências de terceiros: pix_automatic.authorization_pending, pix_automatic.authorized, pix_automatic.rejected, pix_automatic.canceled, pix_automatic.charge_active, pix_automatic.charge_completed, pix_automatic.charge_failed, pix_automatic.charge_canceled.

O domínio subscriptions é do produto Assinaturas (cartão) e não cobre Pix Automático: para receber estes eventos, cadastre PUT /v1/banking/webhooks/pix-automatic.

Domain cards em detalhe

Apenas cartões cadastrados na plataforma Autra geram webhook. Cartões fora da plataforma são ignorados (não despacham).

Event typeQuando dispara
card.createdCartão emitido
card.status_updatedMudança de status (ativo/bloqueado/cancelado)
card.pin_updatedTroca/cadastro de PIN
card.updatedMudanças genéricas de atributos do cartão
card.reissuedCartão reemitido (2ª via)
card.fraud_actionBloqueio/ação por antifraude
card.authorizationAutorização de compra (pass-through — payload da operação em operationData.payload)
card.chargeCobrança/estorno (pass-through)

Segurança: estes eventos nunca carregam PAN ou CVV em claro. Só trafega masked_pan (ex.: 421847******4639).

Domain dda em detalhe

Apenas contas cadastradas na plataforma Autra geram webhook. Contas fora da plataforma são ignoradas (não despacham).

Event typeQuando dispara
dda.payer_status_updatedMudança de status do enrollment do pagador — PENDING → PROCESSING → ACTIVE, ou DISABLED / DENIED. O POST /dda retorna PENDING síncrono; a transição final pra ACTIVE chega por este webhook.
dda.bill_status_updatedQualquer mudança no ciclo de vida de um boleto DDA. Um único event_type cobre todo o ciclo — discrimine pelo status_dda no payload: APRESENTADO, ACEITO, RECUSADO, AGENDADO, PAGO/PAID, BAIXADO.

Pré-requisito: o pagador precisa estar ACTIVE pra começar a receber dda.bill_status_updated. Antes disso só chega dda.payer_status_updated.

Pass-through: o payload completo da operação vai inteiro em operationData.payload — inclui o boleto completo (bar_code, digitable_line_number, bill_amount, original_beneficiary, interest, fine, calculated_amount etc.). Discrimine sempre pelo conteúdo do payload, não pelo eventType do envelope.

Domain pix-keys em detalhe

Cobre a portabilidade (claim) de chave Pix. Apenas contas/chaves da plataforma Autra geram webhook.

Event typeQuando dispara
pix_keys.claim.requestedAlguém pediu a portabilidade de uma chave que é sua (lado doador) — o cliente precisa autorizar ou contestar dentro do prazo
pix_keys.claim.confirmedConfirmação do doador recebida — chave em transferência
pix_keys.claim.completedChave transferida com sucesso
pix_keys.claim.deniedPortabilidade negada/contestada
pix_keys.claim.canceledPortabilidade cancelada pelo solicitante

3. Formato do payload (envelope Autra)

Todos os domínios usam o mesmo envelope. Só muda eventType + o conteúdo de operationData.

Request

POST <sua-url-cadastrada>
Authorization: Basic <opcional, conforme auth da subscription>
Content-Type: application/json
User-Agent: Autra-Webhooks/1.0
X-Autra-Signature: sha256=<hex-lowercase>

{
  "eventId": "01963e6f-aa00-7c00-9d00-000000000001",
  "eventType": "acquiring.financial_transaction",
  "occurredAt": "2026-05-20T15:25:00Z",
  "tenantId": "2916bfe7-ecec-46bc-bc51-8a19aa593c1a",
  "userDocument": "25055662000190",
  "ownerId": "019d4f6e-c21d-4199-9424-9c37a2a1c65d",
  "status": "AUTHORIZED",
  "counterpartyName": "Fulano de Tal",
  "operationData": {
    "subject": "FINANCIAL_TRANSACTION",
    "productType": "CREDIT",
    "slugMerchant": "57DA076635814851A7A4B36FEDB340BB",
    "orderId": "pedido-abc-123",
    "pixId": "1a0b5369c4f24a0e8f0f0d7d0c2f9a11",
    "payerName": "Fulano de Tal",
    "payerDocument": "12345678901",
    "payerEmail": "[email protected]",
    "paymentLinkSlug": "lnk_n02754fh",
    "payload": {
      "rrn": "010008094933",
      "muid": "26F9758DB70336D7547CCA2ECA1C76D4",
      "slug": "4C037E901ADD45E29BA9C80C00A71745",
      "totalAmount": 12.50,
      "productType": "PREPAID_DEBIT",
      "transactionStatus": "AUTHORIZED",
      "...": "demais campos da operação — pass-through"
    }
  }
}
⚠️

operationData.payload traz os dados da operação, repassados sem transformação. Parseie de forma tolerante — campos podem ser adicionados sem aviso. Em especial:

  • totalAmount é decimal em reais (ex.: 12.50, 0.01) — não é inteiro em centavos. Desserialize como número decimal/float, nunca como int/long.
  • productType é string — veja os valores conhecidos na tabela abaixo. Trate como lista aberta: não use enum fechado, novos valores podem surgir sem aviso.

Correlação e identificação do pagador (acquiring)

Além do payload cru, o operationData de acquiring.financial_transaction
e acquiring.payout traz campos de correlação quando aplicáveis (cada um
só aparece se houver valor — trate todos como opcionais):

CampoQuando vemPra que serve
orderIdpedido criado com identificador próprio (API de checkout)casar o evento com o pedido no seu sistema
pixIdpagamentos Pix Checkoutmesmo valor devolvido no create (txid)
payerNameo pagador preencheu os dados no checkout (Pix ou cartão)exibir/conferir quem pagou
payerDocumentidem — só dígitos, sem máscara (CPF ou CNPJ)identificar o cliente quando o pagamento vem de um link público
payerEmailidemalternativa de identificação quando seu cadastro é por e-mail
paymentLinkSlugpagamento originado de um link (/checkout/<slug>)saber de qual link veio (ex.: um link por produto/finalidade)
💡

Link público, muitos clientes. Se você usa um único link para

todos os seus clientes, use payerDocument (ou payerEmail) para saber a
quem creditar — é o dado que o próprio pagador preenche no checkout antes de
pagar. Não é necessário criar um link por cliente. Vale para link pago no
cartão e no Pix.

Correlação entre os dois eventos do mesmo pagamento:
acquiring.payout.payload.payoutId == acquiring.financial_transaction.payload.slug.

Valores de productType (acquiring)

Valores observados na base de produção da Autra (eventos acquiring.financial_transaction e acquiring.payout):

productTypeSignificado
CREDITCartão de crédito
DEBITCartão de débito
PREPAID_CREDITCartão pré-pago, função crédito
PREPAID_DEBITCartão pré-pago, função débito
PIXTransação Pix (QR / maquininha)
VOUCHERVoucher / benefício

⚠️ Lista não fechada — reflete o que já trafegou; podem surgir valores novos. Desserialize productType como string e tenha um caminho de fallback para valores desconhecidos.

Campos top-level (sempre presentes)

userDocument e ownerId são preenchidos para o tráfego do app portador/merchant. Em alguns casos legado podem vir ausentes — nesse caso, roteie pelos dados em operationData.

CampoTipoDescrição
eventIdUUIDIdentificador único do evento. Use pra idempotência (mesmo eventId pode chegar até 16 vezes em retry).
eventTypestringEx.: acquiring.financial_transaction, pix_payment.completed.
occurredAtISO-8601 UTCMomento em que o evento foi gerado pela Autra.
tenantIdUUIDTenant dono do evento (caso o BFF atenda vários tenants).
userDocumentstringCPF (portador) ou CNPJ (merchant) do dono do evento — quem deve receber a notificação no app. tenantId sozinho não distingue qual usuário do tenant é o destinatário; roteie por (tenantId, userDocument). Resolvido pela conta/cartão/merchant. Pode vir ausente em casos legado.
ownerIdUUIDID interno estável do dono (não-PII): persons.id/legal_entities.id. Alternativa ao userDocument quando preferir não indexar por CPF/CNPJ. Mesma ressalva de ausência no legado.
statusstringStatus terminal (COMPLETED, AUTHORIZED, FAILED etc.).
counterpartyNamestringNome da contraparte (opcional): destinatário no envio (Pix/TED out), pagador no recebimento (Pix/TED in). Ausente quando não aplicável — use com fallback.
flavorstringVariante white-label do app do tenant (ex.: merci). Opcional — presente só quando o tenant usa um app white-label; ausente para o app padrão (autra). Use para roteamento/branding por marca.
operationDataobjectConteúdo específico do evento. Em acquiring, traz payload com os dados da operação — parse tolerante; totalAmount decimal, productType string aberta (ver aviso acima) — e os campos de correlação/pagador (orderId, pixId, payerDocument, paymentLinkSlug…), todos opcionais.

Headers enviados

HeaderValor
Content-Typeapplication/json
User-AgentAutra-Webhooks/1.0
X-Autra-Signaturesha256=<hex lowercase> — ver seção HMAC
AuthorizationOpcional, conforme auth da subscription (Basic ou Bearer)
(custom)Opcional, conforme auth.type=HEADER da subscription

4. Validação do X-Autra-Signature (HMAC-SHA256)

A cada POST mandamos:

X-Autra-Signature: sha256=<hex>

Onde <hex> é HMAC-SHA256(secret, raw_body) em lowercase hexadecimal. O secret é uma string de 32+ chars única por tenant — compartilhada entre todos os domínios. Não há secret separado por domínio.

⚠️

O secret aparece apenas uma vez na resposta do PUT (_secretShownOnce: true). GET/LIST nunca o devolvem. Guarde em vault no momento do cadastro.

  • 1ª subscription do tenant: sem secret no body → servidor gera. Com secret no body → usa o passado.
  • Subscriptions seguintes do mesmo tenant: sem secret → reusa o secret existente (recomendado). Com secret diferente → ROTACIONA pra todas as subscriptions do tenant (atomic; retries em andamento passam a usar o novo).

Protocolo de validação no BFF

  1. Capturar o body raw da request — bytes exatos que chegaram na conexão, antes de qualquer parse/re-serialize do JSON.
  2. Ler o header X-Autra-Signature.
  3. Calcular expected = "sha256=" + hex(HMAC-SHA256(secret, raw_body)).
  4. Comparar com constant-time ao valor recebido. Igual → processa. Diferente → responder 401.

Snippets prontos

Node.js / Express — registre o verify no middleware pra ter o raw body:

const crypto = require('crypto');

app.use(express.json({
  verify: (req, _res, buf) => { req.rawBody = buf; }
}));

function verifyAutraSignature(req, secret) {
  const header = req.get('X-Autra-Signature') || '';
  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(req.rawBody)
    .digest('hex');

  const a = Buffer.from(header);
  const b = Buffer.from(expected);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

Java / Spring — use ContentCachingRequestWrapper ou um Filter que cacheie o byte[] antes do JSON binding:

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
import java.nio.charset.StandardCharsets;

public boolean verify(byte[] rawBody, String header, String secret) throws Exception {
    Mac mac = Mac.getInstance("HmacSHA256");
    mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
    byte[] hash = mac.doFinal(rawBody);

    StringBuilder hex = new StringBuilder("sha256=");
    for (byte b : hash) hex.append(String.format("%02x", b));

    return MessageDigest.isEqual(
        hex.toString().getBytes(StandardCharsets.UTF_8),
        header.getBytes(StandardCharsets.UTF_8)
    );
}

Python / FastAPI — use await request.body() antes de qualquer parse:

import hmac, hashlib

def verify(raw_body: bytes, header: str, secret: str) -> bool:
    expected = "sha256=" + hmac.new(
        secret.encode("utf-8"),
        raw_body,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, header or "")

Go (net/http):

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
)

func verify(rawBody []byte, header, secret string) bool {
    mac := hmac.New(sha256.New, []byte(secret))
    mac.Write(rawBody)
    expected := "sha256=" + hex.EncodeToString(mac.Sum(nil))
    return hmac.Equal([]byte(expected), []byte(header))
}

Pegadinhas comuns

  1. Body raw, NÃO o JSON re-serializado. JSON.parse → JSON.stringify muda a ordem das chaves / espaços e quebra a assinatura. Capture os bytes ANTES de qualquer parse.
  2. Comparação constant-time (timingSafeEqual / MessageDigest.isEqual / hmac.compare_digest / hmac.Equal). Não use == / .equals() — vazam timing.
  3. Hex lowercase. Nossa implementação emite lowercase. Não aplique toUpperCase na comparação.
  4. Prefixo sha256= faz parte do header. Compare a string inteira ("sha256=" + hex) ou strippe de ambos os lados.

Teste de mesa (validar o cálculo localmente)

echo -n '{"test":"hello"}' | openssl dgst -sha256 -hmac "SEU_SECRET" -hex

Se o hex resultante for igual ao que a Autra envia para o mesmo body + secret, o BFF está pronto.

Como gerar um secret forte (caso queira fornecer o seu, em vez de deixar o servidor gerar)

openssl rand -hex 32                       # 64 chars hex (256 bits)
openssl rand -base64 48 | tr -d '\n'       # ~64 chars base64

5. Auth opcional no transporte (Basic / Bearer / Header custom)

Além do HMAC (que é integridade do payload), a subscription suporta autenticação de transporte opcional pra satisfazer um gateway/proxy do cliente que exige Authorization ou um header de API key. Tipos:

auth.typeComportamento
NONE (default)Nada além do HMAC.
BASICEnvia Authorization: Basic base64(username:password). Exige username + password.
BEAREREnvia Authorization: Bearer <token>. Exige token.
HEADEREnvia header custom (ex.: x-api-key: <value>). Exige name + value. Não pode usar nomes reservados (Host, Content-Length, Content-Type, X-Autra-Signature, User-Agent).
🔒

password, token e value aparecem apenas na resposta do PUT (mesmo tratamento do secret). GET/LIST devolvem só auth.type (e auth.name, se type=HEADER — o nome do header em si não é segredo). Pra trocar, faça outro PUT com o novo valor.


6. Política de entrega + retry

FaseO que aconteceColuna state
Síncrono (attempt 1)Tentativa imediata no momento do evento. Timeout 5s.SUCCEEDED se 2xx; senão PENDING_RETRY
Retry rápido (attempts 2–11)Worker reenvia +1 min cada → 10 tentativas em 10 min.PENDING_RETRY
Retry lento (attempts 12–16)Worker reenvia +10 min cada → 5 tentativas em 50 min.PENDING_RETRY
EsgotadoApós attempt 16 falho, não tenta mais.DESPREZADO

Total: 16 tentativas em ~60 min antes de DESPREZAR. Cada retry re-assina o HMAC com o secret atual do tenant (rotação propaga pra todas as subscriptions do tenant; retries em voo passam a usar o novo).

O que o BFF deve fazer

  • 2xx → entrega marcada como sucesso. Não retenta.
  • Qualquer outro status / timeout / erro de rede → entra na fila de retry.
  • Timeout: 5s. BFF do cliente deve responder rápido. Recomendação: enfileirar internamente (ex.: SQS, Kafka, fila in-memory) e retornar 200 imediato; processar de forma assíncrona.
  • Idempotência: o mesmo eventId pode chegar até 16 vezes. Use eventId como chave de dedup no BFF.

7. Como cadastrar a subscription

O secret é opcional — se omitido, o servidor gera 64 chars hex (256 bits). Recomendado pra self-service: você não precisa cuidar da geração nem do canal de entrega.

🔑

1 secret por tenant — compartilhado entre todos os domínios. Se você já criou uma subscription em acquiring e agora vai criar em pix-payments (mesmo tenant), basta omitir secret que o servidor reusa o existente. Você não precisa rastrear secrets diferentes por domínio.

Variante A — server gera o secret (recomendado)

curl -X PUT https://api.autra.io/v1/banking/webhooks/acquiring \
  -H "Authorization: Bearer <JWT-do-tenant>" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://bff-do-cliente.com.br/webhooks/autra",
    "eventTypes": ["*"],
    "enabled": true,
    "auth": {
      "type": "BASIC",
      "username": "autra-webhook",
      "password": "<senha-do-bff>"
    }
  }'

Variante B — você fornece o secret

curl -X PUT https://api.autra.io/v1/banking/webhooks/acquiring \
  -H "Authorization: Bearer <JWT-do-tenant>" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://bff-do-cliente.com.br/webhooks/autra",
    "secret": "<32+ chars; gere com openssl rand -hex 32>",
    "eventTypes": ["*"],
    "enabled": true,
    "auth": { "type": "BASIC", "username": "autra-webhook", "password": "..." }
  }'

Resposta do PUT (contém secret revelado — guarde agora!)

{
  "id": "9f7a...",
  "tenantId": "2916bfe7-ecec-46bc-bc51-8a19aa593c1a",
  "domain": "acquiring",
  "url": "https://bff-do-cliente.com.br/webhooks/autra",
  "secret": "EXAMPLE_3f019b734d51e1ed4298ac49fea42a26bef159724247d83bddf0e561",
  "eventTypes": ["*"],
  "enabled": true,
  "auth": {
    "type": "BASIC",
    "username": "autra-webhook",
    "password": "<senha-cru>"
  },
  "createdAt": "2026-05-20T15:25:00Z",
  "updatedAt": "2026-05-20T15:25:00Z",
  "_secretShownOnce": true
}
⚠️

Esta é a única resposta onde secret, password, token e value aparecem. Salve agora em vault — GET /v1/banking/webhooks e GET /v1/banking/webhooks/{domain} sempre redactam (devolvem só auth.type e auth.name se HEADER).

Ver detalhes completos em PUT /v1/banking/webhooks/{domain} na referência da API.


8. Suporte

Em caso de dúvida sobre validação HMAC, retries não chegando, ou cadastro de domínios, fale com o time Autra. Em pedidos de troubleshooting, anexe eventId e timestamp aproximado — conseguimos localizar a delivery e ver attempt, state, response_status e error registrados.