# PumpGTM for agent platforms

Technical guide for the platform version of PumpGTM: one API key and one billing account for you, one MCP URL per end user, a workspace created automatically on each user's first request, and usage recorded per user so you can read, cap, or disable anyone by API. Today the per-user URL exposes people search; LinkedIn and email outreach for partner users are next. An AI coding agent can follow this guide without a human in the loop. The human-facing page is https://pumpgtm.com/mcp/platforms.

## The model in three sentences

1. A partner account is a PumpGTM workspace that owns child workspaces, one per end user you serve.
2. You hold one API key. Each end user has one MCP URL: `https://mcp.pumpgtm.com/mcp/u/YOUR_USER_ID`. The first request to a new URL creates that user's workspace.
3. Every tool call and every lead is recorded against that user. You can read, cap, or disable any user by API.

## Prompt for your coding agent

Paste this into Claude Code, Cursor, Codex, or any coding agent working in your repo.

```prompt
Integrate PumpGTM people search into our product so our agents can find leads for each of our users.

Read https://pumpgtm.com/mcp/platforms.md first and follow it exactly.

1. Create our partner account: POST https://app.pumpgtm.com/api/partner/v1/signup with JSON {"email": OUR_BILLING_EMAIL, "company": OUR_COMPANY_NAME, "website": OUR_WEBSITE}. Ask me for the email if you do not have it. Store the returned apiKey as PUMPGTM_API_KEY in our secrets; it is shown once. If the response is 409, the email already has a PumpGTM workspace; tell me and stop.
2. Each of our users gets the MCP server URL https://mcp.pumpgtm.com/mcp/u/OUR_USER_ID (letters, digits, and _ . : @ - only, up to 120 characters, no spaces or slashes). Send "Authorization: Bearer PUMPGTM_API_KEY" on every MCP request. Do not create users any other way; the first request creates the workspace.
3. Connect our agent's MCP client to that URL over Streamable HTTP. Use the tools as documented: find_people is two calls, first with targeting to get a summary and playId, then with playId and confirmed=true to run it. Show every returned person with name, title, company, LinkedIn URL, and match reason. Never call anything else with the key.
4. Handle the structured errors from find_people: user_lead_cap_reached, billing_required (the message contains a link for whoever owns our PumpGTM account), and user_disabled.
5. Add usage to our admin from GET https://app.pumpgtm.com/api/partner/v1/users and GET https://app.pumpgtm.com/api/partner/v1/account. Use PATCH /api/partner/v1/users/OUR_USER_ID with {"monthlyLeadCap": N} to limit a user and DELETE to disable one.
6. Test before you finish: run tools/list for a test user id (expect exactly get_workspace, find_people, review_people, list_leads), run one real two-step find_people for that user (expect 10 or more candidates), then GET /api/partner/v1/users and confirm the test user shows leadsFound greater than 0. Show me those three results.

Do not build anything beyond these steps.
```

## Quickstart

### 1. Create the partner account

```bash
curl -X POST https://app.pumpgtm.com/api/partner/v1/signup \
  -H "content-type: application/json" \
  -d '{"email":"billing@yourcompany.com","company":"Your Company","website":"https://yourcompany.com"}'
```

Response, status 201:

```json
{
  "apiKey": "eve_mcp_...",
  "mcpUrlTemplate": "https://mcp.pumpgtm.com/mcp/u/{externalUserId}",
  "freeLeads": 50,
  "accountUrl": "https://app.pumpgtm.com/api/partner/v1/account",
  "usersUrl": "https://app.pumpgtm.com/api/partner/v1/users",
  "docsUrl": "https://pumpgtm.com/mcp/platforms"
}
```

The key is shown once. Store it server side; never ship it to an end user's device, and print only its first 12 characters in logs and transcripts. Errors: `400 email_invalid`, `400 company_required`, `400 website_invalid`, `409 email_in_use` (that email already owns a PumpGTM workspace; sign in at app.pumpgtm.com and copy the MCP key from the install page, it is the same kind of key).

### 2. Connect an end user

Any MCP client works: the official SDKs, Claude Code, Cursor, Codex, or Anthropic's MCP connector in the Messages API. The URL carries your user id, the bearer is your key.

```json
"mcp_servers": [{
  "type": "url",
  "url": "https://mcp.pumpgtm.com/mcp/u/user_4f8a",
  "name": "pumpgtm",
  "authorization_token": "eve_mcp_..."
}]
```

Or with the TypeScript SDK:

```ts
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";

const client = new Client({ name: "your-platform", version: "1.0.0" });
await client.connect(
  new StreamableHTTPClientTransport(new URL("https://mcp.pumpgtm.com/mcp/u/user_4f8a"), {
    requestInit: { headers: { Authorization: `Bearer ${process.env.PUMPGTM_API_KEY}` } },
  }),
);
const { tools } = await client.listTools(); // get_workspace, find_people, review_people, list_leads
```

### 3. Find people, two calls

First call saves the targeting and returns a summary to show the user:

```json
{ "name": "find_people", "arguments": {
  "requestId": "req-2026-09-15-0001",
  "targeting": {
    "name": "Heads of sales at mid-market SaaS",
    "candidateCount": 10,
    "hardFilters": { "currentTitleIncludesAny": ["Head of Sales", "VP Sales"] },
    "semanticQuery": "B2B SaaS companies selling to mid-market"
  }
} }
```

The result has `play.id`, `play.revision`, and `plan.targetingSummary`. Second call runs it with a new `requestId`:

```json
{ "name": "find_people", "arguments": {
  "requestId": "req-2026-09-15-0002",
  "playId": "PLAY_ID_FROM_STEP_ONE",
  "confirmed": true
} }
```

The result has `candidates[]` and `batch.id`. Nobody is contacted. `expectedRevision` is not needed to confirm; it is only for editing a saved Play's targeting. Passing `playId` with `confirmed: true` again later reruns the same targeting for fresh people. One candidate looks like this (`person` is a plain string; `id` is the `candidateId` that `review_people` takes):

```json
{
  "id": "5c1d5f0e-2f0e-4a3c-9d8a-0f1e2d3c4b5a",
  "person": "Andrew Wynkoop",
  "title": "Founder & Head of Sales (Fractional)",
  "company": "Revbook",
  "linkedinUrl": "https://www.linkedin.com/in/andrew-wynkoop-666041b2",
  "location": "Austin, Texas, United States",
  "matchReason": "Matched the requested company and workload context. Current title is Founder & Head of Sales (Fractional)."
}
```

Every tool result is one MCP `content` block of type `text` whose `text` is a JSON string; parse it. Errors come back the same way with `isError: true` on the result and `{"error": {"code", "message", "retryable"}}` in the text.

### 4. Read usage

```bash
curl https://app.pumpgtm.com/api/partner/v1/users -H "Authorization: Bearer eve_mcp_..."
```

```json
{ "periodStart": "2026-09-01T00:00:00.000Z",
  "users": [
    { "externalUserId": "user_4f8a", "createdAt": "2026-09-15T16:43:27Z", "status": "active",
      "monthlyLeadCap": null, "leadsFound": 10, "toolCalls": 2, "lastUsedAt": "2026-09-15T16:43:48Z" }
  ] }
```

## API reference

All partner endpoints live on `https://app.pumpgtm.com` and take `Authorization: Bearer YOUR_API_KEY`. Responses are JSON. Errors are `{"error": "code"}` with a 4xx status.

| Method and path | What it does |
|---|---|
| `POST /api/partner/v1/signup` | Create a partner account. No auth. Body: email, company, website (optional). Returns the key once. |
| `GET /api/partner/v1/account` | This period's totals: `leadsThisPeriod`, `freeLeads`, `freeLeadsRemaining`, active `users`, `billing.status` (`free`, `metered`, or `billing_required`) and `billing.checkoutUrl`. |
| `GET /api/partner/v1/users` | One row per end user with `leadsFound`, `toolCalls` (MCP `tools/call` requests this month; initialize and tools/list are not counted), `lastUsedAt`, `monthlyLeadCap`, `status`. |
| `PATCH /api/partner/v1/users/USER_ID` | Body `{"monthlyLeadCap": 100}` caps leads this calendar month, `null` removes the cap. Body `{"status": "active"}` re-enables a disabled user. |
| `DELETE /api/partner/v1/users/USER_ID` | Disable the user. Their data is kept, nothing more is billed for them, their MCP URL returns 403 `user_disabled`. |

Auth errors: `401 unauthorized` (missing or unknown key), `403 partner_key_required` (the key belongs to a child workspace, not a partner). User errors: `400 invalid_external_user_id`, `400 invalid_monthly_lead_cap`, `404 user_not_found`.

### End user ids

Your own id for the user. Letters, digits, and `_ . : @ -`, 1 to 120 characters, starting with a letter or digit. Anything else returns `400 invalid_external_user_id`. Ids are namespaced to your account, so `user_1` at two partners never collide.

## MCP tools available to end users

The per-user URL exposes exactly these four tools. Outreach tools are not listed because the end user has no LinkedIn connected.

**get_workspace** `{ view?: "summary" | "activity", sinceDays?: number }`. Setup state, saved Plays, unfinished reviews. Start here.

**find_people** `{ requestId, targeting?, playId?, expectedRevision?, confirmed?: boolean }`. `requestId` is an idempotency key, 8 to 120 characters of letters, digits, `. _ : -`; use a fresh one per call, reuse only to retry the same call. `targeting` fields: `name` (up to 120 chars), `candidateCount` (10 to 100), `hardFilters` (object, all optional: `currentTitleIncludesAny`, `currentFunctionsAny`, `currentSenioritiesAny`, `personCountriesAny`, `currentCompanyNamesAny`, `currentCompanyDomainsAny`, `currentCompanyIndustriesAny`, `currentCompanyTypesAny`, `currentCompanyHeadcount` with `minimum` or `maximum`, `currentCompanyInvestorsAny`, and `excluded...` variants), `semanticQuery` (plain language fit, up to 2000 chars). Without `confirmed` the call only saves the Play and returns the summary; with `confirmed: true` it runs and returns candidates. Revising a saved Play needs `playId` plus `expectedRevision`.

**review_people** `{ batchId, requestId?, decisions?: [{ candidateId, decision: "good_fit" | "not_a_fit" }], finish?: boolean }`. Without decisions it reads the batch; with decisions it records the user's choices. Sends nothing.

**list_leads** `{ leadId?, stage?, query?, sinceDays?, limit? }`. The user's saved leads.

### Structured errors from find_people

The tool returns `isError: true` with `{"error": {"code", "message", "retryable": false}}`:

| code | Meaning | What to do |
|---|---|---|
| `user_lead_cap_reached` | This user hit the `monthlyLeadCap` you set. | Raise or remove the cap with PATCH. |
| `billing_required` | Your account's free allowance for the month is used. The message contains a link. | Hand the link to whoever owns your PumpGTM account, or write to hello@pumpgtm.com. |
| `user_disabled` | You disabled this user. | PATCH `{"status": "active"}` to re-enable. |

## Usage and allowance

Usage is recorded per user and summed for your account by calendar month. New accounts start with a free allowance of leads found. When it is used, `find_people` returns `billing_required` with a link, and `GET /api/partner/v1/account` shows the same link and the running total. Commercial terms are agreed with PumpGTM directly; write to hello@pumpgtm.com.

## Limits and behaviour worth knowing

- Data isolation: each end user's Plays, candidates, and leads live in their own workspace. Nothing is shared between users or with other partners.
- Provider cap: each workspace has a daily people-search budget with the data provider. A very heavy user can hit it and get a retryable error; it resets daily.
- Disable is not purge: DELETE keeps the user's data. Ask hello@pumpgtm.com for a hard delete.
- Key rotation is not yet available by API. The account email can sign in at app.pumpgtm.com with a login link and rotate the key there.
- Not yet available: an end user connecting their own LinkedIn to unlock outreach tools through a partner URL. Direct customers do that today at https://pumpgtm.com/mcp.

## Verify your integration

A correct integration shows all three of these for a test user id:

1. `tools/list` returns get_workspace, find_people, review_people, list_leads and nothing else.
2. A confirmed `find_people` returns 10 or more candidates and a `batch.id`.
3. `GET /api/partner/v1/users` lists that user with `leadsFound` greater than 0 and `toolCalls` greater than 0.

Questions: hello@pumpgtm.com.
