Skip to content
LogoLogo

Studio APIs

Base URL and reference

POST {STUDIO_URL}/api/rpc/<procedure.path>

A live, interactive reference is served by every deployment and generated from the same schemas the server validates against:

{STUDIO_URL}/api/rpc/api-reference
{STUDIO_URL}/api/rpc/api-reference/spec.json

TypeScript client

The API is built with oRPC, so a fully typed client is available. Procedure names, inputs and outputs are checked at compile time.

import { createORPCClient } from "@orpc/client";
 
const agent = await client.agents.create({ name: "Support Triage" });
 
await client.agents.update({
  agentId: agent.id,
  goal: "Classify support email and draft a first reply.",
  instructions: "Always reply in the customer's language...",
  modelId: "openrouter/anthropic/claude-sonnet-4",
  outputFormat: "TEXT",
});
 
await client.agents.deploy({ agentId: agent.id });

Authentication

CallerCredentialNotes
BrowserSession cookieSet at sign in
Server to serverAPI key, prefixed studio_Scoped to one organization

Every organization scoped call re checks live membership against the database, so a stale active organization cannot grant access to a former member.

What every route does before your handler runs

  1. Authenticate the caller.
  2. Confirm live membership of the active organization.
  3. Record an audit observation.
  4. Enforce the authorization gate, by role, by object permission, or both.

Because the audit observer wraps the gate, a refusal is recorded as a Deny row with a reason. An unauthorized caller sending a malformed body gets a 403 with an audit row, not a 400 with nothing.

The modules

The router exposes agents, audit, connectors, organizations, knowledgeBases, skills and toolkits (plus the healthCheck and privateData probes). Note the plural names.

agents

GroupProcedures
Lifecyclecreate get list update delete deploy pause resume archive restore
DiscoverymodelCatalog
Sessionssessions.create sessions.get sessions.list sessions.rename sessions.delete
Runsruns.create runs.stream runs.cancel runs.respond runs.list runs.get
Attachmentsskills.attach skills.detach knowledgeBases.attach knowledgeBases.detach
Wiringmemory.set guardrail.set toolkits.list toolkits.accounts.set toolkits.mode.set
Sharingsharing.share sharing.unshare sharing.list sharing.searchCandidates
Tracestraces.runs.list traces.runs.get traces.runs.export traces.steps.list traces.steps.get

knowledgeBases

create get list update delete, plus:

  • folders.* for folder management
  • documents.initiateUpload / initiateUploads and completeUpload / completeUploads for uploads
  • documents.get documents.list documents.delete documents.retry
  • search
  • sharing.*

skills

create get list update delete and sharing.*.

connectors

Model, memory and guardrail providers: list the catalogue, create, update, disconnect, reconnect and delete connections, and manage the model allowlist.

toolkits

list get accounts.connect accounts.setTools accounts.disconnect.

organizations

get list delete, member management, invitations, groups, roles and permission editing, userPermissions, availablePermissions, logo upload.

audit

list export health.

Conventions that apply everywhere

ConventionDetail
IDsUUID v7 everywhere. Time ordered, so they also sort chronologically.
PaginationCursor based. Pass the last id as cursor. limit is 1 to 100, default 50.
DeletesSoft. The object disappears from reads but history is preserved.
Request sizeBodies are capped at 10 MB.
IdempotencyStarting a run takes a requestIdempotencyKey. Repeating the same key returns the same run instead of starting a second one.
SecretsWrite only. API keys, OAuth tokens and vault contents are never returned.
CorrelationSend X-Request-Id and it is stamped on every audit row for that request.

Errors

StatusMeaning
400Input failed validation
401Not signed in
403Signed in, but not permitted. An audit row is written.
404Not found, or found but not reachable by you. Deliberately identical, so ids cannot be probed.
409Conflict, usually two concurrent writes. Safe to retry.
429Rate limited
500Server error

A complete example

Create, ground, publish and run an agent:

// 1. Create
const agent = await client.agents.create({ name: "Invoice Parser" });
 
// 2. Configure
await client.agents.update({
  agentId: agent.id,
  goal: "Extract structured data from an invoice PDF.",
  instructions: "Return only fields present in the document. Never guess a value.",
  modelId: "openai/gpt-4o-mini",
  outputFormat: "JSON",
  outputFormatConfig: {
    schema: {
      type: "object",
      properties: {
        supplier: { type: "string" },
        invoiceNumber: { type: "string" },
        total: { type: "number" },
      },
      required: ["supplier", "invoiceNumber", "total"],
    },
  },
});
 
// 3. Ground
await client.agents.knowledgeBases.attach({
  agentId: agent.id,
  knowledgeBaseId: kb.id,
});
 
// 4. Publish
await client.agents.deploy({ agentId: agent.id });
 
// 5. Run
const session = await client.agents.sessions.create({ agentId: agent.id });
const run = await client.agents.runs.create({
  agentId: agent.id,
  sessionId: session.id,
  requestIdempotencyKey: crypto.randomUUID(),
  input: { version: 1, parts: [{ type: "text", text: "Parse the attached invoice." }] },
});
 
// 6. Stream
for await (const event of client.agents.runs.stream({
  agentId: agent.id,
  sessionId: session.id,
  runId: run.agentRunId,
})) {
  if (event.type === "text-delta") process.stdout.write(event.delta);
}