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.
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:
{
"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 createdv1: The signature (HMAC-SHA256 of timestamp + payload)
Verification Example (Node.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)
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
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.
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
idto 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