Skip to main content
Every conversation in Blujay is a chat between one of your Blujay phone numbers and one or more recipients. You start a chat and send its first message with 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
Save data.chat_id for follow-ups and data.message.id to track the message.
  • from is one of your Blujay phone numbers, as a string in E.164 ("+15555550100") or a common US format. List your numbers with GET /v1/phone-numbers and pick one whose send_status.allowed is true.
  • For a direct chat, from is 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 returns 422 FROM_NUMBER_REQUIRED. A group chat always needs from.
  • to takes 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_id back.

Rules for a first message

Until the contact replies to your number, Blujay rejects a send with 400 when it contains: At most 6 texts go out in a row without a response (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.
Blujay treats anything that looks like a domain name as a link, including example.com without https:// and the domain in an email address such as help@example.com. Keep those out of a first message too.
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.
Follow the message to 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 with POST /v1/chats/{chat_id}/messages. The body is a message object with the same shape as in POST /v1/chats.
The response is the same 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 of parts, 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:
Other optional 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 out preferred_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 the 400 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. Pass limit (1 to 100, default 25) and, for the next page, the previous page’s next_cursor as cursor. Stop when has_more is false.
An inbound message has 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.
Common mistakes
  • 202 means queued, not delivered. Track the message before you tell anyone it arrived.
  • from is a string ("+15555550100"), not an array and not a phone_number_id. Endpoints that take one of your numbers by ID use from_phone_number_id.
  • URL-encode + in query strings (%2B15555550123), or the server reads it as a space.
  • next_cursor values are opaque. Pass them back exactly as returned; never send a placeholder such as cursor=string.
  • The 202 field id is the message_id you read back later. delivery_status: queued is status: accepted on reads.

Retry a failed message

POST /v1/messages/{message_id}/retry sends a failed message again with the same message_id.
A successful retry returns 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

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.