Skip to the page

API reference

Make a Text Hook

A Text Hook is an address any app can post to (Calendly, Typeform, Shopify, Stripe, Jotform, your own form) that turns the post into a text from your number.

POST/v1/hooks

A Text Hook is an address any app can post to (Calendly, Typeform, Shopify, Stripe, Jotform, your own form) that turns the post into a text from your number. mapping says which posted field is the person’s number and what to say, with {{body.…}} for posted fields.

Anyone with the address can send texts from your number, so keep it private. Start with test_mode on to check a real post without sending.

Needs a key with the messages:send scope. Keys and scopes

Send an Idempotency-Key so a retry never acts twice. Idempotency

Headers

  • Idempotency-Keystring

    Any string of up to 255 characters, new for each request. A retry with the same key gets the first answer back for 24 hours, with Idempotent-Replayed: true.

    Up to 255 characters.

Request body

  • namestringrequired

    Up to 80 characters.

  • presetstring

    Start from the usual fields of Calendly, Typeform, Shopify, Stripe or Jotform.

    One of calendly, typeform, shopify, stripe, jotform.

  • mappingobject
    Show its 5 fields
    • tostring

      Up to 200 characters.

    • textstring

      Up to 2,000 characters.

    • first_namestring

      Up to 200 characters.

    • last_namestring

      Up to 200 characters.

    • tagsstring

      Up to 200 characters.

  • template_idstring

    A saved template to use instead of text.

    Starts with tpl_.

  • sequence_idstring or null

    A seq_ id.

    Starts with seq_.

  • test_modeboolean

    Default false.

Example

curl -X POST "https://usebluebot.com/api/v1/hooks" \
  -H "Authorization: Bearer $BLUEBOT_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 0f6d8c2e-5b1a-4c7e-9d3f-2a8b6e1c4d70" \
  -d '{
    "name": "Calendly bookings",
    "preset": "calendly",
    "test_mode": true
  }'

Response

201: The Text Hook, with its address.

  • idstringrequired

    A hook_ id.

    Starts with hook_.

  • object"text_hook"required
  • namestringrequired
  • urlstringrequired

    The address to post to. Anyone with it can send texts from your number, so keep it private.

  • enabledbooleanrequired
  • test_modebooleanrequired

    Posts are recorded and checked but no text is sent.

  • mappingobjectrequired

    How a post becomes a text. Each value is a template over the posted fields: body for the JSON or form, query for the address’s query string.

    Show its 5 fields
    • tostring or nullrequired

      Who to text, as a {{body.…}} path into the posted fields.

      Up to 200 characters.

    • textstring or nullrequired

      The words, with {{body.…}} fields. Leave it empty to use template_id.

      Up to 2,000 characters.

    • first_namestring or nullrequired

      Saved on the contact when it has no first name yet.

      Up to 200 characters.

    • last_namestring or nullrequired

      Up to 200 characters.

    • tagsstring or nullrequired

      Tags to add, separated by commas. May use {{body.…}} fields.

      Up to 200 characters.

  • template_idstring or nullrequired
  • sequence_idstring or nullrequired

    A sequence each person is added to.

    Starts with seq_.

  • last_used_attimestamp or nullrequired

    When it last received a post.

  • created_attimestamprequired

    When it was made.

Response201
{
  "id": "hook_1Hq2LxW9cVbN3mKpRtYs0a",
  "object": "text_hook",
  "name": "Calendly bookings",
  "url": "https://usebluebot.com/h/th_8Rk2mQ4zN7wX1cVbL5pT9yHsQ2aVc0Ld",
  "enabled": true,
  "test_mode": true,
  "mapping": {
    "to": "{{body.payload.questions_and_answers.0.answer}}",
    "text": "Hi {{body.payload.name}}, you’re booked for {{body.payload.scheduled_event.start_time}}. Text back if anything changes.",
    "first_name": "{{body.payload.first_name}}",
    "last_name": "{{body.payload.last_name}}",
    "tags": "calendly"
  },
  "template_id": null,
  "sequence_id": null,
  "last_used_at": null,
  "created_at": "2026-11-03T16:00:00.000Z"
}

Errors

Every refusal has the same envelope and a page of its own. These are the ones this operation can give.

CodeStatusWhat it means
unauthorized401The request carried no key, or a key BlueBot doesn’t know: mistyped, revoked, or a test key sent to the live API.
insufficient_scope403The key doesn’t carry the scope the endpoint needs. Each endpoint’s reference names its scope.
rate_limited429Too many requests. A key may make 120 requests a minute. The RateLimit header says how many are left, and Retry-After how long to wait.
internal500Something went wrong on BlueBot’s side. It was reported. Send the request again; with an Idempotency-Key that is always safe.
invalid_request400Something in the request doesn’t fit: a missing or malformed field, a bad id, or a combination the endpoint refuses. details names each field.
not_found404There is no such thing on this account. Ids are prefixed by kind, so a message id starts with msg_.
limit_reached409The account has as many of something as it may have.
plan_lapsed402The account’s plan isn’t active, so BlueBot isn’t taking new texts. Texts already queued wait and go once the plan is active again.
idempotency_conflict409The Idempotency-Key was used in the last 24 hours with a different request. A key stands for one request.

Try it

Call this operation from the page with your own key. The key stays in this tab and is forgotten when you leave.