Webhooks & Integration
Learn how to configure webhooks for real-time event notifications and integrate Grab A Table booking into your systems.
Webhooks Overview
What are Webhooks?
Webhooks provide real-time notifications when events occur:
- Proactive vs. polling
- Instant updates
- Efficient resource usage
- Event-driven architecture
- Reliable delivery
Benefits
Why Use Webhooks:
- Real-time synchronization
- Reduce API polling
- Instant notifications
- Automated workflows
- Better user experience
- Lower API usage

Available Events
Event Notifications
Receive notifications for:
Reservation Events:
reservation.created- New booking madereservation.confirmed- Booking confirmed by venuereservation.updated- Booking details changedreservation.cancelled- Booking cancelledreservation.seated- Guest arrived and seatedreservation.completed- Dining experience finishedreservation.no_show- Guest didn't arrive
Review Events:
review.received- Customer left reviewreview.published- Review went livereview.replied- You responded to review
Waitlist Events:
waitlist.joined- Customer joined waitlistwaitlist.available- Slot became availablewaitlist.converted- Waitlist → booking

Event Payload
Standard Format:
{
"event": "reservation.created",
"event_id": "evt_1234567890",
"timestamp": "2026-03-07T10:30:00Z",
"api_version": "v1",
"data": {
"booking_reference": "RES-ABC123",
"venue_id": "venue-123",
"date": "2026-03-15",
"time": "19:00",
"party_size": 4,
"guest_name": "John Doe",
"guest_email": "[email protected]",
"guest_phone": "+44 7700 900000",
"special_requests": "Window seat please",
"status": "pending"
}
}
Webhook Setup
Create Webhook Endpoint
1. Set Up Your Endpoint:
Create a POST endpoint on your server:
// Node.js/Express example
app.post('/webhooks/grabatable', async (req, res) => {
const event = req.body;
// Verify signature (see security section)
if (!verifySignature(req)) {
return res.status(401).send('Invalid signature');
}
// Process event
await handleEvent(event);
// Respond quickly
res.status(200).send('OK');
});
2. Configure in Dashboard:
- Go to Manage API > Webhooks
- Click "Add Webhook"
- Enter your endpoint URL
- Select events to receive
- Generate webhook secret
- Save configuration

3. Test Webhook:
Use test event to verify:
- Click "Send Test Event"
- Check your endpoint received it
- Verify signature validation works
- Confirm processing is correct

Configuration Options
Webhook Settings:
- URL: Your endpoint (must be HTTPS)
- Events: Select which events to receive
- Secret: For signature verification
- Retry: Automatic retry on failure
- Timeout: Max wait time (default 10s)
- Active/Inactive: Enable/disable webhook

Webhook Security
Signature Verification
Why Verify:
- Confirm webhook is from Grab A Table
- Prevent spoofing attacks
- Ensure data integrity
- Comply with security best practices
Verify Signature
Implementation:
const crypto = require('crypto');
function verifyWebhook(payload, signature, secret) {
// Compute expected signature
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(JSON.stringify(payload))
.digest('hex');
// Compare signatures
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
// Use in your endpoint
app.post('/webhooks/grabatable', (req, res) => {
const signature = req.headers['x-grabatable-signature'];
const secret = process.env.WEBHOOK_SECRET;
if (!verifyWebhook(req.body, signature, secret)) {
return res.status(401).send('Invalid signature');
}
// Process webhook...
});

Signature Header:
- Header:
X-GrabATable-Signature - Algorithm: HMAC SHA-256
- Payload: Raw request body
HTTPS Required
Security Requirements:
- Endpoint must use HTTPS
- Valid SSL certificate
- No self-signed certificates (production)
- TLS 1.2 or higher
IP Whitelisting
Optional Additional Security:
Limit webhook requests to Grab A Table IPs:
Webhook IPs:
- 198.51.100.1
- 198.51.100.2
- 203.0.113.1
(Check documentation for current IPs)
Handling Webhooks
Best Practices
Do:
- ✓ Respond quickly (< 10 seconds)
- ✓ Return 200 status immediately
- ✓ Process async in background
- ✓ Verify signatures
- ✓ Handle idempotency
- ✓ Log all webhooks
- ✓ Implement retry logic
Don't:
- ✗ Perform long-running tasks synchronously
- ✗ Return non-200 for valid webhooks
- ✗ Make API calls back in webhook handler
- ✗ Skip signature verification
- ✗ Process duplicate events multiple times

Async Processing
Recommended Pattern:
app.post('/webhooks/grabatable', async (req, res) => {
// Verify signature
if (!verifySignature(req)) {
return res.status(401).send('Invalid');
}
// Add to queue for processing
await queue.add('webhook', req.body);
// Respond immediately
res.status(200).send('OK');
});
// Process in background worker
queue.process('webhook', async (job) => {
const event = job.data;
// Handle event based on type
switch (event.event) {
case 'reservation.created':
await handleNewReservation(event.data);
break;
case 'reservation.cancelled':
await handleCancellation(event.data);
break;
// ... other events
}
});
Idempotency
Handle Duplicate Events:
// Store processed event IDs
const processedEvents = new Set();
async function handleEvent(event) {
// Check if already processed
if (processedEvents.has(event.event_id)) {
console.log('Duplicate event, skipping');
return;
}
// Process event
await processEventLogic(event);
// Mark as processed
processedEvents.add(event.event_id);
// Persist to database
await db.markEventProcessed(event.event_id);
}
Error Handling
Graceful Error Management:
async function handleWebhook(event) {
try {
await processEvent(event);
} catch (error) {
console.error('Webhook processing failed:', error);
// Log error details
await logError({
event_id: event.event_id,
event_type: event.event,
error: error.message,
stack: error.stack
});
// Don't throw - webhook delivery succeeded
// You received it, processing failed
}
}
Retry Logic
Automatic Retries
Grab A Table Retry Policy:
- Failed webhooks automatically retried
- Exponential backoff
- Up to 3 retry attempts
- 1 hour between attempts
- 24 hour maximum retry window

When Webhooks Fail
Failure Conditions:
- HTTP timeout (10 seconds)
- Non-200 response status
- Connection error
- DNS resolution failure
- Certificate error
Manual Retry
Retry from Dashboard:
- View failed webhooks
- Check error details
- Fix endpoint issue
- Click "Retry Webhook"
- Confirm success

Testing Webhooks
Test Events
Send Test Events:
- Go to webhook configuration
- Click "Send Test Event"
- Select event type
- View test payload
- Check your logs
Test Event Example:
{
"event": "reservation.created",
"test": true,
"data": {
"booking_reference": "TEST-12345",
"venue_id": "venue-123",
"date": "2026-03-15",
"time": "19:00",
"party_size": 4
}
}
Local Testing
Use webhook testing tools:
- ngrok (tunnel to localhost)
- Webhook.site (inspect webhooks)
- RequestBin (capture requests)
- Postman (mock webhooks)

ngrok Example:
# Start local server
node server.js
# Create tunnel
ngrok http 3000
# Use ngrok URL in webhook config
https://abc123.ngrok.io/webhooks/grabatable
Monitoring Webhooks
Webhook Dashboard
View Webhook Activity:
- Total webhooks sent
- Success rate
- Failed deliveries
- Average response time
- Recent activity

Webhook Logs
Detailed Logging:
- Event ID
- Event type
- Timestamp
- Status
- Response code
- Response time
- Retry attempts
- Error messages

Filter Options:
- By event type
- By status (success/failed)
- By date range
- By response time
- Search by ID
Alerts
Configure Webhook Alerts:
- Multiple failures
- High failure rate
- Slow response times
- Endpoint unreachable
- Certificate expiring
Integration Examples
Website Booking Widget
Embed Booking on Your Site:
<div id="grabatable-widget"></div>
<script src="https://grabatable.app/widget.js"></script>
<script>
GrabATable.init({
venueId: 'your-venue-id',
element: '#grabatable-widget',
theme: 'light',
onSuccess: function(booking) {
// Booking created
console.log('Booking confirmed:', booking.reference);
}
});
</script>

Note: Widget uses secure server-side authentication, no API key needed in frontend.
Custom Booking Form
Build Your Own Interface:
// Server-side endpoint
app.post('/api/create-booking', async (req, res) => {
try {
const response = await fetch('https://api.grabatable.app/v1/reservations', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.GRABATABLE_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
venue_id: req.body.venueId,
date: req.body.date,
time: req.body.time,
party_size: req.body.partySize,
guest_name: req.body.name,
guest_email: req.body.email,
guest_phone: req.body.phone special_requests: req.body.specialRequests
})
});
const data = await response.json();
res.json(data);
} catch (error) {
res.status(500).json({ error: 'Booking failed' });
}
});

Availability Widget
Show Real-Time Availability:
// Check available slots
async function getAvailability(date, partySize) {
const response = await fetch(
`/api/availability?date=${date}&party_size=${partySize}`
);
return response.json();
}
// Display available times
availability.forEach(slot => {
const button = `
<button onclick="selectTime('${slot.time}')">
${slot.time} - ${slot.available} tables
</button>
`;
});

Next Steps
- Review API keys management
- Configure API security
- Monitor API usage and limits
- Read API reference documentation
Need Help?
If you have questions about webhooks or integration, contact our support team at [email protected] or via WhatsApp.
Integration Support: We offer assistance with custom integrations and webhook setup.