NOVA Developer Docs

Reference for the public NOVA API. Authenticated endpoints expect Authorization: Bearer <jwt>.

Webhooks

Receive real-time notifications when call lifecycle events occur. Register an HTTPS URL and we POST a signed JSON payload on each subscribed event.

Events

Register a webhook

POST /v1/webhooks
Authorization: Bearer <your-jwt>
Content-Type: application/json

{
  "url": "https://hooks.example.com/nova",
  "events": ["call.started", "call.ended", "call.transcribed", "call.scored"]
}

The response includes a secret — save it immediately. We store only its SHA-256 hash; the raw value is never retrievable after this response.

{
  "id": "8f3b2a4c-1234-...",
  "url": "https://hooks.example.com/nova",
  "events": ["call.started", "call.ended", "call.transcribed", "call.scored"],
  "secret": "raw-token-shown-once-save-it-now",
  "is_active": true,
  "created_at": "2026-05-10T08:30:00Z"
}

Payload shape

POST <your-url>
Content-Type: application/json
X-Nova-Event: call.ended
X-Nova-Signature: sha256=<hex_hmac>
User-Agent: NOVA-Webhooks/1.0

{
  "event": "call.ended",
  "delivered_at": "2026-05-10T08:30:00Z",
  "data": {
    "call_id": "uuid",
    "customer_id": "uuid",
    "started_at": "2026-05-10T08:27:00Z",
    "ended_at": "2026-05-10T08:30:00Z",
    "duration_seconds": 187,
    "outcome": "qualified",
    "goal": "qualify_interest",
    "goal_completed": true,
    "to_phone_e164": "+15555550100"
  }
}

Verifying the signature

The X-Nova-Signature header is an HMAC-SHA256 computed over the raw request body. The HMAC key is the SHA-256 hash of your raw secret — hash your stored token first, then HMAC.

Python

import hashlib
import hmac

def verify(raw_secret: str, body: bytes, header: str) -> bool:
    key = hashlib.sha256(raw_secret.encode()).hexdigest()
    expected = "sha256=" + hmac.new(
        key.encode(), body, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, header)

Node

const crypto = require("crypto");

function verify(rawSecret, body, header) {
  const key = crypto.createHash("sha256")
    .update(rawSecret).digest("hex");
  const expected = "sha256=" + crypto
    .createHmac("sha256", key)
    .update(body).digest("hex");
  return crypto.timingSafeEqual(
    Buffer.from(expected), Buffer.from(header)
  );
}

Retries

Non-2xx responses and network errors are retried with exponential backoff: 1s, 5s, 30sup to 4 attempts total. Each attempt has a 10-second timeout. After the final failure the delivery is marked failed and not retried.

Debugging deliveries

GET /v1/webhooks/{id}/deliveries?limit=50
Authorization: Bearer <your-jwt>

Returns the most recent delivery rows with status_code, attempt_count, delivered_at, and error_message. Use POST /v1/webhooks/{id}/test to send a fake call.started payload end-to-end.

Other endpoints

Constraints