Blooio API Reference

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:

FormatExampleDescription
Phone number+15551234567E.164 format (must be URL-encoded)
Emailuser@example.comSend to iMessage-enabled email
Group IDgrp_abc123Send to an existing group
Multi-recipient+15551234567,+15559876543Comma-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_id and group_created: true if 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:

StatusDescription
queuedAccepted by Blooio, waiting to send
sentTransmitted to Apple/carrier
deliveredConfirmed delivery to recipient
failedDelivery 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:

ProtocolDescription
imessageSent via iMessage (blue bubble)
smsFell back to SMS (green bubble)
rcsSent via RCS
non-imessageGeneric 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 formatting

2. 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

CodeMeaning
200Duplicate request (idempotency key matched)
202Message accepted for sending
400Invalid request (bad phone format, missing text, etc.)
401Invalid or missing API key
403Action forbidden (emergency number, wrong number ownership)
404Chat or group not found
503No active number available

Next steps

On this page