Skip to main content

Base URL and format

Send JSON with Content-Type: application/json and authenticate with Authorization: Bearer … (Authentication). Call the API from your server only: keys must never reach a browser or mobile app.
  • Timestamps are ISO 8601 in UTC, for example 2026-10-14T17:00:00.000Z.
  • Phone numbers are E.164, for example +15555550123. Recipients must be US numbers.
  • Services are iMessage, RCS and SMS in API responses. Webhook payloads use lowercase imessage, rcs and sms.
  • Unknown body fields are rejected with VALIDATION_ERROR, and so is a body that isn’t valid JSON.
Common mistake: in a query string, + means a space. URL-encode a phone number as %2B15555550123, for example GET /v1/contacts/search?phone_number=%2B15555550123. In a JSON body, write +15555550123 as is.

Responses

Every successful response has the same envelope. The result is in data:
Errors use status: "error" and an error object with a stable code. Check status before you read data: an error response has no data. See Errors.

IDs

Resource IDs are opaque strings with a prefix that tells you the type. Store them as strings and don’t parse them. (Credential IDs in GET /v1/me are plain UUIDs.) An ID with the wrong prefix for an endpoint returns INVALID_PUBLIC_ID. IDs from another workspace return NOT_FOUND.

Pagination

Most list endpoints return a page of results and a cursor. These return everything at once, with no cursor: GET /v1/phone-numbers, GET /v1/contacts/search (up to limit matches), GET /v1/workflows, GET /v1/team-members/assignable and GET /v1/webhook-subscriptions.
  • limit sets the page size: 1 to 100, default 25. Contact search is the exception: 1 to 25, default 10, with no cursor.
  • While has_more is true, pass next_cursor as cursor to get the next page. Keep the same filters.
  • Cursors are opaque and tamper-proof. A changed or foreign cursor returns INVALID_CURSOR. Leave cursor out of the first request; a placeholder such as cursor=string is rejected the same way.

Tracing requests

  • Every response includes trace_id in the body and an X-Trace-ID header.
  • Send your own X-Request-ID header to correlate with your logs. Blujay echoes it back as client_request_id in error responses and in most success responses. The send responses (POST /v1/chats, POST /v1/chats/{chat_id}/messages and POST /v1/messages/{message_id}/retry) leave it out, so map your request ID to the returned message.id yourself there.
Log both. When you contact support@blujay.io, include the trace_id, your X-Request-ID and the time of the request. Never include your API key.

Retries and timeouts

Use a client timeout of about 30 seconds. If a write times out or returns a retryable error, retry it with the same Idempotency-Key, so a request that actually succeeded is never repeated. See Idempotency and Rate limits.

Time zones and reset_at

Every timestamp is UTC. One limit follows a local clock: a number’s new-contact count resets at midnight in your workspace’s time zone, which the API doesn’t expose. Don’t compute the reset yourself. Read limits.new_contacts.reset_at on the number, or error.details[0].reset_at on a 429, and wait until then. See Phone numbers and limits.

Versioning

The version is in the path: /v1. Within v1, Blujay works to keep a well-written client working. Changes Blujay can make at any time:
  • adding endpoints, optional request fields and response fields;
  • adding error codes for new situations, webhook event types and enum values;
  • rewording error.message and other text meant for people;
  • adjusting sending limits and messaging-safety rules to protect delivery. These always use documented error codes, and Dedicated numbers report their current limits in GET /v1/phone-numbers.
Changes Blujay avoids:
  • removing or renaming an endpoint, field, error code or enum value;
  • making an optional request field required;
  • changing what a field means or its type;
  • returning a different error code for the same situation;
  • changing a webhook payload within a version you’ve pinned.
If one of these is ever needed, we’ll email the admins of workspaces that use the API ahead of time, and the API reference will mark the affected item Deprecated. The only exception is a change needed right away for security or legal reasons. Write your client to ignore fields it doesn’t know and to handle unknown enum values. Branch on error.code, never on error.message. Webhook payload shapes are versioned separately by date, and a subscription stays on the version you pin. See Webhook versions.

Next steps

Idempotency

Retry safely without sending twice.

Rate limits

Request limits and how to back off.

Phone numbers and limits

Sending limits and the live fields that report them.

Errors

Every error code and whether it is retryable.