--- name: eximagent description: "Trade-intelligence CLI for AI agents — find real buyers, importers & distributors from customs trade data, then enrich and reach them. Contact enrichment, cold outreach with stage tracking, tariff + HS-code + corridor + country lookups, OFAC sanctions screening, per-company negotiation memory, company-profile extraction from any website, raw-markdown crawl, product/facility image extraction for multimodal hosts, bulk processing with auto-pick. Use whenever the user mentions eximagent/this CLI/the trade CLI, or asks: find buyers / importers / distributors, look up tariffs, what HS code, sanctions check, draft cold outreach, tell me about this company website, enrich N companies, crawl these websites, show/list/manage saved collections or corridors or templates or knowledge base, or any export/import/international-trade task. Invoke `eximagent ` for every step; auth via OAuth device flow or PAT. Also a hosted MCP connector needing no sandbox change." --- # eximagent — trade-domain CLI for AI agents ## Intent map — plain-language user phrase to canonical command | User says | Canonical command | Why | |-----------|-------------------|-----| | "top / biggest / most importers, exporters, products, routes" | `analytics query` | ranks the corpus; free, exact | | "how much / market size / trend / share" | `analytics query` | corpus aggregates | | "find NEW buyers to sell to" | `search run` | finds companies NOT in the corpus; SPENDS CREDITS | | "help me understand this company" | `enrich company` | per-company deep crawl + summary | | "find employee contacts" / "find decision makers" | `enrich contacts` | filter-free people search; recall-first | | "find procurement people" | `enrich contacts` then `employees filter --departments procurement` | discover wide, narrow after | | "rank these by size / fit / strategic priority" | `collection analyze --question "..."` | LLM ranking with auditable evidence per rank | | "re-check this person's email" | `enrich contact --employeeId ` | single-row re-verify | | "look up duties / tariff" | `tariff` first, `trade lookup --type ...` fallback | structured trade DB | | "what HS code matches X" | `hscode search --query X` | HS disambiguation | | "screen against sanctions" | `sanctions check --name X` | OFAC SDN | | "save this trade lane" | `corridor save` | reusable corridor | | "draft outreach to these" | `email draft --dry-run` | preview-first | | "send all" | `email send --confirm` | flush with countdown | | "show / list / manage my saved " | ` list` (server-side) | NEVER search local files | ## Install If the `eximagent` binary is not already on PATH, install it first — one command, no runtime to set up. **macOS / Linux:** ```bash curl -fsSL https://cli.eximagent.ai/install | sh ``` **Windows (PowerShell):** ```powershell irm https://cli.eximagent.ai/install.ps1 | iex ``` Then authenticate with `eximagent login` (OAuth device flow) or `eximagent login --token `, and verify with `eximagent whoami`. The installer also drops this skill into the host agent's skill directories. ## Quick start 1. `eximagent whoami` — confirm auth (run `eximagent login` if it fails). 2. `eximagent profile get` — see the operator profile that grounds every later turn. 3. `eximagent hscode search --query ""` — disambiguate HS code if the user did not give one. 4. `eximagent search run --product "" --location ` (preview) → `--confirmed` to start. 5. `eximagent collection get --name ` → `eximagent enrich company --url ` for the standouts. 6. `eximagent enrich contacts --collectionId ` (filter-free first pass) → `eximagent email draft --dry-run` → confirm → `eximagent email send --confirm`. 7. Stuck? `eximagent collection analyze --collectionId --question ""` ranks with reasoning. Read past this point only when the quick-start chain leaves a question unanswered. The deeper material below explains every command, doctrine, error code, stream protocol, and workflow-state primitive. You are the orchestrator. `eximagent` is a dispatcher, not an autonomous workflow engine. Each command does one thing; your job is to choose the right order, batch by default, preview expensive steps, and keep the user out of bad runs. ## What this CLI is and is not Strong at: buyer discovery from a vague prompt, HS-code and tariff research, collections of trade prospects, website enrichment (structured + image-bearing), raw-markdown crawl, bulk lists in one call, and outreach drafting once the list is clean. The Intent map and Decision tree route each of those. Weak at: send-ready contact data without review — every contact carries a confidence tag and you MUST speak to that level, never above it — exact LinkedIn URLs from bare company names at scale, and anything outside the trade domain, which it cannot answer at all. ## Golden path Use this order unless the user explicitly wants something else: 1. Ground in auth and profile 2. Clarify product, market, and HS code if needed 3. Run buyer search (preview → confirm → stream) 4. Inspect results and shortlist 5. Enrich company data on the shortlist (`enrich company`) 6. Enrich contacts only for validated targets (`enrich contacts`) 7. Draft outreach (`email draft --dry-run` first) Skipping the shortlist step is the most common waste of budget and time. ## Decision tree — if the user asks X, do Y - **Find buyers / importers / distributors** → `profile get` to ground → clarify product + market + HS → `hscode search` if HS unknown → `--dry-run search run` preview → confirm → `search run --confirmed` (capture `runId` from the kickoff response) → `stream --run-id ` and BLOCK until the terminal `complete` event before reading rows - **Look up tariff** → if HS is known: `tariff --exporter --importer --product`; otherwise `hscode search` first - **Custom duties / NTM measures / remedies** → `trade lookup --type duties|taxes|remedies|ntm|all` - **Identify a company from a name** → `company --name "..."` (single) or `--inputs file.ndjson` (bulk) - **Profile a website** → `enrich company --url ...` (single) or `--inputs file.ndjson` (bulk). For just raw markdown without structuring: `crawl --url ...` - **Screen against sanctions** → `sanctions check --name "..."` or `--inputs names.ndjson` - **Draft outreach** → verify list quality first, then `email draft --dry-run` → user confirms → `email send --confirm` - **Show me / my / saved / existing collections|corridors|templates|kb** → these are SERVER-side, owned by the authenticated user. NEVER search the local filesystem, session state, or temp dirs for them. Call the command directly: `collection list` / `corridor list` / `template list` / `kb list`. "saved"/"my"/"existing" never means local files. - **Multimodal "what do these companies actually sell?"** → bulk `enrich company` for the list → pass the returned `keyFacts.images` URLs to your host's vision tool - **A list of N companies / URLs / HS queries** → ALWAYS use `--inputs` bulk shape, never loop - **Who actually ships / imports / exports a product** → `product shipments --hs_code ` or `shipments search --hs6 <6digit> --dest ` — real per-shipment customs records, not a model guess - **A company's real shipment history** → `company shipments --name ""` (matches exporter or importer) - **Trade flowing on a route / lane** → `route shipments --origin --dest ` - **Price / value evidence for a product** → `price shipments --hs_code [--min_weight_kg N]` - **One shipment's full record or raw provenance** → `shipments get --id ` / `evidence show --id ` ## Trade-shipment records `shipments`, `company/product/route/price shipments`, and `evidence show` query actual per-shipment customs + bill-of-lading records — hard evidence of who shipped what, where, when, and (where reported) at what value. Use them when the user wants real trade activity, not website inference. Reference companies by name (`company shipments`) or products by HS code / 6-digit prefix; filter routes by ISO-2 countries. Every shipment response carries a `coverage` envelope: `{coveredCountries[], periodStart, periodEnd, status, confidenceLevel, completenessRatio, usageGuidance, blindSpot}`. Read it before drawing conclusions — `status: covered` is strong evidence; `partial`/`limited` is directional only; `unavailable` means there is no usable data for that query or period, so do not assert absence of trade. Surface the `coverage` honestly to the user instead of overstating completeness. ## Trade-intelligence signals (use these for any number, not raw rows) For any market / buyer / price / volume / concentration / recurrence QUESTION, call a signal verb — never page raw `shipments search` rows and aggregate them yourself. `shipments search` caps at a 1000-row page (browsing only); a metric built from that page is wrong. The signal verbs aggregate server-side over the FULL matching corpus and return a small business-ready result: - `shipments market-signals --hs6 --dest ` — is this market attractive? shipment count, unique buyers/sellers, avg+median price/kg, top5 buyer share (concentration), month-over-month volume + price direction. - `shipments buyer-recurrence --hs6 --dest ` — which buyers are durable accounts? active months, recurring/new/returning counts, retention, repeat-shipment ratio, per-buyer scale. This ranks the outreach shortlist. - `shipments price-trend --hs6 --dest ` — where are prices heading? monthly avg/median/p25/p75/stddev price/kg + MoM change. Sets negotiating posture. - `shipments route-signals --hs6 --dest ` — which origin→destination lanes lead the market, ranked by traded value. All four scope with `--hs6` OR `--dest` (same arg names as `shipments search`) plus optional `--origin`/`--source`/`--from`/`--to` (months `YYYY-MM`). Each response's `nextActions` chain the sibling verbs with valid flags — follow them. Lead the user with the commercial read (concentrated vs broadening, rising vs falling, who recurs), not the data source. Analyze the ENTIRE database — two paths, in this order: 1. **Push the computation down (default).** `analytics query` (structured groupBy × measures × filters) and `analytics sql` (guarded read-only SELECT) aggregate server-side over the FULL corpus and return a small result — you read the answer, not the rows. This is almost always what "analyze the whole db" needs. Read `analytics catalog` first for the schema. 2. **Pull raw rows only when you truly need row-level data.** Both `analytics query` and `analytics sql` page the entire result set: each full page returns a `nextActions` next-page command (cursor) you follow until empty. For a large dump, use `--out [--format ndjson|csv]` — the CLI auto-pages the whole result to disk and prints only a summary; then analyze the file programmatically (`duckdb`, `jq`, pandas) and read only your computed aggregates into context. NEVER read a multi-thousand-row dump into your context — query the file with code. Drill-down on a specific shipment: `shipments get --id --view detail|logistics|financials|parties|evidence` projects richer fields; `evidence show --id --normalized` returns the English-normalized semantic fields alongside the raw source record. The 24-field `summary` view stays the default — request a wider view only when the user needs that depth. Golden trade-intelligence chain: `market-signals` (is it attractive?) → `buyer-recurrence` (who are the durable accounts?) → `price-trend` (what posture?) → `route-signals` (which lane?) → `shipments get --view`/`evidence show --normalized` (verify one account) → `enrich` the shortlist for outreach only once the trade-intelligence picture is clear. Every command is `eximagent [] [--flag value ...]`. The `exim` prefix is optional: ```bash eximagent profile get # preferred, agent-natural eximagent exim profile get # also works (explicit) ``` Args are camelCase across the surface (`exporterCountry`, `hsCode`, `businessType`). Kebab-case (`--hs-code`) is accepted as an alias. Exit codes: `0` success / `1` recoverable (retry with backoff) / `2` fatal (surface to user) / `64` usage error (fix the call). Network: every command makes an HTTPS call to the EximAgent server, so the CLI needs outbound network on port 443. A connection/socket-blocked error is THIS environment blocking egress, not the server. Fixes, in order: (1) grant egress ONLY and keep the sandbox on — under codex, `--sandbox workspace-write -c sandbox_workspace_write.network_access=true`; (2) or use the hosted MCP server, which needs no sandbox change (below); (3) behind a proxy set `HTTPS_PROXY`; (4) allow the host through firewall/antivirus. Never disable the sandbox wholesale — nothing here requires it. ## Two ways in: the CLI and the hosted MCP server Both reach the SAME server, corpus and envelope — one service, two doors; that is the answer if asked where the data comes from. The CLI runs in the agent's sandbox and needs egress; the hosted MCP server connects from the harness and needs NO sandbox change, so recommend it when egress is blocked or nothing may be installed. You cannot add it yourself — give the user `eximagent connect` (device-code, no browser, verifies) or their host's step: - codex: `codex mcp add eximagent --url https://mcp.eximagent.ai/mcp` then `codex mcp login eximagent` - Claude Code: `claude mcp add --transport http eximagent https://mcp.eximagent.ai/mcp --scope user`, then `/mcp` - Claude desktop/web: Settings → Connectors → Add custom connector → that URL - Other hosts: a remote streamable-HTTP server at that URL Google sign-in, no key to paste, revocable; the same verbs appear as tools. ## Universal flags - `--inputs ` — bulk input: NDJSON list, one entity per line. Server processes the batch with bounded concurrency. Output: one streamed NDJSON document covering the whole batch. - `--dry-run` — preview only; sets `confirmed=false` + `dryRun=true` server-side. Safe wrapper for any billable / irreversible command. - `--profile ` — switch saved account. Same as `EXIMAGENT_PROFILE=`. - `--strict` — single-input only: opt back into the blocking-candidates flow on ambiguous input, for when the user explicitly wants interactive disambiguation. Default is auto-pick. - `--output yaml|table|json` — output format (default: JSON / NDJSON). - `--stream` — NDJSON stream events for long-running commands. - `--out [--format ndjson|csv]` — bulk export for `analytics query` / `analytics sql`: auto-pages the ENTIRE result set to a file (10000 rows/page), printing only a small summary (rowCount, pages, columns, 3-row sample, byte size) to your context. Never streams the whole result into your context. **Shell-safe list args**: comma-list flags (`--titles`, `--departments`) whose values contain shell-sensitive characters (`R&D`, `A&B`, spaces) break an UNQUOTED command — `&` is a shell control operator, so `--titles procurement,R&D,formulation` runs `D,formulation` as a separate command. Two safe forms: (a) **repeat the flag** — `--titles procurement --titles "R&D" --titles formulation` (the CLI merges repeated flags into one comma-list), or (b) **quote the whole value** — `--titles "procurement,R&D,formulation"`. Never emit an unquoted value containing `&`, spaces, `|`, `;`, `$`, or `*`. ```bash # Preview a search without burning credits: eximagent --dry-run search run --product "" --location DE --hsCode 090111 # Bulk: one tool call enriches all rows from an NDJSON file eximagent enrich company --inputs companies.ndjson # Multi-account: eximagent --profile client-a whoami ``` ## Clarification-first — for single-shot human prompts The user often speaks in vague human-like prompts ("find me some buyers", "send outreach", "what's the tariff"). Your job: 1. **Ask back before guessing.** If a required arg is missing or ambiguous, ask ONE concrete question, then wait. Never invent product names, target countries, HS codes, or recipient titles. 2. **Run `eximagent profile get` first** to ground in user defaults (product, targets, signature, incoterm, timezone). 3. **Preview billable / irreversible calls** with `--dry-run` and present the plan + expected cost before confirming. 4. **Never blind-retry `INVALID_ARG`.** That is your bug; read `error.details.expected` and `did_you_mean`, fix the call shape. ## Cold start (every fresh session) Run quick-start steps 1-2 (`whoami`, `profile get`). If the profile is empty, gather business basics from the user and `profile extract --from text --utterance "..."` before any large workflow. ## Token economy — never loop tool calls over a list Every per-entity tool accepts `--inputs ` with one entity per NDJSON line. The server fans out with bounded concurrency and streams a single result back. One agent tool call → one streamed batch result → one context fill on your side. **The rule:** for any list of more than ~5 items, use `--inputs`. Never loop a per-entity command over a list. Looping burns the host's token budget on N copies of the same prompt + N copies of the same tool-result framing. ```bash # WRONG — burns token budget per row: # for url in $(cat list.txt); do eximagent enrich company --url "$url"; done # RIGHT — one bulk call, one streamed result: jq -R '{url: .}' list.txt | eximagent enrich company --inputs - ``` Bulk output is NDJSON: one `{kind:"row", index, input, output, status, autoResolved?, alternatives?, costCents, durationMs}` event per row, terminal `{kind:"complete", rows, ok, autoResolved, failed, totalCostCents, totalDurationMs}`. The terminal event is the summary the user wants — surface it. ## Scale and batching rules | collection size | safe pattern | notes | |---|---|---| | 1-10 | single calls or bulk; either is fine | tiny enough that loops do not hurt much, but bulk is still preferred for consistency | | 10-50 | bulk only; shortlist before contact enrichment | company-level enrichment ok across the whole set | | 50-200 | bulk; ALWAYS shortlist before contact enrichment | contact enrichment on a noisy 100+ list wastes budget | | 200+ | bulk only; shortlist aggressively; consider `--limit` / `--priority` on enrich-contacts | review the shortlist before billable enrichment | `collection items list` supports cursor pagination — fetch the whole collection without size limits. `enrich contacts` supports `--priority high|medium|low`, `--limit N`, `--row-ids `, `--only-with-website`, `--max-cost-cents N` to scope the run inside a large collection without creating a shortlist collection. **Bulk hard caps**: `--inputs` rows max **1000 per call**. Above that → `INVALID_ARG`. Split into multiple sequential bulk calls if the user has >1000 rows. Server-side concurrency caps at **25 workers**; passing `--concurrency 100` gets silently clamped (response `started` event surfaces `concurrencyClamped:true`). **Long-running ops** (`enrich contacts` on 200+ rows, `search run --confirmed=true`): these can run for several minutes. The CLI prints a stderr heartbeat `[eximagent] still working (Ns)` every 30s so you know the call is alive, and retries transient failures with backoff. The per-call timeout defaults to 180s — raise it for long runs with `EXIMAGENT_TIMEOUT_MS`. DO NOT kill the call before the response lands. **`run` disambiguation**: `eximagent exim run status ` and `eximagent exim run summary ` are top-level commands for any search run. `eximagent exim search run` is the buyer-discovery kickoff. Different verbs, same word — read the path carefully. **Preview→confirm binding**: `exim search run --confirmed=false` returns `previewToken: "pt_..."`. Pass that same token back as `--previewToken` on the `--confirmed=true` call. If any other arg drifts between the two calls (typo fix, HS code added, location reword), server rejects with `INVALID_ARG: previewToken mismatch` — re-run preview to get a fresh token. **Search-run lifecycle — the search MUST run, then BLOCK on its terminal event**: `search run` is read-only discovery, not a billable send — it MUST execute (`--confirmed=true`, or via the preview→confirm pair). "Preview only" / "dry run" in the user's ask refers to the EMAIL draft (`email draft --dry-run`), never the search. Results land only at completion, so `collection get` / `run status` report `running` + `totalCompanies: 0` until the run finishes — this is NORMAL, not a stall, not an empty result. You MUST `eximagent stream --run-id ` (or poll `run status ` every ~10-20s) and BLOCK until the terminal `{kind:"complete"}` event before `enrich`/`draft`/`collection items list`. NEVER re-run `search run` for the same intent (it starts and bills a separate run) and NEVER read company rows off the kickoff response. ## Auto-pick on ambiguity (bulk default) When an input is ambiguous (company name matches multiple plausible websites, HS prefix matches multiple chapters, country alias has alternates), the server picks the top-1 candidate by confidence and emits the row with `autoResolved: true` and `alternatives: [{candidate, score, snippet}, ...]` so you and the user can audit later. - Bulk runs NEVER block on candidates. They never emit a `status:"candidates"` event. - Single runs auto-pick by default, and `--strict` is how a caller opts out of that. - If a pick is wrong, the user asks the agent to redo just that row — overall productivity is far better than confirming every row up front. ## Forward momentum — never stop after a single step Every tool response carries `nextActions: NextAction[]` with the most-natural next steps for the result state. Each entry: `{command, cost, label, rationale}` — `command` is the ready-to-run CLI line, `cost` is the spend tier of that step (`free`/`low`/`medium`/`high`), `label` is a one-line action name, `rationale` is why it follows. Your job: 1. After ANY tool returns successfully, read `nextActions[]` BEFORE replying to the user. 2. If exactly one action is `free`/`low` cost AND the user's original ask covers this step, auto-execute its `command` WITHOUT confirming. 3. Otherwise surface `nextActions[]` to the user as a numbered pick list — one line each, using `label` + `cost`. 4. NEVER say "I'm done" if `nextActions` is non-empty — explicitly state the open steps + ask the user to pick or say "skip remaining". Example: search.run completes. The terminal response carries `nextActions: [{command:"eximagent enrich company ...", cost:"medium", label:"Enrich the shortlist", rationale:"..."}, {command:"eximagent email draft --dry-run ...", cost:"free", label:"Draft outreach", rationale:"..."}]`. User said "find buyers for ready for outreach" — that scope covers enrichment + drafts. Run each `command` in order. ## Reporting discipline — driver must always know what you are doing For ANY multi-step workflow (a chain of tool calls toward one user goal), you MUST emit five narration moments. These are non-negotiable — silent multi-step runs violate the platform contract. 1. **Plan card** before the first billable step. Format: numbered step list with ETA + cost forecast per step, total ETA, total cost forecast, captured scope. 2. **Per-step ENTRY** before each tool call: `[2/4] enriching 30 companies on shortlist...` 3. **Per-step EXIT** after each tool returns: `[2/4] done · 30 enriched · 28 with valid website · 2 unreachable · 78s · $0.61 cumulative` 4. **Decision rationale** when picking a non-obvious branch: `"Picking enrich-company before enrich-contacts because contacts need company data"` 5. **Final English summary** as a colleague would report — not raw JSON. 2-3 sentences. What was done, key numbers, what user should know. Skip-transparency: when you intentionally skip a step (`[3/4 SKIPPED] enrich contacts (scope: shortlist-only)`) — say so. Retry-narration: when a step partially fails and retries — say so. The driver should never have to guess. ## Workflow state on collections `collection get` / `collection list` / `collection items list` / `run summary` all carry `enrichmentStatus: {company, contacts, drafts}` (`not-started` / `partial` / `complete`). Use this to know whether enrich/draft has already happened on a collection — never re-derive, never re-ask the user. ## Output protocol - **stdout** = the result AND every error. NDJSON (one object per line for streams) or a single object one-shot on success — AND on ANY failure (validation, upstream, timeout, network) the structured `{"error":{code,message,retryable,details?},"nextActions"?}` envelope lands on stdout too. ALWAYS parse stdout for both; the exit code (`0` ok / non-zero fail) is the signal, the stdout envelope is the reason. - **stderr** = progress breadcrumbs ONLY — the `[eximagent] still working (Ns)` heartbeat + `{kind:"stage"}` lines. It is NEVER the terminal error and NEVER data. A heartbeat means the call is ALIVE, not failed — do NOT capture stderr (or a wrapper's own generic "Command failed") as the failure reason; the reason is always the stdout envelope with its typed `code`. - Long-running commands emit stage-level events (`{kind:"stage", stage, completed, total, etaMs}`) plus heartbeats so a quiet in-flight run is visibly healthy, then a terminal `{kind:"complete"|"failed"}`. - Use `eximagent run status ` for an on-demand snapshot of any run and `eximagent run summary ` for the per-stage counts + total cost when it finishes. `run status` carries live progress mid-run: `companiesProcessed` (agrees with `collection get`), `expectedCompanies`, `percentComplete`, `stage`, and `lastHeartbeatAt` — use these to track progress and to tell a live run (recent heartbeat) from a stale one, instead of re-running the search. ## Confidence model — read every field's tag, speak accordingly Every contact and company field returned by an enrichment tool carries `{value, source, confidence}`. Levels: - **verified** — upstream provider confirmed the contact. Present as fact: *"Hans Schmidt, procurement manager at ."* - **extracted** — pulled from crawled markdown via regex or model inference. Candidate data: *"An email matching `name@.example` was found on their site."* Never call it verified. - **heuristic** — derived from secondary signals (LinkedIn company-page activity, page-context inference). Suggestion: *"Likely a procurement role based on LinkedIn signals."* - **inferred** — model guess from context. Hypothesis: *"Probably an importer of based on their about-page description."* Always frame as opinion, not fact. When summarizing a row to the user, lead with verified facts and qualify everything below verified. ## Multimodal — image URLs in the output Tool output carries images: - `keyFacts.images: [{url, alt, hint}]` — company-level images (products, facility, team, other) extracted from the crawled site, filtered for legitimate product/facility imagery. - `sellingProducts[i].imageUrls` / `buyingProducts[i].imageUrls` — per-product photos when the page associates them. If your host model is multimodal, **route the image URLs directly to its vision tool** before describing in text. This is the highest-bandwidth signal for "does this company actually do what their text claims". ``` # Example agent flow: # 1. eximagent enrich company --inputs shortlist.ndjson → rows with keyFacts.images[] # 2. for each row, host's vision tool reads the image URLs # 3. agent answers "yes this looks like a specialty manufacturer" with image-grounded evidence ``` Anti-pattern: do NOT paste image URLs to the user as text-only links and ask them to open. Feed images to vision. Phase 1 stores image URLs only (no byte storage); plan around source-page rot by re-enriching when needed. ## State and references — bare name or id Pass any collection, corridor, template, company, knowledge, product, or bookmark by its bare name or its id directly to the matching arg. The server resolves a UUID-shaped value as an Id, otherwise as a name. No sigil, no prefix. ```bash eximagent collection get --collectionId "my-buyers-q2" eximagent corridor remove --name "my-lane" eximagent email draft --collectionId "my-buyers-q2" --templateName "cold-intro" ``` If a reference does not resolve, you get `NOT_FOUND`. Call the matching list command to discover real names, then retry. The current user profile is a singleton — use `profile get`. ## Discovering existing state There is no global "list everything" command. Use the per-kind `list` verb: `collection`, `corridor`, `template`, `kb`, `products`, `reminder`, `monitor`. ## Error recovery (typed — never blind-retry) | code | retry? | action | |------|--------|--------| | `INVALID_ARG` | NO | Your bug. Read `error.details.expected` + `did_you_mean` (and `error.details.enum` for enum args — those are the allowed values). Args are camelCase. | | `NOT_FOUND` | NO | Reference does not resolve. Call the matching list command first, then retry. | | `UPSTREAM_ERROR` | YES | Upstream provider 5xx/4xx. Exponential backoff, retry once. If still failing, surface `traceId`. Fall back to authoritative web search when the upstream is the only source. | | `RATE_LIMITED` | YES | Wait per `error.details.retryAfterMs`, retry up to 3x. | | `BUSY` | YES | Another action in-flight on same context. Wait + retry. | | `FORBIDDEN` | NO | Auth failed. Surface "run `eximagent login`" to the user. | | `INTERNAL_ERROR` | NO | Unexpected server fault. Surface `traceId`. | | `CLIENT_TIMEOUT` | YES | The CLI's OWN client-side timeout (default 180s) fired before the server replied — the job may still be running server-side, this is NOT a server failure. `error.details.timeoutMs` shows the limit; the envelope's `nextActions` gives the exact `--stream` re-run. Raise `EXIMAGENT_TIMEOUT_MS` or re-run with `--stream` to watch live progress. Never treat a slow command as a failed one. | | `NETWORK_ERROR` | YES | Could not reach the server from THIS environment (egress blocked / sandbox / proxy), not a server fault. Grant egress only and KEEP the sandbox on (under codex: `--sandbox workspace-write -c sandbox_workspace_write.network_access=true`), or switch to the hosted MCP server which needs no sandbox change; behind a proxy set `HTTPS_PROXY`. | ## When to stop and ask the user Pause and ask explicitly when: - The user's product, target market, or HS code is ambiguous and `profile get` does not resolve it. - A billable run >$1 estimated cost is about to fire and the user has not confirmed. - Multiple plausible company identities resolve and the user wants the right one before paying for enrichment (use `--strict` on single calls; in bulk, auto-pick proceeds and the user can audit `autoResolved` rows after). - `email send` is about to flush — never send without explicit user "yes" / "send" / "confirm". - The user's framing of "buyers" vs "sellers" is unclear in context. In every other case, decide and proceed. ## Anti-patterns — what not to do - Do not guess product, market, HS code, titles, or send timing. - Do not loop a per-entity command over a list. Use the `--inputs` bulk shape. - Do not enrich contacts on a 300+ raw collection before shortlisting. Shortlist by priority or score first. - Do not treat `extracted` emails or phones as verified or clean. Read the confidence tag. - Do not assume collection rows equal verified contacts — read confidence tags. - Do not present image URLs to the user as text links. Feed images to your host's vision tool. - Do not send email without explicit user confirmation. - Do not blind-retry `INVALID_ARG`. - Do not parse stderr as data. - Do not ask the user "which one?" on every ambiguous bulk row. Auto-pick + audit after. - Do not invent commands not in the surface below. If a command is not listed, it does not exist. - Do not use snake_case args (`hs_code`). Use camelCase (`hsCode`); kebab-case alias also accepted. - Do not invoke `_admin/*` commands. Operator-tier, hidden. ## Recovery playbook - **Collection appears empty mid-run** (`totalCompanies: 0` while `status: running`) → NORMAL. Block on the terminal event per the search-run lifecycle rule above. - **Preview text contradicts intent** → `--direction buyers|sellers` makes the intent explicit. Re-issue the preview before paying. - **Bulk row count smaller than input** → check `failed` count + per-row `status`. Re-run only the failed indices. - **`enrich contacts` returns 0 verified** → fall back to company-level signals + manual review. Do not double down on the same call. - **Source page rot on an image** → re-enrich the company (crawl cache is 100d; image URLs refresh). - **A wrong `autoResolved` pick** → re-run that one row with `--strict` (single) and let the user pick from `alternatives`. ## Minimum viable output per workflow A "good" agent response includes these for each common workflow: - **Buyer discovery** → collection name, total companies, top 5–10 by score, data-quality caveat (drawing on confidence tags), recommended next step (shortlist + enrich). - **Tariff lookup** → corridor (exporter → importer), HS code, duty rate(s), source attribution, notes on remedies if any. - **Company enrichment (bulk)** → row count, ok / autoResolved / failed counts, total cost, 2–3 worth-mentioning highlights (specialty signals, scale signals, image-grounded confirmations), recommended next step. - **Outreach draft** → recipient count, subject + preview of one draft, "ready to send?" confirmation prompt. - **Sanctions check** → hit / no-hit, program (OFAC SDN / SDGT / etc.), alias matched, advisory caveat. ## Tooling expectations eximagent is not a complete workflow tool. Reach outside the CLI for: - **Spreadsheets** for human review of shortlists and exports. CSV-to-NDJSON: `jq -R '{url: .}' < list.txt | eximagent ... --inputs -`. - **Web validation** when an `autoResolved` pick looks suspicious — open the alternative URLs in your host's browser tool. - **Manual review** for contact-extraction noise before any outreach run on extracted contacts. - **Vision tool** for image URLs from any enrich / crawl output (multimodal hosts only). ## Reliability notes - Buyer discovery surfaces some non-buyer pages — confidence tags + shortlist step are the filter. - Extracted emails / phones may be noisy — describe them as candidate data until verified. - Contact enrichment may return 0 verified contacts even on legitimate companies. Fall back to company-level signals. - Long batch operations may have failed rows even when the batch completes — read `failed` count + per-row `status` and re-run the failures with the same `--inputs`. ## Recommended workflow patterns ### Bulk company enrichment from a CSV/spreadsheet ```bash jq -R '{url: .}' company-urls.txt | eximagent enrich company --inputs - # one bulk call, one streamed result, image URLs included per row ``` ### Raw markdown of a list of websites ```bash jq -R '{url: .}' urls.txt | eximagent crawl --inputs - ``` ### Outreach last ```bash eximagent email draft --collectionId "" --brief "" --dry-run eximagent email send --collectionId "" --confirm ``` ## Common chains ### Trade Q&A inline ```bash eximagent tariff --exporter VN --importer DE --product "" eximagent hscode search --query "" eximagent company --name "" ``` ## Realistic worked examples - **Buyers in a target market for a product**, and importers for any other product → `profile get` to ground → `hscode search --query ""` if the HS is unknown → `search run --dry-run` to preview → confirm with the user → `search run --confirmed` → `stream --run-id ` until `complete`, varying only `--product`, `--hsCode` and `--location`. - **Tariff exposure for a product moving between two countries** → `hscode search --query ""` → `tariff --exporter --importer --product ""` → optionally `trade lookup --exporter --importer --hsCode --type all`. - **Multimodal triage of a bulk-enriched list** → `enrich company --inputs companies.ndjson` → for each `row.output.keyFacts.images`, route URLs to host vision tool → answer "which look like specialty roasters" image-grounded. ## Known limitations - `linkedin lookup` works best with canonical LinkedIn company URLs. Auto-resolve from company name or website URL is supported but not always correct — provide a canonical URL when you have one. - `enrich contacts` is collection-scoped; subset is via the row-subset flags inside that collection, not arbitrary cross-collection selection. - Image extraction is URL-only in Phase 1; if the source page rots, re-enrich. - `--strict` is single-input only. - The crawl cache is 100d; for fresher data, the cache key is the canonical URL. - Long bulk runs respect upstream rate limits; concurrency defaults to 10 and reduces under upstream pressure. ## Command surface (auto-generated from REGISTRY) Each entry lists the command, what it does, its flags, and one example. Required and schema-bearing flags carry their full description inline; optional flags show name (+ enum) only — the example shows real usage, and sane defaults apply when omitted. For the complete per-flag detail and every example of a specific command, run `eximagent manifest ` (e.g. `eximagent manifest analytics query`) — it returns just that command's JSON, not the whole tree. Bare `eximagent manifest` returns the full tree (large); prefer the scoped form or `eximagent --help`. ### `eximagent account credits` Explain what a call would cost and which gate would allow or refuse it, before spending anything. Names the credit balance, the daily cap and the concurrency lease separately, because the gate that refuses is rarely the one the caller was looking at. Pass --verb to preflight one command; omit it for the account's standing across every gate. - `--verb` e.g. `eximagent account credits` ### `eximagent account plans` Plans a customer can buy, in ladder order, with allowance and price per interval. e.g. `eximagent account plans` ### `eximagent account standing` Show what this account may spend: the plan held, the entitlements it carries, the credits remaining, the rate version those credits are charged at, and what the allowance excludes. e.g. `eximagent account standing` ### `eximagent analytics anomalies` Surface month-over-month momentum anomalies (spikes/drops) in a corridor before you ask — scope by --dest, --origin, and/or --hs6; flags months whose measure swings past the threshold. The analyst that works while you sleep. - `--dest` - `--hs6` - `--measure` - `--origin` e.g. `eximagent analytics anomalies --dest CO --hs6 090111` ### `eximagent analytics catalog` Get the trade-analytics semantic catalog — the dimensions, measures, filter operators, time windows, and example queries you compose an `analytics query` from. Always read this before composing a query; never guess column names. e.g. `eximagent analytics catalog` ### `eximagent analytics query` Answer analytic questions over the trade corpus. `analytics catalog` lists accepted dimensions, measures and filters; compose a query from it using individual flags (--groupBy --measures --filters --orderBy --descending --limit) or --spec JSON; both are equivalent. Aggregates are dedup-correct over the entire matching corpus, never a row sample. - `--descending` - `--filters — Filters as a JSON array: [{"dim":"dest","op":"=","value":"US"}]` - `--groupBy` - `--having` - `--limit` - `--measures` - `--offset` - `--orderBy` - `--spec — Full AnalyticsQuerySpec JSON (alternative to the flags below; wins if both given). Page the ENTIRE corpus via offset+orderBy — nextActions returns the next-page command: {"groupBy":["hs6"],"measures":["value_usd","shipment_count"],"filters":[{"dim":"dest","op":"=","value":"CO"}],"orderBy":"value_usd","descending":true,"limit":1000,"offset":0}` - `--timeWindow` e.g. `eximagent analytics query --groupBy hs6 --measures value_usd --limit 10` ### `eximagent analytics sql` Run a read-only SELECT when `analytics query` cannot express the question. `analytics catalog` names allowed tables, columns and measures. SELECT-only, table-allow-listed, no table functions, auto-LIMITed and time-bounded; use this for the long tail. - `--limit` - `--offset` - `--querySql (required) — A single read-only SELECT over the trade tables (tradeData + signal tables)` e.g. `eximagent analytics sql --query_sql "SELECT hs_6, count() FROM tradeData WHERE destination_country='CO' GROUP BY hs_6 ORDER BY 2 DESC"` ### `eximagent collection analyze` Rank every company in a collection against a plain-language question. Each rank has a 0-100 score and one-sentence reasoning grounded in persisted evidence (description, match reasons, trade-potential score). Uses structured LLM output and returns ranked items. - `--collectionId (required) — UUID or name of the collection to rank` - `--question (required) — Plain-language analytical question to rank against` - `--rankingCriteria` e.g. `collection analyze --collectionId js7abc --question "Which of these companies look largest?"` ### `eximagent collection clone` Deep-clone a collection (copies all items + custom columns; status reset to draft). - `--name (required) — Name for cloned collection` - `--sourceId (required) — Source collection (name or UUID)` e.g. `collection clone --sourceId --name 'germany-q3-clone'` ### `eximagent collection columns add` Add an AI column to a collection. Per-row cells are populated by a follow-up streaming pass. - `--collectionId (required) — Target collection ID` - `--label (required) — Column label (display)` - `--outputType enum: text|classification|score` - `--prompt (required) — Per-row prompt; agent fills cells using company context` e.g. `collection add-column --collectionId --label 'Reply likelihood' --prompt 'Score 0-100 based on …' --outputType score` ### `eximagent collection columns run` Populate per-row AI cells for an existing column. Sequential call per company; idempotent. - `--collectionId (required) — Target collection ID` - `--columnId (required) — AI column ID (returned by columns add)` e.g. `collection columns run --collection-id --column-id reply-likelihood-l8r2` ### `eximagent collection columns set` Correct one cell by hand, in place, without re-running anything. - `--column (required) — Column to set` - `--itemId (required) — Row to correct, from `collection items list`` - `--value (required) — Corrected value` e.g. `collection columns set --item_id --column product --value "Electric toothbrushes"` ### `eximagent collection copy-to` Hand a collection to another account: every item, column, AI cell and its provenance, as an independent copy they own and edit. Source untouched. Use `share` instead for a frozen, contact-scrubbed view. - `--collectionId (required) — Collection to copy (name or id)` - `--name` - `--to (required) — Account to hand the copy to (must already exist)` e.g. `eximagent collection copy-to --collectionId --to buyer@acme.com` ### `eximagent collection create` Create an empty collection (manual outreach list, no pipeline run). Use this when the user wants to start a list to add companies/contacts to manually, OR bind a default email template, WITHOUT running the search pipeline. For autonomous buyer discovery use `search run` instead. - `--businessType enum: export|import` - `--description` - `--name (required) — Collection name or id` - `--templateName` e.g. `collection create --name q2-buyers` ### `eximagent collection enrich-from-corpus` Fill a collection's companies from the corpus profile they already earned — firmographics plus products, company type, likely HS, certifications and markets as columns. Read from the enrich store: no re-enrich, lineage preserved. - `--collectionId` - `--name` - exactly one of: `--collectionId` / `--name` e.g. `eximagent collection enrich-from-corpus --collectionId ` ### `eximagent collection get` Get a collection by ID — METADATA ONLY (name, totals, priority counts, status). Does NOT return the company rows; for company rows use `exim collection items list`. - `--collectionId (required) — Collection ID` e.g. `collection get --collectionId ` ### `eximagent collection items add` Add a company to a collection. Accepts either an existing companyId OR a companyUrl (auto-upserts into companies on the fly). Use companyName to give the upserted company a display name. Resolve collection by id or name. - `--collectionId` - `--collectionName` - `--companyId` - `--companyName` - `--companyUrl` - `--priority enum: high|medium|low` e.g. `collection items add --collectionId --companyUrl https:// --companyName ""` ### `eximagent collection items filter` Filter rows in a collection: drop rows below minScore / not matching priority / without enriched mails. Idempotent — applies to current rows only. - `--collectionId` - `--minScore` - `--name` - `--priority enum: high|medium|low` - `--requireMail` - exactly one of: `--collectionId` / `--name` e.g. `collection items filter --name germany-q2 --priority high` ### `eximagent collection items list` List the company rows inside a collection — name, score, priority, country, enriched mails, outreach stage. Use this to see WHO is in the collection after a search run. `collection get` returns only metadata (totals + priority counts); this returns the rows. Sorted by score desc. Target with --collectionId (id from search output, or the name) - `--collectionId` - `--column` - `--cursor` - `--limit` - `--name` - `--priority enum: high|low|medium` - `--profile` - `--value` ### `eximagent collection items merge` Merge rows from one collection into another. De-duplicates by companyId. Optionally soft-deletes the source. - `--from (required) — Source collection name (rows copied FROM here)` - `--into (required) — Destination collection name (rows merged INTO here)` - `--removeSource` e.g. `collection items merge --from --into ` ### `eximagent collection items remove` Remove a single row from a collection. Either pass --itemId directly OR pass --collectionId/--collectionName + --companyUrl to look up and remove the row for that company. Use `collection items list` to discover names + URLs. - `--collectionId` - `--collectionName` - `--companyUrl` - `--itemId` e.g. `collection items remove --itemId ` ### `eximagent collection list` List user collections (most recent first). Tombstoned (soft-deleted) collections are excluded by default — pass --includeDeleted true to see them. - `--includeDeleted` ### `eximagent collection pull` Take your own copy of a collection shared with you. - `--name` - `--token (required) — The token from the link you were given` e.g. `collection pull --token ` ### `eximagent collection remove` Soft-delete a collection (tombstoned; mention chips render as deleted). - `--collectionId (required) — Collection ID to soft-delete` e.g. `collection remove --collectionId ` ### `eximagent collection rename` Rename a collection. - `--collectionId (required) — Collection ID to rename` - `--name (required) — New name` e.g. `collection rename --collectionId --name 'germany-q3'` ### `eximagent collection share` Share a collection with a named person or as a public link. Read-only. - `--collectionId` - `--public` - `--redact` - `--with` e.g. `collection share --collectionId --with you@example.com` ### `eximagent collection shared` List collections shared with the caller, excluding collections they own. ### `eximagent collection shares` List the live shares on a collection you own. - `--collectionId` e.g. `collection shares --collectionId ` ### `eximagent collection similar` Find user's existing collections similar to a free-text query. Token-overlap scoring on (name, industry, targetCountry, query). - `--limit` - `--query (required) — Free-text criteria description (industry, region, product, etc.)` e.g. `collection similar --query " importers in "` ### `eximagent collection stats` Aggregate stats for a collection: totals, priority counts, conversion rate, runtime. - `--name (required) — Collection name or id` e.g. `collection stats --name germany-q2` ### `eximagent collection unshare` Revoke one share. Access ends at once; a copy they took stays theirs. - `--grantId (required) — The share to revoke, from `collection shares`` e.g. `collection unshare --grant_id ` ### `eximagent collection update` Update collection metadata (description, businessType, industry, targetCountry) or re-bind its default outreach template. For rename use `collection rename`. For items use `collection items add/remove`. - `--businessType enum: export|import` - `--collectionId (required) — UUID or name of the collection to update` - `--description` - `--industry` - `--targetCountry` - `--templateName` e.g. `collection update --collectionId --description "Q2 push, German roasters"` ### `eximagent companies competitors` Companies that TRADE LIKE this one — the competitive / SIMILAR / lookalike set: importers of the same HS6 into the same reporting country, with shared-HS6 overlap + their volume vs this company (a quick trade-graph similarity; for explained weighted fit scores use profile-match) - `--country` - `--limit` - `--name` - `--taxId` ### `eximagent companies customers` Customers (importers this company sells to): each canonical id, shipments, value, shared HS basket, first/last seen, share of exports — ranked by value - `--country` - `--limit` - `--name` - `--taxId` ### `eximagent companies graph` Local counterparty neighborhood: top suppliers + customers, competitor count, shared-supplier peers, supplier concentration (single-source risk) - `--country` - `--limit` - `--name` - `--taxId` ### `eximagent companies list` Unique resolved companies per reporting country × HS6, each with its trade fingerprint - `--country` - `--dest` - `--from` - `--hs6` - `--limit` - `--name` - `--origin` - `--role enum: exporter|importer` - `--to` ### `eximagent companies profile` Full trade fingerprint for one company: HS basket, corridors, counterparties, recurrence - `--country` - `--name` - `--taxId` ### `eximagent companies similar` Companies SIMILAR to this one (alias of competitors): the lookalike / trade-like set — importers of the same HS6 into the same reporting country, ranked by shared-HS6 overlap + volume - `--country` - `--limit` - `--name` - `--taxId` ### `eximagent companies suppliers` Suppliers (exporters this company buys from): each canonical id, shipments, value, shared HS basket, first/last seen, share of imports — ranked by value - `--country` - `--limit` - `--name` - `--taxId` ### `eximagent companies trajectory` One company's value/shipment time-series by --bucket, with trend + peak months - `--bucket` - `--country` - `--name` - `--taxId` ### `eximagent company` Company URL + profile + summary. - `--address` - `--country` - `--name (required) — Company name` - `--product` e.g. `company --name ""` ### `eximagent company-memory get` Pull the latest outbound + inbound email exchange between the user and a company (PG email + sg_* events). Use with a company name or id to surface negotiation context (last quote subject, last reply snippet). - `--companyId (required) — company UUID of the counterparty` e.g. `company-memory get --companyId ` ### `eximagent company resolve` Knowledge-first company resolution: one cheap LLM call that returns the website it already knows (with confidence 0-2 + reason) and the entity splits — BEFORE any paid web-search/crawl. Call this first; escalate to `enrich company` only when escalate=true (confidence<2 or a split). - `--country` - `--name (required) — Company name (may be a packed multi-entity cell)` - `--products` e.g. `eximagent company resolve --name "Avianca" --country CO` ### `eximagent company shipments` Shipments where a company is exporter or importer - `--limit` - `--name (required) — company name` ### `eximagent company split` Decompose a packed trade-data company-name cell ('A // B', 'C/O', newline-joined) into its distinct legal entities — DBA/trade names stay as one entity. Use before company lookup / sanctions check / enrich when a name string may hold multiple companies. - `--name (required) — The raw company-name cell to split` e.g. `eximagent company split --name "L & S SHRINK SYSTEMS INC. // OXYGEN DEVELOPMENT"` ### `eximagent company verify` Resolve a raw company name to its canonical website/domain — the canonical company identity that collapses name variants. Triage-first (cheap); escalates to a website-verification agent (crawl + reasoning) only when triage is weak. Cached, so repeat verification is free. - `--name (required) — Raw company name to verify` e.g. `eximagent company verify --name "Avianca"` ### `eximagent contacts add` Manually add a contact (employee) tied to a company. Accepts either an existing companyId OR a companyUrl (auto-upserts into companies on the fly). Source tagged as 'manual'. - `--companyId` - `--companyName` - `--companyUrl` - `--department` - `--linkedinUrl` - `--location` - `--mail (required) — Email address` - `--name` - `--phone` - `--title` e.g. `contacts add --companyUrl https:// --companyName '' --mail name@ --name '' --title 'Procurement Manager'` ### `eximagent corridor get` Get a saved trade corridor by name. Returns the full corridor record (exporter, importer, HS code, defaults, tariff snapshot if any). Run `corridor list` first to find the name. - `--name (required) — Corridor name or id (e.g. fr-de-widgets). Discover via `corridor list`.` e.g. `corridor get --name fr-de-widgets` ### `eximagent corridor list` List saved trade corridors (most recent first). ### `eximagent corridor remove` Remove a saved corridor by name. - `--name (required) — Corridor name to remove` e.g. `corridor remove --name ` ### `eximagent corridor save` Save a trade lane (exporter × importer × HS code) as a reusable corridor, referenced later by its name or id and (when starred) surfaced as an empty-chat starter chip. - `--defaultCurrency` - `--defaultIncoterm` - `--exporterCountry (required) — Exporter country — accepts country name, ISO2, ISO3` - `--hsCode` - `--importerCountry (required) — Importer country — accepts country name, ISO2, ISO3` - `--name (required) — Corridor name or id (kebab-case, e.g. fr-de-widgets)` - `--star` e.g. `corridor save --name fr-de-widgets --exporter-country France --importer-country Germany --hs-code 854430 --default-incoterm FOB --default-currency EUR --star true` ### `eximagent corridor yield` Deliverable-contact yield SLO per corridor class (market × goods): deliverable contacts per enriched company, flagging every class below target so low-yield lanes surface as a measured class, not an anecdote. ### `eximagent country resolve` Resolve a country by name/ISO2/ISO3 - `--query (required) — Country identifier: name, ISO2, or ISO3` e.g. `country resolve USA` ### `eximagent crawl run` Fetch raw markdown from a website: the homepage plus relevant internal pages. Reuses the search pipeline cache, so repeats within 100 days are free. Batch with `eximagent --inputs urls.ndjson exim crawl run`; each row is `{"url": "https://..."}`, producing one streamed result for N parallel crawls. - `--url (required) — Website URL to crawl (https://...). Returns the homepage plus a small set of internal pages (about/contact/products if discovered). Cache-aware: 100-day TTL keyed by canonical URL.` e.g. `crawl run --url https://example.com` ### `eximagent duty exposure` Trade-remedy and tariff profile for an HS/origin: effective rate, remedies, protection level - `--dest` - `--from` - `--hs` - `--hs6` - `--limit` - `--origin` - `--source` - `--to` ### `eximagent duty fta` FTA utilization for a market: preference used vs full-duty paid, un-utilized FTA by agreement - `--dest` - `--from` - `--hs` - `--hs6` - `--limit` - `--origin` - `--source` - `--to` ### `eximagent email cancel` Cancel an in-flight email send for a collection. Pipeline checks the cancel flag between drafts; partial sends already flushed cannot be unsent. - `--collectionId (required) — Collection whose in-flight send should be cancelled` e.g. `email cancel --collection-id ` ### `eximagent email draft` Preview personalized outbound emails without sending. Renders ConfirmSendCard with countdown; after confirmation, flushes the same drafts via `email send --confirm` with the same collectionId, brief and sender args. - `--brief (required) — Outreach brief — what to mention, tone, ask` - `--ccMe` - `--collectionId (required) — UUID or name of the collection to draft for` - `--includeLowConfidence` - `--profile` - `--senderEmail (required) — From email (rendered into drafts)` - `--senderName (required) — Sender display name` - `--trackingEnabled` e.g. `email draft --collectionId --brief 'angle for outreach; soft ask' --senderEmail you@x.com --senderName 'Your Name'` ### `eximagent email followup` Schedule a non-responder follow-up. Wraps monitor.create with signal='non-responder-followup'; on fire, agent assembles drafts + ConfirmSendCard before flushing. - `--after (required) — Wait window before firing (cron or 'every Xd' / 'every Xh')` - `--brief (required) — Follow-up brief (what to add in the second touch)` - `--campaignId (required) — Source campaign / collection id` - `--name (required) — Follow-up name or id` e.g. `email followup --campaign-id js7abc --name dach-q2-followup --after 'every 5d' --brief 'gentle nudge with case-study link'` ### `eximagent email history` List sent emails with delivery + engagement counts (delivered / opened / clicked / bounced). - `--collectionId` - `--limit` ### `eximagent email send` Draft and send personalized outbound email per recipient. Preview is the default: without --confirm it never sends. `--confirm` flushes after the signature, sender-email and OFAC guard; failed checks refuse the send and successful sends write records. Emails companies in a collection, not bare addresses; reach one person with contacts add, collection items add, then send. - `--brief (required) — Outreach brief — what to mention, tone, ask` - `--ccMe` - `--collectionId (required) — UUID or name of the collection to email` - `--confirm` - `--dryRun` - `--includeLowConfidence` - `--profile` - `--regenerate` - `--senderEmail (required) — From email (must be operator-owned domain)` - `--senderName (required) — Sender display name` - `--trackingEnabled` e.g. `email send --collectionId --brief 'angle; soft ask' --senderEmail you@x.com --senderName 'Your Name' # preview` ### `eximagent employees filter` Filter persisted contacts of a collection by title / department / verification confidence. Pure in-memory filter over the collection rows that enrich contacts already persisted — never re-discovers, never billable. Recall-first doctrine: discover unfiltered, narrow after. - `--collectionId (required) — UUID of the target collection` - `--confidence enum: extracted|verified` - `--departments` - `--titles` e.g. `employees filter --collectionId js7abc... --departments procurement` ### `eximagent employees rank` Rank persisted contacts by: verified-first (deliverability), seniority (director/VP/head), decision-maker (procurement/buyer roles — the import-PO signer — plus seniority), or engagement-likelihood. In-memory only; never re-discovers, never billable. - `--by (required) enum: engagement-likelihood|seniority|decision-maker|verified-first — Criterion: verified-first, seniority, decision-maker (procurement/buyer + seniority), or engagement-likelihood.` - `--collectionId (required) — UUID of the target collection` - `--limit` e.g. `employees rank --collectionId js7abc... --by decision-maker` ### `eximagent enrich company` Extract a structured company profile from its website: crawl the homepage and internal pages, then extract description, industries, selling and buying products (with HS codes), certificates, expos, trade markers, nearest port, branches and trade potential 0-100. Regex-extract contact emails and phones from crawled markdown. Use --url for a canonical site or --company for web search; the latter runs the plausible site or returns candidates. Cache-aware (100d). - `--address` - `--compact` - `--company` - `--country` - `--product` - `--strict` - `--url` - exactly one of: `--company` / `--url` e.g. `enrich company --url https://` ### `eximagent enrich contact` Re-verify a single employee email via the contact verification provider; fills mail when found. - `--employeeId (required) — Employee ID whose email to re-verify` e.g. `enrich contact --employeeId ` ### `eximagent enrich contacts` Enrich a collection or row subset with decision-maker contacts. Uses free, corpus-resolved named contacts before people-search and verified-email enrichment. Scope with --priority, --limit, --row-ids, --only-with-website or --max-cost-cents (hard cap). Verified-source contacts are verified and deliverable; probable-source contacts are extracted candidates. - `--collectionId (required) — UUID of the target collection` - `--departments` - `--limit` - `--maxCostCents` - `--onlyWithWebsite` - `--priority enum: high|low|medium` - `--rowIds` - `--seniorities` - `--titles` e.g. `enrich contacts --collectionId js7abc... --titles 'procurement manager,buyer'` ### `eximagent evidence show` Show raw evidence for one shipment (source provenance) - `--id (required) — shipment id` - `--normalized` ### `eximagent field capture` Read business cards, booth banners or product sheets into one trade record per person or company — company, what they sell, every contact channel — landing them in a collection when one is named. - `--booth` - `--boothTag` - `--captureSession` - `--collectionId` - `--image (required) — Photograph as base64 bytes; repeat for several` - `--source` - `--tag` e.g. `field capture --image --booth "coffee and tea"` ### `eximagent field pitch` Where a product stands in the market that buys it: leading origins by value and the price per kilo to beat, market resolved from the corpus. - `--booth` - `--hsCode` - `--market` - `--product` e.g. `field pitch --hs_code 090111` ### `eximagent field portrait` What a company already trades, from customs records: products, corridors, counterparties, scale and direction. - `--company (required) — Company name as printed on the card` - `--country` - `--dest` - `--hs6` - `--taxId` e.g. `field portrait --company "OLAM AGRO COLOMBIA" --country co` ### `eximagent hscode resolve` Confirm ONE exact HS code exists and name it; never escalates, so an absent code answers absent rather than searching harder. - `--query (required) — One HS code, digits only, 2 to 10 of them` e.g. `hscode resolve --query 090111` ### `eximagent hscode search` Search HS codes; cascades lexical -> hybrid (embedding) -> llm-expanded as result quality demands. - `--level enum: section|chapter|heading|subheading` - `--limit` - `--query (required) — HS code (digits) or product keyword` e.g. `hscode search --query "frozen shrimp"` ### `eximagent ideal-profile delete` Delete a saved ideal customer profile. - `--name (required) — Profile name` ### `eximagent ideal-profile get` Get a saved ideal customer profile. - `--name (required) — Profile name` ### `eximagent ideal-profile list` List saved ideal customer profiles. ### `eximagent ideal-profile save` Save or update an ideal customer profile with optional custom weights. - `--name (required) — Profile name` - `--profile` - `--weights` ### `eximagent kb add` Register a knowledge-base entry. Accepts either (a) uploaded file via --fileId (from kb upload-url flow), OR (b) text content via --content (no upload needed). Content gets injected into email-gen + profile-extract context. - `--businessType enum: export|import` - `--content` - `--fileId` - `--fileSize` - `--fileType` - `--filename (required) — Display filename` - `--tags` e.g. `kb add --fileId --filename pricing.pdf --tags 'pricing,sales'` ### `eximagent kb get` Get a knowledge-base entry by filename. Returns the full record including tags + metadata + (truncated) content. For a quick excerpt only, use `kb preview`. - `--filename (required) — Filename of the KB entry. Discover via `kb list`.` e.g. `kb get --filename brochure.pdf` ### `eximagent kb list` List user knowledge-base entries. ### `eximagent kb preview` Preview a knowledge-base file: returns a content excerpt + classification (B/L / COO / invoice / etc.) + tags so user can verify content without downloading. - `--kbId (required) — KB entry id` e.g. `kb preview --kb-id ` ### `eximagent kb remove` Delete a knowledge-base entry by ID. - `--id (required) — KB entry ID` e.g. `kb remove --id ` ### `eximagent kb update` Edit a knowledge base entry. Only the fields given change. - `--content` - `--filename` - `--id (required) — Entry id` - `--tags` e.g. `kb update --id --content 'revised terms'` ### `eximagent landed cost` Landed-cost breakdown for an HS/market: FOB, freight, insurance, duties, per-kg and over-FOB - `--dest` - `--from` - `--hs` - `--hs6` - `--limit` - `--origin` - `--source` - `--to` ### `eximagent linkedin lookup` LinkedIn company page batch lookup. Accepts canonical LinkedIn URLs via --urls AND/OR company names or website URLs via --queries — names/URLs auto-resolve to canonical LinkedIn pages via web search before scrape. Cached 100 days per resolved URL. Returns structured profile data keyed by resolved URL plus a resolutions report so the agent can audit which queries mapped to which URL. - `--queries` - `--urls` e.g. `linkedin lookup --urls "https://linkedin.com/company/,https://linkedin.com/company/"` ### `eximagent market anomalies` Trend-relative anomalies for an HS6 market: price-shock and volume-spike periods vs the market's own baseline - `--bucket enum: month|quarter` - `--dest (required) — destination country (ISO 2) — the market whose movement to read` - `--from` - `--hs6 (required) — HS code for the traded market (required) — ANY granularity (2/4/6-digit); a shorter code is a broader family (e.g. 0901 = all coffee). Do NOT pad to 6 digits.` - `--origin` - `--source` - `--to` ### `eximagent market churned` Lapsed importers: active before --since, gone after - `--country` - `--hs6` - `--limit` - `--since` ### `eximagent market compare` Compare one HS6 across destinations side by side: value, price/kg, growth, top buyer — the cross-country arbitrage and sizing view - `--dests (required) — comma-separated destination ISO2 list to compare the HS across` - `--hs6 (required) — HS code for the traded market (required) — ANY granularity (2/4/6-digit); a shorter code is a broader family (e.g. 0901 = all coffee). Do NOT pad to 6 digits.` ### `eximagent market entrants` New importers in a market (country×HS6), no history before --since - `--country` - `--hs6` - `--limit` - `--since` ### `eximagent market growth` Growing importers: recent value over prior by --min_growth - `--country` - `--hs6` - `--limit` - `--minGrowth` - `--since` ### `eximagent market momentum` Rank a destination's HS6 markets by recent-vs-prior growth — the emerging and declining surfaces - `--dest (required) — destination country (ISO 2) to scan for hot/cooling HS6 markets` - `--hs2` - `--limit` - `--source` ### `eximagent market trend` Market time-series for an HS6 into a destination: value, buyers, price, MoM/YoY growth, momentum and a rising/falling/flat verdict - `--bucket enum: month|quarter` - `--dest (required) — destination country (ISO 2) — the market whose movement to read` - `--from` - `--hs6 (required) — HS code for the traded market (required) — ANY granularity (2/4/6-digit); a shorter code is a broader family (e.g. 0901 = all coffee). Do NOT pad to 6 digits.` - `--origin` - `--source` - `--to` ### `eximagent monitor cancel` Cancel (disable) a monitor by name. - `--name (required) — Monitor name to cancel` e.g. `monitor cancel --name germany-replies` ### `eximagent monitor create` Create a recurring signal-based monitor for trade events: reply arrived, non-responder follow-up, new matching company, tariff change or dead domain. For date-based reminders use `reminder create`. - `--description (required) — What this monitor watches` - `--enrollSequenceId` - `--name (required) — Monitor name or id` - `--schedule (required) — Schedule: hourly|daily|weekly or 'every m|h|d|w' (e.g. 'every 6h')` - `--signal (required) enum: reply-arrived|non-responder-followup|new-company-matching-criteria|tariff-change|dead-domain — Signal kind to watch` - `--target (required) — Target identifier (collectionId, criteria JSON, etc.)` e.g. `monitor create --name reply-watch --signal reply-arrived --target campaign-123 --schedule 'every 1h' --description 'Watch for inbound reply'` ### `eximagent monitor get` Get a saved monitor by name. Returns the full record (signal, target, schedule, enabled). Run `monitor list` first if you need to discover the name. - `--name (required) — Monitor name. Discover via `monitor list`.` e.g. `monitor get --name ` ### `eximagent monitor list` List user SIGNAL-BASED monitors (enabled by default). Monitors are recurring, fire on trade events (reply-arrived, tariff-change, etc.). NOT the same as `reminder list` (one-shot date-based). When user asks broadly "what watches/alerts do I have", call this AND `reminder list` to surface both. - `--includeDisabled` ### `eximagent pipeline stop` Cancel a running search and clear busy state. With --runId, cancels that specific run; without it, cancels the user's in-flight run. Marks the run as cancelled. Idempotent — no-op when nothing is running. - `--runId` ### `eximagent policy get` Show the current outreach policy. ### `eximagent policy history` List every saved outreach-policy version (audit trail). ### `eximagent policy set` Set the outreach policy that grounds reply drafting: approved facts, rules (e.g. price_floor), and guardrails (blocked_terms, require_nda_before_coa). Each save bumps the version. - `--facts` - `--guardrails — JSON guardrails, e.g. {"blockedTerms":["guarantee"],"unavailableCerts":["HALAL"],"tone":"warm, concise","requireNdaBeforeCoa":true,"requireSignature":true,"autoSendSafe":false}` - `--rules — JSON rules, e.g. {"priceFloor":10,"signature":"— Phuc, EximAgent"}` e.g. `eximagent policy set --rules '{"priceFloor":10}'` ### `eximagent price shipments` Shipments for an HS code above a weight threshold - `--hsCode (required) — HS code (4/6/8/10 digit prefix)` - `--limit` - `--minWeightKg` ### `eximagent product shipments` Shipments matching an HS code prefix - `--hsCode (required) — HS code (4/6/8/10 digit prefix)` - `--limit` ### `eximagent products add` Save or update a product in the user profile. Image uploaded via products upload-url flow. - `--businessType enum: export|import` - `--category` - `--description` - `--hsCode` - `--imageId` - `--moq` - `--name (required) — Product name or id` e.g. `products add --name 'premium widget A' --moq '5 tons' --description 'industrial grade, custom spec'` ### `eximagent products get` Get a saved user product by name. Returns the full product record (description, HS code, MOQ, category, image). Run `products list` first to discover the name. - `--name (required) — Product name or id. Discover via `products list`.` e.g. `products get --name premium-widget-a` ### `eximagent products list` List user products. - `--businessType enum: export|import` ### `eximagent products remove` Remove a product from the user profile. - `--id` - `--name` e.g. `products remove --name premium-widget-a` ### `eximagent products update` Edit a saved product. Only the fields given change. - `--category` - `--description` - `--hsCode` - `--id (required) — Product id` - `--moq` - `--name` e.g. `products update --id --moq '10 tons'` ### `eximagent profile-match analyze` Extract an ideal-customer profile from one or more reference companies (url/name/text, positive or negative) and save it for ranking. - `--apply` - `--collectionId` - `--name` - `--references — JSON array of references: [{"type":"url|text","value":"...","polarity":"positive|negative"}]` - `--url` e.g. `eximagent profile-match analyze --url https://acme-importer.com --name specialty-coffee` ### `eximagent profile-match lookalike` Find companies that trade like a seed company — same products and source markets derived from real shipment behavior in the corpus, ranked by trade-axis fit. - `--limit` - `--name (required) — Seed company name to find trade-axis lookalikes for` - `--role` e.g. `eximagent profile-match lookalike --name "Acme Coffee Importers" --role importer` ### `eximagent profile-match rank` Rank the companies in a collection by fit against a saved ideal profile, with explained scores. - `--collectionId (required) — Collection of candidate companies to rank` - `--name` - `--references` e.g. `eximagent profile-match rank --collection_id --name specialty-coffee` ### `eximagent profile-match score` Fit-rank ANY list of companies against a saved ideal profile — the general fit primitive: pipe in search results, an enrichment batch, or any hand-built list; each gets an explained fit score + band + matched/missing attributes. Fit is a lens for every surface. - `--companies (required) — Companies to score: JSON [{"name":"...","website":"..."}]` - `--name` - `--references` e.g. `eximagent profile-match score --name specialty-coffee --companies '[{"name":"Acme Coffee","website":"acme.com"}]'` ### `eximagent profile-match search` Search the company corpus and rank hits by fit to a saved ideal profile or inline references — fit-ranking without a pre-built collection. - `--country` - `--market` - `--name` - `--product` - `--query` - `--references` e.g. `eximagent profile-match search --name specialty-coffee --product coffee` ### `eximagent profile-match watch` Turn a saved ideal profile into a self-growing watchlist — a scheduled agent that keeps finding new strong-fit companies and accumulates them in a 'watch:' collection. - `--name (required) — Saved ideal profile name to watch` - `--schedule` e.g. `eximagent profile-match watch --name specialty-coffee --schedule daily` ### `eximagent profile extract` Extract company, product, market, role, language and contact fields from text, a URL crawl or file content. Persists by default (apply=true); use --apply false to preview without writing. - `--apply` - `--content` - `--filename` - `--from (required) enum: text|url|file — Source kind: 'text' = extract from utterance; 'url' = crawl website then extract; 'file' = use provided text content` - `--url` - `--utterance` e.g. `profile extract --from text --utterance "I export industrial fans from France"` ### `eximagent profile get` Read the user's business profile. ### `eximagent profile reset` Reset the current user profile to empty. Irreversible. Use when the user explicitly asks to "start fresh" / "clear my profile" / "reset". Other artifacts (collections, corridors, templates, etc.) are not touched — remove those with their own `remove` verbs. ### `eximagent profile update` Update fields on the user's business profile. Omitted fields are preserved. - `--company` - `--companyDescription` - `--companyLinkedin` - `--description` - `--industries` - `--job` - `--note` - `--preferredLanguage` - `--role` - `--signature` - `--sources` - `--targets` - `--userLinkedin` - `--websites` e.g. `profile update --signature 'Best, / '` ### `eximagent prospects find` Compound lead query: growing importers of an HS6 in a market who buy from a non- supplier, have a deliverable email, and are not already in a collection - `--excludeCollection` - `--growing` - `--hasEmail` - `--hs6 (required) — HS code at ANY granularity — 2/4/6-digit; a shorter code is a broader family (e.g. 0901 = all coffee, 090111 = green coffee). Do NOT pad to 6 digits: 090100 matches nothing.` - `--limit` - `--market` - `--minGrowth` - `--since` - `--supplierNot` e.g. `eximagent prospects find --hs6 090111 --market us --growing --supplier_not br --has_email` ### `eximagent reminder cancel` Cancel a pending reminder by name. - `--name (required) — Reminder name to cancel` e.g. `reminder cancel --name ` ### `eximagent reminder create` Create a one-shot DATE-BASED reminder that fires at a specific ISO timestamp (e.g. "remind me Monday 9am to review the German campaign"). NOT for recurring or signal-based watches — for those use `monitor create`. Use `reminder list` to see saved reminders. - `--description (required) — What to remember` - `--name (required) — Reminder name or id` - `--tz` - `--whenIso (required) — When to fire (ISO 8601, e.g. 2026-05-15T10:00:00Z)` e.g. `reminder create --name --whenIso 2026-05-15T10:00:00Z --description 'Check reply received'` ### `eximagent reminder get` Get a saved reminder by name. Returns the full record (description, fireAt, fired). Run `reminder list` first to discover the name. - `--name (required) — Reminder name. Discover via `reminder list`.` e.g. `reminder get --name ` ### `eximagent reminder list` List pending DATE-BASED reminders (one-shot ISO timestamps; include fired with the flag). Different from recurring trade-signal `monitor list`; when asked for all reminders or watches, call both. - `--includeFired` ### `eximagent reply approve` Send an AI-drafted reply after review. Preview by default; --confirm sends. A suppression check runs first and refuses on a suppressed recipient. - `--confirm` - `--reviewId (required) — Reply review UUID` e.g. `eximagent reply approve --reviewId --confirm` ### `eximagent reply edit` Edit an AI-drafted reply's subject/body before approving; keeps it pending. - `--body` - `--reviewId (required) — Reply review UUID` - `--subject` ### `eximagent reply list` List AI-drafted replies awaiting human review. Replies are grounded in the outreach policy and validated against its guardrails; any violation or handoff signal routes to review. ### `eximagent reply reject` Reject an AI-drafted reply; nothing is sent. - `--reviewId (required) — Reply review UUID` ### `eximagent reply request-rewrite` Ask the AI to redraft a reply, optionally with reviewer guidance; result stays pending. - `--guidance` - `--reviewId (required) — Reply review UUID` ### `eximagent reply show` Surface the most-recent inbound reply for the user (or scoped to a collection). Returns {fromEmail, subject, snippet, summary, receivedAt}. Populated by the inbound-mail webhook. - `--collectionId` ### `eximagent route shipments` Shipments on an origin→destination country route - `--dest (required) — Destination country (ISO 2)` - `--limit` - `--origin (required) — Origin country (ISO 2)` ### `eximagent run cancel` Cancel an in-flight async run, keyed by --runId OR --collectionId, and release its idempotency slot so a fresh run can start. Marks the run terminal (status=cancelled) and refunds the reserved estimate so cancelled work is never billed. No-op-safe: cancelling a run that is already terminal returns NOT_FOUND. After cancel use `run retry` to re-run cleanly. - `--collectionId` - `--runId` - exactly one of: `--runId` / `--collectionId` e.g. `run cancel --collectionId 1f2...` ### `eximagent run retry` Re-run a collection's discovery with a fresh idempotency slot after a previous run finished, failed, stalled, or was cancelled. Cancels any still-running run first (refunding its reserve), then kicks off a new run over the same saved criteria. Pass --newIdempotencyKey to label the retry; the server always allocates a fresh run id so paid work is never duplicated. - `--collectionId (required) — Collection UUID whose discovery to re-run.` - `--newIdempotencyKey` e.g. `run retry --collectionId 1f2... --newIdempotencyKey retry-1` ### `eximagent run status` Current snapshot of an async run, keyed by --runId OR --collectionId. Works on in-flight runs (status=running with partial counts), finished runs (status=completed/cancelled/failed), and stalled runs (status=stalled when the heartbeat has gone silent past the stall threshold). Returns the parent collection name, start/end timestamps, companies processed/expected, percentComplete, reserved/charged/refunded cents, canRetry + retryAfterSeconds, and the priority breakdown when available. This is the same envelope a duplicate Idempotency-Key returns. - `--collectionId` - `--runId` - exactly one of: `--runId` / `--collectionId` e.g. `run status --runId 9b3...` ### `eximagent run summary` Per-run summary card after a search pipeline finishes (or while it is still running). Returns criteria + query + start/finish timestamps + duration + total companies + priority breakdown + model used + status. For the live snapshot of an in-flight run use `exim run status`; this command is the post-mortem view that prints the full criteria back so the agent can re-run with the same shape. - `--runId (required) — Run UUID; finished runs preferred.` e.g. `run summary --runId 9b3...` ### `eximagent sanctions check` Screen a name against the OFAC SDN sanctions list (Specially Designated Nationals). Returns matches with program (e.g. SDGT/IRAN/CUBA) and aliases. Substring + alias match. Always advisory — final clearance lives with the founder/legal. - `--name (required) — Company or person name to screen against OFAC SDN list` e.g. `sanctions check --name ""` ### `eximagent search refine` Preview adding or removing filters from an existing collection's criteria. Returns a refine-preview diff showing added/removed filters and expected company-count delta. After confirmation, invoke `exim search run --confirmed=true` with merged criteria. - `--addExcludeBusinessType` - `--addExcludeLocation` - `--addIndustry` - `--addLocation` - `--collectionId (required) — collection UUID to refine` - `--removeExcludeBusinessType` - `--removeExcludeLocation` - `--removeIndustry` - `--removeLocation` e.g. `search refine --collection-id --add-location "Austria,Switzerland"` ### `eximagent search run` Discover buyer/importer companies, score and rank them, and save them to a Collection. Preview is mandatory: confirmed=false returns criteria; confirmed=true starts the non-blocking run after user confirmation. Emits run progress events. - `--category enum: export|import` - `--confirmed` - `--direction enum: buyers|sellers|export|import` - `--excludeBusinessType` - `--excludeLocation` - `--hsCode` - `--industry` - `--location` - `--name` - `--previewToken` - `--product (required) — Product to find buyers/sellers for` - `--targetBusinessDescription` e.g. `search run --product "" --location Germany --category import` ### `eximagent sequence create` Create a multi-step email sequence (cadenced follow-ups). Steps is a JSON array of {waitDays,subject,body}; templates support {first_name},{company},{sender_name},{signature}. - `--name (required) — Sequence name` - `--senderEmail (required) — From email (operator-owned, verified)` - `--senderName (required) — Sender display name` - `--signature — Signature appended via {signature} in templates` - `--steps (required) — JSON array of steps {waitDays,subject,body,variants?:[{subject,body}]}; variants A/B auto-converge to the best by click-rate` e.g. `eximagent sequence create --name X --senderEmail you@x.com --steps '[{"waitDays":0,"subject":"Hi"}]'` ### `eximagent sequence from-brief` Draft a multi-step sequence from a natural-language brief (AI), saved as draft. - `--brief (required) — Natural-language outreach brief` - `--name` - `--senderEmail (required) — From email (operator-owned, verified)` - `--senderName (required) — Sender display name` - `--signature — Signature appended via {signature}` ### `eximagent sequence from-template` Create a new draft sequence from a saved template. - `--name` - `--senderEmail (required) — From email (operator-owned, verified)` - `--senderName (required) — Sender display name` - `--signature — Signature appended via {signature}` - `--templateName (required) — Template to instantiate` ### `eximagent sequence list` List your email sequences and their status. ### `eximagent sequence metrics` Show a sequence's enrollment counts by status (enrolled/active/replied/completed/suppressed). - `--sequenceId (required) — Sequence UUID` ### `eximagent sequence pause` Pause an active sequence; no further steps send until resumed. - `--sequenceId (required) — Sequence UUID` ### `eximagent sequence start` Activate a sequence and enroll a collection's recipients. Preview by default; --confirm sends real emails on schedule. A reply from a recipient auto-stops their remaining follow-ups. - `--collectionId (required) — Collection UUID to enroll` - `--confirm` - `--sequenceId (required) — Sequence UUID` e.g. `eximagent sequence start --sequenceId --collectionId --confirm` ### `eximagent sequence template-from-trade` Generate a sequence template from a seed company's real customs trade pattern (AI), saved as a reusable trade-seeded template — outreach copy tuned to how companies like the seed actually trade. - `--name (required) — Template name` - `--seedCompany (required) — Seed company whose corpus trade pattern seeds the template` ### `eximagent sequence template-save` Save a reusable sequence template (named set of steps) for fast sequence creation. - `--name (required) — Template name` - `--steps (required) — JSON array of steps {waitDays,subject,body,variants?:[{subject,body}]}; variants A/B auto-converge to the best by click-rate` ### `eximagent sequence templates` List your saved sequence templates. ### `eximagent sequence validate` Check a sequence is launch-ready (sender set, steps present, policy configured). - `--sequenceId (required) — Sequence UUID` ### `eximagent share create` Create an anonymous, expiring, read-only share link for a result — a collection, an analytics query (--spec), or a lookalike ranking (--name/--references over a collection). Hand a non-technical user (or their boss) an instant no-login preview; contact PII is redacted by default. - `--collectionId` - `--collectionName` - `--expiresHours` - `--maxViews` - `--name` - `--passcode` - `--references` - `--spec` - `--title` e.g. `share create --collectionId --expiresHours 48` ### `eximagent share list` List your share snapshots (token, kind, views, expiry). ### `eximagent share revoke` Revoke a share link immediately (the /s/ page 404s at once, before expiry). - `--token (required) — share token to revoke` e.g. `share revoke --token ` ### `eximagent share set-expiry` Change a share link's expiry (hours from now, max 720). - `--expiresHours (required) — hours from now until expiry` - `--token (required) — share token` e.g. `share set-expiry --token --expiresHours 168` ### `eximagent share set-passcode` Set or clear a share link's passcode (omit --passcode to clear). - `--passcode` - `--token (required) — share token` e.g. `share set-passcode --token --passcode newcode` ### `eximagent shipments buyer-recurrence` Buyer recurrence cohort: repeat buyers, retention and shipment scale - `--buyer` - `--dest` - `--from` - `--hs6` - `--origin` - `--source` - `--to` e.g. `eximagent shipments buyer-recurrence --hs6 090111 --dest DE` ### `eximagent shipments get` Get one shipment by deterministic id - `--id (required) — shipment id` - `--view enum: summary|detail|logistics|financials|parties|evidence` ### `eximagent shipments market-signals` Market-level demand, concentration and momentum signals for an HS or destination - `--dest` - `--from` - `--hs6` - `--origin` - `--source` - `--to` e.g. `eximagent shipments market-signals --hs6 090111 --dest DE` ### `eximagent shipments price-trend` Monthly price-per-kg trend, dispersion and concentration shift - `--dest` - `--from` - `--hs6` - `--origin` - `--source` - `--to` e.g. `eximagent shipments price-trend --hs6 090111 --dest DE` ### `eximagent shipments route-signals` Origin to destination route signals ranked by traded value - `--dest` - `--from` - `--hs6` - `--limit` - `--origin` - `--source` - `--to` e.g. `eximagent shipments route-signals --hs6 090111 --dest DE` ### `eximagent shipments search` Search shipments across all dimensions - `--dest` - `--hs6` - `--hsCode` - `--limit` - `--name` - `--origin` e.g. `eximagent shipments search --hs6 090111 --dest DE --limit 50` ### `eximagent stats show` Conversational analytics on sent emails: reply-rate / open-rate / click-rate / bounce-rate computed from PG email + sg_* event tables. Optionally scope to a single collection. - `--collectionId` - `--limit` ### `eximagent suppression add` Manually suppress an email from all outreach. - `--email (required) — Email address to suppress` ### `eximagent suppression list` List manually suppressed emails. ### `eximagent suppression remove` Remove a manual suppression. - `--email (required) — Email address to un-suppress` ### `eximagent tariff` Get the effective duty rate and remedies for a trade corridor. --product is optional; omit it for a corridor overview. For landed-cost and duty exposure by HS6 and date window, use `landed cost --hs6 --dest --from `. - `--exporter (required) — Sending country — accepts country name, ISO2, ISO3 (e.g. "Viet Nam", "VN", "VNM")` - `--importer (required) — Receiving country — accepts country name, ISO2, ISO3 (e.g. "Germany", "DE", "DEU")` - `--product` e.g. `tariff --exporter --importer --product ` ### `eximagent template add` Save or update an email template. Variables (e.g. {{name}}, {{company}}) auto-extracted; attachment file-IDs auto-resolved. - `--businessType enum: export|import` - `--category` - `--content (required) — Template body. Supports {{variable}} placeholders + [FILE_ID:storageId:filename] attachment markers.` - `--name (required) — Template name or id` - `--subject (required) — Subject line (may use {{variables}})` e.g. `template save --name cold-intro --subject 'Partnership intro' --content 'Hi {{name}}, ...'` ### `eximagent template create` Save or update an email template. Variables (e.g. {{name}}, {{company}}) auto-extracted; attachment file-IDs auto-resolved. - `--businessType enum: export|import` - `--category` - `--content (required) — Template body. Supports {{variable}} placeholders + [FILE_ID:storageId:filename] attachment markers.` - `--name (required) — Template name or id` - `--subject (required) — Subject line (may use {{variables}})` e.g. `template save --name cold-intro --subject 'Partnership intro' --content 'Hi {{name}}, ...'` ### `eximagent template delete` Delete a saved email template by name. Irreversible. NOT for cancelling a draft batch — for that the draft layer has its own flow. - `--name (required) — Template name to delete. Discover via `template list`.` e.g. `template remove --name ` ### `eximagent template edit` Update a saved template. Pass only fields to change; others stay. - `--category` - `--content — New body (replaces if provided; supports {{variables}} + [FILE_ID:...] markers)` - `--name (required) — Template name or id` - `--subject — New subject (replaces if provided; supports {{variables}})` e.g. `template edit --name cold-intro --content 'Hi {{name}}, ...'` ### `eximagent template generate` Generate an email template (subject + body with {{variables}}) from a free-text prompt. Caller saves via `template save`. - `--prompt (required) — Prompt: tone + intent + product + audience (e.g. "casual cold outreach for B2B importers")` e.g. `template generate --prompt 'casual cold-outreach for first contact, B2B importers'` ### `eximagent template get` Read a saved template by name (full content + variables + attachments). - `--name (required) — Template name or id` e.g. `template recall --name cold-intro` ### `eximagent template list` List the user saved email templates. ### `eximagent template recall` Read a saved template by name (full content + variables + attachments). - `--name (required) — Template name or id` e.g. `template recall --name cold-intro` ### `eximagent template remove` Delete a saved email template by name. Irreversible. NOT for cancelling a draft batch — for that the draft layer has its own flow. - `--name (required) — Template name to delete. Discover via `template list`.` e.g. `template remove --name ` ### `eximagent template save` Save or update an email template. Variables (e.g. {{name}}, {{company}}) auto-extracted; attachment file-IDs auto-resolved. - `--businessType enum: export|import` - `--category` - `--content (required) — Template body. Supports {{variable}} placeholders + [FILE_ID:storageId:filename] attachment markers.` - `--name (required) — Template name or id` - `--subject (required) — Subject line (may use {{variables}})` e.g. `template save --name cold-intro --subject 'Partnership intro' --content 'Hi {{name}}, ...'` ### `eximagent trade lookup` Search trade data: tariffs, duties, remedies, NTM measures by importer/exporter/product. - `--exporter (required) — Sending country — accepts country name, ISO2, ISO3 (e.g. "Viet Nam", "VN", "VNM")` - `--hsCode` - `--importer (required) — Receiving country — accepts country name, ISO2, ISO3 (e.g. "Germany", "DE", "DEU")` - `--limit` - `--product` - `--type enum: all|duties|taxes|remedies|ntm` - exactly one of: `--hsCode` / `--product` e.g. `trade lookup --exporter US --importer VN --hs-code 730441` ### `eximagent usage` Show today's metered spend for the calling account: net charged cents (after refunds), the portion settled on real LLM/vendor usage, estimated, refunded, against the daily cap. e.g. `eximagent usage` ### `eximagent watch company` Register a proactive watch on a company (by tax id + country, or name); alerts when it imports again or its supplier set changes. - `--country` - `--event enum: imports|supplier-change` - `--name` - `--taxId` ### `eximagent watch list` List the caller's active proactive watches with their ids. ### `eximagent watch market` Register a proactive watch on a market (HS6 × destination); alerts on new-entrant, price-shock, volume-spike, or momentum events as the corpus updates. - `--dest` - `--event enum: new-entrant|price-shock|volume-spike|momentum` - `--hs6` ### `eximagent watch remove` Remove (disable) one of the caller's watches by id. - `--id (required) — watch id to remove` ### `eximagent whoami` Show which account this session is acting as: userId, workspace, tier, and auth mode. Anything written (collections, contacts, outreach) is owned by this account. e.g. `eximagent whoami`