client_id, redirect URIs, requested scopes) that lets you run the Authorization Code with PKCE flow against a merchant’s SouthPay account. Tokens issued through it look like spo_… and reach any public endpoint the granted scopes allow.
Register the app once, then implement the runtime flow described in Authentication.
Managing apps is a developer-portal task, not a runtime API.
/api/v2/merchants/oauth_apps accepts a logged-in dashboard session only. An sp_ API key or an spo_ token gets 401. Create and edit apps at app.southpay.io; the request shapes below describe what the dashboard does on your behalf.Public or confidential
confidential is immutable after creation, as is client_id. To change either, register a new app.
Scopes
An app declares the scopes it may ever request. Eighteen exist; the merchant approves a subset at consent time.
One rough edge in the consent flow to work around:
The consent screen renders no description for
accounts:read, accounts:write, accounts:act and agents:write. Merchants see the scope name and nothing else. If your app requests any of them, explain what they do in your own UI before you send the merchant to the authorize page.Creating an app
id is a bare UUID. client_id is spo_app_dev_ followed by 24 hex characters. Validation failures come back as {"errors": ["…"]} with 422, the list shape rather than the object shape. See Errors.
Reading and updating
GET /api/v2/merchants/oauth_apps returns a bare array of your apps, newest first. GET /api/v2/merchants/oauth_apps/{id} returns one.
PATCH accepts name, website_url, logo_url, active, redirect_uris and scopes. It does not accept client_id or confidential.
Narrowing scopes does not retroactively narrow tokens already issued. Existing grants keep whatever the merchant approved until they expire or are revoked.
Rotating the secret
client_secret. Public apps get 422 {"error": "not_confidential", "error_description": "Only confidential apps have secrets"}.
Rotation invalidates the old secret immediately, with no overlap window. Every server using it starts failing client authentication at the token endpoint the moment the request returns. Deploy the new value first, or accept a gap.
Deleting
204 and sets active: false. It is a deactivation, not a hard delete. Access tokens already issued keep working until they expire; refresh attempts against a deactivated app fail.
Discovery
https://api.southpay.io/.well-known/oauth-authorization-server advertises the authorize, token, revoke and userinfo endpoints, plus:
Use it for dynamic configuration rather than hardcoding paths.

