Skip to main content
The fastest way to react to calls is to let us push events to you. This guide registers a webhook endpoint, verifies signatures, and handles the three v1 events: call.completed, call.recording.ready, and call_request.completed.

1. Register an endpoint

Requires the webhooks:manage scope. Your URL must be HTTPS and publicly reachable.
The 201 response includes the endpoint’s HMAC signing secretthis is the only time it is shown. Store it next to your API key in a secret manager.

2. Understand a delivery

Every delivery is an HTTPS POST with a JSON body and two headers: The event type is carried in the body envelope’s type field — route on that after verifying the signature. The body is an envelope:
call_request_id and contact_external_ref appear only when the call originated from one of your call requests (and, for the latter, when you supplied external_ref on the contact). Payloads are deliberately PHI-minimal — IDs, outcomes, and API links only. To get the summary, transcript, or recording, follow the link with your API key (subject to your scopes and BAA). Delivery semantics:
  • At-least-once — the same event can arrive more than once. De-duplicate on X-GrowDental-Delivery-Id.
  • Unordered — use the envelope’s created_at, not arrival order.
  • Retried — non-2xx responses and timeouts (10 s) are retried with exponential backoff. Endpoints that fail persistently are auto-disabled (status: "disabled"); re-enable with PATCH /webhook-endpoints/{endpointId} once your receiver is healthy.
Respond 2xx as fast as possible — enqueue the payload and process it out of band.

3. Verify the signature

The signature is computed as:
To verify:
  1. Parse t and v1 from the header.
  2. Reject if |now − t| exceeds 300 seconds (replay protection).
  3. Compute HMAC-SHA256(secret, t + "." + rawBody) over the raw, unparsed request bytes.
  4. Compare against v1 with a constant-time comparison.
Verify against the raw request body, byte for byte. If your framework parses JSON before you can read the raw body (or re-serializes it), the HMAC will not match — configure a raw-body route for the webhook path, as both samples below do.

4. Pull the details you need

The webhook tells you that something happened; the API tells you what. On call.completed, fetch the call for its summary (and transcript, with transcripts:read + BAA):
On call.recording.ready, fetch the audio (requires recordings:read + BAA). The endpoint answers 302 with a short-lived signed URL — let your HTTP client follow it, and never persist the redirect target:

Troubleshooting