Skip to main content

Outgoing Webhooks

Send real-time event notifications from Expedify to external systems when specific events occur in your CRM, workflows, or campaigns.

What are Outgoing Webhooks?​

Outgoing webhooks allow Expedify to push data to your external systems automatically when events happen, such as:

  • New contact created
  • Deal stage changed
  • Workflow execution completed
  • Campaign email opened
  • Task completed

Use Cases:

  • Sync data to external CRM or database
  • Trigger actions in other tools (Zapier, Make, n8n)
  • Send notifications to Slack or Teams
  • Update inventory systems
  • Log events to analytics platforms

Creating an Outgoing Webhook​

Step 1: Access Webhook Settings​

  1. Go to Settings → Webhooks
  2. Click Outgoing Webhooks tab
  3. Click Create Webhook button

Step 2: Configure Webhook​

Basic Information:

  • Name: Descriptive name (e.g., "Slack New Contact Notification")
  • URL: Endpoint URL where events will be sent
  • Status: Active/Inactive

Event Selection:

  • Choose which events trigger this webhook
  • Multiple events can be selected
  • Events organized by category

Authentication (Optional):

  • API Key: Add authentication header
  • Secret: Verify webhook signature
  • Custom Headers: Additional headers

Retry Settings:

  • Max Retries: Number of retry attempts (default: 3)
  • Retry Delay: Time between retries (default: 5 seconds)
  • Timeout: Request timeout (default: 30 seconds)

Step 3: Save and Test​

  1. Click Save Webhook
  2. Click Test button to send test payload
  3. Verify endpoint receives test event
  4. Check response status
  5. Activate webhook

Available Events​

CRM Events​

Contacts

  • contact.created - New contact added
  • contact.updated - Contact information changed
  • contact.deleted - Contact removed
  • contact.tagged - Tag added to contact
  • contact.assigned - Contact assigned to user

Companies

  • company.created - New company added
  • company.updated - Company information changed
  • company.deleted - Company removed
  • company.assigned - Company assigned to user

Deals

  • deal.created - New deal created
  • deal.updated - Deal information changed
  • deal.stage_changed - Deal moved to different stage
  • deal.won - Deal marked as won
  • deal.lost - Deal marked as lost
  • deal.deleted - Deal removed

Tasks

  • task.created - New task created
  • task.updated - Task information changed
  • task.completed - Task marked as complete
  • task.assigned - Task assigned to user
  • task.overdue - Task past due date

Workflow Events​

  • workflow.started - Workflow execution began
  • workflow.completed - Workflow execution finished
  • workflow.failed - Workflow execution errored
  • workflow.paused - Workflow execution paused
  • workflow.cancelled - Workflow execution cancelled

Campaign Events​

Email Campaigns

  • email.sent - Email sent to recipient
  • email.delivered - Email successfully delivered
  • email.opened - Recipient opened email
  • email.clicked - Recipient clicked link
  • email.bounced - Email bounced
  • email.complained - Marked as spam

WhatsApp Campaigns

  • whatsapp.sent - Message sent
  • whatsapp.delivered - Message delivered
  • whatsapp.read - Message read by recipient
  • whatsapp.failed - Message failed to send

Form Events​

  • form.submitted - Form submission received
  • form.contact_created - Contact created from form
  • form.lead_created - Lead created from form

Webhook Payload Format​

Standard Payload Structure​

{
"event": "contact.created",
"timestamp": "2024-01-15T10:30:00Z",
"organization_id": "org_abc123",
"data": {
"id": "contact_xyz789",
"first_name": "John",
"last_name": "Doe",
"email": "john@example.com",
"phone": "+1234567890",
"created_at": "2024-01-15T10:30:00Z",
"created_by": "user_123"
},
"webhook_id": "webhook_456"
}

Payload Fields​

Top Level:

  • event - Event type (e.g., "contact.created")
  • timestamp - When event occurred (ISO 8601)
  • organization_id - Your organization ID
  • data - Event-specific data object
  • webhook_id - This webhook's ID

Data Object:

  • Contains full record data
  • Includes all fields and properties
  • Nested objects for relationships
  • Custom fields included

Example Payloads​

Contact Created:

{
"event": "contact.created",
"timestamp": "2024-01-15T10:30:00Z",
"data": {
"id": "contact_xyz789",
"first_name": "Jane",
"last_name": "Smith",
"email": "jane@company.com",
"phone": "+1234567890",
"company_id": "company_abc123",
"owner": "user_456",
"tags": ["lead", "high-priority"],
"custom_fields": {
"industry": "Technology",
"budget": 50000
}
}
}

Deal Stage Changed:

{
"event": "deal.stage_changed",
"timestamp": "2024-01-15T11:00:00Z",
"data": {
"id": "deal_123",
"name": "Enterprise Deal",
"value": 100000,
"previous_stage": "Negotiation",
"current_stage": "Closed Won",
"contact_id": "contact_xyz789",
"company_id": "company_abc123",
"owner": "user_456"
}
}

Workflow Completed:

{
"event": "workflow.completed",
"timestamp": "2024-01-15T11:15:00Z",
"data": {
"workflow_id": "workflow_789",
"workflow_name": "Lead Nurture Sequence",
"execution_id": "exec_xyz123",
"status": "success",
"duration_ms": 15000,
"contact_id": "contact_xyz789",
"output": {
"email_sent": true,
"task_created": true
}
}
}

Security & Authentication​

Webhook Signatures​

Verify webhook authenticity using HMAC signature:

Signature Header:

X-Expedify-Signature: sha256=abc123...

Verification Process:

  1. Get signature from X-Expedify-Signature header
  2. Compute HMAC-SHA256 of payload using your webhook secret
  3. Compare computed signature with header signature
  4. Accept only if signatures match

Example Verification (Node.js):

const crypto = require('crypto');

function verifyWebhook(payload, signature, secret) {
const computed = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');

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

// In your webhook handler:
app.post('/webhook', (req, res) => {
const signature = req.headers['x-expedify-signature'];
const payload = JSON.stringify(req.body);

if (!verifyWebhook(payload, signature, WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}

// Process webhook...
res.status(200).send('OK');
});

Example Verification (Python):

import hmac
import hashlib

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

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

# In your webhook handler:
@app.route('/webhook', methods=['POST'])
def webhook():
signature = request.headers.get('X-Expedify-Signature')
payload = request.get_data(as_text=True)

if not verify_webhook(payload, signature, WEBHOOK_SECRET):
return 'Invalid signature', 401

# Process webhook...
return 'OK', 200

Custom Headers​

Add authentication headers:

  • Authorization: Bearer tokens
  • X-API-Key: API key authentication
  • Custom: Any header your endpoint requires

Example:

Authorization: Bearer your-token-here
X-API-Key: your-api-key
X-Custom-Header: custom-value

Retry Logic​

How Retries Work​

When webhook fails:

  1. Expedify waits (default: 5 seconds)
  2. Retries sending webhook
  3. Repeats up to max retries (default: 3)
  4. Exponential backoff: 5s, 10s, 20s, 40s
  5. If all retries fail, marks webhook as failed

Success Criteria:

  • HTTP status 200-299 returned
  • Response received within timeout
  • No network errors

Failure Triggers:

  • HTTP status 400-599
  • Request timeout
  • Network error
  • DNS resolution failure

Configuring Retries​

Max Retries:

  • Default: 3 attempts
  • Range: 0-10 retries
  • 0 = no retries

Retry Delay:

  • Default: 5 seconds
  • Exponential backoff applied
  • Delay doubles each retry

Timeout:

  • Default: 30 seconds
  • Range: 5-120 seconds
  • Includes connection + response time

Testing Webhooks​

Using Test Button​

  1. Find webhook in list
  2. Click Test button
  3. Test payload sent to endpoint
  4. View response status and body
  5. Check for errors

Test Payload:

{
"event": "test",
"timestamp": "2024-01-15T12:00:00Z",
"data": {
"message": "This is a test webhook from Expedify",
"webhook_id": "webhook_456"
}
}

Using Tools​

RequestBin / Webhook.site:

  1. Go to webhook.site
  2. Copy unique URL
  3. Add as webhook URL
  4. Trigger event in Expedify
  5. View payload in RequestBin

ngrok (Local Testing):

# Start local server on port 3000
node server.js

# Start ngrok tunnel
ngrok http 3000

# Use ngrok URL in webhook settings
https://abc123.ngrok.io/webhook

Monitoring & Logs​

Webhook Activity Log​

View webhook execution history:

  • Timestamp: When webhook fired
  • Event: Event type
  • Status: Success/Failed
  • Response Code: HTTP status
  • Response Time: Request duration
  • Retry Count: Number of retries
  • Error: Failure reason (if failed)

Filtering Logs​

Filter by:

  • Status: Success, Failed, Pending
  • Event Type: Specific events
  • Date Range: Time period
  • Webhook: Specific webhook

Webhook Statistics​

View webhook metrics:

  • Total Sent: All webhook attempts
  • Success Rate: % successful
  • Average Response Time: Performance
  • Failed Count: Number of failures
  • Last Fired: Most recent execution

Managing Webhooks​

Editing Webhooks​

  1. Find webhook in list
  2. Click Edit button
  3. Update settings
  4. Click Save
  5. Changes apply immediately

Disabling Webhooks​

Temporary Disable:

  1. Toggle webhook to Inactive
  2. No events sent while inactive
  3. Can reactivate anytime
  4. History preserved

Permanent Delete:

  1. Click Delete button
  2. Confirm deletion
  3. Webhook removed
  4. Cannot be undone
  5. History deleted

Webhook Limits​

Free Plan:

  • 5 webhooks maximum
  • 1,000 webhook calls/month

Professional Plan:

  • 25 webhooks maximum
  • 10,000 webhook calls/month

Enterprise Plan:

  • Unlimited webhooks
  • Unlimited webhook calls

Common Integration Examples​

Zapier Integration​

// Zapier webhook catcher
// Trigger: Webhook by Zapier
// URL: Use webhook URL from Zapier
// In Expedify: Add Zapier webhook URL

Slack Notification​

// Slack webhook format
{
"text": "New contact created: John Doe (john@example.com)"
}

// Expedify to Slack via middleware
app.post('/expedify-to-slack', (req, res) => {
const { event, data } = req.body;

if (event === 'contact.created') {
await fetch(SLACK_WEBHOOK_URL, {
method: 'POST',
body: JSON.stringify({
text: `New contact: ${data.first_name} ${data.last_name} (${data.email})`
})
});
}

res.status(200).send('OK');
});

Database Sync​

# Sync new contacts to your database
@app.route('/sync-contact', methods=['POST'])
def sync_contact():
data = request.json

if data['event'] == 'contact.created':
contact = data['data']

# Insert into your database
db.contacts.insert({
'expedify_id': contact['id'],
'name': f"{contact['first_name']} {contact['last_name']}",
'email': contact['email'],
'synced_at': datetime.now()
})

return 'OK', 200

Best Practices​

  1. Respond Quickly

    • Acknowledge webhook with 200 OK
    • Process asynchronously if slow
    • Don't timeout Expedify's request
  2. Verify Signatures

    • Always validate webhook signatures
    • Prevent unauthorized access
    • Protectagainst replay attacks
  3. Handle Duplicates

    • Use event_id for deduplication
    • Idempotent processing
    • Database unique constraints
  4. Monitor Failures

    • Check webhook logs regularly
    • Set up failure alerts
    • Investigate failed webhooks
  5. Use HTTPS

    • Always use secure endpoints
    • Protect data in transit
    • Required for production

Troubleshooting​

Webhook not firing?

  • Check webhook is Active
  • Verify event is selected
  • Test with Test button
  • Check endpoint is accessible

Getting timeout errors?

  • Respond faster (<30 seconds)
  • Process asynchronously
  • Increase timeout setting
  • Check server capacity

Signature verification failing?

  • Use correct secret
  • Verify HMAC algorithm (SHA256)
  • Check header name
  • Ensure raw payload used

Too many retries?

  • Fix endpoint errors
  • Return 200-299 status
  • Reduce processing time
  • Check server uptime
  • Incoming Webhooks: Receive data from external systems
  • API Keys: Alternative integration method
  • Workflows: Automate based on events

Next Steps​

  1. ✅ Create Webhook: Set up first outgoing webhook
  2. ✅ Select Events: Choose relevant event types
  3. ✅ Test Webhook: Verify endpoint receives events
  4. ✅ Add Security: Implement signature verification
  5. ✅ Monitor: Check webhook logs regularly

Need help? Contact support through Settings → Support.