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

# Embed the chat widget

> Mint a browser-safe publishable key, drop in the loader snippet, and lock it to the origins you control.

The chat widget puts a published agent on your website. It runs from a **publishable key**
(`flowyte_pk_…`) — a browser-safe key scoped to a single agent, locked to an origin
allowlist, and limited to the `chat:public` capability so it can never read tenant data. You
mint the key with your secret key, then paste a loader snippet. All paths are relative to
`https://builder.flowyte.com/api/v1`.

<Warning>
  Mint and manage keys server-side with your secret key (`flowyte_sk_…`). The publishable key is
  safe to ship in a web page; **never** put a secret key in client-side code.
</Warning>

## What you'll use

| Action                 | Endpoint                                         | Scope           |
| ---------------------- | ------------------------------------------------ | --------------- |
| Mint a publishable key | `POST /agents/{id}/publishable-keys`             | `pubkeys:write` |
| Get the embed snippet  | `GET /agents/{id}/widget/embed`                  | `widgets:read`  |
| Customize theme / copy | `PUT /agents/{id}/widget`                        | `widgets:write` |
| Rotate the key         | `POST /agents/{id}/publishable-keys/{id}/rotate` | `pubkeys:write` |
| Revoke the key         | `DELETE /agents/{id}/publishable-keys/{id}`      | `pubkeys:write` |

<Steps>
  <Step title="Mint a publishable key">
    `allowedOrigins` is the allowlist of sites that may use this key. Match each origin exactly —
    scheme + host (+ port). Include both the apex and `www` if you serve both. Set `rateLimitRpm`
    to bound abuse. The full key is returned in `keyPublic` (it's public, so it isn't show-once).

    <CodeGroup>
      ```bash curl theme={null}
      curl -X POST https://builder.flowyte.com/api/v1/agents/$AGENT_ID/publishable-keys \
        -H "Authorization: Bearer $FLOWYTE_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{
          "name": "Marketing site",
          "allowedOrigins": ["https://www.example.com", "https://example.com"],
          "env": "live",
          "rateLimitRpm": 60
        }'
      ```

      ```ts Node theme={null}
      const res = await fetch(
        `https://builder.flowyte.com/api/v1/agents/${agentId}/publishable-keys`,
        {
          method: "POST",
          headers: {
            Authorization: `Bearer ${process.env.FLOWYTE_API_KEY}`,
            "Content-Type": "application/json",
          },
          body: JSON.stringify({
            name: "Marketing site",
            allowedOrigins: ["https://www.example.com", "https://example.com"],
            env: "live",
            rateLimitRpm: 60,
          }),
        },
      );
      const { data: key } = await res.json(); // key.keyPublic = flowyte_pk_live_…
      ```
    </CodeGroup>
  </Step>

  <Step title="Get the embed snippet">
    Pass the key's `publishableKeyId` to get the loader. The response carries `scriptSnippet` and
    the `publishableKey` to drop into your page.

    ```bash theme={null}
    curl "https://builder.flowyte.com/api/v1/agents/$AGENT_ID/widget/embed?publishableKeyId=$PUBLISHABLE_KEY_ID" \
      -H "Authorization: Bearer $FLOWYTE_API_KEY"
    ```
  </Step>

  <Step title="Paste it on your site">
    Copy the exact `scriptSnippet` returned above into your page, just before `</body>`. It looks
    like a single async loader tag carrying your publishable key:

    ```html theme={null}
    <script
      src="https://builder.flowyte.com/widget/v1.js"
      data-flowyte-key="flowyte_pk_live_…"
      async
    ></script>
    ```

    The widget loads its theme and copy from a public bootstrap call — no secrets in the browser.
  </Step>

  <Step title="Customize (optional)">
    Tune the look and wording without touching the snippet. `PUT /agents/{id}/widget` accepts
    `theme`, `copy`, and `behavior` objects.

    ```bash theme={null}
    curl -X PUT https://builder.flowyte.com/api/v1/agents/$AGENT_ID/widget \
      -H "Authorization: Bearer $FLOWYTE_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{ "theme": { "accent": "#2563eb" }, "copy": { "greeting": "Hi! How can I help?" } }'
    ```
  </Step>
</Steps>

<Note>
  A request from an origin that isn't on the allowlist is rejected with **403** — add the exact
  origin and try again. To roll a key without changing the agent or origins, use **rotate** (it
  keeps a short grace window); use **revoke** to kill it immediately.
</Note>

The widget always serves the agent's **published** version. Publish after any edits, and make
sure the agent has at least one enabled chat channel.

Related: [Agents](/concepts/agents) · [Draft vs published](/get-started/draft-vs-published) · [OpenAI-compatible chat](/guides/openai-compatible-chat)
