Message fields reference (v2)
Every field on a v2 message object, plus the full status lifecycle and protocol (transport) states
This is the field-by-field reference for the v2 message object
(api.blooio.com/v2/api). It covers what each field means, the full set of
status (delivery lifecycle) values, and the full set of protocol (transport)
values. For sending mechanics and examples, see
Message sending basics. Multi-channel senders
(e.g. WhatsApp) use the v4 message fields reference.
The message object
{
"message_id": "msg_abc123",
"direction": "outbound",
"external_id": "+15551234567",
"internal_id": "+15559876543",
"text": "Hello!",
"attachments": [],
"sender": null,
"reactions": [],
"time_sent": 1720992000000,
"time_delivered": 1720992003000,
"status": "delivered",
"protocol": "imessage",
"error": null
}| Field | Type | Description |
|---|---|---|
message_id |
string | Unique message id, prefixed msg_. |
direction |
enum | inbound or outbound. |
external_id |
string | The contact's phone number or email, or a grp_ group id for group messages. |
internal_id |
string | null | The organization sender number (from-number) used for this message. |
text |
string | null | Message body. null for attachment-only messages. |
attachments |
array | Attachment objects sent with the message. |
sender |
string | null | Sender's phone/email for inbound group messages. null for outbound and 1:1 chats. |
reactions |
array | Tapbacks and emoji reactions on this message. |
time_sent |
int64 | Epoch milliseconds when the message was sent. |
time_delivered |
int64 | null | Epoch milliseconds when delivery was confirmed. null until a receipt arrives. |
status |
enum | Delivery lifecycle state — see Status lifecycle. |
protocol |
enum | Transport that carried the message — see Protocol states. |
error |
string | null | Human-readable failure reason when status is failed (or when protocol resolution failed). |
reply_to |
object | null | Present only on inline-reply messages; omitted for top-level messages. |
Status lifecycle
status describes where a message is in its delivery lifecycle. It is
independent of protocol (which transport carried it).
| Status | Description |
|---|---|
pending |
Persisted and being prepared for dispatch. |
queued |
Accepted and waiting to be handed to Apple/the carrier. |
sent |
Handed off to Apple/the carrier. Protocol resolves around here. |
delivered |
A delivery receipt was received from the recipient. |
failed |
The message could not be delivered — see error. |
cancellation_requested |
A cancel was requested for a still-queued message (best-effort). |
cancelled |
The message was cancelled before it was dispatched. |
Typical happy path:
pending → queued → sent → deliveredNote
Inbound messages don't have a lifecycle — they're surfaced via webhooks with status
received. Areadreceipt arrives as a separatereadwebhook event, not as a change to the messagestatuscolumn.
Checking status
Poll the status endpoint:
curl -H 'Authorization: Bearer YOUR_API_KEY' \
'https://api.blooio.com/v2/api/chats/%2B15551234567/messages/msg_abc123/status'Try itOr subscribe to message.status webhooks for real-time transitions.
Protocol states
protocol records the transport that carried (or will carry) the message.
Blooio picks the transport at send time — iMessage when the recipient supports
it, otherwise RCS or SMS.
Warning
protocolis nevernull. A freshly-accepted message starts atpendingand resolves to a concrete transport within seconds. The legacynon-imessagevalue has been removed — unresolved sends now reportpending(transient) orunknown(terminal).
| Protocol | Meaning |
|---|---|
pending |
Accepted and dispatched; the wire service is not resolved yet. Transient — settles within seconds of sent. |
imessage |
Delivered over iMessage (blue bubble). |
rcs |
Delivered over RCS. |
sms |
Fell back to SMS/MMS (green bubble). |
unknown |
Accepted by the carrier, but the wire service could not be resolved before the tracking window closed (see error). |
Reading protocol safely
If your integration branches on protocol, treat pending as "not yet
known" rather than a final answer — re-check the status endpoint (or wait for
the message.status webhook) to get the resolved transport. unknown is
terminal and means the message was accepted but its transport could not be
confirmed.
switch (message.protocol) {
case "pending":
// Not resolved yet — check again shortly.
break;
case "imessage":
case "rcs":
// Rich transport confirmed.
break;
case "sms":
// Fell back to SMS/MMS.
break;
case "unknown":
// Accepted, but transport never confirmed. Inspect `error`.
break;
}Next steps
- Message sending basics — chat IDs, options, quirks
- Error handling — handle failures gracefully
- Webhooks — receive status transitions in real time
- Message fields reference (v4) — multi-channel senders