Core concepts
Authentication
Every request carries a single Korlogic key. There are no client ids, no OAuth flow and no session, so the key is the whole credential. Treat it like a password.
Sending your key (permalink)
Send it as a bearer token. This is what every OpenAI-compatible SDK does by default, so setting the SDK’s API-key option is usually the only change you need.
curl https://api.korlogic.com/v1/chat/completions \
-H "Authorization: Bearer kl_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-5",
"messages": [{ "role": "user", "content": "ping" }]
}'For compatibility with Anthropic tooling, x-api-key is accepted as well. When both headers arrive, the bearer token in Authorization is the one used, and x-api-key is only read if Authorization is absent or is not a Bearer value.
# Anthropic SDKs send this header for you.
curl https://api.korlogic.com/v1/messages \
-H "x-api-key: kl_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "anthropic-version: 2023-06-01" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-5",
"max_tokens": 64,
"messages": [{ "role": "user", "content": "ping" }]
}'Key format (permalink)
A key is the prefix kl_live_ followed by 32 hexadecimal characters, 40 characters in total, with no dashes and no padding. Copy it whole: a truncated or line-wrapped key hashes to something else and comes back as invalid_api_key.
kl_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxEvery example on this site uses this placeholder.
The prefix is identical on both billing surfaces, so a key never tells you which base URL it belongs to. See Base URLs for how to find out.
What a key controls (permalink)
Keys are issued with settings chosen by whoever created them. You cannot change these yourself, so ask them if one of these limits is in your way.
| Setting | Effect |
|---|---|
| Billing mode | Credit or token. Fixes which base URL the key answers on, and how its consumption is counted. |
| Model allowlist | An optional list of model ids. When it is set, any other model in the request body is refused with 403 model_not_allowed. When it is empty, every enabled model is available. |
| Rate limit | A ceiling on requests per minute, counted in a fixed 60-second window. Going over it returns 429 rate_limit_exceeded with a retry-after header, before the request is read or forwarded, so a throttled call is never billed. |
| Expiry | An optional date. Once it passes, every request fails with 403 key_expired, including requests that would otherwise have credit left. |
| Status | Active or suspended. A suspended key fails with 403 key_suspended and can be resumed later without being reissued. |
When auth fails (permalink)
Failures use the OpenAI error envelope, so SDK error handling surfaces them normally. The machine-readable value is error.code: messages may be reworded, codes will not.
{
"error": {
"message": "Invalid API key.",
"type": "invalid_request_error",
"code": "invalid_api_key",
"param": null
}
}| Status | Code | What went wrong |
|---|---|---|
| 401 | missing_api_key | The request carried no credential at all. Send Authorization: Bearer with your key, or the x-api-key header. |
| 401 | invalid_api_key | Unknown key, or a valid key sent to the other billing surface. Check the key was copied whole, then confirm you are on the right base URL. |
| 403 | key_suspended | The key exists but has been paused by whoever issued it. Ask them to resume it. The key itself stays valid and does not need replacing. |
| 403 | key_expired | The key is past its expiry date. Request an extension or a new key. It will not recover on its own. |
| 403 | model_not_allowed | The key is valid but is not scoped to the model in the request body. Use a model on your allowlist, or ask for the allowlist to be widened. |
None of these are worth retrying: the answer will not change without an operator action. The full list, including billing and upstream failures, is on Errors.
Calling from a browser (permalink)
The gateway answers cross-origin requests. Preflights are handled, access-control-allow-origin is *, and x-request-id is exposed to scripts so you can quote it in a bug report. These headers are accepted on a preflight:
authorizationcontent-typex-api-keyanthropic-versionanthropic-betaopenai-betaopenai-organizationx-request-id
Handling keys safely (permalink)
- Keep the key server-side. Anything shipped to a browser or a mobile binary is public, and a leaked key spends your balance.
- Load it from the environment. Most SDKs read a key from an environment variable without extra code, which keeps it out of your repository.
- Nobody can read it back to you. Keys are stored only as a hash, so a lost key is replaced, never recovered.
- Suspend first, ask questions later. If a key may have leaked, ask for it to be suspended and a replacement issued. Suspension takes effect on the next request.
Rotating without downtime
Ask for the new key first, deploy it, confirm traffic is flowing on the usage checker, then have the old key suspended. Both keys work during the overlap.