StablePay uses Webhooks to asynchronously push key payment, refund, subscription, and invoice events to the callback URL configured in your merchant dashboard. This page summarizes Webhook event types, request headers, signature verification requirements, and the recent risk-control update that introduces theDocumentation Index
Fetch the complete documentation index at: https://docs.stablepayfi.ai/llms.txt
Use this file to discover all available pages before exploring further.
frozen payment status.
Event types
| Category | Event types |
|---|---|
| Payments | payment.completed, payment.failed, payment.expired, payment.cancelled |
| Refunds | refund.succeeded, refund.failed |
| Subscriptions | subscription.created, subscription.trialing, subscription.active, subscription.past_due, subscription.canceled, subscription.updated, subscription.incomplete_expired |
| Invoices | invoice.created, invoice.paid, invoice.payment_failed |
Webhook headers
| Header | Description |
|---|---|
Content-Type | Fixed value: application/json |
User-Agent | StablePay-Webhook/1.0 |
X-StablePay-Signature | Lowercase hex signature of {timestamp}.{nonce}.{raw_body} using HMAC-SHA256 |
X-StablePay-Timestamp | Unix timestamp (seconds) |
X-StablePay-Nonce | Random string (16–64 characters), included in signature calculation, unique per notification |
X-StablePay-Event-Type | Event type, for example payment.completed |
X-StablePay-Event-ID | Unique event ID used for idempotent deduplication |
Signature verification
Always verify the signature using the original request body bytes. Do not parse the JSON and serialize it again before verification. Signature payload format:- Read
X-StablePay-Signature,X-StablePay-Timestamp, andX-StablePay-Nonce - Verify the timestamp is within 5 minutes of current time
- Verify the nonce length is between 16 and 64 characters
- Compute HMAC-SHA256 using your Secret Key
- Compare signatures using a constant-time comparison method
For code examples, see Pre-Integration Setup.
Response and retry behavior
- Your service should return HTTP
2xxwithin 30 seconds - Returning
429or5xxenters the retry queue; other4xxresponses are not retried by default - StablePay retries up to 10 times
- Use
X-StablePay-Event-IDor theidfield in the request body as your idempotency key
payment.failed and the new frozen status
StablePay recently strengthened its risk-control capabilities. After this rollout, when a payment order triggers a high-risk rule, the funds may be temporarily frozen. These cases typically involve suspected illicit activity, abnormal transaction behavior, or other high-risk patterns, and require additional review before the final outcome is determined.
After this change goes live, StablePay will continue using the existing payment.failed Webhook event type. However, the payment status in the callback can now include:
payment.failed handler should now distinguish at least two cases in data.object.status:
failed: handle as a standard payment failurefrozen: do not treat as a standard payment failure
metadata or as standalone fields. Use type, id, and the core state fields inside data.object as the primary source of truth for your logic.
Example frozen callback
Handling guidance
When you receivepayment.failed, add an extra check for data.object.status:
- If
status = "failed", keep your existing payment-failure handling - If
status = "frozen", mark the order as something likerisk_frozenorpending_risk_review
frozen cases, do not automatically treat the order as a normal failed payment, and avoid actions such as:
- automatic make-good / replacement fulfillment
- automatic shipment
- automatic entitlement release or service activation
