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

# Authentication

> How BlueReacher API keys work: format, where to get one, how to send it, how to keep it safe, and what 401 and 403 responses mean.

Every request to the BlueReacher API is authenticated with an API key sent in an `Authorization` header. There are no OAuth flows, no request signing, and no session cookies. One header, one key.

## API key format

A BlueReacher key is a single string that starts with `bluereacher_`:

```
bluereacher_your_api_key
```

Treat the key as opaque. The characters after the prefix are random and carry no meaning you should parse. Store the whole string, prefix included, and pass it through unchanged.

Two rules for storage:

* Store it as a variable-length string. Key length can change over time, so do not cap the column or config field at a fixed size.
* Match on the `bluereacher_` prefix if you need to detect keys in logs or scrub them from output.

## Where keys come from

Keys are generated per customer in the BlueReacher dashboard under **Settings, API Keys**. Create one, give it a name that says where it runs (`production-crm`, `staging-worker`), and copy it immediately. The full key is shown once at creation. After that the dashboard displays only the prefix and last four characters so you can identify a key without seeing it.

You can hold several active keys at once. Use one key per service or environment. When something goes wrong you can then revoke a single key instead of taking every integration offline.

A key is scoped to your account. It can send only from lines assigned to you, and it can read only your own messages and contacts.

## Sending the key

Send the key as a bearer token on every request.

| Header          | Value                             | Required                     |
| --------------- | --------------------------------- | ---------------------------- |
| `Authorization` | `Bearer bluereacher_your_api_key` | Yes, on every request        |
| `Content-Type`  | `application/json`                | On requests with a JSON body |

The base URL is `https://api.bluereacher.com/functions/v1/`.

### Verify a key

The fastest way to confirm a key works is to list your lines:

```bash theme={null}
curl https://api.bluereacher.com/functions/v1/lines \
  -H "Authorization: Bearer bluereacher_your_api_key"
```

A working key returns your lines:

```json theme={null}
{
  "data": [
    {
      "line_id": "line_8fq2n4kd",
      "from": "+14155550142",
      "channel": "imessage",
      "status": "active",
      "created_at": "2026-07-24T15:04:05Z"
    }
  ]
}
```

### Authenticated request in JavaScript

```javascript theme={null}
const response = await fetch("https://api.bluereacher.com/functions/v1/messages", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.BLUEREACHER_API_KEY}`,
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    from: "+14155550142",
    to: "+14155550188",
    content: "Thanks for the call today. Sending the pricing over now.",
    channel: "imessage",
    external_id: "crm_9f21"
  })
});

if (response.status === 401 || response.status === 403) {
  throw new Error(`Auth failed: ${JSON.stringify(await response.json())}`);
}

const message = await response.json();
console.log(message.status);
```

### Authenticated request in Python

```python theme={null}
import os
import requests

response = requests.post(
    "https://api.bluereacher.com/functions/v1/messages",
    headers={
        "Authorization": f"Bearer {os.environ['BLUEREACHER_API_KEY']}",
        "Content-Type": "application/json",
    },
    json={
        "from": "+14155550142",
        "to": "+14155550188",
        "content": "Thanks for the call today.",
        "channel": "imessage",
    },
    timeout=30,
)

if response.status_code in (401, 403):
    raise RuntimeError(response.json())

print(response.json()["status"])
```

Both examples read the key from an environment variable. Do that in your own code too.

## Keeping keys safe

An API key can send messages from your lines and read your message history. Anyone holding it can do the same. Handle it like a database password.

* **Server side only.** Call the API from your backend, a serverless function, or a workflow tool. Never put a key in browser JavaScript, a mobile app bundle, a Chrome extension, or an HTML page. Anything shipped to a device can be read off that device.
* **Never commit keys to source control.** Use environment variables or a secret manager (AWS Secrets Manager, Doppler, 1Password, your platform's built-in store). Add `.env` to `.gitignore` before the first commit, not after.
* **Keep keys out of logs and tickets.** Scrub any string starting with `bluereacher_` from request logs and error reports. Do not paste a key into a support ticket, a screenshot, or a shared channel. If you need help debugging, send the key prefix and last four characters.
* **Separate keys per environment.** Production, staging, and each third-party tool get their own key. Revoking one then costs you one deploy, not five.
* **Rotate on any suspicion of exposure.** A key in a public repo, a shared screen, a departed contractor's laptop, or a vendor breach all count. You do not need proof of misuse to rotate. Assume exposure means compromise.

### Rotating a key

Rotation takes four steps and no downtime:

1. Create a new key in the dashboard with the same name plus a version suffix.
2. Deploy the new key to your service and restart it.
3. Watch traffic on the new key in the dashboard until you see requests landing.
4. Revoke the old key.

Revocation takes effect on the next request. Any request that arrives with a revoked key gets a 401.

## 401 versus 403

Both mean the request was rejected. They fail for different reasons and need different fixes.

| Status             | Meaning                                           | Typical cause                                                                                                                                             | Fix                                                                           |
| ------------------ | ------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
| `401 Unauthorized` | We could not identify you                         | Missing `Authorization` header, malformed header, typo in the key, revoked or deleted key                                                                 | Check the header format, then check the key value against the dashboard       |
| `403 Forbidden`    | We identified you, and this action is not allowed | Requesting a `line_id` or `contact_id` that belongs to another account, using a channel not enabled on your plan, account suspended for billing or policy | Check the resource ID belongs to you, then check your plan and account status |

Short version: 401 is about the key, 403 is about permissions.

Error responses share one shape:

```json theme={null}
{
  "error": {
    "status": 401,
    "code": "invalid_api_key",
    "message": "The API key provided is not valid or has been revoked."
  }
}
```

Common `code` values on 401 are `missing_authorization_header`, `malformed_authorization_header`, `invalid_api_key`, and `revoked_api_key`. On 403 you will see `line_not_authorized`, `channel_not_enabled`, and `account_suspended`.

Neither status is retryable. Retrying a 401 or 403 with the same key produces the same result, so treat both as fatal in your queue: stop the job, alert someone, and fix the key or the permission. Reserve retries for `429` and `5xx`.

## Debugging checklist

If a request is failing auth, walk this list in order:

1. Is the header spelled `Authorization` with the word `Bearer` and a single space before the key?
2. Did your shell or config file add quotes, a trailing newline, or a line break inside the key?
3. Does the key in your environment match a key listed as active in the dashboard?
4. Did the environment variable actually load? Print its length, never its value.
5. Is the request hitting `https://api.bluereacher.com/functions/v1/`, on HTTPS, with no redirect stripping the header?

If all five check out and you still get a 401, create a fresh key and test it with the `curl` command above.
