Webhook de payout
A A55 envia um POST para a webhook_url definida no payout sempre que o status muda. O payload é intencionalmente minimalista — use o payout_uuid para buscar o payout completo em Consultar payout.
A webhook_url é informada por payout no corpo da requisição de Criar payout. URLs privadas e localhost são rejeitadas.
Campos do payload
| Campo | Tipo | Descrição |
|---|---|---|
payout_uuid | UUID | Identificador único do payout |
status | string | Novo status do payout (ver tabela abaixo) |
transaction_reference | string | Chave de idempotência enviada na criação |
external_id | string | ID do payout no provider |
type_payout | string | Rail do payout (pix, bank_transfer, ted, spei, cash_pickup) |
payment_code | string ou null | Código de retirada (apenas cash payouts) |
authorization_code | string ou null | Código de autorização do provider, quando disponível |
Exemplo de payload
{
"payout_uuid": "9b1f0c88-3a3c-4f2f-9d6e-1f0a2d4e88c1",
"status": "realized",
"transaction_reference": "PAY-2048",
"external_id": "MN-20260616-0001",
"type_payout": "pix",
"payment_code": null,
"authorization_code": "AUTH-55812"
}
Todos os status possíveis
| Status | Descrição |
|---|---|
pending | Criado, aguardando processamento |
issued | Enviado ao provider |
realized | Confirmado e liquidado |
returned | Devolvido pelo banco receptor |
canceled | Cancelado |
error | Falhou no processamento |
expired | Expirado |
Buscando os detalhes completos do payout
O payload do webhook é minimalista por design. Após recebê-lo, chame a API com seu token Bearer para obter o objeto completo do payout:
curl "https://api.a55.tech/api/v1/bank/wallet/payout/{payout_uuid}/{wallet_uuid}/" \
-H "Authorization: Bearer $ACCESS_TOKEN"
O webhook é o gatilho, a API é a fonte da verdade
Use o webhook para saber quando um payout mudou e então chame GET /api/v1/bank/wallet/payout/{payout_uuid}/{wallet_uuid}/ para obter detalhes completos como fee, confirmed_date e destination.
Exemplos de handler
- Python (Flask)
- JavaScript (Express)
from flask import Flask, request, jsonify
app = Flask(__name__)
processed = set()
@app.route("/webhooks/a55/payouts", methods=["POST"])
def payout_webhook():
data = request.get_json()
payout_uuid = data.get("payout_uuid")
status = data.get("status")
# Deduplicação
if payout_uuid in processed:
return jsonify(status="duplicate"), 200
processed.add(payout_uuid)
if status == "realized":
mark_payout_settled(payout_uuid)
elif status in ("returned", "error", "expired"):
flag_payout_failure(payout_uuid, status)
return jsonify(status="accepted"), 200
const express = require("express");
const app = express();
app.use(express.json());
const processed = new Set();
app.post("/webhooks/a55/payouts", (req, res) => {
const { payout_uuid, status } = req.body;
// Responda imediatamente
res.sendStatus(200);
// Deduplicação
if (processed.has(payout_uuid)) return;
processed.add(payout_uuid);
if (status === "realized") {
markPayoutSettled(payout_uuid);
} else if (["returned", "error", "expired"].includes(status)) {
flagPayoutFailure(payout_uuid, status);
}
});
Boas práticas
| Prática | Por quê |
|---|---|
Retorne 200 imediatamente | Evita timeouts e entregas duplicadas |
| Processe em background | Mantém o tempo de resposta baixo |
Deduplique por payout_uuid | Lida com webhooks reentregues com segurança |
| Busque detalhes completos via API | O payload do webhook é minimalista por design |
| Use uma URL pública e acessível | URLs privadas/localhost são rejeitadas |