> ## Documentation Index
> Fetch the complete documentation index at: https://docs.brandgate.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# OAuth for partners

> Let any BrandGate customer connect your product with one click.

If you build software that many BrandGate customers use, such as an ERP, a WMS or an analytics tool, use OAuth instead of asking customers to copy API keys. The customer clicks **Connect** in your product, approves the access in BrandGate, and you receive tokens that work for that one company.

BrandGate follows **OAuth 2.1**: authorization code flow, PKCE with `S256` is always required, redirect URIs must match exactly, and refresh tokens rotate on every use.

## 1. Register your app

Email BrandGate with:

* your app's name and homepage (`https://…`),
* the exact redirect URIs you will use,
* the [scopes](/authentication#scopes) your app needs,
* whether the app keeps a secret on a server (confidential) or runs on the user's device (public).

You receive a `client_id` (`bg_client_…`) and, for confidential apps, a `client_secret` (`bg_cs_…`), shown once. Apps that BrandGate has reviewed are marked **Verified** on the consent screen.

> [!NOTE]
>
> MCP clients such as Claude and Cursor can register themselves through [dynamic client registration](#dynamic-client-registration) when it is switched on. They are shown as **Not verified**.

## 2. Send the user to BrandGate

Create a random `code_verifier` (43 to 128 characters), derive the challenge, and redirect the user:

```text theme={null}
https://api.brandgate.dev/oauth/authorize
  ?response_type=code
  &client_id=bg_client_…
  &redirect_uri=https://app.example.com/brandgate/callback
  &scope=orders:read orders:write inventory:write
  &state=<random, max 500 characters>
  &code_challenge=<BASE64URL(SHA256(code_verifier))>
  &code_challenge_method=S256
```

`redirect_uri` may be left out only when your app has exactly one registered. `scope` may be left out to ask for everything your app is registered for. You may send `resource=https://api.brandgate.dev/v1` or `https://api.brandgate.dev/mcp` (RFC 8707).

The user signs in to BrandGate if needed. A **company admin** sees your app's name, whether it is verified, where they will be sent back to, and the access in plain language. The request is valid for 10 minutes.

## 3. Handle the callback

BrandGate redirects back to your `redirect_uri`:

```text theme={null}
https://app.example.com/brandgate/callback?code=bg_ac_…&state=…&iss=https://api.brandgate.dev
```

Check that `state` is the one you sent and that `iss` is `https://api.brandgate.dev` (RFC 9207). If the admin declined, you get `error=access_denied` instead of `code`.

## 4. Exchange the code

The code is valid for 2 minutes and works once.

```bash Confidential app theme={null}
curl https://api.brandgate.dev/oauth/token \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d grant_type=authorization_code \
  -d code=bg_ac_… \
  -d redirect_uri=https://app.example.com/brandgate/callback \
  -d code_verifier=$CODE_VERIFIER
```

```bash Public app theme={null}
curl https://api.brandgate.dev/oauth/token \
  -d grant_type=authorization_code \
  -d client_id=$CLIENT_ID \
  -d code=bg_ac_… \
  -d redirect_uri=http://127.0.0.1:53682/callback \
  -d code_verifier=$CODE_VERIFIER
```

```json theme={null}
{
  "access_token": "bg_at_…",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "bg_rt_…",
  "scope": "orders:read orders:write inventory:write"
}
```

Use the access token exactly like an API key: `Authorization: Bearer bg_at_…`.

## 5. Refresh

Access tokens last one hour. Refresh tokens last 90 days and **rotate**: every refresh returns a new refresh token and the old one stops working. Store the new one before you use the access token.

```bash theme={null}
curl https://api.brandgate.dev/oauth/token \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d grant_type=refresh_token \
  -d refresh_token=bg_rt_…
```

You may pass a smaller `scope` to narrow the new access token.

> [!WARNING]
>
> If an old refresh token is used again, BrandGate assumes it was stolen and revokes the whole connection. The customer then has to connect again. Make sure two workers never refresh the same token at the same time.

## Disconnecting

The customer can disconnect your app at any time in **Settings → Developers → Connected apps**; all its tokens stop working immediately. You can also revoke a token yourself (RFC 7009):

```bash theme={null}
curl https://api.brandgate.dev/oauth/revoke -u "$CLIENT_ID:$CLIENT_SECRET" -d token=bg_rt_…
```

Revoking a refresh token also revokes every access and refresh token issued from the same authorization. Call it when the user disconnects BrandGate in your product.

## Redirect URI rules

* `https://` URIs, matched exactly, including the path.
* `http://127.0.0.1`, `http://[::1]` and `http://localhost` on any port, for native and CLI apps.
* Private-use schemes such as `com.example.app:/callback` for mobile apps.

## Dynamic client registration

When it is switched on, MCP clients can register themselves (RFC 7591):

```bash theme={null}
curl https://api.brandgate.dev/oauth/register \
  -H "Content-Type: application/json" \
  -d '{"client_name":"My MCP client","redirect_uris":["http://127.0.0.1:33418/callback"],"token_endpoint_auth_method":"none"}'
```

Self-registered apps are public clients, get read scopes only, and are marked **Not verified** for the admin.

## Discovery

| Document | URL |
| - | - |
| Authorization server metadata (RFC 8414) | `https://api.brandgate.dev/.well-known/oauth-authorization-server` |
| Protected resource metadata for the REST API (RFC 9728) | `https://api.brandgate.dev/.well-known/oauth-protected-resource/v1` |
| Protected resource metadata for MCP | `https://api.brandgate.dev/.well-known/oauth-protected-resource/mcp` |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.