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:
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
Sandbox providers
Sandbox provider connections are organization-shared — every project uses the same pool, addressed through any project path — and requireproject: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:
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 requireproject: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 requireIdempotency-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 useskill: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: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 requiresession: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:
data array and null
backup fields. To download a file, append its exact relative path, encoding
each path segment independently:
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-leveldelegations 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 returnedexpiresAt
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 requiresproject: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 acceptlimit and an opaque cursor. Responses use:
nextCursor back unchanged.
Errors
Non-success responses use a redacted envelope and include the request ID:Agent model-turn limits
Agent creation and version publication acceptconfig.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.
