Blue Reacher
n8n43 / 50

n8n

Self-hosted or cloud n8n workflows that send iMessage and process replies, including proper signature verification in a Code node.

n8n is the best of the no-code options for this, because you can verify the webhook signature and still keep the build visual.

Credential setup

Credentials, New, Header Auth. Name it Blue Reacher. Header name Authorization, header value Bearer brk_your_api_key. Every HTTP Request node then references the credential instead of carrying the key.

Outbound workflow

  1. Your trigger node: Schedule, Webhook, a database node, a CRM node.
  2. HTTP Request node:
    • Method POST
    • URL https://api.bluereacher.com/v1/messages
    • Authentication: Generic Credential Type, Header Auth, Blue Reacher
    • Send Body: on, Body Content Type JSON
    • Specify Body: Using JSON
{
  "to": "{{ $json.phone }}",
  "message": "Hi {{ $json.first_name }}, it's Marcus from Northside. Quick question about your enquiry.",
  "metadata": { "crm_id": "{{ $json.id }}" }
}
  1. Add Settings, Retry On Fail with 3 tries for transient 5xx responses. Do not retry 4xx; those need a fix, not a repeat. Error semantics are in errors and status codes.

Pacing a batch

Sends are paced platform-side, so you do not need to build delays for line health, and cold volume past today's capacity queues rather than erroring. The one limit your workflow can hit is the API request rate (30/min on POST /v1/messages): a Split In Batches node with a small Wait keeps a big batch under it, and available_today from GET /v1/devices at the start of the run tells you how much lands today versus queues for tomorrow.

Inbound workflow with signature verification

  1. Webhook node, method POST. Set Response Mode to Immediately so n8n acknowledges before processing.
  2. Register the production URL as your Blue Reacher webhook endpoint.
  3. Code node, run once for each item:
const crypto = require('crypto');

const raw = JSON.stringify($input.item.json.body);
const provided = $input.item.json.headers['x-bluereacher-signature'];
const expected = 'sha256=' + crypto
  .createHmac('sha256', $env.BLUEREACHER_WEBHOOK_SECRET)
  .update(raw)
  .digest('hex');

if (
  !provided ||
  provided.length !== expected.length ||
  !crypto.timingSafeEqual(Buffer.from(provided), Buffer.from(expected))
) {
  throw new Error('Invalid signature');
}

return $input.item;

Enable Raw Body on the Webhook node if your n8n version re-serializes JSON differently from the sent bytes, and hash the raw string instead. A signature that never matches is almost always this.

  1. Switch node on {{ $json.body.event }}: route message.received to your CRM update, contact.opted_out to your suppression flag, message.sent / message.failed to your delivery logging.

A pattern worth building

Reply, then classify, then route. Send the inbound text to an LLM node with a short prompt that returns one of interested, not-interested, question, or wrong-number, then branch: interested pings the rep in Slack immediately, question drafts a suggested answer for a human to approve, not-interested and wrong-number update the CRM and stop the sequence.

Keep a human in the loop on anything that sends. Auto-replying to a live prospect from a classifier is how a good conversation becomes a bad one, and an incorrect auto-reply to someone asking to opt out is a compliance problem, not just an awkward one.

On this page