curl --request POST \
--url https://api.blujay.io/v1/chats/{chat_id}/messages \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"message": {
"parts": [
{
"type": "text",
"text": "Here is the follow-up we discussed."
}
]
}
}
'{
"status": "success",
"trace_id": "trc_8f2a1c0e4b7d",
"timestamp": "2026-10-14T17:01:00.000Z",
"data": {
"chat_id": "chat_abc123",
"message": {
"id": "msg_abc123",
"direction": "outbound",
"delivery_status": "queued",
"service": "iMessage",
"from": "+15555550100",
"to": [
"+15555550123"
],
"created_at": "2026-10-14T17:01:00.000Z",
"parts": [
{
"type": "text",
"text": "Hi Ava, this is Sam from the Northwind support team.",
"text_decorations": [
{
"range": [
1,
1
],
"style": "bold"
}
]
}
],
"preferred_service": "iMessage",
"reply_to": {
"message_id": "msg_abc123",
"part_index": 0,
"thread_root_message_id": "msg_abc123"
}
}
},
"client_request_id": "<string>"
}Send a message
Sends a message in an existing chat. Blujay returns 202 as soon as the message is accepted and sends it in the background. Follow its progress with message webhooks or GET /v1/messages/{message_id}.
A message can have text, link, media and vcard parts. A link part must be the only part.
Optional features:
reply_tosends an iMessage threaded reply. It isn’t available for SMS or RCS chats.effectadds an iMessage screen or bubble effect to a text message.text_decorationsstyles text in direct iMessage chats.preferred_servicepicks iMessage, RCS or SMS for a direct chat. Omit it to let Blujay choose. Group chats don’t accept it.
If an effect or text style can’t be delivered because the message goes out as SMS or RCS, the text is still sent and the message lists what was dropped in degradations.
These limits protect your number’s reputation. Until the contact replies to this number with a text or a tapback:
- Links, media and contact cards return
400 PRE_REPLY_CONTENT_BLOCKED. - Text longer than 300 characters returns
400 PRE_REPLY_LENGTH_EXCEEDED. At any time, before or after a first reply, at most 6 texts go to a contact in a row without a response (a text or tapback from them, or a message from them in a group) on any of your numbers. Calls don’t count as a response. The next returns400 PRE_REPLY_LIMIT_REACHED. With an API key,pre_reply_override: truesends a 7th once. The count starts again when the contact responds. In a group, the content limits apply until someone in the group replies, and the 6-text limit counts the group’s texts since anyone in it last wrote. These errors are never retryable and never429.
Send an Idempotency-Key header (or message.idempotency_key) so you can safely retry. Repeating a request with the same key returns 200 with the original response instead of sending again.
Errors worth handling:
400 PRE_REPLY_*: the contact hasn’t replied yet, or hasn’t responded to your last 6 texts (see the limits above).403 RECIPIENT_SUPPRESSED: the recipient opted out.403 CONTACT_NOT_VERIFIED: on the Free Plan, the contact hasn’t texted your Free Plan number to verify yet.403 CONTACT_MUST_TEXT_FIRST: the number is on the AI Agent plan: it only replies to people who text it first, and can’t send to group chats.429 RATE_LIMIT_EXCEEDEDor429 QUEUE_FULL: the sending number hit a limit. WaitRetry-Afterseconds. Each number’s current limits are inGET /v1/phone-numbersunderlimits.409 SENDER_RETIRED: the chat’s number was retired. Start a new chat from an active number.503 QUEUE_UNAVAILABLE: Blujay couldn’t accept the message right now. Retry with the sameIdempotency-Key.
curl --request POST \
--url https://api.blujay.io/v1/chats/{chat_id}/messages \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"message": {
"parts": [
{
"type": "text",
"text": "Here is the follow-up we discussed."
}
]
}
}
'{
"status": "success",
"trace_id": "trc_8f2a1c0e4b7d",
"timestamp": "2026-10-14T17:01:00.000Z",
"data": {
"chat_id": "chat_abc123",
"message": {
"id": "msg_abc123",
"direction": "outbound",
"delivery_status": "queued",
"service": "iMessage",
"from": "+15555550100",
"to": [
"+15555550123"
],
"created_at": "2026-10-14T17:01:00.000Z",
"parts": [
{
"type": "text",
"text": "Hi Ava, this is Sam from the Northwind support team.",
"text_decorations": [
{
"range": [
1,
1
],
"style": "bold"
}
]
}
],
"preferred_service": "iMessage",
"reply_to": {
"message_id": "msg_abc123",
"part_index": 0,
"thread_root_message_id": "msg_abc123"
}
}
},
"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.
Headers
A unique value you choose (1–255 characters) that makes this request safe to retry. Send the same key with the same request and Blujay returns the original response instead of doing the work again. Use a new key for each new action.
Blujay keeps keys for at least 24 hours. Keys are separate for each API key and each route family (for example every POST /v1/chats/{chat_id}/messages shares one). Reusing a key with a different request returns 409 IDEMPOTENCY_REQUEST_MISMATCH. Sending it while the first request is still running returns 409 IDEMPOTENCY_IN_PROGRESS; retry shortly.
1 - 255Path Parameters
^chat_[A-Za-z0-9_-]+$"chat_abc123"
Body
Show child attributes
Show child attributes
API keys only. Sends a 7th text in a row to a contact who hasn't responded, once. A later text before they respond still returns PRE_REPLY_LIMIT_REACHED. It never lifts PRE_REPLY_CONTENT_BLOCKED or PRE_REPLY_LENGTH_EXCEEDED, and connected apps such as Zapier can't use it.
Response
This Idempotency-Key was already used for the same request. The response is the original one, and nothing was sent again.
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.