Authorization: Bearer <token>. The prefix on the token decides which credential type SouthPay authenticates it as, and which store and mode the request runs in.
There is no separate sandbox host. Test and live mode are carried on the credential, so
sp_sk_test_… reads and writes test data and sp_sk_live_… reads and writes live data, on the same base URL. See Environments.
You never send X-Store-Id or X-Livemode with an API key, OAuth token or agent credential. Both are implied by the credential. Those two headers exist only for logged-in dashboard sessions, which are not part of the public API.
API keys
Create keys in the dashboard under Settings → API keys. Choose the mode (live or test) and the type (secret or publishable). The full key value is shown once, at creation. There is no endpoint that returns it again, and SouthPay stores only a SHA-256 hash.A secret key is full access to a store’s money, bounded only by its scopes. Never put one in client-side code, a mobile binary, a public repository, or a URL. If one leaks, revoke it in the dashboard before you do anything else. Rotation is instant and the old value stops working immediately.
sp_live_… / sp_test_… prefix. They still authenticate and behave as secret keys. Nothing generates that form any more.
Publishable keys
A publishable key may call exactly one endpoint:POST /api/v2/payments. Every other path returns 403 with code publishable_key_forbidden. Its scope set is fixed to payments:write and cannot be widened.
That restriction is the whole security model, so a publishable key is safe to embed in a page. It cannot read the payments it creates, list anything, or move money.
Scopes
Fifteen scopes exist. A new secret key gets all of them except the threeaccounts: scopes, which are added only for platform accounts.
Holding
<resource>:write implies <resource>:read. A scope check that fails returns 403 with code insufficient_scope.
The required scope is derived from the resource and the HTTP verb: any method other than GET or HEAD needs :write. Reports map to the balances resource, not their own.
Some endpoints are not in the scope map at all and accept any valid credential: balance swaps, connected integrations, OAuth connections and verification requests. Do not read a narrow scope list as a guarantee that a key cannot reach those.
Acting as a sub-merchant
A platform sendsX-Southpay-Account: <account_id> alongside its own credential to run any merchant endpoint against one of its sub-merchants. It requires accounts:act, and the platform feature must be enabled on your store. See Platform accounts.
OAuth 2.0
Use OAuth when you are building an application that other merchants connect to their own SouthPay accounts. The flow is Authorization Code with PKCE. Register the application first; see OAuth apps for that. The discovery document athttps://api.southpay.io/.well-known/oauth-authorization-server advertises the endpoints, response_types_supported: ["code"], grant_types_supported: ["authorization_code", "refresh_token"], and code_challenge_methods_supported: ["S256"].
Available scopes
Eighteen scopes may be requested. The first fifteen mirror the API key scopes above.profile · stores:read · payments:read · payments:write · refunds:read · refunds:write · payouts:read · payouts:write · balances:read · billing:read · billing:write · webhooks:read · webhooks:write · accounts:read · accounts:write · accounts:act · agents:write · offline_access
The consent screen currently renders no description for
accounts:read, accounts:write, accounts:act and agents:write. Merchants approving those scopes see the name only. If your app requests them, explain them in your own UI before you send the merchant to the authorize page.Step 1: generate the PKCE pair
code_verifier server-side and bound to the session. You send it only in step 3.
Step 2: authorize
Authorize request
livemode selects the mode for the whole grant. Pass livemode=true for production access. The mode is recorded on the authorization code, copied onto the access token, and cannot be changed afterwards. A token issued in test mode can never read live data, and there is no header that overrides it. Omitting the parameter yields a test-mode grant.
The GET returns consent data as JSON: the application, the requested scopes, and the merchant’s stores. Render your own consent UI from it, then post the decision.
{"redirect_to": "https://yourapp.example/oauth/callback?code=…&state=…"}. Send the browser there, and compare the returned state to what you issued before doing anything else.
Authorization codes live for 10 minutes and are single use.
Step 3: exchange the code
client_secret, in the body or by HTTP Basic auth.
Token lifetimes
The access token’s lifetime is set by the scopes you asked for, not by a fixed policy. Refresh tokens last 90 days and are single use. Each refresh rotates them and returns a new one. Store the new value on every refresh.Using and revoking
200 with an empty body.
OAuth endpoints return errors in the RFC 6749 shape,
{"error": "invalid_request", "error_description": "…"}, which is not the shape the rest of the API uses. See Errors.Flow
Agent credentials
spa_live_ and spa_test_ credentials authenticate the /api/v2/agentic/* surface and nothing else. A regular API key is rejected there, and an agent credential is rejected everywhere else.
Their scope set is different and smaller: payments:read, payments:write, payouts:read, payouts:write, assets:read, assets:write, x402:read, x402:write. The agentic API is also rate limited at 600 requests per minute per credential in live mode, shared across every agentic path. See Rate limits.
An agent only ever sees records it created. See Agent credentials.
Choosing
Endpoints you cannot reach with these credentials
Several parts of the API accept only a logged-in dashboard session, and return401 for an API key or OAuth token. They are not part of the public API and no integration should depend on them:
API keys and agent credentials themselves, agent spend limits, payout destinations, custom domains, store records and store settings, merchant-owned OAuth apps, and the reference-data endpoints supported_assets, networks and payment_currencies.
Where you need one of those, configure it in the dashboard. The catalogue of assets and currencies is documented on Supported currencies and assets so you do not need to call those endpoints at all.
