This is the runnable companion to the [Quickstart](/developers/sdks/quickstart): a
tiny Express server and a single HTML page that embed identity verification
directly in your own page. It is deliberately small so you can read the whole
integration top to bottom, then adapt it to your stack.

The integration is three steps, and it hinges on one split of responsibility:

- Your **backend** holds the **secret key** (`ah_sk_...`) and does the two things
  the browser must never do: create a session and mint short-lived browser
  tokens.
- Your **frontend** loads the SDK with the **publishable key** (`ah_pk_...`, safe
  to expose because it is origin-pinned) and renders the verification.

You write no token-lifecycle code. The SDK owns refresh: whenever it needs a
token it calls your `getSessionToken` callback, which asks your backend for a
fresh one.

## The three steps

| Step | Where    | Call                                         |
| ---- | -------- | -------------------------------------------- |
| 1    | backend  | Create a session (`POST /v1/sessions`)       |
| 2    | backend  | Mint a browser token (`.../browser_token`)   |
| 3    | frontend | `Checktiv.init(...)` then `mountProvisioned` |

## Step 1 - Create a session (backend)

Your secret key must never reach the browser. Create the session on your server
and return only the session id (a `vs_...` string). Every response is wrapped in
a `data` envelope, so the id is at `data.id`.

```js
// POST /api/session (your backend)
app.post('/api/session', async (_req, res) => {
  const response = await fetch('https://api.us.checktiv.com/v1/sessions', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.CHECKTIV_SECRET_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      // `checks` is an array of check-type strings. `id_verification`
      // renders the managed document + selfie flow in the SDK.
      checks: ['id_verification'],
      // Inline applicant details for a new applicant. Every field is optional.
      applicant: { first_name: 'Ada', last_name: 'Lovelace', email: 'ada@example.com' },
    }),
  });
  const { data } = await response.json();
  res.json({ sessionId: data.id }); // a `vs_...` string
});
```

## Step 2 - Mint a browser token (backend)

The SDK calls your `getSessionToken` callback whenever it needs a `bt_...` token.
Back it with a mint endpoint. This route takes no request body, and the token is
returned at `data.browser_token`.

```js
// GET /api/token?sessionId=... (your backend)
app.get('/api/token', async (req, res) => {
  const { sessionId } = req.query; // the `vs_...` id from Step 1
  const response = await fetch(
    `https://api.us.checktiv.com/v1/sessions/${sessionId}/browser_token`,
    {
      method: 'POST',
      headers: { Authorization: `Bearer ${process.env.CHECKTIV_SECRET_KEY}` },
    },
  );
  const { data } = await response.json();
  res.set('Cache-Control', 'no-store'); // never cache a short-lived token
  res.json({ token: data.browser_token }); // a `bt_...` string
});
```

The `bt_...` token is short-lived. You never schedule a refresh yourself: the SDK
calls `getSessionToken` again when it needs a new one.

## Step 3 - Load and mount the SDK (frontend)

Load the SDK bundle from the CDN. It exposes a global `Checktiv`.

```html
<script src="https://sdk.us.checktiv.com/v1/sdk.js" crossorigin="anonymous"></script>
```

This loads the latest release. For production, pin to a specific version with a
Subresource Integrity hash instead. SRI requires the immutable pinned URL
(`/sdk/<ver>/sdk.js`), not the moving `/v1/sdk.js` pointer. See
[Versioning](/developers/sdks/versioning) for the pin URL shape and the SRI hash
from the [release notes](/developers/sdks/release-notes).

Then initialize the client with your publishable key plus a `getSessionToken`
callback that fetches a fresh token from your backend, and mount into a target
element:

```html
<div id="checktiv"></div>

<script>
  const client = Checktiv.init({
    publishableKey: 'ah_pk_us_test_...', // public by design (origin-pinned)
    getSessionToken: function () {
      // Ask YOUR backend for a fresh browser token. This is the whole token
      // lifecycle - the SDK calls this whenever it needs one.
      return fetch('/api/token?sessionId=' + encodeURIComponent(sessionId))
        .then(function (r) {
          return r.json();
        })
        .then(function (d) {
          return d.token;
        });
    },
  });

  // `mountProvisioned` reads the modules the server declared for the session
  // and renders them. For an `id_verification` session that is the managed IDV
  // (document + selfie) capture flow. The secret key is never in this code.
  client.mountProvisioned({
    target: document.getElementById('checktiv'),
    onEvent: function (event) {
      if (event.type === 'checktiv.idv.submitted') {
        // Capture finished. Wait for the webhook verdict before acting on it.
      }
    },
  });
</script>
```

`checktiv.idv.submitted` means capture finished, not that verification passed.
The authoritative verdict arrives on your server as a signed webhook. See
[Verdict and webhooks](/developers/sdks/verdict-and-webhooks) for the receiver
and signature verification.

## Run the full sample

The complete runnable version of this walkthrough ships as an SDK example named
`idv-sdk-quickstart`: one Express server file, one HTML file, a `.env.example`,
and a run script. It reads the secret key from the `CHECKTIV_SK_KEY` environment
variable, never from a file, and injects the publishable key into the page at
render time.

```bash
npm install
export CHECKTIV_SK_KEY='ah_sk_...'     # your secret key, from your environment
export CHECKTIV_PK_KEY='ah_pk_...'     # your publishable key
node server.js                          # serves the page on port 3000
```

Because the publishable key is origin-pinned, open the page through the origin
your key is registered for, not bare `http://localhost`.

## Next steps

- [Quickstart](/developers/sdks/quickstart) - the same flow with the webhook receiver
- [Token handoff](/developers/sdks/token-handoff) - the full session and token lifecycle
- [Error reference](/developers/sdks/error-reference) - handle every error code in `onEvent`
- [React](/developers/sdks/react) - React integration with `<ChecktivProvider>`