Skip to main content

Troubleshooting: disposition and wrap-up errors

Voice queues and dashboards ship two disposition catalogs — per-queue wrap-up codes and tenant-wide disposition tags — plus a presence gate that can hold an agent out of the dispatch pool until a code is recorded. Four recovery-relevant errors come out of that surface: DISPOSITION_REQUIRED (the queue’s requireDisposition gate keeps the busy → available flip blocked), DISPOSITION_CODE_NOT_FOUND (the submitted code is not in the queue’s catalog), DISPOSITION_TAG_NOT_FOUND (the submitted tag is not in the tenant’s catalog), and NOT_IN_WRAPUP (a wrap-up call arrived while the agent is not in the wrapup presence state). This page tells each one apart and gets the next valid call through. For building the catalogs themselves, see wrap-up codes and call disposition tags.

Recognize the signal

Every one of these is a deterministic refusal — the envelope tells you what was wrong; a blind retry is never the fix. meta.request_id stays the handle support reads when the refusal recurs.

1. DISPOSITION_REQUIRED — the dispatch-blocking envelope

A queue with requireDisposition on refuses the membership’s busy → available flip while its most recent finished call carries no recorded code. The agent sits in wrapup, off the dispatch pool, and the status POST comes back 422 with the exact queue and call it is waiting on:
The fix is a sequence, not a retry: read the queue’s catalog, post a code against it, then flip the status again.
Only that one edge is gated — the agent can still step away (away, offline, paused) without recording, and the gate re-prompts the next time they try to go available. The details.dispositionEndpoint on the envelope is the exact path to post to; follow it rather than reconstructing the URL. The per-queue switch itself is documented on wrap-up codes.

2. The catalog 4xx pair — code vs tag, queue vs tenant

Both rejects mean the same shape of failure at two different scopes: what you posted is not an active catalog row.

DISPOSITION_CODE_NOT_FOUND — queue-scoped

Wrap-up codes live per queue: the slug or dispositionId you send must resolve to an active row in that queue’s catalog. A slug that reads fine on another queue 404s here — the resolver looks at the queue named in the path, not the whole tenant.
Read the queue catalog (GET .../queues/{queueId}/dispositions), pick the slugs it actually serves, and re-post. When every agent on the queue 404s on the same slug, extend the catalog — the POST .../queues/{queueId}/dispositions row-creation endpoint is owner/admin-only, and after it lands every picker resolves the new code at once.

DISPOSITION_TAG_NOT_FOUND — tenant-scoped

Disposition tags live tenant-wide: one curated catalog stamps labels on calls across queues and campaigns. The stamp endpoint rejects any tagSlugs or tagIds entry that does not resolve and names the offenders so you can fix exactly those:
Read GET /api/v1/voice/disposition-tags (add ?includeInactive=true if you suspect retirement), drop or correct the missing values, and re-stamp. Retired tags soft-delete — historical stamps keep resolving the label and the slug frees up for reuse, so a 404 on a slug that used to work usually means retirement, not a typo. Expose the right catalog writer: owners and admins curate tags in Voice → Disposition Tags or via POST /api/v1/voice/disposition-tags; agents stamp but do not curate.

Which catalog do I write where?

3. NOT_IN_WRAPUP — the presence mismatch

409 NOT_IN_WRAPUP fires when a wrap-up endpoint — submitting the disposition, extending the window, or ending wrap-up — arrives while the agent’s membership on that queue is not in the wrapup state. The written-by-thread write-through can lag or a supervisor can flip the state first; the envelope is the route telling you “there is no open wrap-up window to act on.” Check the membership’s current state before posting, then only call the wrap-up endpoints while the state is wrapup. The full presence matrix lives on the agent presence lifecycle page; the dispatch-relevant slice: Soften the client flow to: read the current membership state, and when it already reads available treat the earlier submit as landed (the record is idempotent on (queueId, callId)), instead of hard-failing the agent UX.

4. cURL per code

Collect all four envelopes the same way — the failing call plus a read-back of the catalog or state it refused against:

5. What not to retry

  • Do not loop a 404. Catalog membership is deterministic — the same slug misses a hundred times in a row. Extend the catalog (or correct the value), then send exactly one re-post.
  • Do not force the status flip. DISPOSITION_REQUIRED is the queue asking for a record, not a bug in the state machine — record the disposition first, then the flip succeeds on the very next attempt.
  • Do not invent free-text outcomes. The catalog reject is the reason free text never accumulates in reporting; write the outcome into the queue’s catalog, not the note field alone.
  • Do not rebuild the slug locally. Stale details in a picker cache, or a retired tag, both look like “the slug should work.” Re-read the catalog with includeInactive=true before concluding the row exists.

6. Bundle for support

When the refusal keeps coming after the catalog was extended:
  • the meta.request_id from the failing response envelope,
  • the queue id and agent id,
  • the attempted code or tag slug (and dispositionId / tagId when posted by id), and
  • the catalog read-back showing the row active at the time of the refusal.
Support reads the logged refusal straight off the request id and compares it against the catalog state instead of re-deriving it from queue config.

See also

  • Wrap-up codes — the per-queue catalog rows, requireDisposition, analytics, and the owning-agent rule.
  • Call disposition tags — the tenant-wide tag catalog, stamps, and audit posture.
  • Agent presence lifecycle — the five-state presence matrix NOT_IN_WRAPUP points at, and who moves each edge.
  • Voice queues guide — queue configuration, memberships, and wrap-up window sizing.