# 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

```text
https://southbill.com/oauth/authorize
  ?client_id=app_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

```bash
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": "…"
  }'
```

```json
{
  "access_token": "sb_at_live_…",
  "refresh_token": "sb_rt_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

```json
{ "grant_type": "refresh_token", "refresh_token": "sb_rt_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://localhost` is 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 `installation.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 installation_suspended`. Back off and retry later — do not delete data on a suspension.
