Webhook Overview
Real-time Events
Instant notifications for contacts, deals, properties, and communication events
Secure Delivery
HTTPS delivery with signature verification and retry mechanisms
Flexible Filtering
Subscribe to specific events and filter by conditions
Reliable Processing
Automatic retries with exponential backoff and dead letter queues
Supported Events
Contact Events
| Event | Description | Payload |
|---|---|---|
contact.created | New contact created | Contact object |
contact.updated | Contact information updated | Updated contact object |
contact.deleted | Contact deleted | Contact ID |
contact.tagged | Tags added to contact | Contact object with new tags |
contact.untagged | Tags removed from contact | Contact object with removed tags |
contact.assigned | Contact assigned to agent | Contact object with assignment |
Deal Events
| Event | Description | Payload |
|---|---|---|
deal.created | New deal created | Deal object |
deal.updated | Deal information updated | Updated deal object |
deal.deleted | Deal deleted | Deal ID |
deal.stage_changed | Deal moved to different stage | Deal object with stage change |
deal.won | Deal marked as won | Won deal object |
deal.lost | Deal marked as lost | Lost deal object |
deal.task_created | Task added to deal | Deal object with new task |
deal.task_completed | Task completed | Deal object with completed task |
Property Events
| Event | Description | Payload |
|---|---|---|
property.created | New property listing created | Property object |
property.updated | Property information updated | Updated property object |
property.deleted | Property listing deleted | Property ID |
property.status_changed | Listing status changed | Property object with status |
property.price_changed | List price modified | Property object with price change |
property.photos_uploaded | New photos added | Property object with photo URLs |
property.inquiry_received | Property inquiry submitted | Inquiry object |
Communication Events
| Event | Description | Payload |
|---|---|---|
communication.email_sent | Email sent to contact | Email object |
communication.email_received | Email received from contact | Email object |
communication.sms_sent | SMS sent to contact | SMS object |
communication.sms_received | SMS received from contact | SMS object |
communication.call_started | Phone call initiated | Call object |
communication.call_ended | Phone call completed | Call object with duration |
communication.voicemail_received | Voicemail left by contact | Voicemail object |
System Events
| Event | Description | Payload |
|---|---|---|
organization.updated | Organization settings changed | Organization object |
user.created | New user added to organization | User object |
user.updated | User information updated | Updated user object |
integration.connected | External integration connected | Integration object |
integration.disconnected | External integration disconnected | Integration object |
Creating Webhooks
Using the API
Create webhooks programmatically using the Winnerr API:const webhook = await client.webhooks.create({
url: 'https://your-app.com/webhooks/winnerr',
events: [
'contact.created',
'contact.updated',
'deal.stage_changed',
'property.status_changed'
],
secret: 'your-webhook-secret-key',
enabled: true,
description: 'Lead management webhook'
});
console.log('Webhook created:', webhook.id);
webhook = client.webhooks.create({
'url': 'https://your-app.com/webhooks/winnerr',
'events': [
'contact.created',
'contact.updated',
'deal.stage_changed',
'property.status_changed'
],
'secret': 'your-webhook-secret-key',
'enabled': True,
'description': 'Lead management webhook'
})
print('Webhook created:', webhook.id)
curl -X POST https://api.winnerr.com/v1/webhooks \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-app.com/webhooks/winnerr",
"events": [
"contact.created",
"contact.updated",
"deal.stage_changed",
"property.status_changed"
],
"secret": "your-webhook-secret-key",
"enabled": true,
"description": "Lead management webhook"
}'
Using the Dashboard
You can also create and manage webhooks through the Winnerr dashboard:- Navigate to Settings > Integrations > Webhooks
- Click “Create New Webhook”
- Enter your endpoint URL
- Select the events you want to subscribe to
- Configure filtering options (optional)
- Set up authentication and security settings
- Test the webhook and save
Webhook Configuration
Endpoint Requirements
Your webhook endpoint must meet these requirements:- HTTPS Only: Webhooks are only delivered to HTTPS endpoints
- Response Time: Respond within 30 seconds
- Status Code: Return 2xx status code for successful processing
- Content-Type: Accept
application/jsoncontent type
Event Filtering
Filter webhooks based on specific conditions:// Example: Only receive contact events for high-value leads
const webhook = await client.webhooks.create({
url: 'https://your-app.com/webhooks/high-value-leads',
events: ['contact.created', 'contact.updated'],
filters: {
'contact.leadScore': { gte: 80 },
'contact.source': { in: ['REFERRAL', 'WEBSITE'] }
},
secret: 'your-secret-key'
});
Webhook Headers
Each webhook request includes these headers:POST /webhooks/winnerr HTTP/1.1
Host: your-app.com
Content-Type: application/json
Content-Length: 1234
X-Winnerr-Event: contact.created
X-Winnerr-Signature: sha256=abc123...
X-Winnerr-Delivery: 12345678-1234-1234-1234-123456789abc
X-Winnerr-Timestamp: 1640995200
User-Agent: Winnerr-Webhooks/1.0
Webhook Payload Format
Standard Payload Structure
All webhook payloads follow this consistent structure:{
"id": "evt_123456789",
"event": "contact.created",
"apiVersion": "2024-01",
"createdAt": "2024-01-15T10:30:00Z",
"data": {
"object": {
"id": "contact_123",
"firstName": "John",
"lastName": "Doe",
"email": "john.doe@example.com",
"phone": "+1-555-123-4567",
"type": "LEAD",
"status": "ACTIVE",
"leadScore": 85,
"source": "WEBSITE",
"createdAt": "2024-01-15T10:30:00Z",
"updatedAt": "2024-01-15T10:30:00Z"
},
"previousAttributes": {},
"changes": ["firstName", "lastName", "email"]
},
"organization": {
"id": "org_123",
"name": "Premier Realty Group"
},
"user": {
"id": "user_456",
"name": "Sarah Johnson",
"email": "sarah@premierrealty.com"
}
}
Event-Specific Payloads
Contact Created Event
{
"id": "evt_contact_created_123",
"event": "contact.created",
"apiVersion": "2024-01",
"createdAt": "2024-01-15T10:30:00Z",
"data": {
"object": {
"id": "contact_123",
"firstName": "John",
"lastName": "Doe",
"email": "john.doe@example.com",
"phone": "+1-555-123-4567",
"type": "LEAD",
"status": "ACTIVE",
"leadScore": 85,
"source": "WEBSITE",
"tags": ["website-lead", "first-time-buyer"],
"preferences": {
"propertyTypes": ["CONDO", "TOWNHOUSE"],
"priceRange": { "min": 300000, "max": 600000 }
},
"createdAt": "2024-01-15T10:30:00Z"
},
"previousAttributes": null,
"changes": null
}
}
Deal Stage Changed Event
{
"id": "evt_deal_stage_123",
"event": "deal.stage_changed",
"apiVersion": "2024-01",
"createdAt": "2024-01-15T14:20:00Z",
"data": {
"object": {
"id": "deal_456",
"title": "Miami Waterfront Condo Sale",
"stage": "CONTRACT",
"previousStage": "NEGOTIATION",
"amount": 750000,
"probability": 90,
"contactId": "contact_123",
"assignedTo": "agent_789",
"updatedAt": "2024-01-15T14:20:00Z"
},
"previousAttributes": {
"stage": "NEGOTIATION",
"probability": 75
},
"changes": ["stage", "probability"]
}
}
Property Price Changed Event
{
"id": "evt_property_price_123",
"event": "property.price_changed",
"apiVersion": "2024-01",
"createdAt": "2024-01-15T16:45:00Z",
"data": {
"object": {
"id": "property_789",
"mlsId": "MLS12345678",
"address": {
"street": "123 Ocean Drive",
"city": "Miami Beach",
"state": "FL",
"zipCode": "33139"
},
"listing": {
"listPrice": 725000,
"originalPrice": 795000,
"priceReduction": 70000,
"status": "ACTIVE"
}
},
"previousAttributes": {
"listing": {
"listPrice": 750000
}
},
"changes": ["listing.listPrice"],
"priceChange": {
"previousPrice": 750000,
"newPrice": 725000,
"changeAmount": -25000,
"changePercentage": -0.033
}
}
}
Implementing Webhook Handlers
Basic Webhook Handler
Implement a basic webhook handler to process events:const express = require('express');
const crypto = require('crypto');
const app = express();
// Middleware to capture raw body for signature verification
app.use('/webhooks', express.raw({ type: 'application/json' }));
app.post('/webhooks/winnerr', (req, res) => {
const signature = req.headers['x-winnerr-signature'];
const timestamp = req.headers['x-winnerr-timestamp'];
const event = req.headers['x-winnerr-event'];
const payload = req.body;
try {
// Verify webhook signature
if (!verifySignature(payload, signature, timestamp)) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Parse event data
const eventData = JSON.parse(payload);
// Process event based on type
processWebhookEvent(eventData);
// Acknowledge receipt
res.status(200).json({ received: true });
} catch (error) {
console.error('Webhook processing error:', error);
res.status(500).json({ error: 'Processing failed' });
}
});
function verifySignature(payload, signature, timestamp) {
// Prevent replay attacks (reject requests older than 5 minutes)
const now = Math.floor(Date.now() / 1000);
if (Math.abs(now - timestamp) > 300) {
return false;
}
// Verify signature
const expectedSignature = crypto
.createHmac('sha256', process.env.WEBHOOK_SECRET)
.update(timestamp + '.' + payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature.replace('sha256=', '')),
Buffer.from(expectedSignature)
);
}
async function processWebhookEvent(event) {
switch (event.event) {
case 'contact.created':
await handleContactCreated(event.data.object);
break;
case 'deal.stage_changed':
await handleDealStageChanged(event.data.object);
break;
case 'property.price_changed':
await handlePriceChanged(event.data.object);
break;
default:
console.log('Unhandled event type:', event.event);
}
}
from flask import Flask, request, abort
import hmac
import hashlib
import json
import time
app = Flask(__name__)
@app.route('/webhooks/winnerr', methods=['POST'])
def handle_webhook():
signature = request.headers.get('X-Winnerr-Signature', '')
timestamp = request.headers.get('X-Winnerr-Timestamp', '')
event_type = request.headers.get('X-Winnerr-Event', '')
payload = request.get_data()
try:
# Verify webhook signature
if not verify_signature(payload, signature, timestamp):
abort(401)
# Parse event data
event_data = json.loads(payload)
# Process event based on type
process_webhook_event(event_data)
return {'received': True}, 200
except Exception as error:
print(f'Webhook processing error: {error}')
abort(500)
def verify_signature(payload, signature, timestamp):
# Prevent replay attacks
now = int(time.time())
if abs(now - int(timestamp)) > 300:
return False
# Verify signature
expected_signature = hmac.new(
WEBHOOK_SECRET.encode(),
f'{timestamp}.{payload.decode()}'.encode(),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(
signature.replace('sha256=', ''),
expected_signature
)
def process_webhook_event(event):
event_type = event['event']
if event_type == 'contact.created':
handle_contact_created(event['data']['object'])
elif event_type == 'deal.stage_changed':
handle_deal_stage_changed(event['data']['object'])
elif event_type == 'property.price_changed':
handle_price_changed(event['data']['object'])
else:
print(f'Unhandled event type: {event_type}')
<?php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
class WebhookController extends Controller
{
public function handleWinnerrWebhook(Request $request)
{
$signature = $request->header('X-Winnerr-Signature');
$timestamp = $request->header('X-Winnerr-Timestamp');
$eventType = $request->header('X-Winnerr-Event');
$payload = $request->getContent();
try {
// Verify webhook signature
if (!$this->verifySignature($payload, $signature, $timestamp)) {
return response()->json(['error' => 'Invalid signature'], 401);
}
// Parse event data
$eventData = json_decode($payload, true);
// Process event based on type
$this->processWebhookEvent($eventData);
return response()->json(['received' => true]);
} catch (Exception $e) {
Log::error('Webhook processing error: ' . $e->getMessage());
return response()->json(['error' => 'Processing failed'], 500);
}
}
private function verifySignature($payload, $signature, $timestamp)
{
// Prevent replay attacks
$now = time();
if (abs($now - intval($timestamp)) > 300) {
return false;
}
// Verify signature
$expectedSignature = hash_hmac('sha256', $timestamp . '.' . $payload, config('webhook.secret'));
return hash_equals(
str_replace('sha256=', '', $signature),
$expectedSignature
);
}
private function processWebhookEvent($event)
{
switch ($event['event']) {
case 'contact.created':
$this->handleContactCreated($event['data']['object']);
break;
case 'deal.stage_changed':
$this->handleDealStageChanged($event['data']['object']);
break;
case 'property.price_changed':
$this->handlePriceChanged($event['data']['object']);
break;
default:
Log::info('Unhandled event type: ' . $event['event']);
}
}
}
Event-Specific Handlers
Implement specific handlers for different event types:// Contact created handler
async function handleContactCreated(contact) {
console.log('New contact created:', contact.id);
// Add to external CRM
await addToExternalCRM(contact);
// Send welcome email
if (contact.email) {
await sendWelcomeEmail(contact);
}
// Create follow-up task
await createFollowUpTask(contact);
// Update lead scoring
await updateLeadScore(contact);
}
// Deal stage changed handler
async function handleDealStageChanged(deal) {
console.log(`Deal ${deal.id} moved to ${deal.stage}`);
// Update external systems
await updateExternalDeal(deal);
// Send notifications
await notifyTeamMembers(deal);
// Trigger stage-specific workflows
switch (deal.stage) {
case 'CONTRACT':
await initiateContractWorkflow(deal);
break;
case 'CLOSING':
await prepareClosingDocuments(deal);
break;
case 'WON':
await processClosedDeal(deal);
break;
}
}
// Property price changed handler
async function handlePriceChanged(property) {
console.log(`Property ${property.id} price changed to $${property.listing.listPrice}`);
// Update property listings on external sites
await updateExternalListings(property);
// Notify interested clients
await notifyInterestedClients(property);
// Update market analytics
await updateMarketData(property);
// Send price alert emails
if (property.listing.listPrice < property.listing.originalPrice) {
await sendPriceReductionAlerts(property);
}
}
# Contact created handler
async def handle_contact_created(contact):
print(f'New contact created: {contact["id"]}')
# Add to external CRM
await add_to_external_crm(contact)
# Send welcome email
if contact.get('email'):
await send_welcome_email(contact)
# Create follow-up task
await create_follow_up_task(contact)
# Update lead scoring
await update_lead_score(contact)
# Deal stage changed handler
async def handle_deal_stage_changed(deal):
print(f'Deal {deal["id"]} moved to {deal["stage"]}')
# Update external systems
await update_external_deal(deal)
# Send notifications
await notify_team_members(deal)
# Trigger stage-specific workflows
if deal['stage'] == 'CONTRACT':
await initiate_contract_workflow(deal)
elif deal['stage'] == 'CLOSING':
await prepare_closing_documents(deal)
elif deal['stage'] == 'WON':
await process_closed_deal(deal)
Advanced Webhook Features
Idempotency Handling
Handle duplicate webhook deliveries gracefully:// Idempotency tracking
const processedEvents = new Set();
async function processWebhookEvent(event) {
const eventId = event.id;
// Check if event already processed
if (processedEvents.has(eventId)) {
console.log('Event already processed:', eventId);
return;
}
try {
// Process event
await handleEvent(event);
// Mark as processed
processedEvents.add(eventId);
// Clean up old events (keep last 1000)
if (processedEvents.size > 1000) {
const eventsArray = Array.from(processedEvents);
processedEvents.clear();
eventsArray.slice(-500).forEach(id => processedEvents.add(id));
}
} catch (error) {
console.error('Event processing failed:', error);
throw error;
}
}
Webhook Retry Logic
Implement robust retry handling:// Webhook retry configuration
const RETRY_CONFIG = {
maxRetries: 5,
baseDelay: 1000,
maxDelay: 30000,
backoffMultiplier: 2
};
async function processWithRetry(operation, eventId) {
let attempt = 0;
while (attempt < RETRY_CONFIG.maxRetries) {
try {
await operation();
return;
} catch (error) {
attempt++;
if (attempt >= RETRY_CONFIG.maxRetries) {
// Send to dead letter queue
await sendToDeadLetterQueue(eventId, error);
throw error;
}
// Calculate retry delay with exponential backoff
const delay = Math.min(
RETRY_CONFIG.baseDelay * Math.pow(RETRY_CONFIG.backoffMultiplier, attempt - 1),
RETRY_CONFIG.maxDelay
);
console.log(`Attempt ${attempt} failed, retrying in ${delay}ms`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
Webhook Filtering
Filter events on the server side:// Server-side event filtering
function shouldProcessEvent(event, filters) {
if (!filters) return true;
for (const [field, condition] of Object.entries(filters)) {
const value = getNestedValue(event.data.object, field);
if (!matchesCondition(value, condition)) {
return false;
}
}
return true;
}
function matchesCondition(value, condition) {
if (typeof condition === 'object') {
for (const [operator, operand] of Object.entries(condition)) {
switch (operator) {
case 'eq':
return value === operand;
case 'ne':
return value !== operand;
case 'gt':
return value > operand;
case 'gte':
return value >= operand;
case 'lt':
return value < operand;
case 'lte':
return value <= operand;
case 'in':
return Array.isArray(operand) && operand.includes(value);
case 'nin':
return Array.isArray(operand) && !operand.includes(value);
default:
return false;
}
}
}
return value === condition;
}
// Example usage
const filters = {
'leadScore': { gte: 80 },
'source': { in: ['WEBSITE', 'REFERRAL'] },
'type': { eq: 'LEAD' }
};
if (shouldProcessEvent(event, filters)) {
await processEvent(event);
}
Webhook Management
List Webhooks
Retrieve all configured webhooks:const webhooks = await client.webhooks.list();
webhooks.data.forEach(webhook => {
console.log(`Webhook ${webhook.id}:`);
console.log(` URL: ${webhook.url}`);
console.log(` Events: ${webhook.events.join(', ')}`);
console.log(` Status: ${webhook.enabled ? 'Enabled' : 'Disabled'}`);
console.log(` Created: ${webhook.createdAt}`);
});
webhooks = client.webhooks.list()
for webhook in webhooks.data:
print(f'Webhook {webhook.id}:')
print(f' URL: {webhook.url}')
print(f' Events: {", ".join(webhook.events)}')
print(f' Status: {"Enabled" if webhook.enabled else "Disabled"}')
print(f' Created: {webhook.created_at}')
Update Webhook
Modify webhook configuration:const updatedWebhook = await client.webhooks.update('webhook_123', {
events: [
'contact.created',
'contact.updated',
'deal.created',
'deal.stage_changed',
'property.created'
],
enabled: true,
description: 'Updated webhook for comprehensive event handling'
});
updated_webhook = client.webhooks.update('webhook_123', {
'events': [
'contact.created',
'contact.updated',
'deal.created',
'deal.stage_changed',
'property.created'
],
'enabled': True,
'description': 'Updated webhook for comprehensive event handling'
})
Delete Webhook
Remove a webhook configuration:await client.webhooks.delete('webhook_123');
console.log('Webhook deleted successfully');
client.webhooks.delete('webhook_123')
print('Webhook deleted successfully')
Testing Webhooks
Webhook Testing Tool
Use the built-in testing tool to verify your webhook endpoint:// Test webhook endpoint
const testResult = await client.webhooks.test('webhook_123', {
event: 'contact.created',
data: {
id: 'contact_test_123',
firstName: 'Test',
lastName: 'Contact',
email: 'test@example.com'
}
});
console.log('Test result:', testResult);
Local Development
Use tools like ngrok for local webhook testing:# Install ngrok
npm install -g ngrok
# Expose local server
ngrok http 3000
# Use the generated URL for webhook endpoint
# https://abc123.ngrok.io/webhooks/winnerr
Webhook Simulator
Create a webhook simulator for testing:// Webhook simulator for testing
class WebhookSimulator {
constructor(endpoint, secret) {
this.endpoint = endpoint;
this.secret = secret;
}
async simulateEvent(eventType, data) {
const event = {
id: `evt_${Date.now()}`,
event: eventType,
apiVersion: '2024-01',
createdAt: new Date().toISOString(),
data: { object: data },
organization: { id: 'org_test', name: 'Test Organization' }
};
const payload = JSON.stringify(event);
const timestamp = Math.floor(Date.now() / 1000);
const signature = this.generateSignature(payload, timestamp);
const response = await fetch(this.endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Winnerr-Event': eventType,
'X-Winnerr-Signature': `sha256=${signature}`,
'X-Winnerr-Timestamp': timestamp.toString(),
'X-Winnerr-Delivery': `delivery_${Date.now()}`
},
body: payload
});
return {
status: response.status,
response: await response.text()
};
}
generateSignature(payload, timestamp) {
const crypto = require('crypto');
return crypto
.createHmac('sha256', this.secret)
.update(`${timestamp}.${payload}`)
.digest('hex');
}
}
// Usage
const simulator = new WebhookSimulator(
'https://your-app.com/webhooks/winnerr',
'your-webhook-secret'
);
const result = await simulator.simulateEvent('contact.created', {
id: 'contact_test_123',
firstName: 'Test',
lastName: 'Contact',
email: 'test@example.com'
});
console.log('Simulation result:', result);
Monitoring & Debugging
Webhook Logs
Monitor webhook delivery attempts:// Get webhook delivery logs
const logs = await client.webhooks.getLogs('webhook_123', {
startDate: '2024-01-01T00:00:00Z',
endDate: '2024-01-31T23:59:59Z',
status: 'failed' // or 'success', 'pending'
});
logs.data.forEach(log => {
console.log(`Delivery ${log.id}:`);
console.log(` Event: ${log.event}`);
console.log(` Status: ${log.status}`);
console.log(` Response: ${log.responseCode}`);
console.log(` Attempts: ${log.attempts}`);
console.log(` Next Retry: ${log.nextRetryAt}`);
});
Error Handling
Implement comprehensive error logging:// Webhook error logging
class WebhookLogger {
constructor() {
this.errors = [];
}
logError(event, error) {
const logEntry = {
timestamp: new Date().toISOString(),
eventId: event.id,
eventType: event.event,
error: {
message: error.message,
stack: error.stack,
code: error.code
},
data: event.data
};
this.errors.push(logEntry);
// Send to monitoring service
this.sendToMonitoring(logEntry);
}
async sendToMonitoring(logEntry) {
// Send to your monitoring service (e.g., Sentry, DataDog)
try {
await fetch('https://monitoring-service.com/webhook-errors', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(logEntry)
});
} catch (error) {
console.error('Failed to send error to monitoring service:', error);
}
}
getRecentErrors(hours = 24) {
const cutoff = new Date(Date.now() - hours * 60 * 60 * 1000);
return this.errors.filter(error =>
new Date(error.timestamp) > cutoff
);
}
}
const logger = new WebhookLogger();
// Use in webhook handler
try {
await processWebhookEvent(event);
} catch (error) {
logger.logError(event, error);
throw error;
}
Best Practices
1. Security
Implement proper security measures:// Security best practices
const securityMiddleware = (req, res, next) => {
// Rate limiting
if (isRateLimited(req.ip)) {
return res.status(429).json({ error: 'Rate limit exceeded' });
}
// IP whitelist (optional)
const allowedIPs = process.env.WEBHOOK_ALLOWED_IPS?.split(',') || [];
if (allowedIPs.length > 0 && !allowedIPs.includes(req.ip)) {
return res.status(403).json({ error: 'IP not allowed' });
}
// Signature verification (handled in main webhook handler)
next();
};
2. Performance
Optimize webhook processing:// Asynchronous processing
const queue = require('bull');
const webhookQueue = new queue('webhook processing');
app.post('/webhooks/winnerr', async (req, res) => {
// Verify signature and respond quickly
if (!verifySignature(req)) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Add to processing queue
await webhookQueue.add('process-webhook', {
event: JSON.parse(req.body),
headers: req.headers
});
// Respond immediately
res.status(200).json({ received: true });
});
// Process webhooks asynchronously
webhookQueue.process('process-webhook', async (job) => {
const { event, headers } = job.data;
await processWebhookEvent(event);
});
3. Reliability
Ensure reliable webhook processing:// Reliability patterns
const reliableWebhookProcessor = {
// Circuit breaker pattern
circuitBreaker: {
failures: 0,
threshold: 5,
timeout: 60000,
state: 'closed' // closed, open, half-open
},
async processWithCircuitBreaker(operation) {
if (this.circuitBreaker.state === 'open') {
if (Date.now() - this.circuitBreaker.lastFailure > this.circuitBreaker.timeout) {
this.circuitBreaker.state = 'half-open';
} else {
throw new Error('Circuit breaker is open');
}
}
try {
const result = await operation();
if (this.circuitBreaker.state === 'half-open') {
this.circuitBreaker.state = 'closed';
this.circuitBreaker.failures = 0;
}
return result;
} catch (error) {
this.circuitBreaker.failures++;
if (this.circuitBreaker.failures >= this.circuitBreaker.threshold) {
this.circuitBreaker.state = 'open';
this.circuitBreaker.lastFailure = Date.now();
}
throw error;
}
}
};
Webhooks are essential for building responsive real estate applications. Always implement proper security, error handling, and monitoring to ensure reliable webhook processing. Test thoroughly in the sandbox environment before deploying to production.