Calling and AI agents
What FaceTime calling from the API does, what it never does, and how to wire a reply-YES call flow with a person on the line or an AI voice agent on your own telephony.
The short version
The Calls API places FaceTime audio calls from your iMessage line, and a person always starts them. Your code prepares the call. Someone signed in to Blue Reacher opens the confirmation link and presses Start call. Nothing dials before that.
No route, request field or MCP tool dials on its own, and no route streams call audio to your server. That is by design and it is not on the roadmap to change.
| You can | You cannot |
|---|---|
| Prepare a call from code or an AI agent and hand the link to a person | Dial without a person pressing Start call |
| Follow the whole call lifecycle on webhooks | Stream or bridge live call audio to your server (no SIP, WebSocket or WebRTC leg) |
| Get the transcript, a summary and the recording link after the call | Answer an incoming call from the API |
| Hand an answered call to a teammate | Put an AI voice agent on a FaceTime call |
| Cancel a prepared call or end a live one | Queue a second call on a line while one is waiting |
Live keys need Calling switched on for the workspace, which your account manager does, and a key with calls:write. Test keys (brk_test_) get simulated calls on every calling route.
How a call runs, end to end
- Your code calls
POST /v1/callswith the lead's number. The answer carries aconfirmation_urlthat is valid for five minutes. - You put that link in front of a person: a Slack message, a CRM task, a push notification.
- The person opens it, signs in to Blue Reacher if they are not already, checks the line and the masked destination, and presses Start call.
- The call runs as FaceTime audio from your line, in the Blue Reacher app in their browser.
call.ringing,call.answered,call.completed,call.missedandcall.failedarrive on your webhook, each carrying yourclient_reference.- With recording and transcripts switched on,
call.transcribeddelivers the transcript, a summary and the recording link about a minute after the call ends.
If nobody presses Start call within five minutes, the call becomes expired, call.expired fires, and the line is free again.
The reply-YES pattern
The flow most teams want: a lead replies YES to "want a quick call?", and a rep calls them inside a minute, from the same number the lead has been texting.
- Your inbound webhook sees the YES reply.
- Your backend prepares the call, using the inbound message id as the
Idempotency-Keyso a retried webhook never prepares twice. - It posts the
confirmation_urlto your team's Slack channel with the lead's name. - The first rep to tap it places the call.
call.answeredorcall.missedupdates your CRM.call.transcribedadds the notes.- On
call.expired, text the lead a booking link instead, or prepare the call again.
// Inbound webhook handler: the lead replied YES
const res = await fetch("https://api.bluereacher.com/v1/calls", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.BLUEREACHER_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": inbound.message_id,
},
body: JSON.stringify({
to: inbound.from,
contact_name: lead.name,
client_reference: lead.crm_id,
}),
});
const call = await res.json();
if (res.ok) {
await fetch(process.env.SLACK_WEBHOOK_URL, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ text: `${lead.name} said YES. Call now: ${call.confirmation_url}` }),
});
} else if (call.error_code === "line_busy") {
// Another call is waiting on this line. Retry after it starts or expires,
// or prepare on a second calling line.
}One prepared call holds its line until it is started, cancelled or expires, so a team taking many YES replies at once should run more than one calling line, or cancel calls nobody picked up.
AI voice agents
A FaceTime call has no audio interface for software, so an AI voice agent cannot place or speak on one through Blue Reacher.
When you want an AI agent to make the call, run it on your own telephony provider, any SIP or programmable voice platform your agent already supports, and show your Blue Reacher number as the caller ID. The lead sees the same number they have been texting with. Ask your account manager to verify the number as caller ID with your provider. That call is a regular phone call, not FaceTime.
Two rules for AI calls:
- Call only people who asked for the call. In the US, the FCC treats an AI-generated voice as an artificial voice under the TCPA, so the call needs the person's prior express consent, and prior express written consent when it is telemarketing. A YES reply to a message that says an AI assistant will call is a clean consent record.
- Keep the calling pattern conversational: one call to each person who asked, not a list dialed top to bottom.
None of this is legal advice; run your program past counsel before scaling it.
Behaviour you can rely on
Checked against the live API on 5 October 2026.
| Behaviour | Detail |
|---|---|
| Confirmation window | Five minutes from created_at, then expired. An unconfirmed call never rings |
| One waiting call per line | A second prepare on the same line answers 409 line_busy until the first is started, cancelled or expires. Cancelling frees the line at once |
| Strict request body | Any field outside to, line_id, contact_name and client_reference is refused with 400 invalid_request, naming the field |
| Idempotency | Same Idempotency-Key and body returns the original call. Same key, different body answers 409 idempotency_conflict |
| Default line | line_id can be left out when the key has a default line |
| Cancel and end | DELETE /v1/calls is safe to repeat and returns the call as it stands |
| Transfer | Only on an answered, live call; otherwise 409 call_not_answered |
| Rate limit | POST /v1/calls and POST /v1/calls/transfer share 10 a minute per key, and refused requests count toward it |
| MCP | list_calling_lines, prepare_call, get_call_status and end_call behave exactly like the REST routes. No MCP tool dials |
Errors
| Status | error_code | When |
|---|---|---|
| 400 | invalid_destination | to is missing or not E.164 |
| 400 | invalid_request | A field the route does not take, such as auto_start |
| 400 | invalid_call_id | call_id is not a UUID |
| 400 | call_id_required | GET /v1/calls without call_id or direction=inbound |
| 404 | line_not_found | line_id is not a calling line in this workspace |
| 404 | call_not_found | No call with that id for this key |
| 409 | line_busy | A prepared call is already waiting on this line |
| 409 | idempotency_conflict | The Idempotency-Key was used with a different body |
| 409 | call_not_answered | Transfer on a call that is not live |
| 429 | rate_limited | Over 10 prepares and transfers a minute |
The full list for every route is on Errors.
MCP Server
Point Claude, Cursor, or any MCP client at the hosted Blue Reacher MCP endpoint and an AI assistant can send, read, react, and manage the account with no package to install.
GoHighLevel
The native GoHighLevel integration: two-way conversation sync, workflow actions, and where the API fits alongside it.

