When a customer record is updated in Salesforce, a deal closes, or data synchronizes between systems, you need to know immediately—not when your polling script runs in 5 minutes. Salesforce webhooks solve this problem by sending real-time HTTP POST notifications to your server the moment events occur, enabling you to:
- Track record changes instantly when accounts, contacts, or custom objects are created, updated, or deleted
- Synchronize data across systems in real-time using Change Data Capture (CDC) events
- Trigger workflows automatically when business-critical data changes in Salesforce
- Monitor data quality by tracking validation errors and data sync status
- Build integrations that react to Salesforce events without constant API polling
Salesforce webhooks use industry-standard HMAC-SHA256 signature verification to ensure every notification authentically comes from your Salesforce organization, protecting your application from spoofed requests. In this comprehensive guide, you'll learn how to set up Salesforce webhooks, implement secure signature verification in Node.js, Python, and PHP, handle Change Data Capture events, and build production-ready webhook endpoints.
Testing your webhook integration is crucial before going live. Our Webhook Payload Generator tool lets you create properly signed Salesforce webhook payloads with custom event data, allowing you to test your signature verification logic and event handlers without configuring Data Cloud or exposing your local development environment.
What Are Salesforce Webhooks?
Salesforce webhooks are HTTP POST callbacks that Salesforce sends to your specified endpoint URL whenever events occur in your Salesforce organization. Unlike traditional API polling where your application repeatedly queries Salesforce's servers asking "did anything happen?", webhooks invert this model—Salesforce proactively notifies your server the instant a record changes, data syncs, or a platform event fires.
The webhook architecture follows this flow:
[Salesforce Event Occurs] → [Salesforce Server] → [HTTP POST to Your Endpoint] → [Your Application Logic]
Salesforce webhooks are delivered through multiple mechanisms:
- Change Data Capture (CDC): Real-time events for record changes (create, update, delete, undelete)
- Platform Events: Custom event-driven messaging within Salesforce
- Data Cloud Webhooks: Data synchronization and integration events from Data Cloud
- Streaming API: Push notifications using long-lived connections (CometD protocol)
Key differences from generic webhooks:
- Event Replay: Salesforce provides replay IDs allowing you to replay events from a specific point
- Transaction Context: Events include transaction keys and sequence numbers for ordering
- Change Metadata: Detailed information about which fields changed and their old/new values
- Multi-Entity Support: Subscribe to changes across multiple object types with single configuration
- Governor Limits Integration: Event delivery respects Salesforce API limits
Benefits specific to Salesforce webhooks:
- Real-time synchronization between Salesforce and external systems without polling
- Reduced API usage by eliminating constant queries for changes
- Transaction-level change tracking with before/after field values
- Automatic handling of record merges, undeletes, and cascade operations
- Integration with Salesforce's security model (sharing rules, field-level security)
Prerequisites for setting up Salesforce webhooks:
- Active Salesforce organization (Developer Edition or higher recommended)
- Data Cloud or Platform Events enabled (varies by license type)
- Publicly accessible HTTPS endpoint (localhost won't work without tunneling)
- SSL/TLS certificate on your webhook endpoint (required for security)
- Ability to handle HTTP POST requests with JSON payloads
- (Recommended) API access to manage webhook configurations programmatically
Setting Up Salesforce Webhooks
Salesforce offers multiple webhook mechanisms. This guide focuses on Data Cloud webhooks, which provide the most flexible integration options for external systems.
Step 1: Access Data Cloud Settings
- Log in to your Salesforce organization
- Navigate to Setup (gear icon, top right)
- In Quick Find, search for Data Cloud
- Click Data Cloud Setup to access Data Cloud settings
- Ensure Data Cloud is enabled for your organization
Note: Data Cloud may require specific licenses. Check with your Salesforce administrator if you don't see this option.
Step 2: Generate Secret Key for Signature Validation
Salesforce uses HMAC-SHA256 signature verification to authenticate webhook requests.
- In Data Cloud Setup, navigate to API Access section
- Click Webhook Secret Keys
- Click Generate New Secret Key
- Copy the generated key immediately (shown only once)
- Store securely in your application's environment variables
Example secret key format:
sk_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
Important: Each webhook endpoint should have its own unique secret key for security isolation.
Step 3: Configure Webhook Endpoint
- In Data Cloud Setup, go to Webhooks section
- Click New Webhook
- Enter configuration details:
Webhook Configuration Fields:
- Name: Descriptive name (e.g., "Production Record Changes")
- Endpoint URL: Your HTTPS URL (e.g.,
https://yourdomain.com/webhooks/salesforce) - Active: Toggle to enable/disable webhook
- Secret Key: Select the secret key generated in Step 2
URL Requirements:
- Must use HTTPS (HTTP not supported)
- Must be publicly accessible
- Should be a dedicated endpoint for Salesforce events
- Path can be customized to your application structure
Step 4: Subscribe to Events
Configure which events trigger webhook deliveries:
Change Data Capture Events:
-
Navigate to Setup > Change Data Capture
-
Click Enable Change Data Capture
-
Select objects to track:
- ☐ Account - Customer and prospect accounts
- ☐ Contact - Individual contacts
- ☐ Opportunity - Sales opportunities
- ☐ Lead - Potential customers
- ☐ Case - Customer support cases
- ☐ Custom Objects - Your organization's custom data
-
Select change types:
- ☐ Create - New records
- ☐ Update - Modified records
- ☐ Delete - Deleted records
- ☐ Undelete - Restored records
Data Cloud Events:
- In Data Cloud Setup > Webhooks
- Configure event subscriptions:
- ☐ Data Sync Completed - Synchronization finished
- ☐ Data Quality Issues - Validation failures
- ☐ Record Matching - Duplicate detection events
Step 5: Configure Retry Policy
Set up retry behavior for failed webhook deliveries:
- In webhook configuration, expand Advanced Settings
- Configure retry parameters:
- Max Retries: 3-5 attempts recommended
- Retry Interval: Exponential backoff (e.g., 1s, 2s, 4s, 8s)
- Timeout: Request timeout (10-30 seconds)
Step 6: Test Your Integration
Salesforce provides testing capabilities:
- In webhook configuration, click Send Test Event
- Select event type (e.g., Account Create)
- Review sample payload format
- Send to your endpoint
- Verify your endpoint receives and processes the test
Verify in Your Application:
- Check logs for incoming request
- Confirm signature verification passes
- Validate payload structure matches expectations
Step 7: Enable the Webhook
- Toggle Active to ON in webhook configuration
- Click Save to activate
- Events will now be sent to your endpoint in real-time
Monitoring Webhook Delivery
Track webhook health and delivery status:
- Navigate to Setup > Event Monitoring
- Filter by webhook events
- Review:
- Delivery success rate
- Failed delivery attempts
- Response times from your endpoint
- Error messages
Set up alerts for delivery failures exceeding threshold.
Pro Tips for Setup
Best Practices:
- Use separate webhooks for sandbox/test and production environments
- Configure different endpoints for different event types (CDC vs Data Cloud)
- Enable all event types initially for debugging, then narrow down to needed events
- Set up monitoring dashboards for webhook metrics
- Document your secret key locations for team members
- Implement log archiving for webhook delivery history
Common Mistakes to Avoid:
- ❌ Using HTTP instead of HTTPS (will fail validation)
- ❌ Sharing secret keys across multiple endpoints (security risk)
- ❌ Not testing before enabling (broken endpoints cause event loss)
- ❌ Subscribing to all objects when only needing a few (unnecessary processing)
- ❌ Using localhost URLs without ngrok tunneling (Salesforce can't reach them)
- ❌ Forgetting to configure retry policy (no automatic retry on failure)
Rate Limits and Restrictions:
- No explicit rate limits on webhook deliveries (events sent as they occur)
- Maximum endpoint URL length: 2000 characters
- Maximum retry period: Configurable up to 24 hours
- Event batch sizes vary (typically 1-10 events per POST for CDC)
- API limits apply to webhook configuration changes
Salesforce Webhook Events & Payloads
Salesforce sends webhook payloads as JSON objects containing event metadata and change details. The exact structure varies by event source (Change Data Capture, Platform Events, Data Cloud).
Event Types Overview
| Event Type | Description | Common Use Case |
|---|---|---|
record.created | New record created in Salesforce | Track new accounts, contacts, leads for CRM integration |
record.updated | Existing record modified | Synchronize field changes to external databases |
record.deleted | Record deleted from Salesforce | Remove corresponding data from external systems |
record.undeleted | Deleted record restored | Restore previously removed data in external systems |
data.sync | Data Cloud synchronization completed | Monitor data pipeline status and quality |
webhook.test | Test event for endpoint verification | Validate webhook configuration and connectivity |
Common Fields Across All Events
Every Salesforce webhook event includes these base fields:
event.type- Event type identifier (record.created, data.sync, etc.)event.createdDate- ISO 8601 timestamp when event occurredevent.replayId- Unique replay identifier for event recoverydata.event.replayId- Duplicate replay ID in data section (for CDC compatibility)data.payload- Event-specific payload with record/sync details
Detailed Event Examples
Event: record.created
Description: A new record was created in Salesforce (triggered by Change Data Capture).
Payload Structure:
{
"event": {
"type": "record.created",
"createdDate": "2025-01-24T12:00:00.000Z",
"replayId": 123456
},
"data": {
"event": {
"replayId": 123456
},
"payload": {
"Id": "rec_abc123",
"Name": "Acme Corporation",
"Type": "Customer",
"Industry": "Technology",
"CreatedDate": "2025-01-24T12:00:00.000Z",
"ChangeEventHeader": {
"entityName": "Account",
"changeType": "CREATE",
"changeOrigin": "com/salesforce/api/rest/56.0",
"transactionKey": "txn_def456",
"sequenceNumber": 1,
"commitTimestamp": 1643723456000
}
}
}
}
Key Fields:
Id- Salesforce record ID (15 or 18 character)ChangeEventHeader.entityName- Object type (Account, Contact, Opportunity, etc.)ChangeEventHeader.changeType- CREATE, UPDATE, DELETE, UNDELETEChangeEventHeader.transactionKey- Groups related changes in same transactionChangeEventHeader.sequenceNumber- Order of changes within transactionChangeEventHeader.commitTimestamp- When transaction was committed
Event: record.updated
Description: An existing Salesforce record was modified.
Payload Structure:
{
"event": {
"type": "record.updated",
"createdDate": "2025-01-24T13:30:00.000Z",
"replayId": 123457
},
"data": {
"event": {
"replayId": 123457
},
"payload": {
"Id": "rec_abc123",
"Name": "Acme Corporation",
"Type": "Customer",
"Industry": "Healthcare",
"LastModifiedDate": "2025-01-24T13:30:00.000Z",
"ChangeEventHeader": {
"entityName": "Account",
"changeType": "UPDATE",
"changeOrigin": "com/salesforce/api/rest/56.0",
"transactionKey": "txn_ghi789",
"sequenceNumber": 2,
"commitTimestamp": 1643729400000,
"changedFields": ["Industry"]
}
}
}
}
Key Fields:
changedFields- Array of field API names that were modifiedLastModifiedDate- When the record was last changed- All other fields show current values (not before/after deltas)
Important Note: Change Data Capture does NOT include old field values, only current state. For field-level audit trails, use Field History Tracking or store changes in your external system.
Event: record.deleted
Description: A record was deleted from Salesforce (moved to Recycle Bin).
Payload Structure:
{
"event": {
"type": "record.deleted",
"createdDate": "2025-01-24T14:00:00.000Z",
"replayId": 123458
},
"data": {
"event": {
"replayId": 123458
},
"payload": {
"Id": "rec_abc123",
"ChangeEventHeader": {
"entityName": "Account",
"changeType": "DELETE",
"changeOrigin": "com/salesforce/api/rest/56.0",
"transactionKey": "txn_jkl012",
"sequenceNumber": 3,
"commitTimestamp": 1643731200000
}
}
}
}
Key Fields:
- Only
IdandChangeEventHeaderare present (record details are gone) - Use
Idto remove corresponding data from external systems
Action Required: Archive or soft-delete the record in your external database. Don't permanently delete immediately in case of undelete event.
Event: data.sync
Description: Data Cloud completed a data synchronization operation.
Payload Structure:
{
"event": {
"type": "data.sync",
"createdDate": "2025-01-24T15:00:00.000Z",
"replayId": 123459
},
"data": {
"event": {
"replayId": 123459
},
"payload": {
"syncId": "sync_xyz789",
"entityName": "Contact",
"recordCount": 150,
"status": "completed",
"startTime": "2025-01-24T14:45:00.000Z",
"endTime": "2025-01-24T15:00:00.000Z"
}
}
}
Key Fields:
syncId- Unique identifier for this sync operationentityName- Object type that was synchronizedrecordCount- Number of records processedstatus- completed, failed, or partialstartTime/endTime- Sync duration
Use Case: Monitor data pipeline health, track sync latency, alert on failures.
Event: webhook.test
Description: Test event sent when validating webhook configuration.
Payload Structure:
{
"event": {
"type": "webhook.test",
"createdDate": "2025-01-24T16:00:00.000Z",
"replayId": 123460
},
"data": {
"event": {
"replayId": 123460
},
"payload": {
"message": "This is a test webhook event from Salesforce",
"timestamp": "2025-01-24T16:00:00.000Z"
}
}
}
Use Case: Verify endpoint connectivity and signature verification before enabling production events.
Replay ID Usage
The replayId field enables event replay functionality:
// Store last processed replay ID
const lastReplayId = 123456;
// On application restart, request events after last processed ID
// Salesforce retains events for 24-72 hours (varies by configuration)
Replay Strategies:
- Store in Database: Persist replay ID after successful processing
- Use for Recovery: Replay missed events during downtime
- Idempotency Check: Skip already-processed events based on replay ID
Webhook Signature Verification
Why Signature Verification Matters
Without signature verification, attackers could:
- Spoof webhook requests by sending fake events to your endpoint
- Manipulate data by injecting false record changes or sync events
- Trigger unintended actions like deleting valid records from external systems
- Exploit business logic by fabricating data quality issues or sync completions
Salesforce's HMAC-SHA256 signature verification cryptographically proves that webhook requests genuinely originated from your Salesforce organization, preventing all these attack vectors.
Salesforce's Signature Method
Algorithm: HMAC-SHA256 (Hash-based Message Authentication Code with SHA-256)
Header Name: X-Salesforce-Signature
What's Signed: Salesforce creates a signature by:
- Taking the raw request body bytes (unmodified JSON)
- Computing HMAC-SHA256 hash using your secret key
- Encoding the signature as base64 or hex (based on configuration)
- Sending in
X-Salesforce-Signatureheader
Additional Security:
- Secret key uniquely tied to your Data Cloud instance
- No timestamp header (use your own timestamp validation)
- Separate keys per webhook endpoint for isolation
Step-by-Step Verification Process
- Extract the signature from
X-Salesforce-Signatureheader - Retrieve your secret key from environment variables
- Get the raw request body (must be unmodified bytes, not parsed JSON)
- Compute HMAC-SHA256 of raw body using secret key
- Encode signature as base64 or hex (match Salesforce configuration)
- Compare signatures using constant-time comparison (prevent timing attacks)
- Validate business logic (check replay ID hasn't been processed already)
Code Examples
Node.js / Express
const express = require('express');
const crypto = require('crypto');
const bodyParser = require('body-parser');
const app = express();
// IMPORTANT: Use raw body parser for webhook endpoint
app.use(
'/webhooks/salesforce',
bodyParser.raw({ type: 'application/json' })
);
// Signature verification function
function verifySignature(payload, signature, secret) {
// Compute HMAC-SHA256 of raw payload
const hmac = crypto.createHmac('sha256', secret);
hmac.update(payload);
const computedSignature = hmac.digest('base64'); // or 'hex' based on config
// Constant-time comparison to prevent timing attacks
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(computedSignature)
);
}
// Salesforce webhook endpoint
app.post('/webhooks/salesforce', async (req, res) => {
try {
// Extract signature from header
const signature = req.get('X-Salesforce-Signature');
if (!signature) {
console.error('Missing X-Salesforce-Signature header');
return res.status(401).json({ error: 'Missing signature' });
}
// Get raw body (Buffer)
const payload = req.body;
// Retrieve secret key from environment
const secret = process.env.SALESFORCE_WEBHOOK_SECRET;
if (!secret) {
console.error('SALESFORCE_WEBHOOK_SECRET not configured');
return res.status(500).json({ error: 'Server configuration error' });
}
// Verify signature
const isValid = verifySignature(payload, signature, secret);
if (!isValid) {
console.error('Invalid Salesforce webhook signature');
return res.status(403).json({ error: 'Invalid signature' });
}
// Parse payload after verification
const event = JSON.parse(payload.toString());
// Check for duplicate using replay ID
const replayId = event.event.replayId;
const alreadyProcessed = await checkReplayId(replayId);
if (alreadyProcessed) {
console.log(`Event ${replayId} already processed, skipping`);
return res.status(200).json({ status: 'duplicate' });
}
// Process event
console.log(`Processing Salesforce event: ${event.event.type}`);
console.log(`Replay ID: ${replayId}`);
// Return 200 immediately (process async if needed)
res.status(200).json({ status: 'received' });
// Async processing
await processEventAsync(event);
} catch (error) {
console.error('Salesforce webhook processing error:', error);
res.status(500).json({ error: 'Processing failed' });
}
});
// Helper function to check replay ID
async function checkReplayId(replayId) {
// Query your database for this replay ID
// Return true if already processed
return false; // Implement your logic
}
// Async event processing
async function processEventAsync(event) {
const eventType = event.event.type;
switch (eventType) {
case 'record.created':
await handleRecordCreated(event.data.payload);
break;
case 'record.updated':
await handleRecordUpdated(event.data.payload);
break;
case 'record.deleted':
await handleRecordDeleted(event.data.payload);
break;
case 'data.sync':
await handleDataSync(event.data.payload);
break;
default:
console.warn(`Unhandled event type: ${eventType}`);
}
}
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Salesforce webhook server listening on port ${PORT}`);
});
Python / Flask
import hmac
import hashlib
import json
from flask import Flask, request, jsonify
app = Flask(__name__)
# Load secret key from environment
SECRET_KEY = 'your_secret_key_here' # Use environment variable in production
def verify_signature(payload, signature, secret):
"""Verify HMAC-SHA256 signature"""
# Compute HMAC-SHA256
computed_hmac = hmac.new(
secret.encode('utf-8'),
payload,
hashlib.sha256
)
computed_signature = computed_hmac.hexdigest() # or .digest().encode('base64')
# Constant-time comparison
return hmac.compare_digest(signature, computed_signature)
@app.route('/webhooks/salesforce', methods=['POST'])
def salesforce_webhook():
try:
# Get signature from header
signature = request.headers.get('X-Salesforce-Signature')
if not signature:
return jsonify({'error': 'Missing signature'}), 401
# Get raw body (must be bytes)
payload = request.get_data()
# Verify signature
is_valid = verify_signature(payload, signature, SECRET_KEY)
if not is_valid:
print('Invalid Salesforce webhook signature')
return jsonify({'error': 'Invalid signature'}), 403
# Parse payload after verification
event = json.loads(payload)
# Check for duplicate using replay ID
replay_id = event['event']['replayId']
if check_replay_id(replay_id):
print(f'Event {replay_id} already processed')
return jsonify({'status': 'duplicate'}), 200
# Process event
event_type = event['event']['type']
print(f'Processing Salesforce event: {event_type}')
print(f'Replay ID: {replay_id}')
# Return 200 immediately
return jsonify({'status': 'received'}), 200
except Exception as error:
print(f'Salesforce webhook processing error: {error}')
return jsonify({'error': 'Processing failed'}), 500
def check_replay_id(replay_id):
"""Check if replay ID already processed"""
# Implement database lookup
return False
if __name__ == '__main__':
app.run(port=3000, debug=False)
PHP
<?php
// Get secret key from environment
$secret = getenv('SALESFORCE_WEBHOOK_SECRET');
if (!$secret) {
http_response_code(500);
die(json_encode(['error' => 'Server configuration error']));
}
// Extract signature from header
$signature = $_SERVER['HTTP_X_SALESFORCE_SIGNATURE'] ?? '';
if (empty($signature)) {
http_response_code(401);
die(json_encode(['error' => 'Missing signature']));
}
// Get raw POST body
$payload = file_get_contents('php://input');
// Compute HMAC-SHA256
$computedSignature = hash_hmac('sha256', $payload, $secret);
// For base64: base64_encode(hash_hmac('sha256', $payload, $secret, true))
// Constant-time comparison
if (!hash_equals($signature, $computedSignature)) {
error_log('Invalid Salesforce webhook signature');
http_response_code(403);
die(json_encode(['error' => 'Invalid signature']));
}
// Parse payload after verification
$event = json_decode($payload, true);
if (!$event) {
http_response_code(400);
die(json_encode(['error' => 'Invalid JSON']));
}
// Check for duplicate using replay ID
$replayId = $event['event']['replayId'] ?? null;
if ($replayId && checkReplayId($replayId)) {
error_log("Event $replayId already processed");
http_response_code(200);
die(json_encode(['status' => 'duplicate']));
}
// Process event
$eventType = $event['event']['type'];
error_log("Processing Salesforce event: $eventType");
error_log("Replay ID: $replayId");
// Return 200 immediately
http_response_code(200);
echo json_encode(['status' => 'received']);
// Process async (implement your queue logic here)
function checkReplayId($replayId) {
// Implement database lookup
return false;
}
?>
Common Verification Errors
- ❌ Parsing JSON before verification: Body modified, signature won't match
- ✅ Use raw body parser, verify first, then parse JSON
- ❌ Using wrong secret key: Test vs production keys are different
- ✅ Verify key from Data Cloud matches your environment variable
- ❌ Incorrect encoding: Base64 vs hex mismatch
- ✅ Match Salesforce configuration (check Data Cloud webhook settings)
- ❌ Not using constant-time comparison: Vulnerable to timing attacks
- ✅ Use
crypto.timingSafeEqual(),hmac.compare_digest(), orhash_equals()
- ✅ Use
- ❌ Whitespace trimming: Modifies raw body, breaks signature
- ✅ Preserve exact bytes received from Salesforce
Testing Salesforce Webhooks
Local Development with ngrok
# Install ngrok
brew install ngrok # macOS
# Or download from ngrok.com
# Start your local webhook server on port 3000
node server.js
# In another terminal, create ngrok tunnel
ngrok http 3000
# Output: Forwarding https://abc123.ngrok.io -> http://localhost:3000
Configure in Salesforce:
- Copy the ngrok HTTPS URL
- Navigate to Data Cloud > Webhooks
- Set endpoint to:
https://abc123.ngrok.io/webhooks/salesforce - Save and test
Using Webhook Payload Generator
Visit Webhook Payload Generator:
- Select Salesforce from provider dropdown
- Choose event type (record.created, data.sync, etc.)
- Customize payload (edit record IDs, timestamps, field values)
- Enter your secret key
- Generate properly signed HMAC-SHA256 signature
- Copy complete HTTP request including headers
- Send to
http://localhost:3000/webhooks/salesforcewith curl
Example cURL Command:
curl -X POST http://localhost:3000/webhooks/salesforce \
-H "Content-Type: application/json" \
-H "X-Salesforce-Signature: <generated-signature>" \
-d '{"event":{"type":"record.created","createdDate":"2025-01-24T12:00:00.000Z","replayId":123456},"data":{"payload":{"Id":"rec_abc123"}}}'
Best Practices
Security
- ✅ Always verify signatures: Never process unverified webhooks
- ✅ Use HTTPS only: Salesforce requires HTTPS endpoints
- ✅ Store secrets securely: Environment variables, not hardcoded
- ✅ Rotate secret keys: Periodic rotation improves security
- ✅ Implement replay ID checks: Prevent duplicate processing
- ✅ Validate payload structure: Check required fields exist before processing
Performance
- ✅ Respond quickly: Return 200 within seconds to avoid timeouts
- ✅ Process asynchronously: Queue events for background processing
- ✅ Use database connection pooling: Don't create connections per request
- ✅ Implement batching: Process multiple events together when possible
Reliability
- ✅ Implement idempotency: Use replay IDs to prevent duplicate processing
- ✅ Store replay IDs: Enable event replay during downtime
- ✅ Log all events: Comprehensive logging for debugging
- ✅ Monitor webhook health: Track delivery success rates
- ✅ Set up alerts: Notify on high failure rates
Common Issues & Troubleshooting
Issue 1: Signature Verification Failing
Causes & Solutions:
❌ Using wrong secret key ✅ Verify key from Data Cloud matches environment variable
❌ Parsing JSON before verification
✅ Use raw body parser, verify signature before JSON.parse()
❌ Incorrect encoding (base64 vs hex) ✅ Match encoding configured in Data Cloud webhook settings
Issue 2: Missing Webhooks
Causes & Solutions:
❌ Firewall blocking ✅ Check firewall rules, allowlist Salesforce IPs
❌ SSL certificate issues ✅ Verify certificate is valid and trusted
❌ Endpoint returning errors ✅ Check application logs, fix bugs causing 5xx responses
Issue 3: Duplicate Events
Causes & Solutions:
❌ No replay ID check ✅ Implement idempotency using replay ID storage
❌ Network retries ✅ Return 200 only after successfully queuing event
Frequently Asked Questions
Q: How do I generate a secret key?
A: Navigate to Data Cloud Setup > API Access > Webhook Secret Keys, click "Generate New Secret Key," copy immediately (shown only once), and store in environment variables.
Q: Can I replay missed events?
A: Yes, use the replay ID system. Salesforce retains events for 24-72 hours (varies by configuration). Store the last successfully processed replay ID and request events after that ID on application restart.
Q: What's the difference between Change Data Capture and Platform Events?
A: Change Data Capture automatically publishes events for record changes (create/update/delete) on standard and custom objects. Platform Events are custom event definitions you create for application-specific messaging. CDC is better for data sync, Platform Events for custom workflows.
Q: How do I test without Salesforce account?
A: Use our Webhook Payload Generator to create signed test payloads with custom event data. This lets you test signature verification and event handling without Salesforce configuration.
Next Steps & Resources
Additional Resources
Salesforce Official Documentation:
- Change Data Capture Developer Guide
- Data Cloud API Reference
- Platform Events Developer Guide
- Streaming API Guide
Related Guides:
Testing Tools:
- Webhook Payload Generator - Create signed Salesforce payloads
- ngrok - Expose localhost for testing
Conclusion
Salesforce webhooks provide a powerful real-time integration mechanism for tracking record changes, synchronizing data, and triggering workflows. By following this guide, you now know how to:
- ✅ Set up Data Cloud webhooks with secret key generation
- ✅ Verify webhook signatures using HMAC-SHA256
- ✅ Implement production-ready endpoints with async processing
- ✅ Handle Change Data Capture events and replay functionality
- ✅ Test webhooks using ngrok or our payload generator
- ✅ Troubleshoot common signature and delivery issues
Remember these key principles:
- Always verify signatures - HMAC-SHA256 prevents spoofing
- Respond quickly - Return 200 within seconds
- Process asynchronously - Queue events for reliability
- Implement idempotency - Use replay IDs to handle duplicates
Start building with Salesforce webhooks today, and use our Webhook Payload Generator to test your integration safely.
Sources: