Installs & OAuth
The authorization code flow with PKCE, step by step.
Merchants install your app through OAuth 2.0 — authorization code with PKCE. No client secret is ever needed in a browser.
1. Send the merchant to consent
https://southbill.com/oauth/authorize
?client_id=sb_client_live_123
&redirect_uri=https://yourapp.com/callback
&response_type=code
&scope=merchant:read%20payments:read
&state=<random>
&code_challenge=<base64url(sha256(verifier))>
&code_challenge_method=S256
The merchant sees your icon, name and a plain-language list of every scope with its risk level, then approves or declines.
2. Exchange the code
curl -X POST https://api.southbill.com/v1/oauth/token \
-H "Content-Type: application/json" \
-d '{
"grant_type": "authorization_code",
"code": "ac_…",
"redirect_uri": "https://yourapp.com/callback",
"client_id": "sb_client_live_123",
"code_verifier": "…"
}'
const res = await fetch("https://api.southbill.com/v1/oauth/token", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
grant_type: "authorization_code",
code,
redirect_uri: "https://yourapp.com/callback",
client_id: "sb_client_live_123",
code_verifier: verifier,
}),
});
const tokens = await res.json(); // { access_token, refresh_token, installation_id, … }
tokens = requests.post(
"https://api.southbill.com/v1/oauth/token",
json={
"grant_type": "authorization_code",
"code": code,
"redirect_uri": "https://yourapp.com/callback",
"client_id": "sb_client_live_123",
"code_verifier": verifier,
},
timeout=30,
).json()
<?php
$ch = curl_init("https://api.southbill.com/v1/oauth/token");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ["Content-Type: application/json"],
CURLOPT_POSTFIELDS => json_encode([
"grant_type" => "authorization_code",
"code" => $code,
"redirect_uri" => "https://yourapp.com/callback",
"client_id" => "sb_client_live_123",
"code_verifier" => $verifier,
]),
]);
$tokens = json_decode(curl_exec($ch), true);
{
"access_token": "sbat_live_…",
"refresh_token": "sbrt_live_…",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "merchant:read payments:read",
"installation_id": "inst_live_7c1f92a8",
"environment": "live"
}
Store the tokens per installation, never globally.
3. Refresh
{ "grant_type": "refresh_token", "refresh_token": "sbrt_live_…", "client_id": "sb_client_live_123" }
Refresh tokens rotate: the response contains a new refresh token and the old one dies. If a refresh returns invalid_grant, treat the installation as gone and stop calling.
Redirect URI rules
- HTTPS only, exact match, no wildcards.
http://localhostis allowed in test mode. - Up to five URIs per app.
- Changing a URI on a published app requires a new version.
Uninstall
When a merchant uninstalls, tokens are revoked immediately and you receive an app.uninstalled webhook. Delete the merchant's data within 30 days unless law requires otherwise.
Suspension
If a merchant is suspended, API calls for that installation return 403 merchant_suspended. Back off and retry later — do not delete data on a suspension.