1. Documentation
  2. Manage Website
  3. AI Chat
ReadmeGitHub
  • Introduction
  • Quickstart
  • Text
  • Code
  • Lists
  • Tables
  • Accordion
  • Badge
  • Button
  • Callout
  • Code Block
  • Code Block Group
  • Code Snippet
  • Columns
  • Custom components
  • GitHub
  • Hover Card
  • Mermaid
  • Properties
  • Related Topics
  • Tabs
  • Tree
  • Images
  • Video
  • Files
  • Grain
  • Shade
  • Moss
  • Heyo
  • Configuration
  • Content
  • Navigation
  • Site Identity
  • Appearance
  • Header and Footer
  • Fonts
  • Icons
  • Integrations
  • Search
  • OpenAPI
  • AI Chat
  • React Router
  • Astro
  • Next.js
  • Cloudflare
  • Vercel
  • robots.txt
  • sitemap.xml
  • JSON-LD
  • rss.xml
  • llms.txt
  • llms-full.txt
  • .md endpoints

AI Chat

Add a documentation-aware AI chat with server-only Pi provider authentication.

AI Chat answers questions using the pages in your Heyo Docs site. It uses Pi's provider catalog and supplies the model with tools to search and read your documentation. Configure the provider, model, and authentication in heyo-docs.config.ts. The endpoint reads ai.chat.auth from that configuration; it does not accept a second auth setting. The browser never receives the provider, model, or authentication settings.

Configure the chat

Add ai.chat to the site configuration. provider must be a lowercase Pi provider identifier and model must be an available model identifier for that provider. The official templates use OpenAI as the example:

heyo-docs.config.ts
export default heyoDocs({
  content: "content",
  ai: {
    chat: {
      provider: "openai",
      model: "gpt-5-mini",
      auth: {
        type: "api-key",
        token: () => {
          const token = process.env.OPENAI_API_KEY;
          if (!token) throw new Error("OPENAI_API_KEY is not configured.");
          return token;
        },
      },
      variant: "right",
      icon: "chat",
      text: "AI Chat",
      name: "Docs Assistant",
      placeholder: "Ask AI about the docs",
    },
  },
});

provider and model identify the model; configure auth here to make chat requests work. The function reads the secret only on the server when a request arrives, not during the build. Without ai.chat.auth, a chat request returns 500 before contacting the provider.

SettingDefaultPurpose
providerRequiredA Pi provider identifier, for example openai.
modelRequiredA model identifier available from that provider.
auth—Server-only credentials; required to answer chat requests.
variantrightright shows a trigger; center shows a compact prompt at the bottom.
iconchatIcon in the right trigger.
textAI ChatLabel in the right trigger.
nameAIDrawer title and assistant message label.
placeholderAsk AI about the docsText in both the center prompt and drawer composer.

With variant: "center", submitting the compact prompt opens the drawer. With variant: "right", the reader opens the drawer from its floating trigger. The drawer remains available while readers move between pages.

Choose server authentication

ai.chat.auth is the only place to configure credentials. Use an API key resolver (as above) for a runtime secret, or a string for a key already available when the configuration loads. Do not pass a separate auth option to createAiChatResponse. Pi's provider determines which authentication type is valid:

TypeUse for
api-keyA secret string or a token: () => string | Promise<string> resolver.
oauthProviders using a fresh access token: { type: "oauth", getAccessToken }.
awsAmazon Bedrock's AWS credential chain, with optional region or profile.
bedrock-bearerAmazon Bedrock bearer authentication, with token and optional region.

The runtime checks that the provider supports the configured authentication type and resolves an API-key function once per chat request. A missing or failing token resolver returns 500 without calling the provider. Keep all of these values on the server; the client receives only the chat label, icon, variant, name, and placeholder.

Authorize before calling a provider

Use the optional server-only ai.authorize guard for authentication, rate-limiting, or an abuse check. Return a Response to stop the request before Heyo Docs resolves a provider or starts a stream:

heyo-docs.config.ts
export default heyoDocs({
  ai: {
    chat: {
      provider: "openai",
      model: "gpt-5-mini",
      auth: { type: "api-key", token: () => process.env.OPENAI_API_KEY! },
    },
    authorize: async (request) => {
      if (!request.headers.get("authorization"))
        return Response.json({ error: "Unauthorized" }, { status: 401 });
    },
  },
});

The guard runs only in the request handler. Do not put browser-only session state or a provider secret in the public configuration.

Add the chat endpoint

The chat UI sends POST requests to /heyo-docs-internal/ai-chat. Add this endpoint before enabling ai.chat; createAiChatResponse supplies the model with the loaded documentation pages and streams its answer back to the UI.

React Router

Add this entry to the existing route array:

app/routes.ts
import { route, type RouteConfig } from "@react-router/dev/routes";

export default [
  // Existing routes…
  route("heyo-docs-internal/ai-chat", "routes/ai-chat.ts"),
] satisfies RouteConfig;

Then create app/routes/ai-chat.ts:

app/routes/ai-chat.ts
import type { ActionFunctionArgs } from "react-router";

import config from "../../heyo-docs.config";
import { pages } from "virtual:heyo-docs-content";
import { pages as markdownPages } from "virtual:heyo-docs-content/server";

export async function action({ request }: ActionFunctionArgs) {
  if (request.method !== "POST")
    return new Response("Method Not Allowed", {
      headers: { Allow: "POST" },
      status: 405,
    });

  const { createAiChatResponse } = await import("@heyo-sh/heyo-docs/ai");
  return createAiChatResponse(request, {
    ai: config.ai,
    markdownPages,
    pages,
    title: config.title,
  });
}

Astro

Create src/pages/heyo-docs-internal/ai-chat.ts:

src/pages/heyo-docs-internal/ai-chat.ts
import { createAiChatResponse } from "@heyo-sh/heyo-docs/ai";
import type { APIRoute } from "astro";

import config from "../../../heyo-docs.config";
import { pages as markdownPages } from "virtual:heyo-docs-content/server";

export const prerender = false;

export const POST: APIRoute = ({ request }) => {
  return createAiChatResponse(request, {
    ai: config.ai,
    markdownPages,
    pages: markdownPages.map((page) => ({
      description: page.description,
      searchContent: page.raw,
      slug: page.slug,
      title: page.title,
    })),
    title: config.title,
  });
};

Next.js

Create app/heyo-docs-internal/ai-chat/route.ts:

app/heyo-docs-internal/ai-chat/route.ts
import { createAiChatResponse } from "@heyo-sh/heyo-docs/ai";

import config from "../../../heyo-docs.config";
import { docsPages, markdownPages } from "../../_heyo-docs/server";

export const runtime = "nodejs";

export async function POST(request: Request) {
  return createAiChatResponse(request, {
    ai: config.ai,
    markdownPages,
    pages: docsPages,
    title: config.title,
  });
}

Cloudflare secrets

On Cloudflare, set HEYO_DOCS_AI_API_KEY with Wrangler (wrangler secret put HEYO_DOCS_AI_API_KEY). Astro can read that binding inside ai.chat.auth in heyo-docs.config.ts:

heyo-docs.config.ts
auth: {
  type: "api-key",
  token: async () => {
    const { env } = await import("cloudflare:workers");
    const token = env.HEYO_DOCS_AI_API_KEY;
    if (!token) throw new Error("HEYO_DOCS_AI_API_KEY is not configured.");
    return token;
  },
},

In React Router on Cloudflare, use token: () => process.env.HEYO_DOCS_AI_API_KEY ?? "" with the generated nodejs_compat and compatibility date; cloudflare:workers is not available to React Router's Node-based prerender build. In Next.js with OpenNext, read the same binding inside the token function using getCloudflareContext() from @opennextjs/cloudflare. The generated Cloudflare endpoints also support existing setups: if ai.chat.auth is absent, they map the HEYO_DOCS_AI_API_KEY binding into that field at request time. Explicit ai.chat.auth always takes precedence. Neither path passes a separate auth argument to createAiChatResponse. Do not commit secrets or place them in wrangler.jsonc.

Keep credentials server-only

Use a server environment variable, platform binding, or OAuth resolver for credentials. Do not put an API key in client code or commit it to the repository. The framework integrations remove provider, model, and auth from the browser-side configuration; the endpoint retains what is needed to call the provider.

OpenAPI< PreviousReact RouterNext >

Powered by heyo

On this page

Configure the chatChoose server authenticationAuthorize before calling a providerAdd the chat endpointReact RouterAstroNext.jsCloudflare secretsKeep credentials server-only