sp_sk_test_.
Everything below runs against https://api.southpay.io/api/v2. There is no separate sandbox host. Test and live mode are selected by which key you send.
In test mode your store has exactly one usable asset: Ethereum Sepolia (
SETH). Every test payment intent returns one deposit address on Sepolia. Live mode returns one address per asset your store has enabled. See Environments, and Sepolia test wallet to get the test ETH you need to pay an intent.1. Create a payment intent
You price in fiat. SouthPay locks an exchange rate and works out the crypto amount.idis the UUID you use on every later API call. All SouthPay ids are bare UUIDs; there is nopi_prefix scheme.referenceis 16 uppercase alphanumeric characters. This is the customer-facing identifier and the checkout token, and it is what arrives in webhooks.expires_atis 60 minutes from creation by default. Sendexpires_in(seconds,300to86400) on create for a different window. See Payment lifecycle.
crypto_amount_atomic is the exact amount the customer must send, in the asset’s smallest unit. Divide by 10 ** atomic_decimals for a human amount, here 0.022533243243243243 ETH.
The Idempotency-Key header is optional on this endpoint but retrying without one creates a second intent. Read Idempotency before you build a retry loop; the behaviour has edges.
2. Send the customer to checkout
Redirect to thehosted_url on the response. Do not build the URL yourself.
success_url when the payment completes. There is no frontend work to do.
3. Register a webhook endpoint
subscribed_events is required and must list every event you want by name. Wildcards are not supported. "*" and "payment_intent.*" both fail validation. See the full event list.
4. Verify and handle the delivery
When the payment confirms, SouthPayPOSTs this to your URL:
Webhook body
2xx within 30 seconds. Any non-2xx response is retried, up to 10 attempts over roughly 18 hours, including a 400 or 404 your framework returns by accident. Deduplicate on the event id.
Test the whole path without paying anything:
Polling instead
If you cannot receive webhooks, fetch the intent by UUID and readstatus.
terminal: true means the outcome is settled. It does not mean the record is frozen. A terminal intent can still become refunded later.
Next
Payment lifecycle
The six statuses, the ten state reasons, and what each one means for an order.
Webhooks
Signing, retries, the event catalogue, and the five events that never fire.
Errors
Three response shapes, and why
error.message will throw if you assume one.Payout destinations
Where the money goes, and how to configure it before your first payout.

