Skip to main content
@personaai/express exposes the Persona runtime as an Express Router. Mount it, give it a user resolver, and your Express app has the entire runtime surface — AG-UI streaming chat, threads, files, memory, MCP OAuth callbacks, health — without writing any stream forwarding, protocol parsing, or thread-CRUD plumbing. It’s a thin translation layer: all logic lives in the runtime. If you need a raw call the adapter doesn’t cover, drop down to @personaai/sdk alongside it.
Server-side only. The credential you configure is a server-side secret — never bundle it into a browser app.

Prerequisites

  • Node.js 18+ and Express 4 or 5 (the express >= 4 peer range is tested against both)
  • A Project credential shaped "<keyId>.<secret>", minted in Developer Studio
  • An Agent to talk to (create one in Studio, or via the SDK’s agents.create)

Install

The package ships ESM, CommonJS, and TypeScript declarations. It depends on @personaai/runtime (the engine) and peer-depends on express >= 4.

Mount the runtime

That’s the whole backend. The runtime surface now lives at /api/persona/*: The complete route table is in the Express routes reference, and the underlying SDK calls each route proxies to are in the Runtime routes reference.

Two ways to tell the runtime who a request is from

Persona never authenticates users. The runtime asks you who a request is from — see the auth guide for the full picture. Both idioms in brief: Runtime-level resolver — receives the translated request; read cookies, headers, or JWTs however your app already does (shown above). Express middleware pattern — your own auth middleware runs before the adapter and attaches the identity to req. Use the createExpressAdapter factory and a resolveUserFrom resolver:
A resolver returning null (or throwing) makes the runtime respond 401. Every route except GET /health requires an authenticated user.

Lifecycle hooks

All eight hooks (beforeRun, afterRun, onError, beforeToolCall, afterToolCall, onFileUpload, onThreadCreate, onMemoryWrite) pass straight through to the runtime — see the Runtime Hooks reference. Configure them on createRuntime or in the factory options; the adapter never inspects them.

What you get for free

  • SSE streaming with heartbeats and drain-based backpressure; the chat x-persona-run-id header and GET /chat/:runId/resume power reconnects — see Streaming & disconnects.
  • Multipart uploads parsed natively (POST /files, POST /knowledge/:id/documents) — and if you already use multer, the adapter honors your parsed req.file/req.files. See Uploads.
  • Clean disconnects — a client hanging up mid-stream unsubscribes the runtime immediately.
  • Sanitized errors — runtime envelopes ({"error":{code,message}}) are forwarded verbatim; adapter-side failures (e.g. malformed JSON) respond 400 INVALID_REQUEST in the same shape.

Next steps

  • Auth & user resolution — the two resolver patterns in depth.
  • Routes — every route the mount exposes, and adapter translation details.
  • Uploads — multipart parsing, multer coexistence, body-size limits.
  • Streaming & disconnects — SSE, backpressure, reconnect, teardown.
  • Build a chat UI with the raw SDK’s chat.stream and the AG-UI events it emits (see the SDK chat guide), or drop in pre-built components when the experience layer ships.
  • Drop to @personaai/sdk for any call the adapter doesn’t cover.