Messages
Every call needs Authorization: Bearer <key>. 7 calls:
- Check whether a number is on iMessage:
POST /v1/lookup - List messages:
GET /v1/messages - Send a message:
POST /v1/messages - Get a message and its status:
GET /v1/messages/:id - Get a message’s delivery timeline:
GET /v1/messages/:id/events - Sandbox state:
GET /v1/sandbox - Simulate an inbound reply (sandbox only):
POST /v1/sandbox/inbound
Check whether a number is on iMessage
POST /v1/lookup
Cached for 30 days per workspace. Pass refresh: true to check again. Test keys and test numbers are answered by the simulator.
Headers
| Name | Type | Required | Description |
|---|---|---|---|
x-workspace-id | string | No | Workspace for session (JWT) callers. API keys carry their own workspace. |
idempotency-key | string | No | Makes the write safe to retry for 24 hours. 1 to 255 printable characters. Keys starting with outreach:, workflow: or agent: are reserved for messages bubbl3 sends itself and are refused with 400. Up to 255 characters. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
phone | string | Yes | Phone number in E.164 format. |
refresh | boolean | No | Ignore the 30-day cache. Default false. |
Response 200
| Field | Type | Description |
|---|---|---|
cached | boolean | |
checked_at | string (date-time) | ISO 8601 timestamp. |
imessage | boolean | |
phone | string | Phone number in E.164 format. |
Errors: 400, 401, 403, 404, 409, 413, 415, 429, 500. See errors for the shape and codes.
List messages
GET /v1/messages
Newest first. Agents see messages in conversations they can see; service identities see messages on their lines and messages they sent.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
limit | integer | No | From 1 to 100. Default 50. |
cursor | string | No | Opaque cursor from next_cursor. Up to 512 characters. |
status | enum | No | One of: queued, scheduled, sending, sent, delivered, read, failed, received. |
direction | enum | No | One of: outbound, inbound. |
channel | enum | No | One of: imessage, sms. |
contact_id | string | No | Contact id (ctc_...). |
conversation_id | string | No | Conversation id (cnv_...). |
line_id | string | No | Line id (ln_...). |
external_id | string | No | Up to 255 characters. |
created_after | string (date-time) | No | ISO 8601 timestamp. |
created_before | string (date-time) | No | ISO 8601 timestamp. |
Headers
| Name | Type | Required | Description |
|---|---|---|---|
x-workspace-id | string | No | Workspace for session (JWT) callers. API keys carry their own workspace. |
Response 200
| Field | Type | Description |
|---|---|---|
data | object[] | |
data[].body | string | |
data[].channel | enum | One of: imessage, sms. |
data[].contact_id | string | Contact id (ctc_...). |
data[].conversation_id | string | Conversation id (cnv_...). |
data[].created_at | string (date-time) | ISO 8601 timestamp. |
data[].delivered_at | string (date-time) or null | ISO 8601 timestamp. |
data[].direction | enum | One of: outbound, inbound. |
data[].error_code | string or null | |
data[].error_details | object or null | |
data[].external_id | string or null | |
data[].from | string or null | Phone number in E.164 format. |
data[].id | string | Message id (msg_...). |
data[].is_test | boolean | |
data[].line_id | string or null | Line id (ln_...). |
data[].media | object[] | |
data[].media[].filename | string or null | |
data[].media[].mime | string or null | |
data[].media[].size | integer or null | From 0 to 9007199254740991. |
data[].media[].url | string | |
data[].metadata | object | |
data[].read_at | string (date-time) or null | ISO 8601 timestamp. |
data[].scheduled_for | string (date-time) or null | ISO 8601 timestamp. |
data[].sent_at | string (date-time) or null | ISO 8601 timestamp. |
data[].sent_by_staff | boolean | |
data[].source | enum or null | One of: api, dashboard, workflow, agent, outreach, mcp. |
data[].status | enum | One of: queued, scheduled, sending, sent, delivered, read, failed, received. |
data[].to | string or null | Phone number in E.164 format. |
data[].updated_at | string (date-time) | ISO 8601 timestamp. |
next_cursor | string or null | Pass as cursor to get the next page; null at the end. |
Errors: 400, 401, 403, 404, 409, 413, 415, 429, 500. See errors for the shape and codes.
Send a message
POST /v1/messages
Queues the message and returns it. The worker routes, governs, paces and sends it; follow progress with GET /v1/messages/:id or the message.status webhook.
Headers
| Name | Type | Required | Description |
|---|---|---|---|
x-workspace-id | string | No | Workspace for session (JWT) callers. API keys carry their own workspace. |
idempotency-key | string | No | Makes the write safe to retry for 24 hours. 1 to 255 printable characters. Keys starting with outreach:, workflow: or agent: are reserved for messages bubbl3 sends itself and are refused with 400. Up to 255 characters. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
body | string | No | Up to 2000 characters. |
contact_id | string | No | Contact id (ctc_...). |
external_id | string | No | Up to 255 characters. |
fallback | boolean | No | Set false to forbid SMS fallback; otherwise the pool's fallback_sms_mode decides. |
line_id | string | No | Line id (ln_...). |
media | object[] | No | Up to 10 items. |
media[].filename | string | No | Up to 255 characters. |
media[].mime | string | No | Up to 100 characters. |
media[].url | string (uri) | Yes | Publicly fetchable URL of the file. Up to 2048 characters. |
metadata | object | No | Up to 50 key/value pairs. |
pool_id | string | No | Pool id (pool_...). |
send_at | string (date-time) | No | Schedule for later, at most one year ahead; the governor may still defer it. |
to | string | No | Recipient phone number. Provide to or contact_id. |
Response 201
| Field | Type | Description |
|---|---|---|
body | string | |
channel | enum | One of: imessage, sms. |
contact_id | string | Contact id (ctc_...). |
conversation_id | string | Conversation id (cnv_...). |
created_at | string (date-time) | ISO 8601 timestamp. |
delivered_at | string (date-time) or null | ISO 8601 timestamp. |
direction | enum | One of: outbound, inbound. |
error_code | string or null | |
error_details | object or null | |
external_id | string or null | |
from | string or null | Phone number in E.164 format. |
id | string | Message id (msg_...). |
is_test | boolean | |
line_id | string or null | Line id (ln_...). |
media | object[] | |
media[].filename | string or null | |
media[].mime | string or null | |
media[].size | integer or null | From 0 to 9007199254740991. |
media[].url | string | |
metadata | object | |
read_at | string (date-time) or null | ISO 8601 timestamp. |
scheduled_for | string (date-time) or null | ISO 8601 timestamp. |
sent_at | string (date-time) or null | ISO 8601 timestamp. |
sent_by_staff | boolean | |
source | enum or null | One of: api, dashboard, workflow, agent, outreach, mcp. |
status | enum | One of: queued, scheduled, sending, sent, delivered, read, failed, received. |
to | string or null | Phone number in E.164 format. |
updated_at | string (date-time) | ISO 8601 timestamp. |
Errors: 400, 401, 403, 404, 409, 413, 415, 429, 500. See errors for the shape and codes.
Get a message and its status
GET /v1/messages/:id
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Message id (msg_...). |
Headers
| Name | Type | Required | Description |
|---|---|---|---|
x-workspace-id | string | No | Workspace for session (JWT) callers. API keys carry their own workspace. |
Response 200
| Field | Type | Description |
|---|---|---|
body | string | |
channel | enum | One of: imessage, sms. |
contact_id | string | Contact id (ctc_...). |
conversation_id | string | Conversation id (cnv_...). |
created_at | string (date-time) | ISO 8601 timestamp. |
delivered_at | string (date-time) or null | ISO 8601 timestamp. |
direction | enum | One of: outbound, inbound. |
error_code | string or null | |
error_details | object or null | |
external_id | string or null | |
from | string or null | Phone number in E.164 format. |
id | string | Message id (msg_...). |
is_test | boolean | |
line_id | string or null | Line id (ln_...). |
media | object[] | |
media[].filename | string or null | |
media[].mime | string or null | |
media[].size | integer or null | From 0 to 9007199254740991. |
media[].url | string | |
metadata | object | |
read_at | string (date-time) or null | ISO 8601 timestamp. |
scheduled_for | string (date-time) or null | ISO 8601 timestamp. |
sent_at | string (date-time) or null | ISO 8601 timestamp. |
sent_by_staff | boolean | |
source | enum or null | One of: api, dashboard, workflow, agent, outreach, mcp. |
status | enum | One of: queued, scheduled, sending, sent, delivered, read, failed, received. |
to | string or null | Phone number in E.164 format. |
updated_at | string (date-time) | ISO 8601 timestamp. |
Errors: 400, 401, 403, 404, 409, 413, 415, 429, 500. See errors for the shape and codes.
Get a message’s delivery timeline
GET /v1/messages/:id/events
Oldest first: accepted, routed, deferred, sent, delivered, read and so on.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Message id (msg_...). |
Headers
| Name | Type | Required | Description |
|---|---|---|---|
x-workspace-id | string | No | Workspace for session (JWT) callers. API keys carry their own workspace. |
Response 200
| Field | Type | Description |
|---|---|---|
data | object[] | |
data[].detail | object | Event-specific detail, e.g. the deferral reason. |
data[].id | string (uuid) | |
data[].occurred_at | string (date-time) | ISO 8601 timestamp. |
data[].type | enum | One of: accepted, routed, capability_checked, deferred, rejected, sending, sent, delivered, read, failed, retried, fallback_sms, received, receipt_missing. |
Errors: 400, 401, 403, 404, 409, 413, 415, 429, 500. See errors for the shape and codes.
Sandbox state
GET /v1/sandbox
The organisation's sandbox setting and the workspace's sandbox lines. A sandbox line is created by the first sandboxed message, so the list starts empty.
Headers
| Name | Type | Required | Description |
|---|---|---|---|
x-workspace-id | string | No | Workspace for session (JWT) callers. API keys carry their own workspace. |
Response 200
| Field | Type | Description |
|---|---|---|
enabled | boolean | The organisation's sandbox setting. |
lines | object[] | The workspace’s sandbox lines. Empty until the first sandboxed message. |
lines[].contact_card | object | |
lines[].contact_card.image_url | string | Up to 2048 characters. |
lines[].contact_card.name | string | Up to 100 characters. |
lines[].created_at | string (date-time) | ISO 8601 timestamp. |
lines[].daily_new_cap | integer | From 0 to 9007199254740991. |
lines[].display_name | string or null | |
lines[].health_score | integer | From 0 to 100. |
lines[].id | string | Line id (ln_...). |
lines[].is_sandbox | boolean | |
lines[].phone | string | Phone number in E.164 format. |
lines[].pool_id | string or null | Pool id (pool_...). |
lines[].state | enum | One of: provisioning, ready, warming, active, throttled, paused, quarantined, retired. |
lines[].state_changed_at | string (date-time) or null | ISO 8601 timestamp. |
lines[].state_reason | string or null | |
lines[].usage_today | object | |
lines[].usage_today.day | string (date) | Usage day in America/New_York, rolling over at 3 AM. |
lines[].usage_today.new_contacts | integer | From 0 to 9007199254740991. |
lines[].usage_today.new_contacts_cap | integer | From 0 to 9007199254740991. |
lines[].usage_today.sends | integer | From 0 to 9007199254740991. |
lines[].usage_today.sends_cap | integer | From 0 to 9007199254740991. |
lines[].warmup_day | integer | From 0 to 9007199254740991. |
Errors: 400, 401, 403, 404, 409, 413, 415, 429, 500. See errors for the shape and codes.
Simulate an inbound reply (sandbox only)
POST /v1/sandbox/inbound
Records a text from a test number (+1 NPA 555 01xx) to a sandbox line, as if the contact had replied: the contact is matched or created, the conversation opens and message.received fires. STOP and START keywords apply. Without line_id the workspace’s sandbox line is used (created on first use).
Headers
| Name | Type | Required | Description |
|---|---|---|---|
x-workspace-id | string | No | Workspace for session (JWT) callers. API keys carry their own workspace. |
idempotency-key | string | No | Makes the write safe to retry for 24 hours. 1 to 255 printable characters. Keys starting with outreach:, workflow: or agent: are reserved for messages bubbl3 sends itself and are refused with 400. Up to 255 characters. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
body | string | Yes | Up to 2000 characters. |
from | string | Yes | Sender. Sandbox only accepts test numbers. |
line_id | string | No | A sandbox line in the workspace. Defaults to the workspace’s sandbox line. |
media | object[] | No | Up to 10 items. |
media[].filename | string | No | Up to 255 characters. |
media[].mime | string | No | Up to 100 characters. |
media[].url | string (uri) | Yes | Publicly fetchable URL of the file. Up to 2048 characters. |
Response 201
| Field | Type | Description |
|---|---|---|
body | string | |
channel | enum | One of: imessage, sms. |
contact_id | string | Contact id (ctc_...). |
conversation_id | string | Conversation id (cnv_...). |
created_at | string (date-time) | ISO 8601 timestamp. |
delivered_at | string (date-time) or null | ISO 8601 timestamp. |
direction | enum | One of: outbound, inbound. |
error_code | string or null | |
error_details | object or null | |
external_id | string or null | |
from | string or null | Phone number in E.164 format. |
id | string | Message id (msg_...). |
is_test | boolean | |
line_id | string or null | Line id (ln_...). |
media | object[] | |
media[].filename | string or null | |
media[].mime | string or null | |
media[].size | integer or null | From 0 to 9007199254740991. |
media[].url | string | |
metadata | object | |
read_at | string (date-time) or null | ISO 8601 timestamp. |
scheduled_for | string (date-time) or null | ISO 8601 timestamp. |
sent_at | string (date-time) or null | ISO 8601 timestamp. |
sent_by_staff | boolean | |
source | enum or null | One of: api, dashboard, workflow, agent, outreach, mcp. |
status | enum | One of: queued, scheduled, sending, sent, delivered, read, failed, received. |
to | string or null | Phone number in E.164 format. |
updated_at | string (date-time) | ISO 8601 timestamp. |
Errors: 400, 401, 403, 404, 409, 413, 415, 429, 500. See errors for the shape and codes.