# OAuth API

### Start OAuth authorization

Validates the application, exact redirect URI, requested granular scopes, state, and PKCE S256 challenge, then sends the browser to Demarky consent.

`GET /oauth/authorize`

#### Authentication

No authentication required.

#### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `client_id` | query | string | Yes | Public identifier of the registered partner application. An unknown client is reported directly as a 400 instead of redirected. |
| `redirect_uri` | query | string | Yes | Must match one of the redirect URIs registered for the application exactly, including scheme, host, port, and path. A mismatch is reported directly as a 400 instead of redirected. |
| `response_type` | query | string | Yes | Constant `code`. Only the authorization code flow is supported; any other value redirects back with `error=unsupported_response_type`. |
| `scope` | query | string | Yes | Space-separated granular scopes to request. At least one is required, every value must be one of: products:read, products:write, pages:read, pages:write, pages:deploy, leads:read, leads:write, and the set must be within the scopes the application itself is registered for. Duplicates are collapsed, and anything unknown or beyond the application registration redirects back with `error=invalid_scope`. |
| `state` | query | string | Yes | Opaque CSRF value returned unchanged on the redirect back to `redirect_uri`, on both success and error. Must be at least 16 characters; 22 or more of base64url randomness is recommended. A missing or too-short value redirects back with `error=invalid_request` and no state. |
| `code_challenge` | query | string | Yes | PKCE challenge: the base64url-encoded SHA-256 of the code verifier, 43 to 128 characters from `[A-Za-z0-9_-]`. A malformed value redirects back with `error=invalid_request`. |
| `code_challenge_method` | query | string | Yes | Constant `S256`. Plain PKCE is not accepted; any other value redirects back with `error=invalid_request`. |

#### cURL example

```bash
curl -X GET "https://api.demarky.ai/oauth/authorize?client_id=app_0123456789abcdefghjkmnpqrs&redirect_uri=https%3A%2F%2Fpartner.example%2Foauth%2Fcallback&response_type=code&scope=products%3Aread%20pages%3Awrite&state=ac9f7e2b13c84f0f&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256"
```

### Read pending OAuth consent

Returns the application name, target account, and exact requested scopes for an active pending authorization request. It never returns credentials or authorization codes.

`GET /v1/oauth/authorize/request`

#### Authentication

Requires bearer authentication.

#### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `request` | query | string | Yes |  |

#### cURL example

```bash
curl -X GET https://api.demarky.ai/v1/oauth/authorize/request \
  -H "Authorization: Bearer dmk_at_live_xxx"
```

### Submit OAuth consent

Approves or denies a pending authorization request using the logged-in account owner session.

`POST /v1/oauth/authorize/decision`

#### Authentication

Requires bearer authentication.

#### Request body

This request body is required.

| Field | Type | Required | Notes |
|---|---|---|---|
| `request` | string | Yes |  |
| `approve` | boolean | Yes |  |

The example below includes optional fields for context; only fields marked **Yes** are always required. Conditional requirements are listed in **Notes**.

#### cURL example

```bash
curl -X POST https://api.demarky.ai/v1/oauth/authorize/decision \
  -H "Authorization: Bearer dmk_at_live_xxx"
```

### Exchange or refresh OAuth tokens

Exchanges a single-use authorization code with PKCE or rotates a refresh token. Clients authenticate with HTTP Basic or form credentials.

`POST /oauth/token`

#### Authentication

Requires HTTP Basic application authentication.

#### Request body

This request body is required.

| Field | Type | Required | Notes |
|---|---|---|---|
| `grant_type` | string | Yes | Allowed: authorization_code, refresh_token |
| `code` | string | No |  |
| `redirect_uri` | string (uri) | No |  |
| `code_verifier` | string | No |  |
| `refresh_token` | string | No |  |
| `scope` | string | No |  |
| `client_id` | string | No |  |
| `client_secret` | string (password) | No |  |

The example below includes optional fields for context; only fields marked **Yes** are always required. Conditional requirements are listed in **Notes**.

```json
{
  "grant_type": "authorization_code",
  "code": "dmk_ac_live_replace_me",
  "redirect_uri": "https://partner.example/oauth/callback",
  "code_verifier": "replace_with_the_original_pkce_code_verifier"
}
```

#### cURL example

```bash
curl -X POST https://api.demarky.ai/oauth/token \
  -u "app_0123456789abcdefghjkmnpqrs:dmk_cs_live_xxx"
```

### Revoke an OAuth token

Revokes one access token or an entire refresh-token family. Unknown tokens also return 200.

`POST /oauth/revoke`

#### Authentication

Requires HTTP Basic application authentication.

#### Request body

This request body is required.

| Field | Type | Required | Notes |
|---|---|---|---|
| `token` | string | Yes |  |
| `token_type_hint` | string | No | Allowed: access_token, refresh_token |
| `client_id` | string | No |  |
| `client_secret` | string (password) | No |  |

The example below includes optional fields for context; only fields marked **Yes** are always required. Conditional requirements are listed in **Notes**.

```json
{
  "token": "dmk_at_live_replace_me",
  "token_type_hint": "access_token"
}
```

#### cURL example

```bash
curl -X POST https://api.demarky.ai/oauth/revoke \
  -u "app_0123456789abcdefghjkmnpqrs:dmk_cs_live_xxx"
```
