# Dromad for coding agents

This page is for an agent asked something like *"Investigate our visibility for AI search APIs."* Dromad gives you evidence — what ChatGPT, Claude and Gemini answered, what they cited, how pages look to a crawler — and counts it. Interpreting it is your job: Dromad does not explain, recommend or score.

Every command accepts `--json` and prints one JSON document on stdout: the same object the API returns. Use `--json` whenever you will read the output programmatically.

With `--json`, that document is the whole output. stdout carries exactly one JSON document — the result, or the error envelope — and stderr carries nothing: no progress, no cost line, no balance. So `dromad questions "<topic>" --json > out.json 2>&1` leaves a file that parses. What the progress lines say without `--json` is in the document (`charge`) and in `dromad balance --json`. The one exception is `dromad login`, which prints the sign-in URL and code on stderr because a person has to act on them. Read the exit code as well as the document: it is listed under [Reading the JSON](#reading-the-json).

## Check the CLI version

```sh
dromad --version
```

The server answers `dromad` 0.2.0 and newer. An older CLI is refused on every command with `cli_outdated` (exit code 2): nothing was run or charged, and the message gives the installed version, the required one and the upgrade command (`uv tool upgrade dromad` or `pip install -U dromad`). This matters because commands have changed meaning: before 0.2.0, `dromad fanout` generated questions; it now runs prompts on every engine, and `dromad questions` generates questions. If a command here is missing from `dromad --help`, the CLI is old: upgrade before doing anything else, and do not guess at an older command's meaning from this page.

## Authenticate

Check first:

```sh
dromad whoami --json
```

It fails with `not_authenticated` (exit code 2) when there is no credential, and with `unauthenticated` (exit code 1) when the server refuses the one there is, because it was revoked or is unknown. Either way, ask the user either to run `dromad login` (opens a browser) or to create an API key at https://dromad.dev/account/api-keys and set `DROMAD_API_KEY`. Never print a key.

**Check who, not only whether.** When it succeeds, read `email`: a credential existing does not mean it is the account the user means, and the account is the one whose workspace pays. `dromad login` prints a code and a URL, and the token belongs to **whichever account the browser session that approves the code is signed in to**. The CLI cannot know that account beforehand, and a user with several browser profiles can approve from the wrong one without noticing. So:

- If the user has named an account, sign in with `dromad login --email <address>`. It runs the same browser flow, and if another account approves it, it stores nothing, revokes the token that was issued and fails with `account_mismatch` (exit code 1), leaving any earlier credential as it was. Then ask the user to open the URL in the browser profile that is signed in to that address, and run it again.
- After any sign-in, confirm `dromad whoami --json` shows the `email` you expect before spending anything. `login` also prints `Logged in as <email>.`
- An API key has no such ambiguity: it is made in one account, for one workspace. On a machine with several browser profiles, prefer `DROMAD_API_KEY`.

## Check the project

Work is filed under a project, and the directory you are in is normally linked to one:

```sh
dromad status --json
```

It prints the workspace, the project and the project's context (its domain, brands and competitors, which tell you what the work is about). It fails with `not_linked` (exit code 2) when the directory has no project. Then ask the user which project to use, or whether to start one, and run `dromad link --project <pr_…>` or `dromad init --name "<name>" --workspace "<workspace>"`; `dromad list --json` shows the choices. Both take flags for everything and ask nothing under `--json`. Do not invent a project for the user's work without asking: the workspace it is in is the one that pays. `dromad unlink` forgets the link.

## Commands, and when to use them

| Goal | Command | Cost |
|---|---|---|
| See what each engine actually searches for a prompt | `dromad fanout "<prompt>" --runs 3 --yes --json` | its prompt runs: prompts × engines × runs (`--estimate` prints it first) |
| Generate questions people might ask about a topic | `dromad questions "<topic>" --json` | $0.002 a question generated |
| See what the engines answer and cite for a question | `dromad prompt "<question>" --json` | $0.05 ChatGPT, $0.20 Claude, $0.07 Gemini; $0.32 on all three, the default |
| Count citations across many runs; check a domain's coverage | `dromad citations <rq_…/dr_…> --domain <d> --brand <b> --json` | free |
| Inspect one page as a crawler sees it | `dromad audit <url> --json` | $0.01 |
| Find earlier work | `dromad runs list --json`, `dromad fanouts list --json`, `dromad analyses list --json` | free |
| Read one object in full | `dromad runs show <dr_…> --json`, `dromad fanouts show <fo_…> --json`, `dromad analyses show <fa_…/ca_…> --json` | free |
| Give the user a link | `dromad share <id>` (and `dromad unshare <id>`), `dromad open <id> --print` | free |
| Check the credential, and whose it is | `dromad whoami --json`, `dromad login --email <address>`, `dromad logout` | free |
| Check or choose the project | `dromad status`, `dromad list`, `dromad link`, `dromad init`, `dromad unlink` | free |
| See what each operation costs | `dromad pricing --json` | free |
| See what credit is left before a large batch | `dromad balance --json` | free |

## How the commands compose

A typical investigation:

1. **Questions.** `dromad questions "AI search API" --json` returns `questions` (each with `id`, `text`, `cluster_id`, `intent`, `specificity`) and `clusters`. Choose the questions that matter for the user's goal — for visibility, the ones where an answer would name providers (discovery, comparison, pricing). These are generated, not observed: a list of prompts worth trying.
2. **Fanout (when the question is what engines search).** `dromad fanout "<prompt>" "<prompt>" --runs 3 --yes --json` records the search queries each engine issued for each prompt, run by run. Use it to learn the vocabulary engines search with before deciding what a page should answer.
3. **Prompt runs.** Run each chosen question: `dromad prompt "<question>" --json`. Keep the batch ID from `.batch.id`. To start several without waiting, use `--no-wait` and collect the IDs; `dromad runs list --batch <rq_…> --json` shows their progress.
4. **Citations.** Pass all the batch IDs to one analysis: `dromad citations rq_… rq_… --domain their-site.com --domain competitor.com --brand Their --brand Competitor --json`. Read `domain_coverage` (cited runs and prompts per domain, per engine, the pages cited, and the prompts where the domain was absent or seen but not cited), `domains`, `pages` and `overlap`. An analysis counts answers already stored, and finished runs never change: running it again over the same runs with the same domains and brands gives the same numbers and only adds a duplicate. Before creating one, check `dromad analyses list --kind citations --json` and read an existing one with `dromad analyses show <ca_…> --json`. Create a new analysis only when the question changed (other domains or brands), the runs changed (new prompt runs, or a different subset such as one engine), or the analysis predates a change to how Dromad counts (its `method`; the current one is `citations/v2`). To learn whether anything changed in the engines themselves, run new prompts; no analysis of old runs can show it.
5. **Audits.** Audit the user's pages the engines cited, the ones they skipped, and the competitor pages they cited most: `dromad audit <url> --json`.
6. **Report.** Combine the evidence with your own reasoning and the user's repository. Link the analysis (`dromad share ca_…`) so the user can check the numbers.

## Reading the JSON

- **Prompt:** `{"batch": {...}, "runs": [...]}`. In each run, `result.answer` is the text and `result.sources[]` has `url`, `domain`, `cited` (`true`/`false`/`null`), `citation_count` and `citation_positions`. `null` means the provider does not expose it: never count it as "not cited".
- **Questions, citations:** the analysis document, with results at the top level (`.questions`, `.domain_coverage`).
- **Fanout:** `.prompts[].engines[].runs[].queries[]`. Each query has `text` and `search_call`: searches are in the provider's order, and queries with the same `search_call` were issued together, with no order among them. Nothing is merged across runs, so the same `text` under two runs is two observations and `query_count` counts observations; when you compare runs or engines, say how many runs a query appeared in rather than treating the union as one search. A run with no queries has `searched` `true` (the provider hid the text), `false` (it did not search) or `null` (not recorded): never read `null` as "did not search". Each `run_id` is a prompt run with the answer, sources and raw envelope.
- **Audit:** `dromad audit --json` prints the audit result with `run_id` at the top (`.findings[]`, `.content`, `.metadata`); `dromad runs show <dr_…> --json` prints the run with the same result under `.result`. Each finding has `key`, `status` (`pass`/`fail`/`not_applicable`, or `error` when the check itself failed — never read that as a pass), `severity` and `evidence_level`. Weigh `suggestive` and `experimental` findings accordingly.
- **Errors:** `{"error": {"code", "message", "engine", "retryable"}}`. Retry only when `retryable` is true.
- **Exit codes:** `0` done (a prompt where some engines failed is still `0`, its batch `partial`), `1` the operation failed, `2` it could not be attempted (arguments, credentials, no project, a CLI too old, a fanout that needed `--yes`).

The full schema is at [https://dromad.dev/api/v1/openapi.json](https://dromad.dev/api/v1/openapi.json).

## Spend the user's credit with care

- **Look before you run.** `dromad runs list --json` and `dromad analyses list --json` show earlier work. A finished run never changes; reuse it instead of re-asking the same question.
- **Choose engines.** A prompt is priced per engine. `--engines chatgpt` is $0.05 where all three are $0.32, when one engine answers the question you have.
- **Choose questions.** `dromad questions` gives 30 by default; running all of them on three engines is 30 × $0.32 = $9.60. Pick the ones that bear on the goal, say which you picked, and say what the batch will cost before you run it.
- **A fanout multiplies.** It costs prompts × engines × runs prompt runs, and `--runs` is the multiplier: 3 is enough to see whether an engine searches the same way twice, 5 is the most allowed. Run `dromad fanout … --estimate --json` first and tell the user the estimated total; it also reports `available_usd` and whether that is `sufficient`. Without a terminal the command starts nothing unless you pass `--yes`: it fails with `confirmation_required` (exit code 2). The estimate is informational, not a quote: the fanout starts only if the workspace's credit covers all of it when it is created, and otherwise fails whole with `insufficient_credit`. One fanout queues at most 30 prompt runs (a queue limit, not a spending one): split more prompts across several fanouts.
- **Check the balance first.** `dromad balance --json` gives `available_usd`. A request the credit does not cover fails whole with `insufficient_credit` and charges nothing; it will not succeed until the user adds credit at the URL in the message, so ask them rather than retrying.
- **Citation analyses are included.** Re-slice existing runs with new `--domain` and `--brand` values rather than running new prompts.
- **A failed run costs nothing, but don't retry blindly.** It records its error; check `retryable` first.
- **Mind the account's limits.** `rate_limited` means the account has too many runs unfinished. Wait for runs to finish instead of retrying in a loop.

## What not to claim

Dromad's numbers come from specific runs at a specific time; engines change, and the same question can be answered differently tomorrow. Report counts with their denominators ("cited in 9 of 24 runs"), say which engines and questions they come from, and keep "unknown" distinct from "no". Brand counts are naive whole-word matches. Audit findings describe one fetch of one page.
