API Reference
Complete list of all TextBubbles API endpoints. Base URL: https://api.textbubbles.com
Messages
| Method | Path | Description |
|---|---|---|
POST | /v1/messages | Send a message |
GET | /v1/messages | List messages |
GET | /v1/messages/:id | Get message status |
POST | /v1/messages/:id/unsend | Unsend a message |
PUT | /v1/messages/:id | Edit a message |
POST | /v1/messages/:id/reactions | Send tapback reaction |
DELETE | /v1/messages/:id | Soft-delete a message |
GET | /v1/messages/scheduled | List scheduled messages |
DELETE | /v1/messages/:id/schedule | Cancel scheduled message |
Capabilities
| Method | Path | Description |
|---|---|---|
GET | /v1/capabilities/:phoneNumber | Check iMessage/SMS/FaceTime support |
GET /v1/capabilities/:phoneNumber
Check which messaging channels are available for a recipient, along with their current Focus status and a recommended channel. Results are cached to avoid repeatedly probing Apple infrastructure.
Authentication: Requires Bearer token (Authorization: Bearer tb_xxxxx)
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
phoneNumber | string | Recipient phone number in E.164 format (e.g., +14155551234). Must start with + followed by 2–15 digits. |
Example Request
curl https://api.textbubbles.com/v1/capabilities/+14155551234 \
-H "Authorization: Bearer YOUR_API_KEY"Example Response
{
"success": true,
"data": {
"phoneNumber": "+14155551234",
"capabilities": {
"imessage": true,
"sms": true,
"facetime": true
},
"focused": false,
"recommendedChannel": "imessage",
"lastChecked": "2026-03-28T09:55:00.000Z",
"cached": true
},
"requestId": "req_abc123"
}When the recipient is currently in a Focus mode, the response also includes focusMode:
{
"success": true,
"data": {
"phoneNumber": "+14155551234",
"capabilities": { "imessage": true, "sms": true, "facetime": true },
"focused": true,
"focusMode": "Do Not Disturb",
"recommendedChannel": "imessage",
"lastChecked": "2026-03-28T09:55:00.000Z",
"cached": false
},
"requestId": "req_abc123"
}Response Fields
| Field | Type | Description |
|---|---|---|
phoneNumber | string | Echoes the queried phone number in E.164 format |
capabilities.imessage | boolean | Recipient is reachable via iMessage |
capabilities.sms | boolean | Recipient is reachable via SMS |
capabilities.facetime | boolean | Recipient is reachable via FaceTime (audio or video) |
focused | boolean | Whether the recipient is currently in a Focus mode that may suppress notifications |
focusMode | string | Name of the active Focus mode (only present when focused is true) |
recommendedChannel | string | Suggested channel to use: "imessage" if iMessage is available, otherwise "sms" |
lastChecked | string | ISO 8601 timestamp of when capabilities were last refreshed |
cached | boolean | true if the response came from the capability cache, false if freshly probed |
Errors
| Status | Code | Description |
|---|---|---|
400 | INVALID_PHONE_NUMBER | Phone number is not in E.164 format |
401 | UNAUTHORIZED | Missing or invalid Bearer token |
403 | FORBIDDEN | API key has no associated customer |
Tip: Use this endpoint before calling
POST /v1/messageswhen you want to branch your application logic based on whether the recipient supports iMessage effects, tapbacks, or FaceTime calls. TherecommendedChannelfield is a convenient default for therouting.preferenceon message send.
Numbers
| Method | Path | Description |
|---|---|---|
GET | /v1/numbers | List available sender addresses |
GET /v1/numbers
List all phone numbers and email addresses available as valid from addresses for your account.
Authentication: Requires Bearer token (Authorization: Bearer tb_xxxxx)
Example Request
curl -H "Authorization: Bearer tb_xxxxx" https://api.textbubbles.com/v1/numbersExample Response
{
"success": true,
"data": [
{
"phoneNumber": "+14155551234",
"email": "user@icloud.com",
"instanceName": "TextBubbles Service 001",
"isDefault": true,
"healthStatus": "healthy"
},
{
"phoneNumber": "+19876543210",
"email": null,
"instanceName": "TextBubbles Service 002",
"isDefault": false,
"healthStatus": "healthy"
}
]
}Fields
| Field | Type | Description |
|---|---|---|
phoneNumber | string | E.164 phone number |
email | string | null | iMessage email or null |
instanceName | string | Friendly name of the instance |
isDefault | boolean | Whether this is the default sender when no from is specified |
healthStatus | string | One of healthy, degraded, unhealthy, or unknown |
Note: Use the
phoneNumberorfromfield when sending messages to control which sender address is used.
Webhooks
| Method | Path | Description |
|---|---|---|
POST | /v1/webhooks | Register a webhook |
GET | /v1/webhooks/list | List your webhooks |
GET | /v1/webhooks/:id | Get a single webhook |
PATCH | /v1/webhooks/:id | Update a webhook |
DELETE | /v1/webhooks/:id | Delete a webhook |
POST | /v1/webhooks/:id/test | Send a test event to a webhook |
POST | /v1/webhooks/:id/rotate-secret | Rotate signing secret |
GET / PUT / DELETE | /v1/webhooks | Legacy single-webhook endpoints (deprecated) |
POST | /v1/webhooks/test, /v1/webhooks/rotate-secret | Legacy single-webhook endpoints (deprecated) |
Health Check
| Method | Path | Description |
|---|---|---|
GET | /health | Service health check |
The health check endpoint requires no authentication and is not rate-limited. It returns the overall service status along with individual dependency checks.
Response
{
"status": "ok",
"timestamp": "2026-04-10T12:00:00.000Z",
"checks": {
"database": { "status": "ok" },
"redis": { "status": "ok" },
"imessage": { "status": "ok" },
"whatsapp": {
"status": "not_enabled",
"experimental": true
}
}
}The top-level status is "ok" when the database and Redis are both reachable, or "degraded" if either is down. iMessage and WhatsApp statuses are reported for visibility but do not affect the top-level status.
SMS fallback is handled natively by the iMessage provider — when an iMessage send fails to a non-iMessage recipient, the provider automatically falls back to SMS via a connected iPhone. There is no separate SMS provider to monitor.
Instance Health Status
Each iMessage provider instance is independently monitored every 2 minutes. Instance health is stored as one of four values:
| Status | Meaning |
|---|---|
healthy | Instance is reachable and fully functional |
degraded | Instance is reachable but has issues (see warnings below) |
unhealthy | Instance is unreachable or the health check failed |
unknown | Instance has not been checked yet |
Instance Health Warnings
When an instance has degraded status, the response includes an array of warning strings describing what is wrong. The following conditions trigger warnings:
| Condition | Warning | Impact |
|---|---|---|
| Private API disabled | ”Private API is disabled” | Typing indicators, reactions, read receipts, and FaceTime events will not work |
| Private API helper disconnected | ”Private API helper is not connected” | Same features as above are unavailable; requires operator intervention to resolve |
| No iMessage account detected | ”No iMessage account detected” | iMessage and FaceTime will not work on this instance |
| No iCloud account detected | ”No iCloud account detected” | iMessage and FaceTime will not work on this instance; requires operator intervention to resolve |
An instance can have multiple warnings at the same time. Warnings are cleared automatically when the next health check finds the condition resolved.
Error Codes
| Code | HTTP Status | Description |
|---|---|---|
UNAUTHORIZED | 401 | Missing or invalid Bearer token |
FORBIDDEN | 403 | API key has no associated customer |
ADDRESS_NOT_AUTHORIZED | 403 | Sender address not authorized for this customer |
NO_DEFAULT_ADDRESS | 400 | No authorized address configured for customer |
VALIDATION_ERROR | 400 | Request body failed validation |
INVALID_PHONE_NUMBER | 400 | Phone number not in E.164 format |
NOT_FOUND | 404 | Resource not found |
REPLY_NOT_FOUND | 404 | Reply target message not found |
CHANNEL_NOT_SUPPORTED | 400 | Operation not supported on channel |
NO_CHANNEL_AVAILABLE | 400 | Recipient not reachable |
INVALID_RECIPIENT | 400 | Must provide exactly one of to or conversationId on send |
CONVERSATION_NOT_FOUND | 404 | conversationId does not match any conversation for this customer |
CONVERSATION_NOT_READY | 409 | Conversation has no provider chat yet; retry after the chat is created |
GROUP_SMS_UNSUPPORTED | 400 | Group send is not supported on SMS; send to participants individually |
INVALID_STATUS | 400 | Operation not valid for current message status |
MISSING_EXTERNAL_ID | 400 | Message has no external provider ID |
ALREADY_UNSENT | 409 | Message already unsent |
ALREADY_DELETED | 409 | Message already deleted |
WEBHOOK_NOT_FOUND | 404 | Webhook does not exist or does not belong to your customer |
WEBHOOK_DUPLICATE | 409 | A webhook with this URL and event set already exists |
WEBHOOK_NOT_CONFIGURED | 404 | No webhook registered (legacy single-webhook endpoints only) |
NOT_SCHEDULED | 400 | Message is not in scheduled status |
INVALID_SCHEDULED_AT | 400 | Invalid ISO 8601 datetime format |
SCHEDULED_IN_PAST | 400 | Scheduled time is in the past |
SCHEDULED_TOO_FAR | 400 | Scheduled time is more than 30 days away |
DEPRECATED | 410 | Endpoint has been deprecated |
RATE_LIMIT_EXCEEDED | 429 | Too many requests |
PROVIDER_TIMEOUT | 504 | Upstream iMessage service timed out |
EDIT_FAILED | 502 | Message edit could not be completed |
IMESSAGE_SEND_FAILED | 500 | iMessage delivery error |
INTERNAL_ERROR | 500 | Unexpected server error |