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

# Webhooks

> Receive real-time event notifications when your ontology changes

## Overview

Webhooks deliver real-time HTTP POST notifications to your endpoint when events occur in your Doris workspace. Configure webhooks in the console at **Settings > Developer > Webhooks**.

Each tenant can have up to 10 webhook subscriptions. Any member can view configured webhooks and delivery history; creating, editing, deleting, and testing webhooks requires a Doris admin role.

## Event Types

Subscribe to specific event types using glob patterns:

| Pattern              | Matches                                   |
| -------------------- | ----------------------------------------- |
| `meeting.processed`  | Meeting processing completed              |
| `deal.*`             | All deal events (updated, stage\_changed) |
| `deal.stage_changed` | Deal moved to a new pipeline stage        |
| `commitment.created` | New commitment extracted from a meeting   |
| `commitment.overdue` | Commitment past its due date              |
| `email.classified`   | Email classified by the deal engine       |

## Payload Format

Every webhook delivery is an HTTP POST with a JSON body:

### Property change events

```json theme={null}
{
  "event_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "event_type": "deal.stage_changed",
  "timestamp": "2026-05-05T14:30:00Z",
  "entity": {
    "type": "deal",
    "id": "sf-0061a000012345"
  },
  "changes": {
    "stage": {
      "old": "Discovery",
      "new": "Presentation of Value"
    }
  },
  "context": {
    "provider": "salesforce"
  }
}
```

### Semantic events

```json theme={null}
{
  "event_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
  "event_type": "meeting.processed",
  "timestamp": "2026-05-05T15:00:00Z",
  "entity": {
    "type": "meeting",
    "id": "uuid-of-meeting"
  },
  "data": {
    "title": "Acme Weekly Sync",
    "status": "completed",
    "deal_id": "sf-0061a000012345"
  }
}
```

## Headers

Every delivery includes these headers:

| Header              | Value                                   |
| ------------------- | --------------------------------------- |
| `Content-Type`      | `application/json`                      |
| `X-Doris-Signature` | HMAC-SHA256 signature (see below)       |
| `X-Doris-Event`     | Event type (e.g., `deal.stage_changed`) |
| `X-Doris-Delivery`  | Unique delivery ID                      |

## Verifying Signatures

Each webhook has a signing secret (shown once at creation, starts with `whsec_`). To verify a delivery is authentic:

```python theme={null}
import hmac
import hashlib

def verify_signature(payload_body: bytes, signature_header: str, secret: str) -> bool:
    expected = "sha256=" + hmac.HMAC(
        secret.encode(), payload_body, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature_header)
```

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

function verifySignature(body, signatureHeader, secret) {
  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(body)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signatureHeader)
  );
}
```

<Note>
  Signature verification is optional. If you're using n8n, Power Automate, or Zapier, you can safely ignore the `X-Doris-Signature` header — the payload will be delivered as standard JSON regardless.
</Note>

## Retry Policy

If your endpoint returns a non-2xx status code or is unreachable:

| Attempt   | Delay      |
| --------- | ---------- |
| 1st retry | 1 minute   |
| 2nd retry | 5 minutes  |
| 3rd retry | 15 minutes |

After 3 failed attempts, the delivery is marked as failed. You can see delivery history in the console.

## Responding

Return any `2xx` status code to acknowledge receipt. The response body is ignored. If your endpoint takes longer than 10 seconds to respond, the delivery is treated as failed and will be retried.

## Testing

Use the **Test** button in the console to send a `test.ping` event to your endpoint. This verifies your URL is reachable and responding with a 2xx status.
