# Authentication

The Kordiam API uses OAuth2 client credentials for token issuance. Protected resource
endpoints use JWT bearer authentication. `/api/v2/auth/token` and `/api/v2/auth/refresh` are the
bootstrap endpoints for obtaining and rotating tokens. They are public bootstrap endpoints and do
not require a bearer token themselves.

## API Credentials

Generate, refresh, or delete your API credentials (client ID and client secret) on the
[API credentials page](https://kordiam.app/apiPage.htm) in the Kordiam app. These credentials let
other systems (such as a CMS) access Kordiam.

## Obtaining a Token

<Mermaid chart={`sequenceDiagram
    participant Client
    participant Kordiam as Kordiam API
    Client->>Kordiam: POST /api/v2/auth/token (client credentials)
    Kordiam-->>Client: access_token + refresh_token
    Client->>Kordiam: POST /api/v2/auth/refresh (refresh_token)
    Kordiam-->>Client: new access_token + new refresh_token`} />

<CodeTabs>
  <CodeTabPanel title="JSON" language="bash" code={`curl -X POST https://api.kordiam.app/api/v2/auth/token \\
  -H "Content-Type: application/json" \\
  -d '{
    "client_id": "your-client-id",
    "client_secret": "your-client-secret",
    "grant_type": "client_credentials"
  }'`} />
  <CodeTabPanel title="Form-encoded" language="bash" code={`curl -X POST https://api.kordiam.app/api/v2/auth/token \\
  -H "Content-Type: application/x-www-form-urlencoded" \\
  -d "client_id=your-client-id&client_secret=your-client-secret&grant_type=client_credentials"`} />
</CodeTabs>

### Response

```json title="Response"
{
  "access_token": "eyJhbGciOi...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "api.read api.write",
  "refresh_token": "dGhpcyBpcyBh..."
}
```

:::tip{title="Schedule refresh from expires_in"}

Save the returned `access_token` and use the returned `expires_in` value when scheduling refresh logic instead of assuming a fixed token lifetime.

:::

## Using the Token

Include the token in the `Authorization` header of every API request:

```
Authorization: Bearer eyJhbGciOi...
```

## Refreshing a Token

Use the refresh token to obtain a new access token before the current one expires:

```bash title="Refresh a token"
curl -X POST https://api.kordiam.app/api/v2/auth/refresh \
  -H "Content-Type: application/json" \
  -d '{
    "refresh_token": "dGhpcyBpcyBh..."
  }'
```

### Response

```json title="Response"
{
  "access_token": "eyJhbGciOi...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "api.read api.write",
  "refresh_token": "bmV3IHJlZnJlc2g..."
}
```

:::caution{title="Refresh tokens are single-use"}

Each refresh returns a **new** `refresh_token`, and the token you sent stops working. Always store the `refresh_token` from the latest response and use it for the next refresh — reusing a spent refresh token fails with `401 unauthorized`.

:::

## Token Lifetime

- Use the `expires_in` value from the response instead of hard-coding an access-token lifetime.
- Refresh tokens are single-use: every `/api/v2/auth/refresh` call returns a new `refresh_token` that replaces the previous one.
- Use the [API reference](/api) as the source of truth for the exact request and response
  contract.

## Error Responses

| Status | Error Type      | Description                                                |
|--------|-----------------|------------------------------------------------------------|
| <Badge variant="destructive">401</Badge> | <Badge variant="muted">unauthorized</Badge>  | Invalid client credentials, refresh token, or bearer token |
| <Badge variant="destructive">401</Badge> | <Badge variant="muted">token-revoked</Badge> | Bearer token has been revoked                              |
