Install
One line, no account, no key, no config.
npx @whereamimentioned/cli site check-crawlers example.comTo keep it around, install it once. That puts wam on your PATH, with wamctl as an alias for machines where wam is already taken.
npm install -g @whereamimentioned/cli
wam --helpAlways use the scoped package name, @whereamimentioned/cli. The unscoped wam on npm is an unrelated package. Requires Node 20.19 or newer. This CLI sends no telemetry: it makes no request beyond the API calls that serve the command you ran, plus an update check against the npm registry that WAM_NO_UPDATE_NOTIFIER turns off.
Who it is for
The developer in CI. You have a site, a workflow and one question: did the content change we just shipped make our brand harder to find in AI answers? You want a step that prints machine-readable output and exits non-zero when a number crosses a line, and you will never open the dashboard during a build.
The founder in a terminal. You already live in a shell. You want wam scan run --wait, a progress line and a score, without a browser. You also want the site checks before you have an account at all, because that is the cheapest possible first look at the product.
Both get one binary, one credential store and one output contract: stdout is data, stderr is chatter, and --json is a stability guarantee rather than a debug convenience, so wam report get --json | jq .score works with no 2>/dev/null.
Site checks, no account
Seven commands work with no account, no key and no config, straight from a cold npx. They spend no credits.
| Command | What it tells you |
|---|---|
| wam site check-crawlers <url> | which AI crawlers your robots.txt lets in |
| wam site validate-llms-txt <url> | score and findings for an existing llms.txt |
| wam site generate-llms-txt <url> [--full] [-o <file>] | crawl the site and write an llms.txt |
| wam site generate-sitemap <url> [-o <file>] | crawl the site and write a sitemap.xml |
| wam site validate-schema <url> | validate the page's structured data |
| wam site check-meta <url> [--keyword <keyword>] | check the page's meta description |
| wam site analyze-chunks <url> | how the page's content chunks for retrieval |
Every URL is fetched by the server, never by the CLI, so the checks see your site from the public internet and not from inside your network. generate-llms-txt and generate-sitemap write the generated file to stdout, so > llms.txt works, and put every count and notice on stderr; with -o they write the file and print a summary instead. Add --json to any of them for the full structured result. The same seven checks are the no-account tools of the MCP server.
Sign in
wam auth login # browser, loopback on your own machine (default)
wam auth login --device # SSH, containers, WSL, Codespaces
wam auth login --token # paste a key you minted in the dashboard
wam auth status # what key, which organization, where it is stored
wam auth logout # revoke on the server, then clear locallyThe default flow opens your browser at whereamimentioned.com. You pick an organization and approve, and the key comes back to the CLI over a loopback port on your own machine: a PKCE code exchange with no client secret and nothing to paste. --device is for SSH sessions, containers, WSL and Codespaces, where no browser can reach back to the machine; it prints a URL and a short code, and you enter the code in a browser anywhere. --token reads a key you already minted in the dashboard (Settings > API keys), from stdin or a prompt.
The key is stored in your operating system's credential store (Keychain, Credential Manager, or Secret Service on Linux), with a permission-checked file as the fallback where no store is available. wam auth status always says which one is in use. The CLI never prints the token.
CI never calls wam auth login. It sets WAM_TOKEN and nothing else.
Two kinds of key
An organization key reaches one organization. It is what wam auth login mints by default, it is what the dashboard mints, and it is the key to put in CI: a secret that is read by every job and every collaborator should reach exactly one organization.
An account-wide key reaches every organization you belong to, today and any you join later, and it is the only kind that can create a new one. That makes it a bigger secret, so keep it for a human's own shell: wam auth login --account-wide spells out what it reaches and asks before the browser opens. With one, you name the organization per command with --org, WAM_ORG or wam config set org.
The safety property is worth one sentence: an account-wide key gives you no more permission anywhere, only more places to use it, and in each of them you are whatever you already are. If you are a viewer in an organization, the key is a viewer there. If you are removed from an organization, the key loses it on the next request, with nothing to rotate.
API keys, the CLI and the API are included on every paid plan; the site checks need no key at all.
Scans and reports
wam scan estimate # cost and balance, spends nothing
wam scan run --wait # estimate, confirm, run, follow
wam scan run --wait --yes --fail-under 55 --json # the CI form
wam report get --json | jq .score
wam scan evidence <scanId> # the answers behind the numberswam scan run always estimates first, prints the cost, asks, and only then starts the scan. --wait follows it to the end with a progress line; --fail-under exits 7 when the resulting AI visibility score is below your threshold, on scan run and on report get alike. Every command also has organization, workspace, competitor and prompt counterparts, and wam <group> --help lists them with a copy-pasteable example each.
A committed wam.json pins the organization, the workspace and the threshold for a repo, so every checkout and every CI run agree. An unknown key in it is a usage error, not a silent ignore, and it has no field for a token. It cannot set the server either: a committed file must not decide where your key is sent, so apiUrl lives in the user config (wam config set apiUrl), in WAM_API_URL or on --api-url, and is refused in wam.json.
{
"$schema": "https://whereamimentioned.com/schema/wam.config.json",
"org": "acme",
"workspace": "acme-eu",
"failUnder": 55
}In CI
A weekly scan that fails the job when the score drops below a line, keeps the report as an artifact, and cannot spend credits by accident.
# .github/workflows/ai-visibility.yml
name: AI visibility
on:
schedule:
# Weekly, Monday 07:00 UTC. See the cost note below before making this more frequent.
- cron: "0 7 * * 1"
workflow_dispatch:
# Prevents two scheduled runs overlapping. The API would 409 anyway, but this is cheaper.
concurrency:
group: ai-visibility
cancel-in-progress: false
jobs:
scan:
runs-on: ubuntu-latest
timeout-minutes: 45
permissions:
contents: read
steps:
- uses: actions/setup-node@v7
with:
node-version: 22
# Fail early and cheaply if the balance cannot cover a scan. Costs nothing.
- name: Check the balance
env:
WAM_TOKEN: ${{ secrets.WAM_TOKEN }}
WAM_ORG: acme
WAM_WORKSPACE: acme-eu
run: npx --yes @whereamimentioned/cli@0.1 scan estimate --json
- name: Run the scan
id: scan
env:
WAM_TOKEN: ${{ secrets.WAM_TOKEN }}
WAM_ORG: acme
WAM_WORKSPACE: acme-eu
run: |
npx --yes @whereamimentioned/cli@0.1 scan run \
--wait --yes \
--fail-under 55 \
--timeout 2400 \
--json | tee scan.ndjson
tail -n 1 scan.ndjson > scan-result.json
- name: Fetch the full report
if: always() && steps.scan.outcome != 'skipped'
env:
WAM_TOKEN: ${{ secrets.WAM_TOKEN }}
WAM_ORG: acme
WAM_WORKSPACE: acme-eu
run: npx --yes @whereamimentioned/cli@0.1 report get --json -o report.json
- uses: actions/upload-artifact@v4
if: always()
with:
name: ai-visibility
path: |
scan.ndjson
report.jsonWhat that workflow does on purpose:
WAM_TOKENis an organization key, not an account-wide one. Nothing on the server can enforce that; it is a rule for whoever mints the secret.@0.1pins the minor. While the package is0.x, a breaking change to the output lands in the minor, so@0would protect nothing. Move the pins to@1on the day1.0.0ships.--yesis mandatory. Without it,scan runrefuses in a non-interactive context and exits 2, so no workflow spends credits by accident.- The estimate step runs first and exits 4 when the balance is short, before anything is charged.
--timeout 2400is below the job'stimeout-minutes. On timeout the CLI exits 1 with the scan id and the scan keeps running server side, so the next run can still read the report.--fail-under 55is an example threshold. Put yours on the flag or inwam.json.- A retried step does not start a second scan. The idempotency key is derived from the organization, the workspace, the date and the CI run id, so a re-run follows the scan that already exists.
Cost rules
A scan spends real credits, and a CLI is the surface most likely to run one in a loop, so the CLI carries the most conservative rules of any surface in the product.
- Estimate before spend, always.
wam scan runasks the server for the cost first, even with--yes, and prints it before anything is charged, so it is in the CI log whether or not anyone confirmed. - A confirmation is a real one. The default answer is no; anything other than
yoryes, including an empty line, cancels with exit 0 and no credits spent. - Non-interactive without
--yesis a hard failure, exit 2, with nothing spent. - There is no flag for scan mode or repetitions, and there never will be. Every scan started from the CLI runs your plan's default; the one-off choices that cost more are made by a person in the dashboard, not by a script in a loop.
--fail-undernever triggers a rescan. There is no--retryand no automatic re-run anywhere in the CLI. Every scan is started by exactly one explicit command.- Read commands are free:
scan estimate,scan status,scan list,scan evidence,report get,prompt list,competitor list,org list,workspace listand everywam sitecommand spend zero credits. wam org createandwam workspace createspend nothing either, and they still confirm, because neither can be undone from the CLI.- When the balance is short, the CLI prints what the scan needs, what the organization has and the billing page, and exits 4. It does not retry, does not offer to buy credits, and does not suggest a smaller scan.
- As a ceiling that does not depend on the CLI behaving, a key may start at most 3 scans per hour. The server names the cap and when it resets.
Installing the MCP server
The CLI is the installer for the whereamimentioned MCP server, because no single config snippet works everywhere: the top-level key and the transport name differ per client.
wam mcp install --client cursor
wam mcp install --client vscode --print # show the snippet, write nothing
wam mcp url # the endpoint URL for this org and workspace--client accepts claude-code, cursor, vscode, windsurf, zed, warp, opencode, amp and jetbrains. The command merges the whereamimentioned entry into the client's existing config, preserves every other key, and writes a backup next to the file first. A project-scoped file never receives the raw token: it gets ${WAM_TOKEN} and a reminder to export it. Only --global writes the literal token, and only after you confirm. --print writes the snippet to stdout and touches nothing. wam mcp url prints the endpoint URL for the current organization and workspace, and nothing else, so it composes.
Exit codes
Every command exits with one of these, and wam --help prints the same table. Nothing above 125 is used, because the shell reserves 126, 127 and 128 and above.
| Code | Meaning | Typical cause |
|---|---|---|
| 0 | Success | |
| 1 | Generic failure | Unexpected server 500, unhandled condition |
| 2 | Usage error | Unknown flag, missing argument, bad value, no workspace resolved, no org resolved on an account-wide key |
| 3 | Auth required or expired | No credential, a credential the server no longer accepts (revoked or expired), insufficient scope, insufficient role, tier gate |
| 4 | Insufficient credits | HTTP 402 from the scan-create route |
| 5 | Rate limited | HTTP 429 |
| 6 | Scan failed | The scan reached status "failed" while being followed |
| 7 | Threshold not met | --fail-under was set and the score was below it |
| 8 | Scan already running | A 409 scan_already_running and --no-follow was passed |
| 9 | Conflict | The server rejected the request as conflicting (409 conflict): the priced terms moved, or an Idempotency-Key was reused with different terms |
| 10 | Ceiling reached | A 409 conflict at a limit that waiting does not clear: the plan's workspace cap, or the 25 organizations one account may own |
| 11 | No paid organization | A 403 feature_not_available from wam org create, because the account owns no organization on a paid plan |
Environment variables
CI sets WAM_TOKEN and, usually, WAM_ORG and WAM_WORKSPACE. Credential precedence is the --token flag, then WAM_TOKEN, then the credential store or file for the active profile. Prefer the variable over the flag: arguments leak into shell history and process listings.
| Variable | Meaning |
|---|---|
| WAM_TOKEN | The API key. Highest-precedence credential source. |
| WAM_API_URL | API origin override. Default https://whereamimentioned.com. |
| WAM_ORG | Org slug override. |
| WAM_WORKSPACE | Workspace slug override. |
| WAM_PROFILE | Credential and config profile name. Default: default. |
| WAM_CONFIG_DIR | Override the user config and credential directory wholesale. |
| WAM_NO_UPDATE_NOTIFIER | Any non-empty value disables the update check. |
| WAM_MCP_URL | Only read by wam mcp url and wam mcp install, for staging. |
| NO_COLOR | Standard. Presence disables colour regardless of value. |
| CI | Standard. Presence implies --non-interactive. |
Related
- The MCP server, which
wam mcp installconfigures - The OpenAPI document for the REST API behind every command
- All docs