Message sending basics
A complete guide to sending messages, including chat IDs, options, and common quirks
This guide covers everything you need to know about sending messages through the Blooio API, including chat ID formats, message options, and common pitfalls.
The basics
Send a message by POSTing to /chats/{chatId}/messages:
cURL
curl -X POST 'https://api.blooio.com/v2/api/chats/%2B15551234567/messages' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{"text": "Hello!"}'Try itNode.js
const chatId = encodeURIComponent('+15551234567')
const res = await fetch(`https://api.blooio.com/v2/api/chats/${chatId}/messages`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.BLOOIO_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ text: 'Hello!' })
})Python
from urllib.parse import quote
chat_id = quote('+15551234567', safe='')
res = requests.post(f'https://api.blooio.com/v2/api/chats/{chat_id}/messages',
headers={
'Authorization': f"Bearer {os.environ['BLOOIO_API_KEY']}",
'Content-Type': 'application/json'
},
json={'text': 'Hello!'}
)
Chat ID formats
The chatId parameter determines who receives your message. Blooio supports four formats:
| Format | Example | Description |
|---|---|---|
| Phone number | +15551234567 |
E.164 format (must be URL-encoded) |
user@example.com |
Send to iMessage-enabled email | |
| Group ID | grp_abc123 |
Send to an existing group |
| Multi-recipient | +15551234567,+15559876543 |
Comma-separated list |
URL encoding phone numbers
Phone numbers contain a + character which has special meaning in URLs. Always URL-encode your chat ID:
// ✅ Correct
const chatId = encodeURIComponent('+15551234567') // becomes %2B15551234567
// ❌ Wrong - the + becomes a space
const chatId = '+15551234567'Warning
Forgetting to URL-encode phone numbers is the most common integration mistake. The
+character becomes a space, resulting in invalid phone numbers.
Multi-recipient messages
Send to multiple people at once with a comma-separated list:
curl -X POST 'https://api.blooio.com/v2/api/chats/%2B15551234567,%2B15559876543/messages' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{"text": "Hello everyone!"}'Try itWhen you send to multiple recipients:
- Blooio creates an unnamed group automatically (or reuses an existing one with the same participants)
- The response includes
group_idandgroup_created: trueif a new group was created - Subsequent messages to that combination reuse the same group
{
"message_id": "msg_abc123",
"status": "queued",
"group_id": "grp_xyz789",
"group_created": true,
"participants": ["+15551234567", "+15559876543"]
}Message body options
Text messages
The text field can be a string or an array of strings:
// Single message
{"text": "Hello!"}
// Multiple messages (sent in sequence)
{"text": ["Hello!", "How are you?", "Let me know if you have questions."]}When you pass an array, each string becomes a separate message. The response includes message_ids (plural) instead of message_id.
Attachments
Include media by providing publicly accessible URLs:
{
"text": "Check out this image",
"attachments": ["https://cdn.example.com/image.png"]
}Or provide named attachments:
{
"attachments": [
{ "url": "https://cdn.example.com/doc.pdf", "name": "contract.pdf" }
]
}Note
Attachment URLs must be publicly accessible. Blooio downloads them before sending.
Typing indicators
Show a typing indicator before sending:
{
"text": "Hello!",
"use_typing_indicator": true
}This makes messages feel more natural. The default follows your organization's preference in the dashboard.
Sender number
For accounts with multiple numbers, specify which number to send from:
{
"text": "Hello!",
"from_number": "+15559876543"
}See Number Pools for details on how sender selection works.
Message status lifecycle
Messages progress through these statuses:
| Status | Description |
|---|---|
queued |
Accepted by Blooio, waiting to be handed to Apple/the carrier |
sent |
Transmitted to Apple/the carrier (protocol resolves around here) |
delivered |
Confirmed delivery to the recipient |
failed |
Delivery failed (see the error field) |
cancellation_requested |
A cancel was requested for a still-queued message (best-effort) |
cancelled |
Cancelled before it was dispatched |
Inbound messages arrive via webhooks with status received, and read receipts
arrive as a read event. See the Message fields reference
for the full field-by-field breakdown.
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 updates.
Protocol detection
Blooio automatically detects whether the recipient supports iMessage. The protocol field indicates how the message was sent. It is never null — a freshly-accepted message starts at pending and resolves to a concrete transport within seconds of send:
| Protocol | Description |
|---|---|
pending |
Accepted and dispatched; the wire service is not resolved yet (transient, settles quickly) |
imessage |
Sent via iMessage (blue bubble) |
rcs |
Sent via RCS |
sms |
Fell back to SMS/MMS (green bubble) |
unknown |
Accepted by the carrier, but the wire service couldn't be resolved before the tracking window closed (see error) |
Note
Protocol is resolved at send time. A contact may switch between iMessage and SMS if they change devices or disable iMessage.
Warning
The legacy
non-imessagevalue has been replaced. Unresolved sends now reportpending(transient) orunknown(terminal) instead. If you branch onprotocol, treatpendingas "not yet known" rather than a final transport.
Common quirks and gotchas
Phone numbers must be E.164 format
Always include the country code with + prefix:
// ✅ Correct
"+15551234567";
// ❌ Wrong
"5551234567"; // Missing country code
"1-555-123-4567"; // Contains dashes
"(555) 123-4567"; // Contains formattingMessages return 202, not 200
Successful message sends return 202 Accepted because messages are queued asynchronously. The message isn't sent yet—it's accepted for delivery.
Use idempotency keys for retries
Network issues can cause duplicate messages. Always include an Idempotency-Key header:
curl -X POST 'https://api.blooio.com/v2/api/chats/%2B15551234567/messages' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: order-12345-confirmation' \
-d '{"text": "Order confirmed!"}'Try itIf you retry with the same key, Blooio returns the original message with 200 instead of creating a duplicate.
Group messages need a linked chat
When sending to a group ID (grp_xxx), the group must have an active iMessage chat linked. Groups created via API don't automatically have chats—the first message creates the link.
Emergency numbers are blocked
Blooio refuses to send to emergency numbers (911, etc.) with a 403 Forbidden response.
Sender availability matters
If no active number is currently available for your account, message sends fail with 503 Service Unavailable. Retry once one of your provisioned senders is available again.
Messaging safety limits exist
Blooio enforces per-conversation allowances and applies graduated protections
when a sending number shows risky one-way, repeated-template, delivery, or
ban-related activity. Some limits return 403 or 429; queue and slow
instead accept the message and defer dispatch. See
Messaging safety limits and recovery for every
limit, machine-readable error code, and recovery step.
Response codes summary
| Code | Meaning |
|---|---|
200 |
Duplicate request (idempotency key matched) |
202 |
Message accepted for sending |
400 |
Invalid request (bad phone format, missing text, etc.) |
401 |
Invalid or missing API key |
403 |
Action forbidden (emergency number, wrong number ownership) |
404 |
Chat or group not found |
429 |
Conversation or configured outbound limit reached |
503 |
No active number available |
Next steps
- Send attachments - Include images and files
- Messaging safety limits and recovery - Handle conversation and number protections
- Error handling - Handle failures gracefully
- Number Pools - Understand sender number selection
- Webhooks - Receive delivery updates