curl --request GET \
--url https://api.blujay.io/v1/messages/{message_id}/thread \
--header 'Authorization: Bearer <token>'const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.blujay.io/v1/messages/{message_id}/thread', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.blujay.io/v1/messages/{message_id}/thread"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.blujay.io/v1/messages/{message_id}/thread"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}require 'uri'
require 'net/http'
url = URI("https://api.blujay.io/v1/messages/{message_id}/thread")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.blujay.io/v1/messages/{message_id}/thread",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}{
"status": "success",
"trace_id": "trc_8f2a1c0e4b7d",
"timestamp": "2026-10-14T17:01:00.000Z",
"data": {
"data": [
{
"message_id": "msg_abc123",
"chat_id": "chat_abc123",
"phone_number_id": "phone_abc123",
"direction": "inbound",
"preferred_service": "iMessage",
"service": "iMessage",
"status": "received",
"parts": [
{
"type": "text",
"text": "Hi Ava, this is Sam from the Northwind support team.",
"text_decorations": [
{
"range": [
1,
1
],
"style": "bold"
}
]
}
],
"degradations": [
{
"code": "effect_not_delivered",
"reason": "The iMessage effect was not delivered because the message was sent as SMS or RCS."
}
],
"reply_to": {
"message_id": "msg_abc123",
"part_index": 0,
"thread_root_message_id": "msg_abc123"
},
"sent_at": "2026-10-14T17:01:02.000Z",
"delivered_at": "2026-10-14T17:01:03.000Z",
"read_at": "2023-11-07T05:31:56Z",
"failed_at": "2023-11-07T05:31:56Z",
"failure": {
"code": "recipient_unavailable",
"reason": "The messaging network rejected the message."
},
"edited_at": "2026-10-14T17:01:00.000Z",
"edit_count": 0,
"unsent_at": "2026-10-14T17:01:00.000Z",
"unsent_scope": "all",
"created_at": "2026-10-14T17:01:00.000Z",
"updated_at": "2026-10-14T17:01:00.000Z",
"contact_id": "contact_abc123",
"sender": {
"type": "phone",
"address": "<string>",
"contact_id": "contact_abc123"
},
"failure_code": "ATTACHMENT_FETCH_FAILED",
"effect": {
"type": "screen",
"name": "confetti"
},
"retryable": true,
"client_reference": "<string>",
"edit_history": [
{
"text": "See you at 3",
"replaced_at": "2026-10-14T17:01:00.000Z"
}
],
"trace_id": "trc_8f2a1c0e4b7d"
}
],
"has_more": true,
"limit": 25,
"next_cursor": "AbC9xQ2nRk7vT1mZ"
},
"client_request_id": "<string>"
}{
"status": "error",
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"type": "validation",
"http_status": 400,
"retryable": false,
"doc_url": "https://docs.blujay.io/errors/validation-error",
"details": []
},
"trace_id": "trc_8f2a1c0e4b7d",
"timestamp": "2026-10-14T00:00:00.000Z"
}{
"status": "error",
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "<string>",
"type": "validation",
"http_status": 400,
"retryable": true,
"doc_url": "<string>",
"details": [
{}
],
"retry_after": 2,
"reset_at": "2026-10-14T17:01:00.000Z"
},
"trace_id": "trc_8f2a1c0e4b7d",
"timestamp": "2026-10-14T17:01:00.000Z",
"client_request_id": "<string>"
}List a reply thread
Given any message in an iMessage reply thread, returns the original message and every reply, including replies that are still sending. Reactions are never part of a thread.
A message that isn’t in a thread, or a reaction, returns only itself. One request covers at most the 1,000 nearest messages in the thread, in the order you ask for.
curl --request GET \
--url https://api.blujay.io/v1/messages/{message_id}/thread \
--header 'Authorization: Bearer <token>'const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.blujay.io/v1/messages/{message_id}/thread', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.blujay.io/v1/messages/{message_id}/thread"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.blujay.io/v1/messages/{message_id}/thread"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}require 'uri'
require 'net/http'
url = URI("https://api.blujay.io/v1/messages/{message_id}/thread")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.blujay.io/v1/messages/{message_id}/thread",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}{
"status": "success",
"trace_id": "trc_8f2a1c0e4b7d",
"timestamp": "2026-10-14T17:01:00.000Z",
"data": {
"data": [
{
"message_id": "msg_abc123",
"chat_id": "chat_abc123",
"phone_number_id": "phone_abc123",
"direction": "inbound",
"preferred_service": "iMessage",
"service": "iMessage",
"status": "received",
"parts": [
{
"type": "text",
"text": "Hi Ava, this is Sam from the Northwind support team.",
"text_decorations": [
{
"range": [
1,
1
],
"style": "bold"
}
]
}
],
"degradations": [
{
"code": "effect_not_delivered",
"reason": "The iMessage effect was not delivered because the message was sent as SMS or RCS."
}
],
"reply_to": {
"message_id": "msg_abc123",
"part_index": 0,
"thread_root_message_id": "msg_abc123"
},
"sent_at": "2026-10-14T17:01:02.000Z",
"delivered_at": "2026-10-14T17:01:03.000Z",
"read_at": "2023-11-07T05:31:56Z",
"failed_at": "2023-11-07T05:31:56Z",
"failure": {
"code": "recipient_unavailable",
"reason": "The messaging network rejected the message."
},
"edited_at": "2026-10-14T17:01:00.000Z",
"edit_count": 0,
"unsent_at": "2026-10-14T17:01:00.000Z",
"unsent_scope": "all",
"created_at": "2026-10-14T17:01:00.000Z",
"updated_at": "2026-10-14T17:01:00.000Z",
"contact_id": "contact_abc123",
"sender": {
"type": "phone",
"address": "<string>",
"contact_id": "contact_abc123"
},
"failure_code": "ATTACHMENT_FETCH_FAILED",
"effect": {
"type": "screen",
"name": "confetti"
},
"retryable": true,
"client_reference": "<string>",
"edit_history": [
{
"text": "See you at 3",
"replaced_at": "2026-10-14T17:01:00.000Z"
}
],
"trace_id": "trc_8f2a1c0e4b7d"
}
],
"has_more": true,
"limit": 25,
"next_cursor": "AbC9xQ2nRk7vT1mZ"
},
"client_request_id": "<string>"
}{
"status": "error",
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"type": "validation",
"http_status": 400,
"retryable": false,
"doc_url": "https://docs.blujay.io/errors/validation-error",
"details": []
},
"trace_id": "trc_8f2a1c0e4b7d",
"timestamp": "2026-10-14T00:00:00.000Z"
}{
"status": "error",
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "<string>",
"type": "validation",
"http_status": 400,
"retryable": true,
"doc_url": "<string>",
"details": [
{}
],
"retry_after": 2,
"reset_at": "2026-10-14T17:01:00.000Z"
},
"trace_id": "trc_8f2a1c0e4b7d",
"timestamp": "2026-10-14T17:01:00.000Z",
"client_request_id": "<string>"
}Authorizations
Your workspace API key, sent as Authorization: Bearer blu_.... Workspace admins can find it in the Blujay app under API.
An API key has full access to your workspace and works on every endpoint. There is no test mode: every key sends real messages and changes real data, so keep it secret and use it only from your server.
Each API key can make 600 read requests and 120 write requests per minute, plus 60 attachment requests per minute. Over a limit you get 429 RATE_LIMIT_EXCEEDED with Retry-After.
Path Parameters
^msg_[A-Za-z0-9_-]+$"msg_abc123"
Query Parameters
Maximum number of items to return.
1 <= x <= 100The next_cursor value from the previous page. Leave it out to get the first page.
Oldest first (asc, the default) or newest first (desc).
asc, desc Response
A page of messages in the thread.
Wraps every successful response. The result is in data.
success ^trc_[A-Za-z0-9_-]+$"trc_8f2a1c0e4b7d"
"2026-10-14T17:01:00.000Z"
Show child attributes
Show child attributes
Your X-Request-ID header value, echoed back when you send one. Not included on the send responses (POST /v1/chats, POST /v1/chats/{chat_id}/messages, POST /v1/messages/{message_id}/retry); keep your own mapping from your request ID to the returned message ID there.