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 -X POST 'https://backend.blooio.com/v2/api/chats/%2B15551234567/messages' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{"text": "Hello!"}'const chatId = encodeURIComponent('+15551234567')
const res = await fetch(`https://backend.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!' })
})import os, requests
from urllib.parse import quote
chat_id = quote('+15551234567', safe='')
res = requests.post(f'https://backend.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'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://backend.blooio.com/v2/api/chats/%2B15551234567,%2B15559876543/messages' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{"text": "Hello everyone!"}'When 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"}
]
}Attachment URLs must be publicly accessible. Blooio downloads them before sending.
Metadata
Attach custom data to correlate messages in your system:
{
"text": "Your order has shipped!",
"metadata": {
"order_id": "12345",
"tracking_number": "1Z999AA10123456784"
}
}Metadata is returned in webhooks and message queries, limited to 1KB.
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 send |
sent | Transmitted to Apple/carrier |
delivered | Confirmed delivery to recipient |
failed | Delivery failed (see error field) |
Checking status
Poll the status endpoint:
curl -H 'Authorization: Bearer YOUR_API_KEY' \
'https://backend.blooio.com/v2/api/chats/%2B15551234567/messages/msg_abc123/status'Or 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:
| Protocol | Description |
|---|---|
imessage | Sent via iMessage (blue bubble) |
sms | Fell back to SMS (green bubble) |
rcs | Sent via RCS |
non-imessage | Generic non-iMessage fallback |
Protocol is determined at send time. A contact may switch between iMessage and SMS if they change devices or disable iMessage.
Common quirks and gotchas
1. 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 formatting2. Messages 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.
3. Use idempotency keys for retries
Network issues can cause duplicate messages. Always include an Idempotency-Key header:
curl -X POST 'https://backend.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!"}'If you retry with the same key, Blooio returns the original message with 200 instead of creating a duplicate.
4. 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.
5. Emergency numbers are blocked
Blooio refuses to send to emergency numbers (911, etc.) with a 403 Forbidden response.
6. 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.
7. Rate limits exist
While Blooio doesn't strictly rate limit, sending too many messages too quickly can trigger carrier-level throttling. Space out bulk sends appropriately.
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 |
503 | No active number available |
Next steps
- Send attachments - Include images and files
- Error handling - Handle failures gracefully
- Number Pools - Understand sender number selection
- Webhooks - Receive delivery updates