Authentication
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:
client_idstringrequiredPublic identifier for your client.
client_secretstringrequiredSecret for your client. Store it as you would a database password — never in frontend code or a public repository.
tenantstringrequiredYour tenant identifier. It also appears in your authorization server URL.
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.
- cURL
- Node.js
- Python
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'
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();
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"]
{
"access_token": "eyJraWQiOiJ...",
"token_type": "Bearer",
"expires_in": 3600
}
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.
Call the API
Send the token as a bearer token and name your tenant:
curl -X GET 'https://api.sandbox.korint.io/policies/{policyId}' \
--header 'Authorization: Bearer <access_token>' \
--header 'tenant: <your-tenant>'
Environments
| Host | Use |
|---|---|
https://api.sandbox.korint.io | Build and test your integration. Credentials are issued per environment, so your sandbox client is not your production client. |
https://api.korint.io | Production. |
Examples throughout this guide use the sandbox host. Korint also runs internal environments that are not part of the published surface; if you have been pointed at one, treat its host as given to you rather than inferred.
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.
Scopes
Ask only for the scopes your integration needs. Every scope you request must be one your client was
granted when it was provisioned: the authorization server rejects the whole token request with
invalid_scope if any requested scope is not allowed, rather than issuing a token with the rest.
| 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/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/metrics | Metrics endpoints |
korint.io/external | External lookups (vehicle, company, address) |
Those are the scopes an integration client is normally granted. Some clients are provisioned with
korint.io/admin in addition, which unlocks administrative routes; check the scope claim of a token
you have been issued to see what yours actually carries. A few scopes exist only for Korint's own
applications and are never granted to integration clients.
A new-business integration typically needs:
korint.io/customers korint.io/policies korint.io/assets korint.io/quotes
korint.io/branches korint.io/documents korint.io/signature korint.io/payments
Add korint.io/billing to read invoices or balances, and korint.io/invitations to run anonymous
quoting sessions.
The scope table above applies to client-credentials tokens. A user access token carries no
korint.io/* scope at all — its authority comes from the user's permissions — and neither does an
anonymous session token. Which token identifies whom, and what each may act on, is covered in
Authentication.
What needs no credentials at all
A few routes take no Authorization header, and they are exactly the ones a public funnel needs before
it has an actor:
POST /anonymous-session— start a session. Rate limited to 100 per hour.GET /configandGET /config/{productId}— read the tenant and product configuration.
The tenant header is still required. Everything else needs one of the three tokens above.
Calling the API from a browser
Whether your funnel needs a backend comes down to CORS, and the answer is not "any origin works".
The API allows a configured list of origins per environment. That list holds Korint's own
https://*.korint.io subdomains, an entry per partner front end, and — in the sandbox and development
environments only — localhost. So:
- Developing locally against sandbox: your
localhostdev server is already allowed. This is why a browser-only funnel appears to work from the start. - Deploying your own front end: your production origin is not allowed until it is added. Ask Korint to allowlist it, exactly, before you ship — a browser request from an unlisted origin is refused by the browser regardless of whether your token is valid.
Allowed requests may send authorization, tenant and content-type.
If you would rather not depend on an allowlist, put a thin server of your own in front: it holds no secrets for the unauthenticated routes above, and it removes the origin question entirely.
Troubleshooting
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.
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.
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. Which check refused you, and in what order they run, is in How a request is checked.
404 USER_NOT_FOUND on a policy operation
Your client-credentials token is valid, but record-level permissions resolve against a user and your client is not one. Widening scopes does not help — act as a signed-in user, or mint an anonymous session for the visitor. See The three kinds of token.
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.