Zapier
BlueBot’s Zapier app texts from your own number and starts a Zap when a text comes in, a text changes state or somebody texts STOP. This page lists every trigger, action and search and each of their fields.
The BlueBot app for Zapier joins your number to the apps Zapier connects. A form fills in and the person gets a text from you; a client texts back and the reply goes into your CRM; a text fails and you hear about it in Slack. It has five triggers, eight actions and three searches.
Every trigger is instant. BlueBot posts to Zapier as soon as something happens, so a Zap never waits for a polling interval.
Texts sent through Zapier go through the same sending rules as everything else: pacing, quiet hours, the daily limits and your stop list. A text that can’t go yet is accepted and held, never refused, and the Zap step still succeeds. Each one shows source: "zapier" in BlueBot, so you can tell them apart in the inbox and in your numbers.
Connect your account
The app signs in with an API key. Make one for Zapier alone, so you can revoke it without touching anything else.
Make a key
In BlueBot, open Developers → API keys and press New key. Name it “Zapier” and tick every scope: the triggers need webhooks:manage to subscribe, the contact steps need contacts:read and contacts:write, and the connection test needs account:read. Copy the key. It starts with bb_live_ and is shown once.
Add it in Zapier
Add a BlueBot step to a Zap and press Sign in. Paste the key into API Key and press Yes, Continue to BlueBot. Zapier checks it with GET /v1/me and names the connection after your account’s email address.
Check the connection
| You got | It means | Do next |
|---|---|---|
| A connection named after your email | The key works and can read the account. | Pick a trigger or an action. |
| “That API key isn’t valid” | BlueBot doesn’t know the key: mistyped, revoked, or only part of it pasted. | Copy the key again, or make a new one. |
| “This key needs the account:read scope” | The key was made without account:read. | Make a new key with every scope ticked. |
One connection serves every Zap in your Zapier account. To change keys later, open My Apps in Zapier, find BlueBot and press Reconnect. A revoked key makes every Zap that uses it fail with unauthorized until you reconnect.
Triggers
All five are instant. When you turn a Zap on, the app makes a webhook in your BlueBot account that points at the Zap, subscribed to only the events that trigger needs; when you turn the Zap off or delete it, the app deletes that webhook. You can see them under Developers → Webhooks in BlueBot, each described as “Zapier: ” and the Zap’s name. Don’t delete one there while its Zap is on, or the Zap stops hearing anything.
Each trigger’s output is the record as the API returns it (a message, a contact or a sequence enrollment), plus three fields about the event: event_id, event_type and event_timestamp. Zapier flattens nested fields when you map them, so metadata.record_id shows as Metadata Record ID and each file as a line item under Files.
When you press Test trigger, the app reads your three newest matching records from BlueBot, so you map real fields from your own account. If there are none yet, it shows the sample below.
New Incoming Text
Starts when somebody texts your number. Pictures and voice memos come with it as files. BlueBot subscribes the Zap to message.received.
| Field | Key | Type | Required | Help text |
|---|---|---|---|---|
| Include Group Chats | include_groups | Boolean | No | Also start for texts in group chats. Default: yes. |
| Only From | only_from | String | No | Only start for texts from this phone number or email address. Leave it empty for everyone. |
BlueBot copies the texts of people you’ve texted through it, unless you set Which chats to copy to Everything in Settings → Sending rules. A text from somebody you’ve never texted through BlueBot doesn’t start the Zap until you change that setting.
{
"id": "msg_4Tz8QvN2pLk7RcYw1HmXe3",
"object": "message",
"direction": "in",
"state": "received",
"to": "+15555550100",
"from": "+15555550123",
"chat_id": null,
"contact_id": "ct_1Hq2LxW9cVbN3mKpRtYs0a",
"conversation_id": "conv_1Hq2LxW9cVbN3mKpRtYs0a",
"text": "Yes, 3 PM works. See you there.",
"files": [],
"service": "imessage",
"source": "mac",
"metadata": {},
"reply_to_id": null,
"received_at": "2026-11-13T19:44:12.000Z",
"event_id": "evt_7Kd3WnP9xQ2mLc5RvT8bHy",
"event_type": "message.received",
"event_timestamp": "2026-11-13T19:44:13.000Z"
}Text Status Changed
Starts when a text you sent changes state: sent, delivered, read, failed and so on. Use it to write the result back to the record that caused the text, or to hear about failures.
| Field | Key | Type | Required | Help text |
|---|---|---|---|---|
| States | states | Multiple select: Sent, Delivered, Read, Failed, Unconfirmed, Expired, Canceled | Yes | Which changes start the Zap. Pick Failed, Unconfirmed and Expired to hear only about texts that didn’t go. |
| Only With Metadata | metadata_key | String | No | Only start for texts whose metadata has this key, such as record_id, so texts you typed by hand don’t start it. |
BlueBot subscribes the Zap to the matching events (message.sent, message.delivered and so on). Delivered and read come only from iMessage, and read only from people who share read receipts, so an SMS goes from sent to nothing more. has the whole picture.
The output is the message as it is after the change, so state, failure and metadata are all there. A failed text carries failure.reason: not_on_imessage, messages_refused or file_unavailable.
New Contact
Starts when a person is added to your contacts: by a first text either way, an import, the API, the website widget, a Text Hook or the dashboard. BlueBot subscribes the Zap to contact.created. It has no fields.
{
"id": "ct_1Hq2LxW9cVbN3mKpRtYs0a",
"object": "contact",
"handle": "+15555550123",
"kind": "phone",
"first_name": "Maria",
"last_name": "Lopez",
"company": null,
"tags": ["lead"],
"custom_fields": {},
"stopped": false,
"created_via": "widget",
"created_at": "2026-11-13T19:40:02.000Z",
"event_id": "evt_2Mx8TqR4vB9nKd1WcY6pLs",
"event_type": "contact.created",
"event_timestamp": "2026-11-13T19:40:02.000Z"
}Contact Stopped
Starts when somebody goes on your stop list: they replied with a stop word, or somebody added them in the dashboard or through the API. Their queued texts are already canceled. Use it to mark them as opted out in your CRM. BlueBot subscribes the Zap to contact.stopped.
| Field | Key | Type | Required | Help text |
|---|---|---|---|---|
| Only Replies | only_replies | Boolean | No | Only start when the person texted a stop word themselves, not when somebody added them. Default: no. |
The output is the contact with stopped: true, stopped_at and stop_source (reply, dashboard, api, import or admin).
Sequence Completed
Starts when a person comes to the end of a sequence, or leaves it early because they replied, texted STOP or were taken off. BlueBot subscribes the Zap to sequence.completed.
| Field | Key | Type | Required | Help text |
|---|---|---|---|---|
| Sequence | sequence_id | Dropdown of your sequences | No | Only start for this sequence. Leave it empty for every sequence. |
| Outcome | outcome | Select: Any, Completed, Ended early | No | Default: Any. |
The output is the enrollment: id (enr_…), sequence_id, contact_id, state (completed, stopped or canceled), stop_reason (replied, stopped, canceled, sequence_archived or failed) and completed_at, plus the contact’s phone number and name as contact_handle, contact_first_name and contact_last_name.
Actions
Every action that sends a text carries an Idempotency-Key made from the Zap’s id and a hash of the step’s inputs. If Zapier runs the step again after a timeout, or you replay the run, BlueBot answers with the text it already accepted instead of sending a second one. The key lasts 24 hours.
That has one effect worth knowing: if the same Zap sends exactly the same words to the same person twice within 24 hours, the second run gets the first text back and nothing new goes. When a Zap should be able to repeat itself, map something unique into Metadata, such as the record’s id or the date.
Send Text
Sends a text from your number now, or as soon as your sending rules allow. The answer is the message, usually state: "queued"; it moves to sent within seconds once your Mac takes it.
| Field | Key | Type | Required | Help text |
|---|---|---|---|---|
| To | to | String | Yes, unless Group Chat is set | A mobile number in any format, such as (555) 555-0123; US numbers can leave off the +1. Or an email address the person uses for iMessage. |
| Group Chat | chat_id | Dropdown of your group chats | No | Text an existing group chat instead of one person. Leave To empty. BlueBot can’t start a new group. |
| Text | text | Long text | Yes, unless Files is set | The words, up to 2,000 characters. |
| Files | files | File, list | No | Up to 10 pictures, videos, audio files, PDFs or contact cards (.vcf), each up to 25 MB, sent after the words. The app uploads each one first. |
| Service | service | Select: Auto, iMessage, SMS | No | Auto (the default) uses whatever the newest chat with the person used, else iMessage, and sends an iMessage that never went once more as SMS. iMessage never falls back. SMS needs your iPhone with Text Message Forwarding. |
| Give Up At | expires_at | Date and time | No | If the text is still waiting at this time, it expires instead of going late. Default: 24 hours after it’s due (a setting). |
| Metadata | metadata | Key and value pairs | No | Your own data, kept on the text and sent back on every status change: up to 20 keys of up to 40 characters, values up to 500. Put the record’s id here. |
The output is the message. Map id into a later Cancel Scheduled Text, or keep it on your record. If the text is held (quiet hours, your Mac offline, a daily limit), hold.reason and hold.until say why and until when, and the step still succeeds.
A text to somebody on your stop list succeeds as a step but comes back state: "canceled" with cancel_reason: "opted_out", and nothing is sent. Add a Filter after the step on State if later steps should only run for texts that will go.
Send Template
Sends one of your BlueBot templates, filled in with the values you map.
| Field | Key | Type | Required | Help text |
|---|---|---|---|---|
| To | to | String | Yes | A mobile number or an iMessage email address, as on Send Text. |
| Template | template_id | Dropdown of your templates | Yes | The template to send. Its words appear under the field once you pick it. |
| Variables | variables | Key and value pairs | No | A value for each {{name}} in the template, keyed by the name without braces. A variable you leave out comes from the contact when BlueBot knows it (first_name, last_name, company and custom fields) or from your business name (business), else from the template’s own fallback. One with none of those stops the send, naming it. |
| Service | service | Select: Auto, iMessage, SMS | No | As on Send Text. |
| Metadata | metadata | Key and value pairs | No | As on Send Text. |
The output is the message, with the template’s words filled in as text. A variable with no value and no fallback fails the step with invalid_request and the variable’s name, and nothing is sent.
Schedule Text
Schedules a text for a time. Until it’s due it can be changed in BlueBot or canceled with Cancel Scheduled Text. When the time comes it goes through your sending rules like any other text, so a text scheduled inside quiet hours waits for morning.
| Field | Key | Type | Required | Help text |
|---|---|---|---|---|
| To | to | String | Yes, unless Group Chat is set | As on Send Text. |
| Group Chat | chat_id | Dropdown of your group chats | No | As on Send Text. |
| Text | text | Long text | Yes, unless Files is set | As on Send Text. |
| Send At | send_at | Date and time | Yes | When to send it. Zapier reads times like “tomorrow 9am” in your Zapier time zone. |
| Files | files | File, list | No | As on Send Text. Each file is uploaded when the step runs and kept with the text for up to 30 days, so schedule a text with files at most 30 days ahead. |
| Service | service | Select: Auto, iMessage, SMS | No | As on Send Text. |
| Give Up At | expires_at | Date and time | No | Default: 24 hours after Send At. |
| Metadata | metadata | Key and value pairs | No | As on Send Text. |
The output is the message with state: "scheduled" and send_at. Keep its id if anything might need to cancel it, such as a booking being moved.
Cancel Scheduled Text
Cancels a text that hasn’t gone yet: one that’s scheduled, or queued and waiting on a hold.
| Field | Key | Type | Required | Help text |
|---|---|---|---|---|
| Message ID | id | String | Yes | The msg_ id from the step that scheduled it. |
The output is the message with state: "canceled" and cancel_reason: "requested". A text the Mac already took can’t be called back: the step fails with conflict, “Your Mac already took that text, so it can’t be canceled.” Use a Filter on the earlier step’s State if that can happen often.
Create or Update Contact
Finds the person by phone number or email address and updates them, or adds them when BlueBot doesn’t know them.
| Field | Key | Type | Required | Help text |
|---|---|---|---|---|
| Phone or Email | handle | String | Yes | The person’s mobile number in any format, or an email address they use for iMessage. |
| First Name | first_name | String | No | |
| Last Name | last_name | String | No | |
| Company | company | String | No | |
| Tags | tags | String, list | No | Tags to add. Tags they already have stay. |
| Custom Fields | custom_fields | Key and value pairs | No | Your own fields, for templates. A key you send replaces that field; keys you don’t send stay as they were. |
A field you leave empty doesn’t clear what BlueBot has. The output is the contact, with created: true when the person was new.
Add Tag
Adds tags to a person, adding them as a contact first if BlueBot doesn’t know them.
| Field | Key | Type | Required | Help text |
|---|---|---|---|---|
| Phone or Email | handle | String | Yes | |
| Tags | tags | String, list | Yes | One or more tags. Tags are matched without regard to case. |
The output is the contact with all of its tags.
Enroll in Sequence
Starts a person on one of your sequences. The sequence’s texts go through your sending rules, and by default it stops the moment they reply or text STOP.
| Field | Key | Type | Required | Help text |
|---|---|---|---|---|
| Phone or Email | handle | String | Yes | Added as a contact if BlueBot doesn’t know them. |
| Sequence | sequence_id | Dropdown of your sequences | Yes | |
| Variables | variables | Key and value pairs | No | Values for the {{name}}s in the sequence’s templates, kept for every step. |
A person already in the sequence isn’t started again; the step succeeds and returns the enrollment they’re in. A person on your stop list isn’t enrolled, and the step stops the run as Halted rather than Errored, with BlueBot’s reason. The output is the enrollment: id (enr_…), sequence_id, contact_id, state and the time of the first text.
Remove from Sequence
Takes a person off a sequence. Their texts from it that haven’t gone are canceled.
| Field | Key | Type | Required | Help text |
|---|---|---|---|---|
| Phone or Email | handle | String | Yes | |
| Sequence | sequence_id | Dropdown of your sequences | No | Leave it empty to take them off every sequence. |
The output lists the enrollments that ended. A person in none of them isn’t an error; the list is empty.
Searches
Find Contact
Finds a person by phone number or email address. Turn on Create BlueBot Contact if it doesn’t exist yet? to add them when there’s no match, with the fields of Create or Update Contact.
| Field | Key | Type | Required | Help text |
|---|---|---|---|---|
| Phone or Email | handle | String | Yes | Any format; BlueBot compares numbers in E.164. |
Find Conversation
Finds your conversation with a person and its newest text, to check whether they’ve answered before you send something else.
| Field | Key | Type | Required | Help text |
|---|---|---|---|---|
| Phone or Email | handle | String | Yes |
The output is conversation_id, contact_id, last_message_text, last_message_direction (in or out), last_message_at and waiting_on_you, which is true when the newest text came from them.
Check iMessage
Says whether your newest chat with a number used iMessage or SMS. It reads your own Messages history through your Mac and waits up to 15 seconds for it; it calls GET /v1/lookup.
| Field | Key | Type | Required | Help text |
|---|---|---|---|---|
| Phone or Email | number | String | Yes |
The output is service (imessage, sms or unknown), basis (history, none, device_offline or timeout) and last_seen_at. Apple gives no way to ask about a number you’ve never texted, so a new number answers unknown with basis: "none". You don’t need this step before sending: Service: Auto already tries iMessage and falls back to SMS.
When a step fails
The app turns BlueBot’s error into Zapier’s, with BlueBot’s message, its hint, the error code and the request id, such as “Send to or chat_id. Check the fields named in the message and send it again. (invalid_request, req_1Hq2LxW9cVbN3mKpRtYs0a)”. Quote the request id if you write to support.
| BlueBot answers | Zapier shows | What to do |
|---|---|---|
invalid_request (400) | The step shows as Errored and names the field that didn’t fit. | Fix the mapping. A number BlueBot can’t read is the usual cause. |
unauthorized (401) | The connection as expired, and an email asking you to reconnect. | Reconnect with a working key. |
plan_lapsed (402) | The step as Errored. | Update the card on the Billing page. |
insufficient_scope (403) | The step as Errored, naming the scope. | Reconnect with a key that has every scope. |
account_held (403) | The step as Errored. | Write to support. |
not_found (404) | The step as Errored. | Check the id you mapped. |
conflict (409) | The step as Errored. | The record changed first, such as a text already sent. |
rate_limited (429) | Nothing at first: the app asks Zapier to try the step again after retry_after seconds. | Nothing, unless one key runs many busy Zaps. |
internal, upstream_failed, unavailable (5xx) | The step as Errored. With Autoreplay on, Zapier tries again, and the idempotency key keeps that from sending twice. | Try again, or replay the run. |
A held text is never an error. A text that fails after it was accepted (Messages refused it, or it was iMessage-only to somebody not on iMessage) isn’t either, because the step already succeeded; catch it with Text Status Changed on Failed.
Your first Zap
Maria runs a cleaning company and keeps new leads in a Google Sheet. This Zap texts each new lead from her own number.
Pick the trigger
Make a Zap with Google Sheets’ New Spreadsheet Row trigger and choose the sheet. Press Test trigger so Zapier loads a row.
Add Send Text
Add a BlueBot step, choose Send Text and pick your connection. Map the row’s phone column into To, and write the text with the row’s name column in it: “Hi Dana, it’s Maria from Sparkle Cleaning. Thanks for asking about a quote. When is a good time to come by?” Map the row number into Metadata under row.
Test it with your own number
Before testing, type your own mobile number into To in place of the mapped column. Press Test step.
| You got | It means | Do next |
|---|---|---|
A message with state: "queued", and the text on your phone a few seconds later | It works end to end. | Map the phone column back into To and turn the Zap on. |
state: "queued" with hold.reason: "quiet_hours" | It’s inside your quiet hours, so the text waits for morning. | Nothing; it goes when quiet hours end. Change them in Settings → Sending rules. |
state: "queued" with hold.reason: "device_offline" | Your Mac hasn’t checked in for 2 minutes. | Wake the Mac and open BlueBot on it. The text goes once it’s back. |
invalid_request naming to | The number couldn’t be read. | Check what you typed in To. |
Turn it on
Turn the Zap on. Each new row now gets a text within seconds, paced 20 seconds apart by default so a big import doesn’t send a burst.
Next, add a second Zap with New Incoming Text that writes each reply back to the sheet, or follow a full walkthrough in . The have one page per app with the exact fields to map.
Using the API directly in a Zap
For anything the app doesn’t have a step for, use Zapier’s Webhooks by Zapier or Code by Zapier with a key and the . Send Authorization: Bearer <key>, an Idempotency-Key on every POST, and the header BlueBot-Client: zapier so the text shows as sent from Zapier.