# nvoke for agents

nvoke finds companies, reads their own websites for published email addresses and checks every address with its mail server. You can do all of it through a REST API or an MCP server. This file tells you how.

- REST API base: https://api.nvoke.run/v1
- MCP server: https://api.nvoke.run/mcp
- OpenAPI 3.1: https://api.nvoke.run/v1/openapi.json
- Human docs: https://nvoke.run/docs

## 1. Get a key

If your human already uses nvoke, ask them for an API key (desktop app, Account). Keys start with `nvk_sk_`.

If they have no account, create one yourself. No browser needed:

```sh
curl -X POST "https://api.nvoke.run/v1/agents/signup" \
  -H "Content-Type: application/json" \
  -d '{"email":"<the human’s real email>","agentName":"<your name>"}'
```

The 201 response holds `apiKey`. Store it now; it is shown once. We email the human a link to claim the account. Unclaimed accounts are on the free plan, stop working after 7 days (403 `claim_required`) and are deleted after 30. Tell your human to claim it. If they sign in another way first, your sign-up key is revoked.

Refusals: 409 `account_exists` (ask the human for a key), 422 `email_not_accepted`, 429 `rate_limited`.

## 2. Authenticate

Send `Authorization: Bearer <apiKey>` on every request.

## 3. The loop

1. `POST /v1/searches` with one of:
   - `{"kind":"local","params":{"niche":"dentists","location":"Aarhus"},"limit":50}` (a trade in a place, OpenStreetMap)
   - `{"kind":"web","params":{"keyword":"design studio","country":"dk"}}` (web search)
   - `{"kind":"launches","params":{"keyword":"ai","days":7}}` (Product Hunt and Show HN)
   It returns the search with `status: "queued"`.
2. `GET /v1/searches/{id}` every 5 seconds until `status` is `done`, `failed` or `canceled`. Searches take a few minutes. On `failed`, read `error`.
3. `GET /v1/searches/{id}/companies?verification=valid` for companies with addresses that accept mail. Follow `nextCursor` as `cursor`.
4. Or `GET /v1/searches/{id}/export.csv?verification=valid` for a CSV, one row per email.

## Webhooks instead of polling

`POST /v1/webhooks` with `{"url":"https://...","events":["lead.ready"],"scope":"drops"}` makes nvoke POST each company with a valid address to that URL once its checks are done (`lead.ready`), and each finished search (`search.finished`). Requests are signed the Standard Webhooks way; the 201 response holds the `whsec_` secret, once. Details: https://nvoke.run/docs/webhooks

## Endpoints

- `GET /v1/me`: Who you are, your plan and this month’s usage.
- `GET /v1/plans` (public): The plan ladder.
- `POST /v1/searches`: Start a search.
- `GET /v1/searches`: List your searches.
- `GET /v1/searches/:id`: Get a search.
- `POST /v1/searches/:id/cancel`: Cancel a running search.
- `DELETE /v1/searches/:id`: Delete a search.
- `GET /v1/searches/:id/companies`: List companies with their emails.
- `GET /v1/searches/:id/export.csv`: Export as CSV.
- `GET /v1/webhooks`: List webhooks.
- `POST /v1/webhooks`: Create a webhook.
- `PATCH /v1/webhooks/:id`: Change a webhook.
- `DELETE /v1/webhooks/:id`: Delete a webhook.
- `POST /v1/webhooks/:id/test`: Send a test event.
- `GET /v1/webhooks/:id/deliveries`: Recent deliveries.
- `GET /v1/api-keys`: List API keys.
- `POST /v1/api-keys`: Create an API key.
- `DELETE /v1/api-keys/:id`: Revoke an API key.
- `POST /v1/agents/signup` (public): Sign up as an agent.
- `POST /v1/privacy/opt-out` (public): Opt an address or domain out.
- `GET /v1/openapi.json` (public): This API as OpenAPI 3.1.
- `GET /health` (public): Health check.

## MCP

URL: https://api.nvoke.run/mcp. Streamable HTTP. Auth: the same bearer API key as a header, or OAuth through nvoke sign-in (clients that support MCP OAuth need only the URL).

Claude Code, one line:

```sh
claude mcp add --transport http nvoke https://api.nvoke.run/mcp --header "Authorization: Bearer <apiKey>"
```

Tools:

- `get_account()`: Who you are signed in as, the plan, verified emails used this month and the allowance, and the billing link a human uses to upgrade. Call this first.
- `start_search(kind, niche?, location?, keyword?, country?, days?, limit?)`: Starts finding companies and their verified emails. kind `local`: a trade in a place, from OpenStreetMap (needs niche and location). kind `web`: a keyword, optionally in a country, from web search (needs keyword). kind `launches`: products launched recently on Product Hunt and Show HN (keyword optional). Returns the search at once; it runs for a few minutes on our servers. Then call wait_for_search.
- `wait_for_search(searchId, timeoutSeconds?)`: Waits until the search finishes or the timeout passes, then returns it. Call again while status is `queued` or `running`. Counters grow as it runs.
- `get_search(searchId)`: One search with status and counters.
- `list_searches()`: Your searches, newest first.
- `get_leads(searchId, verification?, kind?, q?, cursor?, limit?)`: Companies a search found with their checked emails, 50 to a page. Use verification `valid` for addresses that accept mail. Pass nextCursor back as cursor for more.
- `export_csv(searchId, verification?, kind?, q?)`: The search as CSV text, one row per email. Same filters as get_leads.
- `cancel_search(searchId)`: Stops a running search. What it found so far stays.
- `delete_search(searchId)`: Deletes a search with its companies and emails. Cannot be undone.
- `list_webhooks()`: Your webhooks: the URL, events, scope and how the last deliveries went.
- `create_webhook(url, events?, scope?, dropId?)`: Pushes leads to a URL as they are ready instead of polling: `lead.ready` per company with a valid address, `search.finished` when a search ends. Scope `drops` or a dropId limits it to daily drops. Returns the signing secret once; give it to whoever verifies the requests.
- `test_webhook(webhookId)`: Sends a signed `ping` to the webhook now and returns how the server answered.
- `delete_webhook(webhookId)`: Stops the webhook at once and drops its pending deliveries.

## Quota and money

Every feature is on every plan. Only addresses whose check comes back valid count. Plans a month: 50 for $0, 1,000 for $5, 2,500 for $9, 7,500 for $19, 20,000 for $39, 50,000 for $79, 100,000 for $149. When the month is used up, searches fail with `quota_exceeded` and new ones answer 403. You cannot pay; give your human `billingUrl` from `GET /v1/me`.

## Errors

Every error is `{"error":"<code>"}` with a 4xx or 5xx status.

- `unauthorized`: No valid token. Send `Authorization: Bearer <key>`.
- `claim_required`: An agent created this account more than 7 days ago and nobody claimed it. The human claims it from the emailed link.
- `invalid_request`: The body or query failed validation. `details` lists the problems.
- `not_found`: No such search, key or webhook on this account.
- `quota_exceeded`: This month’s verified emails are used up. A human upgrades at `billingUrl` from `GET /v1/me`.
- `source_unavailable`: Web search is paused on our side. Local and launch searches still work.
- `not_cancelable`: The search already finished.
- `api_key_limit`: The account holds 20 live keys. Revoke one first.
- `account_exists`: An account with this email exists. Ask the human for an API key.
- `email_not_accepted`: Disposable inbox, an opted-out address, or a domain that receives no mail.
- `rate_limited`: Too many sign-ups. Try again later.
- `signup_unavailable`: Agent sign-up is switched off right now.
- `too_many_webhooks`: The account holds 10 webhooks. Delete one first.
- `webhook_url_not_allowed`: Webhook URLs must be HTTPS on a public address that resolves.
- `database_unavailable`: Our database is unreachable. Retry with backoff.

Retry 5xx and `database_unavailable` with backoff. Do not retry 4xx unchanged.

## Rules

nvoke is a research tool. In the EU, marketing email to these addresses usually needs prior consent. Do not send bulk email to them on the user's behalf.

nvoke never sends email. Respect opt-outs: https://nvoke.run/opt-out.
