Punctual Webhooks
Get real-time notifications about events in your Punctual account. Build powerful integrations that respond instantly to scheduling changes.
Why use webhooks?
Powerful features for real-time integration
Secure Delivery
All webhooks are signed with HMAC-SHA256 to ensure authenticity and prevent tampering
Automatic Retries
Failed webhook deliveries are automatically retried with exponential backoff
Real-time Events
Receive events instantly as they happen in your Punctual account
Event History
Complete history of all webhook events with delivery status and response codes
Easy Configuration
Simple setup through our dashboard with instant testing capabilities
Global Reliability
Webhooks are delivered from multiple data centers for maximum reliability
Available Events
All the events you can subscribe to
booking.created
Triggered when a new booking is created
{
"id": "evt_1234567890",
"type": "booking.created",
"created": "2024-01-15T10:30:00Z",
"data": {
"object": {
"id": "booking_123",
"title": "Product Demo",
"start_time": "2024-01-20T14:00:00Z",
"end_time": "2024-01-20T15:00:00Z",
"attendee_email": "client@example.com",
"status": "confirmed",
"created_at": "2024-01-15T10:30:00Z"
}
}
}booking.updated
Triggered when a booking is modified
{
"id": "evt_1234567891",
"type": "booking.updated",
"created": "2024-01-15T11:00:00Z",
"data": {
"object": {
"id": "booking_123",
"title": "Updated Product Demo",
"start_time": "2024-01-20T15:00:00Z",
"end_time": "2024-01-20T16:00:00Z",
"attendee_email": "client@example.com",
"status": "confirmed",
"updated_at": "2024-01-15T11:00:00Z"
}
}
}booking.cancelled
Triggered when a booking is cancelled
{
"id": "evt_1234567892",
"type": "booking.cancelled",
"created": "2024-01-15T12:00:00Z",
"data": {
"object": {
"id": "booking_123",
"title": "Product Demo",
"start_time": "2024-01-20T14:00:00Z",
"end_time": "2024-01-20T15:00:00Z",
"attendee_email": "client@example.com",
"status": "cancelled",
"cancelled_at": "2024-01-15T12:00:00Z"
}
}
}availability.updated
Triggered when user availability changes
{
"id": "evt_1234567893",
"type": "availability.updated",
"created": "2024-01-15T13:00:00Z",
"data": {
"object": {
"user_id": "user_123",
"date": "2024-01-20",
"slots": [
{
"start": "09:00",
"end": "10:00",
"available": true
},
{
"start": "10:00",
"end": "11:00",
"available": false
},
{
"start": "11:00",
"end": "12:00",
"available": true
}
],
"updated_at": "2024-01-15T13:00:00Z"
}
}
}user.created
Triggered when a new user signs up
{
"id": "evt_1234567894",
"type": "user.created",
"created": "2024-01-15T14:00:00Z",
"data": {
"object": {
"id": "user_456",
"email": "newuser@example.com",
"name": "John Doe",
"created_at": "2024-01-15T14:00:00Z"
}
}
}team.member_added
Triggered when a new member is added to a team
{
"id": "evt_1234567895",
"type": "team.member_added",
"created": "2024-01-15T15:00:00Z",
"data": {
"object": {
"team_id": "team_789",
"user_id": "user_456",
"role": "member",
"added_at": "2024-01-15T15:00:00Z"
}
}
}Code Examples
Get started quickly with these implementation examples
Nodejs
const crypto = require('crypto');
const express = require('express');
const app = express();
app.use(express.raw({ type: 'application/json' }));
app.post('/webhook', (req, res) => {
const signature = req.headers['punctual-signature'];
const payload = req.body;
// Verify webhook signature
const expectedSignature = crypto
.createHmac('sha256', process.env.PUNCTUAL_WEBHOOK_SECRET)
.update(payload)
.digest('hex');
if (signature !== expectedSignature) {
return res.status(400).send('Invalid signature');
}
const event = JSON.parse(payload);
// Handle the event
switch (event.type) {
case 'booking.created':
console.log('New booking:', event.data.object);
break;
case 'booking.updated':
console.log('Booking updated:', event.data.object);
break;
case 'booking.cancelled':
console.log('Booking cancelled:', event.data.object);
break;
default:
console.log('Unhandled event type:', event.type);
}
res.json({ received: true });
});
app.listen(3000, () => {
console.log('Webhook server running on port 3000');
});Python
import hmac
import hashlib
import json
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/webhook', methods=['POST'])
def webhook():
signature = request.headers.get('Punctual-Signature')
payload = request.get_data()
# Verify webhook signature
expected_signature = hmac.new(
bytes(os.environ['PUNCTUAL_WEBHOOK_SECRET'], 'utf-8'),
payload,
hashlib.sha256
).hexdigest()
if not hmac.compare_digest(signature, expected_signature):
return jsonify({'error': 'Invalid signature'}), 400
event = json.loads(payload)
# Handle the event
if event['type'] == 'booking.created':
print(f"New booking: {event['data']['object']}")
elif event['type'] == 'booking.updated':
print(f"Booking updated: {event['data']['object']}")
elif event['type'] == 'booking.cancelled':
print(f"Booking cancelled: {event['data']['object']}")
else:
print(f"Unhandled event type: {event['type']}")
return jsonify({'received': True})
if __name__ == '__main__':
app.run(port=3000)Php
<?php
$webhook_secret = $_ENV['PUNCTUAL_WEBHOOK_SECRET'];
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_PUNCTUAL_SIGNATURE'];
// Verify webhook signature
$expected_signature = hash_hmac('sha256', $payload, $webhook_secret);
if (!hash_equals($signature, $expected_signature)) {
http_response_code(400);
echo json_encode(['error' => 'Invalid signature']);
exit;
}
$event = json_decode($payload, true);
// Handle the event
switch ($event['type']) {
case 'booking.created':
error_log("New booking: " . json_encode($event['data']['object']));
break;
case 'booking.updated':
error_log("Booking updated: " . json_encode($event['data']['object']));
break;
case 'booking.cancelled':
error_log("Booking cancelled: " . json_encode($event['data']['object']));
break;
default:
error_log("Unhandled event type: " . $event['type']);
}
echo json_encode(['received' => true]);
?>Delivery Status
Understanding webhook delivery states
delivered
Webhook was successfully delivered to your endpoint
failed
Webhook delivery failed and will be retried
pending
Webhook is queued for delivery
retrying
Webhook is being retried after a previous failure
Security & Best Practices
Keep your webhooks secure and reliable
Signature Verification
Always verify webhook signatures to ensure the requests are coming from Punctual. We use HMAC-SHA256 to sign all webhook payloads with your webhook secret.
Idempotency
Webhook events may be delivered multiple times. Use the event ID to ensure you don't process the same event twice.
HTTPS Only
Always use HTTPS endpoints for webhook receivers. We will not deliver webhooks to HTTP endpoints for security reasons.
Response Handling
Respond with a 2xx status code to acknowledge receipt. Any other status code will cause us to retry the webhook delivery.
Ready to get started?
Set up your first webhook and start building powerful integrations