---
name: suki-webhooks
description: >-
  Use when implementing Suki partner webhooks for Ambient, Form filling, and
  CKG FHIR ingestion notifications, including callback configuration, HMAC
  signature verification, payload handling, and delivery retries.
license: SUKI_Proprietary
metadata:
  author: suki
  version: "2.4"
---

## Webhooks

Use Suki partner webhooks to receive asynchronous notifications for Ambient,
Form filling, and CKG FHIR ingestion workflows.

Suki webhooks notify your application when supported asynchronous events occur.
Use the webhook payload to identify the event and, when required, use the
appropriate Suki API to retrieve additional data.

## Accuracy rules

- Verify the webhook signature before parsing or processing the JSON body.
- Verify the signature using the exact raw request body Suki sent.
- Do not re-serialize, trim, pretty-print, or otherwise modify the body before
  signature verification.
- Ambient, Form filling, and CKG FHIR ingestion notifications use different
  payload schemas. Do not treat them as interchangeable.
- Do not invent undocumented event, `status`, or `state` values.
- Do not confuse Ambient webhook event values with Ambient session status API
  values.
- Do not infer fields from another Suki workflow.
- If the exact payload shape or field meaning is unclear, read the relevant
  webhook payload and event-type documentation before writing parsing logic.
- Keep the partner secret key on the server only.

## Webhook configuration

Suki configures the webhook callback URL at the partner level during
onboarding.

- Configure one callback URL per partner.
- The callback URL must be publicly reachable.
- The callback URL must use HTTPS with TLS 1.2 or higher.
- Do not use `localhost` or a private IP address for webhook delivery.
- The same partner webhook configuration supports the webhook events documented
  for the partner, including Ambient, Form filling, and CKG FHIR ingestion.
- Webhook configuration is handled during partner onboarding rather than
  through a self-service API.

## Request verification

Suki sends webhook notifications as HTTP `POST` requests.

The request includes:

- `Content-Type: application/json`
- `generated-at`
- `X-API-Key`

The partner secret is used to calculate the HMAC signature. Keep this secret
on the server and never expose it to browser or mobile clients.

### Verification order

Verify the signature before parsing JSON.

Use this order:

```text
receive POST
→ read raw request body
→ read generated-at
→ read X-API-Key
→ calculate HMAC-SHA-256
→ compare signatures
→ reject invalid requests
→ parse JSON
→ validate payload
→ process the event
````

### Signature calculation

Build the message to sign by concatenating:

```text
generated-at + ":" + raw_request_body
```

Calculate an HMAC-SHA-256 digest using the partner secret as the HMAC key.

Convert the digest to hexadecimal and compare it with `X-API-Key`.

Pseudocode:

```text
raw_body = read request body exactly as received
generated_at = request header "generated-at"
provided_signature = request header "X-API-Key"

message = generated_at + ":" + raw_body

computed_signature =
    hex(HMAC_SHA256(key=partner_secret, data=message))

if not constant_time_equal(computed_signature, provided_signature):
    return 401 or 400

parse JSON
continue processing
```

Use a constant-time comparison when comparing the computed signature with
`X-API-Key`.

### Raw request body

The signature is calculated from the exact request body Suki sends.

Do not:

* Parse JSON before signature verification.
* Parse and re-serialize the JSON before verification.
* Pretty-print the JSON.
* Trim whitespace.
* Change JSON formatting.
* Reorder JSON fields.

If your framework automatically parses JSON before your webhook handler runs,
configure the webhook route to provide the raw request body.

### Replay protection

The `generated-at` value is a Unix timestamp in milliseconds.

Reject requests where `generated-at` is older than two minutes.

Use the timestamp together with HMAC verification to prevent replayed webhook
requests.

### Invalid signatures

If the signature does not match:

* Stop processing the request.
* Do not parse or process the payload.
* Do not run application business logic.
* Return an appropriate 4xx response, such as `401` or `400`.

## Response and retries

Your webhook endpoint must return HTTP `200 OK` within 30 seconds for a
successfully received webhook.

Suki retries webhook delivery when the request fails, including when:

* The endpoint is unavailable.
* The request takes longer than 30 seconds.
* The endpoint returns a non-200 HTTP response.

Suki retries a failed delivery up to four additional times, for a maximum of
five delivery attempts.

Design webhook processing to be idempotent because the same event can be
delivered more than once.

Suki does not store or queue webhook notifications for later delivery. Your
application is responsible for receiving and processing notifications.

### Processing pattern

For processing that completes quickly:

```text
verify
→ parse and validate
→ process
→ return 200
```

For processing that may take longer:

```text
verify
→ parse and validate
→ persist or enqueue
→ return 200
→ process asynchronously
```

Do not perform long-running work before returning the webhook response if it
could cause the endpoint to exceed the 30-second response requirement.

Persist or enqueue the validated event before returning `200` when downstream
processing must happen asynchronously.

## Event families

Ambient, Form filling, and CKG FHIR ingestion notifications have different
payload structures.

Identify the event family before accessing event-specific fields.

Do not assume that a field documented for one event family exists in another.

## Ambient webhooks

Ambient webhook events use a top-level `status` field for the documented
completion and failure events.

### Ambient completion

A successful Ambient completion uses:

```json
{
  "status": "success"
}
```

The payload includes:

* `session_id`
* `encounter_id`

The payload can also include:

* `sessions`
* `additional_info`
* `_links`

The `_links` object can provide links for resources such as:

* `contents`
* `encounter_content`
* `structured_data`
* `encounter_structured_data`
* `status`
* `transcripts`

Use only the links present in the payload.

Do not assume every Ambient completion event contains every link.

### Ambient failure

A failed Ambient event uses:

```json
{
  "status": "failure"
}
```

The payload includes:

* `session_id`
* `encounter_id`
* `error_code`
* `error_detail`

Use `error_code` and `error_detail` for application error handling and
logging.

Do not invent error codes or additional failure fields.

### Ambient timeout

Ambient also sends a separate webhook notification when a session times out.

The documented timeout payload includes:

* `session_id`
* `encounter_id`

Do not invent a timeout `status` value.

If the application needs additional information about the session, use the
appropriate Ambient session API.

### Ambient cancellation

Ambient also sends a separate webhook notification when a session is
cancelled.

The documented cancellation payload includes:

* `session_id`
* `encounter_id`

Do not invent a cancellation `status` value such as `cancelled`, `canceled`,
or `timeout`.

If the application needs the Ambient session status, use the Ambient session
status API.

### Ambient webhook status vs. session status

Do not confuse webhook event values with values returned by the Ambient
session status API.

Ambient webhook events can use values such as:

```text
success
failure
```

The Ambient session status API has its own status values, including values such
as:

```text
created
ready
running
aborted
skipped
failed
completed
```

Only use a value in the API or webhook where Suki documents that value.

## Form filling webhooks

Form filling uses the partner webhook configuration and has its own webhook
payload schema.

Do not infer Form filling payload fields from Ambient webhook payloads.

When handling a Form filling webhook:

1. Verify the HMAC signature.
2. Parse the payload only after verification.
3. Identify the documented Form filling event.
4. Use only the fields defined for that Form filling event.
5. Use the appropriate Form filling API to retrieve additional data when
   required.

### Form filling session identifier

The Form filling API uses:

```text
ambient_session_id
```

as the Form filling session identifier.

The Ambient API also uses the field name `ambient_session_id`.

Do not assume that an `ambient_session_id` from Form filling and an
`ambient_session_id` from Ambient represent the same type of session.

Always interpret the identifier in the context of the API that created it.

### Form filling output

Form filling workflows can provide structured form output.

Do not assume that Form filling notifications contain the same content,
transcript, or link structure as Ambient notifications.

Use the documented Form filling structured-data API and webhook payload for
retrieving and processing Form filling output.

## CKG FHIR ingestion webhooks

CKG FHIR ingestion notifications use a different payload structure from
Ambient and Form filling notifications.

CKG ingestion webhooks use a top-level:

```text
state
```

They do not use the Ambient webhook `status` field.

The documented terminal states are:

```text
COMPLETED
FAILED
ABORTED
```

The payload includes:

* `transaction_id`
* `organization_id`
* `correlation_id`
* `state`

### `COMPLETED`

`COMPLETED` means the FHIR ingestion job completed successfully.

Use `transaction_id` and `correlation_id` to identify and correlate the
ingestion workflow.

### `FAILED`

`FAILED` means the ingestion job failed.

The payload can include an `error` object with additional failure information.

Use the documented error fields. Do not invent error codes or messages.

### `ABORTED`

`ABORTED` means the ingestion job was aborted before completion.

The payload can include an `error` object.

Do not treat CKG `state` values as Ambient webhook `status` values.

For example, do not write logic such as:

```text
if payload.state == "success":
```

Use the documented CKG values instead:

```text
if payload.state == "COMPLETED":
```

## Follow-up processing

A webhook notification tells your application that an event occurred. It does
not necessarily contain all data your application needs.

When additional data is required:

1. Identify the event family.
2. Use the identifiers or links provided by that event.
3. Call the appropriate Suki API.
4. Authenticate the API request according to that API's requirements.
5. Process the returned data.

For Ambient completion events, the `_links` object can provide links to:

* Session content
* Encounter content
* Structured data
* Encounter structured data
* Session status
* Transcripts

Do not assume `_links` exists for every webhook event.

Do not assume that CKG ingestion notifications contain the Ambient `_links`
structure.

## Idempotency

Webhook deliveries can be retried. Your receiver must tolerate duplicate
notifications.

A safe processing pattern is:

```text
receive webhook
→ verify signature
→ identify event
→ check whether the event has already been accepted
→ persist or enqueue new work
→ return 200
→ process downstream work
```

Use an identifier documented for the relevant workflow when tracking
processing.

Examples include:

* Ambient `session_id`
* Ambient `encounter_id`
* Form filling `ambient_session_id`
* CKG `transaction_id`

Do not assume these identifiers are globally unique across different webhook
families.

Do not invent an event ID when the payload does not provide one.

## Security requirements

Always:

* Use HTTPS.
* Require TLS 1.2 or higher.
* Verify HMAC-SHA-256 before trusting the payload.
* Read the raw request body before JSON parsing.
* Use `generated-at` and `X-API-Key` as documented.
* Use constant-time comparison for the signature.
* Enforce the two-minute `generated-at` replay window.
* Keep the partner secret in a secure server-side secret store.
* Never expose the partner secret in browser or mobile client code.
* Never commit the partner secret to source control.
* Validate the payload after signature verification.
* Avoid logging the raw clinical payload unnecessarily.
* Avoid logging the partner secret.

## Common implementation mistakes

### Parsing JSON before signature verification

Wrong:

```text
request
→ parse JSON
→ re-serialize JSON
→ calculate HMAC
```

Correct:

```text
request
→ read raw body
→ calculate HMAC
→ compare signature
→ parse JSON
```

### Signing a re-serialized body

Do not calculate the signature over a new JSON representation.

The signature must use the exact raw body Suki sent.

### Using the wrong status field

Ambient webhook events use:

```text
status
```

CKG FHIR ingestion uses:

```text
state
```

Do not map values between these fields.

### Inventing timeout or cancellation status values

Do not assume timeout or cancellation events use values such as:

```text
timeout
cancelled
canceled
```

Use the event type and fields documented for the webhook.

### Confusing Form filling and Ambient identifiers

Both APIs can use:

```text
ambient_session_id
```

Do not assume the identifier has the same meaning across the two APIs.

Use the identifier in the context of the API that created the session.

### Returning non-200 after accepting a webhook

A non-200 response causes the delivery to be treated as unsuccessful and can
result in a retry.

Return `200` after the webhook has been successfully verified, validated, and
accepted for processing.

### Doing long-running work before acknowledging

Do not block the webhook request with expensive processing that can exceed the
30-second response requirement.

Persist or enqueue the validated event and process it asynchronously when
needed.

## Implementation checklist

Before generating or reviewing webhook receiver code, verify:

* [ ] The callback URL is publicly reachable over HTTPS.
* [ ] TLS 1.2 or higher is enabled.
* [ ] The partner secret is stored server-side.
* [ ] The handler reads the raw request body.
* [ ] `generated-at` is read from the request.
* [ ] `X-API-Key` is read from the request.
* [ ] HMAC-SHA-256 is calculated over `generated-at + ":" + raw_body`.
* [ ] The computed signature is compared using a constant-time comparison.
* [ ] Requests older than two minutes are rejected.
* [ ] Invalid signatures are rejected before JSON parsing or business logic.
* [ ] The payload is parsed only after signature verification.
* [ ] The webhook event family is identified correctly.
* [ ] Ambient webhook fields are handled according to the Ambient webhook
  schema.
* [ ] Form filling fields are handled according to the Form filling webhook
  schema.
* [ ] CKG uses `state`, not Ambient `status`.
* [ ] Only documented event/status/state values are used.
* [ ] Timeout and cancellation events are not given invented status values.
* [ ] Processing is idempotent.
* [ ] The endpoint returns HTTP 200 within 30 seconds.
* [ ] Long-running work is persisted or enqueued before asynchronous
  processing.
* [ ] Follow-up API calls use the correct Suki API and authentication.
* [ ] Secrets and sensitive clinical payloads are not unnecessarily logged.

## When to read more

<!-- SKILL-AUTO:START canonical-docs -->
- [Webhook Quickstart](https://developer.suki.ai/documentation/webhook/quickstart): Step-by-step guide to configure, implement, and test a Suki notification Webhook endpoint
- [Notification Webhook Overview](https://developer.suki.ai/documentation/webhook/overview): Learn how Suki pushes notifications to your application via Notification Webhook and how push compares to pull
- [Webhook Signature Verification](https://developer.suki.ai/documentation/webhook/signature-verification): Verify Webhook request signatures with HMAC, X-API-Key, and generated-at headers before processing callback payloads
- [Webhook Payload & Response](https://developer.suki.ai/documentation/webhook/payload-and-response): Parse Webhook POST request headers, JSON payload fields, _links, and expected callback response formats for session notifications
- [Webhook Event Types](https://developer.suki.ai/documentation/webhook/event-types): Event types your Webhook endpoint receives when Ambient sessions complete, fail, time out, or are cancelled
- [Webhook Configuration](https://developer.suki.ai/documentation/webhook/configuration): Register your Webhook callback URL during partner onboarding and meet endpoint requirements for receiving Suki notifications
- [Build Webhook Notification Receiver](https://developer.suki.ai/documentation/tutorials/webhook-notification-receiver): Use-case tutorial: Build a partner webhook receiver that verifies HMAC signatures and handles success and failure notifications
- [Asynchronous Notifications](https://developer.suki.ai/api-reference/asynchronous/webhook): Webhook endpoint for receiving asynchronous notifications from Suki platform
- [llms-full.txt](https://developer.suki.ai/llms-full.txt): Full-site dump for broad cross-product questions
<!-- SKILL-AUTO:END canonical-docs -->


## Sister skills

<!-- SKILL-AUTO:START sister-skills -->
* Ambient:
  [https://developer.suki.ai/.well-known/agent-skills/suki-ambient/SKILL.md](https://developer.suki.ai/.well-known/agent-skills/suki-ambient/SKILL.md)
* Ambient API:
  [https://developer.suki.ai/.well-known/agent-skills/suki-ambient-api/SKILL.md](https://developer.suki.ai/.well-known/agent-skills/suki-ambient-api/SKILL.md)
* Form filling:
  [https://developer.suki.ai/.well-known/agent-skills/suki-form-filling/SKILL.md](https://developer.suki.ai/.well-known/agent-skills/suki-form-filling/SKILL.md)
* Form filling API:
  [https://developer.suki.ai/.well-known/agent-skills/suki-form-filling-api/SKILL.md](https://developer.suki.ai/.well-known/agent-skills/suki-form-filling-api/SKILL.md)
* Patient Summary:
  [https://developer.suki.ai/.well-known/agent-skills/suki-patient-summary/SKILL.md](https://developer.suki.ai/.well-known/agent-skills/suki-patient-summary/SKILL.md)
* Platform router:
  [https://developer.suki.ai/.well-known/agent-skills/suki-platform/SKILL.md](https://developer.suki.ai/.well-known/agent-skills/suki-platform/SKILL.md)
<!-- SKILL-AUTO:END sister-skills -->
