Skip to main content
The local API base URL is http://127.0.0.1:3000/v1. Hosted clients should use their deployment’s HTTPS URL. All tenant resources below are scoped by the project in the path and by the authenticated principal resolved from the credential.

Common headers

Idempotency-Key is required for operations that change application state. An OAO API key authenticates against any project of its organization: the project addressed in the URL path selects the acting project, and requests without a project in the path act in the organization’s earliest-created project. See Authentication for how per-project principals are resolved.

TypeScript SDK helpers

The workspace SDK exposes the same context and run-settling operations used by the guided setup:
waitForRunSettled polls GET /projects/{projectId}/runs/{runId} until the run is completed, failed, cancelled, or timed_out. It throws run_wait_timeout when the configured deadline expires; it does not cancel the durable run.

Browser authentication

GET /context returns principal with id, organizationId, projectId, kind, internal subject, scopes, and optional displayName, plus the current organization, project, available tenant choices, model presets, and authProvider. In IAP mode, isIapDefaultProject identifies the project that anchors console authentication; its memberships cannot be removed. displayName is presentation metadata from the active identity provider; authorization continues to use the PostgreSQL principal and scopes. POST /auth/logout requires the current cookie session, clears both session and refresh cookies, and returns {} or { "redirectUrl": "https://..." }. The console follows the provider redirect when present so the hosted WorkOS session is ended as well. Cookie-authenticated writes require an allowed Origin and an Idempotency-Key.

Organizations, projects, and members

Organization and project reads expose the PostgreSQL-authoritative tenant. GET /projects lists every project of the authenticated organization, projects can be created and deleted through the API, and a signed-in session switches its active project with POST /auth/switch-project. Member responses additionally include the persisted organizationRole; role remains the project role. GET /context includes principal.organizationRole and principal.projectRole (nullable when membership does not exist), independently of scopes. The SDK preserves these fields. For IAP, PATCH /projects/{projectId}/members/{memberId} with { role } changes the person’s organization role, scopes, and existing IAP project memberships in one audited transaction. Owner grants use *; Admin uses a fixed explicit profile, not *. Owners can appoint/change other Owners; Owners/Admins can grant Admin/Member/Viewer access. Admins cannot change Owners. Self changes and loss of the last Owner are rejected. Only authenticated IAP humans qualify; API keys do not. Lowering access revokes keys created by the target identity and any keys derived from them. Key issuance is serialized with role changes so an in-flight request cannot retain revoked authority. DELETE removes organization and existing project memberships and revokes those keys, retaining identity records as the rejoining guard. In IAP mode, POST /members accepts { email } for a user who has already completed a verified IAP login. It copies that immutable identity into the current project and inherits the person’s stored organization role and scope profile. It never invites a new Google user or changes their organization role. DELETE .../project-access removes only the current project membership; the organization membership, other projects, and organization-scoped API keys remain unchanged. Other authentication modes retain the existing project-role and explicitly supplied scope behavior. All routes require project:admin. Member and project writes also require an Idempotency-Key. Non-IAP member create input is { subject, role, scopes }; IAP member create input is { email }. Roles are owner, admin, member, or viewer. A member response includes the stable principal subject, safe scopes, role, and optional identity-provider displayName and email. Provider metadata is presentational and does not grant access.

Create a project

POST /projects takes { "slug": "...", "name": "..." }. The slug is 1-80 characters matching ^[a-z0-9][a-z0-9-]*$ and must be unique inside the organization. The caller needs organization owner or admin role (an API key qualifies through its scopes: * or project:admin). A human creator is provisioned into the new project in the same transaction — a principal copy, an owner membership, identity rows, and tenant links — so the identity provider resolves into the new project immediately. The response is the project object.

Delete a project

DELETE /projects/{projectId} permanently removes the project and every row it owns: agents, Skills, toolsets, presets, sessions, runs, events, and the audit trail, plus the Flue conversation state of its threads. Two rules apply: the project the caller is acting in cannot delete itself, and the last remaining project of an organization cannot be deleted. In IAP deployments, the configured default project also cannot be deleted because it anchors console authentication. Organization-shared resources (API keys, model, storage, sandbox, and MCP connections) are untouched. Workspace archives and run files in object storage are not removed automatically; operators clean the project’s workspace-backups/ and run-files/ prefixes separately. Adding a member does not send a WorkOS invitation or create a provider mapping. To let that person sign in through WorkOS, provision the existing OAO principal with the operator workflow described in Authentication. The active principal cannot remove itself.

Model providers, presets, and catalog

A published agent version names an immutable model preset. A hosted preset references an OpenRouter, OpenAI, Anthropic, or xAI provider connection in the same organization. Provider connections are organization-shared: every project uses the same pool, addressed through any project path. Model presets remain project-scoped. Reads require agent:read; writes require project:admin. Every write requires an Idempotency-Key. Create a provider with { key, displayName, providerType, apiKey }, where providerType is openrouter, openai, anthropic, or xai. Responses contain credentialConfigured, a 12-character credentialFingerprint, and an incrementing credentialVersion; they never contain the API key or encryption material. Rotate a key with { "apiKey": "..." }. DELETE /model-presets/{id} archives a project preset and returns { "id", "key", "archived": true }. Presets stay append-only: the row, key, model, routing, and settings are never changed or deleted, because published agent versions pin the key. An archived preset leaves the list, can no longer be named by a new agent version (400), and its key stays reserved (409 on reuse). Sessions of already published versions keep resolving it. Deployment presets cannot be archived. The audit log records model_preset.archived. DELETE /model-providers/{id} removes a connection. It is refused with 409 while any live preset still routes through it; archive those presets first. Removal wipes the encrypted key, leaves the list, releases the provider key for a new connection, and returns { "id", "removed": true }. Rotating or building presets on a removed connection returns 404, and runs of agents pinned to its archived presets fail with a clear provider-removed error. The audit log records model_provider.removed. GET /model-catalog requires providerId, accepts search and limit, and returns providerId, providerType, and safe catalog entries. Each entry includes providerType, prefixed model, catalogId, display name, context window, output limit, and reasoning support. Anthropic and supported xAI reasoning entries may additionally include adaptiveThinking, thinkingCanBeDisabled, and effortLevels. Create a preset with:
The model must belong to the selected provider catalog. OpenRouter catalog responses include live OpenRouter models and saved presets as openrouter/@preset/<slug> entries. OpenRouter supports the routing policy below. OpenAI catalog responses include the live models available to the selected key in supported Responses text/reasoning families, including new GPT-4.1, GPT-5-and-later, and o3-and-later IDs absent from the pinned catalog. Specialized non-agent variants are excluded. Unknown context/output metadata is returned as null; catalog requests fetch the account list afresh. Entries without verified capabilities and pricing set runtimeSupported: false and cannot be used to create presets (400). Omitted or true means supported. Discovery limitations are described in Adding models. OpenAI presets require an empty routing object and accept the immutable OpenAI settings object below. GPT-6 Astra entries set thinkingCanBeDisabled: false and list low, medium, high, xhigh, and max in effortLevels; creating an Astra preset with effort: "none" returns 400. Anthropic catalog responses likewise include the live models available to the selected key that OAO’s native Messages runtime supports. Anthropic presets require empty routing and accept the immutable Claude settings below. If an older client omits settings, the API stores the provider’s documented OAO defaults. xAI catalog responses come from the selected key’s live GET /v1/language-models result and include text-output Grok models as xai/<catalog-id>. xAI presets require empty routing. Documented reasoning models accept the immutable xAI settings below; if an older client omits them, the API stores high effort and text format. Newly listed language-model IDs do not require a hard-coded OAO catalog update, but reasoning controls appear only after OAO can identify the model’s supported effort levels.

OpenAI model settings

The runtime translates these provider-neutral names to text.format, text.verbosity, reasoning.mode, reasoning.effort, and reasoning.summary in OpenAI Responses requests. Direct settings are rejected for OpenRouter presets.

Anthropic model settings

The selected live model must support the saved thinking mode and effort, and maxTokens cannot exceed its advertised output limit. The runtime translates these values to thinking.type, max_tokens, and output_config.effort in Anthropic Messages requests. Anthropic’s max_tokens is the combined ceiling for thinking tokens and the visible response, not a separate thinking budget. Models without live effort support are omitted from the OAO catalog. Manual thinking.type: "enabled" and budget_tokens are not accepted by this preset contract.

xAI model settings

The selected catalog model must support the requested effort. OAO maps these values to text.format and reasoning.effort in xAI Responses requests. Reasoning cannot be disabled on supported Grok reasoning models. Tools and the system prompt remain part of the immutable agent version rather than the model preset.

OpenRouter routing policy

Failure modes

Provider create and rotation requests accept an API key as write-only secret input. No response, list route, event, audit detail, log, or trace returns it.

Sandbox providers

Sandbox provider connections are organization-shared — every project uses the same pool, addressed through any project path — and require project:admin for writes. List reads use agent:read. daytona is the only supported providerType today; the connection and agent contracts keep provider type and provider key explicit so more adapters can be added later. Create a connection with:
Responses expose only credentialConfigured, a 12-character fingerprint, credential version, target, and allowlists. Rotate with { "apiKey": "..." }. Update configuration with { target, restrictedEgress }. All writes require an Idempotency-Key; no read route returns credential or encryption material. Snapshot discovery decrypts the selected connection only inside the API and uses it to query Daytona. The response returns every snapshot visible to that connection with safe identity, state, image, resource, region, class, and timestamp metadata. It excludes credentials, build logs, build details, and provider error payloads. available is true only when Daytona reports the snapshot as active. An agent version’s sandbox object requires enabled, provider, network, and capabilities. When enabled is true, it also requires an active snapshot UUID as snapshotId. Capabilities are unique values from filesystem_read, filesystem_write, shell, and browser. Publishing an enabled sandbox rejects an unknown project provider, missing encryption setup, a snapshot that is not active for the selected Daytona connection, or a restricted network policy whose selected connection has no allowlist.

Object storage providers

S3-compatible storage connections are organization-shared: every project uses the same pool of connections and the same organization-wide default. Writes require project:admin; list reads use agent:read. Credentials are encrypted with the platform credential key and are accepted only as write-only input. Create input contains key, displayName, providerType: "s3", nullable HTTP(S) endpoint, region, bucket, nullable safe relative prefix, forcePathStyle, optional setDefault, accessKeyId, secretAccessKey, and optional sessionToken. The organization’s first connection becomes the default even when setDefault is false. All writes require an Idempotency-Key. Responses contain connection coordinates, default state, a 12-character credential fingerprint, and credential version. They never contain S3 credentials or encryption material. Provider configuration is immutable; credentials can rotate and the default can change. Each uploaded attachment and each thread with an existing backup stays bound to the provider selected when that object was first written. The objects read lists one folder level under the project’s tenant-scoped storage root and uses agent:read. Optional query parameters are a safe relative folder prefix, an opaque cursor from a previous truncated page, and a limit from 1 through 1000. The response contains the normalized prefix, folders (common prefixes ending in /), objects with key, sizeBytes, and optional lastModifiedAt, plus truncated and, when truncated, the next cursor. Keys are logical: they exclude the bucket, configured provider prefix, and tenant path segments. See Send files to an agent and Workspace backups for object keys, checksum verification, replacement-sandbox restore, and failure contracts.

MCP servers, credentials, and toolsets

Remote MCP servers, credentials, and credential policies are organization-shared and immutable/versioned: every project uses the same connection pool through any project path. Toolsets remain project-scoped — each project selects its own restricted tool sets from the shared servers. All writes require Idempotency-Key. Secret values are accepted only on credential create or rotation and never appear in a response. Create a server with { key, displayName, endpointUrl, transport }, where the endpoint is HTTPS and transport is streamable_http or legacy_sse. Discover with optional { credentialPolicyVersionId }; authenticated discovery requires an active policy whose exact origin and path prefix contain the endpoint. Repeated discovery returns the current immutable version while metadata is unchanged. Tool metadata drift publishes and returns the next server version; existing toolsets remain pinned to their original server version. Create a credential with { key, displayName, kind, headerName, secret }. static_bearer requires headerName: null; api_key_header requires a safe header name. Responses contain only the fingerprint, version, lifecycle, and credentialConfigured: true. Rotate with { secret }. Create a policy with { key, displayName, credentialId, exactOrigin, pathPrefix, timeoutMs, maximumResponseBytes }. Create a toolset with { key, displayName, serverVersionId, tools: [{ remoteToolName, approval }] }. Discovery is capped at 256 tools; a toolset may select 1–64 tools. Agent publication adds mcpBindings, each containing exact toolsetVersionId, credentialPolicyVersionId, and a unique lowercase namespace. Publication requires mcp:bind. See Remote MCP servers for runtime and egress behavior.

Skills

Skills are PostgreSQL-authoritative, immutable procedural packages. Reads use skill:read, package writes use skill:write, agent publication additionally uses skill:bind, and lifecycle changes use skill:revoke. Create and version bodies contain name, description, instructions, optional license, compatibility, string-valued metadata, informational allowedTools, and files. Creating a Skill also accepts key and displayName. Each file is { path, contentType, dataBase64 }. See Versioned Skills for validation, package hashing, progressive disclosure, lifecycle, and script-execution boundaries. PATCH /skills/{skillId} requires skill:revoke, an Idempotency-Key, and the body { "enabled": false } or { "enabled": true }. It returns { "id", "key", "enabled", "disabledAt" }. Disabling is reversible and never touches bindings: a disabled Skill cannot be attached to a new agent version (400) and stays in the list with a non-null disabledAt, while published agent versions that pin it keep running with it. Enabling makes it attachable again. Repeating the current state is a no-op that still returns 200. The audit log and product events record skill.disabled and skill.enabled only when the state changed. DELETE /skills/{skillId} requires skill:revoke and an Idempotency-Key. It returns { "id", "key", "deleted": true } and archives the Skill rather than removing rows: versions, package files, agent-version bindings, and session bindings are immutable and stay stored, so published agent versions and their sessions keep running with it. After removal the Skill returns 404 from reads and version publication, disappears from the list, cannot be attached to a new agent version, and its open drafts are discarded. Its key becomes free for a new Skill. Removal cannot be undone. To also stop existing sessions from using one of its versions, revoke that version. The audit log and product events record skill.deleted. Removing an already removed Skill returns 404. Create a blank draft with {}. To edit an existing package, pass exact skillId and sourceSkillVersionId values; OAO copies that version’s metadata and files into the mutable draft. Directory bodies are { "path": "references/customers" }. File bodies use the same { path, contentType, dataBase64 } representation as direct publication and currently require UTF-8 .md content with text/markdown. Entry deletion uses ?path=references/customers&recursive=true for a non-empty folder. Draft paths are package-relative. They never address the API host filesystem or a Daytona workspace. Published versions continue to use the existing direct create/version endpoints, so API clients do not need to migrate to drafts. Lifecycle accepts { "status": "deprecated" } or { "status": "revoked" }. A deprecated version can later be revoked; a revoked version cannot be restored. Package contents and bindings have no update or delete endpoint.

Agents and versions

Creating an agent accepts name, optional key and description, and requires initialConfig (or its config alias). Agent list and detail responses include the latest immutable sandbox policy so operators can determine delegate compatibility before publication. See Create an agent for the exact configuration shape. DELETE /agents/{agentId} requires agent:write and an Idempotency-Key. It returns { "id": "...", "deleted": true } and archives the agent rather than removing rows: sessions, runs, and delegate bindings keep their references to the immutable versions, and existing session history stays readable. After deletion the agent returns 404 from reads, disappears from the list, cannot publish new versions, cannot start sessions (by agentId or by agentVersionId), and cannot be picked as a new delegate. Its key becomes free for a new agent. Coordinators that already pin one of its versions keep working. The audit log records agent.deleted. Deleting an already deleted agent returns 404. Set initialConfig.sandbox.enabled, provider, snapshotId, network, and capabilities to make the same sandbox selection available in API-driven creation as in the console. provider must be a key returned by the sandbox provider list route; there is no public fake-sandbox provider. Set config.skillVersionIds to a unique array of active, exact Skill version UUIDs from the same project whose Skills are enabled and not removed. Names must also be unique within one agent version. The server records immutable agent-version bindings; the array is not a reference to each Skill’s moving latest version. Set config.harnessOperations to up to 32 immutable entries with { key, description, instructions, resultSchema, timeoutMs }. Keys are 1–64 characters matching ^[a-z][a-z0-9_-]*$ and must be unique across the complete mounted tool namespace. Descriptions are 1–2,000 characters, instructions are 1–100,000 characters, timeouts are 1,000–300,000 milliseconds, and resultSchema is a required supported top-level object schema. The invocation schema’s serialized JSON is limited to 65,536 bytes. The invocation input is fixed by OAO as { task: string }, where task is 1–100,000 characters; clients do not publish an input schema. Operations inherit the parent Agent version’s model, rendered instructions, tools, complete Skill catalog, and live session sandbox. They do not accept a model, capability policy, sandbox, or skillVersionId. See Harness Operations for lifetime, shared-document, structured-output, and sequential/batch behavior. Set config.delegates to a unique roster of { key, description, agentVersionId, maxParallel }. Each UUID names an exact version of another agent in the same project. The maximum is 32 bindings and maxParallel is 1–8. The reserved platform tools delegate_agent and message_agent are supplied by OAO and must not appear in config.tools. Coordinator and child versions must have the same sandbox enabled state, provider, snapshot, and network policy. An incompatible publication returns a bad_request that identifies the child and directs the caller to publish a compatible child version first. config.modelPreset must be an available deployment preset key or a preset this project approved against a provider connection; the check runs inside the same tenant transaction as the write. An unapproved or unavailable key returns 400. Linking a different model always means publishing a new version, and existing sessions stay on the version they were created with.

Sessions and runs

Create a session and its first run atomically:
Use agentVersionId instead of agentId to pin an exact version yourself. If you send agentId, OAO resolves its latest published version during the same transaction. The response contains the session, latestRunId, and first queued run. OAO copies the selected agent version’s Skill bindings into immutable session bindings in that transaction. Session create and follow-up bodies do not accept a Skill list. Each session-list item created by delegation includes parentSessionId and delegateKey. Both fields are null for root sessions. This lets clients group a persistent child session beneath its coordinator without fetching every session detail record. Session list and detail responses report cumulative model usage as inputTokens, outputTokens, cacheReadTokens, and cacheWriteTokens. cacheReadTokens counts input tokens reused from a provider prompt cache; cacheWriteTokens counts input tokens newly written to that cache. Providers that do not report either value contribute zero. The detailed response repeats these fields per model call under debug.modelInvocations. A later turn accepts either { "message": "..." } or the SDK-compatible { "redactedInput": "..." }, plus the same optional files array used during session creation. Message text is limited to 100,000 characters and raw file bytes to 10 MiB per file and 20 MiB per turn. A text field may be omitted only when files is non-empty. File submissions require a sandbox-enabled agent with read tooling, plus a default project storage provider; binary formats require shell. The API uploads the original bytes to that S3-compatible provider and stores only the safe run manifest and object binding in PostgreSQL. See Send files to an agent for accepted media types, base64 encoding, and per-turn limits. Only one run can be active in a session thread; submitting while its latest run is unsettled returns a conflict. Session transcript messages and run-message pages include safe file metadata: ID, filename, content type, byte size, SHA-256, and creation time. Stored bytes and base64 are never stored in PostgreSQL or returned from those endpoints. The runtime downloads and verifies the bound object, then copies the original bytes to .oao/attachments/{runId}/{filename} under the sandbox’s writable working directory before the first model turn; it does not extract file content into the prompt.

Retrieve persisted workspace files

The session Files API exposes every regular file recorded in the latest workspace-backup manifest. Both routes require session:read and the normal authenticated project boundary. An API key can address a project only inside its organization, and a signed-in user must resolve to that project; the session and workspace-owner lookup remains tenant-filtered in PostgreSQL. GET /projects/{projectId}/sessions/{sessionId}/files returns:
A session without a completed backup returns an empty data array and null backup fields. To download a file, append its exact relative path, encoding each path segment independently:
The response is application/octet-stream with Content-Disposition: attachment, Content-Length, Cache-Control: no-store, and X-Content-Type-Options: nosniff. The API resolves delegated sessions through their shared workspace owner, downloads the archive and manifest from the workspace’s bound persistent storage provider, verifies the recorded archive length and SHA-256, validates the complete tar stream, and streams only the requested regular file without writing it to the API host filesystem. It does not read from Daytona. Missing sessions or paths return 404; missing, changed, or invalid persistent backup data returns 409 and no bytes. The SDK exposes the same contract through listSessionFiles(projectId, sessionId) and downloadSessionFile(projectId, sessionId, path). A model can therefore mention a relative path such as output/result.csv; the caller combines that path with the known session ID without giving the model a storage credential or provider URL. The session detail response includes debug.sandboxCommands. Each item returns the recorded safeCommand and safeResult, including model-facing arguments, file/edit text, shell input, browser values, and tool output. The transcript renders each call as a collapsed activity row; expanding it shows arguments and responses directly as formatted, highlighted JSON, without a second payload inspector. Caller tool calls from debug.toolCalls expand the same way and show their recorded safeArguments and immutable safeResult once submitted; successful envelopes display the response value, while failed envelopes display the safe error. Tool events that carry only other safe metadata (for example Skill activations) expand to that metadata instead. Credential-bearing fields and obvious authorization values remain masked. When a connector returns a JSON object or array encoded as a string, the console decodes that string for display and unwraps a sole { "result": "{...}" } wrapper. This presentation step does not mutate the immutable stored result. The same response includes top-level skills metadata with skillId, exact skillVersionId, version number, name, description, content hash, and current lifecycle status. It never includes Skill instructions or resource bytes. It also includes top-level tools: the tool list of the pinned agent version as name, description, owner, and approval per tool, in authored order and without the input or output schemas. The console session sidebar lists these next to the bound Skills and marks how many times each tool was called during the session. debug.productEvents and the project event stream include model progress before the response completes: Each agent model attempt has a five-minute deadline. Timeouts and transient provider failures are retried up to three times after the initial attempt, subject to the run deadline and model-turn budget. These progress events do not indicate completion; use invocation outcomes and the run state for that. The Console labels them Model call started and Model retry scheduled. See Model timeouts and retries for cancellation, compaction, and usage limitations. If an early start observation could not be recorded, its outcome restores the start event with the model’s start time. Start events are deduplicated per turn, including replay; a restored event can therefore arrive after the call finishes. debug.modelInvocations includes each invocation’s actual start/completion window, OAO’s normalized finishReason, and any provider thinking text returned with the response. A failed invocation also includes the provider’s exact bounded providerFinishReason when available and a safe errorExplanation describing why that finish condition is an error. The Console event inspector labels these as Provider finish reason and Why this is an error. The transcript renders each invocation as a collapsed Reasoning row. Expanding it shows the full duration, token counts, and thinking text without duplicating it in a payload tab. Provider reasoning signatures, raw provider error messages, and partial response content are not returned. Invocation records created before these diagnostics were projected can omit providerFinishReason and errorExplanation. Older invocations whose runtime projection recorded a zero-length window use the preceding message or tool completion to reconstruct their visible duration. Every transcript message and activity row, including tool calls, displays its recorded event time in the browser’s local timezone with millisecond precision. Durations remain separate. Hover over a timestamp to see the full local date and the exact UTC timestamp for comparison with server logs. The Debug timeline and Harness Operation internal steps display the same timestamps.

Delegations

The session detail response includes a top-level delegations array for coordinator and child sessions. It contains the delegate key, exact child version, child thread/session, shared workspace ID, relation state, and latest child run/state. The message body is { "message": "..." }. It creates a new run on the same persistent child session only after that child’s latest run settles and returns 202 with delegationId, childSessionId, childRunId, and status: "queued". Both mutations require a fresh Idempotency-Key. Cancelling closes the relation to future follow-ups and requests cancellation of its latest unsettled child run. Raw prompts are not returned in delegation events, audit detail, or tool-call safe arguments. See Multi-agent orchestration.

Caller tools and approvals

New approval requests expire 24 hours after creation. The returned expiresAt remains authoritative; existing and replayed gates retain their original expiry. Approval waiting pauses the configured run execution deadline, including across worker recovery, for the gated run and its requesting delegation ancestors. Concurrent approval waits count once. Acceptance resumes the remaining budget even when a caller has not yet submitted its tool result. See Approval waiting window for the independent submission ceiling. The claim response includes a fence encoded as a positive integer string. The same fence must accompany renew, release, and result operations. Successful tool results use { "version": 1, "status": "success", "value": { ... } }. OAO validates value against the immutable agent version’s output schema before persistence. If validation fails, the API stores a normalized invalid_tool_result failure, returns 202 with normalizedFailure.code and normalizedFailure.path, and resumes the agent. The raw invalid value is neither persisted nor sent to the model. Retryable failures permit two automatic agent retries after the initial attempt; further calls receive tool_retry_exhausted. Approval and cancellation failures are never retried. Tool input and output schema property names are part of the public caller-tool contract because their values can appear in safe arguments, results, events, and session diagnostics. Names classified as sensitive, such as token, apiKey, authorization, password, or secret, are therefore rejected when the agent is published. Use a domain-specific public name such as challengeCode or reference; keep credentials in provider or MCP credential storage. The API returns 400 bad_request with the exact rejected path in error.details.path.

Event webhooks

Event webhooks push product events to a customer HTTPS endpoint in ordered, signed batches. They are project-scoped and every route requires project:admin. See Event webhooks for the delivery format, signature verification, and a Convex receiver. Create a webhook with:
signingSecret is a Standard Webhooks secret: whsec_ followed by base64 encoding 24 to 64 random bytes. eventKinds accepts exact kinds and family wildcards such as run.*; null delivers every event. deliverFrom is now (default) or beginning. The endpoint must use HTTPS and a public host. The list response also reports credentialEncryptionConfigured and privateNetworkEndpointsAllowed. The second is true only on development servers that accept http:// and private-network endpoints. Responses expose credentialConfigured, a 12-character fingerprint, the credential version, and delivery state: status, deliveredPosition, cursor, pendingEvents, consecutiveFailures, nextAttemptAt, the last attempt, success, and failure times, lastResponseStatus, and lastErrorCode. PATCH accepts any subset of displayName, endpointUrl, eventKinds, includeMessageContent, and enabled. Rotate with { "signingSecret": "...", "previousCredentialTtlSeconds": 86400 }; the previous secret keeps signing for that many seconds, from 0 to 604800. DELETE returns { "id": "...", "deleted": true }. All writes require an Idempotency-Key, and a project can have at most 20 webhooks.

Pagination

List endpoints accept limit and an opaque cursor. Responses use:
Never parse or synthesize a cursor. Pass nextCursor back unchanged.

Errors

Non-success responses use a redacted envelope and include the request ID:
Log the request ID and safe error code. Do not log credentials or resend a failed write under a new idempotency key until you know whether it was accepted.

Agent model-turn limits

Agent creation and version publication accept config.limits.maxTurns as a required integer from 1 through 256, for example "limits": { "maxTurns": 128, "timeoutMs": 300000 }. Console and CLI creation default to 32; existing configurations with 32 remain valid. The bound of 256 limits runaway work while allowing longer imports. Invalid values return 400; timeout validation and enforcement remain independent. The limit is stored in the immutable agent version and runtime snapshot. Existing sessions do not adopt newly published values. Model turns, including scratch calls, reserve a durable per-run budget before invoking the provider. Parallel calls share the budget; compaction and worker recovery cannot reset it. Replaying the same Flue turn ID reuses its reservation. Provider-internal retries do not reserve another turn. Delegated runs have their own version and budget. When exhausted, model.invocation_failed public payloads and model invocation safeResponse include errorCode: "model_turn_limit_exceeded" and a safe errorExplanation containing the configured maximum and next steps. No raw provider error or credentials are exposed. Historical failures are not backfilled.