GuidesBackend and frontend integration

Build a multi-tenant application

Use this pattern when many End-users share your Blazing Agents tenant. Your backend remains the identity authority and maps each verified principal to an opaque, stable userId for Attribution.

Outcome

Each authorized application chat starts with trusted Attribution, and your backend can list its Sessions and usage by the same identifier without storing a Blazing Agents End-user record.

Before you begin

Create a backend SDK client and an application-owned mapping from signed-in principals to allowed Agents and Sessions. Review Tenancy and end-user Attribution, Sessions and Turns, Usage and quotas, and Security and credentials.

Map application identity

Derive a stable opaque userId from the authenticated application principal. Resolve agentId, the application chat, and any sessionId through backend authorization; never copy those values from an unchecked browser body.

import { BlazingAgents, type UIMessage } from "@blazing-agents/sdk";

const client = new BlazingAgents({ apiKey: process.env.BLAZING_AGENTS_API_KEY! });

export async function runAuthorizedTurn(input: {
  principal: { subject: string; workspaceId: string };
  appChatId: string;
  message: UIMessage;
}) {
  const userId = `app:${input.principal.subject}`;
  const chat = await resolveAuthorizedChat(input.principal, input.appChatId);
  const result = await client.chat({
    agentId: chat.agentId,
    ...(chat.sessionId ? { sessionId: chat.sessionId } : {}),
    message: input.message,
    userId,
    metadata: { workspaceId: input.principal.workspaceId },
  });
  const sessionId = await result.sessionId;
  for await (const _chunk of result.toUIMessageStream()) { /* drain */ }
  if (!chat.sessionId) await saveAuthorizedSession(input.appChatId, sessionId);

  const sessions = await client.sessions.list(chat.agentId, { userId });
  const usage = await client.usage.getForAgent(chat.agentId, {
    userId, groupBy: "session",
  });
  const otherUserSessions = await client.sessions.list(chat.agentId, { userId: "app:other" });
  return { sessionId, sessions, usage, otherUserSessions };
}

Attach Attribution

userId and metadata stamp the new Session and this Turn's usage. userId: "" is the tenant-level bucket; omitting the field defaults to that value. Attribution supports filtering and reporting, but it is not authorization and does not weaken the platform's credential-derived tenant isolation.

Filter Sessions and usage

sessions.list(agentId, { userId }) filters that Agent's Sessions by exact Attribution. Tenant-wide usage.get() accepts agentId, sessionId, and userId filters. usage.getForAgent(agentId, query) takes the effective Agent scope from its path argument; an agentId inside query is ignored. Its effective query filters are sessionId and userId. Both usage methods also accept from, to, groupBy, and limit; groupBy supports day, agent, model, session, and user. Omitting userId returns all rows visible to the tenant credential, while passing "" selects tenant-level rows.

Verify tenant boundaries

After the stream finishes, assert that sessions.data contains sessionId, its userId equals the derived value, and the usage totals include the Turn. With an unused identifier, otherUserSessions.data should be empty. Also test your application authorization layer rejects another principal's appChatId before calling the SDK. A credential from a different Blazing Agents tenant cannot read or resume this tenant's Session.

Production notes

  • One tenant API key can reach every resource in that tenant. Enforce roles, Agent allowlists, and application-chat ownership before SDK calls.
  • Keep userId stable and opaque. Changing the mapping splits Session and usage filters across identifiers.
  • Apply the same trusted Attribution to Tasks and inspect Artifact userId when exposing their results. Their tenant isolation is enforced by the credential; application-level access remains your responsibility.
  • Do not treat an empty filtered list as proof that a caller may access a separately supplied resource ID.

SDK and REST reference

On this page