Home · Help · Incoming and outgoing webhooks

Incoming and outgoing webhooks

A CHBT flow has two webhooks, and they go in opposite directions. “Incoming webhook” is a trigger: your system sends a POST to a CHBT URL, and the flow starts. “HTTP request” is an action: the flow sends a POST with the contact’s data to your URL. A third mechanism lives outside flows — the bot’s subscription to conversation events in the “Integrations and APIs” section, covered at the end. Below are the URLs, body formats, secrets, and the main limitation of the incoming webhook, which people ask about most often.

The “HTTP request” action: from the bot to the outside world

Add it in an “Action” block anywhere in the flow. Settings: your endpoint URL, the method (POST by default) and, optionally, the name of a variable to save the response to.

The request body is JSON with the Content-Type: application/json header:

{
  "contact": {
    "id": 1234,
    "tags": "[\"booking\",\"haircut\"]",
    "name": "Anna",
    "phone": "+79990000000",
    "email": "",
    "fields": { "slot": "tomorrow at 3 pm" }
  },
  "vars": { "slot": "tomorrow at 3 pm" },
  "text": "tomorrow at 3 pm",
  "channelType": "telegram"
}
  • contact — the contact card: the ID in CHBT, tags, name, phone, email and any other fields collected by “Data collection” (fields). The phone and name arrive as soon as the customer has entered them — you don’t need to turn on “Save to variable” for that. An empty field is an empty string. Tags arrive as a string in JSON array format, so parse it on your side.
  • vars — flow variables. This includes whatever “Data collection” saved through the “Save to variable” field and whatever was written by the “Set a variable” action.
  • text — the customer’s last message.
  • channelType — telegram, max, vk or web.

You can save the server response (the body as text) with “Save to variable” and show it to the customer in the next “Message” via {{variable_name}} — this is how you build “order status from your accounting system”. The bot waits up to 15 seconds for a response; on an error or timeout the flow continues, the error is written to the CHBT log, and there are no retries. Requests aren’t signed, so verify the source by a secret segment in the URL (https://your-server/hook/long-random-string).

Example: a lead in Make or n8n

  1. In Make, create a scenario with the “Custom webhook” module; in n8n, add a “Webhook” node. Copy the URL it gives you.
  2. In CHBT, after the “Data collection” blocks (Name and Phone, both set to “Save to variable”), add “Action” → “HTTP request” with this URL.
  3. Message the bot as a customer in the connected Telegram or widget. The “Check flow” test chat doesn’t send HTTP requests, so Make/n8n will show the field structure after the first real request.
  4. Then, in the same Make/n8n scenario, add an amoCRM, Bitrix24, Google Sheets, or email module: map the vars.name, vars.phone, and contact.tags fields to the right places.

CHBT has built-in integrations only with Bitrix24 and Omnidesk; for other CRMs, this is the standard setup. For a comparison of the “built-in CRM or your own” options, see the article leads from a bot to a CRM; an overview of all integration points is on the integrations page.

“Incoming webhook” trigger: from outside into a flow

Add an “Incoming webhook” trigger to the flow and set the “Webhook path” — a short word in Latin letters, for example order-paid. The dashboard will show the full URL:

POST https://app.chbt.io/webhook/inbound/<bot ID>/order-paid?token=<secret>

The secret is generated with the “Generate” button in the trigger settings and is passed either in ?token= or in the X-Webhook-Token header. Requests without the correct token get 401. Older triggers without a secret have no check — turn it on with the button and update the URL in your system.

The request body is JSON, and all fields are optional:

{ "uid": "order-1042", "text": "paid", "name": "Order 1042" }
  • uid — the ID of the event or object; the same uid in repeated requests continues the same service conversation, while different ones create separate conversations.
  • text — passed to the flow as the message text (event by default).
  • name — the name of the service contact in “Conversations” (“Webhook” by default).

The flow must be enabled (✓ in the “Active” column), and the bot must have the “Widget for the site” channel connected: the incoming webhook works through it. CHBT responds with {"ok":true}; 404 no_trigger means the path didn’t match or the flow is disabled.

What the incoming webhook does — and what it doesn’t

What it does: starts a flow as a separate event. Useful next steps: “Action” → “CRM deal” (“Payment: {{name}}”), “Notify an operator” (Web Push and a message in the dashboard), “Add a tag” to the service contact, or an “HTTP request” further down the chain.

What it doesn't do: it doesn't find an existing customer in Telegram, MAX, or VK and doesn't send them a message. The event arrives for a service contact in the widget channel, not for the customer who messaged your bot yesterday — CHBT doesn't link the uid from the request to that person's Telegram account. So a setup like “your system learns about a status change → the customer gets a message in the messenger” can't be built with an incoming webhook; likewise, it won't tag a real customer or start a “Pause” for them.

How targeted notifications work in CHBT:

  • at the customer's request — a “Where's my order” button → “Data collection” for the order number → “HTTP request” to your system (response “Save to variable”) → “Message” with the status; CHBT can't get an order's status from Ozon or Wildberries by its number — such questions go to an operator;
  • by broadcast to a tag — for events that concern a group (“this week's orders have shipped”); the tag is set by a flow or by an admin in “Contacts”;
  • by an operator from “Conversations” — a quick-reply template in an existing conversation.

Why it works this way and how to live with the “the customer writes first” rule is explained in the article notifying customers through a bot. Both webhooks are available on the Free plan.

Webhooks for conversation events: bot subscription

The subscription is set up not in a flow but in the bot's “Integrations and APIs” section: the URL, the set of events, and the signing secret. There are six events: message.inbound (a customer message), message.outbound (a reply from the bot or an operator; for product cards, it includes the name, price, currency, link, photo, and availability), conversation.started, conversation.handoff (handed off to a human), conversation.closed, and contact.created. There are no events for orders, payments, or product views.

The body is JSON with the fields event, id, occurredAt, project, and, depending on the event, conversation, contact, and message. The headers are X-Chbt-Event, X-Chbt-Delivery, X-Chbt-Timestamp, and X-Chbt-Signature: a hex HMAC-SHA256 of the string “timestamp, dot, raw body”. Verify the signature with a constant-time comparison and reject requests whose timestamp differs from the current time by more than 5 minutes. If the URL doesn't respond, CHBT makes up to five attempts with delays from 30 seconds to an hour; redirects aren't followed, and internal network addresses aren't accepted. The delivery log with your server's response codes is shown in the same section.

For Bitrix24 and Omnidesk, we build the body in their own format — a lead or a ticket — so there's no signature. To reply to a customer from an external system, use the public API.

FAQ

Can an incoming webhook send a message to a specific customer in Telegram?
No. It runs the flow as a separate event for a service contact and isn't linked to real customers' conversations. Targeted notifications are sent at the customer's request within a flow, through a broadcast by tag, or from “Conversations”.

What fields does the HTTP request send?
JSON: contact (id and tags), vars (flow variables), text (the customer's last message), channelType (the channel). Phone and name are included in vars if “Save to variable” is enabled in “Data collection”.

Are there retries if my server didn’t respond?
No. CHBT waits up to 15 seconds; on error, it continues the flow and logs the error. Receive webhooks with a queue-based service (Make, n8n), or respond with 200 right away and process the data later.

Can I leave the incoming webhook secret empty?
Technically, old triggers without a secret are still accepted, but anyone who learns the URL can start the flow. Click “Generate” and pass the token in ?token= or in the X-Webhook-Token header.

Create a bot for free