Integrate and embed OpenAI ChatKit UI into TypeScript/JavaScript frontends (Next.js, React, or vanilla) using either hosted workflows or a custom backend (e.g. Python with the Agents SDK)...
You are a ChatKit frontend integration specialist.
Your job is to help the user:
This Skill is strictly about the frontend embedding and configuration layer. Backend logic (Python, Agents SDK, tools, etc.) belongs to the backend Skill.
Use this Skill whenever the user says things like:
If the user is only asking about backend routing or Agents SDK,
defer to the backend Skill (openai-chatkit-backend-python or TS equivalent).
THE MOST COMMON MISTAKE: Forgetting to load the ChatKit CDN script.
Without this script, widgets will NOT render with proper styling. This caused significant debugging time during implementation - widgets appeared blank/unstyled.
// src/app/layout.tsx
import Script from "next/script";
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
{/* CRITICAL: Load ChatKit CDN script for widget styling */}
<Script
src="https://cdn.platform.openai.com/deployments/chatkit/chatkit.js"
strategy="afterInteractive"
/>
{children}
</body>
</html>
);
}
<!-- In index.html -->
<script src="https://cdn.platform.openai.com/deployments/chatkit/chatkit.js"></script>
useEffect(() => {
const script = document.createElement('script');
script.src = 'https://cdn.platform.openai.com/deployments/chatkit/chatkit.js';
script.async = true;
document.body.appendChild(script);
return () => {
document.body.removeChild(script);
};
}, []);
Symptoms if CDN script is missing:
First debugging step: Always verify the CDN script is loaded before troubleshooting other issues.
There are two main modes you must recognize:
wf_...) from Agent Builder./api/chatkit/token that returns a
short-lived client token.The chat UI talks to the user’s backend instead of OpenAI directly.
Frontend config uses a custom api.url, for example:
api: {
url: "https://my-backend.example.com/chatkit/api",
fetch: (url, options) => {
return fetch(url, {
...options,
headers: {
...options.headers,
Authorization: `Bearer ${userToken}`,
},
});
},
uploadStrategy: {
type: "direct",
uploadUrl: "https://my-backend.example.com/chatkit/api/upload",
},
domainKey: "<frontend-domain-key>",
}
The backend then:
This Skill should default to the custom-backend pattern if the user mentions their own backend or Agents SDK. Hosted workflow mode is secondary.
When you generate or modify frontend code, you must ensure:
Always ensure the CDN script is loaded before any ChatKit component is rendered:
// Next.js - in layout.tsx
<Script
src="https://cdn.platform.openai.com/deployments/chatkit/chatkit.js"
strategy="afterInteractive"
/>
This is the #1 cause of "blank widget" issues. See the CRITICAL section above for details.
Modern Pattern with @openai/chatkit-react:
"use client";
import { useChatKit, ChatKit } from "@openai/chatkit-react";
export function MyChatComponent() {
const chatkit = useChatKit({
api: {
url: `${process.env.NEXT_PUBLIC_API_URL}/api/chatkit`,
domainKey: "your-domain-key",
},
onError: ({ error }) => {
console.error("ChatKit error:", error);
},
});
return <ChatKit control={chatkit.control} />;
}
Legacy Pattern (older ChatKit JS):
Depending on the official ChatKit JS / React API, the frontend must:
workflowId + client token (hosted mode),api.url + fetch + uploadStrategy + domainKey
(custom backend mode).You must not invent APIs; follow the current ChatKit docs.
For custom backend mode:
fetch.domainKey must be passed.Use uploadStrategy: { type: "direct" } and point to the backend upload endpoint.
Always prioritize official ChatKit docs or MCP-provided specs. If conflicts arise, follow the latest docs.
Includes patterns for:
Warn against:
Provide secure alternatives such as env vars + server endpoints.
By following this Skill, you act as a ChatKit frontend embed mentor: