> For a complete page index, fetch https://docs.transak.com/llms.txt

# iFrame

The Transak iFrame integration allows you to embed Transak’s interface directly into your website, enabling users to buy or sell crypto without leaving your page.

## Single Embed iFrame

Call the [Create Widget URL](/api/public/create-widget-url) API from your backend to generate a secure widget url using Query parameters

The response returns a `widgetUrl`.

A `widgetUrl` is valid for 5 minutes and can only be used once. A new `widgetUrl` must be generated for every user flow.

**Example Request:**

```bash
curl --request POST \
  --url https://api-gateway-stg.transak.com/api/v2/auth/session \
  --header 'access-token: YOUR_ACCESS_TOKEN' \
  --header 'content-type: application/json' \
  --data '{
  "widgetParams": {
    "apiKey": "YOUR_API_KEY",
    "referrerDomain": "yourdomain.com"
  }
}'
```

Add the `widgetUrl` to your page by embedding the iframe and message listener below.

**Example:**

```html
<html lang="en" style="height: 100%">
  <body style="margin:0; padding:0; height: 100%; display: grid">
    <div style="position: relative; width: 500px; height: 80dvh; margin: auto; box-shadow: 0 0 15px #1461db; border-radius: 15px; overflow: hidden">
        <iframe
            id="transakIframe"
            src="https://global.transak.com/?apiKey=<YOUR_API_KEY>&sessionId=<YOUR_SESSION_ID>"
            allow="camera;microphone;payment"
            style="height: 100%; width: 100%; border: none">
        </iframe>
    </div>

    <script>
        (function () {
            const transakIframe = document.getElementById("transakIframe")?.contentWindow;

            window.addEventListener('message', (message) => {
                if (message.source !== transakIframe) return;

                // To get all the events
                console.log('Event ID: ', message?.data?.event_id);
                console.log('Data: ', message?.data?.data);

                // This will trigger when the user marks payment is made
                if (message?.data?.event_id === 'TRANSAK_ORDER_SUCCESSFUL') {
                    console.log('Order Data: ', message?.data?.data);
                }
            });
        })();
    </script>
  </body>
</html>
```

### Extensions

We do not recommend using the extension integration, as the Transak interface requires browser camera permission. Integration will only work if the browser permission is already set to **Allow**.

Chrome extensions built using [Manifest v3](https://developer.chrome.com/docs/extensions/mv3/declare_permissions/) cannot programmatically request access to the device camera. Since the Transak KYC flow requires camera access for identity verification (such as capturing ID documents or performing liveness checks), the extension cannot trigger the browser’s camera permission prompt when the widget loads. Because of this limitation, the KYC process may fail unless the user has already manually granted camera access in their browser settings.

## Double Embed iFrame

Call the [Create Widget URL](/api/public/create-widget-url) API from your backend to generate a secure widget url using Query parameters

The response returns a `widgetUrl`.

A `widgetUrl` is valid for 5 minutes and can only be used once. A new `widgetUrl` must be generated for every user flow.

**Example Request:**

```bash
curl --request POST \
  --url https://api-gateway-stg.transak.com/api/v2/auth/session \
  --header 'access-token: YOUR_ACCESS_TOKEN' \
  --header 'content-type: application/json' \
  --data '{
  "widgetParams": {
    "apiKey": "YOUR_API_KEY",
    "referrerDomain": "yourdomain.com"
  }
}'
```

Add `allow=camera;microphone;payment` to both the outer and inner iframe. If you cannot set these attributes, the widget will detect it and provide a unique KYC link during the flow (also emailed to the user).

**Example:**

```typescript title="Outer iframe"
import { ChangeEvent, useState } from "react";
import "./App.css";
import { useSearchParams } from "react-router-dom";

type Environment = "STAGING" | "PRODUCTION";

export default function OuterIframe() {
  const [searchParams] = useSearchParams();

  const [environment, setEnvironment] = useState<Environment>(
    (searchParams.get("environment") as Environment) || "STAGING"
  );
  const [sessionId, setSessionId] = useState<string>(
    (searchParams.get("sessionId") || "")
  );

  const [apiKey, setApiKey] = useState<string>(
    searchParams.get("apiKey") || ""
  );

  const toggleEnvironment = (selectedEnvironment: Environment) => {
    setEnvironment(selectedEnvironment);
  };

  const handleApiChange = (e: ChangeEvent<HTMLInputElement>) => {
    setApiKey(e.target.value);
  };

  const apiUrl =
    environment === "STAGING"
      ? `https://transak-double-iframe-supporter.vercel.app/staging?environment=${environment}`
      : `https://transak-double-iframe-supporter.vercel.app/production?environment=${environment}`;

  const finalUrl = `${apiUrl}${apiKey ? `&apiKey=${apiKey}` : ""}`;

  return (
    <main className="container">
      <div className="content">
        <label htmlFor="dropdown">Select Environment:</label>
        <select
          id="dropdown"
          value={environment}
          onChange={(e) => toggleEnvironment(e.target.value as Environment)}
        >
          <option value="STAGING">Staging</option>
          <option value="PRODUCTION">Production</option>
        </select>
      </div>

      <div className="content">
        <span>API Key</span>
        <input type="text" value={apiKey} onChange={handleApiChange} />
      </div>

      <iframe
        className="outer"
        src={finalUrl}
        allow="camera;microphone;payment"
      />
    </main>
  );
}
```

```typescript title="Inner iframe"
import {
  RouterProvider,
  createBrowserRouter,
  useSearchParams,
} from "react-router-dom";
import "./App.css";

// below code uses react-router-dom. Enclose your Application with BrowserRouter component

export const InnerIframe = () => {
  const [searchParams] = useSearchParams();
  const apiKey = searchParams.get("apiKey") || " ";

  return (
    <iframe
      width="400"
      height="600"
      src={`https://global-stg.transak.com?apiKey=${encodeURIComponent(
        apiKey
      )}`}
      allow="camera;microphone;payment"
    ></iframe>
  );
};

export const Production = () => {
  const [searchParams] = useSearchParams();
  const apiKey = searchParams.get("apiKey") || " ";

  return (
    <iframe
      width="400"
      height="600"
      src={`https://global.transak.com?apiKey=${encodeURIComponent(apiKey)}`}
      allow="camera;microphone;payment"
    ></iframe>
  );
};

export const Home = () => {
  return <div className="container">Transak Double iframe Supporter</div>;
};

const router = createBrowserRouter([
  {
    path: "/",
    element: <Home />,
  },
  {
    path: "/staging",
    element: <InnerIframe />,
  },
  {
    path: "/production",
    element: <Production />,
  },
]);

export default function App() {
  return <RouterProvider router={router} />;
}
```

## Events

<table>
  <thead>
    <tr>
      <th>
        Event Name
      </th>

      <th>
        Description
      </th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td>
        <code>
          TRANSAK_WIDGET_INITIALISED
        </code>
      </td>

      <td>
        Widget initialised with query params
      </td>
    </tr>

    <tr>
      <td>
        <code>
          TRANSAK_WIDGET_OPEN
        </code>
      </td>

      <td>
        Widget fully loaded
      </td>
    </tr>

    <tr>
      <td>
        <code>
          TRANSAK_ORDER_CREATED
        </code>
      </td>

      <td>
        Order created by user
      </td>
    </tr>

    <tr>
      <td>
        <code>
          TRANSAK_ORDER_SUCCESSFUL
        </code>
      </td>

      <td>
        Order is successful
      </td>
    </tr>

    <tr>
      <td>
        <code>
          TRANSAK_ORDER_CANCELLED
        </code>
      </td>

      <td>
        Order is cancelled
      </td>
    </tr>

    <tr>
      <td>
        <code>
          TRANSAK_ORDER_FAILED
        </code>
      </td>

      <td>
        Order is failed
      </td>
    </tr>

    <tr>
      <td>
        <code>
          TRANSAK_WIDGET_CLOSE
        </code>
      </td>

      <td>
        Widget is about to close
      </td>
    </tr>
  </tbody>
</table>