Skip to content

Webhooks

Webhooks

Webhooks allow you to receive real-time notifications when events occur in your CRM. Instead of polling the API for changes, webhooks push data to your specified URL automatically.

How Webhooks Work

  1. Configure: Create a webhook endpoint with your URL and select which events to subscribe to
  2. Trigger: When an event occurs (e.g., contact created), we queue a delivery
  3. Deliver: Our system sends an HTTP POST request to your URL with the event data
  4. Verify: Your server verifies the signature and processes the payload
  5. Retry: If delivery fails, we automatically retry with exponential backoff

Supported Events

Contact Events

EventDescription
contact.createdA new contact was created
contact.updatedAn existing contact was modified
contact.deletedA contact was soft-deleted

Organization Events

EventDescription
organization.createdA new organization was created
organization.updatedAn existing organization was modified
organization.deletedAn organization was soft-deleted

Deal Events

EventDescription
deal.createdA new deal was created
deal.updatedAn existing deal was modified (non-stage change)
deal.stage_changedA deal moved to a different pipeline stage
deal.deletedA deal was soft-deleted

Activity Events

EventDescription
activity.createdA new activity (call, email, meeting, note) was logged

Task Events

EventDescription
task.createdA new task was created
task.completedA task was marked as complete

Webhook Payload Format

All webhook payloads follow a consistent structure:

{
"id": "evt_550e8400-e29b-41d4-a716-446655440000",
"event": "contact.created",
"created_at": "2024-01-15T10:30:00.000Z",
"api_version": "2025-12-01",
"data": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"first_name": "John",
"last_name": "Doe",
"job_title": "Purchasing Manager",
"organization_id": "987e6543-e21b-12d3-a456-426614174999",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z"
}
}

Payload Fields

FieldTypeDescription
idstringUnique event identifier
eventstringEvent type (e.g., contact.created)
created_atstringISO 8601 timestamp of when event occurred
api_versionstringAPI version used for this payload
dataobjectThe full resource data at time of event

HTTP Headers

Each webhook request includes the following headers:

HeaderDescription
Content-TypeAlways application/json
User-AgentSellerCockpit-Webhook/1.0
X-Webhook-IDYour webhook endpoint’s unique ID
X-Webhook-EventThe event type (e.g., contact.created)
X-Webhook-TimestampUnix timestamp when request was signed
X-Webhook-SignatureHMAC-SHA256 signature for verification

Custom Headers

You can configure additional custom headers when creating your webhook endpoint. These are useful for authentication tokens or routing information.


Security & Signature Verification

Every webhook request is signed using HMAC-SHA256. Always verify signatures to ensure requests are authentic.

Signature Format

X-Webhook-Signature: sha256=abc123def456...

Verification Process

  1. Extract the timestamp from X-Webhook-Timestamp header
  2. Get the raw request body (do not parse it first)
  3. Construct the signed payload: {timestamp}.{body}
  4. Compute HMAC-SHA256 using your webhook secret (without whsec_ prefix)
  5. Compare your computed signature with the X-Webhook-Signature header

Example: Node.js Verification

const crypto = require('crypto');
function verifyWebhookSignature(payload, signature, timestamp, secret) {
// Remove 'whsec_' prefix from secret
const secretKey = secret.startsWith('whsec_') ? secret.slice(6) : secret;
// Construct signed payload
const signedPayload = `${timestamp}.${payload}`;
// Compute expected signature
const expectedSignature = crypto
.createHmac('sha256', secretKey)
.update(signedPayload)
.digest('hex');
// Compare signatures (constant-time comparison)
const receivedSig = signature.replace('sha256=', '');
return crypto.timingSafeEqual(
Buffer.from(expectedSignature),
Buffer.from(receivedSig)
);
}
// Express.js example
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-webhook-signature'];
const timestamp = req.headers['x-webhook-timestamp'];
const payload = req.body.toString();
if (!verifyWebhookSignature(payload, signature, timestamp, process.env.WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(payload);
console.log('Received event:', event.event, event.data.id);
res.status(200).send('OK');
});

Example: Python Verification

import hmac
import hashlib
def verify_webhook_signature(payload: str, signature: str, timestamp: str, secret: str) -> bool:
# Remove 'whsec_' prefix from secret
secret_key = secret[6:] if secret.startswith('whsec_') else secret
# Construct signed payload
signed_payload = f"{timestamp}.{payload}"
# Compute expected signature
expected_signature = hmac.new(
secret_key.encode(),
signed_payload.encode(),
hashlib.sha256
).hexdigest()
# Compare signatures
received_sig = signature.replace('sha256=', '')
return hmac.compare_digest(expected_signature, received_sig)
# Flask example
@app.route('/webhook', methods=['POST'])
def handle_webhook():
signature = request.headers.get('X-Webhook-Signature')
timestamp = request.headers.get('X-Webhook-Timestamp')
payload = request.get_data(as_text=True)
if not verify_webhook_signature(payload, signature, timestamp, WEBHOOK_SECRET):
return 'Invalid signature', 401
event = request.json
print(f"Received event: {event['event']} - {event['data']['id']}")
return 'OK', 200

Replay Attack Prevention

To prevent replay attacks, verify that the timestamp is recent (e.g., within 5 minutes):

const MAX_TIMESTAMP_AGE = 5 * 60 * 1000; // 5 minutes
function isTimestampValid(timestamp) {
const timestampMs = parseInt(timestamp) * 1000;
const now = Date.now();
return Math.abs(now - timestampMs) < MAX_TIMESTAMP_AGE;
}

Retry Policy

If your endpoint doesn’t respond with a 2xx status code within 30 seconds, we automatically retry using exponential backoff:

AttemptDelay
1st retry1 minute
2nd retry5 minutes
3rd retry15 minutes
4th retry1 hour
5th retry4 hours

After 5 failed attempts, the delivery is marked as permanently failed. You can manually retry failed deliveries from the webhook settings UI.

Best Practices for Your Endpoint

  1. Respond quickly: Return 200 OK immediately, then process asynchronously
  2. Be idempotent: Handle duplicate deliveries gracefully using the event id
  3. Use HTTPS: We only deliver to secure endpoints
  4. Log everything: Keep records for debugging delivery issues

Delivery Status

Each webhook delivery is tracked with one of these statuses:

StatusDescription
pendingQueued for delivery
successSuccessfully delivered (2xx response)
retryingDelivery failed, scheduled for retry
failedAll retry attempts exhausted

View delivery history and retry failed deliveries from Settings > Developer > Webhooks.


Testing Webhooks

Send Test Event

Use the “Test” button in the webhook settings UI to send a sample event:

{
"id": "evt_test_123",
"event": "test.webhook",
"created_at": "2024-01-15T10:30:00.000Z",
"api_version": "2025-12-01",
"data": {
"message": "This is a test webhook delivery"
}
}

Local Development

For local development, use a tunneling service like:

These create a public HTTPS URL that forwards to your local server.

Webhook Debugging Services

These services let you inspect webhook payloads:


Webhook Limits

LimitValue
Webhooks per company25
Events per webhookUnlimited
Payload timeout30 seconds
Max retry attempts5
Response body storedFirst 10,000 characters

Managing Webhooks

Creating a Webhook

  1. Go to Settings > Developer > Webhooks
  2. Click Create Webhook
  3. Enter your endpoint URL (must be HTTPS)
  4. Select the events you want to receive
  5. Optionally add custom headers
  6. Copy and save your webhook secret

Updating a Webhook

  • Edit the URL, events, or custom headers at any time
  • Changes take effect immediately for new events

Regenerating Secret

If your webhook secret is compromised:

  1. Click Regenerate Secret on the webhook
  2. Update your server with the new secret
  3. The old secret is immediately invalidated

Deleting a Webhook

Deleting a webhook immediately stops all deliveries. Pending retries are cancelled.


API Reference

For programmatic webhook management via the REST API, see:


Integration Examples

n8n

  1. Create a Webhook node in n8n
  2. Copy the webhook URL
  3. Create a webhook in SellerCockpit with this URL
  4. Connect additional nodes to process the data

Zapier

  1. Create a new Zap with “Webhooks by Zapier” trigger
  2. Choose “Catch Hook”
  3. Copy the custom webhook URL
  4. Create a webhook in SellerCockpit
  5. Add actions to process incoming events

Make (Integromat)

  1. Create a new scenario with a Webhooks module
  2. Add a “Custom webhook” trigger
  3. Copy the webhook URL
  4. Configure your SellerCockpit webhook
  5. Add modules to handle the events

Troubleshooting

Webhook not receiving events

  1. Check webhook is Active in settings
  2. Verify the URL is correct and HTTPS
  3. Check your server is accessible from the internet
  4. Review delivery history for error details

Invalid signature errors

  1. Use the raw request body (don’t parse before verifying)
  2. Check you’re using the correct secret (without whsec_ prefix in computation)
  3. Verify timestamp format (Unix seconds, not milliseconds)
  4. Ensure constant-time string comparison

Deliveries timing out

  1. Return 200 OK immediately, process async
  2. Increase server timeout if needed
  3. Check for network/firewall issues

Too many retries

  1. Ensure your endpoint returns 2xx status
  2. Check for server errors in your logs
  3. Verify your endpoint can handle the payload size

Next Steps