Skip to content

Mail Commands

Mail commands live under broodnet mail. All commands connect to your mailbox over IMAP or SMTP using credentials stored in your local config.

broodnet mail inbox lists messages in INBOX with optional pagination, sort order, and flag filters. By default, messages are returned most-recent first.

Flag Default Description
--limit <number> 20 Number of messages to fetch. Must be a positive integer.
--offset <number> 0 Number of matching messages to skip. By default, the newest. Used with --limit to page through results.
--sort <direction> newest Sort order for the listing. Allowed values: newest, oldest.
--unread Show only unread messages.
--read / --seen Show only read messages.
--important / --flagged Show only messages the user has marked as important.
--answered Show only messages that have been answered.
--unanswered Show only messages that have not been answered.
--mailbox <address> active mailbox Mailbox address to use when multiple are configured.
--json Print a structured JSON envelope with the full listing (messages, total, pagination, filters, sort) as data.

Filters combine with AND — e.g. --unread --important returns messages that are both unread and important. Contradictory pairs (such as --unread --read or --answered --unanswered) are rejected before any server call. Invalid sort values are also rejected before any server call.

Use --offset with --limit to page through results. With the default --sort=newest, pages are counted from the most recent matching message. With --sort=oldest, pages are counted from the oldest matching message.

Terminal window
# First page of 20 (default)
broodnet mail inbox
# Second page of 20
broodnet mail inbox --limit=20 --offset=20
# First page from oldest to newest
broodnet mail inbox --sort=oldest
# Third page of 50
broodnet mail inbox --limit=50 --offset=100

Filters are applied before pagination, so --offset advances through the filtered set in the selected sort direction. If --offset lands past the last match, the command returns an empty result without error.

In interactive terminals, the default output is a table preceded by a summary line showing the current page range, the total number of matching messages, and any active filters.

showing 1-20 of 142 · filters: unread, important
UID FLAGS FROM SUBJECT DATE
...

The summary adapts to the result:

  • inbox is empty — no messages at all and no filters active
  • no messages match · filters: unread — filters are active but nothing matches
  • showing 0 of 42 · offset 100 is past end--offset landed past the last match
Column Description
UID Stable message identifier used by mail open, mail mark, and mail delete
FLAGS unseen · seen · answered · flagged · forwarded
FROM Sender
SUBJECT Subject line
DATE Message date

With --json, data is the full listing envelope so callers can drive pagination and surface totals without re-parsing the table:

  • messages — page of message summaries (uid, flags, from, subject, date)
  • total — total number of messages matching the active filters (before pagination)
  • limit — page size used for this listing
  • offset — offset used for this listing
  • sort — sort direction applied to the listing: newest or oldest
  • filters — the filter set the server applied, e.g. { "seen": false, "flagged": true }. Empty object means no filters were applied.
Terminal window
# Default: latest 20 messages
broodnet mail inbox
# Larger page
broodnet mail inbox --limit=50
# Paginate: second page of 20
broodnet mail inbox --limit=20 --offset=20
# Oldest first
broodnet mail inbox --sort=oldest
# Only unread messages
broodnet mail inbox --unread
# Only important (flagged) messages
broodnet mail inbox --important
# Unread AND important
broodnet mail inbox --unread --important
# Unanswered messages, as JSON
broodnet mail inbox --unanswered --json

broodnet mail search searches INBOX with server-side IMAP SEARCH criteria. At least one of --from, --subject, or --since is required before the CLI opens an IMAP connection. Criteria combine with AND.

Flag Default Description
--from <text> Match the From header using IMAP SEARCH substring semantics.
--subject <text> Match the Subject header using IMAP SEARCH substring semantics.
--since <YYYY-MM-DD> Match messages received on or after the date. Must be a valid ISO date-only value.
--limit <number> 20 Number of messages to fetch. Must be a positive integer.
--offset <number> 0 Number of matching messages to skip. Used with --limit to page through results.
--sort <direction> newest Sort order for the listing. Allowed values: newest, oldest.
--mailbox <address> active mailbox Mailbox address to use when multiple are configured.
--json Print a structured JSON envelope with messages, total, pagination, active search, and sort fields.

Search results use the same table columns and flag glyphs as mail inbox. The summary line includes the active search criteria:

Terminal window
broodnet mail search --from=alerts@example.com --subject=invoice --since=2026-05-01
showing 1-20 of 37 · search: from: alerts@example.com, subject: invoice, since: 2026-05-01
UID FLAGS FROM SUBJECT DATE
...

With --json, data.search contains the active criteria, e.g. { "from": "alerts@example.com", "since": "2026-05-01" }.

Terminal window
# Search by sender
broodnet mail search --from=alerts@example.com
# Search by subject
broodnet mail search --subject=invoice
# Search recent matching messages
broodnet mail search --subject=invoice --since=2026-05-01
# Paginate oldest first
broodnet mail search --from=alerts@example.com --limit=50 --offset=50 --sort=oldest

broodnet mail open <uid> fetches a message from INBOX by its UID, marks it as read, and prints it.

If the requested UID does not exist, the command exits non-zero with error: message <uid> not found. In --json mode, the error envelope uses code: "NOT_FOUND".

Flag Required Description
<uid> yes Message UID — get it from broodnet mail inbox
--mailbox <address> no Mailbox address to use when multiple are configured
--raw no Print the full raw message source including all headers
--json no Print a structured JSON envelope with the message as data

--raw and --json are independent flags: --raw controls what content is printed (parsed view vs full RFC822 source), --json controls how it is printed (plain text vs JSON envelope).

  • Default — prints a compact header (from / to / subject / date, plus Cc / Bcc when present) followed by the message body. If the message has no plain-text part, the body is automatically converted from its HTML content.
  • --raw — prints the full raw message source as received from the server.
  • --json — prints a JSON envelope with the parsed message as data.
  • --raw --json — prints a JSON envelope whose data is { uid, raw }. The raw RFC822 source has everything; the envelope carries only the UID alongside it.

With --json (without --raw), data includes:

  • uid — message UID
  • flags{ seen, answered, flagged, forwarded } booleans
  • from — formatted primary sender
  • to — formatted primary recipient
  • cc / bcc / replyTo — arrays of formatted addresses; empty when the message has none
  • subject — subject line
  • date — ISO 8601 timestamp
  • body — plain-text body (falls back from HTML if needed)

With --raw --json, data is { uid, raw } — no parsed fields.

Terminal window
broodnet mail open 1234
broodnet mail open 1234 --json
broodnet mail open 1234 --raw
broodnet mail open 1234 --raw --json

broodnet mail watch is a shortcut for mail inbox + mail open: it waits for the latest message and opens it, with output identical to broodnet mail open <uid>. Designed for flows where you trigger an email (sign up, request an OTP, invite a teammate) and want the contents on stdout without copying a UID.

Two explicit timers, both fixed:

  • 60-second past window. If the newest message in the inbox arrived within the last 60 seconds, the command opens it immediately.
  • 60-second poll budget. Otherwise, the command checks the inbox every 3 seconds for up to 60 seconds and opens the first new message that arrives.

If nothing new arrives within the poll budget, the command exits non-zero with error: no new message arrived within 60s.

Flag Required Description
--mailbox <address> no Mailbox address to use when multiple are configured
--json no Print a structured JSON envelope with the message as data

On success, the output matches broodnet mail open <uid> exactly — compact headers followed by the text body. In interactive terminals, a one-line progress notice is written to stderr while polling (suppressed in --json mode so JSON consumers get clean stdout).

On timeout, error: no new message arrived within 60s is written to stderr and the command exits with code 1.

If a newly detected message disappears before it can be opened, the command surfaces the same not-found response as mail open: message <uid> not found with code: "NOT_FOUND" in --json mode.

Terminal window
# Wait for an OTP right after signing up
broodnet mail watch
# Pipe the body to another tool (e.g. extract a code)
broodnet mail watch | grep -Eo '[0-9]{6}' | head -1
# Agent-friendly: JSON envelope on stdout
broodnet mail watch --json
# Watch a specific mailbox
broodnet mail watch --mailbox=agent@example.com

broodnet mail send sends an email from your active mailbox over SMTP. The message body comes from --body or stdin.

Flag Required Description
--to <addresses> yes Recipient addresses, comma-separated
--subject <text> no Email subject; if omitted, the message is sent with an empty subject
--body <text> no Inline message body; if omitted, reads from stdin
--from <address> no Sender address — defaults to the configured from default, then the mailbox address
--cc <addresses> no CC recipients, comma-separated
--bcc <addresses> no BCC recipients, comma-separated
--json no Print a structured JSON envelope with the send result as data

Broodnet mailboxes can only send to addresses within the same organization, unless the recipient is covered by consent or reply window. External addresses are restricted at the infrastructure level and cannot be bypassed — this applies to SMTP, CLI, API, and MCP equally. See Sending Email and Outbound Policy for the full policy.

The body is read from --body if provided. Otherwise from stdin — pipe, redirect, or heredoc all work.

Terminal window
# Inline body
broodnet mail send --to=alice@example.com --body="Hello world"
# Piped body
echo "Hello world" | broodnet mail send --to=alice@example.com
# Redirected file
broodnet mail send --to=alice@example.com --subject="Report" < report.txt
# Heredoc
broodnet mail send --to=alice@example.com --subject="Hello" <<'EOF'
Line one
Line two
EOF
Terminal window
# Basic
broodnet mail send --to=alice@example.com --body="Hi!"
# With subject
broodnet mail send --to=alice@example.com --subject="Hello" --body="Hi!"
# With CC and BCC
echo "Notes" | broodnet mail send --to=alice@example.com --cc=bob@example.com --bcc=carol@example.com --subject="Notes"
# From a specific mailbox
echo "Hi" | broodnet mail send --from=agent@org.broodnet.email --to=alice@example.com --subject="Hi"
# JSON output
echo "Done" | broodnet mail send --to=alice@example.com --subject="Done" --json

With --json, the result envelope includes:

  • messageId — server-assigned message ID
  • to — list of accepted recipient addresses
  • rejected — list of addresses the server rejected
  • subject — the subject as sent; empty string when omitted

broodnet mail consent shows whether the active mailbox can request recipient consent for external addresses. It reports mute, recipient-consent, and reply-window state for the mailbox’s organization.

Command Description
broodnet mail consent Show current consent and reply-window status
broodnet mail consent status Explicit alias for the status view
broodnet mail consent request Send a consent request email to one external recipient
broodnet mail consent list List pending, approved, denied, and revoked consent rows for the organization

Use request when an external recipient needs to approve mail from the organization before a message is sent.

Flag Required Description
--to <address> yes External recipient email address
--note <text> no Short note shown in the consent email
--mailbox <address> no Mailbox address to use when multiple are configured
--json no Print a structured JSON envelope with the request result

Notes are intentionally constrained: short text only, common punctuation only, and no links, protocols, or IP addresses. If a consent request is already pending, the command returns the existing pending row and does not resend the email.

If recipient consent is not available for the current organization plan or policy, broodnet mail consent reports the reason before an agent attempts a request. Free-plan organizations receive a paid-plan availability message; paid organizations with consent disabled receive a policy-disabled message.

Terminal window
broodnet mail consent
broodnet mail consent request --to=person@example.com
broodnet mail consent request --to=person@example.com --note="Need approval for support replies"
broodnet mail consent request --to=person@example.com --json

broodnet mail consent --json returns the live policy state in data:

  • canRequesttrue when this mailbox can request recipient consent now
  • reasonnull when requests are available, otherwise the human-readable reason
  • policy.enableWhitelist / policy.enableReplyWindow — stored organization settings
  • policy.effectiveEnableWhitelist / policy.effectiveEnableReplyWindow — whether each setting currently applies after plan and mute gates
  • policy.planAllowsWhitelist / policy.planAllowsReplyWindow — whether the current plan includes each feature

broodnet mail consent request --json returns data.result as one of:

  • created — a new consent email was sent
  • already_pending — an unexpired request already exists; no new email was sent
  • already_approved — the recipient is already approved
  • not_required — the address is internal to the organization, so consent is unnecessary

Policy and plan failures return an error envelope with code: "POLICY" and a readable error message. Validation failures, such as unsafe notes or invalid email addresses, return code: "VALIDATION".

list shows consent lifecycle rows, not a full address book. It does not list organization members or sibling mailboxes.

Flag Required Description
--status <status> no Filter by pending, approved, denied, or revoked
--search <text> no Search by recipient email or note
--limit <number> no Number of rows to fetch. Default: 20
--offset <number> no Number of rows to skip
--mailbox <address> no Mailbox address to use when multiple are configured
--json no Print a structured JSON envelope with the listing as data
Terminal window
broodnet mail consent list
broodnet mail consent list --status=pending
broodnet mail consent list --search=example.com --json

With --json, data is { total, items }. Each item includes lifecycle fields such as email, status, note, requestedAt, approvedAt, deniedAt, expiresAt, and isExpired; database metadata such as createdAt and updatedAt is not included.

broodnet mail mark <uid...> sets or clears supported IMAP flags on one or more messages without opening or deleting them.

Flag Required Description
<uid...> yes One or more message UIDs to update
--seen no Mark the selected messages as read
--unseen no Mark the selected messages as unread
--flagged no Mark the selected messages as flagged
--unflagged no Remove the flagged marker from the selected messages
--mailbox <address> no Mailbox address to use when multiple are configured
--json no Print a structured JSON envelope with affected UIDs as data

At least one flag change is required. Contradictory pairs are rejected before any server call:

  • --seen --unseen
  • --flagged --unflagged

Different flag families can be combined in one call, for example --seen --flagged.

With --json, data includes:

  • marked — the list of UIDs submitted for update
Terminal window
broodnet mail mark 1234 --seen
broodnet mail mark 10 11 12 --flagged
broodnet mail mark 1234 --seen --flagged
broodnet mail mark 1234 --unflagged --mailbox=agent@example.com
broodnet mail mark 1234 --seen --json

broodnet mail delete <uid...> permanently deletes one or more messages by UID. Deletion is immediate and irreversible — messages are expunged from the server.

Flag Required Description
<uid...> yes One or more message UIDs to delete
--mailbox <address> no Mailbox address to use when multiple are configured
--yes no Skip confirmation prompt. Required in non-interactive mode.
--json no Print a structured JSON envelope with deleted UIDs as data

In interactive terminals, the command prompts for confirmation before deleting. Pass --yes to skip the prompt. In non-interactive environments (scripts, CI), --yes is required — the command will fail without it.

Terminal window
broodnet mail delete 1234
broodnet mail delete 10 11 12
broodnet mail delete 1234 --mailbox=agent@example.com
broodnet mail delete 1234 --yes

broodnet mail config views or updates per-mailbox defaults. The only configurable default is the sender address (from), which mail send uses when --from is not passed.

Flag Required Description
--from <address> no Set the default sender for this mailbox. Must be verified. Omit to view current config.
--mailbox <address> no Mailbox to view or update (defaults to active mailbox)

The --from value must be a valid email address or Display Name <email> format, and must be a verified sender for the mailbox. If validation fails, the command exits with an error and the config is unchanged.

Running broodnet mail config with no flags prints the current config for the active mailbox.

Terminal window
# View current defaults
broodnet mail config
# Set a default sender
broodnet mail config --from='Agent Bot <agent@acme.com>'
# Set defaults for a specific mailbox
broodnet mail config --mailbox=agent@broodnet.com --from=agent@acme.com
# View as JSON
broodnet mail config --json

broodnet mail status shows the current state of a mailbox: its address, configured sender default, connection settings, last sync time, and live storage usage.

Flag Required Description
--sync no Force a sync from the server before displaying
--mailbox <address> no Mailbox to inspect (defaults to active mailbox)

Storage is shown as used / quota MiB (percent%). If the server is unreachable, the command still prints the locally cached config — storage is omitted.

With --json, data includes:

  • mailbox — the mailbox address
  • from — configured sender default, or null
  • host / port — connection settings
  • lastSync — ISO 8601 timestamp of the last sync, or null
  • storage{ quotaUsed, quota, percentInUse, unit: "MiB" }, or null if unavailable
Terminal window
broodnet mail status
broodnet mail status --sync
broodnet mail status --mailbox=agent@example.broodnet.com
broodnet mail status --json