---
title: Chat Health | API Docs
description: How we score the health of a chat and how it relates to your line's standing.
---

Note

This feature is currently in beta and may be inaccurate at times. We are actively improving our health score models. Expect additional engagement and intent signals as the scoring gets smarter.

**Review your setup with an agent.** Copy this prompt into your own AI coding agent to check your integration against these guidelines.

Audit your Linq integration Copy prompt

```
You are auditing a codebase that integrates with the Linq Partner API
(iMessage / RCS / SMS messaging). Verify it follows Linq's best practices for
deliverability, chat health, and line reputation. This is READ-ONLY — do not
change code unless I ask.

Step 1 — Ground yourself in Linq's public docs. Start with the index at
https://docs.linqapp.com/channel/imessage/llms.txt, then fetch the pages you need — at minimum
Best Practices, Chat Health, Phone Reputation, Sending Messages, and Webhooks,
plus the /v3 API reference for the endpoints below. (https://docs.linqapp.com/channel/imessage/llms-full.txt
has every page in one file, but it is large — prefer the index and targeted
pages.) If you cannot fetch these, stop and tell me rather than auditing from
memory.

Step 2 — Locate the integration. Search the codebase for the Linq base URL
(api.linqapp.com/api/partner), "/v3/" request paths, an official SDK — Node
`@linqapp/sdk`, Python `linq-python` (imported as `linq`), or Go
`github.com/linq-team/linq-go` — and the inbound webhook handler, so you know
where sending, onboarding, and webhook handling live.

Step 3 — Audit against these requirements. Each item is something my code is
supposed to do — confirm whether it actually does, and cite the file and line:

Opt-out (compliance — most important)
- The code should scan every inbound message on the message.received webhook for
  opt-out keywords — STOP, UNSUBSCRIBE, OPTOUT, CANCEL, END, QUIT (whole message;
  exact and case-sensitive, except OPT OUT which matches in any casing, spaced,
  hyphenated or not) — plus any clear "stop messaging me" intent, and a match
  should immediately stop all outbound to that recipient. Linq rejects sends to
  a keyword-opted-out recipient with 403 (error code 2024), but only the exact
  keywords trigger that block — conversational stop requests are the code's job
  to catch — and a 2024 rejection should be honored, not retried.
- Every send to an opted-out recipient is rejected, including a final courtesy
  message. If the code sends one confirmation telling the recipient they can
  reply any time to resume, that single request should set override_optout: true.
  The override applies only to the request it is set on and does not lift the
  block; each use is recorded, so it should appear once per opt-out, never in a
  retry loop.
- The code should treat a chat whose health_status is OPTED_OUT as never-send
  until Linq clears the status. Linq clears it as soon as the recipient replies
  again in any chat with you (any inbound that is not itself an opt-out
  keyword), so the code should gate on the current health_status rather than
  tracking opt-ins itself.

Sending & line selection
- The code should send with POST /v3/messages using `to` and NO `from`. Linq
  then picks the best line, load-balances across your pool, reuses the
  recipient's existing healthy line, and fails over off a flagged line
  automatically (see from_selection.reason in the response).
- The code should NOT call GET /v3/available_number (or pin a fixed `from`)
  before each send — that defeats the automatic load-balancing and failover.

Onboarding new users
- The code should use GET /v3/available_number when onboarding a NEW user, to
  get the best available line (and its vcf_url contact card) to show them — e.g.
  a number or deeplink shown at signup — so new users spread evenly across the
  pool. That is what available_number is for; it is not a per-message call.

Contact card
- The code should create the contact card once per line with
  POST /v3/contact_card (initial setup only — later changes use
  PATCH /v3/contact_card), and share it through the dedicated
  POST /v3/chats/{chatId}/share_contact_card endpoint.
- New contacts should be inbound-first — let the recipient message first. The
  card should be shared only after at least one outbound message exists in the
  chat, and re-shared about once a day, since there's no confirmation the user
  saved it.

Health & reputation gating
- Before sending, the code should check the chat's health_status and the line's
  reputation from GET /v3/phone_numbers, and slow or pause on AT_RISK /
  CRITICAL. It should also handle the phone_number.status_updated webhook to
  react when a line's reputation changes.
- New users should onboard onto HEALTHY lines. The code should NOT migrate users
  off an AT_RISK line to escape the status — improve engagement and let the line
  recover instead.

Engagement & cadence
- Outbound should be built to get replies (aim for 3+ replies early and roughly
  a 1:2 inbound:outbound ratio). When a recipient stops replying, the code
  should slow down and then stop, rather than keep messaging someone who isn't
  responding.

Volume & ramp
- The code should keep each line under ~7,000 messages/day (inbound + outbound).
  That is a performance guideline, not a reputation threshold — steady high
  volume with healthy reply rates is fine.
- The code should not start roughly 50 or more brand-new conversations per line
  in a rolling 24 hours. Check bulk import, list upload, and campaign kickoff
  paths for anything that opens a whole audience at once, and confirm first
  contact is spread across days and across lines.
- The code should ramp a line's daily volume gradually rather than jumping
  several-fold above what that line has recently been sending. Look for
  scheduled or triggered sends that can take a quiet line to a large day in one
  step.

Step 4 — Report:
1. A table: Check | Status (pass / gap / n/a / unknown) | Where (file:line) | Fix.
2. A short action list, highest deliverability and compliance risk first.
Ground every finding in code you actually read. If you cannot determine an item,
mark it unknown rather than guessing.
```

Every chat carries a `health_status` — the field you can check **before sending** to decide what to do with the next outbound on that conversation. While there isn’t a direct link between deliverability and chat health, it is a prediction and analysis of messaging behavior. You’ll see it on every chat-related webhook event and on every chat read. Treat it as a pre-send gate, not as much a report.

## Engagement is a strong signal

If you take one thing from this page: **two-way engagement is among the strongest signals we use**, and it rolls directly into your [line’s reputation](/channel/imessage/guides/phone-numbers/phone-reputation/index.md). Continuing to send messages into silence is one of the most common reasons a conversation — and its line — slides to `AT_RISK` or even `CRITICAL`.

So:

- **Send messages built to get a reply.** Lead with a question or a clear, relevant prompt.
- **Let replies set your pace, and back off when they stop.** See [How many messages should I send?](#how-many-messages) for the cadence and back-off ladder.

## How to use it

We recommend you do whatever is best for your messaging use case, but one example could be to cache the most recent `health_status.status` from your webhook stream and check it as a pre-flight before queueing each outbound:

```
switch (chat.health_status.status) {
  case 'HEALTHY':   send(message); break;
  case 'AT_RISK':   checkReplyRate(); break;
  case 'CRITICAL':  pause(chat); break;
  case 'OPTED_OUT': skip(chat); // terminal — never resume sending
}
```

Acting on the status before each send is what turns the signal into delivery improvement — ultimately leading to a healthier line.

## Statuses

| `status`                  | What it means                                                                                       | What to do                                                                                                                            |
| ------------------------- | --------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| [`HEALTHY`](#healthy)     | Healthy conversation.                                                                               | Send normally.                                                                                                                        |
| [`AT_RISK`](#at-risk)     | Poor engagement signals, which may lead to worsening health if current messaging patterns continue. | Slow outbound on this chat and check your reply rate.                                                                                 |
| [`CRITICAL`](#critical)   | Strong signals that messages aren’t landing well.                                                   | Pause messaging on this chat until healthy.                                                                                           |
| [`OPTED_OUT`](#opted-out) | The recipient asked you to stop.                                                                    | Terminal. Stop messaging this recipient — Linq rejects further sends with [`2024`](/channel/imessage/error/codes/2xxx/2024/index.md). |

### HEALTHY

The chat looks like a normal conversation. Replies are landing, delivery signals look good, and no opt-out language has been detected. No action needed.

### []()AT\_RISK

One or more soft signals suggest this chat is heading in the wrong direction. Common drivers:

- **Low engagement.** The ratio of recipient replies to your sends is low.

`AT_RISK` is a *warning*, not a hard stop. What to do:

- **Slow outbound and vary your content.** Reduce send frequency; repeated near-identical messages amplify negative signal.
- **Back off if replies have stopped.** Don’t hold the same cadence into silence — see [How many messages should I send?](#how-many-messages).

Watch for the chat moving back to `HEALTHY` (good) or down to `CRITICAL` (act fast).

### CRITICAL

Strong signals that messages on this chat aren’t reaching the recipient the way you expect. Continuing to send is unlikely to help and may make the situation worse for the broader line.

Recommended action: Pause this chat. Re-engage only after chat becomes healthy again.

### []()OPTED\_OUT

The recipient sent an opt-out keyword on this chat. Regardless of any other signals, do not send further outbound messages to them — the status clears the moment they reply again (see below).

**Linq enforces this for you.** While a recipient is opted out, every send to them is rejected with `403` and error code [`2024`](/channel/imessage/error/codes/2xxx/2024/index.md) before the message is queued — nothing is dispatched. The block covers that recipient in direct messages across every line on your account, so sending from a different number does not get around it. Group threads are not affected.

This includes a final courtesy message. There is no free follow-up: if you want to confirm you have stopped and let the recipient know they can reply any time to resume, send that one message with `override_optout: true`. That is the only way through. It applies to the single request it is set on, does not lift the block, and each use is recorded against your account.

```
// the one message you can still send
POST /v3/chats/{chatId}/messages
{
  "override_optout": true,
  "message": { "parts": [{ "type": "text", "value": "You've been unsubscribed. Reply any time if you'd like to hear from us again." }] }
}
```

The full set of opt-out keywords (case sensitive, except “optout”):

`STOP`, `UNSUBSCRIBE`, `OPTOUT`, `CANCEL`, `END`, `QUIT`

The keyword must be the entire message, never part of a longer one — `STOP` counts, `please stop` does not. Most keywords must match exactly, including case. `OPT OUT` is the exception: it matches in any casing, with or without the space or a hyphen, so `opt out`, `Opt-Out` and `optout` all count.

The opt-out clears as soon as the recipient replies again. Any later message from them — in any chat they have with you, the same way the block covers every chat — opts them back in immediately, unless that message is itself an opt-out keyword (a restated `STOP` keeps them opted out). There is no opt-in keyword to teach: `START`, `OPTIN`, and `UNSTOP` still work in any casing, but so does any other reply. Once they reply, sends are accepted again and the chat moves back to whichever health bucket current signals indicate.

## Chat health and phone reputation

Chat health is a leading signal for your line’s [reputation](/channel/imessage/guides/phone-numbers/phone-reputation/index.md): the health of the conversations on a line rolls up into the line’s overall reputation. Many `AT_RISK` or `CRITICAL` chats on a single line increase the chance that the line will be flagged by our systems or carriers.

That said, **chat health is not the only thing that affects line reputation.** We are continuously improving our models to create a healthy ecosystem. See the [Phone Reputation guide](/channel/imessage/guides/phone-numbers/phone-reputation/index.md) for the line-level view.

## Tips

- New chats start as `HEALTHY` and move to `AT_RISK`, `CRITICAL`, or `OPTED_OUT` as signals warrant.
- `updated_at` tells you when the status last changed; use it to detect rapid status drops in your dashboards.
- Switch on `health_status` according to the use cases above.

## FAQ

**[]()How many messages should I send to keep a chat healthy?**

Let replies set the pace. A back-and-forth conversation can sustain a normal cadence; a one-sided one can’t. As a rule of thumb, keep at least **2–3 recipient replies** flowing for the volume you send, and don’t send many messages a week into a chat that isn’t replying.

When a recipient goes quiet, slow down and eventually stop — sending harder into silence is one of the fastest ways to push a chat to `AT_RISK` or `CRITICAL`. Use an escalating back-off:

1. **No reply?** Wait about a day, then send **one** follow-up.
2. **Still no reply?** Wait a few days, then send **one** more.
3. **Still nothing?** Send a final message that gives the recipient an easy way out — for example, asking whether they’d like to stop receiving messages — then **halt all outbound to that recipient.**
4. **Wait for a reply before sending again.** When they respond, read it carefully: a clear “stop” (or an [opt-out keyword](#opted-out)) means you’re done; genuine interest means you can resume at a normal, reply-paced cadence.

**Does chat health read message content or store any PII?**

No. Evaluating health status runs on anonymous, aggregate signals — message volume, sends vs. receives, response cadence, and similar metadata. Inbound text is scanned at runtime (JIT) to detect opt-out language for [`OPTED_OUT`](#opted-out) signals, but message content is never collected, stored, or retained. No PII is persisted.
