Platform Backend

Studio Frontend

The Studio is a Next.js application (App Router) in frontend/. It renders the two user interfaces of the platform — the classic Studio and the AI Assistant — signs users in, and serves the OAuth endpoints of the hosted MCP server. It holds no business logic of its own: data comes from the backend’s REST API and, for the assistant, from its Socket.IO connection. frontend/ARCHITECTURE.md states the rendering and data rules in short; this page puts them in context.

Layout

Path Contents

app/

Routes. (frontface) holds /login (and the company deep link /login/{idpRef}), /signup and /invite/{id}; (error-pages) holds /auth-expired and /server-error; dashboard/studio/... is the Studio; dashboard/aiassistant the AI Assistant; design-system a showcase of the UI components; oauth/ and api/ the route handlers. Folders whose name starts with _ (_api, _components, _forms, _dialogs) are private to their route and not routable.

features/

Feature modules shared by both interfaces: agent, analytics, billing, knowledge, members, test-suite, identity-providers and others, each with its query options and mutations (_api), forms and tables.

ui/

The component library: buttons, inputs, selects and comboboxes, dialogs and sheets, tables, tags, toggle cards — built on Base UI and Tailwind CSS.

components/

Application components used across features: breadcrumbs, GuardedLink, the @-mention editor of agent fields, the confirmation card of the assistant, warning cards, form field bindings.

context/, hooks/

React contexts — the assistant’s WebSocket connection, workspace scope, navigation guard, UI preferences — and hooks such as useProjectPermissions and useListViewState.

lib/

The API client, authentication helpers, environment detection, the MCP OAuth implementation, the query client, HydratedQuery and the fuzzy search of pickers.

The Studio’s route tree follows the domain: /dashboard/studio/accounts/{tenantId}/projects/{projectId}/ and below it agents, knowledges, evaluations, integrations, interactive-content, content-navigation, analytics, members, settings, identity-providers and the Test section (test/simulated-users, test/agent-evaluations, test/test-suites). Account pages (account-members, billing, settings) sit under the account, and the staff-only admin section (templates, skills, voices) under /dashboard/studio/admin. / redirects to /login.

Rendering and Data

Server Components render the static structure of a page; data-dependent sections sit behind the smallest useful Suspense boundary so the page shell streams immediately.

Application data goes through TanStack Query, including the data of the first render:

  1. HydratedQuery (lib/hydrated-query.tsx) creates a request-scoped query client on the server and prefetches the queries a section needs.

  2. It passes the dehydrated cache through a HydrationBoundary.

  3. The client component calls useQuery with the same query options and reads the hydrated cache; later refetches carry the session’s access token.

  4. Mutations update known cache entries with setQueryData where useful and invalidate the affected query keys. Query keys are feature-scoped and exported by the feature, so every consumer invalidates the same key.

Two consequences to keep in mind:

  • HydrationBoundary writes server data for queries that already exist in the browser cache in an effect, after the first render. A form that takes its defaults on first render can therefore start from an older cached copy: write the mutation response into the cache, and let an untouched form adopt newer data.

  • Form fields (components/form) are lazy components. A form inside an animated container or a dialog needs its own local Suspense boundary, or the first, cold render suspends the whole dialog.

Requests go through createPlatformApiClient(accessToken) (lib/api-client.ts, Axios): the bearer token, JSON or multipart bodies, one token refresh and retry on 401, and a readable message for an empty 403. Response payloads are parsed with Zod schemas next to the feature that uses them.

Authentication in the UI

auth.ts configures NextAuth with Keycloak; the details are in Studio Sign-In. Pages call requireSession() or requireAccessToken() on the server, and the scope guards in lib/routing/scope-guards.ts load the account or project context and answer "not found" when the caller may not see the page. Client components read their capabilities — view, edit, manage, members, billing — from useProjectPermissions() and the access state of lib/authorization/access-control.ts. While the role is still loading, editing is enabled optimistically; the backend enforces the real rule. Staff accounts (isAdminEmail) additionally see the Admin section.

Two Interfaces

The Studio and the AI Assistant show the same objects and may present them differently. They share what is genuinely common — query options, mutations, form logic, types — and keep separate views where forcing one component to serve both would hide real differences: the assistant’s views live in app/dashboard/aiassistant/_components/*-view.tsx, and many reuse the Studio’s _forms.

The assistant view is driven by the backend: context/websocket-context.tsx keeps one Socket.IO connection per tab, reconnects with backoff, replays the current context after a reconnect, and dispatches render.* events to the matching view (AI Assistant). The switch in the Studio header moves between the interfaces and keeps the open account, project or object.

Route Handlers

Route Purpose

/api/auth/[...nextauth]

NextAuth.

/login/{idpRef}

Starts a sign-in straight at a company’s identity provider (Studio Sign-In).

/oauth/*, /api/mcp-oauth/* (served as /.well-known/*)

The authorization server of the hosted MCP server (OAuth Authorization Server).

/api/realtime-configurations/preview

Streams a voice preview from the backend, adding the session’s token.

/api/v1/visual/...

Proxies of the backend’s visual endpoints for requests made to the Studio’s own origin. In deployments the ingress routes /api/v1/ to the backend before such a request reaches the Studio.

/api/contact

Creates a Zendesk ticket from the contact form.

Conventions

The rules for Studio code — which components and patterns to reuse, GuardedLink, list state, picker search, page titles, and the formatting, lint and type checks — are collected in Code Conventions. Build and run instructions are in Development Setup.