> ## Documentation Index
> Fetch the complete documentation index at: https://docs.go.gbgplc.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Integration Checklist

> Step-by-step walkthrough from zero to a working GBGBridge Web SDK integration.

A step-by-step walkthrough from zero to a working GBGBridge integration. Follow this page in order; each step links to the detailed guide if you need more context.

## Prerequisites

* Node.js 18+ for your bundler / package manager
* A modern evergreen browser (ES2020+) for development and testing
* React 19 (if using `@gbg/go-bridge-web-react`), or any framework with `BridgeHost` directly
* A journey URL minted server-side via `@gbg/go-core` — see [Journey URL](/docs/go-v2/developer-integration/sdks/web/journey-url)
* A backend endpoint that returns the journey URL to your frontend (never expose `@gbg/go-core` credentials in the browser)

## Step 1: Install the SDK

Install one or both packages depending on your framework:

```bash theme={null}
# React 19 app
npm install @gbg/go-bridge-web @gbg/go-bridge-web-react

# Vanilla JS / Vue / Angular / Svelte
npm install @gbg/go-bridge-web
```

<Note>
  These packages are not yet on public npm — publish lands with [GGO-13991](https://gbg.atlassian.net/browse/GGO-13991). The SDK source lives in the [reference build](https://github.com/gbgplc/gbg-go-web-reference) under `packages/` until the public package is available. See [Getting Started — Installation](/docs/go-v2/developer-integration/sdks/web/getting-started#installation).
</Note>

## Step 2: Obtain a journey URL

Your backend uses `@gbg/go-core` server-side to mint a tokenized journey URL. The frontend fetches this URL via your own API and passes it to the iframe.

<Note>
  Never run `@gbg/go-core` in the browser — its OAuth flow uses a client secret that must not ship in browser code. See [Journey URL](/docs/go-v2/developer-integration/sdks/web/journey-url) for the full backend pattern and [Security](/docs/go-v2/developer-integration/sdks/web/security#token-exchange-and-credential-safety) for the rationale.
</Note>

## Step 3: Configure page-level requirements

These are properties of your host page, not the SDK. Set them once for each environment you ship to.

### Content Security Policy

Add the journey origin to your CSP `frame-src` directive:

```
Content-Security-Policy: frame-src https://journey.example.com;
```

List each environment explicitly (staging, production). Avoid `frame-src *`.

### iframe `allow` attribute

Set the minimum browser permissions the journey needs:

```html theme={null}
<iframe src="..." allow="camera"></iframe>
```

Most identity journeys need at least `camera`. Add `microphone` only if a step requires it. Do not blanket-grant all permissions.

### HTTPS

In production, both the host page and the journey URL must be HTTPS. Browsers block mixed content (HTTPS page + HTTP iframe) and the integration will fail silently.

## Step 4: Set up the bridge host

Two code paths depending on your framework — the React adapter for React 19 apps, and the core class directly for everything else. Both use the same underlying `BridgeHost`, so the semantics are identical.

### React

```tsx theme={null}
import { BridgeHostProvider, useBridgeCapability } from "@gbg/go-bridge-web-react";
import { useState } from "react";

export function JourneyView({ journeyUrl }: { journeyUrl: string }) {
  const [iframe, setIframe] = useState<HTMLIFrameElement | null>(null);

  return (
    <BridgeHostProvider
      iframe={iframe}
      allowedOrigins={[new URL(journeyUrl).origin]}
      hostVersion="1.0.0"
      capabilities={{
        "camera.document": { supported: true, version: "1.0" },
      }}
    >
      <DocumentCaptureHandler />
      <iframe ref={setIframe} src={journeyUrl} allow="camera" />
    </BridgeHostProvider>
  );
}
```

<Note>
  Track the iframe in `useState`, not `useRef`. Refs are not reactive, so the provider's effect would never see the iframe attach. See [Common Pitfalls](/docs/go-v2/developer-integration/sdks/web/getting-started#provider-never-builds-the-host).
</Note>

### Framework-agnostic

```typescript theme={null}
import { BridgeHost } from "@gbg/go-bridge-web";

const iframe = document.getElementById("journey-frame") as HTMLIFrameElement;
iframe.src = journeyUrl;

const host = new BridgeHost({
  iframe,
  allowedOrigins: [new URL(journeyUrl).origin],
  hostVersion: "1.0.0",
  capabilities: {
    "camera.document": { supported: true, version: "1.0" },
  },
});
```

## Step 5: Declare capabilities and register handlers

The two-step pattern is essential — declaring a capability does not register a handler, and registering a handler does not declare the capability. Both are required for the journey to invoke the host.

```tsx theme={null}
function DocumentCaptureHandler() {
  useBridgeCapability("camera.document.capture", {
    handle: async (request, responder) => {
      const image = await captureDocument();
      responder.success({ image });
    },
  });
  return null;
}
```

See [Capability Handling](/docs/go-v2/developer-integration/sdks/web/capability-handling) for the full pattern including custom capabilities and `registerCustomCapability`.

## Step 6: Report permission state

Surface browser permission state to the journey so it can prompt the user before requesting capture:

```typescript theme={null}
async function getCameraPermissionState() {
  if (!navigator.permissions) return "prompt";
  try {
    const status = await navigator.permissions.query({ name: "camera" as PermissionName });
    return status.state;
  } catch {
    return "prompt";
  }
}
```

Pass the result through `CapabilityInfo.permissionState`:

```typescript theme={null}
capabilities: {
  "camera.document": {
    supported: true,
    version: "1.0",
    permissionState: cameraPermissionState,
  },
}
```

## Step 7: Subscribe to journey lifecycle events

Wire up listeners for terminal journey events so you can route the user appropriately:

```tsx theme={null}
import { useBridgeHostEvent } from "@gbg/go-bridge-web-react";
import { BridgeActions } from "@gbg/go-bridge-web";

function LifecycleListeners() {
  useBridgeHostEvent(BridgeActions.JOURNEY_COMPLETED, (data) => {
    // route to success screen
  });
  useBridgeHostEvent(BridgeActions.JOURNEY_FAILED, (data) => {
    // route to failure screen
  });
  useBridgeHostEvent(BridgeActions.JOURNEY_ABANDONED, () => {
    // route to retry prompt
  });
  return null;
}
```

See [Messaging — Bridge Event Catalogue](/docs/go-v2/developer-integration/sdks/web/messaging#bridge-event-catalogue-iframe-host) for the full event list.

## Step 8: Build and run

1. Start your dev server (`npm run dev` or equivalent).
2. Navigate to the page that renders `JourneyView`.
3. The journey loads inside the iframe.
4. The journey sends `capability.query`; the SDK responds automatically with the capabilities you declared.
5. When the journey reaches a capture step, your handler runs.
6. On terminal events (`journey.completed`, `journey.failed`, etc.), your event listeners route the user.

### Verify it works

* The journey loads and renders without CSP errors or blocked frames.
* `capability.query` response includes the capabilities you declared (visible in `delegate.onMessageSent`).
* Capture requests trigger your handler; the response data flows back to the journey.
* Terminal events route correctly.
* No `console.warn` from `HostIframeChannel` indicating dropped outbound messages.

### Common issues

| Symptom                                       | Likely cause                                                    | Fix                                                                         |
| --------------------------------------------- | --------------------------------------------------------------- | --------------------------------------------------------------------------- |
| Blank iframe                                  | CSP `frame-src` missing journey origin, or mixed-content block  | Add the origin to `frame-src`; switch to HTTPS                              |
| `capability.query` returns empty              | Forgot the `capabilities` prop (only registered handlers)       | Add `capabilities={{ ... }}` to the provider                                |
| Provider's `host` stays `null`                | Used `useRef` instead of `useState` for the iframe              | Switch to `useState<HTMLIFrameElement \| null>(null)` + `ref={setIframe}`   |
| Inbound messages silently dropped             | Origin allowlist mismatch (trailing slash, path, or wrong port) | Use exact origin strings: `https://example.com`                             |
| Outbound dropped with `console.warn`          | `iframe.src` not set when send fires                            | Set `iframe.src` before the first outbound message                          |
| Capture request times out on the journey side | Handler throws or never calls `responder.success/error/etc.`    | Make sure every code path in your handler ends in a terminal responder call |

See [Troubleshooting](/docs/go-v2/developer-integration/sdks/web/troubleshooting) for the comprehensive diagnostic guide.

## Complete minimal example

Putting it all together — a minimal React component that loads a journey, declares document capture, registers a handler, and routes the completion event:

```tsx theme={null}
import { BridgeHostProvider, useBridgeCapability, useBridgeHostEvent } from "@gbg/go-bridge-web-react";
import { BridgeActions } from "@gbg/go-bridge-web";
import { useState, useEffect } from "react";

export function JourneyView({ journeyUrl, onComplete }: { journeyUrl: string; onComplete: (data: any) => void }) {
  const [iframe, setIframe] = useState<HTMLIFrameElement | null>(null);
  const [permissionState, setPermissionState] = useState("prompt");

  useEffect(() => {
    if (!navigator.permissions) return;
    navigator.permissions
      .query({ name: "camera" as PermissionName })
      .then((status) => setPermissionState(status.state))
      .catch(() => undefined);
  }, []);

  return (
    <BridgeHostProvider
      iframe={iframe}
      allowedOrigins={[new URL(journeyUrl).origin]}
      hostVersion="1.0.0"
      capabilities={{
        "camera.document": { supported: true, version: "1.0", permissionState },
      }}
    >
      <DocumentCaptureHandler />
      <CompletionListener onComplete={onComplete} />
      <iframe ref={setIframe} src={journeyUrl} allow="camera" />
    </BridgeHostProvider>
  );
}

function DocumentCaptureHandler() {
  useBridgeCapability("camera.document.capture", {
    handle: async (_request, responder) => {
      const image = await captureDocument();
      responder.success({ image });
    },
  });
  return null;
}

function CompletionListener({ onComplete }: { onComplete: (data: any) => void }) {
  useBridgeHostEvent(BridgeActions.JOURNEY_COMPLETED, onComplete);
  return null;
}

async function captureDocument(): Promise<string> {
  // Your camera UI here — returns a base64 image
  return "";
}
```

See [Hello Journey](/docs/go-v2/developer-integration/sdks/web/examples/hello-journey) for the fully annotated example.

## What's next

* Add selfie capture — same pattern as document capture, with action `"camera.selfie.capture"`.
* Add custom capabilities — see [Capability Handling](/docs/go-v2/developer-integration/sdks/web/capability-handling).
* Wire up analytics forwarding via `analytics.forward`.
* Review [Security](/docs/go-v2/developer-integration/sdks/web/security) before shipping to production.
