Orchestrator links¶
An Orchestrator link is an edge in the sub-Orchestrator graph: it grants one Orchestrator, the "from" side in the URL path, the ability to reach another Orchestrator, the "to" side in the request body, as one of its own tools. This is what Orchestrators: multi-model, multi-agent delegation calls a sub-Orchestrator. It's a distinct mechanism from Agent-to-Orchestrator delegation, which is how a native, realtime Agent, not another Orchestrator, reaches an Orchestrator.
Gated behind a deployment setting, admin session token required
See the two notes at the top of Orchestrators: every endpoint here only exists when your deployment has Orchestrators turned on, and every endpoint here authenticates with an admin session token.
The link object¶
| Field | Type | Notes |
|---|---|---|
id |
integer | Response only. |
from_orchestrator_id |
integer | Response only. The Orchestrator in the URL path, the one this link grants a new capability to. |
to_orchestrator_id |
integer | The target Orchestrator's id. Required on create. |
to_orchestrator_slug |
string | Response only. The target Orchestrator's slug, denormalized onto the link so a list view doesn't need a separate lookup per row. |
to_orchestrator_name |
string | Response only. The target Orchestrator's name, denormalized the same way. |
invocation_style |
string enum | One of agent_tool, sub_agent. Defaults to agent_tool on create. See Invocation styles below. |
enabled |
boolean | Defaults to true on create. |
Invocation styles¶
agent_tool: the parent Orchestrator calls the linked Orchestrator explicitly, as an ordinary tool call, and gets one answer back. Control returns to the parent immediately afterward, the same shape as an Agent's owndelegate_to_agentcall.sub_agent: control, not just an answer, can transfer to the linked Orchestrator for the rest of the turn. The model decides for itself when a hand-off is warranted, rather than going through an explicit call-and-return.
Listing an Orchestrator's links¶
Requires: Viewer and above.
Returns every outgoing link from this Orchestrator, that is, every
sub-Orchestrator it can reach, ordered by ascending id.
[
{
"id": 1,
"from_orchestrator_id": 1,
"to_orchestrator_id": 2,
"to_orchestrator_slug": "shipping-carrier-lookup",
"to_orchestrator_name": "Shipping Carrier Lookup",
"invocation_style": "agent_tool",
"enabled": true
}
]
A nonexistent orchestrator_id gets a 404.
Creating a link¶
Requires: Editor and above.
| Field | Type | Required | Default |
|---|---|---|---|
to_orchestrator_id |
integer | Yes | none |
invocation_style |
string enum (agent_tool, sub_agent) |
No | agent_tool |
enabled |
boolean | No | true |
const response = await fetch("https://api.your-domain.com/api/v1/orchestrators/1/links", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${adminToken}`,
},
body: JSON.stringify({ to_orchestrator_id: 2, invocation_style: "agent_tool" }),
});
const link = await response.json();
Returns 201 Created with the full link object shown in
Listing an Orchestrator's links above.
Error responses on create¶
| Status | When | detail |
|---|---|---|
404 |
orchestrator_id in the path doesn't exist. |
Orchestrator {orchestrator_id} not found |
404 |
to_orchestrator_id in the body doesn't exist. |
Orchestrator {to_orchestrator_id} not found |
409 |
A link from this Orchestrator to that same to_orchestrator_id already exists. |
A link to '{slug}' already exists |
422 |
Creating this link would introduce a cycle in the sub-Orchestrator graph. | Linking to '{slug}' would create a cycle in the sub-Orchestrator graph. |
Cycle rejection¶
A link is rejected with a 422 if adding it would let the graph, starting
from the target Orchestrator and following existing links, ever reach back
to the source Orchestrator. A few properties of this check are worth
knowing:
- A self-link always counts as a cycle. Setting
to_orchestrator_idto the same id as theorchestrator_idin the path is rejected the same way, with the same422and the same message shape (slugis simply that Orchestrator's own slug). - Disabled links still count. The check considers every existing link
regardless of its
enabledvalue, since a disabled link could always be re-enabled later. A cycle that's merely dormant today is still rejected now, not deferred until it would actually be traversed. - It runs again when an Orchestrator is actually invoked, not only at link-creation time, as defense in depth in case a cycle were ever introduced outside the normal write path. In ordinary use, going through this API is enough on its own: a cycle can't get created through these endpoints in the first place.
Example rejection response, linking Orchestrator 2 back to Orchestrator 1 after 1 already links to 2:
Updating a link¶
Requires: Editor and above.
Both fields are optional; only the fields present in the request body are
changed. to_orchestrator_id can't be changed after creation and isn't
an accepted field on this endpoint; delete the link and create a new one
to point somewhere else.
| Field | Type | Required |
|---|---|---|
invocation_style |
string enum (agent_tool, sub_agent) |
No |
enabled |
boolean | No |
Returns 200 OK with the full, updated link object. A nonexistent
link_id (or one that doesn't belong to orchestrator_id) gets a 404.
Deleting a link¶
Requires: Editor and above.
Returns 204 No Content. A nonexistent link_id gets a 404.