Key concepts
The object model and the ideas the whole API hangs off: lines, contacts, conversations, drip vs instant, engaged vs cold, and test mode.
Five objects and three ideas explain the whole API.
The objects
| Object | What it is | Where it lives |
|---|---|---|
| Line | One managed iMessage sending number. Called device in API field names | GET /v1/devices |
| Contact | A person, keyed by E.164 phone number, carrying opt-out state, tags and custom fields | /v1/contacts |
| Message | One send or receive. Statuses commonly pending, sent, failed; delivery channel imessage or sms | POST /v1/messages, GET /v1/status/{id} |
| Conversation | The thread between a line and a contact (or a group), with reactions and channel per message | GET /v1/conversations |
| Group | A group iMessage thread owned by one line | GET /v1/groups |
Ids are opaque strings; do not parse them for meaning. Sandbox ids are prefixed sim_ so simulated traffic can never be mistaken for real.
Idea one: two sending lanes
Every send picks a lane, and the default is the protected one.
Drip is for outreach. The platform schedules the message about 10 minutes out, delivers it inside the org-local send window, spaces it against everything else the line is doing, and counts it against capacity. You hand over what; the platform owns exactly when.
Instant is for conversation. It dispatches now, velocity-capped at 10/min and 75/day per key, and it is the only lane that carries group sends and message effects. Use it when a human (or your agent) is mid-thread and a 10-minute delay would be absurd.
The lane split is why an integration built on this API is hard to burn: bulk pressure physically cannot bypass the pacing machinery.
Idea two: engaged is unlimited, cold is capped
The capacity model counts one thing: brand-new cold contacts per line per day. A contact who has ever replied is engaged and unlimited. A contact you have messaged before is returning and unlimited. Inbound never counts. So the economics of the channel reward exactly what you want anyway: winning replies. The full model, the 50 opted-in / 30 cold policy caps, warm-up and quiet hours are in Rate limits and sending capacity.
Idea three: events over polling
Delivery is asynchronous by nature. A successful send means accepted, and what happened next arrives as signed webhooks: message.sent (the definitive "it left the line"), message.delivered / message.read (when receipts exist), message.failed, and message.received for replies. Poll GET /v1/status/{id} when you must; subscribe when you can. Deduplicate on event_id, verify signatures over raw bytes. Details in Webhooks.
Test mode
brk_test_ keys run the whole messaging surface against a simulator: same validation, same shapes, zero real sends, "sandbox": true on every response plus an X-BlueReacher-Sandbox: true header. /v1/contacts and /v1/capability answer from the simulator too, and /v1/usage returns a fixed sample, so nothing a test key does can reach a real workspace. Build the integration on a test key, swap the key at go-live, change nothing else.
Opt-out is enforced platform-side
STOP suppresses the contact everywhere, fires contact.opted_out, and no send path (API, campaign, workflow) can reach them afterwards. You never parse STOP yourself, and re-subscribing deliberately requires an explicit confirmation flag. Your side of the contract: mirror the suppression in your own system when the webhook arrives.

