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 secret — this 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