Messages
Unified send + list across every line. The `channel` field picks the pipeline.
Unified send + list across every line. The channel field picks the pipeline.
Every route takes Authorization: Bearer <ACCESS_TOKEN> unless the auth line says otherwise. Ids are numbers. Errors use the shape on the errors page.
| Route | Scope | What it does |
|---|---|---|
GET /v1/messages | messages.read | List messages across any combination of channels — inbound + outbound — with filtering. |
POST /v1/messages | messages.write | Send an outbound message to a contact or a phone number, on any channel. |
POST /v1/messages/{id}/reactions | messages.write | Send an iMessage reaction (tapback) — ❤️ 👍 👎 😂 ‼️ ❓ — to a message in a contact's conversation. |
POST /v1/messages/{id}/edit | messages.write | Edit the text of an iMessage you sent, within 15 minutes — the recipient sees it as edited. |
POST /v1/messages/{id}/unsend | messages.write | Unsend an iMessage you sent, within 2 minutes — it disappears from the recipient's conversation. |
POST /v1/messages/{id}/notify-anyway | messages.write | Break through the recipient's Focus for an iMessage you sent, within 24 hours (Apple's Notify Anyway). |
GET /v1/messages/{id} | messages.read | Check the delivery status of a message by its id (the message_id returned from Send message). |
POST /v1/messages/scheduled | messages.write | Send a message later. |
GET /v1/messages/scheduled | messages.read | A workspace's scheduled messages. |
DELETE /v1/messages/scheduled/{id} | messages.write | Cancel a message that has not been sent yet. |
List messages
GET https://api.bluereacher.com/v1/messagesAuth: Bearer access token. Scope: messages.read.
List messages across any combination of channels — inbound + outbound — with filtering.
Returns the messages in the installer's workspaces, every channel in one list, from the same conversation records the app.bluereacher.com Conversations page shows. All filters are optional and combine with AND semantics.
One id everywhere. A row's id (and message_id, the same value as a string) is the message id the other message endpoints take: GET /messages/{id} for its status, POST /messages/{id}/reactions to react to it, and the id in the conversation thread. That holds for inbound messages too.
Order. One list across channels, ordered by created_at (newest first by default). breakdown counts the matching messages per channel; total is their sum.
Without workspace_id, every workspace the installer owns is listed.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
channel | query | string | no | Restrict to one or more channels. Omit for all. |
workspace_id | query | number | no | Filter to one workspace. Must belong to the installer. |
phone | query | string | no | Filter to messages exchanged with this phone number (E.164), or iMessage email address for iMessage — every channel. |
status | query | string | no | Single value or comma-separated list of statuses. Case-insensitive — ?status=Sent, ?status=SENT and ?status=sent all match. Values: "queued", "webhook_sent", "accepted", "sent", "delivered", "read", "failed", "received", "unsent". |
page | query | number | no | Page number (>= 1). Default 1. Anything else is a 400. |
limit | query | number | no | Page size (1-100). Default 25. Anything else is a 400. |
sort | query | enum | no | Sort by created_at — "created_at_asc" or "created_at_desc". Default "created_at_desc". |
Request
curl -X GET "https://api.bluereacher.com/v1/messages?channel=sms%2Cimessage&workspace_id=7&phone=%2B15551234567" \
-H "Authorization: Bearer <ACCESS_TOKEN>"Responses
A 200 reply (OK — interleaved across channels, newest first):
{
"success": true,
"data": {
"messages": [
{
"id": 84220,
"message_id": "84220",
"channel": "imessage",
"workspace_id": 7,
"contact_id": 1234,
"direction": "inbound",
"to": null,
"from": "+15551234567",
"is_group": false,
"text": "❤️ Loved: “Here is the brochure.”",
"effect": null,
"reaction": {
"kind": "love",
"emoji": "❤️",
"snippet": "Here is the brochure."
},
"media": null,
"status": "received",
"error_message": null,
"created_at": "2026-06-09T14:24:00.000Z",
"updated_at": "2026-06-09T14:24:00.000Z"
}
],
"total": 87,
"page": 1,
"limit": 25,
"breakdown": {
"sms": 32,
"imessage": 19
}
}
}| Status | When | Message |
|---|---|---|
| 200 | OK — interleaved across channels, newest first | |
| 400 | Validation — bad sort key | sort must be created_at_asc or created_at_desc. |
| 401 | Missing or invalid token | Invalid or expired access token. |
| 403 | Missing scope | Missing scope(s): messages.read. |
| 404 | workspace_id is not accessible to this token | Workspace not found or not accessible to this token |
| 500 | Unexpected server error | Failed to list messages. |
Response fields
messages: Array of messages, normalized across channels. Fields:
| Field | Type | Description |
|---|---|---|
id | number | The message id. The same id works with Get message status, React to message, and in the conversation thread. Example: 84213 |
message_id | string | The same id as a string — the message_id a Send message response returns. Example: 84213 |
channel | enum | Example: imessage |
workspace_id | number | The workspace this message belongs to. Example: 7 |
contact_id | number, nullable | The contact the message was sent to / received from. Null for group messages. Example: 1234 |
direction | enum | "outbound" (sent from the workspace) or "inbound" (received). Example: outbound |
to | string, nullable | Outbound: the contact's phone (E.164), or iMessage email address for iMessage. Null for inbound messages. Example: +15551234567 |
from | string, nullable | Inbound: the contact's phone or iMessage email address — in a group, the member who wrote it. Null for outbound messages. |
is_group | boolean | True for a message in a group conversation (iMessage group). Example: false |
text | string (text), nullable | The message body. May be null when only media was sent. |
effect | string, nullable | The iMessage send effect on the message (either direction), e.g. "confetti" or "slam"; null when there is none. The text then starts with a "✦ Effect: <name>" line. |
reaction | object, nullable | Set when the message is an iMessage reaction (tapback) to another message, in either direction: { kind, emoji, snippet }. kind is love | like | dislike | laugh | emphasize | question, or "emoji" for a custom-emoji reaction from a contact; snippet is the start of the message reacted to. Null for any other message. The text is the reaction line, e.g. "❤️ Loved: “See you at 5?”". |
media | array<{url,type}>, nullable | Media attachments, or null when there are none. |
status | string, nullable | queued | webhook_sent | accepted | sent | delivered | read | failed | received | unsent. |
error_message | string, nullable | Why a failed message failed; null otherwise. |
created_at | string (ISO 8601) | When the message was created. |
updated_at | string (ISO 8601), nullable | When the message was last updated. |
Send message
POST https://api.bluereacher.com/v1/messagesAuth: Bearer access token. Scope: messages.write.
Limits: 30 messages / 15 min per workspace · 1 attachment per message, 5 MB max
Send an outbound message to a contact or a phone number, on any channel.
Before you can send: on app.bluereacher.com → Devices, add a device (iMessage) and assign it to the workspace. A workspace with no assigned device for an eligible channel records the message but does not deliver it.
Supply contact_id or phone (with country code, e.g. +61 / +1 — a contact is auto-created if none matches), plus message and/or attachments — a single media URL (attachment-only sends are valid).
iMessage to an email address: iMessage also reaches an iMessage email address. Send it as email, or put the email in phone. If no contact has that email, an email-only contact is auto-created, the same as for a new phone number. A contact that has a phone number is always messaged at the phone; an email-only contact (by contact_id too) is messaged at its email over iMessage.
Omit channel (or send default) to use the workspace Message Routing (Primary → Fallback; same-row channels in parallel), or force one with channel = imessage. The send is recorded in the conversation / Message Logs.
mp3, m4a/aac, ogg/opus and wav work (mp3 is the safe choice), one file up to 5 MB at a public https:// URL that answers a HEAD request with Content-Length and an audio/* content type. message is optional and becomes the caption or an accompanying text. Delivery is asynchronous as for any send; replies and reactions come back as ordinary inbound messages.
Send effects (iMessage): add effect to send the message with an iMessage effect — a full-screen one (confetti, fireworks, balloons, hearts, lasers, shooting_star, sparkles, echo, spotlight) or a bubble one (slam, loud, gentle, invisible_ink). An effect sends over iMessage (leave channel out or send imessage; another channel is a 400), needs message text, and needs a line with Advanced Mode on in the workspace — the same rule as the effect button on the dashboard; without one the request is a 400 NO_ADVANCED_MODE_LINE. In message reads the effect is the effect field (also on messages a contact sends with an effect). The stored text keeps the effect as its first line, ✦ Effect: confetti, as the dashboard writes it; the recipient sees only the body.
Threaded replies (iMessage): add reply_to with a message id to answer that message the way the reply button on the app.bluereacher.com does. The message is sent with a leading line, ↩︎ Replying to “<the first 60 characters of that message>” (“an attachment” for a photo or file), which a line in Advanced Mode turns into a real threaded reply; the contact sees only your text. Leave the recipient out and the reply goes to that message's contact. Replies are iMessage only, need message text, can't answer a group message or a reaction, and need a line with Advanced Mode on (else 400 NO_ADVANCED_MODE_LINE). With effect too, the effect line comes first. The line finds the target by its quoted text, so if two messages in the thread start the same way, the reply attaches to the one Messages picks.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
workspace_id | body | number | yes | The workspace to send from. Must belong to the installer. Its Message Routing decides the channel(s) + device when channel is omitted. |
contact_id | body | number | no | The contact to send to (must exist in the workspace). Provide this, phone or email — at least one is required. |
phone | body | string | no | Recipient phone with country code (e.g. +61…, +1…). Provide this, contact_id or email. If no contact with this number exists in the workspace, one is auto-created and the message is sent to it. iMessage only: an iMessage email address is accepted here too, and is treated as email. |
email | body | string | no | iMessage only. An iMessage email address to message over iMessage, when there is no contact_id or phone. Another channel is a 400. If no contact has this email, an email-only contact is auto-created. |
channel | body | enum | no | (type is accepted as an alias.) |
message | body | string (text) | no | The message body. Required unless attachments is provided. text is accepted as a legacy alias. |
attachments | body | string (URL) | no | Optional media as a single URL string — 1 attachment per message, file size up to 5 MB. Send with or instead of message; an attachment-only send is valid. Sending an array with more than one URL is rejected with a 400. (media is accepted as a legacy alias for this field.) |
effect | body | enum | no | An iMessage send effect. Screen effects: confetti, fireworks, balloons, hearts, lasers, shooting_star, sparkles, echo, spotlight. Bubble effects: slam, loud, gentle, invisible_ink. iMessage only, needs message text and a line in Advanced Mode on the workspace — see the description. |
reply_to | body | number | no | Send the message as an iMessage threaded reply to this message id (a List messages / conversation message id, or a Send message message_id). iMessage only, needs message text and a line in Advanced Mode on the workspace. With no recipient, the reply goes to that message's contact; a different recipient is a 400. |
Request
curl -X POST "https://api.bluereacher.com/v1/messages" \
-H "Authorization: Bearer <ACCESS_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"workspace_id": 7,
"phone": "+61412345678",
"channel": "default",
"message": "Thanks for your enquiry — we open at 09:00.",
"attachments": ""
}'Responses
A 200 reply (OK — queued for delivery):
{
"success": true,
"message": "Message queued for delivery.",
"data": {
"id": 84213,
"status": "webhook_sent",
"message_id": "84213",
"conversation_id": 512,
"channel": "imessage",
"queued_at": "2026-07-20T10:22:07.000Z"
}
}| Status | When | Message |
|---|---|---|
| 200 | OK — queued for delivery | |
| 400 | Validation — no recipient (need contact_id, phone or email) | Provide contact_id, phone or email. |
| 400 | An email recipient with a channel other than iMessage | Email addresses can only be messaged over iMessage. |
| 400 | Validation — malformed email | email must be a valid email address. |
| 400 | Validation — neither message nor attachments supplied | message or attachments is required. |
| 400 | Validation — more than one attachment supplied | Only one attachment is allowed per message (max 5 MB). |
| 400 | Effect — not iMessage | Effects are iMessage only: send channel imessage (or leave channel out). |
| 400 | Effect — no line with Advanced Mode on the workspace | No line with Advanced Mode is connected to this workspace. Send effects need a line with Advanced Mode on. |
| 400 | Effect — unknown name, or no message text | effect must be one of: confetti, fireworks, balloons, hearts, lasers, shooting_star, sparkles, echo, spotlight, slam, loud, gentle, invisible_ink |
| 400 | Reply — not iMessage, a group message, a reaction, or nothing to reply to | Replies are iMessage only: the reply_to message was not sent over iMessage. |
| 400 | Reply — the recipient is not the reply_to message's contact | reply_to is a message in another conversation: a reply goes to the same contact. |
| 400 | Reply — no line with Advanced Mode on the workspace | No line with Advanced Mode is connected to this workspace. Replies need a line with Advanced Mode on. |
| 404 | Reply — reply_to is not a message in this workspace | reply_to message not found |
| 401 | Missing or invalid token | Invalid or expired access token. |
| 403 | Missing scope | Missing scope(s): messages.write. This integration was granted: … |
| 404 | workspace / contact not found or not accessible to this token | Contact not found in this workspace |
| 409 | Recipient has outbound Do-Not-Disturb active | Cannot send message as DND is active |
| 429 | Rate limit exceeded — too many queued sends for this workspace (30 messages / 15 min) | Too many queued messages for this workspace |
| 502 | The message could not be delivered to the messaging provider — retryable | Failed to forward message to the gateway. |
| 503 | Message sending is not available for this account | Outbound gateway is not configured. |
| 500 | Unexpected server error | Failed to queue message for delivery. |
Response fields
send_result: The queued outbound message. Fields:
| Field | Type | Description |
|---|---|---|
id | number | The id of the queued message — use it to correlate delivery-status callbacks. Example: 84213 |
status | string | Delivery status immediately after the send is accepted — typically "webhook_sent", or "failed". Example: webhook_sent |
message_id | string | String form of the message id — use it to correlate delivery-status callbacks / webhook events. Example: 84213 |
conversation_id | number, nullable | The conversation this message belongs to, when known. Example: 512 |
channel | string, nullable | The forced channel, or null when the workspace routing decided it. Example: imessage |
queued_at | string (ISO 8601) | When the message row was created / queued. Example: 2026-07-20T10:22:07.000Z |
reply_to | string | Only on a threaded reply: the id of the message replied to. Example: 84214 |
React to message
POST https://api.bluereacher.com/v1/messages/{id}/reactionsAuth: Bearer access token. Scope: messages.write.
Limits: 30 messages / 15 min per workspace (counts with Send message)
Send an iMessage reaction (tapback) — ❤️ 👍 👎 😂 ‼️ ❓ — to a message in a contact's conversation.
Reacts to a message the way the reaction bar on the app.bluereacher.com conversation does. {id} is the message's id: the id of a row from List messages or List conversation messages, or the message_id from Send message. Messages you sent and messages the contact sent can both be reacted to.
How it is sent: a reaction is an iMessage whose text is one line, ❤️ Loved: “<the message reacted to>”. The quote is the start of the target's text — up to 60 characters, spaces and line breaks collapsed, with "…" when it is cut. A line in Advanced Mode turns that line into a real tapback on the quoted message; the contact never sees the text. It goes to the message's contact through the same pipeline as Send message, so DND, credits and the send rate limit apply as for any message, and the response is the same.
Rules (the same as the dashboard's):
- The message must be an iMessage with text, in a one-to-one conversation.
The line finds the target by its quoted text, so when the conversation holds two messages that start the same way, the tapback lands on the one Messages picks.
In message reads a reaction is the reaction field — { kind, emoji, snippet } — on reactions you send and on the contact's, which also carry custom emoji (kind: "emoji").
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | number | yes | The id of the message to react to — a List messages or conversation message id, or the message_id from Send message. |
workspace_id | body | number | yes | The workspace the message belongs to. |
reaction | body | enum | yes | love ❤️, like 👍, dislike 👎, laugh 😂, emphasize ‼️ or question ❓. |
Request
curl -X POST "https://api.bluereacher.com/v1/messages/84214/reactions" \
-H "Authorization: Bearer <ACCESS_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"workspace_id": 7,
"reaction": "love"
}'Responses
A 200 reply (OK — reaction queued for delivery):
{
"success": true,
"message": "Message queued for delivery.",
"data": {
"id": 84230,
"status": "webhook_sent",
"message_id": "84230",
"conversation_id": 512,
"channel": "imessage",
"queued_at": "2026-10-27T09:16:00.000Z",
"reaction": "love",
"reacted_to_message_id": "84214"
}
}| Status | When | Message |
|---|---|---|
| 200 | OK — reaction queued for delivery | |
| 400 | Validation — unknown reaction | reaction must be one of: love, like, dislike, laugh, emphasize, question |
| 400 | Validation — no workspace_id | workspace_id is required |
| 400 | The message is not an iMessage | Reactions are iMessage only: this message was not sent over iMessage. |
| 400 | The message has no text (attachment only) | This message has no text to react to. |
| 400 | The message is itself a reaction | A reaction can't be reacted to. |
| 400 | The message is in a group conversation | Reactions to group messages are not supported. |
| 400 | No line with Advanced Mode on the workspace | No line with Advanced Mode is connected to this workspace. Reactions need a line with Advanced Mode on. |
| 401 | Missing or invalid token | Invalid or expired access token. |
| 403 | Missing scope | Missing scope(s): messages.write. This integration was granted: … |
| 404 | Message not found in this workspace (or not accessible to this token) | Message not found |
| 409 | The contact has outbound Do-Not-Disturb active | Cannot send message as DND is active |
| 429 | Rate limit exceeded (30 messages / 15 min per workspace) | Too many queued messages for this workspace |
| 502 | The reaction could not be delivered to the messaging provider — retryable | Failed to forward message to the gateway. |
Response fields
send_result: The queued reaction message — the Send message result, plus what it reacted to. Fields:
| Field | Type | Description |
|---|---|---|
id | number | The reaction message's id. Use it with Get message status. Example: 84230 |
status | string | Delivery status immediately after the send is accepted — typically "webhook_sent", or "failed". Example: webhook_sent |
message_id | string | String form of the reaction message's id. Example: 84230 |
channel | string | Always "imessage". Example: imessage |
reaction | string | The reaction sent. Example: love |
reacted_to_message_id | string | The id of the message reacted to. Example: 84214 |
Edit message
POST https://api.bluereacher.com/v1/messages/{id}/editAuth: Bearer access token. Scope: messages.write.
Edit the text of an iMessage you sent, within 15 minutes — the recipient sees it as edited.
Edits a sent iMessage the way the edit button on the app.bluereacher.com conversation does: the line that sent it edits it in Messages, and the recipient sees the new text marked Edited. Apple allows edits within 15 minutes of sending (and up to five edits).
Send the new body in message. If the message was sent with an effect, a subject or as a threaded reply, those stay as they were; only the body changes. The edited text is what message reads return afterwards.
Rules (the same as the dashboard's): the message must be one you sent, over iMessage, with text, sent from a line, and not itself a reaction; the workspace needs a line with Advanced Mode on (else 400 NO_ADVANCED_MODE_LINE). The request waits for the line (up to 15 s): 422 LINE_REFUSED when Messages refused, 503 LINE_UNREACHABLE when the line is offline, 504 LINE_TIMEOUT when it did not answer in time — the change may still land, so read the message again before retrying.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | number | yes | The id of the message — a List messages or conversation message id, or the message_id from Send message. |
workspace_id | body | number | yes | The workspace the message belongs to. |
message | body | string (text) | yes | The new text. text is accepted as an alias. |
Request
curl -X POST "https://api.bluereacher.com/v1/messages/84213/edit" \
-H "Authorization: Bearer <ACCESS_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"workspace_id": 7,
"message": "Booking confirmed for 5 PM."
}'Responses
A 200 reply (OK — done on the line):
{
"success": true,
"message": "Message edited",
"data": {
"id": 84213,
"message_id": "84213",
"channel": "imessage",
"workspace_id": 7,
"contact_id": 1234,
"direction": "outbound",
"to": "+61412345678",
"from": null,
"is_group": false,
"text": "Booking confirmed for 5 PM.",
"effect": null,
"reaction": null,
"media": null,
"status": "delivered",
"error_message": null,
"created_at": "2026-10-29T09:15:58.000Z",
"updated_at": "2026-10-29T09:20:03.000Z",
"edited_at": "2026-10-29T09:20:03.000Z"
}
}| Status | When | Message |
|---|---|---|
| 200 | OK — done on the line | |
| 400 | Outside Apple's window | Messages can only be edited within 15 minutes of sending |
| 400 | Not an iMessage you sent with text | Only messages you sent can be changed |
| 400 | No line with Advanced Mode on the workspace | No line with Advanced Mode is connected to this workspace. Edit, unsend and Notify Anyway need a line with Advanced Mode on. |
| 401 | Missing or invalid token | Invalid or expired access token. |
| 403 | Missing scope | Missing scope: one of messages.send, messages.write. This integration was granted: … |
| 404 | Message not found in this workspace (or not accessible to this token) | Message not found |
| 422 | Messages on the line refused the change | The line couldn't edit the message |
| 503 | The line that sent the message is not connected | The line isn't connected right now |
| 504 | The line did not answer within 15 s — the change may still land; read the message again | The line didn't answer in time. Check the thread in a moment. |
Unsend message
POST https://api.bluereacher.com/v1/messages/{id}/unsendAuth: Bearer access token. Scope: messages.write.
Unsend an iMessage you sent, within 2 minutes — it disappears from the recipient's conversation.
Unsends a sent iMessage the way the unsend button on the app.bluereacher.com conversation does. Apple allows unsending within 2 minutes of sending. The message's status becomes unsent afterwards.
A recipient on an older iOS / macOS may still keep the original.
Rules (the same as the dashboard's): the message must be one you sent, over iMessage, with text, sent from a line, and not itself a reaction; the workspace needs a line with Advanced Mode on (else 400 NO_ADVANCED_MODE_LINE). The request waits for the line (up to 15 s): 422 LINE_REFUSED when Messages refused, 503 LINE_UNREACHABLE when the line is offline, 504 LINE_TIMEOUT when it did not answer in time — the change may still land, so read the message again before retrying.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | number | yes | The id of the message — a List messages or conversation message id, or the message_id from Send message. |
workspace_id | body | number | yes | The workspace the message belongs to. |
Request
curl -X POST "https://api.bluereacher.com/v1/messages/84213/unsend" \
-H "Authorization: Bearer <ACCESS_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"workspace_id": 7
}'Responses
A 200 reply (OK — done on the line):
{
"success": true,
"message": "Message unsent",
"data": {
"id": 84213,
"message_id": "84213",
"channel": "imessage",
"workspace_id": 7,
"contact_id": 1234,
"direction": "outbound",
"to": "+61412345678",
"from": null,
"is_group": false,
"text": "Booking confirmed for 4 PM.",
"effect": null,
"reaction": null,
"media": null,
"status": "unsent",
"error_message": null,
"created_at": "2026-10-29T09:15:58.000Z",
"updated_at": "2026-10-29T09:20:03.000Z",
"edited_at": null
}
}| Status | When | Message |
|---|---|---|
| 200 | OK — done on the line | |
| 400 | Outside Apple's window | Messages can only be unsent within 2 minutes of sending |
| 400 | Not an iMessage you sent with text | Only messages you sent can be changed |
| 400 | No line with Advanced Mode on the workspace | No line with Advanced Mode is connected to this workspace. Edit, unsend and Notify Anyway need a line with Advanced Mode on. |
| 401 | Missing or invalid token | Invalid or expired access token. |
| 403 | Missing scope | Missing scope: one of messages.send, messages.write. This integration was granted: … |
| 404 | Message not found in this workspace (or not accessible to this token) | Message not found |
| 422 | Messages on the line refused the change | The line couldn't unsend the message |
| 503 | The line that sent the message is not connected | The line isn't connected right now |
| 504 | The line did not answer within 15 s — the change may still land; read the message again | The line didn't answer in time. Check the thread in a moment. |
Notify anyway
POST https://api.bluereacher.com/v1/messages/{id}/notify-anywayAuth: Bearer access token. Scope: messages.write.
Break through the recipient's Focus for an iMessage you sent, within 24 hours (Apple's Notify Anyway).
When the recipient has a Focus on, Messages silences the notification and shows "has notifications silenced". Notify Anyway pushes that message's notification through once, as the app.bluereacher.com button does. Apple allows it within 24 hours of sending. The message itself does not change.
To see whether a recipient has notifications silenced first, use Check iMessage (silenced).
Rules (the same as the dashboard's): the message must be one you sent, over iMessage, with text, sent from a line, and not itself a reaction; the workspace needs a line with Advanced Mode on (else 400 NO_ADVANCED_MODE_LINE). The request waits for the line (up to 15 s): 422 LINE_REFUSED when Messages refused, 503 LINE_UNREACHABLE when the line is offline, 504 LINE_TIMEOUT when it did not answer in time — the change may still land, so read the message again before retrying.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | number | yes | The id of the message — a List messages or conversation message id, or the message_id from Send message. |
workspace_id | body | number | yes | The workspace the message belongs to. |
Request
curl -X POST "https://api.bluereacher.com/v1/messages/84213/notify-anyway" \
-H "Authorization: Bearer <ACCESS_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"workspace_id": 7
}'Responses
A 200 reply (OK — done on the line):
{
"success": true,
"message": "Notified anyway",
"data": {
"id": 84213,
"message_id": "84213",
"channel": "imessage",
"workspace_id": 7,
"contact_id": 1234,
"direction": "outbound",
"to": "+61412345678",
"from": null,
"is_group": false,
"text": "Booking confirmed for 4 PM.",
"effect": null,
"reaction": null,
"media": null,
"status": "delivered",
"error_message": null,
"created_at": "2026-10-29T09:15:58.000Z",
"updated_at": "2026-10-29T09:20:03.000Z",
"edited_at": null
}
}| Status | When | Message |
|---|---|---|
| 200 | OK — done on the line | |
| 400 | Outside Apple's window | Notify Anyway is only available within 24 hours of sending |
| 400 | Not an iMessage you sent with text | Only messages you sent can be re-notified |
| 400 | No line with Advanced Mode on the workspace | No line with Advanced Mode is connected to this workspace. Edit, unsend and Notify Anyway need a line with Advanced Mode on. |
| 401 | Missing or invalid token | Invalid or expired access token. |
| 403 | Missing scope | Missing scope: one of messages.send, messages.write. This integration was granted: … |
| 404 | Message not found in this workspace (or not accessible to this token) | Message not found |
| 422 | Messages on the line refused the change | The line couldn't re-notify the message |
| 503 | The line that sent the message is not connected | The line isn't connected right now |
| 504 | The line did not answer within 15 s — the change may still land; read the message again | The line didn't answer in time. Check the thread in a moment. |
Get message status
GET https://api.bluereacher.com/v1/messages/{id}Auth: Bearer access token. Scope: messages.read.
Check the delivery status of a message by its id (the message_id returned from Send message).
Look up a single message by its id — the message_id returned from Send message, or the id of any List messages or conversation thread row — and read its current delivery status. Scoped to your token, so you can only read your own messages.
The response returns the message_id, channel, status and message, with effect and reaction (see List messages). Status lifecycle: queued → webhook_sent → accepted → sent → delivered (or failed).
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | number | yes | The message id — the message_id returned from POST /messages. |
Request
curl -X GET "https://api.bluereacher.com/v1/messages/84213" \
-H "Authorization: Bearer <ACCESS_TOKEN>"Responses
A 200 reply (OK — current status):
{
"success": true,
"data": {
"message_id": "84213",
"channel": "imessage",
"status": "delivered",
"message": "Thanks for your enquiry — we open at 09:00.",
"effect": null
}
}| Status | When | Message |
|---|---|---|
| 200 | OK — current status | |
| 401 | Missing or invalid token | Invalid or expired access token. |
| 403 | Missing scope | Missing scope(s): messages.read. This integration was granted: … |
| 404 | Message not found (or not accessible to this token) | Message not found |
Response fields
message: The message record with its current delivery status. Fields:
| Field | Type | Description |
|---|---|---|
message_id | string | The message id (matches what Send message returned). Example: 84213 |
channel | string, nullable | Channel the message went out on (null until routing resolves it). Example: imessage |
status | string | queued | webhook_sent | accepted | sent | delivered | failed | received. Example: delivered |
message | string, nullable | Message body. |
Schedule message
POST https://api.bluereacher.com/v1/messages/scheduledAuth: Bearer access token. Scope: messages.write.
Limits: 30 messages / 15 min per workspace (counts with Send message) · 1 attachment per message
Send a message later.
Same recipient and channel rules as Send message. send_at is an ISO 8601 time at least 1 minute and at most 1 year ahead. The message goes out within a minute of send_at; credits, DND, routing and the device are checked then, not now — a message that cannot be sent at that time ends as failed with the reason in error.
An effect works as on Send message; the iMessage and Advanced Mode checks are made when you schedule it.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
workspace_id | body | number | yes | The workspace to send from. |
contact_id | body | number | no | The contact. Or send phone / email. |
phone | body | string | no | Recipient number with country code. |
email | body | string | no | An iMessage email address (iMessage only). |
message | body | string | no | The text. Required unless an attachment is sent. |
attachments | body | string (URL) | no | One public file URL. |
channel | body | enum | no | As for Send message; omit to use Message Routing. |
send_at | body | string (ISO 8601) | yes | When to send, UTC. |
effect | body | enum | no | An iMessage send effect. Screen effects: confetti, fireworks, balloons, hearts, lasers, shooting_star, sparkles, echo, spotlight. Bubble effects: slam, loud, gentle, invisible_ink. iMessage only, needs message text and a line in Advanced Mode on the workspace — see the description. |
Request
curl -X POST "https://api.bluereacher.com/v1/messages/scheduled" \
-H "Authorization: Bearer <ACCESS_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"workspace_id": 7,
"contact_id": 1234,
"message": "Reminder: your call is tomorrow at 10am.",
"send_at": "2026-10-28T23:00:00Z"
}'Responses
A 201 reply (Scheduled):
{
"success": true,
"message": "Message scheduled",
"data": {
"id": 912,
"workspace_id": 7,
"contact_id": 1234,
"to": "+61412345678",
"message": "Reminder: your call is tomorrow at 10am.",
"effect": null,
"reaction": null,
"attachments": [],
"channel": null,
"send_at": "2026-10-28T23:00:00.000Z",
"status": "pending",
"sent_message_id": null,
"error": null,
"created_at": "2026-10-27T09:20:00.000Z"
}
}| Status | When | Message |
|---|---|---|
| 201 | Scheduled | |
| 400 | Bad time, no recipient, or no text | Pick a time at least a minute from now |
| 400 | Effect — not iMessage | Effects are iMessage only: send channel imessage (or leave channel out). |
| 400 | Effect — no line with Advanced Mode on the workspace | No line with Advanced Mode is connected to this workspace. Send effects need a line with Advanced Mode on. |
| 400 | Effect — unknown name, or no message text | effect must be one of: confetti, fireworks, balloons, hearts, lasers, shooting_star, sparkles, echo, spotlight, slam, loud, gentle, invisible_ink |
| 401 | Missing or invalid token | Invalid or expired access token. |
| 403 | Missing scope | Missing scope: one of messages.send, messages.write. This integration was granted: … |
| 404 | Contact does not exist or is not owned by the installer (same response for both) | Contact not found or not accessible to this token |
| 429 | Workspace send limit reached | Too many queued messages for this workspace |
List scheduled messages
GET https://api.bluereacher.com/v1/messages/scheduledAuth: Bearer access token. Scope: messages.read.
A workspace's scheduled messages.
status defaults to pending (waiting or being sent now), soonest first. Any other status lists newest first.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
workspace_id | query | number | yes | The workspace. |
status | query | enum | no | Default pending. |
contact_id | query | number | no | Only this contact. |
page | query | number | no | Default 1. |
limit | query | number | no | 1–100, default 50. Anything else is a 400. |
Request
curl -X GET "https://api.bluereacher.com/v1/messages/scheduled?workspace_id=7" \
-H "Authorization: Bearer <ACCESS_TOKEN>"Responses
A 200 reply (OK):
{
"success": true,
"data": [
{
"id": 912,
"workspace_id": 7,
"contact_id": 1234,
"to": "+61412345678",
"message": "Reminder: your call is tomorrow at 10am.",
"effect": null,
"reaction": null,
"attachments": [],
"channel": null,
"send_at": "2026-10-28T23:00:00.000Z",
"status": "pending",
"sent_message_id": null,
"error": null,
"created_at": "2026-10-27T09:20:00.000Z"
}
],
"pagination": {
"page": 1,
"limit": 50,
"max_limit": 100,
"total": 1,
"total_pages": 1
}
}| Status | When | Message |
|---|---|---|
| 200 | OK | |
| 401 | Missing or invalid token | Invalid or expired access token. |
| 403 | Missing scope | Missing scope(s): messages.read. This integration was granted: … |
| 404 | Workspace does not exist or is not owned by the installer (same response for both) | Workspace not found or not accessible to this token |
Cancel scheduled message
DELETE https://api.bluereacher.com/v1/messages/scheduled/{id}Auth: Bearer access token. Scope: messages.write.
Cancel a message that has not been sent yet.
Only a pending message can be cancelled. One already being sent, sent, failed or cancelled answers 409.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | number | yes | The scheduled message id. |
Request
curl -X DELETE "https://api.bluereacher.com/v1/messages/scheduled/912" \
-H "Authorization: Bearer <ACCESS_TOKEN>"Responses
A 200 reply (OK):
{
"success": true,
"message": "Scheduled message cancelled"
}| Status | When | Message |
|---|---|---|
| 200 | OK | |
| 401 | Missing or invalid token | Invalid or expired access token. |
| 404 | Scheduled message does not exist or is not owned by the installer (same response for both) | Scheduled message not found or not accessible to this token |
| 409 | Already sent or being sent | This message is already sent |

