Blue Reacher
CRM integrations6 / 50

CRM integrations

Wire any CRM to Blue Reacher: the send API for outbound, webhooks for inbound replies, and contact custom_fields for record matching, with worked patterns for HubSpot, Salesforce, and no-code tools.

Before building anything: check whether your CRM already has a native integration. GoHighLevel, HubSpot, Salesforce, Close, Pipedrive, Follow Up Boss, and Jobber all connect natively, with conversations synced for you and nothing to host. This page is for everything else, and for the send-triggering direction the native connections leave to you.

Every CRM integration is the same three wires, whatever the CRM.

Outbound. Your CRM automation calls POST /v1/messages when a record hits a condition. New lead created, stage changed, no reply in three days.

Inbound. Blue Reacher webhooks push message.received to your endpoint, and your handler writes the reply onto the right CRM record.

Identity. The contact's custom_fields tie the two directions together. Stamp your CRM record ID onto the Blue Reacher contact once, and every webhook resolves to the right record without fragile phone-number guessing.

Outbound: your CRM calls the send API

curl -X POST https://api.bluereacher.com/v1/messages \
  -H "Authorization: Bearer brk_your_api_key" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 0035f00000LmQ2xAAF-step1" \
  -d '{
    "to": "+14155550123",
    "message": "Hi Dana, saw you booked a demo. Want me to send the pricing sheet?",
    "metadata": { "crm_id": "0035f00000LmQ2xAAF", "sequence": "demo-follow-up" }
  }'

Three habits that make this production-grade:

  1. Idempotency-Key on every send, derived from the CRM record and step, so a workflow retry can never double-text a lead.
  2. metadata for your own context. Free-form, stored with the send.
  3. Default to the drip lane. CRM-triggered outreach belongs on drip: it inherits pacing, capacity and quiet hours. Save send_mode: "instant" for reply handling.

Call the API from your server or automation platform, never from browser code, since the key would be exposed.

Identity: stamp the CRM ID on the contact

Before (or right after) the first send to a lead, upsert the contact with your record ID:

curl -X POST https://api.bluereacher.com/v1/contacts \
  -H "Authorization: Bearer brk_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "phone_number": "+14155550123",
    "first_name": "Dana",
    "custom_fields": { "crm_id": "0035f00000LmQ2xAAF" }
  }'

Upserts key on phone_number, so this is safe to repeat. From then on, any webhook can be resolved: the event carries contact_id and phone_number, and one GET /v1/contacts?phone=... returns the contact with custom_fields.crm_id. Most handlers skip even that lookup by keeping their own phone-to-record map and treating the contacts API as the fallback for unknown numbers.

Inbound: replies to your endpoint

Register your endpoint, verify the X-BlueReacher-Signature HMAC over the raw body, and handle:

{
  "event": "message.received",
  "event_id": "9f3c1b7e-2a64-4d8f-b0e5-c7a92d4f8e13",
  "timestamp": "2026-08-31T07:41:22.310Z",
  "data": {
    "message_id": "m_313",
    "phone_number": "+14155550123",
    "contact_id": "c9b41d68-3a2f-4e07-b5d1-6f8e2a9c4d17",
    "contact_name": "Dana Reyes",
    "content": "Yes please, send it over",
    "direction": "incoming",
    "service_type": "iMessage",
    "device_name": "Line 2"
  }
}

The handler shape, in any runtime:

app.post("/webhooks/bluereacher", async (req, res) => {
  if (!verifySignature(req)) return res.status(401).end();
  res.status(200).end(); // ack fast, work async

  const { event, event_id, data } = req.body;
  if (await seen(event_id)) return; // at-least-once delivery

  if (event === "message.received") {
    const recordId = await crmIdForPhone(data.phone_number);
    await crm.logReply(recordId, data.content);
  }
  if (event === "contact.opted_out") {
    const recordId = await crmIdForPhone(data.phone_number);
    await crm.setDoNotContact(recordId);
  }
});

message.sent and message.failed complete the loop for delivery reporting, and contact.opted_out is the one you must not skip: mirror it into the CRM's DNC field in the same run.

HubSpot

Outbound runs from a workflow. Add a Send a webhook action, method POST, URL https://api.bluereacher.com/v1/messages, and a custom payload with to mapped to the contact's phone and message from your template. Custom payloads and headers need Operations Hub Professional; on lower tiers, use a Custom code action in Node and call the API with fetch, which also lets you branch on the response.

Inbound goes to your own endpoint, not to HubSpot directly. On message.received, call HubSpot's Notes API and associate the note to the contact whose phone (or stored crm_id) matches. Set a last_imessage_reply property in the same call so lists and workflows can act on it.

Salesforce

Create an External Credential with a Custom authentication header, name Authorization, value Bearer brk_your_api_key. Attach it to a Named Credential pointing at https://api.bluereacher.com. This keeps the key out of Flow.

Build a record-triggered Flow on Lead or Contact. Add an HTTP Callout action against that Named Credential, path /v1/messages, method POST. Map to from the phone field and message from a formula or text template.

Inbound needs an Apex REST endpoint, for example @RestResource(urlMapping='/bluereacher/*') exposed through a Site or Connected App. Resolve the record by phone (or your stored mapping), insert a completed Task with the reply text, and update any status field your reps watch.

Zapier, n8n, Make

Outbound. Zapier: Webhooks by Zapier, POST action, add the Authorization header and a JSON payload. n8n: HTTP Request node, POST, Header Auth credential, JSON body. Make: HTTP, Make a request module, POST, header plus raw JSON body. Per-tool walkthroughs are in integrations.

Inbound. Zapier: Catch Hook trigger. n8n: Webhook node. Make: Custom webhook module. Copy the generated URL into your webhook registration. Then add a filter for event equals message.received and a CRM update step keyed on the phone number.

Go-live checklist

  • Send one message with a brk_test_ key end to end through the CRM automation and confirm the simulated response parses.
  • Stamp custom_fields.crm_id on contact creation and confirm a reply resolves to the right record.
  • Handle contact.opted_out into the CRM's DNC field, and test it with a STOP reply.
  • Swap to the live key. Change nothing else.

On this page