Text Hooks
A Text Hook is an address any app can post its webhook to, and each post becomes a text from your number. Set one up, map the fields, use a preset for Calendly, Typeform, Shopify, Stripe or Jotform, and test it with curl.
Most apps can call a webhook when something happens: a form is filled in, a booking is made, an order is paid. A Text Hook gives that webhook somewhere to go. You make the hook in BlueBot, paste its address into the other app, and say which posted field holds the phone number and what the text should say. From then on, every post becomes a text from your own number, with no Zapier account and no code.
Rosa owns a yoga studio. She pastes her hook’s address into Calendly, maps Calendly’s phone field to the recipient, and writes “Hi {{body.payload.first_name}}, you’re booked. See you on the mat.” Each person who books gets that text from Rosa’s number within seconds.
Texts from a hook go through your sending rules like every other text: pacing, quiet hours, the daily limits and your stop list. They show source: "hook" in BlueBot, with the hook’s id (hook_…) as source_ref.
Make a hook
Start it
In BlueBot, open Automations → Text Hooks and press New hook. Name it after the app that will post to it (“Calendly bookings”). If the app is Calendly, Typeform, Shopify, Stripe or Jotform, pick it under Preset and BlueBot fills in the mapping for that app’s usual payload; you can change any of it.
Copy the address
The hook’s address looks like https://usebluebot.com/h/ followed by a long random token. Copy it and paste it into the other app as a webhook URL.
Send one real post
Make the other app post once: book a test appointment, submit the form, or use the app’s Send test button. The hook’s page lists the post under Recent posts with the names of the fields it carried.
Map the fields
Fill in the settings below, picking field names from the post you sent.
Turn it on
A new hook starts off, so posts are listed but send nothing. Turn it on when the mapping is right.
Settings
| Setting | Required | What it is |
|---|---|---|
| Recipient | Yes | Who gets the text: a path into the post, such as {{body.phone}}, or a fixed number, such as your own to text yourself about each post. Any phone format works; US numbers can leave off the +1. |
| Text | Yes | The words, up to 2,000 characters once filled in. {{body.…}} is replaced by that field of the post. |
| Service | No | Auto (the default), iMessage or SMS, as on any send. |
| Metadata | No | Up to 20 keys kept on each text and echoed on its webhooks. Values may use {{body.…}}, such as an order number. |
| Add as a contact | No | Adds the recipient to your contacts, with first and last name from the paths you give. |
| Tags | No | Tags for that contact, such as calendly or lead. |
| Enroll in a sequence | No | Starts the person on a sequence instead of, or as well as, sending the text. |
Paths into a post
Everything posted is under body. A dot goes one level down, and a number picks an item from a list.
| The post has | Write |
|---|---|
{"phone": "+15555550123"} | {{body.phone}} |
{"customer": {"first_name": "Maria"}} | {{body.customer.first_name}} |
{"answers": [{"text": "Tuesday"}]} | {{body.answers.0.text}} |
A form field named phone | {{body.phone}} |
A path the post doesn’t have becomes nothing, so write texts that still read well without it: “Hi {{body.first_name}}, thanks” reads as “Hi , thanks” when the name is missing. A post whose recipient path is empty, or holds something that isn’t a phone number or email address, sends nothing, and Recent posts says why.
What a hook accepts
POSTonly, withContent-Type: application/json,application/x-www-form-urlencodedormultipart/form-data. A form field whose value is JSON (Jotform’srawRequest) is read as JSON, so you can write paths inside it.- Up to 64 KB of body. A bigger post is answered
413and sends nothing. - No key: the token in the address is the credential. Treat the address like a password. Anybody who has it can make BlueBot text a number they post, though your new-people-per-day limit (50 by default) caps how many strangers that could reach. If an address leaks, delete the hook and make a new one.
BlueBot answers 202 to every post it could read, whether or not it became a text, so the other app never retries a post or turns its webhook off because of a mapping problem. It answers 404 for an address that doesn’t exist or a hook that was deleted, 413 for a body over 64 KB, and 415 for another content type.
Recent posts
The hook’s page lists its last 20 posts from the past 30 days: when each came, the names of the fields it carried, and what happened (a text queued, held, or nothing and why). It never shows the values, and BlueBot never writes them to its logs, because webhooks carry people’s names, numbers and answers. To see the values, look in the other app’s webhook log, or post a test yourself.
Presets
A preset fills in the recipient, a first name for the text and the contact, and a starting text for that app’s usual webhook. The paths below are what each preset fills in; your form or store may use different field names, so check them against Recent posts.
| App | Where you add the address | Recipient | First name |
|---|---|---|---|
| Calendly | A webhook subscription for invitee.created (on a paid Calendly plan) | {{body.payload.text_reminder_number}} | {{body.payload.first_name}} |
| Typeform | Your form’s Connect → Webhooks | The first answer of type phone_number, such as {{body.form_response.answers.2.phone_number}} | The first short-text answer, such as {{body.form_response.answers.0.text}} |
| Shopify | Settings → Notifications → Webhooks, event Customer creation, format JSON | {{body.phone}} | {{body.first_name}} |
| Stripe | Developers → Webhooks, event checkout.session.completed | {{body.data.object.customer_details.phone}} | {{body.data.object.customer_details.name}}, the full name |
| Jotform | Your form’s Settings → Integrations → WebHooks | The phone field inside rawRequest, such as {{body.rawRequest.q4_phoneNumber.full}} | {{body.rawRequest.q3_name.first}} |
Some notes on each:
- Calendly fills
text_reminder_numberonly when the event type asks for a number for text reminders. Turn that on, or add a phone question and map its answer. - Typeform posts answers in the order of the form. Moving questions around changes the numbers in the paths, so check the mapping after editing the form.
- Shopify has a phone number only when the customer gave one. For orders, use the Order creation event and
{{body.customer.phone}}. Text only customers who agreed to texts; Shopify sends that as{{body.sms_marketing_consent.state}}. - Stripe includes a phone number only when Checkout collects one. BlueBot doesn’t check Stripe’s signature on the post, so anybody with the address could post a fake one; keep the address private.
- Jotform names fields after their question number (
q4_…), which differs from form to form.
Every app above can also send its posts twice now and then. A hook sends one text per post, so a repeated post sends again; if that matters, have the hook enroll people in a sequence, which never starts the same person twice.
Test it with curl
Post a sample shaped like the app’s, with your own number in the recipient field. Here the recipient is {{body.phone}} and the text uses {{body.first_name}}:
curl -X POST "https://usebluebot.com/h/YOUR_HOOK_TOKEN" \
-H "Content-Type: application/json" \
-d '{"phone":"+15555550123","first_name":"Maria"}'The same as a form post. Use --data-urlencode, since a bare + in a form body means a space:
curl -X POST "https://usebluebot.com/h/YOUR_HOOK_TOKEN" \
--data-urlencode "phone=+15555550123" \
--data-urlencode "first_name=Maria"| You got | It means | Do next |
|---|---|---|
202, and a text on your phone a few seconds later | The hook works end to end. | Put the address in the other app. |
202, no text, and Recent posts says the hook is off | Posts are listed but nothing sends until you turn it on. | Turn the hook on. |
202, no text, and Recent posts says the recipient is empty | The recipient path doesn’t match a field in the post. | Pick the field name from Recent posts. |
202, and the text is held | It’s quiet hours, your Mac is offline, or a daily limit was reached. | Nothing; it goes once the hold ends. |
404 | The token is wrong or the hook was deleted. | Copy the address again. |
415 | The body isn’t JSON or a form. | Send Content-Type: application/json. |
When to use something else
A hook sends a text when a post arrives, and that’s all. For anything that needs a decision first (only text new customers, wait until the day before, look something up), use , , or the . The say which path suits each app.