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.Documentation Index
Fetch the complete documentation index at: https://docs.photon.codes/docs/llms.txt
Use this file to discover all available pages before exploring further.
Inbound-first is the decision that matters
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. How to make inbound-first work in practice:- 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. - Share a contact card early. Push a native iMessage contact card (or a
.VCFfor Android) shortly after the first exchange. Once they save it, you’re a known contact and “Report Junk” is gone for good.
Capacity
Two quotas govern how much traffic a deployment can carry. They’re enforced limits.| Limit | Guidance |
|---|---|
| 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 for an increase. |
| 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. |
What gets a line flagged
Because Apple filters on behavior, the same five patterns account for nearly every flag we see:- Burst sending: 100+ messages from one line in a tight window
- No conversation: broadcasting without exchange
- Hammering non-responders: more than 2–3 follow-ups
- Cold outreach: texting people who never opted in
- Off-hours sending: 3am messages signal automation
Do
- Design for inbound-first. Users text you, not the other way around.
- Pace messages naturally. Don’t fire several within seconds. Bursts of 100/min look automated because they are.
- Make outreach conversational. If you need to push an update (digest, accountability ping), open with a question and wait: “Ready for your update?”
- Share a contact card after the first exchange. Once saved, the “Report Junk” surface is gone.
- Round-robin new users across lines. Spread load before any one line stands out.
- Watch the dashboard. When a line goes Flagged, review what the agent was doing in the hour before — the cause is almost always there.
Don’t
- Push past the per-server quota. Add capacity; horizontal scaling is the design.
- 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.
- Leave fallback lines dormant. Apple deactivates lines with no traffic for ~2 months. Every line you keep around needs some traffic.
- Bombard non-responders. Cap at 2–3 follow-ups, spaced across days, not hours.
- Segment Android users onto separate lines. Spread them through the pool — they may be your power users.
- Use iMessage for cold outreach. Cold belongs on A2P channels (Twilio etc.). iMessage is for warm conversations.