# Quickstart



You need a Surface environment and an SDK form record. Create one in the dashboard (Forms, Create New Form, Build SDK form) or through the [MCP server](/docs/forms-sdk/building-with-ai).

## 1. Install [#1-install]

```bash
npm install @surface-labs/forms-sdk
```

pnpm and yarn work the same. ESM-first, zero runtime dependencies; `init` returns a promise, so use `target: "esnext"` for top-level `await` or wrap the call in an async function. Alternatively, self-host the prebuilt IIFE bundle and use the global `SurfaceFormsSDK`.

## 2. Get your IDs [#2-get-your-ids]

`init` needs `environmentId`, `formId`, and `apiBaseUrl`. The `get_sdk_binding_map` MCP tool returns all three plus a ready-to-paste `init` snippet and every question and step ID; the form's page in the dashboard shows the same IDs.

<Warning>
  A wrong `apiBaseUrl` is the number one boot failure. The default is `https://forms.withsurface.com`; any other deployment 404s until you pass its host. If `init` throws, the error names the URL it tried.
</Warning>

## 3. Write the HTML [#3-write-the-html]

One container per step, one `data-question-id` per question:

```html
<div id="form" hidden>
  <section data-step-id="step_about">
    <label>
      Work email
      <input type="email" data-question-id="q_email" />
    </label>
    <button type="button" class="surface-next-button">Continue</button>
  </section>

  <section data-step-id="step_role" hidden>
    <div data-question-id="q_role" data-question-type="MultipleOptionsQuestion">
      <label><input type="radio" name="role" value="Sales Ops" /> Sales Ops</label>
      <label><input type="radio" name="role" value="Leadership" /> Leadership</label>
    </div>
    <button type="button" class="surface-submit-button">Submit</button>
  </section>

  <section data-step-id="step_thanks" hidden><h2>Thanks!</h2></section>
</div>
```

* Every step container after the first is authored `hidden`; otherwise steps flash stacked during boot.
* The choice question binds as a group: `data-question-type` on the container, each `value` equal to the record's option key, no `data-field-name` on options.
* The submit button sits on the last question step; the SDK reveals the thank-you step itself.

Full attribute vocabulary: [binding contract](/docs/forms-sdk/binding).

## 4. Boot the SDK [#4-boot-the-sdk]

```js
import { SurfaceForms } from "@surface-labs/forms-sdk";

const form = await SurfaceForms.init({
  environmentId: "env_xxx",
  formId: "form_xxx",
  apiBaseUrl: "https://forms.withsurface.com",
  container: document.querySelector("#form"),
});
document.querySelector("#form").hidden = false;
```

With the IIFE bundle, call `SurfaceFormsSDK.SurfaceForms.init(...)` the same way.

During development, add `debug: true` to log every captured answer, lifecycle event, and navigation intent. Remove before shipping.

## 5. Verify [#5-verify]

Submit a test response, then check the Responses table (or `get_response` over MCP). SDK responses carry `surfaceTagStatus: "sdk"` in their metadata.

If an answer is missing, the browser console warns about unbound record questions. See [troubleshooting](/docs/forms-sdk/troubleshooting) for the full symptom table.

## Next steps [#next-steps]

<CardGroup cols="2">
  <Card title="Binding contract" icon="list-check" href="/docs/forms-sdk/binding">
    Multi-field components, dropdowns, and inference rules.
  </Card>

  <Card title="Navigation" icon="diagram-project" href="/docs/forms-sdk/navigation">
    Branching and code-driven navigation.
  </Card>

  <Card title="Headless and React" icon="react" href="/docs/forms-sdk/headless-and-react">
    Drive the engine from your components.
  </Card>

  <Card title="Build with AI" icon="wand-magic-sparkles" href="/docs/forms-sdk/building-with-ai">
    Let an agent build and validate the whole form.
  </Card>
</CardGroup>
