Skip to content

Commit 537bff1

Browse files
committed
add docs for imessage-deliverability
1 parent 5fe34a0 commit 537bff1

3 files changed

Lines changed: 83 additions & 1 deletion

File tree

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
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.

docs-src/spectrum-ts/providers/imessage.mdx.vel

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,15 @@ When traffic to a dedicated line approaches its per-line capacity, Spectrum can
7575
These are managed Spectrum Cloud features. If you're on the open-source path (`imessage.config({ local: true })` or your own dedicated relay), you provide your own iCloud account and managed-line concepts don't apply.
7676
</Note>
7777

78+
## Quotas
79+
80+
<Warning>
81+
Default per-server and per-line quotas apply. Contact [help@photon.codes](mailto:help@photon.codes) for an increase.
82+
83+
- **5,000 messages per server per day.** Counts every message your instance sends across all chats. Additional sends are rejected until the window resets.
84+
- **50 new conversations initiated per line per day.** A "new conversation" is the first message your line sends to a recipient it has never messaged before. Replies within existing conversations don't count.
85+
</Warning>
86+
7887
## Space types
7988

8089
iMessage spaces carry a `type` field — `"dm"` or `"group"` — and a `phone` field indicating which phone number the conversation is routed through. Both are accessible through narrowing:

docs.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@
5757
"pages": [
5858
"best-practices/architecture",
5959
"best-practices/inbound-pipeline",
60-
"best-practices/recovery-and-state"
60+
"best-practices/recovery-and-state",
61+
"best-practices/imessage-deliverability"
6162
]
6263
}
6364
]

0 commit comments

Comments
 (0)