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

# Endpoints

> Full reference for all Busha OAuth2 protocol endpoints.

The OAuth2 surface is split into two hosts: protocol endpoints at the issuer (`login.busha.io`) and the resource API (`api.busha.io`). OAuth app management — creating apps, revealing secrets, editing redirect URIs — is dashboard-only at [app.busha.io](https://app.busha.io).

| Method | Endpoint                            | Purpose                                                     |
| ------ | ----------------------------------- | ----------------------------------------------------------- |
| `GET`  | `/oauth2/auth`                      | Start an authorization request (browser redirect).          |
| `POST` | `/oauth2/token`                     | Exchange a code for tokens, or refresh an existing session. |
| `POST` | `/oauth2/revoke`                    | Revoke an access or refresh token.                          |
| `GET`  | `/.well-known/openid-configuration` | OIDC discovery document.                                    |
| `GET`  | `/.well-known/jwks.json`            | Public keys for JWT signature verification.                 |

## `GET /oauth2/auth`

Browser redirect — this is where you send the user's browser to start authorization. Never call this server-to-server.

**Required query parameters:**

| Parameter               | Value                                                              |
| ----------------------- | ------------------------------------------------------------------ |
| `response_type`         | `code`                                                             |
| `client_id`             | Your app's `client_id`                                             |
| `redirect_uri`          | Byte-identical to a registered URI                                 |
| `scope`                 | Space-separated scope list                                         |
| `state`                 | CSRF guard — at least 16 random bytes, bound to the user's session |
| `code_challenge`        | `base64url(sha256(code_verifier))`                                 |
| `code_challenge_method` | `S256` — `plain` is not accepted; missing challenges are rejected  |

**Success:** Redirects to your callback with `?code=...&state=...`

**User cancelled:** Redirects with `?error=access_denied&state=...`

## `POST /oauth2/token`

Server-to-server. Authenticate with HTTP Basic using `client_id:client_secret`. Never call this from the browser.

### Authorization code → tokens

```bash theme={null}
curl -X POST https://login.busha.io/oauth2/token \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data-urlencode "grant_type=authorization_code" \
  --data-urlencode "code=$AUTH_CODE" \
  --data-urlencode "redirect_uri=https://yourapp.example.com/oauth2/callback" \
  --data-urlencode "code_verifier=$CODE_VERIFIER"
```

### Refresh token → new pair

```bash theme={null}
curl -X POST https://login.busha.io/oauth2/token \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data-urlencode "grant_type=refresh_token" \
  --data-urlencode "refresh_token=$REFRESH_TOKEN"
```

**Important details:**

* `id_token` is returned only when `openid` is in the granted scopes.
* `refresh_token` is returned only when `offline_access` is granted.
* `redirect_uri` must exactly match a registered URI.
* Authorization codes are **single-use** and expire in **10 minutes**.
* Token exchange is **not idempotent** — if your callback handler runs twice, the second call returns `invalid_grant`.

**Accepted grant types:** `authorization_code` (with PKCE-S256) and `refresh_token` only. Implicit, resource-owner password, and `client_credentials` are not supported.

## `POST /oauth2/revoke`

Revoke an access or refresh token. Call this when a user disconnects your integration.

```bash theme={null}
curl -X POST https://login.busha.io/oauth2/revoke \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data-urlencode "token=$REFRESH_TOKEN" \
  --data-urlencode "token_type_hint=refresh_token"
```

Returns `200` with an empty body regardless of whether the token was valid — this is intentional to avoid leaking token validity information.

<Note>
  Revoking a refresh token kills future refreshes immediately but does **not** invalidate a currently-issued access token. The access token continues to validate until `exp`.
</Note>

## `GET /.well-known/openid-configuration`

The OIDC discovery document. Contains the canonical `issuer` string and the URLs of all protocol endpoints.

```bash theme={null}
curl https://login.busha.io/.well-known/openid-configuration
```

Read the `issuer` value from this document at startup and pin your JWT validation against it. Do not hardcode the issuer string.

## `GET /.well-known/jwks.json`

The public key set used to verify JWT signatures.

```bash theme={null}
curl https://login.busha.io/.well-known/jwks.json
```

Cache the response for \~5 minutes. Refetch when you encounter a `kid` in a JWT header that is not present in your cache.
