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

# Inbox lifecycle hygiene — auto-close, and tags and dispositions at close

> Keep the open queue clean with the auto-close sweep, and keep Quality and Insights reports clean by applying a tagged 'auto-closed' tag plus a 'no-reply' disposition pattern at close time.

# Inbox lifecycle hygiene — auto-close, and tags and dispositions at close

An open queue that never gets swept stops meaning anything. Customers who never reply leave rows parked in `open` or `pending`; queue length, per-agent open counts, and freshness metrics all inflate. The hygiene jobs fix that on two axes: **deterministic cleanup** (the auto-close sweep closes idle threads on a schedule) and **consistent close-out metadata** (tags and dispositions applied the same way at close, so reports can trust the label).

This guide covers the auto-close sweep. SLA timers are a separate guide — see [Inbox SLA timers](/guides/inbox-sla-timers).

## 1. The hygiene jobs

**Auto-close** is a per-workspace opt-in sweep that runs every 10 minutes and closes any conversation whose most recent message — customer, agent, or AI — is older than your configured idle window. Every auto-closed row is stamped `closed_reason: "auto_stale"`, fires the same `conversation.closed` webhook a manual close fires, and is logged to the audit trail as `conversation.auto_closed_stale` — so downstream reporting always separates platform-closed volume from agent-closed volume.

**Tags and dispositions at close** are the second axis. A conversation closed with no disposition and no tags is a report row you cannot filter. Apply a **customer-defined `auto-closed` tag** and a **customer-defined `no-reply` disposition** whenever the sweep (or a human closing purely to clear the queue) closes a thread, and your report filters can exclude that whole class cleanly.

## 2. Where they live

**Inbox → Settings → Auto-close** (owner or admin) is the control surface for the sweep — the page header links the background job documentation; this guide owns the auto-close workflow end to end. Reference detail for the same page lives at [Auto-close stale conversations](/inbox/auto-close) — the two pages serve different reader needs (walkthrough vs. reference).

Tags live at **Inbox → Settings → Tags** and dispositions at **Inbox → Settings → Dispositions**. Both consoles write the vocabulary that the close-time labels draw from; create the vocabulary there first, then close-time automation has something to apply.

## 3. What the sweep does on each tick

On every 10-minute tick the sweep:

1. Reads the workspace-level configuration: **enabled**, **idle-days bound** (1–90, default 7), and **eligible statuses** (`open`, `pending`, or both).
2. Skips the workspace entirely if the sweep is off, so an off-save is a full pause that preserves your other settings.
3. Atomically claims up to **500 rows per tick** per workspace (oldest-idle first) and flips them to `closed`. The claim guard keeps two replicas from double-closing a row; the per-tick cap keeps the sweep from blowing the transaction budget.
4. Stamps each closed row with `closed_reason = "auto_stale"`, sets the resolved timestamp, and fills in the resolution duration so dashboards display a coherent number.
5. Fires the real-time inbox event plus the `conversation.closed` webhook per row, and writes the audit entry.

### What downstream pipelines must tolerate

Any export, report, or data pipeline that reads conversations must tolerate the `auto_stale` marker, not just manual closes. Check:

* **Warehouse exports** — the closed-reason column is nullable; treat `null` as an agent-driven close, and treat `auto_stale` as a platform-driven close.
* **Report filters** — an Insights query that filters "closed conversations" over-counts if it doesn't explicitly include `closed_reason` in the projection.
* **Webhook consumers** — the `conversation.closed` payload carries a `reason` field exactly for this.

## 4. Walkthrough — bind the idle window, save, monitor

Open **Inbox → Settings → Auto-close** (owner or admin).

1. **Enable auto-close.** The master switch; leave it off while you pick the window — turning the sweep on with a bad window is the one mistake this page can't absorb.
2. **Set the idle-days bound.** The slider is a whole number from **1 to 90 days**, default **7**. Fractions and out-of-range values are clamped on save.
3. **Pick the eligible statuses.** `open` and `pending` are the only statuses the sweep can close; at least one must be selected, or the save is blocked.
4. **Save.** Changes take effect on the next sweep — at most 10 minutes later.
5. **QA the sweep before it scales.** If your queue carries a long-lived backlog, give the first save a narrow window (1–2 days), watch the audit trail for a few ticks at `conversation.auto_closed_stale`, and check that closed rows read the way your reports expect — then settle on the production window. That same audit filter is how you build the recurring QA panel: **Settings → Audit log → filter on `conversation.auto_closed_stale`** is the honest "top auto-close rows" view, sorted by close time.
6. **Monitor downstream.** The sweep fires a `conversation.closed` webhook per closed row; if you want a Slack or dev notification on every auto-close, register a webhook endpoint subscribed to `conversation.closed` and filter on `reason: "auto_stale"`. Endpoint creation is under **Settings → Webhooks**.

The sample CRON semantics: the sweep is a **10-minute periodic job**, not a wall-clock cron expression. A conversation crossing the idle threshold is closed on the next tick, so the effective close lands between N days and N days plus 10 minutes after the last message.

If you prefer the API, the same page reads and writes through the organization settings endpoints:

```bash theme={null}
curl -X PUT https://api.orbit.devotel.io/api/v1/settings/general \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "settings": {
      "conversation_auto_close": {
        "enabled": true,
        "idle_days": 14,
        "statuses": ["open", "pending"]
      }
    }
  }'
```

`GET /api/v1/settings/general` returns the same block. Values you write directly are subject to the same clamps the page applies — idle days are floored to a whole number and clamped to 1–90, and statuses outside the `open`/`pending` allow-list are filtered out. An empty `statuses` array re-expands to both statuses on the next read, so never persist an empty array to "pause" the sweep — use `enabled: false`.

## 5. Tags and dispositions applied at close

The sweep itself stamps only the closed-reason marker; tags and dispositions are a separate vocabulary you own. The recommended pattern for close-time consistency:

1. **Create a tag named `auto-closed`** at **Inbox → Settings → Tags** (see [Tags](/inbox/tags)). Apply it whenever you close a thread purely because it went stale — manually or as part of a bulk tidy-up. The tag is the report-side marker that survives a reopen: a reopened-and-reclosed thread still carries it.
2. **Create a disposition set with a `no-reply` label** at **Inbox → Settings → Dispositions** (see [Dispositions](/inbox/dispositions)). Instruct agents and any close-out automation to apply `no-reply` when the thread was closed because the customer never responded.
3. **Build report filters on the pair.** Filter closed conversations on `closed_reason = "auto_stale"` for sweep-driven volume, on tag `auto-closed` for any staleness close regardless of who clicked it, and on disposition `no-reply` for the outcome the customer produced (silence). The three axes are deliberately independent — conflating them into one label makes the report lie on at least one of them.

A close without a disposition is not a report failure, but a close without a reason is. The closed-reason column is always authoritative; tags and dispositions layer the human-readable interpretation on top.

## 6. Failure modes

* **Reopened-after-close.** A customer reopens an auto-closed thread the way any close is reopened — the `closed_reason` marker stays on the row for reporting, but the conversation is live again and the idle window restarts on the next message. Reopened volume for auto-closed threads is a real signal: if a material share of swept threads reopens, the idle window is shorter than your customers' return cadence.
* **Quality-score consistency.** AI Auto-QA scores completed agent-handled **calls**; the auto-close sweep is a conversation-side cleanup, so sweep closes never enter or block the call-side autoscore pipeline. The conversation-side QA risk is different — a conversation closed by the sweep with no agent interaction yet carries no agent context, so any evaluation a human reviewer later writes by hand on that thread measures the cleanup itself, not the agent. Treat auto-closed rows as a separate volume in your QA ledger and do not let a rising auto-close share inflate or deflate an agent's evaluation sample.
* **Hospitality-score consistency.** Any aggregate that divides by "number of closed conversations" includes sweep closes; any aggregate that divides by "number of agent-handled conversations" should not. Keep the denominators separate — conflating them manufactures a declining trendline when the only change is that the sweep is doing more cleanup.
* **Sweep closes in bounded batches of 500 per tick.** If your first save lands on a long-idle queue with thousands of eligible rows, the sweep drains up to 500 per workspace per 10-minute tick — roughly 3,000 rows per hour until the backlog clears. That is the design trade: bounded transactions over instant drain. Reopen by disabling the sweep in the interim only if you actually want the backlog to stay live.

***

Related: [Auto-close stale conversations](/inbox/auto-close) · [Inbox SLA timers](/guides/inbox-sla-timers) · [Tags](/inbox/tags) · [Dispositions](/inbox/dispositions) · [Quality management program](/guides/quality-management-program)
