Skip to main content
A campaign is a named, ordered set of message steps. You create the campaign once, then enroll contacts into it. BlueReacher schedules each step, sends it from one of your dedicated iMessage lines, and stops the sequence the moment a contact replies. Campaigns send over iMessage by default and fall back to SMS when a recipient cannot receive iMessage. No A2P registration required. Base URL for every endpoint on this page:
All requests use Content-Type: application/json and a bearer token:

How sequences run

Steps and delays

A campaign holds 1 to 12 steps. Each step has content, a delay, and optional conditions. Step 1 sends as soon as the contact is enrolled, subject to quiet hours. Its delay is ignored. Every step after that waits its delay measured from the moment the previous step was sent, not from enrollment. A campaign with delays of 0, 2 days, and 3 days sends on day 0, day 2, and day 5. Delays are objects so you do not have to convert everything to minutes:
unit is minutes, hours, or days. Minimum delay between two steps is 15 minutes. Maximum is 90 days. content supports merge fields in double braces, resolved from the merge_fields object you pass at enrollment:
Add a fallback after a pipe when a value might be missing: {{first_name|there}}. A merge field with no value and no fallback blocks the send and marks the message failed with error code merge_field_missing, so the contact never gets a message with a blank in it.

Stop-on-reply and step conditions

stop_on_reply is true by default. Any inbound message from the contact on the campaign’s thread ends the sequence: pending steps are cancelled, the enrollment moves to status replied, and the reply counts once in your stats. Reactions like a thumbs up do not count as a reply and do not stop the sequence. Set stop_on_reply to false when you want every step to run no matter what, then control skipping per step with conditions. Per-step conditions fields: A skipped step does not delay the rest of the sequence. The next step’s delay still counts from the previous step that actually sent. Opt-out keywords are handled before any condition. If a contact replies with STOP, UNSUBSCRIBE, or any keyword on your account opt-out list, the enrollment ends with status opted_out and the contact is suppressed from all future campaigns on your account.

Quiet hours and pacing

Every campaign has a send window. The default window is 09:00 to 20:00 in the contact’s local time, Monday through Friday. Timezone resolution happens per contact, in this order:
  1. The timezone you pass on the contact record or at enrollment (IANA name, such as America/Chicago).
  2. The timezone inferred from the recipient number.
  3. Your account timezone.
A step scheduled outside the window rolls forward to the next time the window opens, plus a random offset of 0 to 40 minutes so your whole queue does not fire at 09:00 exactly. A step scheduled inside the window sends inside the window. Quiet hours are always enforced. There is no per-contact override. BlueReacher also paces sends automatically. Messages on the same line are spaced 45 to 90 seconds apart, and each line carries a default cap of 200 campaign messages per day. Enrollments beyond that cap queue for the next day rather than failing. Raise or lower the cap with daily_send_limit.

Create a campaign

Creates a campaign and its steps. Campaigns are created in draft status unless you set status to active. A draft campaign accepts enrollments but sends nothing until you activate it.
Auth: send Authorization: Bearer bluereacher_your_api_key. The key determines which account and lines the campaign belongs to.

Request fields

Step object:

Response fields

Example request

Example response

201 Created

Errors

Add a contact to a campaign

Enrolls one contact into a campaign. Enrollment assigns a line, resolves the contact’s timezone, and schedules step 1.
Auth: send Authorization: Bearer bluereacher_your_api_key. Send one contact per request. The rate limit is 600 requests per minute, so a straightforward loop handles bulk enrollment. If the contact is already enrolled in this campaign with an active status, the request returns 409 already_enrolled and changes nothing. If the contact is on your opt-out list, the request returns 409 contact_opted_out.

Request fields

Response fields

Example request

Enrolling a list from Node:

Example response

201 Created

Errors

Cancel pending messages

Stops every message still waiting to go out for a contact. Use this when a lead books a call, replies on another channel, or asks you to stop.
Auth: send Authorization: Bearer bluereacher_your_api_key. Cancellation covers messages in scheduled and queued status. A message already handed to the network cannot be recalled, so anything in sent or delivered stays as it is. Cancelling stops sends and does not add the contact to your opt-out list. To suppress a contact from all future campaigns, add them to your opt-out list instead. Omit campaign_id to cancel across every campaign the contact is in. The endpoint is idempotent. Calling it twice returns cancelled: 0 the second time.

Request fields

Response fields

Example request

Example response

200 OK

Errors

Get campaign stats

Returns performance counters for one campaign, with an optional per-step or per-line breakdown.
Auth: send Authorization: Bearer bluereacher_your_api_key. Counters update within about 60 seconds of the underlying event. Rates are decimals rounded to four places, so 0.1834 means 18.34 percent. A rate is null when its denominator is zero. booked counts contacts with a booking recorded against the campaign. Bookings come from a connected calendar or from your own system posting to the bookings webhook. With no booking source connected, booked and booking_rate return null rather than 0, so you can tell “nothing tracked” apart from “nobody booked”.

Query parameters

Response fields

Example request

Example response

200 OK

Errors

Status reference

Enrollment status values: Message status values inside a campaign:

Error format

Every error returns the same shape:
field is present when the problem maps to a specific request field, and absent otherwise. Treat code as the stable value to branch on. Message text can change. Any 5xx response is safe to retry with exponential backoff. Use external_id on /create-campaign and /add-contact-to-campaign so retries do not create duplicates.

Rate limits

600 requests per minute per API key, counted across all endpoints. A 429 includes a Retry-After header with the seconds to wait. Campaign send throughput is governed separately by daily_send_limit and line pacing, not by the API rate limit.