> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Build and operate an agent squad

> Design a multi-agent squad with a classifier router, give each member clean skill boundaries, wire routing through queues and handoff targets, and verify utilization from analytics through live monitoring.

A **squad** is a multi-agent collaborative unit: one **classifier** agent routes each inbound conversation turn to the right **specialist** member, so you split capability across several focused AI providers (for example a voice skill plus a chat skill) instead of growing one agent without bound. The operator UI lives at **`/agents/squads`**; the concepts and reference pages at [agents/squads](/agents/squads), [concepts/agent-run-lifecycle](/concepts/agent-run-lifecycle), and [Language quality](/guides/language-quality) describe the shape; this guide walks the operator loop end to end.

Every control here is **tenant-owned**: the squad definition, member list, and routing loop decisions live in your organization's settings on reading and writing. Squads run on Orbit's agent runtime (agent conversations, handoffs, and the run lifecycle) — none of these controls touch outbound voice or SMS termination; a squad is inbound routing config, not a carrier- or provider-side change.

**Base path:** `/agents/squads`

**Authentication:** Clerk session or API key. Writes require an owner, admin, or developer role. Reads require a signed-in session or API key. The guide keeps voice and chat provider selection to the models and agents you already run — see [Agent model selection](/agents/model-selection) and [Cost controls](/agents/cost-controls).

***

## 1. What a squad is in Orbit

A squad is one row plus a member list:

| Field                    | Meaning                                                                                                                        |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------ |
| `name`                   | Display name in the operator UI (`/agents/squads`).                                                                            |
| `classifier_agent_id`    | The agent that decides which member takes the turn.                                                                            |
| `members[]`              | 2–6 specialist agents; each carries `agent_id`, 1–10 `intent_labels`, and a `description` the classifier uses to disambiguate. |
| `fallback_agent_id`      | Safe target when no member matches. Must not equal the classifier.                                                             |
| `active`                 | Master switch; an inactive squad leaves routing to the classifier alone.                                                       |
| `max_cost_per_day_cents` | Optional hard daily cost ceiling for the whole squad.                                                                          |

Three shapes must stay loop-free or the API rejects the create/update with a `422`:

1. The **classifier must not appear in `members[]`** — routing the classifier's decision back to itself recurses and bills LLM calls until the API ceiling.
2. The **fallback must not equal the classifier** — an unmatched intent bounces back and re-classifies the same message forever.
3. **Intent labels must be unique across members** — duplicates make routing ambiguous and first-match-wins varies by row order.

Read [concepts/agent-run-lifecycle](/concepts/agent-run-lifecycle) for how an agent run is started, halted, escalated, and finished — a squad plugs into that exact run envelope once the classifier has chosen a member.

## 2. Create a squad via POST /agents/squads

Prerequisites:

* the classifier agent already exists ([Creating agents](/agents/creating-agents));
* each member agent exists and carries its own prompt and providers (see below for a voice-plus-chat split);
* you hold an owner, admin, or developer role.

Create a squad with two members — a voice skill plus a chat skill — each answering a distinct intent cluster:

```bash theme={null}
curl -X POST https://orbit.devotel.io/api/v1/agents/squads \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Inbound skills squad",
    "description": "Voice triage + chat resolution for first-touch inbound",
    "classifier_agent_id": "agent_clf_triage",
    "members": [
      {
        "agent_id": "agent_voice_yours",
        "intent_labels": ["wants_voice", "callback_request", "urgent"],
        "description": "Handles any turn that needs spoken interaction or a live voice follow-up"
      },
      {
        "agent_id": "agent_chat_yours",
        "intent_labels": ["write_only", "how_to", "status_check"],
        "description": "Resolves written questions and status checks without a voice handoff"
      }
    ],
    "fallback_agent_id": "agent_human_intake",
    "active": true,
    "max_cost_per_day_cents": 5000
  }'
```

A `201` returns the new squad including its generated `id` (prefix `squad_`). If a referenced agent id is wrong the API returns `422` and names the unknown ids — fix the member list and retry. Use [Agent versions](/agents/agent-versions) to pin the exact prompt revision each member runs before entering traffic.

The same object is uploaded by the **`/agents/squads/new`** wizard in the dashboard; the UI walks the same fields and ends with a test run (see next section) before deployment.

## 3. Assign agents and teams

Each member is a normal agent — its prompt, providers, and version pin come from that agent's own configuration; a squad does not override them. To assign a **human** team as the fallback or as a deliberate endpoint, route the handoff through [agents/squads](/agents/squads) (the human/handoff shapes that page documents), then point `fallback_agent_id` at the human squad-intake agent that wraps that team, or stop routing human-intent lanes to a member that hands off to the team directly.

Assignment is a write operation: `PUT /agents/squads/:id` replaces any field you send (name, classifier, members, fallback, `active`, cost ceiling). Leave `members` untouched to rotate just the classifier, or send a full new member list to rotate skills. The same `422` loop-shape guard runs on the merged state, so a partial update cannot sneak in a classifier-self loop.

To retire a squad: `DELETE /agents/squads/:id`. Audit entries (`agent_squad.created`, `agent_squad.updated`, `agent_squad.deleted`) flow to the [audit log](/guides/audit-log), so the team-review trail records who changed routing and when.

## 4. Route the squad in the omnichannel queue

Once a squad is live each inbound conversation lands in your normal omnichannel queue machinery and the squad's classifier decides the member on the next run. The two routing layers interact in one place:

* **Queue layer** — which queue or affinity bucket the conversation enters, with SLA previewed on-queue ([Omnichannel queue routing](/guides/omnichannel-queue-routing)).
* **Squad layer** — which specialist member takes the run once the work item dispatches.

Set attribute routing and affinity first; the squad changes nothing about where a conversation queues — it only changes which AI capability answers it once it dispatches. Watch the blended queue at `GET /api/v1/voice/supervisor/omnichannel-queue` (same list the supervisor UI renders) to see voice and digital items shared into one position-ordered backlog; that is where you verify the same queueing that the squad will inherit.

## 5. SLA and handoff targets

A squad runs inside the broader SLA and escalation model:

* [agents/handoff-targets](/agents/handoff-targets) — the named resolution for every handoff verb (queue, team, human, callback, close). Point the squad's member `handoff_targets` at the same named targets; escalation from a member then lands in the operator state you designed.
* [concepts/agent-run-lifecycle](/concepts/agent-run-lifecycle) — the run envelope: states, clock, halt conditions, and how an escalated run is counted. SLA timers and AI guardrails operate on the run, not on the squad name, so a squad that tips a run into human\_review instantly joins the SLA wallboard lane.

Set handoff targets on each member before first traffic — a member with no targets has no clean place to escalate, and the classifier will keep re-routing the same message.

## 6. Monitor squad utilization on the wallboard

The squad rollup at `GET /agents/squads/:id/analytics?days=30` is the utilization surface for one squad across a rolling window (default 30 days). It reports per-specialist utilization, cross-agent handoff success rate, drop-off, and containment lift versus the classifier-alone baseline. Read it from the dashboard's squad analytics panel or pull it programmatically into your own ops dashboard.

The same numbers feed the operator surfaces you already run: the [wallboard](/guides/wallboard) shows the live backlog and SLA posture for the queues the squad routes into, and [wallboard alarm rules](/guides/wallboard-alarm-rules) let you post a threshold on backlog or SLA breach before it unfolds.

Tune member composition from the analytics: if one member absorbs almost every matched label while another is starved, the skill split is wrong — rebalance intent labels and re-deploy.

## 7. Live monitoring

For the collective picture during a traffic spike, the supervisor surfaces show the queue and any escalated run the squad produced:

* [Supervisor live monitoring of voice](/guides/supervisor-live-monitoring-voice) — listen, whisper, and barge on a voice leg the squad's voice member escalated to a human.
* [Supervisor live monitoring of digital](/guides/supervisor-live-monitoring-digital) — observe and step into a digital conversation the chat member handed off.
* [Inbox SLA timers](/guides/inbox-sla-timers) and [Quality management](/guides/quality-management-program) — the run-level QA and SLA review loop for conversation outcomes.

The squad shape itself does not change any of these surfaces; it only changes which agent picked up the run upstream of the supervisor layer.

## Worked example — escalate LP1 → LP2 on a guardrail trip

Language proficiency level 1 (LP1) is a confined inbox + inbound-chat agent, LP2 is a fuller-context agent with a larger model and the authority to finish a billing write. The trigger for the step-up is a guardrail event: the [Language quality](/guides/language-quality) guardrail (or a sensitive-words event — see [Sensitive words guardrail](/agents/sensitive-words-guardrail)) fires for two consecutive runs, and you want the next turn to route to the heavier LP2 specialist instead of repeating a containment failure.

Build the two-tier squad once:

```bash theme={null}
curl -X POST https://orbit.devotel.io/api/v1/agents/squads \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "LP1 to LP2 escalation squad",
    "classifier_agent_id": "agent_clf_lp_router",
    "members": [
      {
        "agent_id": "agent_lp1_inbox",
        "intent_labels": ["general_question", "account_status", "billing_lookup"],
        "description": "Confined tier: answers read-only questions on inbox and chat"
      },
      {
        "agent_id": "agent_lp2_resolve",
        "intent_labels": ["guardrail_trip", "billing_write", "refund_request", "escalation"],
        "description": "Escalation tier: finishes billing writes and handles any turn that tripped a guardrail in LP1"
      }
    ],
    "fallback_agent_id": "agent_human_intake",
    "active": true,
    "max_cost_per_day_cents": 8000
  }'
```

Then operate the trigger:

1. The LP1 member runs with its normal handoff targets (step 4–5 above) pointing at the `escalation` lane. When the language-quality guardrail fires on two consecutive runs, set that lane to hand off to the squad's classifier — see [agents/handoff-targets](/agents/handoff-targets).
2. The classifier re-runs against the last LP1 message; a label such as `guardrail_trip` or `billing_write` matches the LP2 member, which serves the turn.
3. Watch it on the squad analytics rollup (step 6): handoff success rate for LP1→LP2 tells you the step-up actually fires, and containment lift versus the classifier alone tells you the escalation is doing work.
4. If LP2 cannot resolve, its own handoff target routes to `agent_human_intake`; the supervisor picks up from the live-monitoring surfaces in step 7.

When volume shifts — for example LP1 absorbs nearly every label — rebalance `intent_labels` between members and re-test before redeploying.

## Test before you deploy

Run the classifier once against a sample message to confirm label coverage without deploying:

```bash theme={null}
curl -X POST https://orbit.devotel.io/api/v1/agents/squads/<squad_id>/test \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"message": "I want to dispute the invoice from last month"}'
```

The response carries the matched member, the raw label emitted by the classifier, and the match source (`matched`, `fallback`, or `unavailable`) — the same decision the runtime will make. A `unavailable` source or a match against the wrong member means the squad's intent labels do not yet cover that kind of message; edit the member list and re-test.

## Reference

* [agents/squads](/agents/squads) — concept and field reference
* [concepts/agent-run-lifecycle](/concepts/agent-run-lifecycle) — states, halt, escalation
* [agents/handoff-targets](/agents/handoff-targets) — named resolution for every escalation
* [Language quality](/guides/language-quality) — multilingual model routing drove the worked example
* [Continuous production evals](/agents/continuous-production-evals) — keep member quality measurable
