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

Send a message to a chat

POST/chats/{chatId}/messages

Sends a message into an existing chat. Both the sender channel and recipient are inferred from the chat, so from and to must not be supplied. Returns the message object at the top level (not wrapped in data).

Path parameters

chatIdRequiredstring

Unique identifier of the chat, prefixed with chat_. Returned by the List chats endpoint.

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

Optional key for safe retries. Replaying the same key with an identical body returns the original result; a different body returns 409. Use a unique value (such as a UUID) per logical request.

Body parameters

JSON
textoptionalstring

Plain text message. May be combined with attachments (sent as media with a caption).

attachmentsoptionalstring[]

Public https media URLs. May be combined with text.

rich_linkoptionalobject

A tappable rich link bubble (Blooio /).

urlRequiredstring
titleoptionalstring
polloptionalobject

A native Blooio poll. Routed sends automatically consider only poll-capable channels.

titleRequiredstring
optionsRequiredstring[]
interactiveoptionalobject

Interactive content (WhatsApp Business / RCS). Requires kind.

kindRequiredstring
templateoptionalobject

Template content (WhatsApp Business).

template_idoptionalstring
partsoptionalobject[]

Ordered text/media parts sent as one multipart message (Blooio only).

Array of object

textoptionalstring
urloptionalstring
reply_tooptionalstring

Message id to reply to. Applies to the whole outgoing message.

effectoptionalstring

iMessage screen/bubble effect (Blooio, text only).

link_previewoptionalLinkPreview
imageUrloptionalstring
titleoptionalstring
dry_runoptionalboolean

Returns

idoptionalstring
chat_idoptionalstring
channel_idoptionalstring
channel_typeoptionalstring
"blooio""twilio""whatsapp""whatsapp_business""rcs_business"
protocoloptionalstring

Transport state or resolved wire service; never null. pending = accepted and dispatched, wire service not resolved yet; imessage / rcs / sms = the device-confirmed transport (iMessage blue bubble, RCS, or SMS/MMS green bubble); whatsapp = sent over WhatsApp; unknown = accepted but the transport could not be resolved before the tracking window closed (see error).

"pending""unknown""imessage""sms""rcs""whatsapp"
fromoptionalstring | null

The resolved sender as a human value: the sending channel's phone number (numbered channels) or alias (non-numbered channels). This is the primary sender reference; channel_id remains available for exact follow-up and audit.

directionoptionalstring
"inbound""outbound"
typeoptionalstring
statusoptionalstring

Initial lifecycle state for the send: normally queued (accepted for delivery), or failed if rejected before dispatch. Later transitions (sentdelivered / read, or failed) are reported via the message status endpoint and message.* webhooks.

group_idoptionalstring

Existing group id for a group-targeted send.

hybridoptionalobject

Hybrid routing phase and optional transition placement.

erroroptionalobject | null
fallbackoptionalobject

Present when a send could not be delivered on the requested channel and a different channel should be tried.

recommendedoptionalboolean

True when falling back to another channel is recommended.

reasonoptionalstring
tooptionalstring

Recipient identifier. Present only on per-recipient items inside a fan-out (FanOutResult) response.

dry_runoptionalboolean
would_sendoptionalboolean
previewoptionalobject
polloptionalobject
titleoptionalstring
optionsoptionalstring[]
routingoptionalRoutingMetadata

How Blooio selected the concrete sender channel.

modeoptionalstring
"explicit""priority""hybrid"
channel_typeoptionalstring
"blooio""twilio""whatsapp""whatsapp_business""rcs_business"
numberoptionalstring
aliasoptionalstring
priority_idoptionalstring
priorityoptionalinteger

Response codes

200Dry-run preview
201Message accepted. The message object is returned with its current `status`; a synchronously rejected send returns `status: "failed"` with an `error` object (still 201, since the message was created).
401Your API key is missing or invalid. Pass it as a bearer token.
403Blocked by messaging safety. The error `code` identifies the gate: `safety_account_review` (the sending number is blocked pending review), `safety_reply_only` (outbound is restricted to conversations with prior inbound), `safety_new_conversations_paused` (brand-new conversations are temporarily paused; replies are unaffected), or `conversation_content_restricted` (links, media, and attachments are not allowed before the recipient's first written reply, or in a re-engagement of an inactive conversation — an emoji reaction does not unlock them). Safety actions clear automatically as the underlying pattern ages out; current per-number state is delivered via the `safety.state_changed` webhook event.
404No message was found with the provided `chatId`.
409The request conflicts with the current state of the message (e.g. it already exists, the chat is closed, or the channel isn't active).
422Validation failed — one or more fields in the request are invalid or missing.
429Conversation-state limit reached. The error `code` identifies the rule: `conversation_awaiting_reply` (up to 3 messages may be sent before the recipient responds at all), `conversation_streak_limit` (consecutive-message cap since their last response), or `conversation_inactive_paused` (no response in 14+ days and the single re-engagement message was already sent). Every response body carries `conversation_state` — one of `new`, `acknowledged` (reacted but never wrote back), `active`, `established`, `trusted`, or `inactive` — and the streak errors also carry `current_streak` and `limit`, so you can see which rung of the ladder a thread is on. The cap rises with the thread's written history, and an **emoji reaction counts as a response**: a tapback clears the consecutive-message counter and keeps a thread from going inactive, so the limit lifts the moment the recipient reacts. A reaction is not treated as a reply, though — only writing back raises the cap and unlocks links, media, and attachments (see `conversation_content_restricted`).
501This message endpoint is defined but not implemented yet.

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/v4/chats/chat_a1b2c3d4/messages \
Body object
Response objectexample
{  "id": "obj_a1b2c3d4",  "chat_id": "chat_a1b2c3d4",  "channel_id": "ch_a1b2c3d4",  "channel_type": "blooio",  "protocol": "pending",  "from": "+15551234567",  "direction": "inbound",  "type": "string",  "status": "active",  "group_id": "grp_a1b2c3d4",  "hybrid": {},  "error": {},  "fallback": {    "recommended": false,    "reason": "string"  },  "to": "+15551234567",  "dry_run": false,  "would_send": false,  "preview": {},  "poll": {    "title": "Example title",    "options": [      "string"    ]  },  "routing": {    "mode": "explicit",    "channel_type": "blooio",    "number": "+15551234567",    "alias": "string",    "priority_id": "obj_a1b2c3d4",    "priority": 0  }}