# Webhooks & Events

Receive real-time notifications when events occur in your Twig AI organization.

## What are Webhooks?

Webhooks are HTTP callbacks that notify your application when specific events happen. Instead of polling for changes, Twig AI pushes updates to your server.

**Benefits:**

* Real-time notifications
* No polling required
* Efficient resource usage
* Event-driven architecture

## Supported Events

### Interaction Events

| Event                   | Description            | Payload                |
| ----------------------- | ---------------------- | ---------------------- |
| `interaction.created`   | New question asked     | Interaction object     |
| `interaction.completed` | Response generated     | Interaction + response |
| `interaction.feedback`  | User provided feedback | Interaction + feedback |

### Agent Events

| Event           | Description                 | Payload                |
| --------------- | --------------------------- | ---------------------- |
| `agent.created` | New agent created           | Agent object           |
| `agent.updated` | Agent configuration changed | Agent object + changes |
| `agent.deleted` | Agent removed               | Agent ID               |

### Data Source Events

| Event                  | Description           | Payload            |
| ---------------------- | --------------------- | ------------------ |
| `datasource.created`   | New data source added | DataSource object  |
| `datasource.processed` | Processing completed  | DataSource + stats |
| `datasource.failed`    | Processing failed     | DataSource + error |

### User Events

| Event          | Description          | Payload               |
| -------------- | -------------------- | --------------------- |
| `user.created` | New user added       | User object           |
| `user.updated` | User details changed | User object + changes |

## Setting Up Webhooks

### Via UI

1. Navigate to **Settings** → **Webhooks**
2. Click **Create Webhook**
3. Configure:
   * **URL**: Your endpoint (must be HTTPS)
   * **Events**: Select events to receive
   * **Secret**: Auto-generated signing secret
4. Click **Create**
5. Test webhook
6. Save

### Via API

```bash
curl -X POST https://api.twig.so/api/webhooks \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-server.com/webhooks/twig",
    "events": [
      "interaction.completed",
      "agent.updated"
    ],
    "description": "Production webhook"
  }'
```

**Response:**

```json
{
  "id": "wh_abc123",
  "url": "https://your-server.com/webhooks/twig",
  "events": ["interaction.completed", "agent.updated"],
  "secret": "whsec_xyz789...",
  "status": "active",
  "createdAt": "2024-01-15T10:00:00Z"
}
```

## Webhook Payload

### Standard Format

All webhooks follow this structure:

```json
{
  "id": "evt_abc123",
  "type": "interaction.completed",
  "timestamp": "2024-01-15T10:30:00Z",
  "data": {
    // Event-specific data
  },
  "organizationId": "org-123"
}
```

### Event Examples

**Interaction Completed:**

```json
{
  "id": "evt_001",
  "type": "interaction.completed",
  "timestamp": "2024-01-15T10:30:00Z",
  "data": {
    "interaction": {
      "id": "int-456",
      "prompt": "What is your refund policy?",
      "response": "Our refund policy allows...",
      "agentId": "agent-123",
      "userId": "user-789",
      "sources": [...]
    }
  },
  "organizationId": "org-123"
}
```

**Data Source Processed:**

```json
{
  "id": "evt_002",
  "type": "datasource.processed",
  "timestamp": "2024-01-15T11:00:00Z",
  "data": {
    "dataSource": {
      "id": "ds-101",
      "name": "Product Docs",
      "type": "WEBSITE",
      "status": "COMPLETED",
      "stats": {
        "documentsProcessed": 245,
        "tokensProcessed": 125000,
        "duration": 180
      }
    }
  },
  "organizationId": "org-123"
}
```

## Receiving Webhooks

### Endpoint Implementation

**Node.js/Express:**

```javascript
const express = require('express');
const crypto = require('crypto');
const app = express();

app.post('/webhooks/twig', express.raw({type: 'application/json'}), (req, res) => {
  const signature = req.headers['x-twig-signature'];
  const payload = req.body;
  
  // Verify signature
  if (!verifySignature(payload, signature)) {
    return res.status(401).send('Invalid signature');
  }
  
  const event = JSON.parse(payload);
  
  // Handle event
  switch(event.type) {
    case 'interaction.completed':
      handleInteraction(event.data);
      break;
    case 'agent.updated':
      handleAgentUpdate(event.data);
      break;
    // Add more handlers
  }
  
  res.status(200).send('OK');
});

function verifySignature(payload, signature) {
  const secret = process.env.WEBHOOK_SECRET;
  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}
```

**Python/Flask:**

```python
from flask import Flask, request
import hmac
import hashlib

app = Flask(__name__)

@app.route('/webhooks/twig', methods=['POST'])
def webhook():
    signature = request.headers.get('X-Twig-Signature')
    payload = request.get_data()
    
    # Verify signature
    if not verify_signature(payload, signature):
        return 'Invalid signature', 401
    
    event = request.get_json()
    
    # Handle event
    if event['type'] == 'interaction.completed':
        handle_interaction(event['data'])
    elif event['type'] == 'agent.updated':
        handle_agent_update(event['data'])
    
    return 'OK', 200

def verify_signature(payload, signature):
    secret = os.environ['WEBHOOK_SECRET'].encode()
    expected = hmac.new(secret, payload, hashlib.sha256).hexdigest()
    return hmac.compare_digest(signature, expected)
```

## Security

### Signature Verification

**Always verify webhook signatures:**

```typescript
import crypto from 'crypto';

function verifyWebhookSignature(
  payload: string,
  signature: string,
  secret: string
): boolean {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
    
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );
}
```

### HTTPS Required

* All webhook URLs must use HTTPS
* Valid SSL certificate required
* Self-signed certificates not accepted

### IP Whitelisting

Webhook requests come from:

```
34.123.45.67/32
52.89.123.45/32
```

Add to firewall whitelist if needed.

## Best Practices

### 1. Respond Quickly

✅ Return 200 OK immediately ✅ Process asynchronously ✅ Use job queue for heavy processing

```javascript
app.post('/webhooks/twig', async (req, res) => {
  // Verify signature
  if (!verifySignature(req.body, req.headers['x-twig-signature'])) {
    return res.status(401).send('Invalid signature');
  }
  
  // Return 200 immediately
  res.status(200).send('OK');
  
  // Process asynchronously
  const event = JSON.parse(req.body);
  await queue.add('process-webhook', event);
});
```

### 2. Handle Retries

✅ Implement idempotency ✅ Store event IDs to detect duplicates ✅ Handle same event multiple times safely

```javascript
const processedEvents = new Set();

function handleWebhook(event) {
  // Check if already processed
  if (processedEvents.has(event.id)) {
    return; // Skip duplicate
  }
  
  // Process event
  processEvent(event);
  
  // Mark as processed
  processedEvents.add(event.id);
}
```

### 3. Error Handling

```javascript
try {
  await processWebhook(event);
} catch (error) {
  console.error('Webhook processing error:', error);
  // Log but don't fail
  // Twig will retry
}
```

### 4. Logging

✅ Log all webhook receipts ✅ Track processing time ✅ Monitor failure rates

```javascript
logger.info('Webhook received', {
  eventId: event.id,
  type: event.type,
  timestamp: event.timestamp
});
```

## Retry Logic

### Automatic Retries

Twig AI automatically retries failed webhooks:

| Attempt | Delay      | Total Time |
| ------- | ---------- | ---------- |
| 1       | Immediate  | 0s         |
| 2       | 5 seconds  | 5s         |
| 3       | 30 seconds | 35s        |
| 4       | 5 minutes  | 5m 35s     |
| 5       | 1 hour     | 1h 5m 35s  |

After 5 attempts, webhook is marked as failed.

### Handling Failed Webhooks

View failed webhooks:

1. Settings → Webhooks
2. Select webhook
3. View "Failed Deliveries"
4. Retry manually if needed

## Testing Webhooks

### Test Mode

```bash
curl -X POST https://api.twig.so/api/webhooks/wh_abc123/test \
  -H "Authorization: Bearer YOUR_API_KEY"
```

Sends test event to your endpoint.

### Local Development

Use tools like ngrok for local testing:

```bash
# Start ngrok
ngrok http 3000

# Update webhook URL to ngrok URL
https://abc123.ngrok.io/webhooks/twig
```

### Event Simulator

```bash
# Simulate interaction.completed event
curl -X POST https://your-server.com/webhooks/twig \
  -H "Content-Type: application/json" \
  -H "X-Twig-Signature: test_signature" \
  -d '{
    "id": "evt_test",
    "type": "interaction.completed",
    "timestamp": "2024-01-15T10:00:00Z",
    "data": {...}
  }'
```

## Monitoring

### Webhook Metrics

Track these metrics:

* Delivery success rate
* Average response time
* Retry rate
* Error types

### Dashboard

```
Webhook: Production
├─ Status: Active
├─ Success Rate: 99.2%
├─ Avg Response: 145ms
├─ Failed (24h): 3
└─ Last Delivery: 2 minutes ago
```

### Alerts

Configure alerts for:

* Failed deliveries > 10
* Response time > 5s
* Success rate < 95%

## Troubleshooting

### Webhook Not Received

**Check:**

1. Webhook is active
2. URL is correct and accessible
3. HTTPS with valid certificate
4. Events are subscribed
5. Firewall allows Twig IPs

### Signature Verification Fails

**Check:**

1. Using correct secret
2. Payload not modified
3. Using raw body (not parsed JSON)
4. Comparing hex digests correctly

### High Failure Rate

**Check:**

1. Server is responding within 10s
2. Returning 200 OK status
3. Not throwing errors
4. Handling retries correctly

## Next Steps

* [REST API Overview](/product/developer-api/overview.md) - API basics
* [Authentication](/product/developer-api/authentication.md) - Secure your webhooks
* [SDKs](/product/developer-api/sdks.md) - Use SDK webhook helpers
* [Rate Limits](/product/developer-api/rate-limits.md) - Webhook rate limits


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://help.twig.so/product/developer-api/webhooks.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
