Skip to main content

Webhooks Guide

Webhooks allow you to receive real-time notifications when events occur in the SBM CRM Platform. This guide explains how to set up and use webhooks.

What are Webhooks?

Webhooks are HTTP callbacks that notify your application when specific events occur. Instead of polling the API, you receive instant notifications.

Supported Events

Customer Events

  • customer.created - New customer registered
  • customer.updated - Customer profile updated
  • customer.tier_changed - Customer tier upgraded/downgraded
  • customer.deleted - Customer account deleted

Loyalty Events

  • points.earned - Customer earned points
  • points.redeemed - Customer redeemed points
  • points.expired - Points expired
  • reward.claimed - Reward claimed by customer
  • reward.redeemed - Reward redeemed at store

Campaign Events

  • campaign.created - New campaign created
  • campaign.started - Campaign started
  • campaign.ended - Campaign ended
  • campaign.participant.joined - Customer joined campaign
  • campaign.participant.completed - Customer completed campaign

Transaction Events

  • transaction.completed - Purchase transaction completed
  • transaction.refunded - Transaction refunded

Notification Events

  • notification.sent - Notification sent to customer
  • notification.delivered - Notification delivered
  • notification.failed - Notification delivery failed

Setting Up Webhooks

Step 1: Create Webhook Endpoint

Create an HTTP endpoint in your application that can receive POST requests:

// Express.js example
app.post('/webhooks/sbmcrm', async (req, res) => {
const event = req.body;

// Verify webhook signature
const isValid = verifyWebhookSignature(req);
if (!isValid) {
return res.status(401).send('Invalid signature');
}

// Process the event
await handleWebhookEvent(event);

// Respond quickly
res.status(200).send('OK');
});

Step 2: Register Webhook URL

Register your webhook endpoint in the Admin Web App:

  1. Navigate to SettingsWebhooks
  2. Click Create Webhook
  3. Enter your webhook URL
  4. Select events to subscribe to
  5. Save the webhook

Or use the API:

POST /api/v1/webhooks
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json

{
"url": "https://your-app.com/webhooks/sbmcrm",
"events": [
"customer.created",
"points.earned",
"campaign.participant.joined"
],
"secret": "your_webhook_secret"
}

Webhook Payload

Event Structure

{
"id": "evt_1234567890",
"type": "customer.created",
"created_at": "2024-01-15T10:30:00Z",
"data": {
"customer": {
"id": "cus_12345",
"name": "John Doe",
"email": "john@example.com",
"tier": "bronze"
}
}
}

Example: Customer Created

{
"id": "evt_abc123",
"type": "customer.created",
"created_at": "2024-01-15T10:30:00Z",
"data": {
"customer": {
"id": "cus_12345",
"name": "John Doe",
"email": "john@example.com",
"phone": "+1234567890",
"tier": "bronze",
"points_balance": 0,
"created_at": "2024-01-15T10:30:00Z"
}
}
}

Example: Points Earned

{
"id": "evt_def456",
"type": "points.earned",
"created_at": "2024-01-15T11:00:00Z",
"data": {
"customer_id": "cus_12345",
"points": 100,
"reason": "Purchase bonus",
"transaction_id": "txn_789",
"new_balance": 100
}
}

Example: Campaign Participant Joined

{
"id": "evt_ghi789",
"type": "campaign.participant.joined",
"created_at": "2024-01-15T12:00:00Z",
"data": {
"campaign_id": "camp_456",
"campaign_name": "Summer Sale 2024",
"customer_id": "cus_12345",
"joined_at": "2024-01-15T12:00:00Z"
}
}

Webhook Security

Signature Verification

All webhooks include a signature header for verification:

X-SBMCRM-Signature: t=1609459200,v1=abc123def456...

Verifying Signatures

const crypto = require('crypto');

function verifyWebhookSignature(req, secret) {
const signature = req.headers['x-sbmcrm-signature'];
if (!signature) return false;

const [timestamp, ...signatures] = signature.split(',');
const timestampValue = timestamp.split('=')[1];

// Check timestamp (prevent replay attacks)
const currentTime = Math.floor(Date.now() / 1000);
if (Math.abs(currentTime - parseInt(timestampValue)) > 300) {
return false; // More than 5 minutes old
}

// Verify signature
const payload = JSON.stringify(req.body);
const signedPayload = `${timestampValue}.${payload}`;
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(signedPayload)
.digest('hex');

return signatures.some(sig => {
const sigValue = sig.split('=')[1];
return crypto.timingSafeEqual(
Buffer.from(sigValue),
Buffer.from(expectedSignature)
);
});
}

Python Example

import hmac
import hashlib
import time

def verify_webhook_signature(request, secret):
signature = request.headers.get('X-SBMCRM-Signature')
if not signature:
return False

# Parse signature
parts = signature.split(',')
timestamp = None
signatures = []

for part in parts:
if part.startswith('t='):
timestamp = int(part.split('=')[1])
elif part.startswith('v1='):
signatures.append(part.split('=')[1])

# Check timestamp
current_time = int(time.time())
if abs(current_time - timestamp) > 300:
return False

# Verify signature
payload = request.get_data()
signed_payload = f"{timestamp}.{payload.decode()}"
expected_signature = hmac.new(
secret.encode(),
signed_payload.encode(),
hashlib.sha256
).hexdigest()

return any(
hmac.compare_digest(sig, expected_signature)
for sig in signatures
)

Handling Webhooks

Idempotency

Webhooks may be delivered multiple times. Always make your handlers idempotent:

async function handleCustomerCreated(event) {
const customerId = event.data.customer.id;

// Check if already processed
const existing = await db.findWebhookEvent(event.id);
if (existing) {
return; // Already processed
}

// Process event
await processCustomer(customerId);

// Mark as processed
await db.saveWebhookEvent(event.id, {
processed_at: new Date(),
event_type: event.type
});
}

Error Handling

Always respond quickly, even if processing fails:

app.post('/webhooks/sbmcrm', async (req, res) => {
try {
// Verify signature
if (!verifyWebhookSignature(req, webhookSecret)) {
return res.status(401).send('Invalid signature');
}

// Queue for async processing
await queueWebhookEvent(req.body);

// Respond immediately
res.status(200).send('OK');
} catch (error) {
console.error('Webhook error:', error);
res.status(500).send('Internal error');
}
});

Retry Logic

The platform will retry failed webhook deliveries:

  • Initial attempt: Immediate
  • Retry 1: After 1 minute
  • Retry 2: After 5 minutes
  • Retry 3: After 30 minutes
  • Retry 4: After 2 hours
  • Retry 5: After 24 hours

After 5 failed attempts, the webhook is marked as failed and requires manual intervention.

Testing Webhooks

Using Webhook Testing Tools

Use tools like ngrok or webhook.site for local testing:

# Start ngrok tunnel
ngrok http 3000

# Use the ngrok URL as your webhook URL
# https://abc123.ngrok.io/webhooks/sbmcrm

Manual Testing

Trigger test events from the Admin Web App:

  1. Navigate to SettingsWebhooks
  2. Select your webhook
  3. Click Send Test Event
  4. Choose an event type
  5. Verify the webhook is received

Webhook Management API

List Webhooks

GET /api/v1/webhooks
Authorization: Bearer YOUR_API_KEY

Get Webhook

GET /api/v1/webhooks/whk_12345
Authorization: Bearer YOUR_API_KEY

Update Webhook

PATCH /api/v1/webhooks/whk_12345
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json

{
"events": [
"customer.created",
"points.earned"
],
"active": true
}

Delete Webhook

DELETE /api/v1/webhooks/whk_12345
Authorization: Bearer YOUR_API_KEY

Best Practices

  1. Respond quickly - Return 200 OK within 5 seconds
  2. Verify signatures - Always verify webhook signatures
  3. Handle idempotency - Process events idempotently
  4. Queue for processing - Don't do heavy processing in the webhook handler
  5. Log everything - Log all webhook events for debugging
  6. Monitor failures - Set up alerts for webhook delivery failures
  7. Use HTTPS - Always use HTTPS for webhook endpoints
  8. Test thoroughly - Test webhook handling in staging before production

Troubleshooting

Webhook Not Received

  1. Check webhook URL is accessible (not behind firewall)
  2. Verify webhook is active in Admin Web App
  3. Check webhook logs for delivery attempts
  4. Verify SSL certificate is valid

Signature Verification Fails

  1. Ensure you're using the correct webhook secret
  2. Check timestamp is within 5 minutes
  3. Verify payload is not modified before verification
  4. Ensure you're using the raw request body

Duplicate Events

  1. Implement idempotency checks using event ID
  2. Check if webhook is registered multiple times
  3. Verify your handler is not triggering additional events