Docs
Guides

Handle replies

Triage and answer every email, LinkedIn, and WhatsApp reply from one inbox — in the app or from the CLI.

Replies to your sequences, plus LinkedIn and WhatsApp DMs, land in one place: Unibox in the sidebar under Data, at /unibox. A LinkedIn account connected without Sync full inbox (off by default) syncs only the conversations OXYGEN started; switch it later with oxygen senders inbox-scope set <account-id> --scope all.

Read the inbox

Three panes. Left: a rail with Senders (once two people in the workspace have sending accounts), Channels (an All channels row, then each channel down to the individual mailbox or account), Replies (on Primary), Status, and Tags. Middle: icon-only tabs — Primary, All, Others, Sent, Warmup, DMARC (hover for the name) — with Mark all read at the end of that row, over a full-width search field and the conversation rows. Right: the thread and the reply box. Sync in the top bar reloads the list and the open thread.

Primary shows sent and received conversations. Check Unanswered under Replies to keep only conversations whose last message is inbound; clear it to bring back threads you already answered. Mark all read counts what your filters match, asks to confirm, and says to run it again if more still match.

The view chip in the top bar (Unibox › All conversations) saves the current filters as a view your whole team can open. Star a view to make it the one /unibox opens for you.

The same list from the terminal:

oxygen inbox list --channel all --unanswered --json
oxygen inbox get <conversation-id> --channel email

list defaults to the merged stream (every channel, interleaved) and narrows with --channel; get and send resolve a conversation's channel from the conversation itself when --channel is omitted. status, tag, archive, and analyze still default to email, so pass --channel linkedin or --channel whatsapp there for a DM thread. A search spans every non-warmup conversation — all channels, every folder except the Warmup and DMARC tabs, and message text — whichever tab it is typed on.

Triage

Each row and each open thread carries a status pill. Click it for the picker: a search box, the label list, and Create Label at the bottom. The eight system labels are Interested, Neutral, Out of office, Wrong person, Not now, Not interested, Lost, and Bounced — the rail's Status section groups them under Positive / Neutral / Negative.

oxygen inbox labels list --json
oxygen inbox status <conversation-id> interested --channel email
oxygen inbox tag <conversation-id> --add "q3 outbound" --channel email
oxygen inbox archive <conversation-id> --channel email

Setting a status by hand is an override — the AI classifier will not change it again. Tags cost 0 credits and are never shown to the counterpart; oxygen tags list shows the workspace vocabulary. oxygen inbox tag with neither --add nor --remove exits 1 with Pass --add and/or --remove.

Triage up to 100 conversations at once. Without --yes you get counts only:

oxygen inbox bulk --action set-status --ids <id-1>,<id-2> --status interested --channel email
oxygen inbox bulk --action set-status --ids <id-1>,<id-2> --status interested --channel email --yes

In the thread header, Archive works; Forward is disabled and its tooltip says forwarding ships with the inbox actions update.

Reply

In the app, type into the composer at the bottom of the thread. Its footer names the mailbox you are sending from. Ctrl+Enter (⌘+Enter on a Mac) sends; Enter adds a line. On an email thread, Attach adds up to five files (PDF, DOCX, XLSX, PPTX, TXT or CSV; 10 MiB in total). From the CLI, --attach <path> on oxygen inbox send does the same and can be repeated; on a LinkedIn thread it takes a public URL instead.

From the CLI, run it once without --approved — that returns a preview of exactly what would go out — then send:

oxygen inbox send <conversation-id> --channel email --text "Thursday 10:00 works. Here is the link."
oxygen inbox send <conversation-id> --channel email --text "Thursday 10:00 works. Here is the link." --approved

Sends cost 0 credits per message on all three channels. Connected senders have separate monthly costs: credit reservations when sender_billing is credit_reservations, or dollar sending seats in the legacy model. Check oxygen billing estimate --json for the workspace's model and current prices (pricing reference). The message still reaches a real person, so --approved is required.

When it fails: mailbox_not_active or mailbox_not_sendable means the mailbox that owns the conversation is paused or has no send transport — fix it under Sequencer → Accounts. email_conversation_no_recipient means the thread has no address to reply to. linkedin_reply_sender_required means two of your LinkedIn accounts share the thread; pick one with --sender-account-id <id>. A LinkedIn send returning unibox_stale: true did send, it is only missing from the thread; run oxygen inbox sync (which pulls LinkedIn and WhatsApp — email is polled by the background worker).

Sent a LinkedIn message by mistake? Delete message on your own message removes it for everyone in the conversation, within 60 minutes of sending; it cannot be undone. From the CLI, oxygen inbox delete-message <conversation-id> <message-id> previews the deletion and --approved carries it out.

A new WhatsApp chat starts from the CLI or MCP: oxygen inbox start-whatsapp --sender <account-id> --phone <number> --text "…" (or --person-row-id <id> for a CRM person) previews the recipient and first message, and --approved sends it. A cold chat runs through your linked WhatsApp device and can raise that account's restriction risk. When a chat's own WhatsApp account is disconnected, Continue with connected account in the thread picks the conversation up on one you still have connected.

Let the agents take the first pass

Neither agent sends anything.

AI Inbox Tagger tags each new inbound conversation using only the allowlist you give it, additively, and never re-adds a tag you removed. It has no web form — set it from the CLI:

oxygen inbox auto-tagger set --enabled --tags "pricing,demo request,not now"

AI Sales Agent classifies replies and writes draft answers into a review queue — each draft it writes spends credits, so cap it. A human approves every one.

oxygen inbox reply-agent set --enabled --persona "Jonas, founder of Acme (dev-tools for CI caching)" --tone "warm, concise, low-pressure" --max-drafts-per-day 25
oxygen inbox drafts list --json
oxygen inbox drafts edit <draft-id> --text "Rewritten answer."
oxygen inbox drafts reject <draft-id>
oxygen inbox send <conversation-id> --channel email --text "Rewritten answer." --draft-id <draft-id> --approved

Configuring is free. In the app, the AI Sales Agent link at the bottom of the Unibox rail opens /agents/inbox-reply-drafts: a Configuration card (Persona, Tone, Instructions, Signature, Draft for these statuses, Draft for these campaigns, Max drafts per day, Save settings) above a Review queue with Approve & send, Edit, and Reject on every draft.

What a reply changes elsewhere

  • The lead's sequence enrollment stops and its open steps are cancelled on every channel, before the next step can dispatch.
  • If the campaign pauses the company on reply (Sending settings → Reply handling, or oxygen sequences update <sequence> --company-reply-pause default), the replier's colleagues in that campaign are held at once. When the reply is classified, the pause stays if its category is one you chose and lifts on its own if not. Resume from Sequencer → Do not contact → Paused companies or oxygen sequences resume-company. To pause a company yourself, for example after a meeting is booked, run oxygen sequences pause-company --domain acme.com; unlike a do-not-contact entry, a pause can be resumed.
  • A hard opt-out in the reply body sets the conversation to Not interested and suppresses that address, so the sequencer never emails it again (Sequencer → Do not contact).
  • By default, any LinkedIn or WhatsApp reply also adds that person to the do-not-contact list with the reason replied, so no campaign reaches them again. Change these rules in the Rules card on Sequencer → Do not contact, or with oxygen suppressions policy show and oxygen suppressions policy set.
  • A timeline activity is logged on the matching CRM person, when that person already exists. While the CRM lead-stage router is armed — a sequence's first live start arms it unless you set it up before — the replier is also created in the CRM if missing and their lead stage moves; see Standing automations.
  • Once classified, the reply emits the workspace event crm.email_reply_classified, which a workflow can trigger on.

Search conversations and message bodies

oxygen inbox list --search is fuzzy — typos, word order and prefixes all match — and it always spans every non-warmup conversation: all channels, archived and sent-only threads included, matching names, addresses, subjects and the text of every message in both directions. The folder, --unanswered and the Primary tab's negative-tier exclusion are ignored while a term is set; explicit facets such as --status still narrow. Conversations come back newest first:

oxygen inbox list --search "sandra prak" --json

For a relevance-ranked, message-level read across every channel:

oxygen messages query -q "pricing" --channel all --since 2026-07-01 --json

On this page