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.
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
Send with wait mode#
primitive send --to alice@example.com --body "Hello!" --wait - 2
Read the delivery status#
The JSON response includes a
delivery_statusfield:delivered,bounced,deferred, orwait_timeout. Treatwait_timeoutas "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.
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 thesemantic_search_enabledentitlement; without them the API returns a 403.
primitive search --help
primitive semantic-search --help
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 separatereceiving_ready/processing_readyflags. - 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), ornot_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
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#
Understand the normalized email object and wait-mode delivery statuses these commands rely on.
Managing Domains and Recipient RoutesConfigure inbound DNS and recipient routing rules so mail actually reaches an endpoint.
Primitive Functions: Deploy, Route, and ManageScaffold and deploy the Function that inbox-setup points you toward.
Direct API Access and Generic CommandsCall any generated operation (like forward-email) directly when a task command doesn't exist yet.
Was this page helpful?