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

# The voicemail and MWI (Message-Waiting Indicator) model

> How voicemail works as a model end to end — capture sources and mailbox identity, the message-waiting new/old counts, who sees the badge, the recording-library ingestion path, and the boundaries of what MWI is not.

# The voicemail and MWI model

A voicemail is a captured inbound call recording filed into one mailbox. The
Message-Waiting Indicator (MWI) is the per-mailbox count of unread versus
read messages that a desk phone lamp or the browser softphone envelope badge
renders. This page defines the whole model once — capture sources, mailbox
identity, the count semantics, and the ownership of the notify path — so the
[recording library](/voice/recording-library) and
[extensions](/voice/extensions) guides stay thin procedure surfaces. Read
this before you wire a queue's overflow to voicemail or build a client
against `GET /api/v1/voice/mwi`.

Voicemail capture is an inbound-record flow. Outbound MT voice and SMS
termination stay on the Devotel wholesale softswitch; nothing on this page
touches a termination path.

## 1. What MWI is and when it fires

MWI is the same idea every PBX has shipped for decades: a per-mailbox pair
of counters — **new** (unread) and **old** (read-but-retained) messages —
plus one boolean, `Messages-Waiting`. In the SIP world that payload is a
`NOTIFY` carrying an `application/simple-message-summary` body (RFC 3842)
for the `message-summary` event package; a Polycom, Yealink, Grandstream, or
Bria client subscribes to it and lights its lamp when the new count is
non-zero.

The platform derives those counters from the `voicemails` rows already
stored for the tenant — there is no separate MWI table and nothing to
provision per mailbox. The derivation is one grouped aggregate per tenant:

* `new` = inbox messages with `is_read = false`
* `old` = inbox messages with `is_read = true`
* `Messages-Waiting` = `new > 0`

Only the active **inbox folder** counts toward the lamp. A voicemail the
spam classifier foldered never flips the lamp and never raises the badge —
the unread badge an operator sees tracks messages that actually need
attention.

MWI fires (is republished) whenever a capture or toggle path changes the
counts: a new voicemail lands, or an operator marks one read or unread.
Each of those paths publishes a Redis wake-up, and every listener re-reads
the full snapshot rather than tracking deltas.

## 2. Where the MWI hooks sit

Three components share the work. Together they cover the SIP lamp and the
browser badge with no duplicated state.

* **Voicemail capture service** — persists the `voicemails` row with its
  mailbox identity (`assigned_user_id` or `box_id`, never both) and the
  initial `is_read = false`, then publishes the wake-up. The Jambonz edge
  dispatch (`dispatch_to_voicemail_box` for department boxes, the softphone
  user path for personal boxes) and the IVR/queue fallback verbs all land
  here.
* **MWI snapshot service** — answers `GET /api/v1/voice/mwi` with the
  per-mailbox counts and builds the RFC 3842 body the voice-gateway NOTIFY
  worker stamps onto the SIP lamp notification.
* **MWI SSE stream** — `GET /api/v1/voice/mwi/stream` pushes the snapshot
  on each Redis wake-up, with a 3-second safety-net tick so a missed
  pub/sub message still self-heals. The browser softphone never receives a
  SIP NOTIFY — it renders the envelope badge off this stream instead.

The voice-gateway NOTIFY worker is the SIP half of the same picture: it
resolves mailbox → address-of-record → registered contact and emits the
`NOTIFY` with the body the snapshot service built. Until a desk phone
subscribes, the web badge path above is the visible surface.

## 3. From capture to the recording library

Every captured voicemail lands in the [recording
library](/voice/recording-library) the same way every other recording does:
the row carries a recording URL, a transcript when the speech pipeline
completes, and the mailbox identity that makes it filterable. Two surfaces
consume the same row:

* the **voicemail inbox view** (the operator-facing list of unheard
  messages), and
* the **recording library** (the QA/review surface, where the same
  recording joins search and evaluation).

The unread marker is `is_read` on the row. Marking a message read or unread
updates that flag and immediately republishes the MWI counts, so the badge
an operator sees and the badge a desk phone lamp would show never disagree.
Retention (`retention_days` / `delete_at`) removes the row; the counts
recompute on the next snapshot because counters are derived, not stored.

## 4. Who owns the notify, and the tenant-scoped mailbox

A mailbox is either a **personal box** (one assigned user, keyed by
`assigned_user_id`) or a **department box** (a named box such as Support or
Sales, keyed by `box_id`). The two are mutually exclusive per row — that is
the mailbox model, and MWI is keyed on mailbox identity, not on a SIP
credential. The gateway resolves the mailbox to a registered contact at
NOTIFY time.

Every component in the chain is tenant-scoped. Counts are computed against
the caller's own tenant schema, and the Redis pub/sub channel is namespaced
by tenant id, so one tenant's badge cannot leak another tenant's counts.
That is also what tenant-owned means here: operators of a tenant read,
toggle, and clear their own voicemail; the platform never silently opens
that surface beyond the tenant. See the [compliance tenant-owned
model](/concepts/compliance-tenant-owned-model) for the general rule.

## 5. Supervisor badge versus extension owner

The badge is rendered from the same snapshot for everyone, but the mailbox
identity decides whose view includes it:

* An **extension owner** sees the envelope badge for their personal
  mailbox — it is their unread count.
* A **supervisor** sees the department-box badge for every box they can
  reach, plus the recording-library row for the same message, because the
  box is a shared mailbox rather than a private one.

There is no separate supervisor-side count: one snapshot, filtered by the
mailbox each viewer is entitled to see.

## 6. Boundaries — what MWI is not

* **Not a delivery-receipt generator.** MWI is an inbound-record signal —
  the message already arrived. There is no carrier delivery receipt to
  synthesize, and treating it like a DLR would leak inbound-only data into
  outbound reporting.
* **Not a CDP event.** Voicemail/MWI state does not publish into the
  customer-data event bus, so segmentation cannot subscribe to "voicemail
  received." The event model the CDP consumes lives in [the CDP event
  model](/concepts/cdp-event-model); voicemail stays a voice surface.
* **The queue is the inbox-level pointer, not the mailbox.** A queue's
  overflow action can hand a waiting call to voicemail (`voicemail`
  overflow), but the mailbox identity the capture writes is what the MWI
  counts follow — the queue points into the inbox; it does not become a
  mailbox itself.
* **Outbound termination is untouched.** Capture is inbound; nothing here
  is a termination decision.

## 7. Further reading

| Surface | What to read next |
| - | - |
| [Recording library](/voice/recording-library) | The unified read surface where each voicemail recording is searchable and QA-scored |
| [Voice extensions](/voice/extensions) | Provisioning the SIP credentials whose device or softphone the MWI lamp/badge notifies |
| [ACD queue model](/concepts/acd-queue-model) | How a queue's overflow hands a waiting call to voicemail |
| [Softphone client lifecycle](/concepts/softphone-client-lifecycle) | The browser client that renders the badge off the MWI snapshot |
| [Compliance tenant-owned model](/concepts/compliance-tenant-owned-model) | Why voicemail reading and clearance stay tenant-scoped |
| [CDP event model](/concepts/cdp-event-model) | The event bus MWI deliberately does not publish into |
