Cloud and BYOC for Orca Agent Engine are in Private Preview — request an invite
Docs

Define agents

Define an agent resource in Orca Agent Engine - model, system prompt, MCP servers, tools, skills, and metadata.

An Agent is a versioned, reusable configuration for an agent in Orca Agent Engine. It declares the model the agent uses, the system prompt that frames its behavior, the MCP servers it can call, any custom tools it exposes, the skills it can load on demand, and arbitrary metadata for organizing agents across teams. Every change to an agent produces a new version; sessions pin to a version so behavior stays predictable as agents evolve.

Get your registry endpoint

Registry endpoint

Examples on this page target your registry endpoint - the deployment host root, with no path suffix. For CLI, set ORCA_REGISTRY_URL and exactly one of ORCA_ACCESS_TOKEN (Bearer) or ORCA_API_KEY (x-api-key). For TypeScript SDK, set ORCA_BASE_URL / ORCA_API_KEY (Bearer). To find the endpoint, see Connect to the registry.

Agent configuration fields

The Agent resource exposes the fields below. Field names use snake_case over the wire; the table shows the JSON form.

FieldTypeRequiredDescription
namestringYesHuman-readable name. Visible in ork output.
descriptionstringNoFree-form description of what the agent does.
metadataobjectNoArbitrary string -> string labels for filtering and organizing agents, up to 16 pairs. Two keys are reserved and interpreted by the platform: harness and mode select the harness the agent runs on.
modelobjectYesLLM model selection. Accepts a model ID string or an object, for example {"id": "claude-opus-5"}. The object form recognizes id, speed, effort, and provider. For managed SDK harnesses, provider participates in model validation and Pi SDK protocol selection; omitting it uses the harness default. Available effort levels depend on the selected harness and model. Default responses omit provider; orca-beta responses return { "provider": ..., "id": ... }. Other keys are accepted and silently dropped before storage. There is no temperature or max_tokens here. See Model access.
systemstringNoSystem prompt that frames the agent's role and behavior. The system prompt is distinct from user messages, which describe the work to be done.
toolsarrayNoThe tools available to the agent. Each entry sets type to agent_toolset, mcp_toolset, or custom. Permission policies govern the two toolset types; custom tools are authorized by the application that handles their calls.
mcp_serversarrayNoMCP servers available to a supporting harness. Each entry has name, type (always url), and url. Every entry must be referenced by an mcp_toolset in tools.
skillsarrayNoSkills the agent can load. Each entry has type, skill_id (returned by Skills), and an optional pinned version.
multiagentobjectNoMakes this agent a coordinator that can delegate to other agents. {"type": "coordinator", "agents": [...]} with 1 to 20 roster entries. See Multiagent.

The response from any agent endpoint includes server-managed fields: id, type, version, created_at, updated_at, and (for archived agents) archived_at.

Agent IDs returned by the registry are registry scoped. They stay stable as you update the agent and are resolved by the registry into provider-side identifiers when the agent runs.

You can also override model, system, tools, mcp_servers, and skills for each session without changing the agent.

Choose a harness

The harness is the process that runs the agent loop. Which one an agent uses is fixed at the agent level, through two reserved metadata keys:

KeyValuesDefault
harnessclaude_agent_sdk, claude_code, codex_sdk, pi_sdkclaude_agent_sdk
modeseparate, colocatedseparate

claude_agent_sdk runs in separate mode, and claude_code runs in colocated mode. codex_sdk and pi_sdk support both; omitting mode selects separate. For a self-hosted environment, set mode to colocated for either SDK harness. in_sandbox remains a deprecated alias for colocated; use colocated for new agents. Setting mode alone still selects the default claude_agent_sdk harness, so colocated is rejected. Unsupported pairs return 400.

colocated mode is not fully implemented in this release. Use separate, the default.

codex_sdk and pi_sdk are distinct from the older codex and pi CLI harness identifiers. Select the _sdk identifier for the managed SDK integration described here.

# Default harness - the metadata keys can be omitted entirely
ork agent create --name "support-triage" --model claude-sonnet-4-6

# Run the loop inside the sandbox instead
ork agent create \
  --name "repo-worker" \
  --model claude-sonnet-4-6 \
  --metadata harness=claude_code

The following comparison applies to the two Claude harnesses. Codex SDK and Pi SDK have their own model and capability rules on their Codex SDK and Pi SDK pages.

claude_agent_sdkclaude_code
Built-in toolsall eightsix - no list, no delete
always_ask toolspause for approvaldropped from the session
MCP serversreachablenot wired
Token-level streamingyesno
Sandbox runtimeanyrequires a runtime that exposes a port

A session cannot change or override the harness. agent_with_overrides accepts only model, system, tools, mcp_servers, and skills, and neither session nor environment metadata is read for harness selection. To run the same configuration on a different harness, create a second agent.

See the per-harness pages for configuration: Claude Agent SDK, Claude Code, Codex SDK, and Pi SDK.

Create an agent

Create an agent by POSTing a CreateAgentRequest to /v1/agents. The example below registers an agent that uses a system prompt, declares one MCP server, and pins the model speed.

The mcp_toolset entry is not optional here: a declared server that no tool references is rejected with 400. See MCP servers for why the two are checked against each other.

The later examples on this page reuse the new agent's ID and version, so capture them here.

agent=$(ork agent create \
  --name "support-triage" \
  --description "Triages incoming support tickets" \
  --system "You categorize support tickets and route them to the right team." \
  --model-json '{"id":"claude-sonnet-4-6","speed":"standard"}' \
  --mcp-server name=tickets,type=url,url=https://mcp.example.com/tickets \
  --tool-json '{"type":"mcp_toolset","mcp_server_name":"tickets"}' \
  --metadata team=support \
  -o json)

AGENT_ID=$(jq -r '.id' <<< "$agent")
AGENT_VERSION=$(jq -r '.version' <<< "$agent")

A successful create returns 200 OK with the new agent:

{
  "id": "agt_01H8...",
  "type": "agent",
  "name": "support-triage",
  "description": "Triages incoming support tickets",
  "version": 1,
  "model": {
    "id": "claude-sonnet-4-6",
    "speed": "standard",
    "effort": { "type": "high" }
  },
  "system": "You categorize support tickets and route them to the right team.",
  "tools": [
    {
      "type": "mcp_toolset",
      "mcp_server_name": "tickets",
      "default_config": {
        "enabled": true,
        "permission_policy": { "type": "always_allow" }
      },
      "configs": []
    }
  ],
  "mcp_servers": [
    { "name": "tickets", "type": "url", "url": "https://mcp.example.com/tickets" }
  ],
  "skills": [],
  "metadata": { "team": "support" },
  "multiagent": null,
  "archived_at": null,
  "created_at": "2026-05-11T17:24:08Z",
  "updated_at": "2026-05-11T17:24:08Z"
}

$AGENT_ID is what you pass when creating sessions.

The echoed permission_policy is not the effective one. A toolset sent without a default_config is stored and returned as always_allow, but an mcp_toolset with no explicit policy runs as always_ask - the harness fills that default separately, reading the stored tools rather than this response.

That gap bites hardest on an in-sandbox harness, where always_ask tools are dropped from the session altogether: the response says the tools are allowed, and the agent never sees them. Send the policy you want explicitly rather than reading it back. See Permission policies.

Update an agent

Update an existing agent by sending a POST to /v1/agents/{id}. Every update produces a new version of the agent.

Updating takes the current version as well as the ID. Supplying it makes the write a compare-and-swap: if something else changed the agent since you read it, the request returns 409 version mismatch instead of silently overwriting. This is why the create example captured AGENT_VERSION.

updated=$(ork agent update "$AGENT_ID" \
  --version "$AGENT_VERSION" \
  --system "Updated system prompt" \
  -o json)

AGENT_VERSION=$(jq -r '.version' <<< "$updated")

Each of these reassigns AGENT_VERSION from the response, so a second update in the same shell session still compare-and-swaps correctly.

version is optional everywhere - --version on the CLI, AgentUpdateParams.version in the SDK, and version in the request body. Supplying it makes the write a compare-and-swap; omitting it applies the update unconditionally, last write wins. Prefer supplying it.

Update semantics

The agents endpoint uses POST with partial-update semantics: fields omitted from the request body are left unchanged on the server. The method is POST rather than PATCH, but the merge behavior on the body is what PATCH would give you.

  • Omitted fields are preserved. Sending only system leaves mcp_servers, tools, and skills unchanged.
  • Scalar fields are replaced. A new system value overwrites the old one.
  • Array fields are replaced as a whole. To add one tool, send the full new tools array.
  • Metadata keys are merged. Set a key to null to remove it; omit a key to leave it unchanged.

The response is the new agent version. Existing sessions continue to use the version they pinned at session-creation time.

Agent lifecycle

StateDescription
ActiveThe agent is creatable into sessions. Updates produce new versions.
ArchivedThe agent is excluded from default list results, cannot be used to create new sessions, and cannot be referenced by a multiagent roster. Existing sessions continue to run.

List versions

Every update increments the version. To inspect prior versions:

cURL
curl -fsS "$ORCA_REGISTRY_URL/v1/agents/$AGENT_ID/versions" \
  -H "Authorization: Bearer $ORCA_ACCESS_TOKEN"

The response is a paginated cursor whose data entries are full agent records for each version.

Retrieve a specific version

ork agent get "$AGENT_ID" --version 2 -o json

Omit the version to get the latest.

Archive an agent

Archiving hides an agent from default list results while preserving its history. Sessions started before the archive continue to run.

Archive an agent by sending a POST to /v1/agents/{id}/archive. Nothing is deleted: the agent record stays queryable with include_archived=true and retains its full version history.

ork agent archive "$AGENT_ID"

The response is the agent record with archived_at populated.

Delete an agent

DELETE /v1/agents/{id} is a different operation from archive, and it destroys data. It removes every version row and the agent row in one transaction. The agent is gone: include_archived=true will not bring it back, the version history is not recoverable, and the response carries no agent fields.

Prefer archive. Reach for delete only when the records themselves must not persist.

cURL
curl -fsS -X DELETE "$ORCA_REGISTRY_URL/v1/agents/$AGENT_ID" \
  -H "Authorization: Bearer $ORCA_ACCESS_TOKEN"
{ "id": "agt_01H8...", "type": "agent_deleted" }

Neither ork nor the TypeScript SDK exposes agent deletion, so the registry endpoint is the only route to it.

List agents

ork agent list

Add include_archived=true to surface archived agents. The endpoint reads four other query parameters - limit, page, created_at[gte], and created_at[lte] - and ignores anything else, so filter on metadata labels client-side after listing.

Permissions

Treat every Workspace API key as full access to its Workspace's resources, and separate access with Workspaces. See Control registry access.

What's next

On this page