Skip to content

Latest commit

 

History

History
385 lines (300 loc) · 8.84 KB

File metadata and controls

385 lines (300 loc) · 8.84 KB

Messages

Send and retrieve iMessages through the Texting Blue API. Messages are delivered through your connected Apple device and support text, media, and group conversations.

Message Status Lifecycle

Every message progresses through the following statuses:

queued → polling → sent → delivered
                       ↘ failed
Status Description
queued Message accepted and waiting to be picked up by your device.
polling Your device has received the message and is attempting to send it.
sent Message was handed off to Apple's iMessage servers.
delivered Recipient's device confirmed delivery.
failed Message could not be delivered. See error_code and error_message.

Send a Message

POST /v1/messages/send

Permission: messages:send

Queues a message for delivery through your connected device. Returns immediately with a 202 Accepted status.

Request Body

Field Type Required Description
to string Yes Recipient phone number in E.164 format (e.g., +14155551234).
from string Yes Your connected phone number in E.164 format.
content string Yes Message body. Maximum 5,000 characters.
media_url string No HTTPS URL of a media file to attach. Maximum 5 MB.

Response — 202 Accepted

{
  "id": "msg_xxxxxxxxxxxx",
  "status": "queued",
  "to": "+14155551234",
  "from": "+14155559876",
  "content": "Hello from Texting Blue!",
  "media_url": null,
  "created_at": "2026-02-07T12:00:00Z"
}

Examples

cURL

curl -X POST https://api.texting.blue/v1/messages/send \
  -H "x-api-key: tb_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+14155551234",
    "from": "+14155559876",
    "content": "Hello from Texting Blue!"
  }'

Node.js

const response = await fetch("https://api.texting.blue/v1/messages/send", {
  method: "POST",
  headers: {
    "x-api-key": "tb_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    to: "+14155551234",
    from: "+14155559876",
    content: "Hello from Texting Blue!",
  }),
});

const message = await response.json();
console.log(message.id); // "msg_xxxxxxxxxxxx"

Python

import requests

response = requests.post(
    "https://api.texting.blue/v1/messages/send",
    headers={
        "x-api-key": "tb_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
        "Content-Type": "application/json",
    },
    json={
        "to": "+14155551234",
        "from": "+14155559876",
        "content": "Hello from Texting Blue!",
    },
)

message = response.json()
print(message["id"])  # "msg_xxxxxxxxxxxx"

PHP

$ch = curl_init("https://api.texting.blue/v1/messages/send");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    "x-api-key: tb_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
    "Content-Type: application/json",
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
    "to" => "+14155551234",
    "from" => "+14155559876",
    "content" => "Hello from Texting Blue!",
]));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);

$message = json_decode($response, true);
echo $message["id"]; // "msg_xxxxxxxxxxxx"

Sending Media

Attach an image or file by providing a publicly accessible HTTPS URL:

curl -X POST https://api.texting.blue/v1/messages/send \
  -H "x-api-key: tb_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+14155551234",
    "from": "+14155559876",
    "content": "Check out this photo!",
    "media_url": "https://example.com/photo.jpg"
  }'

List Messages

GET /v1/messages

Permission: messages:read

Retrieve a paginated list of messages. Use query parameters to filter results.

Query Parameters

Parameter Type Default Description
from string -- Filter by sender phone number (E.164).
to string -- Filter by recipient phone number (E.164).
status string -- Filter by status: queued, polling, sent, delivered, failed.
direction string -- Filter by direction: inbound or outbound.
limit integer 50 Number of messages to return (1--100).
cursor string -- Pagination cursor from a previous response.

Response — 200 OK

{
  "messages": [
    {
      "id": "msg_xxxxxxxxxxxx",
      "status": "delivered",
      "direction": "outbound",
      "to": "+14155551234",
      "from": "+14155559876",
      "content": "Hello from Texting Blue!",
      "media_url": null,
      "created_at": "2026-02-07T12:00:00Z",
      "delivered_at": "2026-02-07T12:00:03Z"
    }
  ],
  "cursor": "msg_yyy",
  "has_more": true
}

Examples

cURL

curl -X GET "https://api.texting.blue/v1/messages?from=%2B14155559876&limit=10" \
  -H "x-api-key: tb_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"

Node.js

const params = new URLSearchParams({
  from: "+14155559876",
  limit: "10",
});

const response = await fetch(
  `https://api.texting.blue/v1/messages?${params}`,
  {
    headers: {
      "x-api-key": "tb_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
    },
  }
);

const { messages, cursor, has_more } = await response.json();

Python

import requests

response = requests.get(
    "https://api.texting.blue/v1/messages",
    headers={"x-api-key": "tb_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"},
    params={"from": "+14155559876", "limit": 10},
)

data = response.json()
messages = data["messages"]

PHP

$query = http_build_query([
    "from" => "+14155559876",
    "limit" => 10,
]);

$ch = curl_init("https://api.texting.blue/v1/messages?" . $query);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    "x-api-key: tb_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);

$data = json_decode($response, true);
$messages = $data["messages"];

Pagination

When has_more is true, pass the cursor value to fetch the next page:

curl -X GET "https://api.texting.blue/v1/messages?cursor=msg_yyy&limit=50" \
  -H "x-api-key: tb_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"

Get a Message

GET /v1/messages/:id

Permission: messages:read

Retrieve the full details of a single message, including error information for failed messages.

Response — 200 OK

{
  "id": "msg_xxxxxxxxxxxx",
  "status": "delivered",
  "direction": "outbound",
  "to": "+14155551234",
  "from": "+14155559876",
  "content": "Hello from Texting Blue!",
  "media_url": null,
  "error_code": null,
  "error_message": null,
  "created_at": "2026-02-07T12:00:00Z",
  "delivered_at": "2026-02-07T12:00:03Z"
}

For failed messages:

{
  "id": "msg_xxxxxxxxxxxx",
  "status": "failed",
  "direction": "outbound",
  "to": "+14155551234",
  "from": "+14155559876",
  "content": "Hello!",
  "media_url": null,
  "error_code": "device_offline",
  "error_message": "The sending device is not connected.",
  "created_at": "2026-02-07T12:00:00Z",
  "delivered_at": null
}

Examples

cURL

curl -X GET https://api.texting.blue/v1/messages/msg_xxxxxxxxxxxx \
  -H "x-api-key: tb_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"

Node.js

const response = await fetch(
  "https://api.texting.blue/v1/messages/msg_xxxxxxxxxxxx",
  {
    headers: {
      "x-api-key": "tb_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
    },
  }
);

const message = await response.json();
console.log(message.status); // "delivered"

Python

import requests

response = requests.get(
    "https://api.texting.blue/v1/messages/msg_xxxxxxxxxxxx",
    headers={"x-api-key": "tb_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"},
)

message = response.json()
print(message["status"])  # "delivered"

PHP

$ch = curl_init("https://api.texting.blue/v1/messages/msg_xxxxxxxxxxxx");
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    "x-api-key: tb_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);

$message = json_decode($response, true);
echo $message["status"]; // "delivered"

Errors

HTTP Status Code Description
400 invalid_request Malformed request body or invalid parameters (e.g., bad phone number format).
401 unauthorized Missing or invalid API key.
402 plan_limit_exceeded Your monthly message limit has been reached. Upgrade your plan.
403 forbidden Your API key does not have the required permission.
429 rate_limited Too many requests. Check the Retry-After header.