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 SettingsWebhooks
  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.