OXYGENOxygen/ 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.

Read the inbox

Three panes. Left: a rail with Channels (an All channels row, then each channel down to the individual mailbox or account), Status, and Tags. Middle: icon-only tabs — Primary, All, Others, Sent, Warmup, DMARC (hover for the name) — over a 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 opens filtered to Unanswered: conversations whose last message is inbound. The All pill beside it brings back threads you already answered. The checklist button ending that row is Mark all read — it counts what your filters match, asks to confirm, and says to run it again if more still match.

The same list from the terminal:

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

Channel defaults differ per subcommand: list, get, and send default to LinkedIn, while status, tag, archive, and analyze default to email. Pass --channel explicitly every time.

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. Enter sends, Shift+Enter adds a line.

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 on all three channels — sending is priced per mailbox, not per message, and connecting a LinkedIn or WhatsApp account is free today (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. 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).

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.
  • 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).
  • A timeline activity is logged on the matching CRM person, when that person already exists.
  • Once classified, the reply emits the workspace event crm.email_reply_classified, which a workflow can trigger on.

Search message bodies

oxygen inbox list works at conversation level. To search individual messages across every channel:

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

On this page