# Webhooks

Receive real-time HTTP notifications when events occur in your LVNG workspace. Webhooks let you build integrations that react to messages, agent activity, workflow completions, and more.

## Event Types

| Event | Description |
|-------|-------------|
| `message.created` | A new message was sent in a channel |
| `message.updated` | A message was edited |
| `message.deleted` | A message was deleted |
| `agent.started` | An agent started running |
| `agent.completed` | An agent finished its task |
| `agent.error` | An agent encountered an error |
| `workflow.started` | A workflow execution began |
| `workflow.completed` | A workflow execution finished |
| `workflow.failed` | A workflow execution failed |
| `member.joined` | A user joined a workspace or channel |
| `member.left` | A user left a workspace or channel |

## Payload Format

All webhook payloads are JSON with a consistent structure: `event` type, `timestamp`, and `data` object.

```json
{
  "event": "message.created",
  "timestamp": "2026-03-15T10:30:00Z",
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "channel_id": "660e8400-e29b-41d4-a716-446655440000",
    "content": "Hello from LVNG!",
    "sender_id": "770e8400-e29b-41d4-a716-446655440000",
    "content_type": "text",
    "created_at": "2026-03-15T10:30:00Z"
  }
}
```

## Verifying Signatures

Every webhook request includes an `X-LVNG-Signature` header containing an HMAC-SHA256 signature of the payload. Always verify this signature to ensure the request is from LVNG.

```typescript
const crypto = require('crypto');

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

// In your Express handler:
app.post('/webhooks/lvng', (req, res) => {
  const signature = req.headers['x-lvng-signature'];
  const isValid = verifyWebhook(
    JSON.stringify(req.body),
    signature,
    process.env.WEBHOOK_SECRET
  );

  if (!isValid) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  // Process the event
  const { event, data } = req.body;
  console.log('Received event:', event, data);

  res.status(200).json({ received: true });
});
```

## Best Practices

- **Respond quickly** -- Return a 2xx status code within 5 seconds. Process events asynchronously if needed.
- **Handle duplicates** -- Webhooks may be delivered more than once. Use the event ID for idempotency.
- **Verify signatures** -- Always validate the X-LVNG-Signature header before processing events.
- **Use HTTPS** -- Webhook endpoints must use HTTPS. HTTP endpoints will be rejected.
