Skip to main content
Webhooks allow your application to be notified automatically when changes happen in payments and cashouts.

How it works

When you create a payment or cashout with callbackUrl, Safefy sends an HTTP POST request to that URL whenever status reaches a relevant state. For full domain-specific payloads, see:

Payment events

EventDescriptionWhen it happens
payment.completedPayment confirmedWhen PIX is paid and confirmed by the bank
payment.expiredTransaction expiredWhen the payment window ends
payment.failedProcessing failedWhen there is an acquirer or validation error
payment.cancelledPayment cancelledWhen the payment is manually cancelled
payment.refundedFull refundWhen a confirmed payment is refunded
payment.partially_refundedPartial refundWhen part of the payment is refunded
payment.refund_requestedRefund requestedWhen the refund is in processing

Cashout events

EventCashout status
cashout.completedCompleted
cashout.failedFailed
cashout.rejectedRejected
cashout.cancelledCancelled

Sent headers

HeaderDescription
X-Safefy-SignatureHMAC-SHA256 signature for validation
X-Safefy-EventEvent type (payment.* or cashout.*)
X-Safefy-DeliveryUnique delivery ID
X-Safefy-AttemptAttempt number (1, 2, 3)

Signature

  • For payment.* events, the secret is paymentId.
  • For cashout.* events, the secret is payoutId.
Always validate the signature before processing the webhook.

Retries

If your endpoint does not return 2xx, Safefy retries:
  1. Attempt 1 (immediate)
  2. Attempt 2 after ~2 seconds
  3. Attempt 3 after ~4 seconds
Recommendation: acknowledge quickly (200 OK) and process asynchronously.

Idempotency

Because retries can happen, your webhook processing must be idempotent.
  • Recommended key: id (delivery id) or data.id + type
  • Ignore already-processed events

Quick example

{
  "id": "4d955fd7-cbb9-4d3c-8d36-4cb5de773b35",
  "type": "cashout.completed",
  "createdAt": "2026-02-25T12:00:00Z",
  "data": {
    "id": "8f8fce31-5c83-42c4-b90b-c29636b595e3",
    "status": "Completed",
    "amount": 50000,
    "netAmount": 49000
  }
}

Payment status flow

Understand the transaction lifecycle and when each webhook is fired:
AttemptInterval
1Immediate
22 seconds
34 seconds

Best practices

Respond quickly

Return 200 OK immediately and process the webhook asynchronously.

Be idempotent

Use the webhook id to avoid processing the same event twice.

Validate the signature

Always verify X-Safefy-Signature before trusting the payload.

Use HTTPS

Configure your callbackUrl with HTTPS in production.