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

# Authentication

> Authenticate every API request with a Bearer token. Generate keys per scope, rotate them freely, and handle the standard 401/403 error responses.

All API requests require a **Bearer token** in the `Authorization` header.

```bash theme={"dark"}
curl https://api.open.cx/contacts \
  -H "Authorization: Bearer YOUR_API_KEY"
```

***

## Generate an API key

1. Go to [Settings → Access](https://platform.open.cx/settings/access) in the OpenCX dashboard.
2. Click **Create API Key**.
3. Give it a name (e.g. "Production", "Staging").
4. Choose access level:
   * **Full access** — the key can call every endpoint with no restrictions.
   * **Custom scopes** — select only the resources and actions (read / write) this key needs.
5. Copy the key.

<Note>
  Existing keys created before scopes were introduced remain full-access.
</Note>

<Warning>
  The full key is only shown once. Store it securely — if you lose it, generate
  a new one.
</Warning>

***

## Using the key

Pass the key as a Bearer token in the `Authorization` header on every request:

```bash theme={"dark"}
Authorization: Bearer ocx_live_abc123...
```

<CodeGroup>
  ```bash cURL theme={"dark"}
  curl -X GET https://api.open.cx/contacts \
    -H "Authorization: Bearer YOUR_API_KEY"
  ```

  ```javascript Node.js theme={"dark"}
  const response = await fetch("https://api.open.cx/contacts", {
    headers: {
      Authorization: "Bearer YOUR_API_KEY",
    },
  });
  ```

  ```python Python theme={"dark"}
  import requests

  response = requests.get(
      'https://api.open.cx/contacts',
      headers={'Authorization': 'Bearer YOUR_API_KEY'},
  )
  ```
</CodeGroup>

***

## Scopes

Scopes restrict an API key to specific resources and actions. A **full-access** key bypasses all scope checks, while a **scoped** key can only call the endpoints it was granted.

Each scope follows the pattern `resource:action`, where action is `read` (GET / list) or `write` (create / update / delete).

### Available scopes

| Resource          | Read                     | Write                   |
| ----------------- | ------------------------ | ----------------------- |
| Actions           | `actions:read`           | `actions:write`         |
| Audit logs        | `audit-logs:read`        | —                       |
| Autopilot         | `autopilot:read`         | `autopilot:write`       |
| Blocklist         | `blocklist:read`         | `blocklist:write`       |
| Chat sessions     | `chat-sessions:read`     | `chat-sessions:write`   |
| Contact segments  | `segments:read`          | `segments:write`        |
| Contacts          | `contacts:read`          | `contacts:write`        |
| Crawl             | `crawl:read`             | `crawl:write`           |
| CSAT              | `csat:read`              | `csat:write`            |
| Email             | `email:read`             | `email:write`           |
| Handoff analytics | `handoff-analytics:read` | —                       |
| Help center       | `help-center:read`       | `help-center:write`     |
| Impact report     | `impact-report:read`     | —                       |
| Insights          | `insights:read`          | `insights:write`        |
| Media             | —                        | `media:write`           |
| Office hours      | `office-hours:read`      | `office-hours:write`    |
| Organization      | `org:read`               | `org:write`             |
| Phone             | `phone:read`             | `phone:write`           |
| Redaction         | `redaction:read`         | `redaction:write`       |
| Salesforce MIAW   | —                        | `salesforce-miaw:write` |
| Sequences         | `sequences:read`         | `sequences:write`       |
| SIP               | `sip:read`               | `sip:write`             |
| SLA analytics     | `sla-analytics:read`     | —                       |
| SLA policies      | `sla:read`               | `sla:write`             |
| Tags              | `tags:read`              | `tags:write`            |
| Teams             | `teams:read`             | `teams:write`           |
| Training          | `training:read`          | `training:write`        |
| Users             | `users:read`             | `users:write`           |
| WhatsApp          | `whatsapp:read`          | `whatsapp:write`        |
| Widget            | `widget:read`            | `widget:write`          |
| Workflows         | `workflows:read`         | `workflows:write`       |

<Note>
  Read-only resources (Audit logs, Handoff analytics, Impact report, SLA
  analytics) have no write scope. Media is write-only — there is no
  `media:read`. Salesforce MIAW only exposes a write scope for sending webhook
  events.
</Note>

<Note>
  Workflows also expose `workflows:trigger`, granted independently of read /
  write. It only authorizes calls to a workflow webhook trigger URL whose
  trigger configuration has **Access Control** set to *Private (Authentication
  Required)*. Public webhook triggers do not require any scope.
</Note>

***

## Error responses

| Status             | Meaning                                   |
| ------------------ | ----------------------------------------- |
| `401 Unauthorized` | Missing or invalid API key                |
| `403 Forbidden`    | Key is valid but lacks the required scope |

**401 — invalid or missing key**

```json theme={"dark"}
{
  "statusCode": 401,
  "message": "Unauthorized",
  "error": "Unauthorized"
}
```

**403 — missing scope**

```json theme={"dark"}
{
  "statusCode": 403,
  "message": "This API key does not have the required scope: \"tags:read\"."
}
```

***

## Best practices

* **Never commit keys to source control.** Use environment variables or a secrets manager.
* **Rotate keys periodically.** You can create multiple keys and revoke old ones from the dashboard.
* **Use separate keys for each environment** (production, staging, development) so revoking one doesn't break the others.
* **Use scoped keys in production.** Grant only the permissions each integration needs (principle of least privilege).
* **Prefer separate keys per integration** so revoking one doesn't break others, and each gets only the scopes it needs.
