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

# Webhooks

> Receive real-time events from the Truedy API

# Webhooks

Truedy can send HTTP callbacks to your server when certain events occur (e.g. call completed, campaign status).

## Overview

* **Configuration**: Webhook URL and signing secret are configured in the **Truedy Dashboard** (e.g. Settings or Integrations). There is no Public API endpoint to create or update webhook endpoints; use the dashboard only.
* Truedy sends **POST** requests to your URL with a JSON body and signature headers.
* Respond with **2xx** quickly; process the payload asynchronously if needed.

## Payload format

Each webhook request has:

* **Body**: JSON object with at least:
  * **event** (string): Event type (e.g. `call.ended`, `batch.completed`).
  * **timestamp** (string): ISO 8601 or Unix timestamp when the event occurred.
  * **data** (object): Event-specific payload (e.g. call id, status, batch id).
  * **id** or **event\_id** (optional): Unique identifier for this delivery. Use it to deduplicate if the same event is retried.

Example:

```json theme={null}
{
  "event": "call.ended",
  "timestamp": "2026-03-03T12:00:00Z",
  "data": {
    "call_id": "uuid",
    "status": "completed",
    "agent_id": "uuid",
    "duration_seconds": 120
  },
  "id": "evt_abc123"
}
```

* **Headers**:
  * **X-Truedy-Signature**: HMAC-SHA256 signature (see below).
  * **X-Truedy-Timestamp**: Timestamp used in the signature (Unix seconds or ISO string, depending on implementation).

## Signature verification

Verify the request before trusting it:

1. Read the raw request body as a string (do not parse JSON and re-serialize; use the exact bytes received).
2. Get the **X-Truedy-Timestamp** and **X-Truedy-Signature** headers.
3. Build the signed message: `message = timestamp + "." + raw_body` (timestamp and body concatenated with a dot).
4. Compute `HMAC-SHA256(message, secret)` using your webhook signing secret (hex-encoded).
5. Compare the result with **X-Truedy-Signature** using a constant-time comparison (e.g. `hmac.compare_digest`).

Example (Python):

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

def verify_webhook(raw_body: bytes, signature: str, timestamp: str, secret: str) -> bool:
    message = f"{timestamp}.{raw_body.decode('utf-8')}"
    expected = hmac.new(
        secret.encode("utf-8"),
        message.encode("utf-8"),
        hashlib.sha256,
    ).hexdigest()
    return hmac.compare_digest(signature, expected)
```

Reject the request if the signature does not match or if the timestamp is too old (e.g. older than 5 minutes) to limit replay attacks.

## Event types

| Event                      | Description                                                               |
| -------------------------- | ------------------------------------------------------------------------- |
| `call.started`             | Call has started                                                          |
| `call.joined`              | Participant joined the call                                               |
| `call.ended`               | Call ended (also sent as `call.completed` and normalized to `call.ended`) |
| `call.billed`              | Call billing event                                                        |
| `call.failed`              | Call failed                                                               |
| `batch.status.changed`     | Batch call status changed                                                 |
| `batch.completed`          | Batch completed                                                           |
| `voice.training.completed` | Voice training finished successfully                                      |
| `voice.training.failed`    | Voice training failed                                                     |

Subscribe only to the events you need when configuring your webhook in the dashboard.

## Idempotency and retries

* **Deduplication**: The same event may be delivered more than once (e.g. after a retry). Use the payload's **id** or **event\_id** (when present) to detect duplicates and process each event only once.
* **Retries**: If your endpoint returns a non-2xx status or times out, Truedy may retry with exponential backoff. Implement idempotent handling so duplicate deliveries are safe (e.g. store processed event IDs and skip if already seen).

## Security

1. **Verify the signature** using the shared secret and the steps above.
2. Use **HTTPS** only for your webhook URL.
3. **Timestamp**: Reject requests whose timestamp is outside an acceptable window (e.g. ±5 minutes).

## Need help?

* Configure webhook URL and secret in the [Truedy Dashboard](https://app.truedy.ai).
* Check [Error Handling](/guides/error-handling) for how to log and report issues using `request_id`.
