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

# Knowledge Graph Widget

> Embed the interactive knowledge graph from your Knowledge base on any website — read-only, domain-restricted, optionally locked with a signed token

The knowledge graph widget puts the **ontology graph control tower** from **Team → Knowledge** on any website with one iframe. Visitors explore the entities and relations your organization has published — search, filter by type, and inspect a node's neighbours — without logging in.

<Info>
  **Prerequisites**:

  * **OWNER** or **ADMIN** role in your Junis organization
  * At least one **ready** source in **Team → Knowledge** (files, URLs, a JSON API, or a database)
  * A website where you can paste an `<iframe>` snippet
</Info>

> Paste one `<iframe>` tag → your website shows a live, interactive graph of your published knowledge.

<CardGroup cols={2}>
  <Card title="Read-only, no AI cost" icon="lock">
    The widget only reads the graph. No model is called, so it never consumes credits
  </Card>

  <Card title="You choose what is published" icon="eye">
    Only entities and relations from sources you mark <b>Public in widget</b> leave your workspace — never summaries, attributes or passages
  </Card>

  <Card title="Domain-restricted" icon="shield-check">
    The widget opens only on the domains you allow — the browser enforces it too (CSP frame-ancestors)
  </Card>

  <Card title="Signed embed token" icon="key">
    For non-public data, require a short-lived token issued by your own server through the API
  </Card>
</CardGroup>

***

## What does the widget show?

| Shown                                                    | Not shown                               |
| -------------------------------------------------------- | --------------------------------------- |
| Entity names and types from published sources            | Entity summaries and attributes         |
| Relations between those entities (predicate, confidence) | Passages, citations, page references    |
| Type legend, search, a node's neighbour list             | Anything from sources not marked public |

The widget shows up to **3,000 entities** (the most connected first) and up to **10,000 relations** among them — the same limits as the control tower inside Junis, so visitors see the graph you see. There is no cache: when you re-read a source or change what is marked public, the widget reflects it on the next page load.

<Note>
  The knowledge graph widget is **independent of the chat widget**. You do **not** need to switch the organization to Public Anonymous — the graph opens with only the allowed-domains list and the widget switch below.
</Note>

***

## How do I enable it?

<Steps>
  <Step title="Add allowed domains">
    In **Organization Settings → Publish → Allowed Domains**, add the exact **origin** of each page that will host the widget — scheme plus host, nothing else:

    ```text theme={null}
    https://example.com
    https://portal.example.com
    ```

    <Warning>
      `https://example.com` and `https://www.example.com` are different origins — add both if both host the widget. Do not add `*` for the knowledge graph: it would allow every website to show your graph.
    </Warning>
  </Step>

  <Step title="Switch on the graph widget">
    Open **Team → Knowledge** and turn on **Embed the knowledge graph on your site**. The card shows a checklist of what is still missing.
  </Step>

  <Step title="Mark the sources to publish">
    In the source list on the same page, mark each source you want to expose as **Public in widget**. Start with one source and add more later.

    <Note>
      **Public in widget** also defines what the anonymous *chat* widget may search, if you ever enable it. Keep private manuals unmarked.
    </Note>
  </Step>

  <Step title="Copy the iframe snippet">
    Copy the snippet from the card and paste it where the graph should appear:

    ```html theme={null}
    <iframe
      src="https://junis.ai/embed/{your-org-slug}/graph"
      title="Knowledge graph"
      width="100%" height="560" style="border:0;border-radius:12px"
      sandbox="allow-scripts allow-same-origin" allow=""
      loading="lazy" referrerpolicy="strict-origin-when-cross-origin"
    ></iframe>
    ```

    Add `?theme=light` to the `src` for a light theme.

    <Check>Your knowledge graph is live on your website.</Check>

    To verify the domain restriction, open the same snippet from a site that is not on the list — it must show *This site is not allowed to embed the knowledge graph.*

    <Warning>
      Keep `referrerpolicy` as generated. With `no-referrer` the browser hides your page's origin from the widget, the domain check cannot run, and the widget shows "not allowed".
    </Warning>
  </Step>
</Steps>

***

## What should my website do?

Three settings on the hosting page keep the embed both working and safe:

1. **Allow the frame** — if your page sends a Content Security Policy, include `https://junis.ai` in `frame-src`.
2. **Do not let others wrap your page** — send `Content-Security-Policy: frame-ancestors 'self'` on the hosting page. The widget also verifies the *entire* chain of parent frames, so a page that is itself embedded by a foreign site is refused.
3. **Keep the attributes** — `sandbox="allow-scripts allow-same-origin"` and `allow=""` limit the frame to what the graph needs.

***

## How does the domain check work?

The widget reads the origins of **all** its parent frames as reported by the visitor's browser and sends them to Junis. Every one of them must be on your allowed-domains list. In addition:

* The browser itself refuses to render the widget on other sites — the embed page is served with `frame-ancestors` set to your allowed domains.
* Requests that do not come from the widget's own page (address-bar navigation, scripts on other sites) are refused.
* The chat widget and the knowledge graph widget share the same allowed-domains list.

<Note>
  The domain check relies on what the visitor's **browser** reports. Anyone who knows your organization slug and an allowed domain can still fetch the published graph with a non-browser client. If that matters for your data, require a signed embed token (next section).
</Note>

***

## Require a signed embed token (recommended for non-public data)

With a signed token, the graph is served **only** to frames that present a token your own server obtained through the API — even a request that claims an allowed domain is refused without it.

<Steps>
  <Step title="Issue a token on your server for every page view">
    Call the External API with an API key that has the `brain:read` scope. Never call it from the browser.

    <Warning>
      `brain:read` is **not** part of the default scope set. A key you already use for chat completions will get `403` here — create a key with `brain:read` selected in **Organization Settings → API Keys**, or add the scope to an existing key.
    </Warning>

    ```bash theme={null}
    curl -X POST https://api.junis.ai/api/external/brain/embed-token \
      -H "X-API-Key: $JUNIS_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{"embed_domain": "https://example.com"}'
    ```

    ```json theme={null}
    {
      "token": "eyJhbGciOi...",
      "expires_in": 300,
      "embed_domain": "https://example.com",
      "embed_path": "/embed/your-org-slug/graph",
      "embed_url": "https://junis.ai/embed/your-org-slug/graph#t=eyJhbGciOi..."
    }
    ```

    * `embed_domain` must be one of your allowed domains (otherwise `403 embed_domain_not_allowed`).
    * The graph widget must be switched on (otherwise `409 graph_widget_off`).
    * Tokens live **5 minutes** by default (`ttl_seconds`, 30–600) and can be exchanged **once**.
  </Step>

  <Step title="Put the token in the iframe URL fragment">
    Use `embed_url` as the iframe `src`. The token sits after `#` — a fragment is never sent to servers, never appears in referrers or logs. The widget removes it from the address as soon as it has read it.

    Alternatively send it from the hosting page after the frame loads:

    ```js theme={null}
    iframe.contentWindow.postMessage({ type: 'junis:embed-token', token }, 'https://junis.ai')
    ```
  </Step>

  <Step title="Turn on Require a signed embed token">
    Once your server issues tokens, enable **Require a signed embed token** in the graph widget card. From then on, requests without a valid token are refused with `403 token_required`.

    <Check>The graph is now served only through tokens your server issued.</Check>
  </Step>
</Steps>

Behind the scenes the widget exchanges the token for a 30-minute viewing session and sends that session in a request header — nothing sensitive stays in the URL. Reloading the page must fetch a **new** token; a reused token is refused.

***

## Why does the widget show a message instead of the graph?

| Message                                                  | Cause                                                                               | Fix                                                                                                      |
| -------------------------------------------------------- | ----------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| *This site is not allowed to embed the knowledge graph.* | The page origin — or one of its parent frames — is not on the allowed-domains list  | Add the exact origin; check for `www`, `http` vs `https`, ports; make sure no other site wraps your page |
| *The embedding page hides its address (referrerpolicy)…* | The iframe uses `referrerpolicy="no-referrer"` or the parent page hides its origin  | Use the snippet as generated (`strict-origin-when-cross-origin`)                                         |
| *This knowledge graph requires a signed embed token…*    | **Require a signed embed token** is on and the frame presented no token             | Issue a token on your server and put it in the URL fragment                                              |
| *The embed token is invalid or has expired.*             | Token older than its lifetime, already used, issued for another domain, or tampered | Fetch a fresh token for every page view                                                                  |
| *The knowledge graph is temporarily busy.*               | Request limit or a short service hiccup                                             | Retry in a moment                                                                                        |
| *Nothing is published yet.*                              | No source is marked **Public in widget**                                            | Mark at least one ready source                                                                           |

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Chat Widget" icon="comments" href="/guides/widget-embed">
    Let visitors chat with your AI team on the same site
  </Card>

  <Card title="External API" icon="code" href="/api-reference/external-overview">
    API keys, scopes, and rate limits for the token endpoint
  </Card>

  <Card title="Organization Wiki" icon="brain" href="/guides/wiki-second-brain">
    Grow the knowledge your graph is built from
  </Card>

  <Card title="Team Management" icon="users" href="/guides/organization/team-management">
    Widget settings require OWNER or ADMIN
  </Card>
</CardGroup>

***

<Note>
  **Questions?** Contact us at [contact@junis.ai](mailto:contact@junis.ai) for help embedding your knowledge graph.
</Note>
