POST /v1/chats, then send everything after that with POST /v1/chats/{chat_id}/messages. Both calls return 202 Accepted as soon as Blujay has queued the message, and the message is sent in the background.
Send the first message
POST /v1/chats starts a chat from one of your numbers and sends its first message. One number in to starts a direct chat. Two or more start a group chat (see Group chats).
202 Accepted
data.chat_id for follow-ups and data.message.id to track the message.
fromis one of your Blujay phone numbers, as a string in E.164 ("+15555550100") or a common US format. List your numbers withGET /v1/phone-numbersand pick one whosesend_status.allowedistrue.- For a direct chat,
fromis optional. When you leave it out, Blujay uses the number the recipient already knows you on. If there is none, it uses your only number, or the default number of the teammate the contact is assigned to. If none of those applies, the request returns422FROM_NUMBER_REQUIRED. A group chat always needsfrom. totakes US phone numbers, in E.164 (+15555550123) or a common US format such as(555) 555-0123. Email addresses aren’t accepted as recipients.- If you already have a direct chat with that recipient from the same number, the message goes into that chat and you get its
chat_idback.
Rules for a first message
Until the contact replies to your number, Blujay rejects a send with400 when it contains:
- a link, in a
linkpart or in the text (FIRST_MESSAGE_LINK_NOT_ALLOWEDonPOST /v1/chats,PRE_REPLY_CONTENT_BLOCKEDon follow-ups), - a
mediaorvcardpart (PRE_REPLY_CONTENT_BLOCKED), - text over 300 characters (
PRE_REPLY_LENGTH_EXCEEDED).
PRE_REPLY_LIMIT_REACHED). A first message also can’t be a threaded reply or carry an effect or text styles: reply_to returns 400 VALIDATION_ERROR, and effect or text_decorations return 400 FEATURE_NOT_ENABLED.
The full rules, what counts as a response, and the group variants are in Sending rules.
These rules apply to every POST /v1/chats request, even when the recipient already has a chat with your number. Use POST /v1/chats/{chat_id}/messages for everything after the first message.
What 202 Accepted means
202 means Blujay validated the request, saved the message and queued it for sending. It doesn’t mean the message was sent or delivered.
Send vs read field names. The
202 body names the message id and its state delivery_status. Every read (GET /v1/messages/{message_id}, the list endpoints) and every webhook names them message_id and status. Reads also drop from, to and direction. Map data.message.id to message_id when you store it.sent, delivered, read or failed with webhooks or by polling. See Track a message.
If the request times out or you get a 5xx, send it again with the same Idempotency-Key. If Blujay already accepted it, you get 200 with the original response instead of a second message. A request Blujay rejected with a 4xx isn’t remembered under its key, so you can fix the request and send it again with the same key. See Idempotency.
Send a follow-up
Send every message after the first withPOST /v1/chats/{chat_id}/messages. The body is a message object with the same shape as in POST /v1/chats.
202 shape as the first send. Once the contact has replied, follow-ups can use every part type, including links, plus threaded replies and effects and text styles. Until then the first-message rules still apply.
Message parts
A message is a list ofparts, sent in order.
A message has 1 to 100 parts, at most 40 of them
media or vcard, and at most 40,000 characters of text in total. Text and media parts can be mixed, for example a caption followed by a photo:
message fields:
One optional field sits next to
message at the top level of the request:
Unknown fields are rejected with
400 VALIDATION_ERROR.
Choose a service or use Auto
In a direct chat, leave outpreferred_service to use Auto: Blujay chooses iMessage, RCS or SMS for the recipient. Auto is the right choice for most messages.
Set preferred_service only when you need a specific outcome. Values are case-sensitive.
Group chats don’t accept
preferred_service and return 400 VALIDATION_ERROR. To check a number before you send, see iMessage, RCS or SMS.
Handle send errors
Most send errors come back before anything is queued, so nothing was sent. None of the400 and 403 errors below is retryable; fix the request or wait for the contact.
Every error carries a
trace_id. Include it when you contact support. See Errors for the envelope.
List chats and messages
All list endpoints return a page of results, newest change first. Passlimit (1 to 100, default 25) and, for the next page, the previous page’s next_cursor as cursor. Stop when has_more is false.
status: received. Messages are ordered by when they last changed (updated_at), not when they were sent. An edit, a read receipt or any status change moves a message back to the top, so a message can appear on a page you already fetched. Deduplicate on message_id, and sort by created_at if you’re showing a conversation. The trace_id on a message is the trace of the request that sent it; inbound messages have none.
Retry a failed message
POST /v1/messages/{message_id}/retry sends a failed message again with the same message_id.
202 with the same shape as a send, and the message’s status goes back to accepted.
A failed message’s retryable field tells you whether retry is possible. false means the message may already have reached the recipient, so check the conversation before you send a replacement. true means it never left Blujay; retry can still be refused if the recipient has since opted out or the number can no longer send. Only direct-chat messages that failed before they left Blujay can be retried. Everything else returns 409 RETRY_NOT_ALLOWED, including group messages and messages that may already have reached the recipient. Blujay refuses those so the recipient never gets a duplicate. No field on the message tells you in advance whether a retry is allowed; the call is the check. When you get RETRY_NOT_ALLOWED, don’t automatically send a replacement: check the message status and the conversation first, and send a new message only when you’re sure the first one didn’t arrive or a duplicate is acceptable. Retrying an inbound message returns 400 INVALID_MESSAGE.
Limitations
- Recipients must be US phone numbers. Email addresses return
400VALIDATION_ERROR. - Until the contact replies: no links, media or contact cards (
400PRE_REPLY_CONTENT_BLOCKED), 300 characters per text (400PRE_REPLY_LENGTH_EXCEEDED), 6 texts in a row (400PRE_REPLY_LIMIT_REACHED). See Sending rules. - Free Plan numbers send only to verified contacts (
403CONTACT_NOT_VERIFIED) and can’t use group chats (400FEATURE_NOT_ENABLED). AI Agent numbers only reply to people who text first (403CONTACT_MUST_TEXT_FIRST). See Plans. - A
linkpart must be the only part in its message. preferred_serviceworks in direct chats only (400VALIDATION_ERRORin groups).- Webhook payloads don’t carry
client_reference; reads do. - Retry works only for direct-chat messages that failed before leaving Blujay; check
retryableon the message first (409RETRY_NOT_ALLOWED).
Next steps
Sending rules
The pre-reply rules, what counts as a response, and the exact error codes.
Track a message
Statuses, services and failure codes, and how to follow them.
Media
Upload files once or send them from a URL.
Idempotency
Retry any send safely without sending twice.