> ## Documentation Index
> Fetch the complete documentation index at: https://developer.suki.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Retrieve Results from Ambient Webhook

> Use Ambient webhook links to check session status and retrieve notes, transcripts, and other session or encounter results

When Suki sends a `SUCCESS` Ambient webhook, the payload includes links to the session status, note, transcript, structured data, and other results for that encounter.

> [!NOTE]
>
> The webhook does not contain the generated note or other result bodies. Your application must call the linked Ambient API endpoints to retrieve them.

Before you fetch anything else, call the `status` link for the session in the notification. Load the note with `contents` only when that status is `completed`. Pull the transcript, structured data, E/M codes, diagnoses, or Medication orders the same way when your product needs them.

Each `href` is relative to `/api/v1/ambient`. Join your Ambient API base URL with that path, and authenticate the request with your Suki Token.

## Prerequisites

Before you retrieve results, make sure you have:

* **A webhook receiver:** An endpoint that receives Suki webhooks and verifies their signatures. Refer to [Webhook quickstart](https://developer.suki.ai/documentation/webhook/quickstart.md) and [Signature verification](https://developer.suki.ai/documentation/webhook/signature-verification.md).
* **A valid Suki Token:** Authenticate Ambient API requests with the `sdp_suki_token` header. Include the `sdp_provider_id` header when your partnership requires it. Refer to [Ambient API quickstart](https://developer.suki.ai/api-reference/quickstart.md).

## Build the full URL

Each `href` in an Ambient webhook link is relative to the Ambient API path, `/api/v1/ambient`. Append `/api/v1/ambient` and the `href` to your API base URL to build the full request URL.

For example, when the production base URL is `https://sdp.suki.ai` and the link contains:

```text theme={"theme":{"light":"one-light","dark":"material-theme-darker"}}
/session/{session_id}/content
```

the full request URL is:

```text theme={"theme":{"light":"one-light","dark":"material-theme-darker"}}
https://sdp.suki.ai/api/v1/ambient/session/{session_id}/content
```

For staging, use `https://sdp.suki-stage.com` as the API base URL.

### Available endpoints

This table maps each `_links` key to its Ambient API endpoint and API reference page.

| `_links` key | Endpoint | API reference |
| :- | :- | :- |
| `status` | `GET /api/v1/ambient/session/{session_id}/status` | [Get ambient session status](https://developer.suki.ai/api-reference/ambient-content/status.md) |
| `contents` | `GET /api/v1/ambient/session/{session_id}/content` | [Get ambient session content](https://developer.suki.ai/api-reference/ambient-content/content.md) |
| `transcripts` | `GET /api/v1/ambient/session/{session_id}/transcript` | [Get session transcript](https://developer.suki.ai/api-reference/ambient-content/transcript.md) |
| `structured_data` | `GET /api/v1/ambient/session/{session_id}/structured-data` | [Get ambient session structured data](https://developer.suki.ai/api-reference/ambient-content/structured-data.md) |
| `section_structured_data` | `GET /api/v1/ambient/session/{session_id}/structured-data/section-structured-data` | [Get ambient session section structured data](https://developer.suki.ai/api-reference/ambient-content/section-structured-data.md) |
| `em_codes` | `GET /api/v1/ambient/session/{session_id}/structured-data/em-codes` | [Retrieve E/M codes using ambient session ID](https://developer.suki.ai/api-reference/ambient-content/session-em-codes.md) |
| `diagnoses` | `GET /api/v1/ambient/session/{session_id}/structured-data/diagnoses` | [Get ambient session structured data](https://developer.suki.ai/api-reference/ambient-content/structured-data.md) |
| `orders` | `GET /api/v1/ambient/session/{session_id}/structured-data/orders` | [Get ambient session structured data](https://developer.suki.ai/api-reference/ambient-content/structured-data.md) |
| `encounter_content` | `GET /api/v1/ambient/encounter/{encounter_id}/content` | [Get ambient encounter content](https://developer.suki.ai/api-reference/ambient-content/encounter-content.md) |
| `encounter_structured_data` | `GET /api/v1/ambient/encounter/{encounter_id}/structured-data` | [Get ambient encounter structured data](https://developer.suki.ai/api-reference/ambient-content/encounter-structured-data.md) |
| `encounter_section_structured_data` | `GET /api/v1/ambient/encounter/{encounter_id}/structured-data/section-structured-data` | [Get ambient encounter section structured data](https://developer.suki.ai/api-reference/ambient-content/encounter-section-structured-data.md) |
| `encounter_em_codes` | `GET /api/v1/ambient/encounter/{encounter_id}/structured-data/em-codes` | [Retrieve E/M codes using encounter ID](https://developer.suki.ai/api-reference/ambient-content/encounter-em-codes.md) |
| `encounter_diagnoses` | `GET /api/v1/ambient/encounter/{encounter_id}/structured-data/diagnoses` | [Get ambient encounter structured data](https://developer.suki.ai/api-reference/ambient-content/encounter-structured-data.md) |
| `encounter_orders` | `GET /api/v1/ambient/encounter/{encounter_id}/structured-data/orders` | [Get ambient encounter structured data](https://developer.suki.ai/api-reference/ambient-content/encounter-structured-data.md) |

Every link object includes a `method` field. Use that method when you call the endpoint. For the links in this table, the method is always `GET`.

> [!NOTE]
>
> Diagnoses and Medication orders are Ambient API results. The published API reference documents them on the session and encounter structured data pages. The webhook also includes dedicated `diagnoses` and `orders` links. Call those `href` values when you only need that slice, or call `structured_data` / `encounter_structured_data` when you want the full structured data response documented in the API reference.

## Retrieve results step by step

Follow this sequence after your webhook endpoint receives a `SUCCESS` notification:

**1. Verify the Webhook and Return HTTP 200**

Verify the webhook signature and return HTTP **200** within 30 seconds. Do not wait for Ambient API calls to finish before you respond. Process the notification and retrieve results in a background job.

**2. Select the Correct Links**

* Read `_links` and select the keys you need.

* Session-level arrays contain one link for each session in the encounter. Match the link's `name` to the webhook's `session_id` when you want results for the session that triggered the notification.

* Encounter-level arrays contain one link for the entire encounter.

**3. Check the Session Status**

* Call the `status` link for the current session. Retrieve the note and other content only when the returned status is `completed`.

* If the status is `skipped`, Suki did not generate a note for that session. Skip the content requests and offer the clinician a chance to record again.

* Do not treat every non-`completed` status as `skipped`. Handle `failed`, `aborted`, and in-progress values according to the status API response. Refer to [Track note generation status](https://developer.suki.ai/documentation/how-to/ambient-clinical-notes/handle-ambient-session-status.md).

**4. Retrieve the Required Results**

* Call `contents` for the selected session's note. Use `encounter_content` when your application needs the combined encounter note.

* Call the transcript, structured data, E/M codes, diagnoses, and Medication orders links only when your application needs those results.

For help choosing session-level versus encounter-level note content, refer to [Get shared note content](https://developer.suki.ai/documentation/how-to/ambient-clinical-notes/retrieve-note-and-encounter-content.md).

> [!NOTE]
>
> A link in `_links` does not guarantee that the response contains values. `em_codes`, `diagnoses`, and `orders` can return HTTP **200** with an empty `values` list. Refer to [Retrieve E/M codes, diagnoses, and Medication orders](https://developer.suki.ai/documentation/webhook/ambient/overview.md#retrieve-em-codes-diagnoses-and-medication-orders).

## Example: retrieve one session note

The following examples retrieve the note for the session that triggered the webhook. Keep this path focused on `status` and `contents`. The endpoint table above lists every other key.

Run this code after your webhook endpoint has verified the signature and returned HTTP **200**. The examples:

1. Read `session_id` and `_links` from the webhook payload.
2. Find the `status` link for that session.
3. Continue only when the session status is `completed`.
4. Retrieve the session note through the `contents` link.

The examples use the production API base URL. Replace it with the staging base URL when you test against staging.

**Language tabs (agents):** Equivalent code samples are available in: Python, TypeScript, Go. Humans see one language at a time. Use the variant that matches the user's stack; behavior is the same across tabs.

### Code example

> [tab] Python
>
> ```python expandable theme={"theme":{"light":"one-light","dark":"material-theme-darker"}}
> import requests
>
> BASE_URL = "https://sdp.suki.ai"
> AMBIENT_PATH = "/api/v1/ambient"
> HEADERS = {"sdp_suki_token": "<sdp_suki_token>"}
>
>
> def link_for(links, key, name):
>     for link in links.get(key) or []:
>         if link.get("name") == name:
>             return link
>     return None
>
>
> def load_ambient_results(payload):
>     session_id = payload["session_id"]
>     links = payload.get("_links") or {}
>
>     status_link = link_for(links, "status", session_id)
>     if not status_link:
>         raise ValueError(f"No status link found for session {session_id}")
>
>     status_response = requests.get(
>         BASE_URL + AMBIENT_PATH + status_link["href"],
>         headers=HEADERS,
>     )
>     status_response.raise_for_status()
>     session_status = status_response.json().get("status")
>
>     if session_status != "completed":
>         print(f"Session {session_id} is not completed: {session_status}")
>         return None
>
>     content_link = link_for(links, "contents", session_id)
>     if not content_link:
>         raise ValueError(f"No content link found for session {session_id}")
>
>     content_response = requests.get(
>         BASE_URL + AMBIENT_PATH + content_link["href"],
>         headers=HEADERS,
>     )
>     content_response.raise_for_status()
>     return content_response.json()
> ```

> [tab] TypeScript
>
> ```typescript expandable theme={"theme":{"light":"one-light","dark":"material-theme-darker"}}
> const BASE_URL = "https://sdp.suki.ai";
> const AMBIENT_PATH = "/api/v1/ambient";
> const HEADERS = { sdp_suki_token: "<sdp_suki_token>" };
>
> type Link = {
>   href: string;
>   method: string;
>   name: string;
>   type: string;
> };
>
> function linkFor(
>   links: Record<string, Link[]>,
>   key: string,
>   name: string
> ): Link | undefined {
>   return (links[key] ?? []).find((link) => link.name === name);
> }
>
> export async function loadAmbientResults(payload: any) {
>   const sessionId: string = payload.session_id;
>   const links: Record<string, Link[]> = payload._links ?? {};
>
>   const statusLink = linkFor(links, "status", sessionId);
>   if (!statusLink) {
>     throw new Error(`No status link found for session ${sessionId}`);
>   }
>
>   const statusRes = await fetch(BASE_URL + AMBIENT_PATH + statusLink.href, {
>     headers: HEADERS,
>   });
>   if (!statusRes.ok) {
>     throw new Error(`Status call failed: ${statusRes.status}`);
>   }
>
>   const { status } = await statusRes.json();
>   if (status !== "completed") {
>     console.log(`Session ${sessionId} is not completed: ${status}`);
>     return null;
>   }
>
>   const contentLink = linkFor(links, "contents", sessionId);
>   if (!contentLink) {
>     throw new Error(`No content link found for session ${sessionId}`);
>   }
>
>   const contentRes = await fetch(BASE_URL + AMBIENT_PATH + contentLink.href, {
>     headers: HEADERS,
>   });
>   if (!contentRes.ok) {
>     throw new Error(`Content call failed: ${contentRes.status}`);
>   }
>
>   return contentRes.json();
> }
> ```

> [tab] Go
>
> ```go expandable theme={"theme":{"light":"one-light","dark":"material-theme-darker"}}
> package main
>
> import (
> 	"encoding/json"
> 	"fmt"
> 	"io"
> 	"net/http"
> )
>
> const (
> 	baseURL     = "https://sdp.suki.ai"
> 	ambientPath = "/api/v1/ambient"
> 	sukiToken   = "<sdp_suki_token>"
> )
>
> type link struct {
> 	Href   string `json:"href"`
> 	Method string `json:"method"`
> 	Name   string `json:"name"`
> 	Type   string `json:"type"`
> }
>
> func linkFor(links map[string][]link, key, name string) (link, bool) {
> 	for _, item := range links[key] {
> 		if item.Name == name {
> 			return item, true
> 		}
> 	}
> 	return link{}, false
> }
>
> func getJSON(href string) (map[string]any, error) {
> 	req, err := http.NewRequest(http.MethodGet, baseURL+ambientPath+href, nil)
> 	if err != nil {
> 		return nil, err
> 	}
> 	req.Header.Set("sdp_suki_token", sukiToken)
>
> 	res, err := http.DefaultClient.Do(req)
> 	if err != nil {
> 		return nil, err
> 	}
> 	defer res.Body.Close()
>
> 	body, err := io.ReadAll(res.Body)
> 	if err != nil {
> 		return nil, err
> 	}
> 	if res.StatusCode < 200 || res.StatusCode >= 300 {
> 		return nil, fmt.Errorf("request failed: %s", res.Status)
> 	}
>
> 	var payload map[string]any
> 	if err := json.Unmarshal(body, &payload); err != nil {
> 		return nil, err
> 	}
> 	return payload, nil
> }
>
> func loadAmbientResults(payload map[string]any) (map[string]any, error) {
> 	sessionID, _ := payload["session_id"].(string)
> 	rawLinks, _ := payload["_links"].(map[string]any)
>
> 	links := map[string][]link{}
> 	for key, value := range rawLinks {
> 		encoded, err := json.Marshal(value)
> 		if err != nil {
> 			return nil, err
> 		}
> 		var items []link
> 		if err := json.Unmarshal(encoded, &items); err != nil {
> 			return nil, err
> 		}
> 		links[key] = items
> 	}
>
> 	statusLink, ok := linkFor(links, "status", sessionID)
> 	if !ok {
> 		return nil, fmt.Errorf("no status link found for session %s", sessionID)
> 	}
>
> 	statusPayload, err := getJSON(statusLink.Href)
> 	if err != nil {
> 		return nil, err
> 	}
>
> 	status, _ := statusPayload["status"].(string)
> 	if status != "completed" {
> 		fmt.Printf("Session %s is not completed: %s\n", sessionID, status)
> 		return nil, nil
> 	}
>
> 	contentLink, ok := linkFor(links, "contents", sessionID)
> 	if !ok {
> 		return nil, fmt.Errorf("no content link found for session %s", sessionID)
> 	}
>
> 	return getJSON(contentLink.Href)
> }
> ```

## Example: retrieve other results

After status is `completed`, reuse the same base URL, Ambient path, headers, and helper. Change only the `_links` key.

### Session transcript

Match `name` to `session_id`, the same way you select `contents`.

**Language tabs (agents):** Equivalent code samples are available in: Python, TypeScript, Go. Humans see one language at a time. Use the variant that matches the user's stack; behavior is the same across tabs.

### Code example

> [tab] Python
>
> ```python expandable theme={"theme":{"light":"one-light","dark":"material-theme-darker"}}
> def load_transcript(links, session_id):
>     transcript_link = link_for(links, "transcripts", session_id)
>     if not transcript_link:
>         raise ValueError(f"No transcript link found for session {session_id}")
>
>     response = requests.get(
>         BASE_URL + AMBIENT_PATH + transcript_link["href"],
>         headers=HEADERS,
>     )
>     response.raise_for_status()
>     return response.json()
> ```

> [tab] TypeScript
>
> ```typescript expandable theme={"theme":{"light":"one-light","dark":"material-theme-darker"}}
> export async function loadTranscript(
>   links: Record<string, Link[]>,
>   sessionId: string
> ) {
>   const transcriptLink = linkFor(links, "transcripts", sessionId);
>   if (!transcriptLink) {
>     throw new Error(`No transcript link found for session ${sessionId}`);
>   }
>
>   const response = await fetch(BASE_URL + AMBIENT_PATH + transcriptLink.href, {
>     headers: HEADERS,
>   });
>   if (!response.ok) {
>     throw new Error(`Transcript call failed: ${response.status}`);
>   }
>   return response.json();
> }
> ```

> [tab] Go
>
> ```go expandable theme={"theme":{"light":"one-light","dark":"material-theme-darker"}}
> func loadTranscript(links map[string][]link, sessionID string) (map[string]any, error) {
> 	transcriptLink, ok := linkFor(links, "transcripts", sessionID)
> 	if !ok {
> 		return nil, fmt.Errorf("no transcript link found for session %s", sessionID)
> 	}
> 	return getJSON(transcriptLink.Href)
> }
> ```

### Encounter note content

Encounter-level arrays hold a single link. Use the first entry. Do not match on `session_id`.

**Language tabs (agents):** Equivalent code samples are available in: Python, TypeScript, Go. Humans see one language at a time. Use the variant that matches the user's stack; behavior is the same across tabs.

### Code example

> [tab] Python
>
> ```python expandable theme={"theme":{"light":"one-light","dark":"material-theme-darker"}}
> def load_encounter_content(links):
>     encounter_links = links.get("encounter_content") or []
>     if not encounter_links:
>         raise ValueError("No encounter_content link found")
>
>     response = requests.get(
>         BASE_URL + AMBIENT_PATH + encounter_links[0]["href"],
>         headers=HEADERS,
>     )
>     response.raise_for_status()
>     return response.json()
> ```

> [tab] TypeScript
>
> ```typescript expandable theme={"theme":{"light":"one-light","dark":"material-theme-darker"}}
> export async function loadEncounterContent(links: Record<string, Link[]>) {
>   const encounterLinks = links.encounter_content ?? [];
>   if (!encounterLinks.length) {
>     throw new Error("No encounter_content link found");
>   }
>
>   const response = await fetch(
>     BASE_URL + AMBIENT_PATH + encounterLinks[0].href,
>     { headers: HEADERS }
>   );
>   if (!response.ok) {
>     throw new Error(`Encounter content call failed: ${response.status}`);
>   }
>   return response.json();
> }
> ```

> [tab] Go
>
> ```go expandable theme={"theme":{"light":"one-light","dark":"material-theme-darker"}}
> func loadEncounterContent(links map[string][]link) (map[string]any, error) {
> 	encounterLinks := links["encounter_content"]
> 	if len(encounterLinks) == 0 {
> 		return nil, fmt.Errorf("no encounter_content link found")
> 	}
> 	return getJSON(encounterLinks[0].Href)
> }
> ```

Use the same pattern for `structured_data`, `em_codes`, `diagnoses`, `orders`, and the other encounter-level keys in the endpoint table.

## Responses from linked endpoints

When you call a linked Ambient API endpoint, Suki returns standard HTTP status codes.

| HTTP status | Meaning | Example response |
| :- | :- | :- |
| `200` | OK. The request succeeded. | |
| `400` | Bad Request. The request was invalid. | `{"code": 400, "message": "invalid request"}` |
| `401` | Unauthorized. The token is invalid or authentication failed. | `{"code": 401, "message": "invalid token"}` |
| `500` | Internal Server Error. The server encountered an error. | `{"code": 500, "message": "internal server error"}` |

* **`400 Bad Request`:** Fix the URL or request parameters, then send the request again.
* **`401 Unauthorized`:** Log in again for a new Suki Token, then retry with the updated token.
* **`5xx Server Error`:** Retry the request after a short delay.

## Next steps

&#x20;**[Ambient payload reference](https://developer.suki.ai/documentation/webhook/ambient/payload.md)** - Every field and link key in the webhook body.

&#x20;**[Track note generation status](https://developer.suki.ai/documentation/how-to/ambient-clinical-notes/handle-ambient-session-status.md)** - Show the right state to clinicians.

&#x20;**[Get the generated note](https://developer.suki.ai/documentation/how-to/ambient-clinical-notes/retrieve-ambient-content.md)** - All the ways to load notes and structured data.

&#x20;**[Webhook request and response](https://developer.suki.ai/documentation/webhook/payload-and-response.md)** - The 200 response, retries, and duplicate deliveries.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.