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

> Register onSubmit, onCancel, and event handlers for Form filling SDK sessions

<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">
    Callbacks let your app respond to events during a Form filling session, such as when the interface is ready, the clinician submits a form, or the session is canceled. Pass callbacks when you call `start()` to handle events for that session. If you want the same handler to run across multiple sessions, use event listeners with `client.on()` instead.

    <br />

    <br />

    When processing is complete, `onSubmit` returns the structured form data. For production, also configure a partner webhook so your server receives the results even if the browser is closed before processing finishes.
  </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">July 2026</span>
  </div>
</div>

Callbacks let your app run your own code when Form filling reaches key moments in a session. You pass functions to the SDK; the SDK calls them when those moments happen. You do not poll for structured data.

**Registering callbacks**

In JavaScript, pass callbacks on the object you give to **`client.start()`**. In React, pass the same fields as props on **`<FormFilling>`**.

| Callback                 | When it runs                                                                                                                                       | Required |
| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| **`onSubmit`**           | Structured JSON is ready after processing, not when the clinician taps submit                                                                      | Yes      |
| **`onReady`**            | The hosted UI loaded and is ready for the clinician                                                                                                | No       |
| **`onCancel`**           | The clinician left without submitting                                                                                                              | No       |
| **`onBackgroundSubmit`** | Results for a superseded session while the iframe is still open (React only): result `ambient_session_id` no longer matches the foreground session | No       |

<Tip>
  For every session field, refer to [Configuration](/form-filling-sdk/guides/configuration). For required vs optional setup, refer to the [JavaScript](/form-filling-sdk/javaScript-integration/javaScript) or [React](/form-filling-sdk/react-integration/react) integration guide.
</Tip>

## Supported Form filling SDK callbacks

Pass these callbacks on **`client.start()`** in JavaScript, or as props on **`<FormFilling>`** in React.

<Expandable title="Supported callbacks" defaultOpen="true">
  <ResponseField name="onSubmit" type="function" required>
    Runs when structured results are ready after processing completes. Receives a **`FormFillingResult`** with **`structured_data.generated_values`** and **`non_generated_values`**. Map **`generated_values`** to your EHR or show results in your app.
  </ResponseField>

  <ResponseField name="onReady" type="function">
    **Optional.** Runs when the hosted UI is loaded and ready. In JavaScript, runs after **`start()`** resolves the iframe handshake.
  </ResponseField>

  <ResponseField name="onCancel" type="function">
    **Optional.** Runs when the clinician cancels during form selection or recording without submitting. No structured data is returned.
  </ResponseField>

  <ResponseField name="onBackgroundSubmit" type="function">
    **React only.** Runs for **`form-filling:background-submitted`**: results whose **`ambient_session_id`** no longer matches the foreground session (replaced or reopened session), not merely because processing took longer than 60 seconds. Use the same handler shape as **`onSubmit`**.
  </ResponseField>
</Expandable>

## SDK callbacks vs webhooks

After the clinician submits, Suki processes the session before structured JSON is ready. Your app can receive those results in two places:

| Where       | What receives results                              | Best for                                                                            |
| ----------- | -------------------------------------------------- | ----------------------------------------------------------------------------------- |
| **Browser** | **`onSubmit`** callback                            | Showing filled forms or confirming success while the clinician is still in your app |
| **Server**  | [Partner webhook](/documentation/webhook/overview) | Saving structured data to your EHR or backend                                       |

**`onSubmit`** and your webhook receive the same structured payload. The callback runs in the browser when processing completes. The webhook runs on your server.

<Note>
  Do not rely on **`onSubmit`** alone to save results. The clinician may close the tab before processing finishes and before your app persists the data.
</Note>

Refer to [Session workflow](/form-filling-sdk/guides/integration-patterns#get-structured-data) and the [Webhook handler example](/form-filling-sdk/examples/webhook-handler).

## Form filling SDK events

Session callbacks run for one **`start()`** call or one **`<FormFilling>`** mount. **`client.on()`** stays wired across many sessions, which is useful for logging, analytics, or a shared error handler.

Subscribe once when you create the client:

```javascript JavaScript theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
const offSubmitted = client.on("form-filling:submitted", (result) => {
  console.log(result.ambient_session_id);
});

// Later, on app teardown:
// offSubmitted();
```

| Event                               | When                                                                                |
| ----------------------------------- | ----------------------------------------------------------------------------------- |
| `form-filling:ready`                | Hosted UI loaded and ready                                                          |
| `form-filling:session-started`      | Session created after form selection (or immediately for single-form)               |
| `form-filling:submitted`            | Structured data ready for the active session                                        |
| `form-filling:background-submitted` | Results for a superseded session (result `ambient_session_id` ≠ foreground session) |
| `form-filling:cancelled`            | Clinician cancelled during selection or recording                                   |
| `form-filling:closed`               | Clinician closed the processing screen before results reached the SDK               |
| `form-filling:error`                | Configuration or runtime error                                                      |

Refer to [Events](/form-filling-sdk/api-reference/events) for payload fields and [Event handling example](/form-filling-sdk/examples/event-handling) for a full listener setup.

<span id="formfillingresult" />

<span id="formfillingresult-payload-shape" />

## FormFillingResult payload shape

**`FormFillingResult`** is the JSON object the SDK sends when a session finishes processing and structured form data is ready. It is not sent when the clinician taps **submit**; the SDK still processes the recording first.

Your handler receives a **`FormFillingResult`** from **`onSubmit`**, **`onBackgroundSubmit`**, **`form-filling:submitted`**, or **`form-filling:background-submitted`**. Use it to match the session to your patient record and map filled forms into your EHR.

| Field                                      | What to use it for                                                                                                                            |
| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
| **`ambient_session_id`**                   | Suki session UUID. Use it to deduplicate saves. This is the Form filling session ID, not an ambient clinical note session ID.                 |
| **`correlation_id`**                       | Your encounter UUID, if you passed **`correlation_id`** at session start. Match it to the patient record in your app.                         |
| **`structured_data.generated_values`**     | Forms with filled values. Map each **`data`** object to your EHR using **`form_template_id`** and **`type`**.                                 |
| **`structured_data.non_generated_values`** | Templates with no output in this session. Entries are often sparse: only **`form_template_id`** may be set. Skip them or show an empty state. |

```json JSON theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
{
  "ambient_session_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "correlation_id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
  "structured_data": {
    "generated_values": [
      {
        "id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
        "form_template_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
        "title": "Skin Assessment",
        "status": "MEDICAL_FORM_STATUS_COMPLETED",
        "type": "SKIN_ASSESSMENT",
        "correlation_id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
        "data": {
          "location": "left forearm",
          "size_cm": 2.5
        }
      }
    ],
    "non_generated_values": [
      {
        "form_template_id": "8b3e2a1f-4c5d-6e7f-8901-234567890abc"
      }
    ]
  }
}
```

Each item in **`generated_values`** is a **`MedicalFormInstance`**: **`id`**, **`form_template_id`**, **`title`**, **`status`**, **`type`**, **`data`**, and optional **`correlation_id`**. Refer to [FormFillingResult](#formfillingresult) above for the full field list.

<Expandable title="FormFillingResult fields" defaultOpen="false">
  <ResponseField name="ambient_session_id" type="string" required>
    Suki Form filling session UUID. Use for deduplication and webhook matching.
  </ResponseField>

  <ResponseField name="correlation_id" type="string">
    **Optional.** Your encounter UUID from session start, if you passed **`correlation_id`** at **`start()`** or on **`<FormFilling>`**.
  </ResponseField>

  <ResponseField name="structured_data.generated_values" type="MedicalFormInstance[]">
    Filled forms with structured output.
  </ResponseField>

  <ResponseField name="structured_data.non_generated_values" type="MedicalFormInstance[]">
    Templates with no output. Often sparse: only **`form_template_id`** is present.
  </ResponseField>
</Expandable>

## Next steps

<Icon icon="file-lines" iconType="solid" /> Refer to [Basic usage example](/form-filling-sdk/examples/basic-usage) for session status UI with **`useFormFilling()`**

<Icon icon="file-lines" iconType="solid" /> Refer to [EHR handoff example](/form-filling-sdk/examples/ehr-handoff) for mapping **`generated_values`** to your chart

<Icon icon="file-lines" iconType="solid" /> Refer to [Configuration](/form-filling-sdk/guides/configuration) for all session options
