> ## 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 JavaScript Integration

> Integrate the Form filling SDK in a JavaScript application using FormFillingClient

This guide walks you through integrating the Form filling SDK in a JavaScript application. You create **`SukiAuthManager`** and **`FormFillingClient`** once, then call **`client.start()`** when the clinician opens Form filling.

## Prerequisites

Before you begin, confirm you have:

* **Packages:** **`@suki-sdk/form-filling`** and **`@suki-sdk/core`**
* **Partner credentials:** **`partnerId`** and **`partnerToken`** from [Partner onboarding](/documentation/get-started/partner-onboarding)
* **Template IDs:** **`template_id`** UUIDs from your **Suki support team** for **`form_template_ids`**.
* **Encounter ID:** pass your encounter ID as **`correlation_id`**, refer to [Configuration](/form-filling-sdk/guides/configuration#correlation_id-your-encounter-id) for more details.
* **Browser and layout:** HTTPS, microphone access, and a DOM container with explicit height

Refer to [Prerequisites](/form-filling-sdk/prerequisites) for webhooks and CSP.

## Integration pattern

Create auth and the client **once per page**. Open Form filling only when the clinician taps your button.

<CardGroup cols={2}>
  <Card title="Suki Auth Manager" icon="lock">
    One **`SukiAuthManager`** after the partner token is available.
  </Card>

  <Card title="Form Filling Client" icon="cube">
    One **`FormFillingClient`**. Reuse it for every **`start()`** call on that page.
  </Card>
</CardGroup>

## Install the packages

<CodeGroup title="Install @suki-sdk/form-filling and @suki-sdk/core">
  ```shell pnpm theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
  pnpm add @suki-sdk/form-filling @suki-sdk/core
  ```

  ```shell npm theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
  npm install @suki-sdk/form-filling @suki-sdk/core
  ```

  ```shell yarn theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
  yarn add @suki-sdk/form-filling @suki-sdk/core
  ```
</CodeGroup>

## Step 1: Add container HTML

The hosted Form filling UI mounts inside the element you pass as **`rootElement`**. That node must have **real height**, or the iframe looks blank.

```html HTML theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
<div id="suki-form-container" style="width: 100%; height: 600px;"></div>
<button type="button" id="fill-forms-btn">Fill Forms with Suki</button>
```

<Tip>
  Refer to [Configuration](/form-filling-sdk/guides/configuration#container-sizing) for recommended CSS.
</Tip>

## Step 2: Create auth manager

Create one **`SukiAuthManager`** to sign in to Suki. Use the following example code:

```javascript JavaScript theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
import { SukiAuthManager } from "@suki-sdk/core";

const authManager = new SukiAuthManager({
  partnerId: "YOUR_PARTNER_ID", // Required
  partnerToken: "YOUR_PARTNER_TOKEN", // Required
  environment: "staging", // Optional
  loginOnInitialize: true, // Optional
  autoRegister: false, // optional (default true); when true, provider fields below are often required
  providerId: "YOUR_PROVIDER_ID", // Optional
  providerName: "Dr. Jane Smith", // Optional
});
```

<Tip>
  Refer to [Authentication](/form-filling-sdk/guides/authentication) for provider fields and other sign-in options.
</Tip>

## Step 3: Create Form filling client

Create one **`FormFillingClient`** and pass the auth manager. Optionally wire **`onError`** for configuration and runtime errors.

```javascript JavaScript theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
import { FormFillingClient } from "@suki-sdk/form-filling";

const client = new FormFillingClient({
  authManager,
  onError: (err) => console.error(err.code, err.reason, err.message),
});
```

## Step 4: Start a session on user action

When the clinician taps your button, call **`await client.start({ ... })`** from a click handler after the DOM is ready.

Pass these options to **`start()`**:

* **`rootElement`**: <Badge color="red" size="sm">Required</Badge> The container from Step 1.
* **`form_template_ids`**: <Badge color="red" size="sm">Required</Badge> **`template_id`** UUIDs from your Suki support team.
* **`correlation_id`**: <Badge color="green" size="sm">Optional</Badge> Your encounter or appointment ID. Strongly recommended so results map to the correct record.
* **`onSubmit`**: <Badge color="red" size="sm">Required</Badge> Called when structured JSON is ready after processing, not when the clinician taps submit. Receives a **`FormFillingResult`**.
* **`onReady`**, **`onCancel`**: <Badge color="green" size="sm">Optional</Badge>

```javascript JavaScript expandable theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
document.getElementById("fill-forms-btn").addEventListener("click", async () => {
  const root = document.getElementById("suki-form-container");
  if (!root) return;

  await client.start({
    rootElement: root,
    form_template_ids: ["YOUR_TEMPLATE_ID"],
    correlation_id: "YOUR_ENCOUNTER_ID",
    onReady: () => console.log("Form UI ready"),
    onSubmit: (result) => {
      console.log("Session:", result.ambient_session_id);
      console.log("Correlation:", result.correlation_id);
      console.log("Forms:", result.structured_data.generated_values);
    },
    onCancel: () => console.log("Cancelled"),
  });
});
```

## Callbacks and events

Your app needs to know when Form filling is ready, when structured results arrive, and when the clinician cancels. You can handle that in two ways.

**Option 1: Pass callbacks on `start()`**

Use this for logic tied to one session. Pass functions on the same object as `rootElement` and `form_template_ids`:

| Callback       | When it runs                                                                                 |
| -------------- | -------------------------------------------------------------------------------------------- |
| **`onReady`**  | The hosted UI loaded and is ready for the clinician                                          |
| **`onSubmit`** | Structured JSON is ready after processing (map this to your EHR or show results in your app) |
| **`onCancel`** | The clinician left without submitting                                                        |

**Option 2: Listen with `client.on()`**

Use this for handlers that stay active across many sessions, such as logging, analytics, or a shared error handler. Register once when you create the client:

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

Common events include **`form-filling:session-started`**, **`form-filling:background-submitted`** (results for a superseded session), and **`form-filling:error`**.
Refer to [Callbacks](/form-filling-sdk/guides/callbacks) for **`FormFillingResult`** shape and [Events](/form-filling-sdk/api-reference/events) for every event and payload.

## Starting another session

When the clinician opens Form filling again, call **`start()`** a second time. The SDK closes the previous session and opens a new one. You do not need **`stop()`** first.

## Complete code example

Below is a full page example: sign-in, client setup, and a button that opens Form filling.

```javascript JavaScript expandable theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
import { SukiAuthManager } from "@suki-sdk/core";
import { FormFillingClient } from "@suki-sdk/form-filling";

const authManager = new SukiAuthManager({
  partnerId: "YOUR_PARTNER_ID", // Required
  partnerToken: "YOUR_PARTNER_TOKEN", // Required
  environment: "staging", // Optional
  loginOnInitialize: true, // Optional
  autoRegister: false, // optional (default true); when true, providerName and providerOrgId are often required
});

const client = new FormFillingClient({
  authManager, // Required
  onError: (err) => console.error(err.code, err.message), // Optional
});

async function openFormFilling() {
  const root = document.getElementById("suki-form-container");
  if (!root) return;

  await client.start({
    rootElement: root, // Required
    form_template_ids: ["YOUR_TEMPLATE_ID"], // Required
    correlation_id: "YOUR_ENCOUNTER_ID", // Optional
    onReady: () => console.log("Form UI ready"), // Optional
    onSubmit: (result) => { // Required
      for (const form of result.structured_data.generated_values) {
        console.log(form.form_template_id, form.data);
      }
    },
    onCancel: () => console.log("Cancelled"), // Optional
  });
}

document.addEventListener("DOMContentLoaded", () => {
  document.getElementById("fill-forms-btn")?.addEventListener("click", () => {
    void openFormFilling();
  });
});
```

Your integration works when the Form filling UI appears, the microphone is active, and **`onSubmit`** returns **`structured_data`**.

## Best practices

<Tip>
  * **Reuse one client and one auth manager** for the page. Do not create a new **`FormFillingClient`** on every click.

  * **Pass `onSubmit`.** It is required. Map **`structured_data.generated_values`** to your EHR or backend.

  * **Pass `correlation_id`.** Use your encounter ID so callbacks and webhooks match the correct record.

  * **Register a partner webhook** for production saves on your server, not browser **`onSubmit`** alone.

  * **Give the container height** before **`start()`**. Zero height is the most common cause of a blank UI.

  * **Use template IDs from Suki support.** The SDK validates IDs at **`start()`** and drops unsupported values.
</Tip>

## Troubleshooting

<AccordionGroup>
  <Accordion title="Blank UI After `start()`">
    Set height on **`#suki-form-container`** before **`start()`**. Add CSP **`frame-src`** for **`https://sdk.suki.ai`** (production) or **`https://sdk.suki-stage.com`** (staging). Refer to [Configuration](/form-filling-sdk/guides/configuration#container-sizing) and [Prerequisites](/form-filling-sdk/prerequisites).
  </Accordion>

  <Accordion title="`SUKI_FF_001` Immediately">
    **`form_template_ids`** is empty, or no IDs match your partner account. Pass at least one **`template_id`** UUID from Suki support. Refer to [Form filling templates](/documentation/concepts/form-filling/form-filling-templates).
  </Accordion>

  <Accordion title="`SUKI_FF_002` Handshake Timeout">
    The iframe did not complete the ready handshake within 10 seconds. Check network, ad blockers, and CSP. Confirm the page uses HTTPS.
  </Accordion>

  <Accordion title="`SUKI_FF_003` Fetch Templates Failed">
    Auth failed or the wrong environment (staging vs production) while loading the template list. Retry and confirm **`partnerId`**, **`partnerToken`**, and **`environment`**.
  </Accordion>

  <Accordion title="`SUKI_FF_004` Iframe Runtime Error">
    Microphone permission denied, session start/submit failed, or form generation failed. Read **`err.message`**, show a retry option, and confirm network access.
  </Accordion>

  <Accordion title="UI Never Opens">
    Sign-in failed before the hosted UI loads. Verify **`partnerId`** and **`partnerToken`** for your environment. Refer to [Authentication](/form-filling-sdk/guides/authentication).
  </Accordion>

  <Accordion title="No `onSubmit` Callback">
    The clinician closed the processing screen early, or the tab closed before processing finished. Register a [partner webhook](/documentation/webhook/overview) for server-side delivery.
  </Accordion>

  <Accordion title="`onSubmit` Never Maps to Your Encounter">
    Pass your encounter ID as **`correlation_id`** at session start so callbacks and webhooks match the correct record.
  </Accordion>

  <Accordion title="Log Errors During Development">
    Wire **`onError`** or **`client.on("form-filling:error", ...)`** so errors are visible in the console:

    ```javascript JavaScript theme={"theme":{"light":"github-dark","dark":"material-theme-darker"}}
    client.on("form-filling:error", (err) => {
      console.error(err.code, err.reason, err.message);
    });
    ```
  </Accordion>
</AccordionGroup>

<Note>
  Refer to [Error handling](/form-filling-sdk/guides/error-handling) for the full error code table.
</Note>

## Next steps

<Icon icon="file-lines" iconType="solid" /> [Session workflow](/form-filling-sdk/guides/integration-patterns) for single-form vs multi-form behavior

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

<Icon icon="file-lines" iconType="solid" /> [React integration](/form-filling-sdk/react-integration/react) if you use React
