> ## Documentation Index
> Fetch the complete documentation index at: https://developers.resistant.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Quickstart

> Embed the Offline iFrame viewer and render results by sending the original file + API outputs via postMessage.

This quickstart shows a **minimal working integration** for the Offline iFrame viewer:

1. Render an `<iframe>` (using a placeholder bundle URL)
2. Fetch the original file and analysis results from your backend
3. Send them to the iframe via `postMessage()`

<Warning>
  The Offline iFrame bundle URL is provided by Resistant AI during enablement.\
  This page uses placeholders so the viewer cannot be launched without coordination.
</Warning>

<Tip>
  See also: [Access and hosting](./access-and-hosting) for delivery models (self-hosted vs Resistant AI–hosted static bundle).
</Tip>

***

## Prerequisites

Before you start, you need:

* An Offline iFrame bundle URL provided by Resistant AI:
  * `<OFFLINE_IFRAME_URL_PROVIDED_BY_RESISTANT_AI>`
* The expected iframe origin (for `postMessage` targetOrigin):
  * `<OFFLINE_IFRAME_ORIGIN_PROVIDED_BY_RESISTANT_AI>`
* A `submission_id` for a document already processed via the Documents API
* Your service can fetch:
  * the original file bytes (as `ArrayBuffer`)
  * API results (`/fraud` required; `/decision` and `/classification` optional)

<Tip>
  If your security posture allows it, we recommend using the Resistant AI–hosted static bundle to reduce maintenance overhead (updates are managed as part of our release process). For restricted environments, self-host the bundle.
</Tip>

***

## Quick start

### Security and CSP notes (important)

This viewer does **not** call the Documents API. Your application must fetch results and the original file bytes, then send them to the iframe.

This example uses:

* an inline `<script>` block, and
* an iframe load handler for initialization.

If your Content Security Policy (CSP) forbids inline scripts (`script-src 'unsafe-inline'`), move the JavaScript into a separate file and attach the load handler with `addEventListener`.

<Warning>
  Never use `*` as the `targetOrigin` in production. Always set `targetOrigin` to the expected iframe origin.
</Warning>

### Display a submission once the iframe is loaded

```html theme={null}
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Resistant AI Offline iFrame Demo</title>
  </head>

  <body>
    <iframe
      id="rai-offline-iframe"
      src="<OFFLINE_IFRAME_URL_PROVIDED_BY_RESISTANT_AI>"
      allow="clipboard-read; clipboard-write"
      width="1250"
      height="750"
      style="border: 0;"
    ></iframe>

    <script>
      async function sendToIframe() {
        const iframe = document.getElementById("rai-offline-iframe");

        // Identifier returned by the Documents API after creating a submission
        const submission_id = "YOUR_DOCUMENT_SUBMISSION_ID";

        // Example: fetch fraud result (recommended with metadata for overlays)
        // const fraudResult = await fetch(`/v2/submission/${submission_id}/fraud?with_metadata=true`).then(r => r.json());
        const fraud = JSON.stringify(fraudResult);

        // Example: fetch original uploaded file bytes
        // const fileResp = await fetch(`/files/${submission_id}`);
        const fileData = await fileResp.arrayBuffer();

        // Send message using postMessage with transferable ArrayBuffer (recommended)
        iframe.contentWindow.postMessage(
          {
            submissionId: submission_id, // for display/debugging
            fraud,
            fileData,
          },
          "<OFFLINE_IFRAME_ORIGIN_PROVIDED_BY_RESISTANT_AI>",
          [fileData]
        );
      }

      // Ensure iframe is loaded before sending message
      window.addEventListener("load", () => {
        const iframe = document.getElementById("rai-offline-iframe");
        iframe.addEventListener("load", () => {
          sendToIframe().catch(console.error);
        });
      });
    </script>
  </body>
</html>
```

***

## Communication protocol

Use `window.postMessage()` to send analysis data to the Offline iFrame viewer.

```js theme={null}
const message = {
  // Optional but recommended (display/debugging)
  submissionId: "YOUR_DOCUMENT_SUBMISSION_ID",

  // Required
  fraud: JSON.stringify(responseFromGetFraudAPI),

  // Optional
  decision: responseFromGetDecisionAPI
    ? JSON.stringify(responseFromGetDecisionAPI)
    : undefined,
  classification: responseFromGetClassificationAPI
    ? JSON.stringify(responseFromGetClassificationAPI)
    : undefined,

  // Required (ArrayBuffer)
  fileData: fileArrayBufferAssociatedWithSubmission,

  // Optional (stringified JSON)
  style: JSON.stringify({
    colors: DEFAULT_COLORS,
  }),

  // Optional
  locale: "en",
};

document.getElementById("rai-offline-iframe").contentWindow.postMessage(
  message,
  "<OFFLINE_IFRAME_ORIGIN_PROVIDED_BY_RESISTANT_AI>",
  [message.fileData]
);
```

***

## Message parameters

* `submissionId` *(optional, recommended)*: identifier used for display/debugging in the viewer. Use the API’s `submission_id` value.
* `fraud` *(required)*: `JSON.stringify()` of the response from `GET /v2/submission/{submission_id}/fraud`
  * Required for overlays/highlights: request fraud with `?with_metadata=true`
* `decision` *(optional)*: `JSON.stringify()` of the response from `GET /v2/submission/{submission_id}/decision`
* `classification` *(optional)*: `JSON.stringify()` of the response from `GET /v2/submission/{submission_id}/classification`
* `fileData` *(required)*: `ArrayBuffer` of the original uploaded file
* `style` *(optional)*: `JSON.stringify()` of a style object (you can pass a partial override)
* `locale` *(optional)*: `"en"` or `"pt"` (ISO 639-1 language code)

***

## Styling

If no style is passed, the UI uses a default color scheme. You can override all or part of the palette.

```js theme={null}
const DEFAULT_COLORS = {
  primary: "#5890fe",
  error: "#e44356",
  background: {
    primary: "#ffffff",
    secondary: "#fbfbfb",
  },
  text: {
    primary: "#0e153c",
    secondary: "#62666e",
  },
  border: {
    primary: "#e6e6e6",
  },
  metadata: {
    background: "#f7f8f9",
    text: {
      primary: "#6b7280",
      secondary: "#62666e",
    },
  },
  pdfViewer: {
    background: "#dcdee4",
    scrollbar: {
      track: "#c5c6cd",
      thumb: "#a7aab1",
      indicator: "#858d95",
      indicatorShown: "#e44356",
    },
  },
  scrollbar: {
    track: "#dcdee4",
    thumb: "#b9bbcb",
    thumbHover: "#b9bbcb",
  },
  documentType: {
    trusted: "#00be90",
    normal: "#5890fe",
    warning: "#ffb646",
    highRisk: "#e44356",
    failed: "#0c0a2c",
    approved: "#00be90",
    declined: "#e44356",
    manualReview: "#ffb646",
  },
  icon: {
    primary: "#0e153c",
    disabled: "#C8CED6",
  },
};
```

***

## Locale

If no locale is passed, `"en"` is used.

```js theme={null}
const SUPPORTED_LOCALES = {
  en: "en",
  pt: "pt",
};
```

Example: override primary color and locale:

```js theme={null}
document.getElementById("rai-offline-iframe").contentWindow.postMessage(
  {
    style: JSON.stringify({
      colors: {
        primary: "blue",
      },
    }),
    locale: "en",
  },
  "<OFFLINE_IFRAME_ORIGIN_PROVIDED_BY_RESISTANT_AI>"
);
```

***

## Implementation notes

### Ensure the iframe loads before sending a message

Messages sent before the iframe is fully loaded may not be received.

* Prefer attaching a `load` event handler to the iframe (as shown in the example).
* If your application has complex routing, ensure you resend on re-mount when needed.

### Recommended iframe dimensions

* Width: **1250 px** (recommended), **1000 px** (minimum)
* Height: **750 px** (recommended), **600 px** (minimum)

### Use transferable objects for `fileData`

Use the transferable object pattern when sending `fileData` to improve performance:

* pass `[message.fileData]` as the third argument to `postMessage()`

***

## Debugging tips

If nothing appears or you see an error screen after sending the message:

* Check the browser console for errors
* Ensure all required fields (`fraud`, `fileData`) are present and correctly formatted
* Confirm the iframe URL origin matches the `postMessage` target origin
* If using a strict CSP, ensure scripts are allowed (avoid inline scripts in production)

***

## Browser compatibility

| Browser | Supported versions |
| ------- | ------------------ |
| Chrome  | 100+               |
| Firefox | 99+                |
| Safari  | 15.4+              |
| Edge    | 100+               |
