Skip to Content
Platform
GuidesIntegrate into a full-stack app

Integrate into a full-stack app

Connect a working agent runtime to your server or browser application, preserve user identity and task state, and record the feedback needed to improve it.

Use this guide to connect the pieces into one application. When a step depends on a platform resource, follow its link for the full explanation and return here to keep building.

Complete the Quickstart first so the recipe has a working runtime. Keep domain behavior in the recipe and product interface state in the application. Resolve the runtime by its stable slug rather than embedding a version UUID.

Runtime connects to a full-stack application, which gathers user feedback that loops back to the runtime
Text representation of the diagram:
Runtime → Full-stack application → User feedback ↑ │ └──────────────────────────────────┘

1. Connect the runtime to your application

Choose how the browser connects

There are two supported ways to put the agent behind a browser product. They run the same recipe and produce the same tasks and conversations; the difference is whether the Introspection client runs on your server or in the browser.

OptionBrowser talks toIntrospection clientBest fit
Node serverYour application serverIntrospectionClient on the serverServer-rendered apps, existing API/BFF layers, or products that keep streaming and policy on the server.
Direct browserIntrospection Data Plane after a short broker exchangeIntrospectionApiClient in the browserSPAs that want native task streaming, cancellation, reconnect, files, and conversations without proxying every Data Plane call.

Both require a trusted server boundary. With the Node option it handles all agent traffic. With the direct-browser option it authenticates the application user and mints an identity-bound runner session; the browser never receives an API key, service-account secret, or general Control Plane token.

Choose the Introspection project boundary and identity trust model in Authentication & Identity. A service-account application can vouch for the user your product already authenticated. Use federation when Introspection must independently verify the end-user identity through your IdP.

Option A: run through your Node server

The browser posts to your own endpoint. The server authenticates that request, opens a runner for the verified application user, starts the task, and forwards events through your existing HTTP, SSE, or WebSocket transport.

import { IntrospectionClient } from "@introspection-sdk/introspection-node"; const client = await IntrospectionClient.fromServiceAccount({ clientId: process.env.INTROSPECTION_SERVICE_ACCOUNT_CLIENT_ID, clientSecret: process.env.INTROSPECTION_SERVICE_ACCOUNT_CLIENT_SECRET, project: process.env.INTROSPECTION_PROJECT, }); const runner = await client.runtimes("support-agent").run({ // Derived from the authenticated server session, not the request body. identity: { user_id: applicationUser.id }, }); try { const handle = await runner.tasks.start({ prompt: submittedPrompt }); await saveApplicationBinding({ introspectionTaskId: handle.run.task_id, }); for await (const event of handle.stream()) { await writeEventToBrowser(event); } } finally { await runner.close(); await client.shutdown(); }

The browser never connects to Introspection with this option. Your server handles runner lifetime, retries, streaming, and product-specific access checks.

Keep the task so users can reconnect

Whichever shape you choose, save the returned Introspection task ID with the relevant application record. That lets the same user reconnect to the task even if your browser transport changes.

As task events arrive, also save the conversation ID and the ID of each response you show in the product. Those IDs come from Introspection events; do not invent them or accept them from an untrusted browser request. Together, the task, conversation, and response IDs identify the exact interaction a user later rates or corrects.

For latency-sensitive first messages, optionally prepare a fresh draft task on a real intent signal and adopt it on submission. The exact lifecycle, cleanup, and fallback rules live in Warming an interactive task.

On reconnect, load the saved task and its exact conversation. Reattach to an active run when possible; if the replay window has a gap, hydrate canonical task and conversation state instead of inventing missing events. See Continue and control a task.

When the product needs to keep a generated report, export, or upload, persist it through runner.files on the backend or client.files in the browser. The sandbox filesystem is temporary. Create a read-only share only when another person needs access to that file or to the source conversation; tasks themselves are never shared. See the JavaScript file and share APIs for the concrete calls.

An application record may point to an Introspection task. It does not become an Introspection project, and application workspace IDs do not replace the end-user identity attached to the runner.

Record what the user thought

The task tells you what the agent did. To improve it, you also need to know what the user thought or what happened next. Record a thumbs up or down, a correction, a reason, or a comment against the exact response the user saw.

For either application shape, the safest default is to post the UI action to your authenticated backend. Verify that the saved task belongs to that user, then emit the signal with IntrospectionLogs:

import { IntrospectionLogs } from "@introspection-sdk/introspection-node/otel"; const outcomes = new IntrospectionLogs({ token: process.env.INTROSPECTION_TOKEN, serviceName: "support-app", }); // `binding` is server-owned application state, not IDs trusted from the body. assert(binding.applicationUserId === authenticatedUser.id); outcomes.feedback(submittedFeedback.name, { conversationId: binding.introspectionConversationId, previousResponseId: binding.introspectionResponseId, comments: submittedFeedback.comments, taskId: binding.introspectionTaskId, outcome: submittedFeedback.outcome, }); await outcomes.flush();

The browser analytics client can send the same signal directly when the app already has a browser-safe telemetry-write token. Do not expose an API key or service-account secret just to avoid the backend endpoint. See Record feedback for both clients.

Use stable, low-cardinality feedback names and put details in the options. The conversation and previous-response IDs are the join keys that let the improve workflow inspect the user signal beside the exact model and tool trajectory.

2. Send production evidence back to the recipe

Use the saved task, conversation, response, and product outcome when you improve the recipe. They let you see the user signal beside the agent behavior that caused it, form a specific hypothesis, and test the smallest useful change.

Done when

  • The application runs a committed recipe through a runtime.
  • Service credentials remain on the backend and each browser session carries the authenticated end-user identity.
  • Application state retains the Introspection task ID needed to recover the conversation.
  • User feedback and product outcomes are attached to the exact conversation and response.
  • Warmup, when used, remains an optional optimization with a cold-path fallback.
  • Production evidence returns to a focused recipe change rather than accumulating as application-specific prompt logic.

Next

Use Learn from production when feedback, product outcomes, or recurring behavior shows what the agent should do better.

Last updated on