Quickstart
From approved access and a published agent to a session-backed chat in your app.
Start with an approved account, publish an agent, then connect it to your own authenticated app. Code generation is optional; the generic hook is enough for the first integration.
Access, workspace, and key
- Request access on the homepage. Onboarding is manual; wait for access approval and the deployment and SDK installation details before continuing.
- Sign in to the dashboard supplied during onboarding (the hosted dashboard is app.inlayai.com). Select the intended workspace in the workspace switcher. API routes call this workspace a tenant.
- Open API Keys at
/<tenant_id>/api-keys, create a named key, and store the one-time-revealedinlay_sk_...value in your server's secret store. A key belongs to the selected workspace, not to a single agent.
If your workspace is missing, have onboarding resolve membership before creating keys or agents elsewhere. Do not put a secret key in a browser env variable, a generated client, or a source-control commit.
Publish an agent
Open Agents in that workspace, create or open an agent, and test its draft on the canvas. For this first chat, use an agent that accepts a user message and replies with assistant text, without client tools that need handlers. Use a model available in your deployment; provider setup must be complete before a test run can succeed.
Click Publish and note the agent UUID and published version. A saved draft is not the release contract your app should depend on. Follow Versions and publishing for the draft, publish, and rollback workflow.
Install the SDK
Use Node.js 20 or later and a Next.js App Router application with React.
The source example uses React 19. The integration below needs @inlayai/sdk,
@inlayai/react, and @inlayai/next from the same release.
Onboarded app: use the package source, versions, and registry credentials
provided during onboarding. Package names in these docs are import names,
not a promise that an unqualified public npm install is available. If no
installation details were supplied, ask for them rather than guessing a
registry, download URL, or CLI package.
Source checkout: if onboarding gives you access to this monorepo, the
included projects/inlay/sdk/examples/next-chat app already declares local
package dependencies. Install the repository's pinned Rust toolchain with
rustup, plus wasm-pack and pnpm 10.28.0 on your PATH, then run from the
monorepo root:
rustup target add wasm32-unknown-unknown
pnpm -C projects/inlay/sdk/packages/sdk run build:wasm
pnpm -C projects/inlay/sdk install
pnpm -C projects/inlay/sdk --filter '@inlayai/*' --fail-if-no-match build
pnpm -C projects/inlay/sdk installRun build:wasm before the first install: it invokes wasm-pack without
needing npm dependencies. The install runs the local React package's
prepublishOnly tests, which load that generated WASM. The full build then
creates the JavaScript packages and WASM assets; the second install refreshes
pnpm's local package copies after the build.
This is a source-development path, not a public SDK distribution mechanism.
After configuring the example's environment and replacing its development
auth stub, run pnpm -C projects/inlay/sdk --filter inlay-next-chat-example dev.
For your own app, the two excerpts below show the server/client split. They omit imports and your app-specific auth implementation; add the imports described with each excerpt.
Environment
| Variable | Where | What |
|---|---|---|
INLAY_SECRET_KEY | server only | The workspace key used to mint sessions. |
NEXT_PUBLIC_INLAY_AGENT_ID | browser | The published agent UUID from the same workspace. |
Keep local secrets in an uncommitted .env.local; configure the equivalent
secrets in your deployment. The SDK defaults to https://app.inlayai.com.
For staging or self-hosting, explicitly pass baseUrl to both mintSession
and useInlayChat. You can read it from INLAY_API_URL on the server and
NEXT_PUBLIC_INLAY_API_URL in the browser, but those two functions do not read
the variables for you. The CLI uses --base-url (or INLAY_API_URL), not a
baseUrl flag.
The full next-chat example has an additional server-side INLAY_AGENT_ID
for its session restriction and a .env.example that targets localhost.
Set both agent IDs to the same UUID and both API URLs to your intended
deployment; do not reuse its local defaults for hosted onboarding.
Mint a session on the server
Your secret key is a server credential — it can start runs for anyone. It must never reach the browser.
mintSession exchanges it for a short-lived, user-scoped session, which is
safe to hand to the client. In app/page.tsx, import mintSession from
@inlayai/next and Chat from ./chat.
Implement getUserId() using your application's server-verified login.
It must reject or redirect unauthenticated visitors before minting, and return
a stable user ID. Never accept that ID from an unchecked request parameter
or use a shared demo ID for real users: it scopes their conversations, memory,
and billing.
// app/page.tsx — a Server Component.
export default async function Page() {
const session = await mintSession({
secretKey: process.env.INLAY_SECRET_KEY!,
userId: await getUserId(),
});
return <Chat session={session} />;
}The minimal mint permits any agent in the key's workspace. For an app that
should run only this agent, also pass agentIds: [yourAgentId] to mintSession.
Mint per authenticated request; do not cache one user's session for everyone.
A plain Session does not refresh itself. For longer-lived pages, pass an
authenticated server action as the session provider, or pair
createSessionRoute and createSessionProvider.
The transport re-calls a provider after a 401 and retries. Recheck your app's
login on every mint, including refreshes.
Render the conversation
In app/chat.tsx, keep "use client" first, then import useInlayChat from
@inlayai/react and the Session type from @inlayai/sdk.
// app/chat.tsx — a Client Component.
"use client";
export function Chat({ session }: { session: Session }) {
const { snapshot, send, canSend } = useInlayChat({
agentId: process.env.NEXT_PUBLIC_INLAY_AGENT_ID!,
session,
});
// send() RESOLVES on a run failure by default — the reason lands on
// `snapshot.error` (failures: "reject" restores the rejection; see
// failure-handling.md). Misuse errors reject either way; the catch covers them.
const showFailure = (e: unknown) => console.error(e);
return (
<>
{snapshot.rows.map((row, i) => (
<p key={row.id}>{row.type === "assistant" ? row.content : null}</p>
))}
<button disabled={!canSend} onClick={() => void send("hello").catch(showFailure)}>
Send
</button>
</>
);
}That is the whole loop: snapshot.rows is what happened, send adds to it, and
canSend tells you when a send is possible.
The split you just wrote — mint on the server, hook in a client component — is the recommended shape, but it is not forced: the hook also runs called directly in Server Components (it renders idle there). See Server and shared components.
What you just got
- Streaming. Assistant text arrives token by token, revealed at a steady pace rather than in whatever bursts the network delivered.
- Reconnect-safe ordering. Rows carry server-assigned identity, so a reopened conversation replays in exactly the order it happened.
- Reactivity that does not cost the whole page. Content, topology, and per-node views are separate subscriptions.
Verify the integration
Send a message as a signed-in user and confirm an assistant row appears. Open Observability in the same workspace to inspect the conversation and the agent version that ran. See the operator guide.
If minting fails, check the key, workspace, API origin, and your auth helper.
If a run fails, render snapshot.error as well as handling rejected calls;
Failure handling explains the distinction. Do not
solve an auth error by exposing the secret key to the browser.
Next
Rendering row.content for every row is the quickstart shortcut. A real UI
wants outputs, tool calls and turns to look different —
The timeline.
Once the generic integration works, optionally
generate a version-pinned typed client. The CLI is not
required to mint sessions or use useInlayChat.