Skip to main content
GET

Retrieve a message

Method: GET | Endpoint: /v1/messages/{message_id} Description: Fetch one message by its ID to check its delivery state. Use it when you can’t receive webhooks. To read many messages, or catch up after missed webhooks, use List messages. Authentication: API key in the Authorization header: Authorization: Bearer ib_pk_<64 hex>. Create keys in the dashboard under Settings → API access.

Request Body

None. The path parameter message_id is the message’s ID (a UUID), from the send response or a webhook event.

Responses

200 OK
direction is outbound or inbound: an inbound message (for example the message_id of a message.inbound event) can be retrieved too, and then status is delivered, or read once it has been read in the dashboard, and to is the contact who sent it. created_at is when iBubbl recorded the message (UTC, microseconds, never changes). status is one of queued, pending, sent, delivered, read, failed, expired, cancelled. channel is how it went out (imessage or sms), or null before it is sent. For an inbound message, it is how the message arrived (null for messages received before iBubbl recorded it). attachments lists the message’s files in order, each as kind (image, gif, video, audio or file), mime_type and size_bytes, for files you sent and files the contact sent you; it’s [] when there are none. File contents and URLs aren’t returned. An inbound file can show size_bytes: 0 for a few seconds, until iBubbl has received it. 401 Unauthorized
Other message values: Authorization Bearer token required, API key required (use Authorization: Bearer ib_pk_…), Invalid API key format, Customer account not found. 403 Forbidden
Your iBubbl account is suspended or cancelled (message is then Account is cancelled). Every API-key endpoint returns this. 404 Not Found
Also returned for a malformed ID and for a message that belongs to another account.

Authorizations

Authorization
string
header
required

Bearer API key. Format: Authorization: Bearer ib_pk_<64 hex> (70 characters). Generate keys from Dashboard → Settings → API Access.

Path Parameters

message_id
string
required

Response

The message.

id
string
required

Unique message identifier (UUID).

direction
enum<string>
required

outbound = sent from your line (through the API, the dashboard or an automation); inbound = received from a contact. An inbound message has status delivered, or read once it has been read in the dashboard.

Available options:
inbound,
outbound
created_at
string
required

ISO 8601 UTC timestamp, with microseconds and a Z, of when iBubbl recorded the message. It never changes, so it is what GET /v1/messages sorts and filters by; pass it back as created_after / created_before unchanged.

Example:

"2026-05-17T14:23:45.123456Z"

status
enum<string>
required

Lifecycle state. pending = accepted and on its way out; it may be waiting for the contact's working hours or the line's hourly limit for new contacts (see held_until). queued = parked by a capacity hold (hourly_cap_hit / line_offline), retried automatically. sent / delivered / read = delivery progression. failed = the send failed — see webhook message.failed for the reason; it can follow sent (or a read with no confirmed delivery) when iMessage reports the send failed after accepting it (failed_reason imessage_undeliverable / imessage_error:<code>), and such a message can still move to delivered / read if Apple later confirms it. expired = still queued after an hour, so dropped; it will not send. cancelled = stopped in the dashboard before it was sent; it will not send (webhook message.cancelled).

Available options:
queued,
pending,
sent,
delivered,
read,
failed,
expired,
cancelled
body
string
required

The message text as sent.

attachments
MessageAttachment · object[]
required

The files on this message, in the order they were sent: sent with attachments, from the dashboard or an automation, or received from the contact. Empty when there are none. File contents and URLs are not returned.

to
string | null

The contact's phone number (E.164) for 1:1 messages: the destination of an outbound message, and the sender of an inbound one. Null for group messages — the group is identified by chat_guid instead.

Example:

"+14155551234"

contact_id
string | null

ID of the contact this message was sent to. For groups, this is the 'primary' participant the message was addressed to (the first participant in participant_phones on a new group create); the conversation's full participant list is available via the conversation.

conversation_id
string | null

ID of the conversation this message belongs to. Stable for the lifetime of the conversation; use it to fetch the thread or correlate webhook events.

conversation_kind
enum<string>
default:one_to_one

one_to_one for sends to a single contact, group for iMessage groups (2+ participants in addition to your iBubbl line).

Available options:
one_to_one,
group
chat_guid
string | null

Opaque identifier for the group chat (e.g. any;+;<hex>). Returned for group messages — use it to address later messages to the same group via the chat_guid request shape. Null for one_to_one messages, and for a brand-new group until the group has been created in iMessage (that happens when the first message goes out, which can wait for sending hours); poll GET /v1/messages/{id} to see it fill in. It changes when a participant is added to the group.

Example:

"any;+;067ace6caf6c43aaa2f7d643975e15ea"

channel
enum<string> | null

How the message travelled: imessage, or sms (a text, including RCS). For an outbound message, how it actually went out (sms when the recipient couldn't be reached on iMessage); null until it has been sent. For an inbound message, how it arrived; null when that wasn't recorded.

Available options:
imessage,
sms
sent_at
string | null

ISO 8601 UTC timestamp. Set when the message is accepted, then replaced by the actual send time once it goes out. Use status (sent / delivered / read), not sent_at, to tell whether it has actually gone out.

Example:

"2026-05-17T14:23:45.123Z"

held_until
string | null

While a pending message is being held, the expected time (ISO 8601 UTC) it will go out: the next opening of the contact's working hours; for hourly_limit, an estimate of when a slot frees. Recomputed within about 10 seconds. Null when the message is not held, when held_reason is working_hours but no sending day is enabled, and when it is hourly_limit with no estimate.

Example:

"2026-10-08T16:00:00+00:00"

held_reason
enum<string> | null

Why a pending message is being held right now: working_hours (outside the contact's working hours) or hourly_limit (the line has started its limit of new contacts for this hour; the message goes out by itself when a slot frees, at about held_until). Null when it is not held.

Available options:
working_hours,
hourly_limit
effect
enum<string> | null

The iMessage send effect requested for this message, or null. It shows on the recipient's device only when channel is imessage.

Available options:
slam,
loud,
gentle,
invisible,
echo,
spotlight,
balloons,
confetti,
love,
lasers,
fireworks,
celebration,
shooting_star
reply_to_message_id
string | null

The ID of the message this one quotes as an inline reply, or null. The quote shows on the recipient's device only when channel is imessage.