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

# Authentication

> API keys, scopes, and the two headers every request needs.

Every request needs **two headers**:

```bash theme={null}
curl https://social.linkiasoft.com/api/v1/connect/channels \
  -H "x-api-key: lk_live_..." \
  -H "X-Tenant-Id: 8f6a2b3c-4d5e-4a7b-9c1e-3f2b1a9c0d4e"
```

| Header        | What it is                                      |
| ------------- | ----------------------------------------------- |
| `x-api-key`   | Your API key. This is what authenticates you.   |
| `X-Tenant-Id` | Your workspace UUID. Required on every request. |

<Note>
  `X-Tenant-Id` does **not** select which workspace you read. Access is always scoped to the
  workspace that owns the API key — sending someone else's workspace id does not grant you
  their data. Both headers are simply required for the request to be accepted.
</Note>

## Creating a key

In Linkiasoft: **Settings → API Keys → Create**.

<Warning>
  The plaintext key is shown **once**, at creation, and cannot be retrieved afterwards. Store
  it in your secret manager immediately. If you lose it, revoke the key and create another.
</Warning>

## Scopes

Keys are scoped. A key that lacks the scope for an endpoint gets `403` with a message
naming what's missing:

```json theme={null}
{
  "statusCode": 403,
  "message": "API key missing required scope(s): conversations:read",
  "error": "Forbidden"
}
```

| Scope                 | Grants                                     |
| --------------------- | ------------------------------------------ |
| `conversations:send`  | Send messages, media, and templates        |
| `conversations:read`  | List conversations and read their messages |
| `conversations:write` | Assign, close, reopen, react, edit, delete |
| `templates:read`      | List and inspect approved templates        |
| `channels:read`       | List connected channels                    |

**Grant the minimum.** An integration that only fires notifications needs
`conversations:send` — and possibly `templates:read` and `channels:read` — and nothing
else. A key scoped that way cannot read your inbox even if it leaks.

<Note>
  A key created with **no** scopes has full access to your workspace, for backwards
  compatibility with keys issued before scopes existed. Always set scopes explicitly on new
  keys.
</Note>

## Errors

| Status | Meaning                                                               |
| ------ | --------------------------------------------------------------------- |
| `400`  | Malformed request. The `message` field says what to fix.              |
| `401`  | Key missing, invalid, revoked, or expired.                            |
| `403`  | Key is valid but lacks the scope — or the workspace hit a plan limit. |
| `404`  | No such record **in your workspace**.                                 |

<Note>
  A record belonging to another workspace returns `404`, not `403`. That's deliberate: a
  `403` would confirm the record exists, which is exactly what someone guessing IDs wants to
  learn. Treat `404` as "not yours or not there" — the API will not distinguish.
</Note>

## Keeping keys safe

* Never put a key in frontend code, a mobile app, or anything a customer can view — it
  carries workspace-wide access for its scopes.
* Use a separate key per integration, so revoking one doesn't take down the others.
* Revoke immediately in **Settings → API Keys** if a key is exposed; it stops working at once.
