> ## 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.

# Wrap-up codes: disposition capture on every conversation

> Configure a per-queue catalog of wrap-up (disposition) reason codes and require agents to record one before they return to available — for voice queues and digital conversations alike.

# Wrap-up Codes and Disposition Enforcement

A wrap-up code (also called a disposition or reason code) is the outcome an agent records when a conversation ends — *Sale completed*, *Wrong number*, *Follow-up needed*. Orbit lets you define those codes per queue and, when you choose to, require that an agent records one before they can take the next conversation.

This page covers the wrap-up catalog for **inbound voice (ACD) queues** and the matching enforcement on **digital (inbox) queues**. For the outbound-dialer disposition matrix that routes contact lifecycle statuses, see [Dialer dispositions](/voice/dialer-dispositions).

**Base path:** `/api/v1/voice`

**Authentication:** Clerk session (`Authorization: Bearer <token>`) or API key (`X-API-Key`).

**Scope:** `voice` read for catalog reads, `voice` write for recording; catalog management endpoints are restricted to owners and admins.

***

## The catalog: codes you configure per queue

Each queue carries its own catalog of disposition rows. A row has:

* **`code`** — the lowercase slug agents (or the softphone) post back (for example `sale_completed`). It is immutable once created; retire and recreate to rename.
* **`label`** — the human-readable text the picker shows.
* **`parentId`** — optional nesting one level deep, so you can group codes under a heading (for example *Sale* → *New*, *Upsell*, *Renewal*).
* **`sortOrder`** — picker display order.
* **`requiredOnTerminate`** — when `true`, the picker marks the row as required; when `false`, the row is optional context the agent may skip. This flag only matters when the queue-level gate is on.
* **`noteTemplate`** — optional starter text the softphone pre-fills into the wrap-up note field when the agent picks this code, so repetitive annotations (case reference, refund amount) start from your wording instead of a blank box.
* **`isActive`** — retiring a code is a soft-delete. Past conversations keep resolving the code's label for reporting, and you can reactivate the row later.

Manage the catalog with:

| Operation                                                                   | Endpoint                                                  |
| --------------------------------------------------------------------------- | --------------------------------------------------------- |
| List (active rows; add `?includeInactive=true` for the admin editor)        | `GET /api/v1/voice/queues/{queueId}/dispositions`         |
| Create                                                                      | `POST /api/v1/voice/queues/{queueId}/dispositions`        |
| Update label, nesting, order, required flag, note template, or active state | `PATCH /api/v1/voice/queues/{queueId}/dispositions/{id}`  |
| Retire (soft-delete; active child codes are promoted to the root)           | `DELETE /api/v1/voice/queues/{queueId}/dispositions/{id}` |

A slug must be unique across the queue's active rows — creating a duplicate returns `409 DISPOSITION_ALREADY_EXISTS` (replaces the code via retire + recreate). Only owners and admins can change the catalog; agents read it.

## Recording the disposition on a conversation

At end of call, the agent (or the softphone picker) posts:

```http theme={null}
POST /api/v1/voice/queues/{queueId}/calls/{callId}/disposition
Content-Type: application/json

{
  "dispositionCode": "sale_completed",
  "note": "Approved the refund, case R-1042"
}
```

Send either `dispositionCode` (the slug) or `dispositionId` (the row id); the code must resolve to an active row on that queue or you get `404 DISPOSITION_CODE_NOT_FOUND`. The record is idempotent on `(queueId, callId)` — a retry replaces the previous code and note instead of piling up duplicate rows.

Two guards keep attribution honest:

* **The assigned agent records the outcome.** A different agent cannot post a disposition for someone else's call (`404 NOT_OWNING_AGENT`); owners, admins, and platform staff keep a blanket capability for after-the-fact corrections.
* **The code must exist on the live catalog.** Free-text outcomes are rejected, so reporting never accumulates near-duplicate strings.

### AI-suggested code

The softphone can ask for a suggestion before the agent picks:

```http theme={null}
GET /api/v1/voice/queues/{queueId}/calls/{callId}/disposition/suggestion
```

A reader classifies the call transcript against the queue's active catalog and returns a code, a confidence, and a one-line rationale. It is an assist, never a gate — nothing is recorded until the agent confirms, and when there is no transcript or the catalog is empty the endpoint returns a `null` suggestion and the picker falls back to manual selection.

***

## The enforcement gate: disposition required on every conversation

Per queue, set `requireDisposition` when you create or update the queue. While it is on:

> An agent cannot flip from `busy` back to `available` while their most recent finished call on that queue has no recorded disposition.

The status change fails with `422` and `code: DISPOSITION_REQUIRED`, and the response tells you exactly which queue and call are waiting:

```json theme={null}
{
  "error": {
    "code": "DISPOSITION_REQUIRED",
    "message": "A disposition code is required before flipping back to 'available' …",
    "details": {
      "queueId": "support",
      "callId": "call_01HZK…",
      "dispositionEndpoint": "/voice/queues/support/calls/call_01HZK…/disposition"
    }
  }
}
```

The correct client flow is: render the picker from the catalog endpoint, let the agent pick, post the disposition, then retry the status change. Only the `busy → available` edge is gated — the agent can still step away (away, offline, paused) without recording, and they are prompted again the next time they try to go available.

**Default is off.** Existing queues keep the legacy behaviour (agents return to available freely) until you turn the switch on — enforcement is strictly opt-in per queue.

### The same gate on digital queues

Digital queues (email, web chat, WhatsApp, SMS, social channels) carry the same `requireDisposition` switch and the same owning-agent rule. A conversation routed through a digital queue tracks its wrap-up through an explicit lifecycle — assigned, awaiting wrap-up, and closed with disposition — so a supervisor can audit "closed but no outcome recorded yet" as a first-class gap instead of an invisible one. The digital disposition lifecycle and its state rules are described on the [inbox reference](/api-reference/endpoints/inbox).

***

## Reporting: what the gate buys you

Once dispositions are enforced, wrap-up reporting stops having a silent "no outcome" bucket:

* **Wrap-up mix analytics** — `GET /api/v1/voice/dispositions/analytics` returns the fleet-wide code distribution, the per-agent distribution, and outlier flags (agents whose share of a code deviates from the fleet baseline far enough to be worth a coaching conversation). Restricted to owners, admins, and supervisors.
* Retired codes stay resolvable forever — historical conversations keep their labels even after you restructure the catalog.
* Every record, override, and catalog change lands in the audit log with the actor, so QA can answer "who set this outcome and when."

Turn on `requireDisposition` for a queue, seed it with the five-to-ten codes you actually use, and AHT, abandon, and first-contact-resolution reporting can all be segmented by outcome instead of one undifferentiated total.
