Webhooks

Webhooks let you receive real-time notifications when events happen in Crumb. Instead of polling our API, you can set up an endpoint to receive automatic updates.

Webhook flow diagram

Setting Up Webhooks

1. Create an Endpoint

Create an HTTPS endpoint on your server that can receive POST requests. The endpoint must:

  • Accept POST requests with JSON body
  • Respond with a 2xx status code within 30 seconds
  • Be publicly accessible (not behind a firewall)

2. Register in Dashboard

Go to Settings → Developers → Webhooks in your Crumb dashboard and click "Add Endpoint." Enter your endpoint URL and select which events you want to receive.

3. Get Your Signing Secret

After creating the endpoint, you'll receive a webhook signing secret (starts with whsec_). Store this securely—you'll use it to verify webhook signatures.

Event Types

Event Description
order.created A new order was placed
order.updated An order was modified
order.fulfilled An order was marked as complete
order.cancelled An order was cancelled
payment.completed A payment was successfully processed
payment.refunded A payment was refunded
menu.item_updated A menu item was modified
inventory.low_stock An item reached low stock threshold

Webhook Payload

All webhooks are sent as POST requests with a JSON body:

Webhook Payload
{
  "id": "evt_abc123",
  "type": "order.created",
  "created_at": "2024-01-15T14:30:00Z",
  "data": {
    "id": "ord_xyz789",
    "type": "order",
    "attributes": {
      "total": 4250,
      "status": "open",
      "items": [
        {
          "name": "Burger Deluxe",
          "quantity": 2,
          "price": 1500
        }
      ],
      "location_id": "loc_abc123"
    }
  }
}

Verifying Signatures

Every webhook includes a signature in the Crumb-Signature header. Always verify this signature before processing the webhook to ensure it came from Crumb.

Signature Header Format

Crumb-Signature: t=1673795400,v1=abc123...

The header contains:

  • t: Unix timestamp when the signature was created
  • v1: The signature (HMAC-SHA256 of timestamp + payload)

Verification Example (Node.js)

webhook-verify.js
const crypto = require('crypto');

function verifyWebhook(payload, header, secret) {
  const parts = header.split(',');
  const timestamp = parts[0].split('=')[1];
  const signature = parts[1].split('=')[1];

  // Check timestamp is recent (within 5 minutes)
  const now = Math.floor(Date.now() / 1000);
  if (now - parseInt(timestamp) > 300) {
    throw new Error('Timestamp too old');
  }

  // Compute expected signature
  const signedPayload = timestamp + '.' + payload;
  const expected = crypto
    .createHmac('sha256', secret)
    .update(signedPayload)
    .digest('hex');

  // Compare signatures
  if (signature !== expected) {
    throw new Error('Invalid signature');
  }

  return JSON.parse(payload);
}

Verification Example (Python)

webhook_verify.py
import hmac
import hashlib
import time
import json

def verify_webhook(payload, header, secret):
    parts = dict(p.split('=') for p in header.split(','))
    timestamp = parts['t']
    signature = parts['v1']

    # Check timestamp is recent
    if time.time() - int(timestamp) > 300:
        raise ValueError('Timestamp too old')

    # Compute expected signature
    signed_payload = f"{timestamp}.{payload}"
    expected = hmac.new(
        secret.encode(),
        signed_payload.encode(),
        hashlib.sha256
    ).hexdigest()

    # Compare signatures
    if not hmac.compare_digest(signature, expected):
        raise ValueError('Invalid signature')

    return json.loads(payload)

Always verify signatures

Never process a webhook without verifying its signature. This protects you from forged requests.

Retry Policy

If your endpoint doesn't return a 2xx response within 30 seconds, we'll retry the webhook with exponential backoff:

  • Retry 1: 1 minute after initial attempt
  • Retry 2: 5 minutes after retry 1
  • Retry 3: 30 minutes after retry 2

After 3 failed retries, the webhook is marked as failed and won't be retried automatically. You can manually retry failed webhooks from your dashboard.

Testing Webhooks

Use the "Send Test Event" button in your dashboard to send a test webhook to your endpoint. You can also use tools like ngrok to expose a local development server.

Test webhook
curl https://api.crumbpos.com/v1/webhooks/test \
  -X POST \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{
    "endpoint_id": "we_abc123",
    "event_type": "order.created"
  }'

Best Practices

  • Respond quickly: Return a 2xx response as soon as you receive the webhook, then process asynchronously
  • Handle duplicates: Webhooks may occasionally be delivered more than once. Use the event id to deduplicate
  • Process in order: Events for the same resource are sent in order, but may arrive out of order due to network issues
  • Monitor failures: Set up alerts for webhook failures in your dashboard