|
| 1 | +--- |
| 2 | +title: iMessage deliverability |
| 3 | +description: Designing messaging flows that don't get a line flagged by Apple's filtering. |
| 4 | +--- |
| 5 | + |
| 6 | +iMessage is end-to-end encrypted, so Apple can't read message content: they filter on behavior. Patterns that look automated, cold, or burst-y will get a line flagged regardless of what the messages actually say. The guidance below is what we see work and fail in production. |
| 7 | + |
| 8 | +## Inbound-first is the decision that matters |
| 9 | + |
| 10 | +The single most important design call is whether users text you first or you text users. Inbound-first integrations never surface the "Report Junk" banner that Apple shows on every message from an unknown number. Outbound-first integrations do, and after a couple of unanswered messages it tends to get tapped. |
| 11 | + |
| 12 | +How to make inbound-first work in practice: |
| 13 | + |
| 14 | +- **Pre-populate the first message.** Ship `sms:+1...&body=Hey!` deep links so tapping opens Messages with text pre-filled. Zero friction, and the user is the one who hits send. |
| 15 | +- **Share a contact card early.** Push a native iMessage contact card (or a `.VCF` for Android) shortly after the first exchange. Once they save it, you're a known contact and "Report Junk" is gone for good. |
| 16 | + |
| 17 | +## Capacity |
| 18 | + |
| 19 | +Two quotas govern how much traffic a deployment can carry. They're enforced limits. |
| 20 | + |
| 21 | +| Limit | Guidance | |
| 22 | +|---|---| |
| 23 | +| **5,000 messages per server per day** | Counts every send across all chats on a server. Past this, sends are rejected until the window resets. Email [help@photon.codes](mailto:help@photon.codes) for an increase. | |
| 24 | +| **50 new conversations per line per day** | A "new conversation" is the first message a line sends to a recipient it has never messaged before. Replies within existing conversations don't count. Most relevant if you're initiating outbound. | |
| 25 | + |
| 26 | +When a server hits 70-80% utilization, stop assigning new users to it. When the whole pool gets there, add capacity. On the Business plan, [auto-scale](/spectrum-ts/providers/imessage#auto-scale) handles the second step automatically. |
| 27 | + |
| 28 | +### What gets a line flagged |
| 29 | + |
| 30 | +Because Apple filters on behavior, the same five patterns account for nearly every flag we see: |
| 31 | + |
| 32 | +1. **Burst sending**: 100+ messages from one line in a tight window |
| 33 | +2. **No conversation**: broadcasting without exchange |
| 34 | +3. **Hammering non-responders**: more than 2–3 follow-ups |
| 35 | +4. **Cold outreach**: texting people who never opted in |
| 36 | +5. **Off-hours sending**: 3am messages signal automation |
| 37 | + |
| 38 | +Avoid all five and blocks are rare. When a line does get flagged, the cause is almost always one of the first three within the hour before. |
| 39 | + |
| 40 | +## Do |
| 41 | + |
| 42 | +- **Design for inbound-first.** Users text you, not the other way around. |
| 43 | +- **Pace messages naturally.** Don't fire several within seconds. Bursts of 100/min look automated because they are. |
| 44 | +- **Make outreach conversational.** If you need to push an update (digest, accountability ping), open with a question and wait: "Ready for your update?" |
| 45 | +- **Share a contact card after the first exchange.** Once saved, the "Report Junk" surface is gone. |
| 46 | +- **Round-robin new users across lines.** Spread load before any one line stands out. |
| 47 | +- **Watch the dashboard.** When a line goes Flagged, review what the agent was doing in the hour before — the cause is almost always there. |
| 48 | + |
| 49 | +## Don't |
| 50 | + |
| 51 | +- **Push past the per-server quota.** Add capacity; horizontal scaling is the design. |
| 52 | +- **Include links or media in the first message.** Apple suppresses link-clicking until a reply lands. Ship a text-only opener built to get a response. |
| 53 | +- **Leave fallback lines dormant.** Apple deactivates lines with no traffic for ~2 months. Every line you keep around needs some traffic. |
| 54 | +- **Bombard non-responders.** Cap at 2–3 follow-ups, spaced across days, not hours. |
| 55 | +- **Segment Android users onto separate lines.** Spread them through the pool — they may be your power users. |
| 56 | +- **Use iMessage for cold outreach.** Cold belongs on A2P channels (Twilio etc.). iMessage is for warm conversations. |
| 57 | + |
| 58 | +## Privacy |
| 59 | + |
| 60 | +You don't need to store phone numbers in your own systems. Spectrum's space IDs are stable identifiers. Use them as your primary key and call the API to resolve a phone number when you actually need one. The mapping stays on our side. |
| 61 | + |
| 62 | +## Recovering from an outage |
| 63 | + |
| 64 | +In rare cases, if Spectrum Cloud is degraded and you're missing webhook deliveries: |
| 65 | + |
| 66 | +1. **Pull from the API.** Chat history is available via the chat endpoint as long as you haven't deleted it. |
| 67 | +2. **Let retries do their job.** We retry failed webhooks up to 10 times over 2 hours. |
| 68 | +3. **Reach out.** If you've lost events outside the retry window, contact support — we can replay them manually. |
| 69 | + |
| 70 | +## Getting help |
| 71 | + |
| 72 | +If you're scaling past a handful of lines, talk to us. We do capacity planning with customers, surface per-line analytics in the dashboard, and run a shared Slack or Discord channel with engineering for production deployments. |
0 commit comments