Skip to main content
A payout schedule answers two questions for a single asset: how often the accumulated balance leaves SouthPay, and where it goes. When next_payout_at passes and the balance clears minimum_fiat_cents, a payout is created for you. One schedule exists per store, asset and mode. Creating a second for the same asset is rejected. Edit the existing one instead. Paths are relative to https://api.southpay.io/api/v2.

Before you can create one

A schedule needs a payout destination: a saved bank account (fiat_payout_detail_id) or a saved wallet (crypto_payout_detail_id). Neither can be created over the public API. Both are configured in the dashboard. See payout destinations. A fiat destination must be verified, and the store must have confirmed KYC.

Authentication and scope

Reads require payouts:read, writes require payouts:write. Publishable keys are rejected.

List schedules

GET /payout_schedules. Scope payouts:read. Returns a bare array with no envelope and no pagination, oldest first. Only active schedules are returned; deleting one hides it from this list permanently.

Create a schedule

POST /payout_schedules. Scope payouts:write. Idempotency-Key is not used here. All fields go inside a required payout_schedule object.
string
required
The asset whose balance this schedule drains. Cannot be changed later.
string
required
daily, weekly or monthly.
string
Off-ramp to this saved bank account. Must be verified, and the store must have confirmed KYC.
string
Send on-chain to this saved wallet.
integer
Skip the run when the accrued balance is worth less than this. Minor units of minimum_fiat_currency.
string
One of USD, EUR, GBP, HKD, SGD, JPY. Falls back to USD.
string
ISO 8601. When the first run is due.
string
Swap the balance into this asset before paying out, producing a fiat_autoswap payout. Write-only: it does not appear in the response.
boolean
default:"true"
Set false to pause the schedule without deleting it.
string
Legacy raw address. Prefer crypto_payout_detail_id.
minimum_fiat_currency accepts six currencies. A payment intent accepts ten, and DKK, CHF, SEK and NOK are valid there but not here. Sending one of the extra four on a schedule silently falls back to USD, so a threshold you meant as 500 DKK becomes 500 USD.

Response fields

string
required
Bare UUID.
string
required
daily, weekly or monthly.
string
required
fiat, crypto, or legacy for a schedule created against a raw destination_address before saved destinations existed.
string
required
fiat_manual for fiat destinations, crypto otherwise, including legacy. This is the rail the produced payouts will carry.
string | null
When the next run is due.
string | null
When this schedule last produced a payout. Null if it never has.
string | null
Why the last run produced nothing: no_balance, below_minimum or below_network_minimum. Check this first when a schedule looks stuck. A non-null value means the schedule ran and declined, not that it failed.
integer | null
Threshold in minor units of minimum_fiat_currency.
boolean
required
False once the schedule is paused or deleted.
string | null
string | null
object
required
Unlike the payout object’s asset, this one carries no atomic_decimals.
swap_target_asset_id can be written but is never returned. To confirm it took effect, look at the payout_rail of the payouts the schedule produces. An auto-swap schedule yields fiat_autoswap.

Retrieve a schedule

GET /payout_schedules/{id}. Scope payouts:read. Returns the schedule plus a payouts array of every payout it has produced, in full payout shape.
Response 200

Update a schedule

PATCH /payout_schedules/{id}. Scope payouts:write. Accepts the same payout_schedule fields as create, except asset_id, which is ignored. Null values are dropped rather than clearing a field, so you cannot unset a destination this way. Switch it to the other destination type instead. Returns 200 with the updated schedule.

Delete a schedule

DELETE /payout_schedules/{id}. Scope payouts:write. A soft delete: the schedule is set active: false and stops running. Returns 204 with no body. It disappears from the list endpoint and cannot be reactivated through the API. Create a new schedule instead.

Run a schedule now

POST /payout_schedules/{id}/run. Scope payouts:write. No body. Evaluates the schedule immediately, ignoring next_payout_at. The minimum-balance rule still applies, so a run can legitimately produce nothing.
Both outcomes are 200. Branch on triggered, not on the status code. skip_reason is no_balance, below_minimum or below_network_minimum. This endpoint runs a step-up MFA check. For API keys and OAuth tokens it is a no-op; dashboard sessions without two-factor get 403 {"error": "Enable two-factor authentication to run a payout", "code": "mfa_required"}.

Errors

Validation failures on create and update use the array envelope {"errors": ["…"]}, which is a third shape distinct from the flat string and the structured object used elsewhere. Service-level failures on run surface through the global handler as {"error": {"message": "…", "code": null}} at 422.
Last modified on August 21, 2026