ChartHero Developer Docs
Request accessAPI ReferenceOpenAPI JSON

Core Concepts

Guide

Verify webhook signatures

Validate ChartHero webhook headers, timestamps, and HMAC signatures.

Planned / not generally available

When to use this

Verify webhook signatures before parsing trusted business fields or fetching clinical follow-up resources. The check proves the delivery was signed with the endpoint signing secret and that the timestamp is within the documented tolerance.

Planned / not generally available: Runtime delivery for recording.transcript_ready is not generally available. Implement this verification pattern before a receiver is activated.

This page covers verification only. For the full receiver workflow, see Receive webhooks.

Required scopes

Signature verification does not use ChartHero API-key scopes. It requires the endpoint signing secret and these webhook headers:

Header Purpose
ChartHero-Event-Id Business idempotency key that must match body id.
ChartHero-Delivery-Id Stable delivery identifier for support/debug metadata and retry correlation.
ChartHero-Timestamp Decimal Unix epoch seconds used in the signature input.
ChartHero-Signature v1= HMAC-SHA256 digest for the delivery.
ChartHero-Webhook-Version Webhook version that must match body api_version.

Reject requests with missing headers, malformed timestamps, unsupported signature versions, or timestamps outside the allowed tolerance.

Request

Example signed delivery inputs:

cURL
curl -X POST "https://partner.example.test/chart-hero/webhooks" \
  -H "Content-Type: application/json" \
  -H "ChartHero-Event-Id: evt_recording_transcript_ready_01" \
  -H "ChartHero-Delivery-Id: whd_recording_transcript_ready_01" \
  -H "ChartHero-Timestamp: 1777649400" \
  -H "ChartHero-Signature: v1=a3bb0750eef64758be4a034aaff5dd5774c21bf7fe25083b51d61ba0214c0419" \
  -H "ChartHero-Webhook-Version: 2026-05-01" \
  --data-raw '{"id":"evt_recording_transcript_ready_01","type":"recording.transcript_ready","api_version":"2026-05-01","occurred_at":"2026-05-01T15:29:55Z","organization_id":"org_synthetic_webhook_001","resources":{"encounter_id":"enc_synthetic_webhook_001","document_id":"doc_synthetic_transcript_001"}}'

Build the signed payload as {timestamp}.{raw_request_body}. Use the exact body bytes received by the HTTP server, before JSON reserialization or whitespace normalization.

Text
1777649400.{"id":"evt_recording_transcript_ready_01","type":"recording.transcript_ready","api_version":"2026-05-01","occurred_at":"2026-05-01T15:29:55Z","organization_id":"org_synthetic_webhook_001","resources":{"encounter_id":"enc_synthetic_webhook_001","document_id":"doc_synthetic_transcript_001"}}

Compute the HMAC-SHA256 digest with the endpoint signing secret and compare it to the v1 value in ChartHero-Signature using a constant-time comparison.

Language-neutral verification steps:

  1. Read the raw request body bytes before JSON parsing.
  2. Read ChartHero-Timestamp and reject it if it is not decimal Unix epoch seconds.
  3. Reject the delivery if the timestamp is more than 300 seconds from your server clock.
  4. Build the signature input as {timestamp}.{raw_request_body}.
  5. Compute HMAC-SHA256 with the endpoint signing secret.
  6. Parse ChartHero-Signature and require the supported v1= value.
  7. Compare the computed digest and received digest with a constant-time comparison.
  8. Parse JSON only after the signature is valid.
  9. Verify ChartHero-Event-Id equals body id.
  10. Verify ChartHero-Webhook-Version equals body api_version.

Response

If verification and lightweight validation succeed, return any HTTP 2xx response. No JSON acknowledgement body is required.

HTTP
HTTP/1.1 204 No Content

If verification fails, do not process clinical follow-up work. Return a non-2xx response according to your receiver policy.

Common errors

After the signature is valid, parse the JSON body and verify these consistency checks:

Header Body field
ChartHero-Event-Id id
ChartHero-Webhook-Version api_version

Do not process clinical follow-up work when either check fails.

Other common verification failures include missing headers, unsupported signature versions, non-decimal timestamps, stale timestamps outside the 300 second tolerance, and comparing against a JSON-reserialized body instead of the raw request body.

Next steps