Developer
WebhookEndpoint 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-Signaturewhen 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.
methodrequiredurlrequiredbodyTemplateheadersTemplateTemplate 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 asnullwhen 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)".
botTokenrequiredchatIdrequiredThe 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