Documentation Index: Fetch llms.txt first to discover every published page. This page is also available as Markdown at /cli-overview/cli-email-commands.md.
Verified · 8/11/2026

Sending, Replying, and Searching Email from the CLI

Send, reply to, and search email straight from the terminal with the primitive CLI, using the same wait-mode delivery semantics and search model as the SDKs.

Use these commands when you want to send, reply to, or inspect mail without leaving the terminal, for one-off testing, CI smoke checks, or scripting an agent workflow around the CLI. All of them wrap the same high-level send/reply/forward flow documented in Inbound and Outbound Email Model; the CLI just gives you flags instead of SDK calls.

Note

Every command below needs an authenticated CLI session or an API key. See Authentication: login, signup, logout, whoami if you haven't run primitive login yet.

Send a new email#

primitive send posts a new outbound message; --to and --body are enough for a minimal send.

primitive send --to alice@example.com --body "Hello!"

Run primitive send --help for the full flag list in your installed version. Add --wait to hold the command open until the first downstream SMTP delivery outcome instead of returning as soon as Primitive accepts the message, the same wait mode the SDKs expose as wait: true:

  1. 1

    Send with wait mode#

    primitive send --to alice@example.com --body "Hello!" --wait
    
  2. 2

    Read the delivery status#

    The JSON response includes a delivery_status field: delivered, bounced, deferred, or wait_timeout. Treat wait_timeout as "outcome unknown", the send may still complete after the command returns.

Reply to an inbound email#

primitive reply --id <inbound-email-id> --body "..." replies to a specific inbound message. Recipients, subject (Re: ...), and threading headers are derived server-side from the inbound row, matching every SDK's reply() behavior. A custom subject is not supported, because Gmail's Conversation View needs a normalized-subject match in addition to a References match, so overriding the subject silently breaks threading for half the recipient population.

primitive reply --id <inbound-email-id> --body "Thanks for reaching out."

Attach a file with --attachment:

primitive reply --id <inbound-email-id> --body "See attached" --attachment ./report.pdf

The interactive REPL form, primitive chat reply "..." --attachment ./report.pdf, works the same way inside a chat session. See Memories and Chat Commands.

Warning

If the inbound row isn't in a repliable state (rejected at ingestion, content discarded, or no recipient recorded), the API returns inbound_not_repliable (HTTP 422) and the CLI exits non-zero. Retrying will not help; the original inbound message has to be resent. A missing Message-Id does not block the reply, it only omits the threading headers.

Forward an inbound email#

The CLI has no dedicated forward command. Forwarding in the SDKs is a send with a generated "Forwarded message" body, so from the terminal you compose the same thing with primitive send, or drive the API directly through the generic operation shortcut described in Direct API Access and Generic Commands. Run primitive list-operations to see every operation available in your installed version.

List and inspect emails#

primitive emails list lists inbound mail; primitive emails get --id <inbound-email-id> fetches one. primitive sent list lists outbound mail the same way.

primitive emails list
primitive emails get --id <inbound-email-id>
primitive sent list

View the latest messages#

primitive emails latest --limit 5

Poll an inbox#

Run primitive emails --help for the polling and watch subcommands available in your installed version, along with their poll-interval and filter flags.

Search email#

Two search modes are available, matching the generated search and semantic-search operations:

  • primitive search, keyword search across received and sent mail.
  • primitive semantic-search, ranked semantic/hybrid/keyword search with match excerpts and a score breakdown. Requires the Pro plan and the semantic_search_enabled entitlement; without them the API returns a 403.
primitive search --help
primitive semantic-search --help
Tip

Semantic search concepts (modes, corpus selection, scoring) are shared across every SDK and the CLI, and each ranked row carries matched fields, a match-centered excerpt, and an additive score breakdown. See Semantic Search for the shared parameter reference.

Verify your inbox is set up to receive mail#

Before you can reply to anything, inbound mail has to actually reach an endpoint or Function. primitive inbox-status reports readiness without side effects: it fetches the server-owned inbox status, shows the first usable receive address on a Primitive-managed domain, and tells you whether inbound mail is stored-only or actively processed by a route.

primitive inbox-status

Output includes:

  • Readiness: whether the account overall is ready, plus separate receiving_ready / processing_ready flags.
  • Receive address: the first receiving-ready, active, Primitive-managed domain, formatted as inbox@<domain>.
  • Mode: actively_processed (a route is bound), stored_only (mail arrives but nothing processes it), or not_receiving.
  • Domains: per-domain status, receive/process readiness, and route count.

Run primitive inbox-setup for the same readiness data plus copy-pasteable next steps, the exact primitive functions init, deploy --wait, and functions test --id <function-id> --wait --show-sends commands to scaffold and wire up a processing Function when none is bound yet:

primitive inbox-setup
Note

inbox-setup never deploys anything itself; it only prints the commands. Follow the printed scaffold steps (or see Primitive Functions: Deploy, Route, and Manage) to actually wire up a processing route.

Next steps#

Was this page helpful?

© Primitive SDKs

Powered by Browzer