Skip to content

Use Natural Language with postsale run

Say what you want in plain language: how the agent loop works, how to authorize money-moving work, and how to read what it did.

Updated September 3, 20262 min readPostsale CLI

postsale run is how you describe shipping work in plain language and let Postsale's CLI agent choose the right tools. It is the fastest path when a task takes several steps, and it is the primary interface for AI hosts.

In this article, we will cover what you need before the first run, explain how the agent loop works, capture results the right way, authorize money-moving work safely, and resume conversations when work spans multiple prompts.

Real-World Example

We need rates for an order, then a shipment, then a label. We would rather say "ship this order with the cheapest carrier" than assemble three JSON bodies by hand.

Before We Begin

To use postsale run, you will need:

  • A signed-in CLI session or POSTSALE_TOKEN (Sign In and Credentials)
  • ANTHROPIC_API_KEY (or ANTHROPIC_AUTH_TOKEN) and/or OPENAI_API_KEY
  • Optional overrides: POSTSALE_LLM_PROVIDER, POSTSALE_LLM_MODEL. On Anthropic the default model is claude-opus-5; the provider adapter drops the model's thinking blocks, so they never appear in the narrative, the stream, the summary, or tool_calls
  • A healthy postsale doctor result when possible

If both provider keys are set and you do not override, Anthropic is selected. Doctor's llm_provider check shows what is active.

What postsale run does

  1. Builds a system prompt and exposes the agent tool set (orders, shipments, labels, reports, configuration, and more; see agent tools for the current list).
  2. Sends your intent to the model.
  3. Runs any tool calls through the same safety gates as the rest of the CLI.
  4. Returns tool results to the model until it finishes, hits the turn limit, or stops on a gate or error.
  5. Writes a completion envelope to stdout (or a stream of events with --stream).

Default turn limit: 20 (--max-turns).

Progress, breadcrumbs, and prompts go to stderr. Data stays on stdout. Details: Output and exit codes.

Try a read-only run

Here's how:

  1. Ask something safe:
bash
postsale run "list my carriers and origin addresses"
  1. Capture the envelope:
bash
postsale run "list my carriers" > /tmp/run.json
  1. Inspect the summary and tools used (example with jq):
bash
jq '.summary, .turns, .tool_calls[].name' /tmp/run.json

Success looks like one JSON object on stdout with a clear summary, a tool_calls list, and stop_reason such as end_turn.

See example intents

bash
postsale run --examples

This prints a JSON array of sample intents and commands. It is not a completion envelope.

Rehearse before you spend money

bash
postsale run --dry-run "Ship order <order_id> with the cheapest carrier"

High-risk tools are simulated. Some other writes can still hit the API. Always read simulated_effects and confirmed_effects. Full detail: Dry-run.

Purchase for real

When you intend to charge:

bash
postsale run "ship order ord_abc123 with the cheapest carrier" --yes

For tighter control in unattended runs:

bash
postsale run --authorize create_shipment,purchase_labels \
  "ship order ord_abc123 with the cheapest carrier"

At a terminal, the agent path prints the same environment badge the direct money-moving commands print, but only at the moment it matters: right before a money-moving confirmation prompt and before a batch authorization. A red [PRODUCTION] badge, with a [test-label mode] suffix when POSTSALE_TEST_LABEL_MODE is set. A read-only run prints no badge, and nothing prints when stderr is not a terminal, so agents and unattended-run logs read the envelope instead.

Stream events for hosts

bash
postsale run --stream "how many orders shipped this month?" 2>/dev/null

Each stdout line is one JSON event (tool_call_start, tool_call_end, text, complete, and more). Ctrl-C emits aborted and exits 130.

Continue a conversation

bash
postsale conversations --human
postsale run --continue "now list origin addresses"
postsale run --continue <conversation_id> "follow up..."
postsale continue <conversation_id>    # interactive multi-turn

--interactive keeps prompting on stdin after each turn until you exit.

Options that change outcomes

FlagWhen to use it
--yesUnattended parent money or destructive tools
--authorizeLeast-privilege allowlist of tool names
--dry-runRehearse high-risk tools
--max-turnsCap cost and runaway loops
--streamEvented consumers
--provider / --modelPin the LLM
--continueMulti-step human sessions

Verify any flag with postsale run --help.

Good to Know

Untrusted customer data

Some tool results wrap customer-origin content (order notes, addresses) as untrusted for the model. Treat that text as data, not instructions, especially if you paste model output elsewhere.

Envelope fields you will rely on

FieldMeaning
summaryNatural-language answer
dataLast successful tool payload, or blocked object
tool_callsOrdered log of tools
turnsLoop iterations used
stop_reasonWhy the loop stopped
provider / modelWhat ran
warningFor example max_turns_reached
simulated_effects / confirmed_effectsDry-run and write honesty

Preflight with empty stdout

Missing LLM keys, bad configuration, or an unknown continue id fail before an envelope is written. Check exit code and stderr.

Finding work

Prefer get_work_queue over inventing order status names. See Search and the work queue.

Working with agents

Hosts should treat AGENTS.md as the machine source of truth. A compact loadable summary lives in agent/QUICKREF.md.

Additional Reading