Skip to main content
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:
string
required
Public identifier for your client.
string
required
Secret for your client. Store it as you would a database password — never in frontend code or a public repository.
string
required
Your 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.
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:

Environments

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. 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:
Add korint.io/billing to read invoices or balances, and korint.io/invitations to run anonymous quoting sessions.

Which token acts on what

There is more than one kind of token, and they are not interchangeable. Choosing the wrong one produces errors that look like permission bugs.
your integration
What the flow above issues. Carries a scope claim and identifies your client, not a person. Use it to read configuration and to start anonymous sessions.
a person signed in
Issued when a human signs in. Identifies a user, so record-level permissions resolve against them.Signing in returns both an id token and an access token, and the API accepts only the access token — send AccessToken, not IdToken. The id token is the intuitive choice, since it is the one carrying the email and profile, and it is rejected with 401 INVALID_CREDENTIALS, which reads as though the credentials were wrong rather than the token being the wrong kind.A user access token carries no korint.io/* scope; its authority comes from the user’s permissions, not from scopes. The scope table above applies to client-credentials tokens.
a visitor in a public funnel
Returned by POST /anonymous-session as accessToken. Not a Cognito token and it carries no scope claim at all — its claims are { tenantId, sub: <sessionId>, type: "SESSION", iat, exp }, and it lasts 24 hours. Only the permission check applies to it.
Record-level permissions resolve against a user. A client-credentials token that has not been provisioned with administrative access can therefore be rejected on policy operations with
even though the token, tenant header and scopes are all valid. Where you see this, the integration pattern is to mint an anonymous session for the visitor and act under that, or to act as a signed-in user — not to widen scopes, which will not help.

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 /config and GET /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 localhost dev 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.

What an anonymous session may do

Measured against a session token:
That last row breaks the obvious way to ask “is this visitor signed in?”. @me does not fail for an anonymous session — it answers 200 with a syntactically valid but semantically empty identity: no brokerageFirmId, no brokerRole, email: "". Branch on a field in the body, not on the status code, or you will render a signed-in interface for a visitor who has no firm and no way forward.The status codes around it are not uniform, so do not branch on the status alone. A malformed, expired or wrong-tenant token is always 401 INVALID_CREDENTIALS. A request carrying no token at all is 401 MUST_BE_AUTHENTICATED on @me and the handful of other endpoints that skip record-level permission checks, but 403 FORBIDDEN on the rest of the API, because there the permission check refuses it before authentication is ever considered. So 403 does not distinguish “not signed in” from “signed in but not allowed here”, while the two 401 codes do tell you whether a token was rejected or never sent.
So a public funnel can take a visitor all the way to a priced, payable contract, but confirmation and company search need a different actor. Plan for a hand-off — see Anonymous quoting.

How a request is authorized

A valid token is necessary but not sufficient. Korint evaluates permissions on the record you named — the policy, customer or firm — for the identity behind the token. See Permissions. Some endpoints also require your token to carry the scope covering the area you are calling. That check runs after the permission check, and endpoints declaring no scope never run it. A 403 therefore means one of two things: your identity may not act on that record, or your token lacks a scope the endpoint requires.

Troubleshooting

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.
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.
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.
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.