Incoming Webhooks
Receive real-time data from external systems and trigger workflows in Expedify when events occur in your other tools and platforms.
What are Incoming Webhooks?
Incoming webhooks allow external systems to push data into Expedify, enabling:
- Receive form submissions from your website
- Get notifications from payment processors (Stripe, Razorpay)
- Sync data from other CRMs or tools
- Trigger workflows from external events
- Capture leads from marketing platforms
Use Cases:
- Website contact form submissions → Create contacts
- E-commerce orders → Create deals
- Payment received → Update deal status
- Support ticket created → Create task
- Calendar event booked → Trigger workflow
Creating an Incoming Webhook
Step 1: Access Webhook Settings
- Go to Settings → Webhooks
- Click Incoming Webhooks tab
- Click Create Webhook button
Step 2: Configure Webhook
Basic Information:
- Name: Descriptive name (e.g., "Contact Form Submissions")
- Description: What this webhook receives
- Status: Active/Inactive
Webhook URL Generated:
https://api.expedify.ai/webhooks/incoming/{webhook_id}
Security Settings:
- Secret Key: Auto-generated for verification
- IP Whitelist: Restrict to specific IPs (optional)
- Rate Limit: Max requests per minute
Data Handling:
- Target Entity: Contact/Company/Deal/Task
- Field Mapping: Map webhook data to Expedify fields
- Workflow Trigger: Optionally trigger workflow on receipt
Step 3: Copy Webhook URL
- Copy generated webhook URL
- Add to external system
- Configure external system to send POST requests
- Save incoming webhook settings
Field Mapping
Automatic Field Detection
Expedify can auto-detect fields from incoming data:
Example Incoming Data:
{
"firstName": "John",
"lastName": "Doe",
"email": "john@example.com",
"phone": "+1234567890",
"company": "Acme Corp"
}
Auto-Detected Mapping:
firstName→ Contact First NamelastName→ Contact Last Nameemail→ Contact Emailphone→ Contact Phonecompany→ Company Name
Manual Field Mapping
Map webhook fields to Expedify fields manually:
- Click Configure Mapping
- See webhook field on left
- Select Expedify field on right
- Example Mappings:
form_name→ Contact First Nameform_email→ Contact Emailform_phone→ Contact Phoneform_message→ Note Contentsource→ Tag
Transformation Rules
Apply transformations to incoming data:
Text Transformations:
- Uppercase / Lowercase
- Trim whitespace
- Remove special characters
- Format phone numbers
Data Transformations:
- Parse dates (various formats)
- Convert currency
- Split full name → First/Last
- Combine fields
Conditional Mapping:
- Map based on conditions
- Default values if empty
- Skip if value matches pattern
Workflow Triggers
Auto-Trigger Workflows
Trigger workflows automatically when webhook receives data:
Setup:
- Enable Trigger Workflow option
- Select workflow from dropdown
- Map webhook data to workflow variables
- Save incoming webhook
How It Works:
- External system sends data
- Incoming webhook receives POST request
- Creates/updates Expedify record
- Automatically triggers selected workflow
- Workflow receives record data
Example Use Case:
- Webhook receives form submission
- Creates new contact
- Triggers "Lead Nurture" workflow
- Workflow sends welcome email
- Creates follow-up task
Supported Data Formats
JSON (Recommended)
{
"first_name": "Jane",
"last_name": "Smith",
"email": "jane@company.com",
"phone": "+1234567890",
"company": "Tech Corp",
"message": "Interested in your product"
}
Form URL-Encoded
first_name=Jane&last_name=Smith&email=jane@company.com&phone=+1234567890
XML
<?xml version="1.0"?>
<contact>
<first_name>Jane</first_name>
<last_name>Smith</last_name>
<email>jane@company.com</email>
<phone>+1234567890</phone>
</contact>
Content-Type Headers:
- JSON:
application/json - Form:
application/x-www-form-urlencoded - XML:
application/xml
Security
Webhook Secret
Verify incoming requests using webhook secret:
Request Header:
X-Webhook-Secret: your-secret-key
Verification:
// Node.js example
app.post('/your-middleware', (req, res) => {
const secret = req.headers['x-webhook-secret'];
if (secret !== EXPEDIFY_WEBHOOK_SECRET) {
return res.status(401).send('Unauthorized');
}
// Forward to Expedify
await fetch(EXPEDIFY_WEBHOOK_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Webhook-Secret': secret
},
body: JSON.stringify(req.body)
});
res.status(200).send('OK');
});
IP Whitelisting
Restrict webhook access to specific IP addresses:
Configure IPs:
- Edit incoming webhook
- Go to Security section
- Add Allowed IP Addresses
- Save changes
Format:
- Single IP:
192.168.1.1 - CIDR range:
192.168.1.0/24 - Multiple IPs: One per line
Rejected Requests:
- Returns 403 Forbidden
- Logged in webhook activity
- Does not consume rate limit
Rate Limiting
Protect against abuse with rate limits:
Settings:
- Requests per minute: Default 60
- Burst allowance: Allow temporary spikes
- Blocking: Temporary block on exceed
Headers in Response:
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 45
X-RateLimit-Reset: 1642345678
Rate Limit Exceeded:
- Returns 429 Too Many Requests
- Retry-After header included
- Request blocked but logged
Webhook Activity Logs
Viewing Incoming Requests
Track all incoming webhook activity:
Log Entries Show:
- Timestamp: When request received
- Source IP: Request origin
- Status: Success/Failed
- Action: Created/Updated contact
- Payload Size: Request size
- Response Time: Processing duration
- Error: Failure reason (if failed)
Filtering Logs
Filter by:
- Status: Success, Failed, Blocked
- Date Range: Time period
- Source IP: Specific IPs
- Action: Create/Update/Skip
Payload Inspection
View full request details:
- Headers: All request headers
- Raw Payload: Exact data received
- Mapped Data: How fields were mapped
- Result: Created/updated record
- Error Details: Stack trace if failed
Common Integration Examples
Website Contact Form
HTML Form:
<form action="https://api.expedify.ai/webhooks/incoming/abc123" method="POST">
<input type="text" name="first_name" required>
<input type="text" name="last_name" required>
<input type="email" name="email" required>
<input type="tel" name="phone">
<textarea name="message"></textarea>
<button type="submit">Submit</button>
</form>
JavaScript Form Submission:
const form = document.getElementById('contact-form');
form.addEventListener('submit', async (e) => {
e.preventDefault();
const formData = {
first_name: form.first_name.value,
last_name: form.last_name.value,
email: form.email.value,
phone: form.phone.value,
message: form.message.value
};
await fetch('https://api.expedify.ai/webhooks/incoming/abc123', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Webhook-Secret': 'your-secret-key'
},
body: JSON.stringify(formData)
});
alert('Form submitted!');
});
Stripe Payment Webhook
Stripe Webhook Setup:
- Go to Stripe Dashboard → Webhooks
- Add endpoint:
https://api.expedify.ai/webhooks/incoming/xyz789 - Select events:
payment_intent.succeeded - Copy webhook secret
Middleware to Transform:
// Middleware to transform Stripe data to Expedify format
app.post('/stripe-webhook', (req, res) => {
const { type, data } = req.body;
if (type === 'payment_intent.succeeded') {
const payment = data.object;
// Transform to Expedify format
const expedifyData = {
email: payment.receipt_email,
amount: payment.amount / 100,
currency: payment.currency,
payment_id: payment.id,
status: 'paid'
};
// Forward to Expedify webhook
await fetch(EXPEDIFY_WEBHOOK_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(expedifyData)
});
}
res.status(200).send('OK');
});
Zapier Integration
Zapier Setup:
- Create Zap
- Choose trigger app (e.g., Google Forms)
- Action: Webhooks by Zapier
- Action Type: POST
- URL: Your Expedify webhook URL
- Payload: Map form fields
- Test and activate
Zapier Payload:
{
"first_name": "{{First Name}}",
"last_name": "{{Last Name}}",
"email": "{{Email}}",
"source": "Google Forms"
}
Make.com (Integromat)
Make Scenario:
- Add webhook module
- Create webhook
- Copy webhook URL
- Paste Expedify incoming webhook URL
- Add HTTP Request module
- Configure POST to Expedify
- Map fields
- Activate scenario
Response Handling
Success Response
{
"success": true,
"message": "Contact created successfully",
"record_id": "contact_abc123",
"entity": "contact",
"action": "created"
}
HTTP Status: 200 OK
Validation Error
{
"success": false,
"error": "Validation failed",
"details": {
"email": "Invalid email format",
"phone": "Required field missing"
}
}
HTTP Status: 400 Bad Request
Authentication Error
{
"success": false,
"error": "Unauthorized",
"message": "Invalid webhook secret"
}
HTTP Status: 401 Unauthorized
Rate Limit Error
{
"success": false,
"error": "Rate limit exceeded",
"retry_after": 60
}
HTTP Status: 429 Too Many Requests
Testing Incoming Webhooks
Using cURL
curl -X POST https://api.expedify.ai/webhooks/incoming/abc123 \
-H "Content-Type: application/json" \
-H "X-Webhook-Secret: your-secret-key" \
-d '{
"first_name": "Test",
"last_name": "User",
"email": "test@example.com"
}'
Using Postman
- Create new POST request
- URL: Your webhook URL
- Headers:
Content-Type: application/jsonX-Webhook-Secret: your-secret-key
- Body: Raw JSON
- Send request
- Check response
Using Test Data
- Go to incoming webhook settings
- Click Test button
- Enter test JSON payload
- Click Send Test
- View result and created record
Deduplication
Prevent Duplicate Records
Email-Based Deduplication:
- Check if contact with email exists
- Update existing record instead of creating new
- Configurable per webhook
Custom Field Deduplication:
- Use custom field as unique identifier
- Example: External system ID
- Match on custom field value
Deduplication Rules:
- Skip: Don't create if exists
- Update: Update existing record
- Create Anyway: Allow duplicates
Best Practices
-
Use Secret Keys
- Always include webhook secret in requests
- Rotate secrets periodically
- Don't commit secrets to version control
-
Validate Data
- Check required fields exist
- Validate email/phone formats
- Sanitize input data
-
Handle Errors
- Check response status codes
- Implement retry logic
- Log failed requests
-
Monitor Activity
- Review webhook logs weekly
- Check for failed requests
- Investigate anomalies
-
Test Thoroughly
- Test with sample data first
- Verify field mapping is correct
- Check workflow triggers work
Troubleshooting
Not receiving webhooks?
- Check webhook is Active
- Verify URL is correct
- Check source system is sending data
- Review activity logs for errors
Data not mapping correctly?
- Review field mapping configuration
- Check incoming data format
- Use payload inspection in logs
- Verify field names match exactly
Rate limit errors?
- Reduce request frequency
- Implement request queuing
- Contact support for limit increase
- Check for duplicate requests
Authentication failing?
- Verify webhook secret is correct
- Check header name (X-Webhook-Secret)
- Ensure secret isn't URL-encoded
- Regenerate secret if compromised
Webhook Limits
Free Plan:
- 3 incoming webhooks
- 500 requests/month
- 60 requests/minute rate limit
Professional Plan:
- 10 incoming webhooks
- 5,000 requests/month
- 120 requests/minute rate limit
Enterprise Plan:
- Unlimited incoming webhooks
- Unlimited requests/month
- Custom rate limits
Related Features
- Outgoing Webhooks: Send data to external systems
- Workflows: Automate actions on webhook receipt
- API: Alternative integration method
Next Steps
- ✅ Create Webhook: Set up incoming webhook
- ✅ Configure Mapping: Map fields correctly
- ✅ Test Webhook: Send test data
- ✅ Add Security: Configure secret and IP whitelist
- ✅ Monitor: Check activity logs
Need help? Contact support through Settings → Support.