> ## 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.

# Manage lines

> List every dedicated iMessage line on your workspace and read one line's status, daily capacity, and health with GET /list-lines and GET /get-line.

Two read endpoints cover lines: `GET /list-lines` returns every line on your workspace, and `GET /get-line` returns one line with extra detail. Use them to pick a line before you send, to watch daily capacity, and to catch a line that needs attention.

Lines are provisioned, paused, and resumed from the BlueReacher dashboard. The API is read only for lines.

## What a line is

A line is a dedicated iMessage number, managed for you. One line is one phone number. It belongs to your workspace and nobody else sends from it.

BlueReacher handles setup and upkeep. You get a number that sends blue bubbles, with no A2P registration required and nothing for you to configure before your first campaign.

Every line has an `id` that starts with `line_`. That value is what you pass as `line_id` when you send a message, or you can set `from` to the line's `phone_number`. Both point at the same line.

A line also carries a delivery order. The `channels` array lists which channels the line can deliver on, in fallback order. The default is `["imessage", "rcs", "sms"]`: iMessage first, RCS if the recipient cannot receive iMessage, SMS if neither is available.

## Line status

`status` is one of three values.

| Status    | Sends today                           | What it means                                                                           |
| --------- | ------------------------------------- | --------------------------------------------------------------------------------------- |
| `active`  | Yes, up to `daily_capacity`           | The line is fully ramped and healthy. This is the normal state.                         |
| `warming` | Yes, up to a reduced `daily_capacity` | The line is new and its daily volume is still ramping up.                               |
| `paused`  | No                                    | The line sends nothing. You paused it, or BlueReacher paused it during a health review. |

Sending to a paused line returns `409 line_paused` from the send endpoint. Call `GET /get-line` for `paused_reason` to find out which kind of pause it is.

## What warming means

A new line does not start at full volume. It ramps over its first 14 days, and its status is `warming` for that whole window. You can send from a warming line on day one. You just get a smaller daily allowance.

The ramp is a documented default, applied to every new line:

| Warming day | `daily_capacity`                    |
| ----------- | ----------------------------------- |
| 1 to 3      | 20                                  |
| 4 to 6      | 45                                  |
| 7 to 9      | 90                                  |
| 10 to 12    | 140                                 |
| 13 to 14    | 175                                 |
| 15 onward   | 200, and `status` flips to `active` |

Two fields track where a line sits in the ramp. `warming_day` is the current day number, 1 through 14, and it is `null` once the line is active. `warming_completes_at` is the timestamp when the line reaches full capacity, also `null` once the line is active.

Do not hardcode 200 as the finish line. Higher plans set a higher ceiling. Read `full_daily_capacity` on the line object for the real number, and read `daily_capacity` for what the line can send right now.

Practical rule while a line is warming: send your best list to it. Reply rate and opt-out rate during the ramp shape the line's health, and health shapes capacity later.

## How capacity works

Three fields describe a line's day.

* `daily_capacity` is the ceiling for the current day.
* `messages_sent_today` is how many outbound messages the line has already sent today.
* `remaining_today` is `daily_capacity` minus `messages_sent_today`, floored at 0.

The counter resets at midnight in the line's own timezone. `timezone` gives you the IANA zone, and `capacity_resets_at` gives you the exact next reset as a timestamp, so you do not have to do zone math yourself.

What counts toward `messages_sent_today`:

* Every outbound message the API accepts from that line, including follow-ups in an existing thread.
* Messages that later fail delivery. They were still sent from the line.

What does not count:

* Inbound replies.
* Requests rejected at validation, such as a bad `to` number.
* Messages sent from a different line.

When `remaining_today` hits 0, further sends on that line return `429 daily_capacity_reached` from the send endpoint. If you send without naming a line, BlueReacher picks an eligible line for you and skips lines that are out of capacity or paused.

Paused lines always report `remaining_today` as 0, whatever `daily_capacity` says.

## Health indicator

`health` is a plain read on whether a line should keep sending.

| Value     | `health_score` | What to do                                                                                                |
| --------- | -------------- | --------------------------------------------------------------------------------------------------------- |
| `good`    | 80 to 100      | Nothing. Keep sending.                                                                                    |
| `watch`   | 50 to 79       | Review the list and the opening message on this line. Tighten targeting before you scale volume.          |
| `at_risk` | 0 to 49        | Stop new volume on this line. Capacity gets trimmed automatically, and the line can be paused for review. |

The score is a rolling 7 day read built from delivery success, reply rate, opt-out rate, and complaint signals on that line. It updates a few times an hour. A brand new warming line starts at `good` with a provisional score until it has enough sending history.

Route around trouble instead of waiting for a pause: filter out `at_risk` lines when you choose where to send.

## Authentication

Every request needs your API key in the `Authorization` header.

```bash theme={null}
Authorization: Bearer bluereacher_your_api_key
Content-Type: application/json
```

Keys are workspace scoped. You only ever see lines on the workspace that issued the key. `Content-Type: application/json` describes the response and is safe to send on these GET requests, which carry no body.

## GET /list-lines

Returns every line on your workspace, newest first by `created_at`.

**Method and path**

```
GET https://api.bluereacher.com/functions/v1/list-lines
```

**Auth:** required. Bearer key in the `Authorization` header. Returns only the lines on the key's workspace.

### Request fields

All parameters go in the query string. All are optional.

| Field    | Type    | Required | Description                                                                                  |
| -------- | ------- | -------- | -------------------------------------------------------------------------------------------- |
| `status` | string  | No       | Filter by status. One of `active`, `warming`, `paused`. Omit to return every status.         |
| `health` | string  | No       | Filter by health indicator. One of `good`, `watch`, `at_risk`.                               |
| `limit`  | integer | No       | Lines per page. Default 50. Maximum 200.                                                     |
| `cursor` | string  | No       | Opaque pagination cursor. Pass back the `next_cursor` from the previous response, unchanged. |

### Response fields

| Field         | Type           | Description                                                  |
| ------------- | -------------- | ------------------------------------------------------------ |
| `lines`       | array          | Array of line objects, newest first by `created_at`.         |
| `has_more`    | boolean        | `true` when more lines exist past this page.                 |
| `next_cursor` | string or null | Cursor for the next page. `null` when `has_more` is `false`. |

Each entry in `lines` is a line object:

| Field                  | Type            | Description                                                                                                |
| ---------------------- | --------------- | ---------------------------------------------------------------------------------------------------------- |
| `id`                   | string          | Line identifier, prefixed `line_`. Pass this as `line_id` when you send.                                   |
| `label`                | string          | The name you gave the line in the dashboard. Empty string if you never set one.                            |
| `phone_number`         | string          | The line's number in E.164 format, for example `+14155550142`.                                             |
| `status`               | string          | `active`, `warming`, or `paused`.                                                                          |
| `channels`             | array           | Channels this line delivers on, in fallback order. Values are `imessage`, `rcs`, `sms`.                    |
| `daily_capacity`       | integer         | Messages this line may send today.                                                                         |
| `full_daily_capacity`  | integer         | Messages per day once warming finishes. Set by your plan.                                                  |
| `messages_sent_today`  | integer         | Outbound messages sent from this line since the last reset.                                                |
| `remaining_today`      | integer         | `daily_capacity` minus `messages_sent_today`, floored at 0. Always 0 when `status` is `paused`.            |
| `capacity_resets_at`   | string          | ISO 8601 UTC timestamp of the next counter reset.                                                          |
| `timezone`             | string          | IANA timezone that governs the reset, for example `America/New_York`.                                      |
| `health`               | string          | `good`, `watch`, or `at_risk`.                                                                             |
| `health_score`         | integer         | 0 to 100. Higher is healthier.                                                                             |
| `warming_day`          | integer or null | Current day in the 14 day ramp. `null` when `status` is not `warming`.                                     |
| `warming_completes_at` | string or null  | ISO 8601 UTC timestamp when the line reaches `full_daily_capacity`. `null` when `status` is not `warming`. |
| `created_at`           | string          | ISO 8601 UTC timestamp of when the line was added to your workspace.                                       |

### Example request

```bash theme={null}
curl -G https://api.bluereacher.com/functions/v1/list-lines \
  -H "Authorization: Bearer bluereacher_your_api_key" \
  -H "Content-Type: application/json" \
  -d limit=50
```

### Example response

```json theme={null}
{
  "lines": [
    {
      "id": "line_8f2c1a",
      "label": "SDR team, west",
      "phone_number": "+14155550142",
      "status": "active",
      "channels": ["imessage", "rcs", "sms"],
      "daily_capacity": 200,
      "full_daily_capacity": 200,
      "messages_sent_today": 138,
      "remaining_today": 62,
      "capacity_resets_at": "2026-07-25T04:00:00Z",
      "timezone": "America/New_York",
      "health": "good",
      "health_score": 94,
      "warming_day": null,
      "warming_completes_at": null,
      "created_at": "2026-05-02T15:41:09Z"
    },
    {
      "id": "line_4b71de",
      "label": "New rep onboarding",
      "phone_number": "+16465550118",
      "status": "warming",
      "channels": ["imessage", "rcs", "sms"],
      "daily_capacity": 45,
      "full_daily_capacity": 200,
      "messages_sent_today": 31,
      "remaining_today": 14,
      "capacity_resets_at": "2026-07-25T04:00:00Z",
      "timezone": "America/New_York",
      "health": "good",
      "health_score": 91,
      "warming_day": 5,
      "warming_completes_at": "2026-08-03T04:00:00Z",
      "created_at": "2026-07-20T18:02:44Z"
    },
    {
      "id": "line_2ad930",
      "label": "Q2 event follow-up",
      "phone_number": "+13125550193",
      "status": "paused",
      "channels": ["imessage", "rcs", "sms"],
      "daily_capacity": 200,
      "full_daily_capacity": 200,
      "messages_sent_today": 12,
      "remaining_today": 0,
      "capacity_resets_at": "2026-07-25T04:00:00Z",
      "timezone": "America/New_York",
      "health": "at_risk",
      "health_score": 41,
      "warming_day": null,
      "warming_completes_at": null,
      "created_at": "2026-03-11T13:20:55Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}
```

### Filtered request

```bash theme={null}
curl -G https://api.bluereacher.com/functions/v1/list-lines \
  -H "Authorization: Bearer bluereacher_your_api_key" \
  -H "Content-Type: application/json" \
  -d status=active \
  -d health=good
```

### Errors

| HTTP | `code`              | When                                                                                                                 |
| ---- | ------------------- | -------------------------------------------------------------------------------------------------------------------- |
| 400  | `invalid_parameter` | `status` or `health` is not one of the documented values, `limit` is below 1 or above 200, or `cursor` is malformed. |
| 401  | `unauthorized`      | The key is missing, malformed, or revoked.                                                                           |
| 403  | `forbidden`         | The key is valid but has no read access to lines.                                                                    |
| 429  | `rate_limited`      | You passed the read limit. Wait the number of seconds in the `Retry-After` header.                                   |
| 500  | `internal_error`    | Retry with exponential backoff.                                                                                      |

An empty workspace is not an error. You get `200` with `"lines": []`.

## GET /get-line

Returns one line by id, with the same fields as `list-lines` plus 7 day performance stats and, when the line is paused, the reason.

**Method and path**

```
GET https://api.bluereacher.com/functions/v1/get-line?id=line_8f2c1a
```

**Auth:** required. Bearer key in the `Authorization` header. Requesting a line on another workspace returns `404`, not `403`.

### Request fields

| Field | Type   | Required | Description                                                      |
| ----- | ------ | -------- | ---------------------------------------------------------------- |
| `id`  | string | Yes      | Query parameter. The line identifier, for example `line_8f2c1a`. |

### Response fields

The response is a single `line` object. It carries every field from the `list-lines` line object, plus:

| Field               | Type           | Description                                                                                                                                                   |
| ------------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `paused_reason`     | string or null | Why the line is paused. `manual` (you paused it), `health_review` (BlueReacher paused it), or `account_hold` (billing). `null` when `status` is not `paused`. |
| `stats`             | object         | Rolling performance window for this line.                                                                                                                     |
| `stats.window_days` | integer        | Length of the window in days. Always 7.                                                                                                                       |
| `stats.sent`        | integer        | Outbound messages sent from this line in the window.                                                                                                          |
| `stats.delivered`   | integer        | Messages confirmed delivered.                                                                                                                                 |
| `stats.replied`     | integer        | Distinct contacts who replied.                                                                                                                                |
| `stats.opt_outs`    | integer        | Opt-outs recorded on this line, including auto-detected STOP keywords.                                                                                        |

### Example request

```bash theme={null}
curl "https://api.bluereacher.com/functions/v1/get-line?id=line_8f2c1a" \
  -H "Authorization: Bearer bluereacher_your_api_key" \
  -H "Content-Type: application/json"
```

### Example response

```json theme={null}
{
  "line": {
    "id": "line_8f2c1a",
    "label": "SDR team, west",
    "phone_number": "+14155550142",
    "status": "active",
    "channels": ["imessage", "rcs", "sms"],
    "daily_capacity": 200,
    "full_daily_capacity": 200,
    "messages_sent_today": 138,
    "remaining_today": 62,
    "capacity_resets_at": "2026-07-25T04:00:00Z",
    "timezone": "America/New_York",
    "health": "good",
    "health_score": 94,
    "warming_day": null,
    "warming_completes_at": null,
    "paused_reason": null,
    "stats": {
      "window_days": 7,
      "sent": 812,
      "delivered": 799,
      "replied": 143,
      "opt_outs": 4
    },
    "created_at": "2026-05-02T15:41:09Z"
  }
}
```

### Errors

| HTTP | `code`              | When                                                                                                       |
| ---- | ------------------- | ---------------------------------------------------------------------------------------------------------- |
| 400  | `invalid_parameter` | `id` is missing or is not a line identifier.                                                               |
| 401  | `unauthorized`      | The key is missing, malformed, or revoked.                                                                 |
| 403  | `forbidden`         | The key is valid but has no read access to lines.                                                          |
| 404  | `line_not_found`    | No line with that id exists on this workspace. Also returned for a line that belongs to another workspace. |
| 429  | `rate_limited`      | You passed the read limit. Wait the number of seconds in the `Retry-After` header.                         |
| 500  | `internal_error`    | Retry with exponential backoff.                                                                            |

## Error format

Every error uses the same envelope, on both endpoints.

```json theme={null}
{
  "error": {
    "code": "line_not_found",
    "message": "No line with id line_9x0000 exists on this workspace."
  },
  "request_id": "req_01J9K4TQ2M"
}
```

Match on `error.code`, not on `error.message`. Messages get clearer over time; codes do not change. Include `request_id` when you contact support about a specific request.

## Choosing a line before you send

The common pattern: list active lines, drop anything unhealthy or out of capacity, then send from the line with the most room left.

```javascript theme={null}
const res = await fetch(
  "https://api.bluereacher.com/functions/v1/list-lines?status=active",
  {
    headers: {
      Authorization: "Bearer bluereacher_your_api_key",
      "Content-Type": "application/json"
    }
  }
);

const { lines } = await res.json();

const nextLine = lines
  .filter((line) => line.health !== "at_risk" && line.remaining_today > 0)
  .sort((a, b) => b.remaining_today - a.remaining_today)[0];

if (!nextLine) {
  throw new Error("No active line has capacity left today.");
}

// Pass nextLine.id as line_id on your send call.
```

If you would rather not manage this yourself, omit `line_id` when you send. BlueReacher picks an eligible line, skipping paused lines, `at_risk` lines, and lines with no capacity left.

## Rate limits and caching

Read endpoints allow 120 requests per minute per API key. Over that, you get `429` with a `Retry-After` header in seconds.

Cache the result of `list-lines` for up to 60 seconds. Counters like `messages_sent_today` refresh within about 5 seconds of a send, so polling faster than once per second buys you nothing.

For pagination, loop until `has_more` is `false`, passing `next_cursor` back verbatim each time. Cursors are opaque. Do not build or edit them.

Defaults on this page (14 day warming ramp, 200 messages per day at full capacity, 120 reads per minute) are current as of July 24, 2026. The line object always reports your account's real numbers, so read the fields rather than assuming the defaults.
