> For the complete documentation index, see [llms.txt](https://docs.taptap3d.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.taptap3d.com/integration-guides/iframe-guide.md).

# Embed a listing

Complete host HTML example for the proposed Taptap3D iframe, including loading, resizing and unavailable states.

Status: proposed integration. Use the [local quickstart](/get-started/local-quickstart.md) to run this example against synthetic data. Production origins and host registration are not available yet.

## Prerequisites

For a live connection, first complete the [connection playbook](/get-started/connection-playbook.md): approved publication ID, issued embed origin and registered storefront origin. You need a storefront content area that permits an iframe and its lifecycle script. This guide does not install a platform-specific app.

## Complete host example

Copy this into an HTML page or adapt it to your storefront template. Replace `embed.example.com` with the issued origin and `pub_demo_vessel` with the publication ID. This sample deliberately keeps approved title and summary outside the frame, so the parent page has readable content when the frame cannot load.

```html
<!doctype html>
<html lang="en">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Listing connection example</title>
<style>
  body { max-width: 48rem; margin: 2rem auto; padding: 0 1rem; font: 1rem/1.6 system-ui; color: #010736; }
  iframe { display: block; width: 100%; border: 0; }
  iframe[hidden] { display: none; }
</style>
<h1>Ceramic vessel</h1>
<p>A short description approved for publication.</p>
<p><a id="listing-link">Open the listing</a></p>
<p id="embed-status" role="status">Loading item details.</p>
<iframe id="listing-frame" title="Ceramic vessel: item details"
  width="640" height="720" loading="lazy"
  referrerpolicy="strict-origin-when-cross-origin"></iframe>
<script type="module">
  // Placeholder only. Replace with the issued HTTPS embed origin at onboarding.
  const embedOrigin = 'https://embed.example.com';
  const publicationId = 'pub_demo_vessel';
  const frame = document.querySelector('#listing-frame');
  const status = document.querySelector('#embed-status');
  const link = document.querySelector('#listing-link');
  const embedUrl = new URL(`/embed/${encodeURIComponent(publicationId)}`, embedOrigin);
  embedUrl.searchParams.set('locale', 'en');
  embedUrl.searchParams.set('theme', 'light');
  link.href = new URL(`/p/${encodeURIComponent(publicationId)}`, embedOrigin).href;

  // A production iframe must be on a different origin from this storefront.
  if (embedUrl.origin !== location.origin) {
    frame.setAttribute('sandbox', 'allow-scripts allow-same-origin');
  }
  const timeout = setTimeout(() => {
    status.textContent = 'Item details are taking longer to load. Use the listing link.';
  }, 10_000);
  let unavailable = false;
  window.addEventListener('message', event => {
    if (event.origin !== embedUrl.origin || event.source !== frame.contentWindow) return;
    const message = event.data;
    if (!message || typeof message !== 'object' || message.version !== 1 ||
        message.publicationId !== publicationId || unavailable) return;
    switch (message.type) {
      case 'taptap3d:ready':
        if (typeof message.revision !== 'string' || !message.revision) return;
        clearTimeout(timeout);
        status.textContent = '';
        break;
      case 'taptap3d:resize':
        if (!Number.isInteger(message.height) || message.height < 240 || message.height > 1600) return;
        frame.height = String(message.height);
        break;
      case 'taptap3d:unavailable':
        if (!['publication_not_found', 'revision_not_found', 'publication_withdrawn',
              'service_unavailable'].includes(message.code)) return;
        clearTimeout(timeout);
        unavailable = true;
        frame.hidden = true;
        status.textContent = 'Item details are unavailable. Use the listing link.';
        break;
    }
  });
  // Register the listener before starting the frame to avoid missing ready.
  frame.src = embedUrl.href;
</script>
</html>
```

To pin a revision, add `embedUrl.searchParams.set('revision', 'rev_demo_001')` and the same parameter to the fallback URL. With multiple embeds, each frame needs its own ID, state, timeout and source check.

Preserve your existing CSP and add the issued embed origin to `frame-src`. A restrictive storefront may disallow inline scripts: move the module into its approved JavaScript bundle or use the storefront's nonce policy. Do not weaken its script policy to copy this example.

## Check the result

The approved title and summary remain readable outside the iframe. A valid ready message clears the loading status; a validated resize changes the frame height. An unavailable message hides the frame and leaves the canonical link visible.

The lifecycle code is explained in [Iframe lifecycle](/proposed-reference/host-integration.md). For a blocked frame or missing resize, see [Troubleshooting](/testing-and-support/troubleshooting.md). Complete the [release acceptance checks](/testing-and-support/acceptance.md) before a live storefront launch.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.taptap3d.com/integration-guides/iframe-guide.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `build a script that syncs our docs to a CMS` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
