/api/v1/voice
Authentication: Clerk session (Authorization: Bearer <token>) or API key (X-API-Key).
Scope: voice:read for the list; voice:write plus an owner, admin, or developer role for claim and read-state changes.
1. Where the shared inbox lives
Open Voice → Voicemails → Team in the dashboard (/voice/voicemails/team). The page sits behind a role gate: owner, admin, and developer roles see it; other members land on their personal voicemail inbox instead. Grant one of those roles — the built-in role ladder is documented in Roles, teams, and permissions — before pointing anyone at the team view. A 403 here is the role, not the page.
The personal inbox (/voice/voicemails) links to the team inbox and back, so operators can move between the two without re-navigating the sidebar.
2. Reading the list
Every row is one captured message with the caller number, the captured number it landed on, and the absolute timestamp pinned to your timezone. Two independent flags sit on top:- Owned (claimed) messages carry a
Claimed by <name>badge so the queue shows who is handling the caller. The claimant name resolves from your team’s roster. - Your read state come from the
user_read_atcolumn — unread rows are highlighted for you, and a pithy “Unread by you” counter tracks only your progress. The legacyis_readflag is projected for older UI surfaces, but the team view keys off the per-user column.
cURL
claimed_by_user_id / claimed_at (who owns it) and user_read_at (your state). Pass folder=spam or folder=inbox to scope the list; pass the previous page’s meta.pagination.cursor as cursor for the next page.
3. Claim and release: triage ownership
Claiming pins a message to one person without touching anyone else’s view. The API body is anaction enum, not a boolean:
cURL
- Claim an unclaimed message — assigns it to you, stamps
claimed_at, and shows your name to the rest of the team. - Claim a message you already hold — a no-op; re-claiming is idempotent, so double-clicks and retry loops are safe.
- Claim a message someone else holds —
409 CONFLICTwith the current claimant’s id in the error details. Refresh and you see their badge. - Release (
{ "action": "release" }) — drops ownership back to Open. Releasing someone else’s claim is owner- and admin-only; a developer role hits the same 409 as a claim attempt.
4. Per-user read markers
Read state lives in a per-(message, user) row, so each teammate keeps an independent unread filter:cURL
is_read: false to reopen a message for yourself — useful when a claim bounced back to Open and you want it back in your unread counter. Marking read never writes the legacy global is_read flag and never changes another member’s user_read_at; that’s what makes a five-person box workable instead of a first-to-open-wins race.
5. Workflow patterns
Claim semantics support a few operating modes; pick one and write it down. Solo operator — one person owns the box. Skip claims and just work the Open filter; claim only when someone covers for you, so the coverage hand-off shows who accepted what. Rotating on-call — each shift empties the Open chip before handoff. The incoming shift’s first move is filtering to Open: anything still there was claimed and not resolved by the outgoing shift. Hand off cleanly by releasing instead of leaving stale claims. Department box coverage — one shared mailbox, several members (up to 50 on the roster). The “Unread by you” tile is each member’s personal backlog; “Open” is the team’s. A healthy box keeps Open at zero more than it keeps any individual’s unread at zero. Claim before you call back — the etiquette that prevents double-calls: claim first, then ring the customer, then mark read. If two people phone the same customer, the message usually sat claimed-by-nobody for a while — treat a 409 as a save, not an error.6. Wire it into the contact center
Triage is half the job; the return call is the other half. Play the message, claim it, then enqueue a callback on the right queue so the return call enters the same SLA tracking and agent pool as a live caller — the pattern in the Queue SLA callback runbook. Supervisors on owner or admin roles can force-release abandoned claims, so a stalled shift doesn’t park messages; the audit ledger records who claimed, released, and toggled read on every row for regulated-content evidence. On go-live, the CCaaS go-live checklist covers the voice cutover end to end — fold the box roster and this triage loop into that run rather than treating voicemail as an afterthought.7. Troubleshoot
- The shared list is empty but callers say they left messages — the route points at a
vmbox_*id whose roster you’re not on, or the box itself is archived. Check the box and its roster withGET /api/v1/voice/voicemail-boxesand have an admin add you withPUT /api/v1/voice/voicemail-boxes/{id}. Non-members never see box messages. - 403 on the dashboard Team tab — your role is below developer. An owner or admin bumps it in Team settings.
- 409 on claim, repeatedly — someone else holds the message and the badge tells you who. Releasing another member’s claim is owner/admin-only, by design.
- A claim was released accidentally — re-claim it; the claim history lives in the audit ledger, and the claimant’s per-user read state survives the release, so no read progress is lost.
- One member’s view shows everything read, another’s shows unread — working as intended. Read state is per user; the discrepancy means one person worked the queue and the other hasn’t. Cross-account anomalies (a user marked read in a different org’s data) are impossible by construction — every query is scoped to the caller’s tenant.
- Transcript looks masked for one colleague, raw for another — transcript visibility follows the caller’s voicemail-read permission, not the claim. Members lacking read permission see a fenced preview; grant the permission rather than forwarding the text by hand.
See also
- Set up voicemail boxes and greetings — create the shared mailbox, the roster, and the greeting this inbox aggregates
- Queue SLA forecast and callbacks — the callback path a returned voicemail should join
- Voice CCaaS go-live checklist — fold the triage loop into the go-live run
- Voice API reference — parameter and response detail for every endpoint used here