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

Screenshot Placeholder: Webhooks benefits

Available Events

Event Notifications

Receive notifications for:

Reservation Events:

  • reservation.created - New booking made
  • reservation.confirmed - Booking confirmed by venue
  • reservation.updated - Booking details changed
  • reservation.cancelled - Booking cancelled
  • reservation.seated - Guest arrived and seated
  • reservation.completed - Dining experience finished
  • reservation.no_show - Guest didn't arrive

Review Events:

  • review.received - Customer left review
  • review.published - Review went live
  • review.replied - You responded to review

Waitlist Events:

  • waitlist.joined - Customer joined waitlist
  • waitlist.available - Slot became available
  • waitlist.converted - Waitlist → booking

Screenshot Placeholder: Webhook events

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:

  1. Go to Manage API > Webhooks
  2. Click "Add Webhook"
  3. Enter your endpoint URL
  4. Select events to receive
  5. Generate webhook secret
  6. Save configuration

Screenshot Placeholder: Webhook setup

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

Screenshot Placeholder: Test webhook

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

Screenshot Placeholder: Webhook configuration

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...
});

Screenshot Placeholder: Webhook verification

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

Screenshot Placeholder: Webhook best practices

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

Screenshot Placeholder: Retry timeline

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:

  1. View failed webhooks
  2. Check error details
  3. Fix endpoint issue
  4. Click "Retry Webhook"
  5. Confirm success

Screenshot Placeholder: Manual retry

Testing Webhooks

Test Events

Send Test Events:

  1. Go to webhook configuration
  2. Click "Send Test Event"
  3. Select event type
  4. View test payload
  5. 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)

Screenshot Placeholder: Testing tools

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

Screenshot Placeholder: Webhook dashboard

Webhook Logs

Detailed Logging:

  • Event ID
  • Event type
  • Timestamp
  • Status
  • Response code
  • Response time
  • Retry attempts
  • Error messages

Screenshot Placeholder: Webhook logs

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>

Screenshot Placeholder: Widget integration

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' });
  }
});

Screenshot Placeholder: Custom form

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>
  `;
});

Screenshot Placeholder: Availability widget

Next Steps

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.

×