> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sendpilot.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Setting Up Webhooks

> Configure webhook subscriptions in SendPilot

This guide walks you through setting up webhook subscriptions to receive real-time event notifications.

## Creating a Webhook Subscription

### Via Dashboard

1. Log in to your SendPilot account
2. Navigate to **Integrations** → **Webhooks**
3. Click **Add Webhook**
4. Configure your webhook:
   * **Name**: A descriptive name for this webhook
   * **URL**: Your HTTPS endpoint that will receive events
   * **Events**: Select which events to subscribe to
5. Click **Create**
6. Copy your webhook secret for signature verification

### Via API

You can also manage webhooks programmatically through the SendPilot dashboard API.

## Endpoint Requirements

Your webhook endpoint must:

* Accept **HTTP POST** requests
* Use **HTTPS** (HTTP is not supported for security)
* Respond with a **2xx status code** within 30 seconds
* Accept **JSON** content type

### Example Endpoint (Node.js/Express)

```javascript theme={null}
const express = require('express');
const crypto = require('crypto');
const app = express();

// Parse raw body for signature verification
app.use('/webhooks', express.raw({ type: 'application/json' }));

app.post('/webhooks/sendpilot', (req, res) => {
  // Verify signature
  const signature = req.headers['webhook-signature'];
  const secret = process.env.WEBHOOK_SECRET;
  
  if (!verifySignature(req.body, signature, secret)) {
    return res.status(401).send('Invalid signature');
  }
  
  // Parse the event
  const event = JSON.parse(req.body);
  
  // Handle the event asynchronously
  handleEvent(event).catch(console.error);
  
  // Respond immediately
  res.status(200).send('OK');
});

function verifySignature(payload, signature, secret) {
  const parts = signature.split(',');
  const timestamp = parts.find(p => p.startsWith('t=')).slice(2);
  const providedSig = parts.find(p => p.startsWith('s=')).slice(2);
  
  const signedPayload = `${timestamp}.${payload}`;
  const expectedSig = crypto
    .createHmac('sha256', secret)
    .update(signedPayload)
    .digest('hex');
  
  return crypto.timingSafeEqual(
    Buffer.from(providedSig),
    Buffer.from(expectedSig)
  );
}

async function handleEvent(event) {
  console.log(`Received ${event.eventType}:`, event.data);
  
  switch (event.eventType) {
    case 'message.received':
      // Handle reply from lead
      await notifySalesTeam(event.data);
      break;
    case 'connection.accepted':
      // Handle new connection
      await syncToCRM(event.data);
      break;
    // ... handle other events
  }
}

app.listen(3000);
```

### Example Endpoint (Python/Flask)

```python theme={null}
from flask import Flask, request, abort
import hmac
import hashlib
import json

app = Flask(__name__)
WEBHOOK_SECRET = os.environ.get('WEBHOOK_SECRET')

@app.route('/webhooks/sendpilot', methods=['POST'])
def handle_webhook():
    # Verify signature
    signature = request.headers.get('Webhook-Signature')
    if not verify_signature(request.data, signature, WEBHOOK_SECRET):
        abort(401)
    
    # Parse event
    event = request.get_json()
    
    # Handle asynchronously (use Celery, RQ, etc. in production)
    handle_event(event)
    
    return 'OK', 200

def verify_signature(payload, signature, secret):
    parts = dict(p.split('=') for p in signature.split(','))
    timestamp = parts['t']
    provided_sig = parts['s']
    
    signed_payload = f"{timestamp}.{payload.decode()}"
    expected_sig = hmac.new(
        secret.encode(),
        signed_payload.encode(),
        hashlib.sha256
    ).hexdigest()
    
    return hmac.compare_digest(provided_sig, expected_sig)

def handle_event(event):
    event_type = event['eventType']
    data = event['data']
    
    if event_type == 'message.received':
        notify_sales_team(data)
    elif event_type == 'connection.accepted':
        sync_to_crm(data)
    # ... handle other events

if __name__ == '__main__':
    app.run(port=3000)
```

## Testing Webhooks

### Using the Test Feature

1. Go to **Integrations** → **Webhooks** in the dashboard
2. Find your webhook subscription
3. Click **Send Test Event**
4. Select an event type
5. Check your endpoint for the test payload

### Local Development

For local development, use a tunnel service to expose your local server:

```bash theme={null}
# Using ngrok
ngrok http 3000

# Your webhook URL will be:
# https://abc123.ngrok.io/webhooks/sendpilot
```

<Warning>
  Remember to update your webhook URL to your production endpoint before going live.
</Warning>

## Managing Subscriptions

### Update Events

To change which events trigger your webhook:

1. Go to **Integrations** → **Webhooks**
2. Click on the webhook to edit
3. Select or deselect event types
4. Save changes

### Rotate Secrets

To rotate your webhook secret:

1. Go to **Integrations** → **Webhooks**
2. Click on the webhook to edit
3. Click **Rotate Secret**
4. Update your server with the new secret
5. Confirm the rotation

<Note>
  After rotation, both the old and new secrets will work for 24 hours to allow for seamless updates.
</Note>

### Disable/Delete

To temporarily disable or permanently delete a webhook:

1. Go to **Integrations** → **Webhooks**
2. Click on the webhook
3. Toggle **Enabled** to disable, or click **Delete** to remove

## Monitoring

### Delivery Logs

View webhook delivery history:

1. Go to **Integrations** → **Webhooks**
2. Click on a webhook
3. View the **Delivery History** tab

Each delivery shows:

* Event type and ID
* HTTP response code
* Response time
* Retry count (if any)

### Failed Deliveries

For failed deliveries, you can:

* View the error message
* See retry attempts
* Manually retry the delivery

## Troubleshooting

<AccordionGroup>
  <Accordion title="Not receiving webhooks">
    1. Verify your endpoint URL is correct and uses HTTPS
    2. Check that your server is publicly accessible
    3. Ensure the webhook is enabled
    4. Check server logs for incoming requests
  </Accordion>

  <Accordion title="Signature verification failing">
    1. Verify you're using the correct webhook secret
    2. Ensure you're verifying against the raw request body
    3. Check that timestamps haven't expired (5 minute tolerance)
  </Accordion>

  <Accordion title="Timeouts">
    1. Ensure your endpoint responds within 30 seconds
    2. Process events asynchronously
    3. Return 200 immediately, handle logic in background
  </Accordion>

  <Accordion title="Duplicate events">
    1. Use the `eventId` field for idempotency
    2. Store processed event IDs in your database
    3. Skip events that have already been processed
  </Accordion>
</AccordionGroup>
