---
title: "External API token (client credentials)"
method: POST
path: "/external/v1/token"
tags: ["External"]
---

# External API token (client credentials)

`POST /external/v1/token`

**Auth:** Anonymous - no token required.

OAuth2 **client-credentials** grant for external API clients (OneRoster auth format).

### Request

The body must be **form-encoded**:

- `Content-Type: application/x-www-form-urlencoded` _(multipart/form-data is also accepted)_
- `grant_type=client_credentials` — **required**; any other value returns `400 unsupported_grant_type`

Supply credentials **one** of two ways:

| Method | How |
| --- | --- |
| HTTP Basic auth | `Authorization: Basic base64(client_id:client_secret)` |
| Form fields | `client_id` and `client_secret` in the body |

> ⚠️ Credentials must **not** be placed in the query string — secrets in URLs leak through
> proxy/server logs, browser history and referrers. A `client_id`/`client_secret` query
> parameter is rejected with `400 invalid_request`.

#### Example

```bash
curl -X POST "{baseUrl}/external/v1/token" \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials"
```

### Response

On success returns a short-lived bearer token carrying the client's configured permissions
and allowed endpoints. **There is no refresh token** — re-request when it expires.

```json
{
  "access_token": "<jwt>",
  "token_type": "Bearer",
  "expires_in": 3600
}
```

Call the API with `Authorization: Bearer <access_token>`.

### Errors

| Status | Meaning |
| --- | --- |
| `400 invalid_request` | Malformed request, or credentials in the query string |
| `400 unsupported_grant_type` | `grant_type` is not `client_credentials` |
| `401 invalid_client` | Unknown or missing credentials |
| `403 access_denied` | The client is disabled |

## Response `200`

Success - access token issued

- PostExternalTokenResponse
  - `access_token` string, nullable
  - `token_type` string
  - `expires_in` integer

## Other responses

- `400` — invalid_request / unsupported_grant_type
- `401` — invalid_client
- `403` — client disabled

---

[API](https://skmtc.net/tonbridge/apis/tonbridge-app-api.md) · [All operations](https://skmtc.net/tonbridge/apis/tonbridge-app-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/tonbridge/tonbridge-app-api/versions/fab57bf5558a/schema)
