# Embed conversations and workspaces with user identity

Embed a conversation or project workspace in your website using the current user's AskTable identity.

使用指南 · 模块: integrations · 应用开发者 · 部署形态：Cloud / 私有部署 · 已确认产品版本：资料未说明 · 来源：编辑整理 · 按产品 4.2.76-1110（CLI 0.0.18）核对；私有部署的旧版本可能不包含近期变化。

User-identity embedding puts AskTable pages into your website using the current user's own AskTable identity: organizations, projects, and data permissions are the same as when the user signs in to AskTable directly. It suits internal platforms and enterprise portals.

When visitors do not sign in and share one data scope, use [anonymous embedding](https://docs.asktable.com/en/integrations/embed) instead.

Guide location: project sidebar → “Agent integrations” → “Integrate AskTable in business systems” → “iframe”.

## Choose a mode

| Mode      | Suits                                                                                               | Page scope                                                                |
| --------- | --------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
| Chat      | Adding conversations to an existing business page, with your website managing the conversation list | One conversation of the current user                                      |
| Workspace | Putting the AskTable workspace into an internal platform or portal                                  | The project workspace in a specified organization, with project switching |

Both modes need the current user's AskTable user credential (`user_token`). Your platform must first exchange it for the user's AskTable credential through an existing sign-in or single sign-on flow. Do not use an API key or a shared administrator credential to represent all users.

## Chat

1. Prepare the AskTable website address, organization ID, project ID, agent ID, and the current user's credential.

2. Call the API to list the user's own conversations, or create a conversation for an agent, and get the conversation `id`:

   ```http
   GET /api/v1/conversations?created_by_me=true&types=chat&page=1&size=20&sort=modified_desc
   Authorization: Bearer <current user's AskTable credential>
   X-Project-Id: <project ID>
   ```

   ```http
   POST /api/v1/conversations
   Authorization: Bearer <current user's AskTable credential>
   X-Project-Id: <project ID>
   Content-Type: application/json

   {"type":"chat","data_agent_id":"<agent ID>"}
   ```

   The examples use the all-in-one deployment's `/api` prefix; for Cloud and other deployments, replace it with the [actual API address](https://docs.asktable.com/en/integrations/openapi#api-addresses).

3. Build the address with the conversation ID and pass the credential using the handshake protocol below:

   ```text
   <website address>/user-embed/p/<project ID>/conv/<conversation ID>?org_id=<organization ID>&parent_origin=<encoded parent page origin>
   ```

Creating a conversation does not send a question automatically. When switching conversations, update the iframe address and handshake again. This entry opens only the user's own conversations; administrators cannot use it to view other people's conversations.

## Workspace

```text
<website address>/workspace-embed?org_id=<organization ID>&parent_origin=<encoded parent page origin>&project_id=<initial project ID>
```

- `project_id` is optional; without it, the user's accessible projects are listed.
- Users use conversations, agents, data sources, skills, project settings, and other features with their existing permissions and can switch projects within the organization; organization switching and system administration are not available.
- Add `&layout=content` before the `#` in the address to hide AskTable's project sidebar and mobile menu button, which suits opening a specific project page directly from your website's menu, for example `...&layout=content#/u/p/<project ID>/data/data-source`. Private deployments need 4.2.76 or later.

## Handshake protocol

Both modes require `org_id` and `parent_origin`. `parent_origin` is the parent page's `location.origin` (protocol, host, and non-default port, without path or trailing slash), encoded with `URLSearchParams`.

**Register the message listener before setting the iframe address.** The example below is for Workspace; for Chat, use the Chat address, change `mode` to `chat`, and also check the project ID and conversation ID in messages.

```html
<iframe id="asktable" title="AskTable" style="width:100%;height:700px;border:0"></iframe>
```

```js
const webBase = 'https://asktable.example.com'; // may include a deployment subpath, no trailing slash
const askTableOrigin = new URL(webBase).origin;
const orgId = 'your organization ID';
const projectId = 'your project ID';
const frame = document.getElementById('asktable');
let channelId;
let generation = 0;
let closing = false;

window.addEventListener('message', async (event) => {
  if (event.origin !== askTableOrigin || event.source !== frame.contentWindow) return;
  const m = event.data;
  if (!m || m.mode !== 'workspace' || m.org_id !== orgId) return;
  if (m.type === 'asktable.ready' && !closing) {
    channelId = m.channel_id;
    const current = ++generation;
    try {
      // Implemented by your app: get the current user's AskTable user credential.
      const token = await getCurrentAskTableUserToken();
      if (closing || current !== generation) return;
      frame.contentWindow.postMessage({
        type: 'asktable.authenticate', channel_id: channelId, token
      }, askTableOrigin);
    } catch (error) {
      if (!closing && current === generation) showIntegrationError(error);
    }
    return;
  }
  if (!channelId || m.channel_id !== channelId) return;
  if (m.type === 'asktable.loaded') hideLoading();
  if (m.type === 'asktable.error') showIntegrationError(m.code);
  if (m.type === 'asktable.auth_required') {
    // Return to sign-in; reload the iframe after a successful sign-in, with limited retries.
    ++generation;
    showSignIn();
  }
  if (m.type === 'asktable.logged_out') {
    ++generation;
    closing = true;
    frame.src = 'about:blank'; // Unload only after sign-out is confirmed.
  }
});

const params = new URLSearchParams({
  org_id: orgId, parent_origin: location.origin, project_id: projectId
});
frame.src = `${webBase}/workspace-embed?${params}`;

function logoutAskTable() {
  closing = true;
  ++generation; // Discard credential requests still in progress.
  if (channelId) frame.contentWindow.postMessage({
    type: 'asktable.logout', channel_id: channelId
  }, askTableOrigin);
}
```

`getCurrentAskTableUserToken`, `hideLoading`, `showIntegrationError`, and `showSignIn` are implemented by your app. In production, also add timeout messages for loading, getting credentials, and signing out; before reloading, switching accounts, or switching conversations, invalidate old credential requests. Keep separate handshake state for each iframe.

| Message                  | Direction       | Meaning and handling                                                                                   |
| ------------------------ | --------------- | ------------------------------------------------------------------------------------------------------ |
| `asktable.ready`         | iframe → parent | Record the new `channel_id` and send authenticate                                                      |
| `asktable.authenticate`  | parent → iframe | Carries the current channel and user credential; the target origin must match exactly, not `*`         |
| `asktable.authenticated` | iframe → parent | Identity confirmed; does not mean the target page is accessible                                        |
| `asktable.loaded`        | iframe → parent | The target page's required data is ready; remove the loading overlay                                   |
| `asktable.auth_required` | iframe → parent | Authenticate again with a new credential; limit retries and ask the user to sign in again on failure   |
| `asktable.error`         | iframe → parent | Show the error or a retry entry; permission errors cannot be fixed by refreshing the credential        |
| `asktable.logout`        | parent → iframe | Request sign-out with the current `channel_id`                                                         |
| `asktable.logged_out`    | iframe → parent | Credentials cleared; only then destroy the iframe; also sent when the user signs out inside the iframe |

Messages from AskTable all include `mode`, `org_id`, and `channel_id`; Chat messages also include `project_id` and `conversation_id`. The parent page must check these fields, the message origin, and the sending window, and handle only messages for the current channel. When the same user renews the credential, it may receive only `authenticated` again, and drafts in the input box are kept.

## Sign-in and sign-out

- After the identity is verified, the credential is stored in browser storage on AskTable's own domain. In the same browser, the iframe and AskTable pages opened directly share one identity: switching accounts overwrites the original account, and signing out signs out of both. It cannot be used to run several accounts side by side.
- Closing the iframe does not sign out. Send `logout` first and unload after receiving `logged_out`. Signing out clears only the credential in the browser; it does not revoke the credential on the server.
- Do not put credentials in URLs, write them to logs, or store them in your website's localStorage.
- Some browsers restrict third-party storage; verify sign-in, refresh, renewal, account switching, and sign-out on the actual domain and target browsers.

## Deployment checks

- `EMBED_ORIGINS` in the AskTable web container defaults to `*`, allowing all HTTP/HTTPS parent pages. To restrict it, set a comma-separated list of exact origins; an empty string disables the handshake. Allowing an origin grants no user permissions.
- Reverse proxies or CDNs must allow `/workspace-embed` and `/user-embed/` to be embedded; check `X-Frame-Options` and CSP `frame-ancestors`. Your website's CSP `frame-src` must also allow AskTable.
- HTTPS pages cannot embed HTTP pages. If you set `sandbox` on the iframe, include at least `allow-scripts allow-same-origin`, and allow downloads as needed.
- When credentials are passed only through postMessage, you do not need to open API CORS for your website. If your website calls the AskTable API directly, configure CORS separately. When proxying streaming replies, disable buffering and allow long connections.

## Upgrading from the earlier Chat embedding

Platforms that integrated Chat embedding before 4.2.66 must, after upgrading to 4.2.66 or later: add `org_id` to the address; add `mode=chat` and `org_id` to message checks; and wait for `logged_out` before closing the window when signing out. The AskTable frontend and server must be upgraded together.

## Acceptance

Verify on the actual domain: access without permission is rejected; only the user's own conversations open; project switching works; uploads, downloads, and streaming replies work; sign-in, refresh, renewal, account switching, and sign-out behave as expected.
