call.completed, call.recording.ready, and call_request.completed.
1. Register an endpoint
Requires thewebhooks:manage scope. Your URL must be HTTPS and publicly reachable.
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 withPATCH /webhook-endpoints/{endpointId}once your receiver is healthy.
2xx as fast as possible — enqueue the payload and process it out of band.
3. Verify the signature
The signature is computed as:- Parse
tandv1from the header. - Reject if
|now − t|exceeds 300 seconds (replay protection). - Compute
HMAC-SHA256(secret, t + "." + rawBody)over the raw, unparsed request bytes. - Compare against
v1with a constant-time comparison.
4. Pull the details you need
The webhook tells you that something happened; the API tells you what. Oncall.completed, fetch the call for its summary (and transcript, with transcripts:read + BAA):
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: