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 withrequireDisposition 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:
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 ordispositionId 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.
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 anytagSlugs or tagIds entry that does not resolve and names the offenders
so you can fix exactly those:
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_REQUIREDis 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
notefield alone. - Do not rebuild the slug locally. Stale
detailsin a picker cache, or a retired tag, both look like “the slug should work.” Re-read the catalog withincludeInactive=truebefore concluding the row exists.
6. Bundle for support
When the refusal keeps coming after the catalog was extended:- the
meta.request_idfrom the failing response envelope, - the queue id and agent id,
- the attempted code or tag slug (and
dispositionId/tagIdwhen posted by id), and - the catalog read-back showing the row active at the time of the refusal.
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_WRAPUPpoints at, and who moves each edge. - Voice queues guide — queue configuration, memberships, and wrap-up window sizing.