Developers

SDK & API.

Two packages do the work. @onpilot/node mints identity tokens on your server. @onpilot/react renders the widget. Both wrap the same public HTTP contract — you can hit it directly if you don't use Node or React.

Early access. The @onpilot/react and @onpilot/node packages are published but stabilizing. Talk to us to get set up with your tenant ID and embed secret.
Docs reflect @onpilot/react and @onpilot/node v0.3.0+. If your lockfile pinned an older version, run npm i @onpilot/react@latest @onpilot/node@latest. The biggest v0.2 → v0.3 break: the externalOrgId claim and option are now copilotId. See the changelog at the bottom of this page.

HTML embed

One script tag

Drop the embed on any page. The widget exchanges the JWT for a session and renders. Works with WordPress, Webflow, Shopify, Rails, Django, Laravel — any framework that can render a script tag.

html
<script
  src="https://chat.onpilot.ai/embed.js"
  data-identity-token="<%= identityToken %>"
></script>

The widget reads data-identity-token, optional data-position, and a small set of styling attributes.

React

React SDK

Render the agent as a React component. Pass either a static identityToken or an async identityTokenProvider so tokens refresh automatically — never hardcode.

tsx
import { CopilotBubble } from "@onpilot/react";

export default function App() {
  return (
    <CopilotBubble
      identityTokenProvider={async () =>
        fetch("/api/onpilot/token").then((r) => r.text())
      }
    />
  );
}

Components: CopilotBubble, CopilotSidebar, CopilotInline, and headless CopilotPanel. See the component picker to choose.

Server

Server SDK (Node)

Sign short-lived identity tokens for your signed-in users. The token carries user identity and agent binding, signed HS256 with your tenant Embed Secret. No network call — sign locally.

ts
import { OnPilot } from "@onpilot/node";

const onpilot = new OnPilot({
  tenantId: process.env.ONPILOT_TENANT_ID!,
  embedSecret: process.env.ONPILOT_EMBED_SECRET!,
});

const identityToken = onpilot.signIdentityToken({
  copilotId: process.env.ONPILOT_COPILOT_ID!,
  user: {
    id: currentUser.id,
    name: currentUser.name,
    email: currentUser.email,
    role: currentUser.isAdmin ? "admin" : "user",
  },
  expiresIn: "1h",
});

Required claims: iss (tenantId), sub (userId), copilot_id. Full claim shape and rotation in Identity tokens.

HTTP contract

No SDK? Sign the JWT yourself

The SDKs wrap a public contract. If your stack isn't Node — Python, Go, Ruby, PHP, Elixir — sign the JWT directly with your embed secret using any HS256 library.

bash
# JWT claims (HS256, signed with your tenant Embed Secret)
{
  "iss": "<tenantId>",
  "sub": "<userId>",
  "copilot_id": "<copilotId>",
  "role": "user",
  "iat": 1730131200,
  "exp": 1730134800
}

Pass the resulting JWT to the embed script's data-identity-token attribute. The widget exchanges it for a session at chat.onpilot.ai/api/v1/embed/resolve.

REST

Conversation REST API

Drive conversations from your backend instead of (or in addition to) the embed widget. Useful for batch automations, server-rendered chat surfaces, or custom UI on top of the same agent.

The conversation REST API is in early access. Endpoints and shapes may change. Talk to us for the current spec and an API key.

Iframe protocol

postMessage events

The widget renders inside an iframe and exchanges typed postMessage events with the host page — for token rotation, page context, theme sync, and lifecycle. The full event catalog (parent ↔ iframe, payloads, when each fires) lives on its own page.

See postMessage protocol for every onpilot:* event the iframe sends and accepts, including how to reach the iframe from outside the React SDK.

Changelog

SDK changelog

Both packages version together. Breaking changes are called out in the migration notes.

v0.3.0 — current

  • Breaking: externalOrgId is renamed to copilotId across signIdentityToken, the JWT claim (copilot_id), and the embed URL query param.
  • resolve() now returns copilotId on the session object.
  • Identity-token errors are explicit: copilotId is required.

Migrate: rename every externalOrgId argument to copilotId. If you sign JWTs by hand, change the claim name from external_org_id to copilot_id. If you build the embed URL by hand, swap ?orgId= for ?copilotId=.

v0.2.0

  • Initial public preview.
  • Used externalOrgId to bind an agent — replaced by copilotId in v0.3.0.