API & Integrations min read Advanced

Webhook Configuration & Events

Receive real-time notifications when important events happen in your account

By george.olah@code24.ro Sep 29, 2025 4 views

Webhook Configuration & Events

Turn Selgora events into real-time actions in your apps. Webhooks let you build powerful automations that respond instantly to what happens in your account.

Understanding Webhooks

What Are Webhooks?

Think of webhooks as automated phone calls:

  • Something happens in Selgora (event)
  • Selgora calls your URL (webhook)
  • Sends event details (payload)
  • Your app takes action (response)

Webhooks vs. API Polling

API Polling (Inefficient):

Your App: "Anything new?"
Selgora: "No"
Your App: "Anything new?"
Selgora: "No"
Your App: "Anything new?"
Selgora: "Yes! Order #123"

Webhooks (Efficient):

Selgora: "Order #123 just happened!"
Your App: "Thanks, processing..."

Setting Up Webhooks

Step 1: Create Endpoint

Your webhook endpoint requirements:

  • Publicly accessible HTTPS URL
  • Accepts POST requests
  • Returns 200 status quickly (<5 seconds)
  • Handles JSON payload

Example Endpoint:

// Express.js webhook handler
app.post('/webhooks/selgora', (req, res) => {
  const event = req.body;

  // Acknowledge receipt immediately
  res.status(200).send('OK');

  // Process event asynchronously
  processWebhookEvent(event);
});

Step 2: Configure in Selgora

Dashboard > Settings > Webhooks
Click: Add Webhook Endpoint

URL: https://yourapp.com/webhooks/selgora
Events: Select events to receive
Secret: Generate signing secret
Status: Active

Step 3: Test Your Webhook

Dashboard > Settings > Webhooks > Your Webhook
Click: Send Test Event
Select: Event type
Click: Send
Check: Your server logs

Available Events

Order Events

order.created

{
  "event": "order.created",
  "created_at": "2024-11-15T10:00:00Z",
  "data": {
    "order_id": "ord_123",
    "customer_email": "customer@example.com",
    "amount": 19700,
    "currency": "USD",
    "status": "pending"
  }
}

order.completed

{
  "event": "order.completed",
  "data": {
    "order_id": "ord_123",
    "customer_email": "customer@example.com",
    "products": [
      {
        "id": "prod_456",
        "name": "Complete Course",
        "price": 19700
      }
    ],
    "payment_method": "card"
  }
}

order.refunded

{
  "event": "order.refunded",
  "data": {
    "order_id": "ord_123",
    "refund_amount": 19700,
    "reason": "requested_by_customer"
  }
}

Contact Events

contact.created

{
  "event": "contact.created",
  "data": {
    "contact_id": "con_456",
    "email": "new@example.com",
    "source": "landing_page",
    "tags": ["lead"],
    "custom_fields": {}
  }
}

contact.updated

{
  "event": "contact.updated",
  "data": {
    "contact_id": "con_456",
    "changes": {
      "tags": {
        "added": ["customer"],
        "removed": ["lead"]
      },
      "custom_fields": {
        "company": "New Company"
      }
    }
  }
}

contact.deleted

{
  "event": "contact.deleted",
  "data": {
    "contact_id": "con_456",
    "email": "deleted@example.com",
    "deleted_at": "2024-11-15T10:00:00Z"
  }
}

Subscription Events

subscription.created

{
  "event": "subscription.created",
  "data": {
    "subscription_id": "sub_789",
    "customer_email": "customer@example.com",
    "plan": "monthly",
    "amount": 9700,
    "next_billing_date": "2024-12-15"
  }
}

subscription.updated

{
  "event": "subscription.updated",
  "data": {
    "subscription_id": "sub_789",
    "changes": {
      "plan": {
        "from": "monthly",
        "to": "annual"
      }
    }
  }
}

subscription.cancelled

{
  "event": "subscription.cancelled",
  "data": {
    "subscription_id": "sub_789",
    "cancelled_at": "2024-11-15T10:00:00Z",
    "reason": "voluntary",
    "feedback": "Too expensive"
  }
}

Course Events

course.enrolled

{
  "event": "course.enrolled",
  "data": {
    "contact_id": "con_456",
    "course_id": "course_123",
    "enrolled_at": "2024-11-15T10:00:00Z"
  }
}

course.completed

{
  "event": "course.completed",
  "data": {
    "contact_id": "con_456",
    "course_id": "course_123",
    "completed_at": "2024-11-15T10:00:00Z",
    "completion_rate": 100,
    "time_spent_minutes": 480
  }
}

lesson.completed

{
  "event": "lesson.completed",
  "data": {
    "contact_id": "con_456",
    "lesson_id": "lesson_789",
    "course_id": "course_123",
    "quiz_score": 95
  }
}

Email Events

email.sent

{
  "event": "email.sent",
  "data": {
    "email_id": "em_123",
    "to": "customer@example.com",
    "subject": "Welcome to Selgora",
    "campaign": "welcome_series"
  }
}

email.opened

{
  "event": "email.opened",
  "data": {
    "email_id": "em_123",
    "opened_at": "2024-11-15T10:30:00Z",
    "ip_address": "192.168.1.1",
    "user_agent": "Mozilla/5.0..."
  }
}

email.clicked

{
  "event": "email.clicked",
  "data": {
    "email_id": "em_123",
    "link": "https://selgora.com/offer",
    "clicked_at": "2024-11-15T10:35:00Z"
  }
}

Webhook Security

Signature Verification

Every webhook includes a signature header:

X-Selgora-Signature: sha256=3b4c5d6e7f8a9b0c1d2e3f4g5h6i7j8k

Verify in Node.js:

const crypto = require('crypto');

function verifyWebhookSignature(payload, signature, secret) {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(payload, 'utf8')
    .digest('hex');

  return `sha256=${expectedSignature}` === signature;
}

app.post('/webhook', (req, res) => {
  const signature = req.headers['x-selgora-signature'];
  const isValid = verifyWebhookSignature(
    JSON.stringify(req.body),
    signature,
    process.env.WEBHOOK_SECRET
  );

  if (!isValid) {
    return res.status(401).send('Invalid signature');
  }

  // Process webhook...
});

Verify in Python:

import hmac
import hashlib

def verify_webhook_signature(payload, signature, secret):
    expected = hmac.new(
        secret.encode(),
        payload.encode(),
        hashlib.sha256
    ).hexdigest()

    return f"sha256={expected}" == signature

@app.route('/webhook', methods=['POST'])
def handle_webhook():
    signature = request.headers.get('X-Selgora-Signature')

    if not verify_webhook_signature(
        request.data,
        signature,
        WEBHOOK_SECRET
    ):
        return 'Invalid signature', 401

    # Process webhook...

Handling Webhooks

Best Practices

1. Acknowledge Quickly:

app.post('/webhook', async (req, res) => {
  // Acknowledge immediately
  res.status(200).send('OK');

  // Process asynchronously
  await queue.add('process-webhook', req.body);
});

2. Idempotency:

// Store processed event IDs
const processedEvents = new Set();

async function processWebhook(event) {
  if (processedEvents.has(event.id)) {
    return; // Already processed
  }

  // Process event...
  processedEvents.add(event.id);
}

3. Error Handling:

async function processWebhook(event) {
  try {
    switch(event.event) {
      case 'order.completed':
        await handleOrderCompleted(event.data);
        break;
      case 'subscription.cancelled':
        await handleSubscriptionCancelled(event.data);
        break;
      default:
        console.log(`Unhandled event: ${event.event}`);
    }
  } catch (error) {
    console.error('Webhook processing error:', error);
    // Log to error tracking service
    await errorTracker.report(error, { event });
  }
}

Retry Logic

Selgora retries failed webhooks:

  • First retry: 5 minutes
  • Second retry: 30 minutes
  • Third retry: 2 hours
  • Fourth retry: 8 hours
  • Final retry: 24 hours

Your endpoint should:

  • Return 200 for success
  • Return 4xx for client errors (no retry)
  • Return 5xx for server errors (will retry)

Common Use Cases

Sync with CRM

// Sync new customers to CRM
async function handleOrderCompleted(data) {
  const { customer_email, products } = data;

  await crm.createOrUpdate({
    email: customer_email,
    customFields: {
      selgora_customer: true,
      products_purchased: products.map(p => p.name),
      lifetime_value: await calculateLTV(customer_email)
    }
  });
}

Slack Notifications

// Notify team of new sales
async function handleOrderCompleted(data) {
  await slack.postMessage({
    channel: '#sales',
    text: `🎉 New sale! ${data.customer_email} purchased ${data.products[0].name} for $${data.amount/100}`
  });
}

Email Automation

// Trigger external email sequences
async function handleCourseCompleted(data) {
  await emailProvider.addToSequence({
    email: data.contact_email,
    sequence: 'course_graduate',
    customFields: {
      course_name: data.course_name,
      completion_date: data.completed_at
    }
  });
}

Analytics Tracking

// Track events in analytics
async function handleWebhookEvent(event) {
  await analytics.track({
    userId: event.data.contact_id,
    event: event.event,
    properties: event.data,
    timestamp: event.created_at
  });
}

Testing Webhooks

Local Development

Use ngrok for local testing:

# Install ngrok
npm install -g ngrok

# Start local server
node server.js # Runs on port 3000

# Expose to internet
ngrok http 3000

# Use ngrok URL in Selgora
https://abc123.ngrok.io/webhooks

Test Payload

# Test with curl
curl -X POST https://yourapp.com/webhooks \
  -H "Content-Type: application/json" \
  -H "X-Selgora-Signature: sha256=test" \
  -d '{
    "event": "order.completed",
    "data": {
      "order_id": "test_123",
      "amount": 10000
    }
  }'

Webhook Logs

View webhook activity:

Dashboard > Settings > Webhooks > Your Webhook > Logs

Shows:
- Event type
- Timestamp
- Response code
- Response time
- Retry attempts

Troubleshooting

Common Issues

Webhook not receiving events:

  • Verify URL is correct
  • Check endpoint is publicly accessible
  • Ensure HTTPS (not HTTP)
  • Verify events are selected

Signature verification failing:

  • Check secret key matches
  • Verify payload is raw (not parsed)
  • Ensure proper encoding (UTF-8)

Duplicate events:

  • Implement idempotency
  • Store processed event IDs
  • Check for retry header

Timeouts:

  • Return 200 immediately
  • Process asynchronously
  • Optimize processing logic

Your Webhook Checklist

Setup

  • [ ] Create webhook endpoint
  • [ ] Implement signature verification
  • [ ] Add error handling
  • [ ] Setup async processing
  • [ ] Configure in Selgora

Testing

  • [ ] Test with ngrok locally
  • [ ] Verify signature validation
  • [ ] Test each event type
  • [ ] Check retry behavior
  • [ ] Monitor logs

Production

  • [ ] Use HTTPS only
  • [ ] Implement idempotency
  • [ ] Add monitoring/alerting
  • [ ] Log all events
  • [ ] Handle scaling

Remember: Webhooks are powerful but require proper handling. Always verify signatures, process asynchronously, and handle errors gracefully. Start with a few events and expand as needed!

Was this article helpful?

Your feedback helps us improve our content

Table of Contents

Need Help?

Can't find what you're looking for? Our support team is ready to assist you.

Contact Support