Coworkkit is a closed loop: your browser never sees a secret key. Your backend holds it and mints a short-lived session token; the SDK calls your route, gets the token, and self-configures the rest (the voice connection, the control channel) from that response. There are two seams to wire — a server route that holds the key, and a Provider that calls it — spread across three small files: the route, the Provider, and a one-line wrap in your app's layout.
1 · Install
The browser half and the backend half, as two packages — they sit on opposite sides of a security boundary, so they install separately (why two?):
npm install @coworkkit/react @coworkkit/serverDuring the private preview the packages ship from private git repos, not the public npm registry — install them from git instead (your onboarding grants your project access):
npm i github:emirsafian/coworkkit-react#v0.4.0 github:emirsafian/coworkkit-server#v0.4.02 · Add your key
Create or edit .env.local in your project root with a key from your Coworker's API keys tab:
COWORKKIT_API_KEY=ck_...your_api_key...Restart your dev server after creating it — Next reads env vars at server start.
3 · The server token route
Create app/api/session/route.ts. The helper reads COWORKKIT_API_KEY, calls Coworkkit, and returns { token, serverUrl, cloudUrl } — nothing else to configure:
import { coworkkitSessionRoute } from "@coworkkit/server/next";
export const POST = coworkkitSessionRoute({
// `getUserId` is the user the agent acts as. "dev-user" is fine while developing;
// before you ship, derive it from your auth, server-side — never from the client:
// getUserId: async () => (await auth()).userId // Auth.js / Clerk / Supabase
getUserId: () => "dev-user",
});Going to production: that getUserId stub is dev-only — replace it before you ship. getUserId runs on your server, receives the Request, and can be async, so derive the id from your authenticated session — async () => (await auth()).userId (Auth.js, Clerk, Supabase). Deriving it server-side, never reading it from the client, is what stops one user acting as another; it's the whole reason the mint lives in this route.
Not on Next.js? coworkkitSessionRoute is the Next drop-in, but mintSession mints the same session from any JavaScript backend — Backends lists the stacks and shows the Express shape.
4 · Mount the Provider
getToken is a function prop, so the Provider needs a client component. You never pass your key as a prop — the Provider takes no key prop at all; your secret key lives only in your server route:
"use client";
import { CoworkkitProvider } from "@coworkkit/react";
export function Providers({ children }: { children: React.ReactNode }) {
return (
<CoworkkitProvider
getToken={async () => {
const res = await fetch("/api/session", { method: "POST" });
if (!res.ok) {
// Check before parsing, and guard the parse: an error page (a proxy 404,
// a framework 500) is often HTML, and a throw here would lose both fields.
const body = await res.json().catch(() => ({}));
// Pass the reason AND the status through — that is what lets the FAB say
// "Setup needed" for a bad key instead of a generic "Can't connect":
throw Object.assign(new Error(body.error ?? "session mint failed"), {
reason: body.reason,
status: res.status,
});
}
return res.json();
}}
>
{children}
</CoworkkitProvider>
);
}Then wire it in once — app/layout.tsx is the only file you write by hand. Import Providers and wrap {children}; leave the rest of your layout exactly as it is:
import { Providers } from "./providers";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<Providers>{children}</Providers>
</body>
</html>
);
}5 · Smoke-test
Run your dev server. A small floating circle — the Coworkkit button — appears in the bottom-right corner of every page. Click it, allow microphone access, and say hello — the agent replies by voice. That's the integration; everything past this point (actions, surfaces, elements, Hand mode) is your own app's brain, covered next.

See what the agent sees
Before you wire actions, drop in <CoworkkitWatch /> (from @coworkkit/react/watch) — a dev-only panel that shows what the agent perceives and every action it calls. It's your instrument panel for everything next, so mount it now and keep it open as you wire your first action; The Watch panel shows how.