SaqSaq Docs
Boas práticas

Lidando com callbacks

A Saq envia callbacks em tempo real para sua callbackUrl toda vez que uma transação muda de status. A regra principal: responda 2xx em até 5 segundos. Quem não responde rápido vira fila de retry (até 72 tentativas, backoff exponencial).

Padrão recomendado: enfileira e devolve 2xx

Processe pesado fora do handler. O handler só recebe, enfileira e responde.

import express from 'express';
const app = express();

app.post('/webhooks/saq', express.json(), async (req, res) => {
  await queue.enqueue('saq-callback', req.body);
  res.status(204).end();
});
from flask import Flask, request

app = Flask(__name__)

@app.post('/webhooks/saq')
def saq_webhook():
    queue.enqueue('saq_callback', request.get_json())
    return '', 204
http.HandleFunc("/webhooks/saq", func(w http.ResponseWriter, r *http.Request) {
    var payload map[string]any
    if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
        w.WriteHeader(http.StatusBadRequest)
        return
    }
    queue.Enqueue("saq-callback", payload)
    w.WriteHeader(http.StatusNoContent)
})
<?php
$payload = json_decode(file_get_contents('php://input'), true);
$queue->enqueue('saq-callback', $payload);
http_response_code(204);

Testar localmente

Exponha seu localhost via ngrok ou Cloudflare Tunnel e dispare o payload manualmente:

curl -X POST https://seu-tunel.ngrok.io/webhooks/saq \
  -H "Content-Type: application/json" \
  -d '{
    "id": "SAQ20251123104518DF75D20A8F",
    "type": "DEPOSIT",
    "status": "COMPLETED",
    "amount": 99.90,
    "clientReference": "order-1234",
    "virtualAccount": "loja-rj-01",
    "paidAt": "2025-11-23T10:46:26.986Z"
  }'
await fetch('https://seu-tunel.ngrok.io/webhooks/saq', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    id: 'SAQ20251123104518DF75D20A8F',
    type: 'DEPOSIT',
    status: 'COMPLETED',
    amount: 99.90,
    clientReference: 'order-1234',
    virtualAccount: 'loja-rj-01',
    paidAt: '2025-11-23T10:46:26.986Z',
  }),
});
import requests

requests.post(
    'https://seu-tunel.ngrok.io/webhooks/saq',
    headers={'Content-Type': 'application/json'},
    json={
        'id': 'SAQ20251123104518DF75D20A8F',
        'type': 'DEPOSIT',
        'status': 'COMPLETED',
        'amount': 99.90,
        'clientReference': 'order-1234',
        'virtualAccount': 'loja-rj-01',
        'paidAt': '2025-11-23T10:46:26.986Z',
    },
)

Reenviar callback real

Para reprocessar um callback que falhou no seu lado (depois de ter ajustado o handler), use os endpoints de reenvio:

Inspecionar o histórico

A Saq guarda todas as tentativas de entrega. Útil para investigar falha:

Armadilhas comuns

ArmadilhaSintoma
Processamento síncrono no handlerTimeouts, retry, baixa em dobro
Responder 4xx por erro de validação internaSaq não retenta, callback perdido
Dedupe só por idEstorno (REFUNDED) é ignorado
Endpoint sem proteção de IPEndpoint pode receber payloads forjados
Logger imprime payload sem mascarar payerDocumentRisco LGPD

On this page