Skip to content
LinqCopy agent prompt
Phone Numbers

Phone Reputation

How we measure reputation of a phone line and how to check it before you send.

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
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/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/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 phone line carries a reputation — a line-level read on whether the line as a whole is in good standing. It’s the line-wide companion to chat health, but it is not simply a sum of it.

Phone reputation draws on two kinds of signals:

  • Patterns across the conversations on the line — engagement and deliverability across the line’s chats, in aggregate. This is the larger driver, and it’s closely related to chat health.
  • Line-level activity that no single conversation reveals — how many different recipients the line messages in a short window, how many brand-new conversations it starts in a day, and its overall send pace.

You’ll see reputation on each number returned by GET /v3/phone_numbers. It’s a prediction of where the line is heading — treat it as a pre-send gate, not a report.

  • GET /v3/phone_numbers — every line in the list response carries its current reputation.
  • phone_number.status_updated webhook — fires when reputation changes. The payload carries previous_reputation and new_reputation, so you can react the moment a line moves to AT_RISK or CRITICAL.

Check reputation shape and value before you queue outbound messages for a line — the same pre-send gate pattern as chat health, one level up. Use it to route work toward your healthiest lines: if a line is CRITICAL, onboard new recipients onto a HEALTHY line instead and let the CRITICAL one recover before you send more on it.

const { phone_numbers } = await client.phoneNumbers.list();
for (const pn of phone_numbers) {
switch (pn.reputation.status) {
case 'HEALTHY': break; // send normally, safe to onboard new recipients
case 'AT_RISK': reviewLine(pn); break; // slow the pace, check chat health
case 'CRITICAL': pauseLine(pn); break; // stop outbound, route new recipients elsewhere
}
}

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

statusWhat it meansWhat to do
HEALTHYThe line is in good standing.Send normally.
AT_RISKWarning signs: low engagement or too many new conversations.Slow the line’s send pace and use chat health to find conversations to fix.
CRITICALStrong signals that messages from this line aren’t landing well.Pause outbound on the line until it recovers.

The line looks like it’s operating normally. Engagement and deliverability across its conversations look good, and its overall activity is within a normal range. No action needed.

One or more soft signals suggest the line as a whole is heading in the wrong direction. Common drivers fall into two groups:

  • Conversation patterns. Engagement is low across many of the line’s chats. Chat health is the best place to see this per-conversation.
  • Line-level activity. Starting too many brand-new conversations in a single day can move the line to at-risk on its own, even when individual chats are fine. A spike in overall send volume adds to that risk when engagement is low — but a high-volume line with healthy replies stays fine.

See What moves a line’s reputation for the specific thresholds behind each of these.

AT_RISK is a warning, not a hard stop. Recommended playbook:

  1. Check chat health. Pull chat health for the line’s conversations to find the AT_RISK and CRITICAL chats, and fix those first.
  2. Slow the line’s overall pace. Reduce total send volume until signals recover.
  3. Bias outbound toward messages that elicit replies. Questions and follow-ups outperform broadcasts.
  4. Spread activity out. Avoid contacting many new recipients or opening many new conversations in a single burst.
  5. Vary your content. Repeated near-identical messages across the line amplify the signal.

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

Strong signals that messages from this line aren’t reaching recipients the way you expect. Continuing to send is unlikely to help and may make the situation worse.

In practice a line reaches CRITICAL one of two ways: a large share of its active conversations are themselves CRITICAL, or the line has been FLAGGED. Line activity on its own — volume, pace, new conversations — doesn’t get a line to CRITICAL without weak engagement alongside it.

Recommended action: pause outbound on the line. Re-engage only after it returns to HEALTHY.

1. Health of the conversations on the line

Section titled “1. Health of the conversations on the line”

This is the largest driver. We look at the line’s active conversations — those with a message in either direction in the last 7 days — and what share of them are unhealthy.

  • Nothing counts here until the line has at least 5 active conversations that are AT_RISK or CRITICAL. A new line with one bad chat is not penalized for it.
  • Above that floor, the share of active conversations that are unhealthy is what matters — not the raw count. A line with 40 unhealthy chats out of 800 active is in better shape than one with 8 out of 10.
  • Conversations that are AT_RISK can carry a line to AT_RISK, but never on their own to CRITICAL, no matter how large the share. Only the share that is CRITICAL escalates the line that far.
Of the line’s active conversationsWhere the line tends to land
Under about a third unhealthyHEALTHY
Around half or more AT_RISK, few or no CRITICALAT_RISK
Nearly all AT_RISK, none CRITICALAT_RISK — at-risk conversations alone never reach CRITICAL
A large majority CRITICALCRITICAL

A conversation where the recipient has opted out counts as CRITICAL for as long as it stays active — that is, for as long as you keep messaging someone who asked you to stop. Stop sending and it ages out of the 7-day window on its own.

Use chat health to see which conversations are dragging the line down.

Starting roughly 50 or more brand-new conversations in a rolling 24 hours will move a line to AT_RISK on its own, even when every individual chat looks fine.

“New” means a first-ever outbound to that person from this line. Re-messaging people you’ve already talked to doesn’t count toward this signal — it’s the cold-start breadth that carriers may react to hardest.

If you need to reach a large list, spread the first touch across days rather than opening the whole list at once.

3. Send volume relative to the line’s own normal

Section titled “3. Send volume relative to the line’s own normal”

We compare a line’s outbound today against that line’s own recent typical daily volume, not against a fixed number. A line that has consistently sent high volume with healthy replies isn’t penalized for its size; a line that jumps several times above its own normal is what we react to.

  • The signal scales with the size of the jump — a several-fold increase is a soft contribution, a ten-fold increase a much stronger one.
  • It needs real volume behind it. Small day-to-day swings on a low-volume line don’t register.
  • A very large absolute day (in the thousands of messages) also contributes when the line’s reply rate is low. If people are replying, it doesn’t.
  • Volume never flags a line by itself. Every level of this signal is a contributing factor only — it moves a line to AT_RISK only alongside low engagement or another driver.
  • A spike keeps counting for about a week afterward, so a single quiet day right after a burst won’t clear it.

The practical read: ramp gradually rather than spiking, spread large sends across days, and prioritize recipients who have already engaged with you.

If the line’s status goes to FLAGGED, reputation goes to CRITICAL immediately regardless of every other signal, and its messages may not be reaching recipients at all.

Move active traffic to a HEALTHY line right away and let this one recover. reputation returns to the line’s current signals once the flag clears.

You don’t have to route that yourself. Send with POST /v3/messages and no from, and Linq picks the line: it reuses the recipient’s existing chat while that line can still send, and moves them onto a fresh line when it can’t. from_selection.reason in the response tells you which happened — failover_flagged means the recipient was moved off a flagged line.

Each signal runs on its own window: conversation health on a rolling 7 days, new conversations on a rolling 24 hours, a volume spike for about a week after it fires. Reputation is re-evaluated as the line sends and receives, and quiet lines are re-checked on a regular sweep — so a line that stops the behavior recovers as the signals age out of their windows rather than needing any manual reset.

ScenarioLikely status
Send volume with engaging reply rates from usersHEALTHY
Steady high volume the line has always sent, replies healthyHEALTHY
Volume jumps several times above the line’s normal, replies still goodHEALTHY
4 of the line’s 60 active chats are AT_RISKHEALTHY
50+ brand-new conversations opened in one dayAT_RISK
Volume jumps several times above the line’s normal, replies drying upAT_RISK
Rapid increase in new conversations with little to no engagementAT_RISK or CRITICAL
Around half the line’s active chats are AT_RISK, none CRITICALAT_RISK
Nearly all the line’s active chats are AT_RISK, none CRITICALAT_RISK
More than two-thirds of the line’s active chats are CRITICALCRITICAL
Still messaging recipients who opted outAT_RISK or CRITICAL
The line’s status is FLAGGEDCRITICAL

Ramp with collapsing replies. A line running a few hundred sends/day with a reply to most of them ramps to a few thousand a day while replies fall to near zero — a single-digit reply rate.

Dormant line to full blast. A mostly idle line sends a couple thousand messages one day and twice that the next. Even at a ~50% reply rate that leaves thousands of messages a day unanswered — a spike and weak engagement in absolute terms at the same time. Watch the absolute number of unanswered messages per day, not only the ratio.

Migrating a list onto a new line and resuming cadence. Contact card, intro message, then straight back into the previous daily cadence without waiting for anyone to reply. Those conversations have no history with the new number, so every send lands cold. Gate cadence on replies — hold at one message until the recipient responds.

Fleet-wide low reply rates. When every line in a fleet sits in the same low reply band, they tend to degrade one at a time rather than all at once. A line that looks fine today isn’t necessarily safe if its peers share the same sending pattern.

Chat health and phone reputation are related but answer different questions:

  • Chat health scores a single conversation — is this recipient engaging, are messages landing.
  • Phone reputation scores the line — both the aggregate of its conversations and line-level activity that no single conversation reveals, like how broadly and how fast the line is messaging.
  • New lines start as HEALTHY and move to AT_RISK or CRITICAL as signals warrant.
  • Let Linq route around reputation for you. Send with POST /v3/messages and no from: the platform keeps traffic spread across your pool, prefers healthier lines for new conversations, reuses the line a recipient is already on, and fails over off a line that can’t send. Pinning a from on every send — or calling GET /v3/available_number before each one — opts you out of that. See Choosing a line.
  • Onboard new users onto healthy lines. available_number is the onboarding call: it hands you the best available line and a contact card to show a new user, and successive calls cycle through the pool so signups spread evenly instead of piling onto one number.
  • Watch reputation, don’t hand-pick on it. Subscribe to phone_number.status_updated and read reputation from GET /v3/phone_numbers to know when a line needs attention — then fix the sending behavior on that line rather than routing around it by hand.

Does phone reputation read message content or store any PII?

No. Phone reputation scoring runs on anonymous, aggregate signals — line-level volume, send/receive ratios, recipient counts, conversation pace, and similar metadata. Linq does not collect or store message content or other PII to compute reputation for phone numbers.

Why is there no OPTED_OUT on phone reputation?

Unlike chat health, phone reputation has no OPTED_OUT status. Opt-out is a per-recipient signal scoped to a single conversation — it can’t apply to a whole line. You’ll only ever see OPTED_OUT on chat health.

Why are most of my high-volume lines AT_RISK?

Volume alone can’t cause this — send volume never flags a line by itself, and a line that has always sent high volume isn’t penalized for its size. Check two things instead: whether reply rates dropped on those lines as they scaled, and whether you’re opening many brand-new conversations at once, which can move a line on its own.

Once a chat has 3+ replies, are we in the clear with Apple?

No. Behavior changes week to week — a recipient who once engaged can fall off, decide to opt out, or report the conversation as spam if they get annoyed after a while. A reply history doesn’t bank you any credit.

Treat engagement as something you maintain rather than something you earn once: keep each recipient engaged at 2–3 replies a week. At that level you know you’re clear. When someone does go quiet, back off on an escalating schedule rather than sending harder into silence.

How many new conversations can I start per day?

Stay meaningfully under 50 first-touch conversations per line per rolling 24 hours, and spread a large list across days instead of opening it in one burst. See new conversations started in a day.

Is AT_RISK reversible?

Yes. The status reflects recent behavior. Improving send pace and engagement on the line returns it to HEALTHY as the signals age out of their windows — see how quickly it moves. No manual reset is needed.

Should I migrate users off an AT_RISK line?

No — fix the line. Migrating users off carries the same underlying sending pattern to a new line and spreads the issue rather than solving it. Use chat health to find the conversations dragging the line down, address those, and let the line recover.