HomeBook a Demo

Embedding inside an iframe

Run the Inline AI SDK inside a cross-origin iframe so every answer, event, and ad request is attributed to the publisher page that hosts the frame.

This guide is for platforms that render inside an iframe on a publisher's page (comment systems, syndication widgets, embedded tools) and want to offer Inline AI to that publisher from within the frame. The SDK runs entirely inside your frame's document. You give it two things it cannot discover on its own: which publisher it is serving, and the URL of the page that hosts the frame.

If you are a publisher adding Inline AI to your own pages, you do not need this guide. See Install and Integration modes.

How it works

Your frame's own window.location points at your domain, not the publisher's. Out of the box the SDK would treat your frame as the page: it would look up configuration for your URL, attribute conversations and engagement to your domain, and send your URL to ad partners. Passing pageUrl to init() fixes all of that at once. Every signal the SDK produces then identifies the publisher page:

  • Page configuration and per-page settings resolve for the host page.
  • Conversations and engagement events report the host page URL and referrer.
  • Ad requests carry the host page URL as the page context.
  • Consent is read from the host page's consent management platform (CMP) across the frame boundary.

Nothing about the script, the API, or the placements changes. Only the page identity does.

Step 1: Map your sites to publisher IDs

Every Inline AI publisher has a publisher ID tied to their domain. The SDK will not load for a domain that is not registered, and a request with an unknown ID is rejected. Before you can render the widget for a publisher, they must be onboarded with Inline AI and you need their ID.

Keep a mapping from your own site or forum identifier to the Inline AI publisher ID, and only initialize the SDK for sites that have one. Inline AI provides the IDs during onboarding.

Step 2: Pass the host page URL into the frame

The frame needs the full URL of the page that hosts it. document.referrer inside the frame is not enough: under the default referrer policy it only carries the origin, and it is empty in some configurations.

Most platforms already know the host page URL from their own embed configuration. Forward it into the frame however you already pass configuration, typically as a query parameter on the frame's src or through your existing postMessage channel.

// On the host page, inside your embed script
var frame = document.createElement('iframe');
var src = new URL('https://frames.your-platform.example/thread');
src.searchParams.set('page', location.href.split('#')[0]);
frame.src = src.toString();

Strip the fragment and any of your own session parameters before forwarding. The SDK normalizes query strings and trailing slashes itself when it resolves configuration.

Step 3: Load the SDK inside the frame

Inside the frame document, set up the command queue, call init() with the publisher ID and pageUrl, then load the script. This is the same command queue flow publishers use, with two additions: pageUrl and, optionally, referrerUrl.

<div id="inline-ai-target"></div>
 
<script>
  (function () {
    var params = new URLSearchParams(location.search);
    var publisherId = 'PUBLISHER_ID_FOR_THIS_SITE';
    var pageUrl = params.get('page');
 
    if (!publisherId || !pageUrl) return;
 
    window.InlineAI = window.InlineAI || {};
    window.InlineAI.cmd = window.InlineAI.cmd || [];
 
    window.InlineAI.cmd.push(['init', {
      publisherId: publisherId,
      pageUrl: pageUrl,
      referrerUrl: params.get('referrer') || undefined
    }]);
 
    window.InlineAI.cmd.push(function (sdk) {
      sdk.mount('integrated-chat', 'inline-ai-target');
    });
 
    var script = document.createElement('script');
    script.type = 'module';
    script.src = 'https://getinline.tech/default/assets/index.js?key=' + publisherId;
    document.head.appendChild(script);
  })();
</script>

Three details matter here:

  • Use the function-callback form to mount. The ready event fires once during the queue drain and is not replayed. A function pushed to the queue runs immediately if the SDK is already up, or once it is. See Programmatic control.
  • Use string placement names. window.InlineAI.Placement does not exist until the script has loaded, so code that runs before the script tag must use 'integrated-chat', 'key-takeaways', and so on.
  • Pass publisherId to init() and as ?key= on the script URL. Both are read; keeping them identical avoids a mismatch if either path changes later.

pageUrl pins the page identity for the life of the SDK instance. If your frame is reused across host page navigations without a reload, call destroy() and then init() again with the new URL. Mounting and unmounting placements alone keeps the old URL.

Step 4: Choose in-flow placements and size the frame

The widget renders inside your frame, so it is subject to the frame's viewport and clipping. Use placements that flow with the document:

PlacementString nameNotes
Integrated chat'integrated-chat'Full conversational embed.
Key takeaways'key-takeaways'Summary card with follow-up questions.
Basic embed'basic-embed'Compact question and answer embed.

Avoid the search placements ('search-embed', 'search-icon', 'search-fab') and the floating 'widget'. They open an overlay positioned against the viewport, which stops at the frame boundary.

In-flow placements change the height of your frame's document as the reader interacts. Your existing frame resize mechanism (the one that grows the frame as content loads) handles this the same way it handles your own content. The placement:mounted SDK event is a good moment to trigger a measurement if you resize on demand rather than on a ResizeObserver.

The SDK reads consent from the host page's CMP through the IAB locator-frame protocol (__tcfapiLocator, __gppLocator, __uspapiLocator). When the CMP APIs are not present in your frame but an ancestor window owns a locator frame, the SDK installs a postMessage proxy and reads consent from the host CMP as if it were on the page. Every TCF 2.2 compliant CMP supports this. There is nothing to configure on your side.

If the host page has no CMP, the SDK falls back to the regional default for the reader, exactly as it does on a publisher page with no CMP.

If your frame has its own CMP that already exposes __tcfapi, __gpp, or __uspapi inside the frame, the SDK uses that and does not install the proxy.

Sandbox attributes and CSP

If you set a sandbox attribute on the frame, the SDK is verified with this set:

<iframe sandbox="allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox"></iframe>
  • allow-scripts and allow-same-origin are required. Without allow-same-origin the frame has an opaque origin, storage is unavailable, and CMPs that reply to event.origin cannot reach the frame.
  • allow-popups and allow-popups-to-escape-sandbox let answer citations and ad clicks open in a new tab.

If your frame document sets a Content Security Policy, allow these Inline AI hosts:

DirectiveValue
script-srchttps://getinline.tech
connect-srchttps://api.getinline.tech https://ask-realtime.api.getinline.tech
style-src'unsafe-inline' (the bundle injects its own stylesheet)
img-srcThe publisher's own domains, for citation favicons and thumbnails

Monetized placements load creatives and measurement from ad partners. Inline AI provides the per-publisher list of partner hosts during onboarding.

What is different inside a frame

  • Article context comes from the host page's configuration, not the DOM. On a publisher page the SDK reads article metadata (JSON-LD, Open Graph) from the document. Inside your frame that document is yours, so there is no article markup to read. Inline AI configures the publisher so answers and ads use the page's crawled content and the conversation instead. No action on your side.
  • Storage is partitioned per top-level site. Browsers partition cookies and local storage in cross-site frames by the top-level site, so reader state does not carry between a publisher's own pages and your frame on the same page. This is expected.
  • Feature flags are read from the host page URL. Query parameters on pageUrl such as ?inline-ai=nomonetize apply inside the frame, and flags on your frame's own URL are honored as well. Useful for QA without changing the embed.

Testing your integration

Inline AI hosts a reference implementation you can load in a browser:

  • Outer page (plays the publisher): https://staging.getinline.tech/iframe-demo-cross-origin.html?variant=b&cmp=1
  • Inner page (plays your frame): https://www.getinline.io/iframe-demo.html, loaded by the outer page

variant=b forwards the host page URL, variant=a omits it so you can see the difference, cmp=1 installs a test CMP with a locator frame on the outer page, and sandbox=1 applies the sandbox attribute above. The inner page prints a diagnostics block with the resolved init() config and every SDK event it receives.

To verify your own integration:

  1. Open the host page with your frame in Chrome and open DevTools.
  2. In Network, filter on graphql. Every request from the frame should carry the host page URL in pageUrl, not your frame's URL.
  3. In Console, switch the context selector from top to your frame and run typeof __tcfapi. On a host page with a TCF CMP it should print function, and __tcfapi('getTCData', 2, function (data, ok) { console.log(ok, data.eventStatus); }) should log true followed by the CMP's status.
  4. Confirm the placement mounted and your frame resized to fit it.

Go-live checklist

  • The publisher is onboarded with Inline AI and you have their publisher ID.
  • Your frame receives the host page URL and passes it as pageUrl on every load.
  • Placements are in-flow ('integrated-chat', 'key-takeaways', or 'basic-embed').
  • Your frame resizes when the placement mounts and as the reader interacts.
  • Sandbox attributes and CSP, if you use them, include the entries above.
  • On a host page with a CMP, __tcfapi resolves inside the frame.