Webhooks
Know the moment a booking changes
Add an HTTPS URL in your dashboard and Bewtee will POST signed JSON to it whenever a booking event happens. Update your CRM, billing or messaging tools without polling the API.
- Eight booking events
- Signed requests
- Automatic retries
Setup
Set one up in a minute
-
Add your URL
In the Developers section of your dashboard, add a public HTTPS address and pick the events you want.
-
Save the signing secret
It is shown once. Your server uses it to check that requests really come from Bewtee.
-
Send a test
Press Send test to deliver a sample event and see whether your server accepted it.
Reference
Events
-
booking.created
A new booking was placed.
-
booking.accepted
The booking was confirmed, by the business or by auto-accept.
-
booking.rejected
The business declined the booking.
-
booking.cancelled
The booking was cancelled.
-
booking.rescheduled
The booking moved to a new date or time.
-
booking.completed
The appointment was marked as completed.
-
booking.no_show
The customer did not turn up.
-
booking.expired
A pending booking was not accepted in time.
Payload
What Bewtee sends
Every event has the same outer shape. The type field tells you what happened and data.booking holds the booking at that moment.
{
"id": "evt_...",
"type": "booking.accepted",
"api_version": "v1",
"business_id": 123,
"created_at": "2026-11-12T10:30:00+05:30",
"data": {
"booking": {
"id": 9812,
"status": "accepted",
"date": "2026-11-15",
"time": "11:00",
"amount": {
"total": 1200,
"currency": "INR"
},
"customer": {
"name": "Asha",
"phone": "98XXXXXXXX"
},
"services": [
{
"name": "Haircut",
"price": 1200,
"duration_minutes": 45,
"quantity": 1
}
]
},
"previous_status": "pending"
}
}
Reply quickly. Answer with any 2xx status within 5 seconds, then do slower work afterwards. Anything else counts as a failed delivery.
Security
Verify the signature
Each request carries a Bewtee-Signature header so you can confirm it really came from Bewtee:
Bewtee-Signature: t=1794373200,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
tis the Unix time the request was signed.v1is the hex HMAC-SHA256 of the text<t>.<raw body>, using the signing secret of your endpoint as the key.- Compute the same value on your server from the raw request body (before parsing the JSON) and compare. Reject the request if they differ.
const crypto = require('crypto');
// rawBody must be the exact request body string, before JSON parsing.
function verifyBewteeSignature(rawBody, header, secret) {
const parts = Object.fromEntries(
header.split(',').map((pair) => pair.split('='))
);
const expected = crypto
.createHmac('sha256', secret)
.update(`${parts.t}.${rawBody}`)
.digest('hex');
const a = Buffer.from(expected);
const b = Buffer.from(parts.v1 || '');
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
<?php
function verifyBewteeSignature(string $rawBody, string $header, string $secret): bool
{
$parts = [];
foreach (explode(',', $header) as $pair) {
[$key, $value] = array_pad(explode('=', $pair, 2), 2, '');
$parts[$key] = $value;
}
$expected = hash_hmac('sha256', ($parts['t'] ?? '').'.'.$rawBody, $secret);
return hash_equals($expected, $parts['v1'] ?? '');
}
$rawBody = file_get_contents('php://input');
$header = $_SERVER['HTTP_BEWTEE_SIGNATURE'] ?? '';
if (! verifyBewteeSignature($rawBody, $header, $signingSecret)) {
http_response_code(400);
exit;
}
Reliability
Retries
If a delivery fails, Bewtee tries again, up to 6 attempts in total over about 8.5 hours.
- First try
- +1 min
- +5 min
- +30 min
- +2 hours
- +6 hours
Because a delivery can be retried, use the event id to skip events you have already handled. A webhook that keeps failing is paused, and you can switch it back on from your dashboard once your server is fixed. Delivery details and the full payload for each event are in the webhooks documentation.
Ready to connect Bewtee?
Everything is set up from the Developers section of your business dashboard. It is free for Bewtee businesses.
New to Bewtee? See what the salon software does