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

# Authentication

> One header, two credentials, very different blast radii.

Every request to the Tied API carries the same header: `api_access_token`. What changes is which token goes in it — and that choice determines the entire blast radius of what happens if the credential leaks.

## The header

```bash theme={null}
curl https://app.tied.com/api/v1/accounts/1/conversations \
  -H "api_access_token: YOUR_TOKEN"
```

No `Bearer` prefix, no OAuth handshake, no request signing. One header — for the Application family. The contact-facing families do not use it at all; they are covered further down.

## The two credentials

<CardGroup cols={2}>
  <Card title="userApiKey" icon="user">
    A personal token that inherits the role of the user who generated it. Reaches the Application family.
  </Card>

  <Card title="agentBotApiKey" icon="bot">
    Scoped to seven conversation and message operations. Built for bot integrations.
  </Card>
</CardGroup>

### `userApiKey`

Generated from **Profile settings** in the dashboard. It is not a service credential — it's a personal token, and it can do exactly what that person can do and nothing more. An `agent` token can't reach what an `administrator` token can; the token doesn't have its own permission level separate from the person who owns it.

It authenticates the Application family: conversations, contacts, inboxes, teams, canned responses, custom attributes, reports, webhooks, and every other `/api/v1/` and `/api/v2/` account-scoped endpoint. It also covers everything listed under `agentBotApiKey` below, since a user token is a strict superset of a bot token.

### `agentBotApiKey`

Issued by an administrator when you connect a bot. It's deliberately narrow. The actual enforcement lives in an explicit action allowlist (`AccessTokenAuthHelper::BOT_ACCESSIBLE_ENDPOINTS`), not a broad role check, and it currently covers exactly seven operations:

| Method | Endpoint |
| - | - |
| POST | `/api/v1/accounts/{account_id}/conversations` |
| PATCH | `/api/v1/accounts/{account_id}/conversations/{conversation_id}` |
| POST | `/api/v1/accounts/{account_id}/conversations/{conversation_id}/toggle_status` |
| POST | `/api/v1/accounts/{account_id}/conversations/{conversation_id}/toggle_priority` |
| POST | `/api/v1/accounts/{account_id}/conversations/{conversation_id}/custom_attributes` |
| POST | `/api/v1/accounts/{account_id}/conversations/{conversation_id}/assignments` |
| POST | `/api/v1/accounts/{account_id}/conversations/{conversation_id}/messages` |

<Note>
  The published OpenAPI schema marks six of these as accepting `agentBotApiKey`, but not `custom_attributes` — the seventh is real in the running code (it's on the bot allowlist) even though the spec's `security` block for that operation only lists `userApiKey`. If you're relying on the generated endpoint reference to decide what a bot token can call, don't trust it for this one operation either way — try it, or ask.
</Note>

Anything outside that list — listing contacts, reading reports, managing teams, touching webhooks — is not reachable with a bot token, full stop. If your bot needs more than this, give it a `userApiKey` on a dedicated user instead of trying to widen the bot's reach.

## The other two families authenticate differently

Neither the Client family nor the Widget family sends `api_access_token`. They are contact-facing surfaces, credentialed by identifiers a browser is allowed to hold, and each has its own page.

<CardGroup cols={2}>
  <Card title="Client API authentication" icon="id-card" href="/api-reference/client-api-authentication">
    `inbox_identifier` and `contact_identifier` in the path, plus the optional `identifier_hash` HMAC. For your own server or a custom channel.
  </Card>

  <Card title="Widget API authentication" icon="message-circle" href="/api-reference/widget-api-authentication">
    A public `website_token` in the query string and a signed, expiring session token in a header. For a browser.
  </Card>
</CardGroup>

The practical difference is blast radius. A leaked Client-family URL reaches one contact's own conversations, and a widget session reaches one visitor's. A leaked `userApiKey` reaches everything that user can reach.

## API tokens are user-scoped, not account-scoped

This is the part that surprises people building anything long-lived. A `userApiKey` belongs to the user who generated it — not to the account, and not to "the integration." Two consequences follow directly:

1. **If the user's role changes, the token's access changes with it** — immediately, with no rotation step on your end to lose access.
2. **If the user is removed from the account, or deleted, the token stops working.** There's no ownership transfer to another user.

For anything long-lived — a webhook processor, a nightly sync, a bot's backing credential — don't mint the token from your own personal login. Create a dedicated user with exactly the role the integration needs, and generate the token from that user. It costs one extra seat in exchange for not having the integration break the next time someone's role gets adjusted or they leave.

## Next

<CardGroup cols={2}>
  <Card title="Base URLs and versioning" icon="git-compare" href="/api-reference/versioning">
    Path versioning, and what counts as a breaking change.
  </Card>

  <Card title="Errors" icon="circle-alert" href="/api-reference/errors">
    What a 401 versus a 403 actually means here.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.