# Authentication

Every request to the LineHQ Public API carries an API key. This page covers getting one, what each permission allows, the limits, the errors you can get back, and how ids and lists work.

Base URL: `https://app.linehq.co.uk/api/v1`

## API keys

An API key belongs to one LineHQ account and acts as the person who created it. The account owner can create keys, and so can a member the owner has allowed to manage API keys. Keys are created in the LineHQ portal; the key itself is shown once, so store it somewhere safe. Revoke a key in the portal and it stops working straight away.

Send the key in the `Authorization` header of every request:

```bash
curl "https://app.linehq.co.uk/api/v1/me" -H "Authorization: Bearer $LINEHQ_API_KEY" -H "Accept: application/json"
```

`GET /me` answers with the account, the user the key acts as, and the permissions it holds, so it's the quickest way to check a key works.

## Permissions

Each key holds the permissions chosen when it was created. Every key can `read`. Give a key only what your integration needs: a key handed to another system can do anything its permissions allow.

| Permission | Allows |
| --- | --- |
| `read` | Every read in the API reference except contacts and webhooks. Every key has it. |
| `contacts:read` | Reading the contact directory. |
| `contacts:write` | Creating, updating and deleting contacts. Includes `contacts:read`. |
| `inbox:write` | Marking notifications read and ticking off to-dos. |
| `transcribe` | Starting a paid transcription of a call. Only an owner, or a member the owner allows to start transcriptions, can create a key with it. |
| `auto-transcribe` | While a key with this permission exists, **every new recorded call on the account is transcribed automatically, and each one is charged** like a transcription you start yourself: from the plan's allowance, then prepaid bundles, then the prepaid balance. Numbers in privacy mode are never transcribed. Includes `transcribe`. Revoke the key to stop it. |
| `webhooks:manage` | Listing, adding, changing and removing webhook endpoints. Owners only. |

Some changes can't be made with an API key at all, such as routing, caller ID, texts and devices: those belong to the LineHQ app and the portal.

## Account roles

Each operation in the reference states the account role it needs. **Owner or member** means any user of the account; **owner only** means the key must belong to an account owner, whatever its permissions. A key is refused once its user leaves the account.

## Rate limits

Each user can make 60 requests a minute, shared by all their API keys and by their own LineHQ app and AI assistant sessions.
Some operations are limited further:

| Operation | Limit |
| --- | --- |
| Add a webhook endpoint (`POST /webhooks`) | 20 a minute |
| Send a test event (`POST /webhooks/{endpoint}/test`) | 10 a minute |
| Resend a delivery (`POST /webhooks/{endpoint}/deliveries/{eventId}/resend`) | 30 a minute |

Over a limit, you get `429` with a `Retry-After` header: wait that many seconds before trying again.

## Errors

Errors answer with JSON carrying a plain-English `message`.

| Status | Means |
| --- | --- |
| `401` | The key is missing, wrong or revoked. |
| `402` | Starting a transcription would cost more than the prepaid balance holds, or pass the account's daily spending limit. |
| `403` | The key lacks a permission the request needs, its user's role doesn't allow it, or the account's plan doesn't include the feature. |
| `404` | No such record on this account, or the path doesn't exist. |
| `422` | Something in the request is missing or invalid. `errors` names each field. |
| `429` | Too many requests; see rate limits. |

A validation error looks like this:

```json
{
  "message": "The display name field is required.",
  "errors": {
    "display_name": ["The display name field is required."]
  }
}
```

### When the account doesn't have Integrations

Calls, messages, contacts and adding webhooks need the Integrations add-on; the operations that do are marked in the [reference](https://linehq.co.uk/developers/reference). An API key on an account without it is refused with `403` and this body (LineHQ's own apps are never refused):

```json
{
  "reason": "addon_required",
  "capability": "integrations",
  "message": "This needs the Integrations add-on. The account owner can add it from the plans page.",
  "pricing_url": "https://linehq.co.uk/pricing"
}
```

`reason` is `addon_required` (the plan can add it), `plan_required` (a different plan is needed), `account_inactive` (the account is paused) or `platform_disabled` (LineHQ has it switched off). Act on `reason` and `capability`; `message` is for people and its wording may change.
## Ids

Records are addressed by opaque ids: most by their `public_id`, such as `8kQ2mVn4Lr7Tz1Xc9Bw3Hd6Fs0Jy5Pa2`; notifications by their `id` and webhook deliveries by their `event_id`, both UUIDs. Store ids as text and don't rely on their length or characters. Some responses also carry a numeric `id` that isn't documented: don't store or send it, as it may disappear.

## Lists and pagination

Long lists come a page at a time. Ask for a page with `page` (from 1) and its size with `per_page` (usually up to 100). Each page carries:

| Field | Means |
| --- | --- |
| `data` | The records on this page. |
| `current_page`, `last_page` | Where you are, and how many pages there are. |
| `per_page`, `total` | The page size, and how many records there are in all. |
| `next_page_url`, `prev_page_url` | The next and previous pages; `null` at either end. |

Follow `next_page_url` until it's `null`. Numbers and blocked callers come whole in `data`, and receipts are the latest 48.
