iMessage REST API
A complete reference to the Blooio iMessage REST API — resources, HTTP verbs, authentication, errors, and idempotency.
The Blooio iMessage REST API lets you send and receive iMessages over plain HTTPS. This guide is a practical tour of the API's resources and conventions so you can integrate with any HTTP client — curl, fetch, requests, axios, or anything else.
What is a REST API for iMessage?
Apple does not expose a public iMessage API. Blooio runs real Apple infrastructure and exposes a standard REST interface on top of it, so any app that can speak HTTP can send iMessages. Every request uses plain JSON and returns predictable, documented response shapes.
Key properties:
- HTTPS only — every request goes to
https://api.blooio.com/v2/api - Stateless — every request carries a Bearer token; no cookies or sessions
- JSON in, JSON out — no XML, no multipart except for file uploads
- Conventional HTTP verbs —
GET,POST,PATCH,DELETEdo what you'd expect - Predictable URLs —
/chats/{chatId}/messages,/contacts/{contactId},/groups/{groupId}/members
Base URL
https://api.blooio.com/v2/apiAll examples in the docs are relative to this base URL.
Authentication
Every request includes a Bearer token in the Authorization header:
curl -X GET https://api.blooio.com/v2/api/me \
-H "Authorization: Bearer YOUR_API_KEY"Try itGet an API key from the Blooio dashboard. See Authentication for details on rotating keys and scoping permissions.
Resources
The REST API is organised into a handful of resources. Each resource is a plural noun at a top-level path.
| Resource | Path | What it represents |
|---|---|---|
| Chats | /chats |
1-to-1 or group conversations |
| Messages | /chats/{chatId}/messages |
Individual messages inside a chat |
| Contacts | /contacts |
People you message |
| Groups | /groups |
Multi-party chats |
| Webhooks | /webhooks |
HTTP endpoints you register to receive events |
| Numbers | /me/numbers |
Phone numbers on your account |
HTTP verbs
Blooio uses the standard REST verb vocabulary. No surprises, no custom verbs.
| Verb | Meaning | Example |
|---|---|---|
GET |
Fetch a resource or list | GET /contacts |
POST |
Create or perform an action | POST /chats/{id}/messages |
PATCH |
Partially update a resource | PATCH /contacts/{id} |
DELETE |
Remove a resource | DELETE /webhooks/{id} |
Sending a message
The most common REST call — send an iMessage — is a single POST:
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 from the REST API"}'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 from the REST API' })
}
)
const { message_id } = await res.json()
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 from the REST API'},
)
print(res.json()['message_id'])Response:
{
"message_id": "Qm8FhbH3P7R",
"status": "queued"
}Request conventions
URL-encoding phone numbers
Phone numbers contain a + which has special meaning in URLs. Always URL-encode the chatId:
// becomes %2B15551234567
const chatId = encodeURIComponent("+15551234567");Forgetting to encode the + is the single most common REST integration mistake.
Pagination
List endpoints use limit and offset:
curl 'https://api.blooio.com/v2/api/chats?limit=25&offset=50' \
-H 'Authorization: Bearer YOUR_API_KEY'Try itDefaults: limit=25, offset=0. Maximum limit is 100.
Sorting and filtering
Most list endpoints accept sort (e.g. created_at:desc) and a q search parameter. See each endpoint's reference page for supported fields.
Response shape
Every successful response is a JSON object. List endpoints return:
{
"data": [ ... ],
"count": 42,
"limit": 25,
"offset": 0
}Single-resource endpoints return the resource directly:
{
"id": "msg_abc123",
"text": "Hello",
"status": "delivered",
"created_at": "2026-04-19T12:34:56Z"
}Errors
Errors use standard HTTP status codes and a consistent JSON body:
{
"error": "invalid_request",
"message": "text or attachments is required",
"details": {
"field": "text"
}
}| Code | Meaning | Action |
|---|---|---|
400 |
Invalid request body | Fix the payload |
401 |
Missing or invalid API key | Check your Bearer token |
403 |
Forbidden | The API key lacks permission |
404 |
Resource not found | Check the ID or path |
409 |
Conflict | Duplicate idempotency key or state mismatch |
422 |
Semantically invalid | Payload is valid JSON but logically wrong |
429 |
Conversation or configured outbound limit reached | Inspect code; do not retry a messaging-safety limit on a timer |
5xx |
Server error | Retry with exponential backoff |
See Messaging safety limits and recovery for every send-limit code and Error handling for retry strategies.
Idempotency
Send the Idempotency-Key header on POST requests to make them safe to retry:
curl -X POST 'https://api.blooio.com/v2/api/chats/%2B15551234567/messages' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Idempotency-Key: a1b2c3d4-order-123' \
-H 'Content-Type: application/json' \
-d '{"text": "Hello"}'Try itIf the same key is sent twice within 24 hours, Blooio returns the original response without creating a duplicate message. See Idempotency for the full rules.
Send limits
Send endpoints use 429 for conversation-state and configured outbound limits.
These responses do not become eligible merely because a timer elapsed. Inspect
the machine-readable code: conversation limits require recipient activity,
while configured outbound limits require a settings or usage change. Do not
invent a retry delay when Retry-After is absent.
Webhooks
The REST API's read side is usually backed by webhooks — Blooio POSTs events to a URL you register. Create a webhook:
curl -X POST 'https://api.blooio.com/v4/webhooks' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"url": "https://yourapp.com/hooks/blooio"
}'Try itEvents include message received, delivered, failed, and reactions. Every subscription receives all of them, so switch on the type field in your handler. See Webhook events for the full catalogue.
Note
If your endpoint is not yet reachable publicly, use a tunnel like ngrok — see Receive webhooks locally.
Code examples
Full end-to-end examples live in message-sending basics. For language-specific walkthroughs see:
Next steps
- Quickstart — send your first iMessage in under five minutes
- Authentication — API key management and rotation
- Webhook signatures — verify event authenticity
- API reference — every endpoint, every field