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

# Form Filling SDK Session Workflow

> How Form filling sessions start, run, and deliver structured data to your application

<div className="quick-summary-wrapper">
  <div className="quick-summary-header">
    <span className="quick-summary-icon" aria-hidden="true" />

    <span className="quick-summary-title">Quick summary</span>
  </div>

  <div className="quick-summary-content">
    When you call `start()` or render `<FormFilling>`, the SDK opens Suki's hosted UI in your page. The clinician fills forms by voice, then submits for processing. Pass one value in `form_template_ids` to skip form selection; pass more than one to show the selection screen first.

    <br />

    <br />

    Structured results arrive in your app through `onSubmit` after processing completes. For production, also register a partner webhook so your server receives the same data even if the clinician closes the browser early.
  </div>

  <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">August 2026</span>
  </div>
</div>

When you call **`client.start()`** or render **`<FormFilling>`**, the SDK opens Suki's <Tooltip tip="Suki-controlled UI loaded inside your web page for Form filling or Dictation." cta="View in Glossary" href="/Glossary/h">hosted iframe</Tooltip> in your page. The clinician completes the session there by voice. After processing, structured JSON arrives in your app through **`onSubmit`** and on your server through your [Partner webhook](/documentation/webhook/overview).

Create **`SukiAuthManager`** and <Tooltip tip="Main class in the Form filling SDK that opens Form filling sessions in your page." cta="View in Glossary" href="/Glossary/f">**`FormFillingClient`**</Tooltip> once per page. Open Form filling only when the clinician is ready.

## How a session works

<Steps>
  <Step title="The Iframe Loads">
    The hosted UI loads inside your container on your web page.
  </Step>

  <Step title="The Clinician Selects Forms (Multi-Form Only)">
    If you passed more than **one** `template_id` in `form_template_ids`, the clinician sees a selection screen. All forms start checked. They confirm which forms to fill, then start recording.

    If you passed **only one** `template_id`, this step is skipped. Recording starts right away.
  </Step>

  <Step title="The Clinician Records and Submits">
    The clinician speaks to fill the selected forms. They can pause, resume, cancel, or submit.

    If they cancel, your app receives **`onCancel`** and **`form-filling:cancelled`**. No structured data is returned.

    If they tap **Close** during recording, a confirmation appears. **Resume Session** returns to recording. **Cancel Session** ends the session with no results.
  </Step>

  <Step title="Suki Processes the Session">
    Submit does not return structured data immediately. Suki processes the recording first. The hosted UI shows a processing screen.

    When structured data is ready, **`onSubmit`** runs and your app receives a **`FormFillingResult`**. Refer to [Payload shape](/form-filling-sdk/guides/callbacks#formfillingresult-payload-shape) to learn more about what it returns.
  </Step>
</Steps>

<Note>
  Before the iframe opens, **`FormFillingClient.start()`** validates **`form_template_ids`**. Unsupported IDs are dropped. If none remain, the SDK emits **`SUKI_FF_001`** and does not open the UI.
</Note>

How many values you pass in **`form_template_ids`** decides whether the clinician sees a selection screen first, or goes straight to recording:

```mermaid actions={false} theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
%%{init: {'theme':'base', 'themeVariables': { 'primaryColor':'#FFE148','primaryTextColor':'#111827','lineColor':'#6B7280','fontSize':'14px','edgeLabelBackground':'#FFFFFF','tertiaryColor':'#F9FAFB','tertiaryTextColor':'#111827','tertiaryBorderColor':'#D1D5DB'}}}%%
flowchart TB
    Start["Start session<br/><small>client.start or FormFilling</small>"] --> Check{"form_template_ids"}

    Check -->|One template_id| Single["Skip selection<br/><small>Recording starts right away</small>"]
    Check -->|Two or more template_ids| Multi["Selection screen<br/><small>Clinician confirms forms</small>"]

    Multi --> Record["Record and submit"]
    Single --> Record

    Record --> Process["Suki processes session<br/><small>Processing screen in iframe</small>"]
    Process --> Result["onSubmit / form-filling:submitted<br/><small>FormFillingResult in your app</small>"]

    style Start fill:#FFE148,stroke:#111827,stroke-width:3px,color:#111827
    style Check fill:#FFF394,stroke:#D1D5DB,stroke-width:2px,color:#111827
    style Single fill:#ffffff,stroke:#9CA3AF,stroke-width:2px,color:#111827
    style Multi fill:#ffffff,stroke:#9CA3AF,stroke-width:2px,color:#111827
    style Record fill:#ffffff,stroke:#9CA3AF,stroke-width:2px,color:#111827
    style Process fill:#FFFADE,stroke:#D1D5DB,stroke-width:2px,color:#111827
    style Result fill:#FFE148,stroke:#111827,stroke-width:3px,color:#111827
```

## Single-form session

<span id="single-form-session" />

A **single-form session** starts Form filling with **one** `template_id` in **`form_template_ids`**. Suki opens the hosted UI and the clinician goes **straight to recording**. There is no form selection screen.

Use this pattern when your app already knows which template the clinician needs, for example a vitals assessment, a skin check, or another fixed form tied to a workflow step in your UI.

**What it lets you do**

* **Skip selection:** The clinician does not pick forms from a list. Recording starts as soon as the iframe loads.
* **Focused workflow:** One session maps to one Medical form template. Your **`onSubmit`** handler receives structured data for that template in **`structured_data.generated_values`**.
* **Faster path:** Fewer steps between opening Form filling and speaking, which suits repeat tasks during a shift.
* **Same callbacks and webhook flow:** Pass **`correlation_id`**, handle **`onSubmit`**, and register a [Partner webhook](/documentation/webhook/overview) the same way as a multi-form session.

The hosted UI shows the **form name** and a **recording banner** so the clinician knows which form they are filling.

Below is an example of how to start a single-form session with one **`template_id`** in **`form_template_ids`**:

```jsx React theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
{isFormOpen && (
  <FormFilling
    form_template_ids={["YOUR_TEMPLATE_ID"]}
    correlation_id={encounterId}
    onSubmit={(result) => {
      handleResult(result);
      setIsFormOpen(false);
    }}
    onCancel={() => setIsFormOpen(false)}
  />
)}
```

## Multi-form session

<span id="multi-form-session" />

A **multi-form session** starts Form filling with **two or more** `template_id` values in **`form_template_ids`**. Suki opens the hosted UI on a **selection screen** first. The clinician confirms which forms to fill, then starts recording.

Use this pattern when one visit or workflow may need **several** Medical form templates, and the clinician should choose which ones apply this time. For example, a head-to-toe assessment where vitals, skin, and neurological forms might all be relevant, but not every form is needed on every round.

**What it lets you do**

* **Offer a form menu:** Pass every template your workflow supports. The clinician picks the subset for this session instead of you hard-coding one form per launch.
* **One recording, multiple forms:** The clinician can fill more than one selected form in a single session. **`onSubmit`** may return multiple entries in **`structured_data.generated_values`**, one per completed form.
* **Change selection during the session:** All forms start selected. The clinician can add or remove forms before or during recording. At least one form must stay selected; when only one remains, they cannot deselect it.
* **Route results per template:** Loop over **`generated_values`** and map each item to the right EHR section using **`form_template_id`** and **`type`**.
* **Same callbacks and webhook flow:** Pass **`correlation_id`**, handle **`onSubmit`**, and register a [Partner webhook](/documentation/webhook/overview) the same way as a single-form session.

Below is an example of how to start a multi-form session with several **`template_id`** values in **`form_template_ids`**:

```jsx React theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
<FormFilling
  form_template_ids={["template-a", "template-b", "template-c"]}
  correlation_id={encounterId}
  onSubmit={(result) => {
    for (const form of result.structured_data.generated_values) {
      routeToEhrSection(form);
    }
  }}
/>
```

## Submit and processing

<span id="get-structured-data" />

**`onSubmit`** and **`form-filling:submitted`** do not fire when the clinician taps **submit**. The hosted UI moves to a processing screen while Suki processes the session on the backend.

When structured data is ready for the session currently open in the SDK, **`onSubmit`** runs in the browser with a **`FormFillingResult`**. The SDK removes the iframe after that callback.

### Processing screen states

While Suki processes the session, the hosted UI may show:

* **Uploading** if the clinician submitted while offline. **Close** is disabled until the upload finishes.
* **Processing** while Suki generates structured data. **Close** is available.
* **Timeout** if processing takes longer than 60 seconds online. **Close** is still available. If the clinician leaves this screen open, results still arrive as **`onSubmit`** / **`form-filling:submitted`** for the same foreground session.

If the clinician closes the processing screen before results reach the SDK, your app receives **`form-filling:closed`**. No **`onSubmit`** from that action.

### Handling results via onSubmit

**`form-filling:submitted`** fires when structured data is ready for the session currently open in the SDK. This happens when the result's **`ambient_session_id`** matches the active session.

Handle this with **`onSubmit`**. It is required in both JavaScript and React. Use it to update your app when results arrive. For example, you can:

* Display the completed forms
* Show a confirmation message

For production, register a [Partner webhook](/documentation/webhook/overview) during [Partner onboarding](/documentation/get-started/partner-onboarding). When processing is complete, Suki sends the same structured results to your server, even if the clinician has already closed the browser.

Use **`correlation_id`** and **`ambient_session_id`** to match the results to the correct patient record. If the webhook does not include the full structured data, call [Get Form filling structured data](/form-filling-api-reference/form-filling-sessions/structured-data) to retrieve it.

<Note>
  Do not rely on **`onSubmit`** alone to save results. Processing may finish after the clinician closes the tab. Use a webhook in production.
</Note>

Refer to [Callbacks](/form-filling-sdk/guides/callbacks), the [Webhook handler example](/form-filling-sdk/examples/webhook-handler), and the [EHR handoff example](/form-filling-sdk/examples/ehr-handoff) for more information.

### Handling background results

**`form-filling:background-submitted`** fires when structured data is ready for a session that is no longer the active session. This happens when the result's **`ambient_session_id`** does not match the session currently open in the SDK.

For example, this can happen if:

* The clinician closes the timeout or processing screen and starts a new session before the previous session finishes.
* A previous or offline session finishes processing while a newer session is already open.

In React, handle this event with **`onBackgroundSubmit`**. The iframe remains open.

If the clinician stays on the timeout or processing screen for the same session, the result is delivered through **`onSubmit`** or **`form-filling:submitted`**, not **`form-filling:background-submitted`**.

## Available cookbooks

<div className="hp-io-method-grid tut-hub-card-grid" data-cookbook-related-grid>
  <a className="hp-io-method-card tut-hub-method-card" href="/documentation/cookbooks/verify-webhook-hmac-signature">
    <div className="tut-hub-card-media tut-hub-card-media--blue" aria-hidden="true" />

    <div className="hp-io-method-card-body">
      <div className="tut-hub-card-badges">
        <span className="hp-wn-badge hp-wn-badge-new">Webhooks</span>
        <span className="hp-wn-badge cookbook-hub-badge-surface cookbook-hub-badge-surface--api">API</span>
      </div>

      <h3 className="hp-io-method-card-title">Verify Webhook HMAC Signature</h3>

      <p className="hp-io-method-card-desc cookbook-hub-card-desc">
        Verify HMAC before parsing JSON.
      </p>

      <div className="hp-io-method-card-meta tut-hub-card-foot" aria-label="5 min">
        <div className="tut-hub-card-foot-meta">
          <span className="hp-io-method-card-meta-time">5 min</span>
        </div>
      </div>
    </div>
  </a>
</div>

## Available tutorials

<div className="hp-io-method-grid tut-hub-card-grid">
  <a className="hp-io-method-card tut-hub-method-card" href="/documentation/tutorials/form-filling-sdk-ehr-handoff">
    <div className="tut-hub-card-media" aria-hidden="true" />

    <div className="hp-io-method-card-body">
      <span className="hp-wn-badge hp-wn-badge-new">Form filling</span>
      <h3 className="hp-io-method-card-title">Build a Form Filling Session with EHR Handoff</h3>

      <p className="hp-io-method-card-desc">
        Open a Form filling session and map structured\_data to your EHR using correlation\_id.
      </p>

      <div className="hp-io-method-card-meta tut-hub-card-foot" aria-label="20 min, Intermediate">
        <div className="tut-hub-card-foot-meta">
          <span className="hp-io-method-card-meta-time">20 min</span>
          <span className="tut-hub-level">Intermediate</span>
        </div>
      </div>
    </div>
  </a>

  <a className="hp-io-method-card tut-hub-method-card" href="/documentation/tutorials/webhook-notification-receiver">
    <div className="tut-hub-card-media tut-hub-card-media--blue" aria-hidden="true" />

    <div className="hp-io-method-card-body">
      <span className="hp-wn-badge hp-wn-badge-new">Webhooks</span>
      <h3 className="hp-io-method-card-title">Build a Webhook Notification Receiver</h3>

      <p className="hp-io-method-card-desc">
        Verify HMAC signatures, parse partner notifications, and handle success and failure events.
      </p>

      <div className="hp-io-method-card-meta tut-hub-card-foot" aria-label="10 min, Beginner">
        <div className="tut-hub-card-foot-meta">
          <span className="hp-io-method-card-meta-time">10 min</span>
          <span className="tut-hub-level">Beginner</span>
        </div>
      </div>
    </div>
  </a>
</div>

## Next steps

<Icon icon="file-lines" iconType="solid" /> Refer to [Callbacks](/form-filling-sdk/guides/callbacks) for all session callbacks and events

<Icon icon="file-lines" iconType="solid" /> Refer to [Configuration](/form-filling-sdk/guides/configuration) for **`form_template_ids`**, **`correlation_id`**, and session options

<Icon icon="file-lines" iconType="solid" /> Refer to [Javascript integration](/form-filling-sdk/javaScript-integration/javaScript) and [React integration](/form-filling-sdk/react-integration/react) for full setup steps
