Prospex docs

Agent skill

What an AI agent needs to know before it uses Prospex: the three objects, the vocabulary, the budgets, and what is absent.

An agent that reads Prospex without this will promise email addresses Prospex does not hold, treat a list's ranking as a change feed, and invent a fit score. The skill is the ~150 lines that stop all three.

npx -y prospex skill install            # into ./.claude/skills/prospex
npx -y prospex skill install --global   # into ~/.claude/skills/prospex
npx -y prospex skill install --agent codex
npx -y prospex skill install --print    # print it, write nothing

Installing from the CLI keeps the skill versioned with the tool it documents. The text below is the same file.

What it covers

  • Lists and Outreach. A list holds companies: built from a description, imported, or the companies you follow. Its signals say what changed on them. Outreach, run on a list, answers who to work now and how to open.
  • The vocabulary. Buckets, bands, size bands, cantons, topics, and the rule that fit is a word.
  • The budgets. 250 a month on Starter, 1,000 on Pro and 10,000 on Business (20 on the free plan), shared with every other surface, with a daily ceiling of half that, and 10,000 API requests a day.
  • What Prospex does not hold. No contact names, no email addresses, no credit data, Switzerland only.
  • Idempotency. Reuse the key on a retry.

SKILL.md

---
name: prospex
description: >
  Swiss company intelligence via the prospex CLI: search the commercial register,
  read a customer's lists (markets, imported lists, followed companies) and
  what changed on them.
  Use when the user wants Swiss company data, a target list for Switzerland, or
  recent events on Swiss companies.
  Triggers: "Prospex", "Swiss companies", "Swiss company register", "Zefix",
  "SHAB", "Swiss market", "Swiss prospects", "Swiss target list", "my market",
  "my list", "my lists", "my watch", "followed companies", "what changed at", "Swiss B2B", "companies in Vaud",
  "companies in Zurich", "Swiss canton", "UID", "CHE-".
---

# Prospex

`prospex` reads Swiss company data: the commercial register with what Prospex has
learned about each company, plus the customer's own **lists**.

```bash
npx -y prospex whoami            # active credential and what is left today
npx -y prospex markets list      # the customer's lists. Costs nothing.
npx -y prospex companies search --q "precision machining" --canton VD
```

Every command prints the API payload as JSON on stdout, unchanged. Rate-limit
state goes to stderr as one line. Errors go to stderr as one JSON object.

## Install and authenticate

`npx -y prospex` needs no install. For interactive use, run:

```bash
npx -y prospex login
```

This opens browser approval and stores short-lived OAuth access and rotating
refresh tokens. If the browser cannot open, give the fallback URL and code to
the customer.

API keys remain available at <https://prospex.ch/app/api-keys/> for CI and
unattended automation. They resolve in this order:

1. `--key pxk_…`
2. `PROSPEX_API_KEY`
3. `--credentials <path>`
4. `PROSPEX_CREDENTIALS`
5. `${XDG_CONFIG_HOME:-~/.config}/prospex/credentials.json`

`prospex login --key pxk_…` writes an API-key profile. Prefer the environment
variable in a CI job. Putting a key in a command can save it in shell history.

If a command exits **3**, the credential is absent, expired, revoked or
rejected. Ask the customer to run `prospex login`.

## What Prospex answers

The customer works in **lists**. The CLI and the API call them markets, and
every list has three readings:

| Command | The question it answers |
|---|---|
| `markets get <id>` | What the list is: its `origin`, state and monitoring. |
| `markets rows <id>` | Who is in it. For a built market, ranked with reasons. |
| `markets signals <id>` | What changed on its companies, newest first. |

`origin` says how the list was made:

- `built`: a **market**, built from a description. It answers who could buy,
  and carries a `definition`.
- `imported`: companies the customer picked, uploaded or brought in from
  HubSpot. `definition` is null.
- `follows`: the companies the customer follows.

`monitoring` is null when the list is not monitored. When it is on, it carries
the list's topics and `new_signals`, the signals on those topics since
monitoring started. `markets signals` answers either way.

**Prospect** answers who to work now and how to open. Pilot only.

The corollaries an agent gets wrong:

- **"My watch" is a monitored list.** Watches became monitoring on a list. Find
  it with `markets list` and read it with `markets signals`.
- **A signal is evidence, not a prospect.** "This company hired a CFO" is a
  reason to start a conversation. It is not a statement that the company is a
  good fit.

## Vocabulary

**Buckets**, on a market's companies:

- `core` — the market's answer to who to sell to. The default.
- `explore` — the wider set the customer has not decided about.
- `excluded` — what the market threw out. Not readable through the API.

**Bands** — the fit words, on every market row as `fit`:

- `strong` — answers the definition at least as well as the customers the
  customer themselves called ideal.
- `good` — a clear match, below that anchored cut.
- `possible` — worth a look.
- `weak` — ranked and kept out of both buckets.

**Fit is a category. The score is deliberately not exposed.** Report the band
word. Do not invent a number, rank on one, or ask for one. The underlying score
is per-query and per-pool: it is not comparable between two markets, and a reader
shown `0.72` reads it as a percentage of something. If a user asks for a score,
say that Prospex publishes the band and why.

## Budgets, and backing off

Two limits. The row budget is shared with every other way the customer takes
companies (the profiles they open, the CSV they click, the market feed their
Clay table polls, MCP, CRM pushes):

- **Rows are distinct companies a month, per plan**: 250 on Starter, 1,000 on
  Pro, 10,000 on Business, 20 on the free plan. A **daily ceiling of half the
  month** (125, 500, 5,000; 20 free) answers `daily_row_limit`. Read the real
  figures from `prospex usage` rather than assuming a plan. A company counts once a month,
  whichever command or surface returned it, so re-reading a company the
  account already had this month is free. Signals cost one row per company they are
  about. Metadata routes (`markets list`, `markets get`) cost nothing.
- **10,000 requests a day**, plus a per-minute guard.

Days reset at **local midnight, Swiss time**; months at midnight on the 1st.
When the budget runs out mid-page, the page carries the companies that fit,
with `has_more` and a `next_cursor` that resumes at the first one held back.

**Run `prospex usage` before any `--all`.** It prints what is left, and a walk
that empties the budget takes the customer's other workflows down with it.
`--all` refuses to spend more than half of what remains and stops at
`--max-pages` (default 10), keeping `has_more` and a live `next_cursor` so you
can resume.

On **exit 9** you are out of allowance: stop, and tell the user when it resets.
Do not sleep: the wait can be most of a day, or until the 1st on
`monthly_row_limit`. On **exit 5** the subscription
lapsed, and no amount of retrying will fix it.

## What Prospex does not hold

Say so rather than promising it:

- **No contact names and no email addresses.** None. An agent that does not know
  this will offer to find the CFO's address.
- **No credit data, no financial statements, no revenue figures.**
- **Switzerland only.** A German or French company is not in here.

What it does hold: the commercial register, a one-line description of what a
company does, sector, canton and commune, headcount band, who it sells to,
website and domain, and dated events with their sources.

Building a new market is an MCP tool, `prospex_market_builder`, and the web
app. The CLI reads markets that already exist.

## Commands

| Command | Costs rows |
|---|---|
| `whoami`, `usage` | no |
| `companies search [--q] [--canton] [--sector] [--size-band] [--audience]` | yes |
| `companies get <id>` | yes |
| `companies signals <id> [--since] [--type]` | yes |
| `markets list`, `markets get <id>` | no |
| `markets rows <id> [--bucket core\|explore] [--band strong,good]` | yes |
| `markets signals <id> [--since] [--type]` | yes |
| `projects list`, `projects get <id>`, `prospects list <project>` | pilot |
| `prospects add`, `prospects decide`, `prospects research`, `prospects approve` | pilot mutation |
| `cycles start`, `cycles get` | pilot |
| `jobs list`, `jobs get`, `jobs claim`, `jobs submit`, `jobs fail`, `jobs cancel`, `jobs retry` | pilot |

Paging flags on every list: `--limit`, `--cursor`, `--all`, `--max-pages`.
Output: `--format json|compact|table|ndjson|csv`, default `json`.

`prospex raw <METHOD> <path>` calls any route the CLI has no command for.

**A value outside a closed set is refused, not ignored.** `--size-band small`,
never `--size-band 11-50`; `--band strong`, never `--band 0.8`. The same holds
server-side, so a typo in a filter is a 400 rather than an unfiltered page.

## Mutations

Prospect write commands need a pilot key with the scope named by
`prospex spec --json`. Read the current prospect, cycle or job before changing
it.

**On a retry, reuse the same `--idempotency-key`.** A fresh key on a retry is a
second mutation, and Prospex will perform it. The same key with the same body
replays the stored answer.

## References

- `references/vocabulary.md`: buckets, bands, signal types, sectors, cantons
- `references/errors.md`: every exit code and API error code, and what to do
- `references/prospect-stages.md`: job lifecycle and the three result schemas
- `references/writing.md`: the rules for drafting outreach copy, if asked to
- Full reference: <https://prospex.ch/docs/cli/>