Blue Reacher
Overview11 / 50

Overview

The Blue Reacher REST API: base URL, bearer authentication, test mode, drip and instant sending, idempotency, errors, rate limits and webhook signature verification.

The Blue Reacher API is JSON over HTTPS. Anything that can make an HTTP request can drive it, and there is no SDK you are required to install. If your agent runs on Claude Code, Claude Desktop or Cursor, the MCP server exposes the same surface as callable tools with one install command.

The machine-readable contract lives at /openapi.json (OpenAPI 3.1). This reference, that spec, and the live API are generated against the same contract; if you ever catch them disagreeing, tell us and we will treat it as a bug.

Base URL

https://api.bluereacher.com/v1

Every path in this reference is relative to that base. There is no separate sandbox host: test mode is selected by the key you use, not the URL.

Authentication

Every request carries a bearer token.

Authorization: Bearer brk_your_api_key

Live keys are prefixed brk_live_, test keys brk_test_. Keys carry read, write or admin permission, and each key is bound to one workspace, so an agent running in staging can hold a key that cannot reach the lines your customers know. Keys are created in the dashboard; the full value is shown once.

Keep keys server-side. Anyone holding a live key can send from your lines. A leaked key is revoked in the dashboard and stops working immediately.

Test mode

brk_test_ keys are answered by a simulator: identical request validation and identical response shapes, but no message is ever sent and no contact, line or queue row is touched. Every sandbox response carries "sandbox": true in the body plus an X-BlueReacher-Sandbox: true header, so it is impossible to mistake simulated traffic for real traffic.

Simulated sim_ message ids progress on a timer, so polling behaves realistically: a drip send goes pending then sent after about 5 seconds, an instant send goes sent then delivered after about 10 seconds. Test keys cover messaging, /v1/conversations, /v1/devices, /v1/status, signals, opt-out, bot, MCP, /v1/contacts and /v1/capability (all answered by the simulator with the live shapes) and /v1/usage (a fixed sample). Two surfaces need a live key: conversation history (GET /v1/conversations/{contact_id}/messages returns 403 sandbox_key_not_supported_here on a test key) and the calling endpoints, which are in private beta. A test key is bound to no workspace, so it can never read or write anything real.

How sending works

POST /v1/messages has two lanes, and the default is the safe one.

Drip (default). The send is inserted into the paced pipeline the platform itself uses: scheduled about 10 minutes out, delivered inside your organization's local send window (9:00 to 18:00 by default), spaced against everything else the line is doing, and counted against the line's daily new-contact cap. delay_minutes, window_start_hour and window_end_hour are tunable per request. This is the lane for outreach.

Instant (send_mode: "instant"). Dispatches through the live pipeline right now. Built for conversational replies, not blasts: velocity-capped at 10/min and 75/day per key, on top of all line-level protections, and it requires an online line (409 no_online_device otherwise). Group sends and message effects require instant.

Media attaches on either lane with media_urls (1 to 10 HTTPS URLs). A successful send means accepted, not delivered; track the outcome with webhooks (preferred) or by polling GET /v1/status/{message_id}.

Idempotency

Pass an Idempotency-Key header (up to 255 characters) on POST /v1/messages. A duplicate request within 24 hours replays the original response with an Idempotent-Replay: true header instead of sending twice. A failed attempt releases the key so a retry can go through; a concurrent duplicate gets 409 idempotency_conflict. Set it from something stable in your own system and a retry after a timeout can never double-send.

Errors

Errors return JSON with a human message and a machine-readable code:

{
  "error": "No contact with this phone in the workspace",
  "error_code": "contact_not_found",
  "help": "Create the contact first with POST /v1/contacts"
}

400, 401, 403, 404 and 422 mean the same request will fail again, so do not retry blindly. 409 usually means the work is already done or in flight. 429 and 5xx are retryable with backoff. The full code list is in Errors and status codes.

Rate limits

Two different limits, and mixing them up is the most common integration bug.

API rate limitsSending capacity
CountsHTTP requestsNew contacts reached
ScopePer API keyPer line
Signal429 with retryAfterDrip queue paces automatically
FixBack off and retryAdd lines, or let the queue spread the volume

API limits: 60/min across all /v1 routes, 30/min on POST /v1/messages, 10/min and 75/day on instant sends, 300/min on contacts reads. Rate-limited responses return 429 with retryAfter in seconds. The full table and the capacity model are in Rate limits and sending capacity.

Webhooks

Events are pushed as signed HTTPS POSTs: message.received, message.sent, message.delivered, message.read, message.failed, reaction.received, contact.created, contact.opted_out, contact.resubscribed, device.status_changed, device.health_changed. Every delivery carries X-BlueReacher-Signature: sha256=<HMAC-SHA256(secret, raw_body)>; verify by recomputing the HMAC over the raw body before parsing. Deliveries are at-least-once with a stable event_id to deduplicate on. Details and payloads are in Webhooks.

Conventions

RuleDetail
Field namessnake_case, in both directions.
Phone numbersE.164 only, for example +14155550142. Normalise before you send.
TimestampsISO 8601 strings, UTC.
IdsOpaque strings. Sandbox ids are prefixed sim_. Do not parse ids for meaning.
Empty valuesnull for "no value", never an empty string.
Lines and devicesThe same object. Prose says line; API field names say device_id, device_name.

Versioning

The path carries the version. /v1 is current and additive: new fields and new event types can appear, so parse defensively and ignore what you do not recognise. Anything that would break an existing integration ships behind a new version.

On this page