# Authentication and API keys

Create the right key scope, send it correctly, rotate it without downtime, and read each authentication failure.

- **Audience:** Backend engineers and operators managing Onlo API credentials
- **Intent:** Build with Onlo
- **Active work:** 10 minutes

Canonical page: https://onlo.ai/docs/developers/conversations-api/authentication

## Create an API key

Dashboard → Integrations → API

## Before you start

- Owner or admin access to your Onlo workspace
- A plan that includes API access

## Required headers

| Header | When | Value |
| --- | --- | --- |
| `Authorization` | Always | `Bearer <your API key>` |
| `Content-Type` | On POST and PUT | `application/json` (a charset parameter is fine) |
| `Accept` | Recommended | `application/json` or `*/*` |

## Choose a scope

> **Keys beginning olk_test_ are retired:** Read-only keys were previously issued with an olk_test_ prefix. Those keys read REAL organization data — they were never sandbox keys, and the name was misleading. The conversation endpoints now reject that prefix with 403 legacy_key_not_accepted. /ping still accepts it so you can identify which of your keys are affected. Create a replacement olk_read_ key and revoke the old one.

Scope is enforced before Onlo parses your request body, so a read-only key attempting a write is rejected without revealing anything about the payload it would have validated.

| Scope | Key prefix | Can do |
| --- | --- | --- |
| Full access | `olk_live_` | Ping, create, fetch, update, search |
| Read-only | `olk_read_` | Ping, fetch, search |

## Create and store a key

1. **Create the key.** In Onlo, go to Dashboard → Integrations → API and create a key with the scope you need.
   - **Expected result:** The raw key is shown exactly once. Onlo stores only a hash of it and can never show it again.
2. **Store it in your secret manager.** Save it as `ONLO_API_TOKEN` and set `ONLO_API_BASE_URL` to `https://onlo.ai/api/v1`. Deploy it to your backend only.
   - **Expected result:** Your application reads the key from the environment, and no key value appears in your source tree.
3. **Verify it end to end.** Call `/ping` with the key from the environment where your integration will actually run.
   ```shell
   export ONLO_API_BASE_URL="https://onlo.ai/api/v1"
   export ONLO_API_TOKEN="olk_live_replace_with_your_key"
   
   curl --request GET "$ONLO_API_BASE_URL/ping" \
     --header "Authorization: Bearer $ONLO_API_TOKEN" \
     --header "Accept: application/json"
   ```
   - **Expected result:** A `200` response echoing your `organization_id` and the `scope` you expect.

### Expected result

Call `GET /ping` from your deployed backend, not from your laptop.

**Success:** A `200` whose `scope` matches the key you created, and whose `organization_id` is your organization.

**If you do not see this:**

- A `401 unauthorized` means the key is missing, malformed, or revoked. Confirm the header is exactly `Bearer <key>` with a single space.
- A `403 https_required` means the request arrived over plain HTTP while your organization requires HTTPS.
- A `403 ip_not_allowed` means your organization has an IP allowlist and your server’s egress address is not on it. Add the address your platform actually egresses from, not your office IP.
- A `403 api_plan_restricted` means the plan does not include API access. This is checked on every request, so it also appears if a subscription lapses.

## Rotate without downtime

> **Rate limits are shared across keys:** Your organization has one rate-limit bucket that every active key draws from. Running two keys during a rotation does not double your throughput.

Both keys work at once, so rotate by adding before removing. Never revoke first — that guarantees an outage for however long your deployment takes.

1. **Create a second key.** Create the replacement while the current key stays active.
   - **Expected result:** Both keys authenticate.
2. **Deploy the new key.** Update the secret in your backend and roll it out.
   - **Expected result:** Traffic moves only when your deployment completes — Onlo does nothing at this step.
3. **Verify the new key.** Call `/ping` with the new key from the deployed environment.
   - **Expected result:** A `200` response.
4. **Revoke the old key.** Revoke the previous key in the dashboard.
   - **Expected result:** Any request still using the old key returns `401 unauthorized`.

### Expected result

After revoking, watch your error rate for one full traffic cycle.

**Success:** No `401` responses — every caller is on the new key.

**If you do not see this:**

- If `401`s appear, a caller you did not account for still holds the old key. Create a new key, deploy it to that caller, and treat the old key as compromised if you cannot identify the source.

## Rate limits

> **On a 429, wait for Retry-After:** Retrying immediately on a 429 spends capacity you do not have and extends the throttle. Wait at least the Retry-After value, then use exponential backoff with jitter.

One bucket per organization, shared by every active key and every one of your callers. The default is 200 requests per second with a burst capacity of 200, refilled continuously. Your organization may be configured lower.

Every response carries the current state in both the `X-RateLimit-*` headers (matching Intercom) and the `RateLimit-*` headers (the IETF draft spelling). They report the same numbers, so read whichever your HTTP client already parses.

| Header | Meaning |
| --- | --- |
| `X-RateLimit-Limit` / `RateLimit-Limit` | Requests-per-second capacity for your organization |
| `X-RateLimit-Remaining` / `RateLimit-Remaining` | Tokens left after this request |
| `X-RateLimit-Reset` | Epoch second at which capacity is available |
| `RateLimit-Reset` | Seconds until capacity is available |
| `Retry-After` | Seconds to wait, sent only on a `429` |

## Next

- [Conversations API reference](https://onlo.ai/docs/developers/conversations-api/reference): Every endpoint, request field, response field, limit, and error code for the Conversations REST API.

## Related pages

- [Conversations API](https://onlo.ai/docs/developers/conversations-api): Create a conversation for one of your users from your own backend, receive its durable Onlo id, set conversation attributes, and search that user’s conversations over REST.
- [Conversations API reference](https://onlo.ai/docs/developers/conversations-api/reference): Every endpoint, request field, response field, limit, and error code for the Conversations REST API.
