Skip to main content
Every webhook request body is a JSON event envelope. Route the event using X-SFPY-EVENT-TYPE or webhookEventType, deduplicate it using X-SFPY-EVENT-ID or identifier, and then inspect subject.payment for the affected resource.

Event envelope

JSON property names use lower camel case. Protobuf int64 values, including payment amounts and internal numeric IDs, are serialized as JSON strings. Timestamps use RFC 3339 and enum values use their string names.
Treat payload evolution as additive. Ignore fields and enum values you do not recognize, and use webhookEventType rather than relying only on kind or code.

Complete payment.completed example

The following is a representative complete body generated by the current V1_0_0 serializer. Empty strings, empty arrays, and null nested resources are included because the serializer emits unpopulated fields.
The exact subject.payment snapshot can contain populated transaction logs, payment parties, QR details, merchant details, refunds, settlement details, authorization details, and aggregator details. Persist the entire event, but select only the fields your integration needs.

Refund event example

Refund lifecycle events use kind: REFUND_EVENT and include the payment plus its refund records. This focused example omits unrelated empty payment fields for readability; the delivered body uses the same complete envelope shown above.
Payment-level events payment.refunded and payment.refund_partial can describe the resulting aggregate payment status. Events in the refund.* family describe the individual refund lifecycle. Make refund processing idempotent using the event identifier and identify the refund by subject.payment.refunds[].token.

Payout event example

Payouts use the payment.* event family. Identify a payout through subject.payment.type; there are no payout.* webhook names.

Failed events

For payment.failed, payment.rejected, or refund.failed, use the resource snapshot and its transaction logs to determine the latest known state. Do not confuse a business failure inside the event with last_error from the Webhook Delivery API: last_error describes Safepay’s HTTP delivery attempt, such as status 422, not why the payment or refund failed.

Match headers to the body

Before processing, verify that the routing headers agree with the signed body: The HMAC covers the body, not these routing headers individually. Verify the body signature first, then reject the request if a routing header does not match its signed body field or your expected aggregator and subscription.

See also