Developer

Webhook

Endpoint kinds

How ServiceReserve delivers events to each endpoint kind — signed webhook, custom HTTP request, or legacy Telegram message.

Updated Sep 30, 2026

When you click Add webhook on the Webhooks page, you pick how the event is delivered: Webhook (kind webhook) or Custom request (kind api). Every endpoint receives the same booking.completed event. The kind decides what ServiceReserve sends to your URL.

webhook — signed JSON POST

The default. ServiceReserve sends a POST with the standard event envelope and, if you set a signing secret, signs the body.

  • Method: always POST.
  • Body: application/json, the full event envelope.
  • Auth: X-ServiceReserve-Signature when a Signing secret is set. See Signing & verification.

Use this for integrations into systems you control.

api — custom HTTP request

You choose the method, URL, body template, and extra headers. Use it to post to third-party services that don't accept the ServiceReserve envelope, such as ingest URLs, chat incoming webhooks, or custom CRMs. Custom requests are never signed.

methodrequired
GET | POST | PUT | PATCH | DELETE
HTTP method. Defaults to POST.
urlrequired
string
Target URL.
bodyTemplate
string
Optional body template. {{path}} tokens are replaced with payload values at send time. With no template, POST, PUT, and PATCH send the full JSON envelope, and GET and DELETE send no body.
headersTemplate
string (JSON object)
Optional JSON object of extra request headers. Values support the same {{path}} tokens. These headers are applied last, so they override the defaults. A header whose value renders empty is dropped.

Template syntax

Templates use double-brace tokens that resolve against the payload. You can address the top-level keys (event, form_id, organization_id, sent_at, data) and every key under data directly, by dotted path. Use a number to index into an array.

New booking from {{contact.first_name}} {{contact.last_name}}
Email: {{contact.email}}
Service: {{services.0.name}}
When: {{preferred_time.date}} ({{preferred_time.slot}})

{{contact.email}} and {{data.contact.email}} resolve to the same value.

How tokens render:

  • Strings, numbers, and booleans are inserted as-is. Nothing is escaped, so write the template for your target content type.
  • Objects and arrays are inserted as JSON.
  • A path that doesn't resolve, or resolves to null, renders as null when the token is an unquoted JSON value ("x": {{path}}), and as an empty string everywhere else. There's no error.

Headers template

The headers template is a JSON object of string values:

{
  "Authorization": "Bearer YOUR_TOKEN",
  "X-Customer-Email": "{{contact.email}}"
}

Custom requests always send User-Agent: ServiceReserve-Webhook/1.0 and X-ServiceReserve-Event, plus a Content-Type when there is a body. Any header in your template with the same name replaces the default.

Body content type

ServiceReserve picks the Content-Type from the rendered body. If the body starts with { or [ and parses as JSON, it is application/json. Otherwise it is text/plain; charset=utf-8. Set Content-Type in the headers template to override it.

GET and DELETE don't carry a body. For those methods, a template that renders to a JSON object is folded into the URL as query-string parameters, with nested values JSON-encoded. A template that doesn't render to a JSON object is ignored, and the request goes out without it.

telegram — chat message (legacy)

A telegram endpoint posts each event to a Telegram chat through the Bot API. The HTML-formatted message includes the customer's name, contact details, address, services, preferred time, intake answers, and attribution. Test sends are labeled "(test)".

botTokenrequired
string
Bot token from @BotFather.
chatIdrequired
string
Target chat: a numeric id (negative for groups and channels) or @channel_username.

The bot must be a member of the chat to post. For private chats, send the bot a message once first.

One endpoint or many

Every endpoint receives booking.completed. Add several endpoints to fan one event out to several systems. Deliveries to each endpoint are queued, retried, and logged separately. To route events inside one handler, switch on the X-ServiceReserve-Event header.

Related