send-message delivers one message to one recipient. It tries iMessage first, falls back to RCS, then to SMS, and returns immediately with a message id you can track. Use it for one-off sends, CRM-triggered follow-ups, and anything an agent or workflow fires in real time.
The call is asynchronous. A 200-class response means BlueReacher accepted the message and queued it, not that the recipient has it. Delivery state arrives on your webhook.
Endpoint
Per-line send pacing is handled for you. Queue as fast as the rate limit allows and BlueReacher spaces the actual sends to protect your line.
Authentication
Every request carries a bearer token:bluereacher_ and are scoped to one workspace. Create and rotate them in the dashboard under Settings, API keys. Send requests from your server only, never from a browser or mobile client. A missing, malformed, or revoked key returns 401.
Request fields
Unknown fields are rejected with
400 rather than ignored, so a typo in a field name fails loudly instead of silently dropping data.
Response fields
Example request
Example response
202 Accepted
Idempotency with external_id
Setexternal_id on every send. It is your key for two jobs at once: matching messages back to your own records, and making retries safe.
How it behaves:
- The first request with a given
external_idcreates the message and returns202with a newid. - Any later request with the same
external_idin the same workspace returns the original message body with200and the headerIdempotent-Replay: true. Nothing is sent twice. - Replaying an
external_idwith a different request body returns409 idempotency_conflict. Nothing is sent, and the original message is untouched. - Keys are held for 30 days from first use. After that, the same value creates a new message.
external_idcomes back on every webhook for the message, so you can thread replies and delivery events onto your record without storing the BlueReacherid.
crm_88213-step2. Bad values are anything reused across contacts, like the campaign name.
Retry 5xx responses, 429, and network timeouts with the exact same body and the same external_id. Use exponential backoff. If the original request landed before the connection dropped, the retry returns the existing message instead of sending a duplicate.
Channel selection and fallback
BlueReacher checks what the recipient can actually receive at send time, then delivers on the best available channel.
The
channel field sets where that chain starts. imessage is the default and runs the full chain. rcs skips the iMessage attempt and starts at RCS. sms goes straight to SMS. Fallback always continues downward from wherever you start, so channel narrows the options but never locks the message to one path.
Things to know about fallback:
- The decision happens at send time, not when you call the API. For a scheduled message, the channel can differ from the
channelvalue in the response if the recipient’s situation changed in between. - The
channelin the API response is the intended channel. Themessage.sentwebhook carries the channel that was actually used. Report on the webhook value. - Read receipts exist on iMessage. On RCS and SMS you get delivery state only, and SMS delivery depends on what the carrier reports back.
- Media is re-encoded for the channel it lands on. See Character and media limits.
- Long messages split into segments on the SMS path. iMessage and RCS send them whole.
- iMessage sends are unlimited on your line. RCS and SMS fallback traffic is metered per message and shows up on your workspace usage page.
- The iMessage path has no A2P registration required. SMS fallback travels carrier networks, so carrier rules apply to that traffic. SMS fallback is off by default on new workspaces and your account manager turns it on.
- To avoid fallback entirely, run the number through the availability check first and only send to iMessage-capable numbers.
403 recipient_opted_out.
Character and media limits
Text
Over 2,000 characters returns
422 content_too_long with the actual count in the error message. On SMS fallback, text is split into standard segments (160 characters GSM-7, 70 characters when the body contains non-GSM characters such as emoji) and billed per segment. Keep cold outreach under 320 characters and the whole thing stays cheap on every channel.
Media
Supported types:
image/jpeg, image/png, image/gif, image/heic, image/webp, video/mp4, video/quicktime, audio/mpeg, audio/mp4, application/pdf. Anything else returns 422 unsupported_media_type.
How media is handled:
- Files are fetched server-side at send time. The URL has to be publicly reachable with no auth header. Signed URLs need to stay valid through the send, which for a scheduled message means valid at
scheduled_at, not at request time. contentsends first, then attachments in array order, all in the same thread under one messageid.- On SMS fallback, attachments become MMS. Files over 1 MB are compressed to fit carrier limits. Anything that cannot be compressed under the cap, such as a PDF, is replaced with a secure link in the message body that stays live for 7 days.
- A URL that returns a non-200, times out, or exceeds the size cap returns
422 media_fetch_failedand nothing is sent. Nothing partially delivers.
Scheduling
Setscheduled_at to a future ISO 8601 timestamp and the message holds in the queue until then. status comes back as scheduled instead of queued.
- Window: at least 60 seconds and no more than 90 days ahead. Outside that returns
422 invalid_scheduled_at. - Include an offset (
-05:00) orZ. A timestamp with no timezone is rejected. - Scheduled sends respect the sending window on the line. If the time lands inside a quiet window, the message goes out at the start of the next allowed window.
- Cancel a scheduled message before it fires from the Messages view in the dashboard.
- The idempotency rules are identical. A retried schedule request with the same
external_idreturns the existing scheduled message rather than queuing a second one.
Status values
Only
scheduled and queued ever come back from this endpoint. Everything after that arrives on your webhook.
Errors
Errors return a singleerror object:
Retry
429, 500, and 503 with the same external_id. Every 4xx other than 429 fails the same way on retry, so fix the request instead.
Next steps
- Check whether a number can receive iMessage before you spend a send: Check availability
- Catch replies, delivery states, and opt-outs: Webhooks
- Background on the platform and what the API covers: iMessage API guide