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

# Authentication

> Obtain an access token and call the Korint API

Every request to the Korint API carries an access token identifying your client. Requests that act on
a tenant's data — nearly all of them — also carry a `tenant` header naming that tenant. A few
endpoints are tenant-agnostic and take no `tenant` header; the reference shows which by not listing
it under Headers.

## Get your credentials

Korint provisions a machine-to-machine client for your tenant and gives you:

<ParamField path="client_id" type="string" required>
  Public identifier for your client.
</ParamField>

<ParamField path="client_secret" type="string" required>
  Secret for your client. Store it as you would a database password — never in frontend code or a
  public repository.
</ParamField>

<ParamField path="tenant" type="string" required>
  Your tenant identifier. It also appears in your authorization server URL.
</ParamField>

Ask your Korint contact if you don't have these yet. Credentials are issued per environment, so your
sandbox client is not your production client.

## Request an access token

Korint uses the OAuth 2.0 **client credentials** grant. Each tenant has its own authorization server:
replace `tenant` in the host below with your tenant identifier.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST 'https://korint-tenant.auth.eu-west-3.amazoncognito.com/oauth2/token' \
    --fail-with-body \
    --user '<client_id>:<client_secret>' \
    --header 'Content-Type: application/x-www-form-urlencoded' \
    --data-urlencode 'grant_type=client_credentials' \
    --data-urlencode 'scope=korint.io/policies korint.io/customers'
  ```

  ```javascript Node.js theme={null}
  const credentials = Buffer.from(`${clientId}:${clientSecret}`).toString('base64');

  const response = await fetch('https://korint-tenant.auth.eu-west-3.amazoncognito.com/oauth2/token', {
    method: 'POST',
    headers: {
      Authorization: `Basic ${credentials}`,
      'Content-Type': 'application/x-www-form-urlencoded',
    },
    body: new URLSearchParams({
      grant_type: 'client_credentials',
      scope: 'korint.io/policies korint.io/customers',
    }),
  });

  if (!response.ok) {
    throw new Error(`Token request failed (${response.status}): ${await response.text()}`);
  }

  const { access_token, expires_in } = await response.json();
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      "https://korint-tenant.auth.eu-west-3.amazoncognito.com/oauth2/token",
      auth=(client_id, client_secret),
      data={
          "grant_type": "client_credentials",
          "scope": "korint.io/policies korint.io/customers",
      },
  )
  response.raise_for_status()
  access_token = response.json()["access_token"]
  ```
</CodeGroup>

<ResponseExample>
  ```json Success theme={null}
  {
    "access_token": "eyJraWQiOiJ…",
    "token_type": "Bearer",
    "expires_in": 3600
  }
  ```
</ResponseExample>

<Tip>
  Cache the token and reuse it until it expires — read `expires_in` rather than assuming a lifetime, and
  request a new token shortly before it runs out. Requesting one per API call will get you rate limited
  by the authorization server.
</Tip>

## Call the API

Send the token as a bearer token and name your tenant:

```bash theme={null}
curl -X GET 'https://api.korint.io/policies/{policyId}' \
  --header 'Authorization: Bearer <access_token>' \
  --header 'tenant: <your-tenant>'
```

<Warning>
  The `tenant` header is required on every endpoint that operates on tenant data — without it the
  request fails with `MISSING_TENANT`, even when your token is valid. A few endpoints are
  tenant-agnostic and take no `tenant` header: the reference marks them by not listing it.
</Warning>

## Scopes

Ask only for the scopes your integration needs. A token is granted the intersection of what you request
and what your client is allowed.

| Scope                      | Grants access to                             |
| -------------------------- | -------------------------------------------- |
| `korint.io/account`        | Account information for the caller           |
| `korint.io/customers`      | Customer endpoints                           |
| `korint.io/policies`       | Policy endpoints                             |
| `korint.io/assets`         | Asset endpoints                              |
| `korint.io/branches`       | Branch endpoints                             |
| `korint.io/quotes`         | Quote endpoints                              |
| `korint.io/documents`      | Document endpoints                           |
| `korint.io/signature`      | Signature endpoints                          |
| `korint.io/billing`        | Billing endpoints                            |
| `korint.io/payments`       | Payment endpoints                            |
| `korint.io/claims`         | Claim endpoints                              |
| `korint.io/derogations`    | Derogation endpoints                         |
| `korint.io/comments`       | Comment endpoints                            |
| `korint.io/internal-notes` | Internal note endpoints                      |
| `korint.io/invitations`    | Invitation and anonymous-session endpoints   |
| `korint.io/permissions`    | Permission management endpoints              |
| `korint.io/broker`         | Broker endpoints                             |
| `korint.io/reportings`     | Reporting endpoints                          |
| `korint.io/analytics`      | Analytics endpoints                          |
| `korint.io/external`       | External lookups (vehicle, company, address) |

## How a request is authorized

A valid token is necessary but not sufficient. Every request passes two checks in order:

<Steps>
  <Step title="Scope check">
    Your token must carry the scope covering the area you are calling. Missing scope fails before any
    business logic runs.
  </Step>

  <Step title="Permission check">
    Korint then evaluates permissions on the specific record — the policy, customer or firm you named —
    for the identity behind the token. See [Permissions](/concepts/security/permissions).
  </Step>
</Steps>

Both must pass. A 403 therefore means one of two different things: your token lacks the scope, or your
identity may not act on that particular record.

## Troubleshooting

<AccordionGroup>
  <Accordion title="401 on every request">
    The token is missing, malformed, expired, or was issued by a different tenant's authorization server
    than the `tenant` header names. Confirm the host in your token URL matches the tenant you are calling.
  </Accordion>

  <Accordion title="MISSING_TENANT">
    The `tenant` header is absent. Every endpoint that touches tenant data requires it, reads
    included; check the endpoint's Headers section if you are unsure.
  </Accordion>

  <Accordion title="403 although the token is valid">
    Either the token lacks the scope for that area, or the identity behind it has no permission on the
    record you named. Compare the scopes you requested against the table above first — that is the
    cheaper of the two to rule out.
  </Accordion>

  <Accordion title="invalid_scope from the token endpoint">
    You requested a scope your client is not allowed. Request only the scopes your integration needs, or
    ask your Korint contact to widen the client.
  </Accordion>
</AccordionGroup>
