https://api.southpay.io/api/v2.
Authentication
Any valid API key or OAuth token can call these endpoints. Balance swaps are not scope-checked, so no specific scope is required. Publishable keys are still rejected.Creating a swap runs a step-up MFA check. That check is a no-op for API keys and OAuth tokens; it only gates dashboard sessions, which return
403 mfa_required without two-factor enabled.Quote a swap
POST /balance_swaps/quote. No idempotency key, creates nothing.
string
required
The asset to sell, for example
ETH_USDC.string
required
The asset to receive. Must differ from
source_asset_id.string
required
Amount to sell, in atomic units of
source_asset_id. Must be positive.string
required
What the route currently expects to return, in atomic units of
buy_asset_id.string
required
The floor after slippage:
estimated * (10000 - slippage_bps) / 10000. Size your downstream payout against this, not against the estimate.integer
required
Slippage tolerance in basis points.
integer
required
Divisor exponent for the two atomic amounts above.
Create a swap
POST /balance_swaps. Idempotency-Key is required.
Same three parameters as the quote, sent at the top level.
string
required
The asset to sell.
string
required
The asset to receive.
string
required
Atomic units of
source_asset_id. Must be positive and available in your balance.Response fields
string
required
Bare UUID.
string
required
pending, submitted, completed or failed.string
required
Amount debited from
source_asset_id.string | null
Estimate recorded at creation.
string | null
What actually arrived. Null until the swap completes. This is the number to reconcile against.
string | null
Routing provider that executed the swap.
string | null
On-chain hash, once broadcast.
boolean
required
Retrieve a swap
GET /balance_swaps/{id}. Returns a bare swap object. Unknown id returns 404 {"error": "Balance swap not found"}.
There are no swap webhooks. Poll this endpoint until status is completed or failed.
List swaps
GET /balance_swaps. Newest first.
string
pending, submitted, completed or failed.integer
default:"1"
integer
default:"25"
Between 1 and 100.
Response 200
pagination envelope, matching payouts. Payments and billing use meta instead.
Lifecycle
Related: auto-swap
Auto-swap is the automatic version of this, applied to incoming payments rather than to your standing balance. It is configured in the dashboard, not over the API, and its outcome surfaces asauto_swap_status and auto_swap_detail on a payment intent. Auto-swap targets are limited to ETH_USDC and ETH_USDT; manual swaps are not.
Errors
Only the last two carry a
code. The rest use the flat {"error": "<message>"} envelope with no machine-readable identifier, so branch on the status code and the endpoint rather than on the message text.
