# OAuth 2.1

Use OAuth when your product connects to your customers' KLCT accounts. It is the standard authorization code flow with PKCE. The customer signs in to KLCT, picks the account to connect and ticks the scopes they allow. You never see their password.

## Get a client

Clients are pre-registered; there is no open self-registration. Ask KLCT to register your product with:

- a name shown on the consent screen,
- one or more redirect URIs (HTTPS, or a loopback address for local testing; exact match, or a wildcard host such as `https://*.example.com/callback`),
- the scopes you may ever request.

You receive a `client_id` and a `client_secret`.

## 1. Send the customer to KLCT

```
GET https://app.klctai.com/oauth/platform/authorize
  ?response_type=code
  &client_id=YOUR_CLIENT_ID
  &redirect_uri=https://yourapp.example/klct/callback
  &scope=contacts:read%20tickets:read%20tickets:write
  &state=RANDOM_STATE
  &code_challenge=BASE64URL_SHA256_OF_VERIFIER
  &code_challenge_method=S256
```

PKCE with `S256` is required. Generate a random `code_verifier` (43 to 128 URL-safe characters) per attempt, keep it server-side, and send its SHA-256 hash, base64url-encoded, as `code_challenge`. Always check that the `state` you get back matches the one you sent.

KLCT redirects to your `redirect_uri` with `?code=...&state=...`, or `?error=access_denied&state=...` if the customer declined. The code is valid for 10 minutes and works once.

## 2. Exchange the code

```bash
curl -X POST "https://app.klctai.com/oauth/platform/token" \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d grant_type=authorization_code \
  -d code=THE_CODE \
  -d code_verifier=YOUR_VERIFIER \
  -d redirect_uri=https://yourapp.example/klct/callback
```

You may send `client_id` and `client_secret` in the body instead of HTTP Basic.

```json
{
  "access_token": "31|xYz...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "def50200...",
  "scope": "identity contacts:read tickets:read tickets:write"
}
```

Check `scope`: it lists what the customer actually allowed, which can be less than you asked for. Then call `GET /platform/account` to learn which account you are connected to.

## 3. Refresh

Access tokens last one hour. Refresh tokens last 30 days and **rotate on every use**: each refresh response contains a new refresh token and the old one stops working. Store the new one every time.

```bash
curl -X POST "https://app.klctai.com/oauth/platform/token" \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d grant_type=refresh_token \
  -d refresh_token=THE_REFRESH_TOKEN
```

A refresh cannot add scopes. To get more, send the customer through step 1 again with the larger `scope`.

## Revoke

```bash
curl -X POST "https://app.klctai.com/oauth/platform/revoke" \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d token=THE_ACCESS_OR_REFRESH_TOKEN
```

Customers can also disconnect your app from Integrations in KLCT. After that every request with that connection's tokens answers `401`.

## Errors

Token endpoint errors follow RFC 6749: `invalid_request`, `invalid_client` (401), `invalid_grant` (expired or reused code, PKCE mismatch, revoked connection), `invalid_scope`, `unsupported_grant_type`. Each comes with an `error_description`.
