What happens if your iMessage number is flagged as spam?
When Apple flags a ZeroSettle iMessage number as spam, every send from it fails, to everyone, and ZeroSettle replaces the number right away at no extra charge. Flags are rare: ZeroSettle says that "across more than 10,000 messages a week we have seen it happen only twice". After a replacement, your API token, webhook and code stay exactly as they are. Only the number changes, so customers see texts from a new number; for accounts with several numbers, the docs add that the line keeps its line_id, conversations, message ids, name and photo, and STOPs.
Key takeaways
- The symptom: sends fail for every recipient at once, not for one contact. ZeroSettle's docs list no flag-specific error code or event, so watch for
message.failedspreading across many people. - How often: ZeroSettle's homepage says "across more than 10,000 messages a week we have seen it happen only twice".
- What ZeroSettle does: replaces the number "right away at no extra charge"; the API token, webhook and code stay as they are.
- What carries over: on accounts with several numbers, the docs say the line keeps its
line_id, conversations, message ids, name and photo, and STOPs. The number changes, and the customer's old thread does not carry over. - Price: Pro is $49/month for one number; Growth is from $599/mo. A replacement adds nothing. See ZeroSettle plans and pricing.
How do you know a number has been flagged?
A flagged ZeroSettle number fails every send, to every recipient, at once; a problem with one contact fails only that contact. ZeroSettle's docs list no error code or webhook event specific to a flag, so the signal is a pattern: message.failed events ("A send was recorded but not delivered") spread across many different people, with no message.sent in between.
| What you see | Who it affects | What it means |
|---|---|---|
403 recipient_opted_out | One contact | They texted STOP; nothing sent or stored |
403 awaiting_reply | One contact | Two unanswered messages; nothing sent or stored |
429 new_contact_limit | New contacts only | The number reached its daily or 15-minute cap; wait retry_after_s |
502 send_failed | Usually one contact | The line could not send it, e.g. a bad handle |
429 line_busy or 503 | The line, briefly | Busy or temporarily unreachable. The failed send is stored, so message.failed fires too; retry with backoff |
message.failed for many contacts, no message.sent, and no 429 or 503 on your sends | Everyone | The pattern of a flagged number |
ZeroSettle posts message.sent for sends "from your code, the console, or MCP", so a webhook receiver sees every one of them. Call the watcher below for every event your receiver has verified (X-ZeroSettle-Signature) and deduplicated (message.id plus event), before the docs' sample receiver returns early on anything but message.received (ZeroSettle webhook docs). It alerts when five or more people have failed sends within 30 minutes and nothing has gone out:
import time
from collections import deque
WINDOW_S, MIN_RECIPIENTS = 30 * 60, 5 # tune to your volume
recent: dict[str, deque] = {}
last_alert: dict[str, float] = {}
def watch(ev: dict, alert) -> None:
if ev.get("event") not in ("message.sent", "message.failed"):
return
m, now = ev["message"], time.time()
key = m.get("line_id", "only") # one-number accounts carry no line_id
q = recent.setdefault(key, deque())
q.append((now, ev["event"], m["handle"]))
while now - q[0][0] > WINDOW_S:
q.popleft()
failed = {h for _, e, h in q if e == "message.failed"}
if (len(failed) >= MIN_RECIPIENTS and all(e == "message.failed" for _, e, _ in q)
and now - last_alert.get(key, 0) > WINDOW_S):
last_alert[key] = now
alert(key, failed) # page a person
STOP and unanswered-message refusals store nothing, so they never reach this counter. A busy or briefly unreachable line does: the docs say a failed send "keeps its record", so a send that fails with 429 line_busy or 503 also posts message.failed. If your own sends are getting those codes, retry with backoff first. If failures keep spreading without them, queue outgoing messages rather than retrying them, and contact ZeroSettle.
What does ZeroSettle do when a number is flagged?
ZeroSettle replaces a flagged number right away at no extra charge, and your integration keeps running unchanged. ZeroSettle is an iMessage API: a real phone number on real iMessage, running on Apple hardware we operate for you, that your code texts customers from and hears back on through a bearer token and signed webhooks (what ZeroSettle is). Its homepage FAQ says:
If it does happen, we replace the number right away at no extra charge. Your API token, webhook, and code stay exactly as they are, so your integration keeps running without changes.
zerosettle.io, FAQ "What if a number does get marked as spam?"
For accounts with several numbers, the docs add that the line "keeps its line_id, conversations, message ids, name and photo, and STOPs, and only its number (line) changes." A one-number account never sees a line_id; the homepage wording above is its published promise.
| Item | After the replacement | Where it is stated |
|---|---|---|
| API token, webhook, your code | Unchanged | Homepage FAQ |
line_id, conversations, message ids, name and photo, STOPs | Kept | API docs, for accounts with several numbers |
The number (line) | New | API docs |
| The customer's existing thread in Messages | Does not carry over | Follows from the new number |
| Cost | No extra charge | Homepage FAQ |
What changes for your customers?
Your customers see texts from a new number, and their thread with the old number does not carry over. To their iPhone the replacement is an unknown sender, even if they saved the old number, so the first text from it has to work like a first message.
- Links wait for a reply. ZeroSettle's docs: "Until the recipient replies to your number or saves it as a contact, a link in your text shows as plain text they can't tap" (why first-message links aren't tappable).
- The first text can be screened out. The docs add that Screen Unknown Senders (iOS 26) moves texts from unknown senders into a separate list without a notification, "so a cold first text may never be seen."
- Reporting is one tap away. Apple's iPhone User Guide says a Report Spam link "appears at the bottom of any message from any unknown sender", and "You can't report a message after you've replied to it."
- Say who you are and that the number changed, then ask something answerable in a word.
- Don't announce it to everyone at once. A burst of "new number" texts to people not expecting one is the unanswered messaging that gets numbers flagged. Mention the change in the next message each customer was due to get anyway.
- Tue 9:40 AM
- Business: Hi Dana, it's Sam at Kettle & Co. Our texts come from this new number now, so it's still us.
- Business: Your next bag ships Thursday. Same Ethiopia roast, or want to switch?
- Customer: oh ok, saving this one. same roast please
- Business: Done, Ethiopia ships Thursday. Your order page: kettle.example.com/orders👍
What should your code do?
Your code needs no change for the replacement itself, because the token and webhook stay the same; what breaks is anything that stored, sent from or printed the old number. The docs' rule is "Store line_id; show line."
- Key per-line records (routing, CRM fields, reports) on
line_id, which messages, conversations, opt-outs and webhook payloads carry on a multi-number account. A one-number account has none; key on the account. - When you choose the sending line, pass
line_id, notline: the docs note thatline"changes if the line's number does", and a number that is no longer one of your lines is refused with404 unknown_line. Never comparemessage.linewith a hardcoded number. - Read the number you display from
GET /api/imessage/line:handle, or each entry'snumberinlines. - Regenerate whatever prints it:
sms:links, QR codes, email footers, app screens. Ansms:link also lets customers text the new number first, which makes it a known sender.
export BASE=https://portal.zerosettle.io
curl -s -H "Authorization: Bearer $ZEROSETTLE_TOKEN" $BASE/api/imessage/line \
| jq '{number: .handle, lines: [.lines[]? | {id, number}]}'
A CRM integration should store line_id on each contact rather than the number, as in logging iMessages in HubSpot or Salesforce.
How do other providers handle flagged numbers?
Published policies differ: ZeroSettle replaces the number, Linq moves recipients to another line in your pool, and myCRMSIM's guide sends you to Apple Support. Each entry comes from the vendor's own page.
| Provider | What its own pages say |
|---|---|
| ZeroSettle | Replaced "right away at no extra charge"; token and webhook unchanged; new number. |
| Sendblue | Lists block causes such as "Too many follow-ups without a response" and says to "Contact support with the affected line, example failures, and approximate timing". Its webhook docs list a line_blocked event. No public replacement policy found. |
| Linq | A FLAGGED line's reputation "goes to CRITICAL immediately". When you send without a fixed sender, Linq "moves them onto a fresh line" (failover_flagged), and its docs say to let the flagged line recover. |
| Project Blue | Its messaging page lists "Flagged number?" with "Recovery support" and "We fix it fast"; it describes no process. |
| Blooio | Conduct that gets your number "flagged, blocked, throttled, or banned by Apple" violates its Acceptable Use Policy, and "Subscription fees are not refundable" when an account is suspended for a violation. |
| LoopMessage | Refunds exclude issues caused by "a violation of a destination messaging platform's or any other party's policies". |
| myCRMSIM (iMessage on your own Apple account) | You contact Apple Support, which provides a re-enable form; the guide expects Apple's reply "within 24 hours". |
Self-hosted setups on your own Mac and account have no vendor in between, so recovery is likewise up to you and Apple (self-hosting vs a managed API). ZeroSettle is the wrong choice if you must keep one specific number: a replacement is a different number, and there is no porting. Project Blue's messaging page, by contrast, offers to port your existing number (as of October 2026).
What if the line was used for unsolicited messaging?
ZeroSettle suspends it. The docs: "ZeroSettle suspends lines used for unsolicited messaging. If you are unsure whether you have consent, you don't." The homepage puts it as "Unsolicited messaging gets a line suspended", and the docs describe no replacement for a suspended line. The iMessage API Terms also say ZeroSettle "is not responsible for any action Apple or any carrier takes" on a line, number or message, "including restriction, suspension, or non-delivery".
How do you keep a number from being flagged?
Message people who asked to hear from you, at the pace ZeroSettle's limits enforce. The docs: "Repeated messages to people who never answer are the fastest way to get a number flagged by Apple."
- Let customers text first with an
sms:link or QR code; anyone who texted the line first is never limited. - Stop after two unanswered messages. Every plan enforces it with
403 awaiting_reply. - Know the new-contact limits: per number, Pro allows 5 a day, Growth 50 a day and 3 per 15 minutes. "This is what keeps the number out of Apple's spam filters," the docs say, and they call the limits "a floor, not a licence".
The full guide is keeping a business iMessage number out of spam filters; see also how reliable ZeroSettle is, how STOP works and the reliability and trust hub. To build, read the limits and error reference or get a number.
Frequently asked questions
Do opt-outs carry over to the new number?
Yes. ZeroSettle's docs say a line whose number is replaced keeps its STOPs, and a STOP blocks every number on your account, so a send to anyone who opted out still returns 403 recipient_opted_out until they text START.
Who owns the number?
ZeroSettle. Its terms say "Phone numbers assigned to your lines remain the property of ZeroSettle", and a number is reclaimed once a subscription is no longer active, so you can't take the old number with you.
What happens to messages that failed while the number was flagged?
They stay stored with record.status failed; the docs describe no automatic resend. Resend from your own queue once the replacement is live, skipping anyone who never answered.
Sources
- ZeroSettle iMessage API docs: "Authentication", "More than one number", "Send a message", "Links in a first message", "Webhooks, step by step" and "Errors, limits, and compliance". Checked October 12, 2026.
- zerosettle.io homepage, FAQ "What if a number does get marked as spam?" and "Will my number get flagged as spam?". Checked October 12, 2026.
- ZeroSettle iMessage API Terms, section 5 (third-party platforms) and section 6 (phone numbers). Last modified September 28, 2026.
- Report spam and block senders in Messages on iPhone, Apple iPhone User Guide. Checked October 8, 2026.
- Why did my Sendblue line get blocked?, Sendblue Support. Last updated April 12, 2026; checked October 8, 2026.
- Webhooks, Sendblue docs. Checked October 8, 2026.
- Phone Reputation, Linq docs. Checked October 8, 2026.
- Messaging, Project Blue. Checked October 8, 2026.
- Acceptable Use Policy, Blooio. Checked October 8, 2026.
- Refund policy, LoopMessage Helpdesk. Checked October 8, 2026.
- What Happens If iMessage Gets Blocked?, myCRMSIM Support Center. Checked October 8, 2026.
Changelog
- Updated for current limits: Pro allows 5 new contacts a day, and the two-unanswered-messages rule applies on every plan.
- First published. Refreshed quarterly, and after any incident or new data.