Skip to main content

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:
{
  "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):
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

EventDescription
call.startedCall has started
call.joinedParticipant joined the call
call.endedCall ended (also sent as call.completed and normalized to call.ended)
call.billedCall billing event
call.failedCall failed
batch.status.changedBatch call status changed
batch.completedBatch completed
voice.training.completedVoice training finished successfully
voice.training.failedVoice 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?