> ## 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.

# Seed & Read Embedded Forms

> Seed Embedded Form templates and prior section text, update them mid-visit, and read filled values with the Headless Web SDK

export const QuickSummary = ({title = "Quick summary", updated, children}) => <div className="quick-summary-wrapper">
    <div className="quick-summary-header">
      <span className="quick-summary-icon" aria-hidden="true" />
      <span className="quick-summary-title">{title}</span>
    </div>
    <div className="quick-summary-content">{children}</div>
    {updated ? <div className="quick-summary-footer">
        <span className="quick-summary-footer-icon" aria-hidden="true" />
        <span className="quick-summary-footer-text">Last updated:</span>
        <span className="quick-summary-footer-date">{updated}</span>
      </div> : null}
  </div>;

<QuickSummary updated="October 2026">
  Call `setSessionContext` to seed Embedded Form templates and prior section text. Call `updateSessionContext` when templates change during the visit. After the note is ready, read filled values from `section_structured_values` with the React structured data hooks.
</QuickSummary>

This guide shows how to seed, update, and read Embedded Forms with the Headless Web SDK. For what Embedded Forms are and when to use `string` versus `structured`, refer to [Embedded Forms overview](/headless-web-sdk/guides/embedded-forms-overview).

<Note>
  To use Embedded Forms, you need Headless Web SDK **v0.3.1** or later (`@suki-sdk/platform` and `@suki-sdk/platform-react`). Make sure your environment has this feature enabled before you use it.
</Note>

## Seed Embedded Forms

Call `setSessionContext` any time after you create the session and before you submit it. You can call it before you start recording. It sends the full context with POST, checks that the session is still `created`, and stores that context in IndexedDB for a later offline upload.

Use `updateSessionContext` only after that first seed, when a template changes during the visit. It sends a PATCH for the fields in `updateMask`. It does not recover the ambient session, and it does not check that the session is still `created`. Seed with `setSessionContext` first so an offline upload still has the templates.

<Tip>
  For when to use `type: "string"` versus `type: "structured"`, refer to [Prior text and Embedded Form templates](/headless-web-sdk/guides/embedded-forms-overview#prior-text-and-embedded-form-templates).
</Tip>

## Sections and precharted\_content shape

<ResponseField name="sections" type="Section[]">
  Sections to seed on the ambient session context.

  <Expandable title="Section properties" defaultOpen={true}>
    <ResponseField name="loinc" type="string" required>
      LOINC code of the note section, for example `10164-2`.
    </ResponseField>

    <ResponseField name="precharted_content" type="PrechartedContent[]">
      **Optional**: Prior text and Embedded Form templates for this section. The same LOINC code can have more than one entry, for example one `string` entry and one or more `structured` entries.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="precharted_content[] (string)" type="object">
  Prior text for the section.

  <Expandable title="properties" defaultOpen={true}>
    <ResponseField name="type" type="&#x22;string&#x22;" required>
      Marks this entry as prior text.
    </ResponseField>

    <ResponseField name="text" type="string" required>
      The prior text, for example `"Patient reports intermittent chest pain for 2 weeks."`
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="precharted_content[] (structured)" type="object">
  An Embedded Form template.

  <Expandable title="properties" defaultOpen={true}>
    <ResponseField name="type" type="&#x22;structured&#x22;" required>
      Marks this entry as an Embedded Form template.
    </ResponseField>

    <ResponseField name="template_id" type="string" required>
      Your identifier for the template. The same value comes back on the filled values.
    </ResponseField>

    <ResponseField name="language" type="string">
      **Optional**: Template language, for example `"en"`.
    </ResponseField>

    <ResponseField name="fields" type="array" required>
      Fields for Suki to fill.

      <Expandable title="field properties">
        <ResponseField name="id" type="string">
          **Optional**: Your identifier for the field.
        </ResponseField>

        <ResponseField name="key" type="string" required>
          Field key. The same value comes back on the filled value.
        </ResponseField>

        <ResponseField name="context" type="string">
          **Optional**: What the field asks for, for example `"When did symptoms start?"` You can include other field `id` values as `{id}` placeholders, for example `"Patient has had {slot-symptoms} for {slot-duration}"`.
        </ResponseField>

        <ResponseField name="type" type="&#x22;text&#x22; | &#x22;radio&#x22; | &#x22;multi_select&#x22; | &#x22;date&#x22;" required>
          Field type.
        </ResponseField>

        <ResponseField name="options" type="string[]">
          **Optional**: Choices for the field, for example `["mild", "moderate", "severe"]`.
        </ResponseField>
      </Expandable>
    </ResponseField>
  </Expandable>
</ResponseField>

<Note>
  Each entry is either `string` or `structured`. Do not mix fields from both types in one entry.
</Note>

The following types show the shape of `sections` you pass to `setSessionContext` and `updateSessionContext`:

```ts TypeScript theme={"theme":{"light":"one-light","dark":"material-theme-darker"}}
// Prior text OR an Embedded Form template for one section entry
type PrechartedContent =
  | { type: "string"; text: string } // Prior / existing section text
  | {
      type: "structured"; // Embedded Form template
      template_id: string; // Your id; comes back on filled results
      language?: string;
      fields: Array<{
        id?: string;
        key: string; // Comes back on each filled value
        context?: string; // What the field asks for; may include {id} placeholders
        type: "text" | "radio" | "multi_select" | "date";
        options?: string[]; // Seed-only choices for radio / multi_select
      }>;
    };

type Section = {
  loinc: string; // Note section LOINC, for example Physical exam
  precharted_content?: PrechartedContent[];
};
```

```tsx React theme={"theme":{"light":"one-light","dark":"material-theme-darker"}}
// Seed Embedded Forms after create and before submit
// From useAmbientSession: ambient.setSessionContext({ sections }) also works
await client.setSessionContext({
  ambientSessionId,
  sections: [
    {
      loinc: "29545-1", // Physical exam
      precharted_content: [
        {
          type: "structured",
          template_id: "partner-exam-iv-1", // Match this id on the filled results
          fields: [
            { key: "blood_pressure", type: "text", context: "Systolic/diastolic" },
            {
              key: "pain_scale",
              type: "radio",
              options: ["0", "1-3", "4-6", "7-10"], // Sent on seed only
            },
          ],
        },
      ],
    },
  ],
});
```

## Update Embedded Forms during a visit

Call `updateSessionContext` to change sections or templates after the first seed. Prefer the `useUpdateAmbientSessionContext` hook, which wraps the same call.

* `updateMask` is required. Prefer `["sections"]`. The string `"sections"` is also valid.
* `precharted_content` lives on `sections[]`. Mask the top-level `sections` field, then send the full `sections` list you want. The PATCH replaces that list. It does not append.
* Do not use nested paths under `sections`, such as `["sections.precharted_content"]` or `["sections.loinc"]`. Those are not supported for Embedded Form updates. Nested paths on object fields, such as `patient.dob`, are supported when you are updating that field.
* Add other top-level paths only when you are also patching those fields, for example `["sections", "orders"]`.

```mermaid actions={false} theme={"theme":{"light":"one-light","dark":"material-theme-darker"}}
flowchart TD
    A[First seed with setSessionContext] --> B[Need to change templates?]
    B -->|No| C[Keep recording and submit]
    B -->|Yes| D[updateSessionContext]
    D --> E[updateMask includes sections]
    E --> F[Send the full sections list you want]
    F --> G[SDK replaces the previous sections list]

    style A fill:#FFF394,stroke:#D4A017,color:#000000
    style D fill:#FFF394,stroke:#D4A017,color:#000000
    style E fill:#FFF394,stroke:#D4A017,color:#000000
    style F fill:#FFF394,stroke:#D4A017,color:#000000
    style G fill:#FFF394,stroke:#D4A017,color:#000000
    style C fill:#e3e0e0,stroke:#8b8a8a,color:#000000
```

```tsx React theme={"theme":{"light":"one-light","dark":"material-theme-darker"}}
// Mid-visit update: updateMask is required, and sections replaces the full list
await client.updateSessionContext({
  ambientSessionId,
  updateMask: ["sections"],
  sections: [
    {
      loinc: "10164-2", // History of Present Illness
      precharted_content: [
        {
          type: "structured",
          template_id: "partner-hpi-iv-1",
          fields: [{ key: "onset", type: "text", context: "When did symptoms start?" }],
        },
      ],
    },
  ],
});
```

## Errors

| Call | Error | When |
| - | - | - |
| `client.setSessionContext` | `AmbientSessionError` with `code: "ContextUpdateFailed"` | The session is no longer in the `created` state (for example, after submit or cancel), or the request fails |
| `updateSessionContext` | `PlatformError` with `reason: "updateSessionContextFailed"` | `ambientSessionId` or `updateMask` is missing, or the request fails |
| Structured data helpers | `PlatformError` with `reason: "getStructuredDataFailed"` | The ID is empty or the request fails |
| Section structured data helpers | `PlatformError` with `reason: "getSectionStructuredDataFailed"` | The ID is empty or the request fails |
| Content helpers | `PlatformError` with `reason: "getContentFailed"` | The ID is empty or the request fails |

<Note>
  `client.setSessionContext` throws `AmbientSessionError`. `setSessionContext` from `useAmbientSession` throws that failure as a string, so do not check it with `instanceof AmbientSessionError`. `updateSessionContext` and `useUpdateAmbientSessionContext` throw `PlatformError`. The read hooks return the `PlatformError` object.
</Note>

## Read filled values

Filled values come back in `section_structured_values`. Use the section helpers when you only need Embedded Forms. Use the full structured data helpers when you also need diagnoses and orders.

```mermaid actions={false} theme={"theme":{"light":"one-light","dark":"material-theme-darker"}}
flowchart TD
    A[Note is ready] --> B{What do you need?}
    B -->|Embedded Forms only| C[getAmbientSessionSectionStructuredData]
    B -->|Embedded Forms plus diagnoses and orders| D[getAmbientSessionStructuredData]
    B -->|Note text| E[getAmbientSessionContent]
    B -->|Note by noteId| F[getAmbientNoteStructuredData]
    F --> G[Diagnoses and orders only. No Embedded Forms in this release]

    style A fill:#FFF394,stroke:#D4A017,color:#000000
    style C fill:#FFF394,stroke:#D4A017,color:#000000
    style D fill:#FFF394,stroke:#D4A017,color:#000000
    style E fill:#e3e0e0,stroke:#8b8a8a,color:#000000
    style F fill:#e3e0e0,stroke:#8b8a8a,color:#000000
    style G fill:#e3e0e0,stroke:#8b8a8a,color:#000000
```

| Method | Returns |
| - | - |
| `getAmbientSessionSectionStructuredData(ambientSessionId)` | `{ section_structured_values }` for the session |
| `getAmbientSessionStructuredData(ambientSessionId)` | `{ structured_data: { diagnoses, orders, section_structured_values } }` for the session |
| `getAmbientEncounterSectionStructuredData(encounterId)` | `{ section_structured_values }` for the encounter |
| `getAmbientEncounterStructuredData(encounterId)` | `{ structured_data: { diagnoses, orders, section_structured_values } }` for the encounter |
| `getAmbientNoteStructuredData(noteId)` | Diagnoses and orders for the note. Does not include Embedded Forms |
| `getAmbientSessionContent({ ambientSessionId, cumulative? })` | Note text for the session. `cumulative` is optional and applies here only |
| `getAmbientEncounterContent(encounterId)` | Note text for the encounter |
| `getAmbientNoteContent(noteId)` | Note text for the note |

<Note>
  Note structured data does not include Embedded Forms in this release. Read Embedded Forms from the session or encounter helpers.
</Note>

<Tip>
  The SDK does not poll for filled values in the background. Call the helpers, or refetch the hooks, when your app knows the note is ready. To poll with a hook, pass `refetchIntervalMs`.
</Tip>

Each filled value is `{ id?, context?, key, value }`. It does not include the `options` or field `type` you sent in the template.

```ts TypeScript theme={"theme":{"light":"one-light","dark":"material-theme-darker"}}
// Protobuf JSON value: null, boolean, number, string, array, or object
type JsonValue = null | boolean | number | string | JsonValue[] | { [key: string]: JsonValue };

// One filled section from section_structured_values
type SectionStructuredData = {
  loinc_code?: string; // Same LOINC you seeded
  forms?: Array<{
    template_id?: string; // Same template_id you seeded
    values?: Array<{
      id?: string;
      context?: string; // Same context you seeded, when returned
      key: string; // Same key you seeded
      value?: JsonValue;
    }>;
  }>;
};

// Session / encounter structured data:
// structured_data.section_structured_values?: SectionStructuredData[]

// Section structured data helpers (Embedded Forms only):
// { section_structured_values?: SectionStructuredData[] }
```

### React hooks

`@suki-sdk/platform-react` includes these hooks for Embedded Forms and related content:

| Hook | What it does |
| - | - |
| `useUpdateAmbientSessionContext` | Updates the session context. Wraps `updateSessionContext` |
| `useAmbientSessionSectionStructuredData` | Reads Embedded Forms for the session |
| `useAmbientSessionStructuredData` | Reads structured data for the session |
| `useAmbientSessionContent` | Reads note content for the session |
| `useAmbientEncounterSectionStructuredData` | Reads Embedded Forms for the encounter |
| `useAmbientEncounterStructuredData` | Reads structured data for the encounter |
| `useAmbientEncounterContent` | Reads note content for the encounter |
| `useAmbientNoteStructuredData` | Reads diagnoses and orders for the note. Does not include Embedded Forms |
| `useAmbientNoteContent` | Reads note content for the note |

Each read hook takes one object:

<ResponseField name="ambientSessionId | encounterId | noteId" type="string" required>
  The ID the hook reads by. Session hooks take `ambientSessionId`, encounter hooks take `encounterId`, and note hooks take `noteId`. The query does not run while this value is empty.
</ResponseField>

<ResponseField name="enabled" type="boolean">
  **Optional**: Controls whether the query runs. Defaults to `true`.
</ResponseField>

<ResponseField name="refetchIntervalMs" type="number">
  **Optional**: Polling interval in milliseconds. When you omit it, the hook does not poll.
</ResponseField>

Each read hook also has a query options factory, such as `ambientSessionStructuredDataQueryOptions`, that you can use to prefetch with your `QueryClient`. After a successful `useUpdateAmbientSessionContext` call, the SDK refreshes the session structured data, section structured data, and session content hooks for that session.

## Code examples

This section shows React examples with @suki-sdk/platform-react hooks. Seed Embedded Forms with setSessionContext from useAmbientSession, record, update with useUpdateAmbientSessionContext, and read section\_structured\_values. The Headless Web SDK is React only.

This component seeds Embedded Forms with `setSessionContext` from `useAmbientSession`, records the visit, updates the templates, and reads the filled values.

```tsx React expandable theme={"theme":{"light":"one-light","dark":"material-theme-darker"}}
import { useCallback } from "react";
import {
  useAmbient,
  useAmbientSession,
  useAmbientSessionSectionStructuredData,
  useUpdateAmbientSessionContext,
} from "@suki-sdk/platform-react";

// One HPI section with prior text + Embedded Form fields
const HPI_SECTIONS = [
  {
    loinc: "10164-2", // History of Present Illness
    precharted_content: [
      {
        type: "string" as const, // Prior text already in the chart
        text: "Patient reports intermittent chest pain for 2 weeks.",
      },
      {
        type: "structured" as const, // Embedded Form template
        template_id: "partner-hpi-iv-1", // Match this on filled results
        language: "en",
        fields: [
          {
            id: "onset",
            key: "onset",
            context: "When did symptoms start?",
            type: "text" as const,
          },
          {
            id: "severity",
            key: "severity",
            context: "Pain severity",
            type: "radio" as const,
            options: ["mild", "moderate", "severe"], // Seed-only choices
          },
        ],
      },
    ],
  },
];

function EmbeddedFormsVisit() {
  const { ambientSessionId, session } = useAmbient();
  const ambient = useAmbientSession({ ambientSessionId: ambientSessionId ?? "" });
  const { mutateAsync: updateContext } = useUpdateAmbientSessionContext();

  // Embedded Forms only; does not poll unless you pass refetchIntervalMs
  const ivQuery = useAmbientSessionSectionStructuredData({
    ambientSessionId: ambientSessionId ?? "",
    enabled: Boolean(ambientSessionId),
  });

  const createSession = useCallback(async () => {
    // ambientSessionId updates from useAmbient after create succeeds
    await session.create({ encounterId: "visit-123" });
  }, [session]);

  const seedEmbeddedForms = useCallback(async () => {
    if (!ambientSessionId) return;

    // Full seed after create, before submit (can be before start)
    // useAmbientSession adds ambientSessionId for you
    await ambient.setSessionContext({ sections: HPI_SECTIONS });
  }, [ambientSessionId, ambient]);

  const updateEmbeddedForms = useCallback(async () => {
    if (!ambientSessionId) return;

    // Mid-visit change: updateMask required; sections replaces the previous list
    await updateContext({
      ambientSessionId,
      updateMask: ["sections"],
      sections: HPI_SECTIONS,
    });
  }, [ambientSessionId, updateContext]);

  return (
    <div>
      <button type="button" onClick={createSession}>
        Create session
      </button>
      <button type="button" onClick={seedEmbeddedForms} disabled={!ambientSessionId}>
        Seed Embedded Forms
      </button>
      <button type="button" onClick={() => ambient.start()} disabled={!ambientSessionId}>
        Start recording
      </button>
      <button type="button" onClick={updateEmbeddedForms} disabled={!ambientSessionId}>
        Update Embedded Forms
      </button>
      <button type="button" onClick={() => ambient.submit()} disabled={!ambientSessionId}>
        Submit
      </button>
      {/* Call again after the note is ready */}
      <button type="button" onClick={() => void ivQuery.refetch()} disabled={!ambientSessionId}>
        Refresh Embedded Forms
      </button>

      <pre>{JSON.stringify(ivQuery.data?.section_structured_values ?? null, null, 2)}</pre>
    </div>
  );
}
```

The following component reads the filled values and the diagnoses after submit. The hooks do not poll by default. Pass `refetchIntervalMs` only if your app needs polling.

```tsx React expandable theme={"theme":{"light":"one-light","dark":"material-theme-darker"}}
import {
  useAmbientSessionStructuredData,
  useAmbientSessionSectionStructuredData,
} from "@suki-sdk/platform-react";

function Results({ ambientSessionId }: { ambientSessionId: string }) {
  // Diagnoses, orders, and Embedded Forms together
  const structured = useAmbientSessionStructuredData({
    ambientSessionId,
    // Turn on when your app knows the note is ready to read
    enabled: Boolean(ambientSessionId),
  });

  // Embedded Forms only
  const embeddedForms = useAmbientSessionSectionStructuredData({
    ambientSessionId,
    enabled: Boolean(ambientSessionId),
  });

  const fills = embeddedForms.data?.section_structured_values ?? [];
  const withDxOrders = structured.data?.structured_data;

  return (
    <div>
      <h3>Embedded Forms</h3>
      {fills.map((section) => (
        <div key={section.loinc_code}>
          <strong>{section.loinc_code}</strong>
          {section.forms?.map((form) => (
            <ul key={form.template_id}>
              {form.values?.map((v) => (
                <li key={v.key}>
                  {v.context ?? v.key}: {String(v.value ?? "")}
                </li>
              ))}
            </ul>
          ))}
        </div>
      ))}

      <h3>Diagnoses</h3>
      <pre>{JSON.stringify(withDxOrders?.diagnoses, null, 2)}</pre>
    </div>
  );
}
```

## Offline behavior

* Seed Embedded Forms with `setSessionContext`. It stores the full context in your browser's IndexedDB, so the SDK can upload it if the session goes offline.
* After a successful `updateSessionContext`, the SDK merges only the fields in `updateMask` into IndexedDB. A later offline upload keeps those updates.
* If there is no IndexedDB context row yet, the SDK does not mirror the update locally and logs a warning. The update still reaches Suki, but a later offline upload does not include it. Call `setSessionContext` first.

```mermaid actions={false} theme={"theme":{"light":"one-light","dark":"material-theme-darker"}}
flowchart TD
    A[setSessionContext] --> B[Full context stored in IndexedDB]
    B --> C{Later updateSessionContext?}
    C -->|Yes| D[Merge only updateMask fields into IndexedDB]
    C -->|No| E[Offline upload uses the full seeded context]
    D --> E
    A2[updateSessionContext with no prior setSessionContext] --> F[Update reaches Suki]
    F --> G[IndexedDB has no context row for that session]
    G --> H[Later offline upload misses that update]

    style A fill:#FFF394,stroke:#D4A017,color:#000000
    style B fill:#FFF394,stroke:#D4A017,color:#000000
    style D fill:#FFF394,stroke:#D4A017,color:#000000
    style E fill:#FFF394,stroke:#D4A017,color:#000000
    style A2 fill:#e3e0e0,stroke:#8b8a8a,color:#000000
    style G fill:#e3e0e0,stroke:#8b8a8a,color:#000000
    style H fill:#e3e0e0,stroke:#8b8a8a,color:#000000
```

For how the SDK handles offline sessions, refer to [Offline mode](/headless-web-sdk/guides/offline-mode).

## Troubleshooting

<AccordionGroup>
  <Accordion title="setSessionContext Throws ContextUpdateFailed">
    The session must still be in the `created` state. If you already called `submit` or `cancel`, `client.setSessionContext` throws `AmbientSessionError` with `code: "ContextUpdateFailed"`. Seed Embedded Forms after create and before submit. `setSessionContext` from `useAmbientSession` throws that failure as a string.
  </Accordion>

  <Accordion title="updateSessionContext Fails with updateSessionContextFailed">
    Pass both `ambientSessionId` and `updateMask`. Prefer `updateMask: ["sections"]`, then send the full `sections` list you want. Nested paths under `sections`, such as `sections.precharted_content`, are not supported. Prefer `useUpdateAmbientSessionContext`. This call throws `PlatformError`, not `AmbientSessionError`.
  </Accordion>

  <Accordion title="section_structured_values Is Empty">
    Wait until your app knows the note is ready, then call the helper or refetch the hook. The SDK does not poll in the background unless you pass `refetchIntervalMs`. Use the session or encounter structured data helpers. `getAmbientNoteStructuredData` does not include Embedded Forms in this release. Note content helpers return note text only, not Embedded Forms.
  </Accordion>

  <Accordion title="Offline Upload Is Missing Embedded Forms">
    Call `setSessionContext` first so the SDK stores the full context in your browser's IndexedDB. If you call `updateSessionContext` with no prior IndexedDB context row, the update can still reach Suki, but a later offline upload does not include it. Refer to [Offline behavior](#offline-behavior).
  </Accordion>

  <Accordion title="cumulative Does Not Affect Embedded Forms">
    The optional `cumulative` flag applies only to session content helpers such as `getAmbientSessionContent`. It does not apply to section structured data reads.
  </Accordion>
</AccordionGroup>

## Next steps

<Icon icon="file-lines" iconType="solid" /> **[Embedded Forms overview](/headless-web-sdk/guides/embedded-forms-overview)** - What Embedded Forms are, the ambient flow, and string versus structured.

<Icon icon="file-lines" iconType="solid" /> **[Platform client and provider](/headless-web-sdk/api-reference/platform-client)** - `setSessionContext`, `updateSessionContext`, and structured data methods.

<Icon icon="file-lines" iconType="solid" /> **[SessionContext](/headless-web-sdk/api-reference/types/session-context)** - Full session context shape, including `precharted_content`.

<Icon icon="file-lines" iconType="solid" /> **[Offline mode](/headless-web-sdk/guides/offline-mode)** - Track sessions that upload after the network returns.

<Icon icon="file-lines" iconType="solid" /> **[Manage ambient session](/headless-web-sdk/guides/hooks/ambient-session-hook)** - Start, pause, and submit recording.


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