BetaThe V4 API is in beta — endpoints and functionality may change.
Messagesv2

Send a message

POST/chats/{chatId}/messages

Send a message to a chat. The chatId can be: (1) E.164 phone number, (2) email address, (3) group ID (grp_xxxx), or (4) comma-separated list of phone/email for multi-recipient chats. For multi-recipient, an unnamed group is automatically created or reused if the exact participant combination already exists. For explicit groups, the group must be linked to an existing iMessage chat.

iMessage send-with-effect: set the optional effect field to attach an Apple expressive send (slam, loud, gentle, invisible-ink) or screen effect (echo, spotlight, balloons, confetti, love, lasers, fireworks, celebration). Effects are an iMessage-only feature — when the recipient is on SMS/RCS the message is delivered without the animation. Effects are not supported in multipart (parts) mode.

Threaded replies (iMessage inline reply): set the optional reply_to field to send the outgoing message as a reply to a specific earlier message. Two shapes are accepted: { "message_id": "msg_…" } references a Blooio-minted message in the same chat (most common — the message_id returned by an earlier send or surfaced on a message.received webhook), or { "guid": "…", "part_index": 0 } references the raw iMessage GUID for the rare case where the parent wasn't recorded by Blooio. The reply must target the same chat and the same from-number as the new send, and the parent must be no older than 30 days (the iMessage on-device retention horizon). Reply support is iMessage-only and is rejected on Twilio, dashboard-Twilio, and hybrid send paths; it's also rejected on multi-message fan-outs (text array or per-part URL-balloon batch). See the 400 responses for the full set of reply_target_* error codes.

Path parameters

chatIdRequiredstring

Chat identifier. Can be: (1) phone number in E.164 format (e.g., +15551234567), (2) email address, (3) group ID (grp_xxxx), or (4) comma-separated list of phone numbers/emails for multi-recipient group chats (e.g., +15551234567,+15559876543). All values should be URL-encoded.

Headers

AuthorizationRequiredstring

Your API key, sent as a bearer token: Authorization: Bearer <api_key>. Editing this stays in sync with the API key box on the right.

Bearer
Idempotency-Keyoptionalstring

Unique key to prevent duplicate message sends. If the same key is used again, the original message_id and status are returned.

Body parameters

JSON
textoptionalstring | string[]

Message text. Can be a single string or array of strings (each becomes a separate message)

attachmentsoptionalstring | object[]

Array of attachment URLs or objects with url/name

Array of string | object

string
object
urlRequiredstring
nameoptionalstring
use_typing_indicatoroptionalboolean

Whether to show typing indicator before sending. Defaults to org preference.

from_numberoptionalstring

E.164 phone number to send from. For Twilio API keys, this is optional — if omitted, the first assigned Twilio number is auto-selected. For Blooio (iMessage) API keys, this selects a specific number from your pool. Must be a number assigned to your API key.

share_contactoptionalboolean

If true, the contact card (Name & Photo) will be shared with this message. The contact card is piggybacked onto the outgoing message. Defaults to false. ⚠️ Only available on **Dedicated Commercial** and **Dedicated Enterprise** plans — other plans receive a 403.

partsoptionalobject[]

Ordered array of message parts. Two modes: 1. **Multipart mode** — parts sent as a single unified iMessage bubble (mix of text and attachment parts). This is the default. 2. **URL-balloon batch mode** — triggered when any part has a link_preview object. Each part becomes its own rich-link-preview iMessage; parts are sent sequentially in array order. In batch mode every part must be text-only with text being a single http(s) URL. Response contains message_ids[] + count instead of message_id.

Array of object

textoptionalstring

Text content for this part. Mutually exclusive with 'url'.

mentionoptionalstring

Participant phone number or email to @-mention. Only valid with 'text'. The entire text of the part is rendered as the mention.

urloptionalstring

URL to an attachment for this part. Mutually exclusive with 'text'.

nameoptionalstring

Filename for the attachment. Only valid with 'url'.

link_previewoptionalLinkPreview

Optional. Per-part rich-link-preview override. When any part carries this, every part must be a text-only single-URL part (URL-balloon batch mode).

image_urloptionalstring

HTTPS URL to an image (png, jpg, webp, gif). Blooio downloads the image server-side and attaches it as the rich-link hero. Max 16 MB. If the download fails or returns a non-image MIME, the send falls back to auto-fetched OG metadata.

titleoptionalstring

Bold title line rendered in the iMessage bubble. Overrides the page's <meta property="og:title">.

link_previewoptionalLinkPreview

Optional. Override the rich-link-preview image and/or title on URL messages. See the LinkPreview schema. When omitted, Blooio auto-generates the preview from the page's Open Graph tags.

image_urloptionalstring

HTTPS URL to an image (png, jpg, webp, gif). Blooio downloads the image server-side and attaches it as the rich-link hero. Max 16 MB. If the download fails or returns a non-image MIME, the send falls back to auto-fetched OG metadata.

titleoptionalstring

Bold title line rendered in the iMessage bubble. Overrides the page's <meta property="og:title">.

effectoptionalstring

Optional. Attach an iMessage send-with-effect to the outgoing message. **Bubble effects** (apply to a single text bubble): - slam — Slam - loud — Loud - gentle — Gentle - invisible-ink — Invisible Ink **Screen effects** (full-screen animation in the recipient's chat): - echo — Echo - spotlight — Spotlight - balloons — Balloons - confetti — Confetti - love — Love (heart) - lasers — Lasers - fireworks — Fireworks - celebration — Celebration (sparkles) Values are case-insensitive and accept either dashes or spaces ("Invisible Ink" and "invisible-ink" both work). Pass "none" or omit the field to send without an effect. **Limitations:** - iMessage-only — when the chat is delivered as SMS or RCS the message is sent without an animation. - Not supported alongside the parts array (multipart bubbles cannot carry an effect). Use the top-level text field instead. - When text is an array, every message in the array is sent with the same effect.

"slam""loud""gentle""invisible-ink""echo""spotlight""balloons""confetti""love""lasers""fireworks""celebration""none"
reply_tooptionalReplyToRequest

Optional. Send this message as an iMessage inline reply targeting a specific earlier message. iMessage-only — rejected on Twilio, hybrid, and multi-message fan-outs (text array or URL-balloon batch).

message_idoptionalstring

Blooio message_id of the parent. Must belong to the same chat, same from-number, and be no older than 30 days. Returns 404 reply_target_not_found if unknown.

guidoptionalstring

Raw iMessage GUID of the parent. When supplied without a message_id, Blooio attempts to look up the parent via provider_message_guid; if the parent isn't in our table the send still proceeds (Lava will thread on the device when possible) and the response carries parent_unresolved: true.

part_indexoptionalinteger

Which part of the parent to reply to. Defaults to 0 (covers the 99% case of replying to a single-part text message).

Returns

message_idoptionalstring

ID of the sent message (single-message sends)

message_idsoptionalstring[]

IDs of sent messages. Present when text is an array or when parts uses per-part link_preview (URL-balloon batch mode).

countoptionalinteger

Number of messages sent. Only present in URL-balloon batch mode.

statusoptionalstring

Initial status of the message(s)

"queued""failed"
group_idoptionalstring

Group ID when sending to multi-recipient (new or existing)

group_createdoptionalboolean

True if a new unnamed group was created for this multi-recipient message

participantsoptionalstring[]

List of participants (present for multi-recipient)

parent_unresolvedoptionalboolean

Present (and true) only when reply_to.guid was supplied without a message_id and the GUID didn't map to any Blooio-minted row. The send still proceeds and the device may still thread it; this flag signals that Blooio couldn't link the new message to a known parent.

Response codes

200Duplicate request (idempotency key matched)
202Message accepted for sending
400Bad request. Threaded-reply failures use the response body's `code` field: - `reply_target_invalid` — `reply_to` was supplied but missing both `message_id` and `guid`, or the resolved parent has no provider GUID. - `reply_target_chat_mismatch` — the parent message lives in a different chat than the new send. - `reply_target_device_mismatch` — the parent was sent or received on a different line (allocation). Switch the from-number to match. - `reply_target_expired` — the parent is older than the 30-day iMessage on-device retention window and may have been purged from the device. - `reply_target_not_supported` — `reply_to` was used on a path that doesn't support inline replies (Twilio, hybrid, or multi-message fan-out). - Other validation failures (missing content, invalid recipient, etc.) — no `code` field.
401Authentication required or invalid
403Forbidden. The response body's `code` field disambiguates the specific failure: - `inbound_only_no_prior_inbound` — the sender is on the Inbound plan and the recipient has never messaged this number first. Inbound numbers are reply-only. Body also includes `allocation_id` and `external_id`. - Emergency number - Integration-assigned number that can't be sent from manually
404Not found. Threaded-reply failures use the response body's `code` field: - `reply_target_not_found` — `reply_to.message_id` did not match any message in this organization. - Other 404s (e.g., chat not found) have no `code`.
429Too many requests. Two possible `code`s on this endpoint: - `outbound_limit_reached` — org-level new-contact cap (configured in Settings) tripped. Body includes `limit`, `current`, `mode`, and on per-number mode `allocation_id` + `sender_number`. - `new_conversation_limit_reached` — shared plan's daily new-conversation cap reached. Body includes `plan_id`, `cap`, `current`. Existing conversations continue to send.
503No active number available

Sends a live request with your values and shows the real response below. Your key is stored only in this browser.

Request
curl -X POST https://api.blooio.com/v2/api/chats/chat_a1b2c3d4/messages \  -H "Authorization: Bearer bl_live_..." \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{    "text": "Hello from Blooio!",    "attachments": [      "string"    ],    "use_typing_indicator": false,    "from_number": "string",    "share_contact": false,    "parts": [      {        "text": "Hello from Blooio!",        "mention": "string",        "url": "https://example.com",        "name": "Jane Doe",        "link_preview": {          "image_url": "https://example.com",          "title": "Example title"        }      }    ],    "link_preview": {      "image_url": "https://example.com",      "title": "Example title"    },    "effect": "slam",    "reply_to": {      "message_id": "msg_a1b2c3d4",      "guid": "obj_a1b2c3d4",      "part_index": 0    }  }'
Body object
Response objectexample
{  "message_id": "msg_a1b2c3d4",  "message_ids": [    "string"  ],  "count": 3,  "status": "queued",  "group_id": "grp_a1b2c3d4",  "group_created": false,  "participants": [    "string"  ],  "parent_unresolved": false}