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 Settings → Webhooks
  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 Settings → Webhooks
  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