> For the complete documentation index, see [llms.txt](https://developers.gallantreecapital.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developers.gallantreecapital.com/guides/oauth.md).

# OAuth 2.0 client credentials

For server-to-server integrations where you'd rather manage a short-lived access token than a long-lived API key, the platform issues JSON Web Tokens via the standard **client credentials** grant.

Which flow you use is up to you:

* **API key** (`x-api-key`) — simplest; the key is the credential. Good for CLIs, batch jobs, and simple integrations.
* **OAuth client credentials** — the app exchanges a `client_id` + `client_secret` for a short-lived JWT, then uses the JWT as a bearer token on subsequent requests. Good for integrations that want token lifetimes measured in minutes, not months.

Both flows resolve to the same `DeveloperAuthContext` on the server side. Every scope check, every rate-limit tier, and every audit-log entry behaves identically.

## Endpoint

```
POST /api/auth/oauth/token
```

## Request

```http
POST /api/auth/oauth/token HTTP/1.1
Host: developers.gallantreecapital.com
Content-Type: application/json

{
  "grant_type": "client_credentials",
  "client_id": "your-app-client-id",
  "client_secret": "your-app-client-secret"
}
```

* `grant_type` **must** be `client_credentials`. Anything else returns `400 unsupported_grant_type`.
* `client_id` and `client_secret` are shown in the portal on the app's Credentials page. The secret is shown **once**; treat it like a password and store it in a secret manager.
* Missing or wrong credentials return `401 invalid_client` — deliberately generic, as with API keys.

## Response

```json
{
  "access_token": "eyJhbGciOiJIUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 900,
  "scope": "loans:read organisations:read"
}
```

* **`access_token`** — a JWT signed by the platform. Use it as a `Bearer` token on subsequent requests.
* **`expires_in`** — seconds until the token expires. The default is **900 (15 minutes)** — request a new token before it expires; the platform does not refresh existing tokens.
* **`scope`** — the space-separated list of scopes on the app. Every request the token makes is bounded by this list.

## Using the token

```http
GET /api/v1/loans HTTP/1.1
Host: developers.gallantreecapital.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
```

Send it in `Authorization: Bearer …` — do **not** put it in `x-api-key`. The middleware distinguishes the two.

## Choosing between key and JWT

| Property                | API key                                                               | OAuth JWT                                                      |
| ----------------------- | --------------------------------------------------------------------- | -------------------------------------------------------------- |
| Lifetime                | 90 days (default)                                                     | 15 minutes                                                     |
| Rotation                | Manual — generate a new key, retire the old within a 24h grace window | Automatic — request a fresh token when the current one expires |
| Storage                 | Secret store; grep-safe prefix `gt_`                                  | Ephemeral — hold in memory only                                |
| Best for                | Long-lived integrations, CLIs, batch jobs                             | Frequent-request services that want short-lived credentials    |
| Compromise blast radius | Up to 90 days if you don't detect it                                  | Up to 15 minutes if you don't detect it                        |

For most integrations, API keys are fine. Reach for OAuth when the token blast radius matters more than the request overhead.

## Runnable example

```bash
# 1. Get a token
TOKEN=$(curl -sS https://developers.gallantreecapital.com/api/auth/oauth/token \
  -H "Content-Type: application/json" \
  -d "{
    \"grant_type\": \"client_credentials\",
    \"client_id\": \"$GALLANTREE_CLIENT_ID\",
    \"client_secret\": \"$GALLANTREE_CLIENT_SECRET\"
  }" | jq -r .access_token)

# 2. Use it
curl -sS https://developers.gallantreecapital.com/api/v1/loans \
  -H "Authorization: Bearer $TOKEN"
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://developers.gallantreecapital.com/guides/oauth.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
