Skip to content

JavaScript SDK

The @cortex/sdk package is the same client the embed widget uses internally — exposed as an npm module for cases where you want a fully custom UI rather than the floating launcher.

It’s tree-shakable, zero-dep at runtime (re-exports types from @cortex/shared only), and weighs <10 KB gzip on the core client. The framework adapters (React, Next.js) are separate entry points so you only pay for what you import.

Install

Terminal window
npm install @cortex/sdk
# or
pnpm add @cortex/sdk

Works in browsers, Node ≥18, Cloudflare Workers, Deno, and any Web-standard runtime — no shims needed.

Core client

import { createCortexClient } from '@cortex/sdk';
const client = createCortexClient({
baseUrl: 'https://api.cortexlayer.dev',
auth: { apiKey: process.env.CORTEX_API_KEY },
});
// Server-side: mint a session for the browser.
const session = await client.mintSession('agt_abc123...', {
origin: 'https://your-site.com',
});
// Browser-side: stream a chat. Set `auth.sessionToken` on a fresh client.
const browserClient = createCortexClient({
baseUrl: 'https://api.cortexlayer.dev',
auth: { sessionToken: session.session_token },
});
for await (const event of browserClient.chat({
agentId: 'agt_abc123...',
messages: [{ role: 'user', content: 'Hi' }],
})) {
if (event.type === 'delta') console.log(event.text);
if (event.type === 'done') break;
}

createCortexClient requires baseUrl — there’s no default. The SDK runs in widgets loaded from arbitrary origins, and silently defaulting to a production URL is a security footgun.

Rotating the session token without re-creating the client

Pass sessionToken per call instead of re-instantiating:

for await (const event of browserClient.chat(
{ agentId: 'agt_abc123...', messages },
{ sessionToken: freshlyMintedToken },
)) { /* ... */ }

The widget uses this pattern internally — its MountHandle.setSessionToken() updates a ref the next chat() call reads — so a token refresh doesn’t unmount the conversation.

React hook

Terminal window
npm install @cortex/sdk react
import { useCortexChat } from '@cortex/sdk/react';
function Chat({ client }) {
const { messages, send, busy } = useCortexChat({
client,
agentId: 'agt_abc123...',
});
return (
<div>
{messages.map((m, i) => <div key={i}>{m.role}: {m.content}</div>)}
<button onClick={() => send('Hello!')} disabled={busy}>Send</button>
</div>
);
}

useCortexChat aborts in-flight streams on unmount, so you can mount/unmount freely without leaking requests. Preact users can use the same hook by aliasing reactpreact/compat.

Next.js (App Router)

The mint route handler is a one-liner that forwards the browser’s Origin header to Cortex (otherwise the API’s allowlist check sees a server-to-server call with no origin and rejects):

app/api/cortex/session/route.ts
import { createCortexClient } from '@cortex/sdk';
import { createMintRouteHandler } from '@cortex/sdk/next';
const client = createCortexClient({
baseUrl: 'https://api.cortexlayer.dev',
auth: { apiKey: process.env.CORTEX_API_KEY! },
});
export const POST = createMintRouteHandler({
client,
agentId: 'agt_abc123...',
// Optional: 401 unauthenticated callers before they hit Cortex.
requireAuth: async (req) => {
const session = await getSession(req);
if (!session) return new Response(null, { status: 401 });
},
});

The same handler works in Cloudflare Workers, Deno Deploy, and any Web-standard runtime — it’s (req: Request) => Promise<Response>.

Vanilla streamer

If you don’t want the full client, streamChat is the one-call low-level streamer:

import { streamChat } from '@cortex/sdk/vanilla';
const stream = streamChat({
baseUrl: 'https://api.cortexlayer.dev',
sessionToken: 'cs_...',
agentId: 'agt_abc123...',
messages: [{ role: 'user', content: 'Hi' }],
});
for await (const event of stream) {
console.log(event);
}

Errors

Every API failure throws CortexApiError:

import { CortexApiError } from '@cortex/sdk';
try {
await client.mintSession('agt_...');
} catch (err) {
if (err instanceof CortexApiError) {
console.error(err.code, err.status, err.requestId);
}
}

code matches the API error codes (agent_not_found, rate_limit_exceeded, …) and is stable across versions. requestId matches the request_id in the API envelope — include it in support tickets.

Bundle budgets

Enforced in CI via size-limit:

EntryBudget
@cortex/sdk (core)<10 KB gzip
@cortex/sdk/react<3 KB gzip
@cortex/sdk/next<2 KB gzip
@cortex/sdk/vanilla<2 KB gzip

Regressions >5% need an explicit sizebot-override PR label.