# Plainscrape — Agent Onboarding Skill

You are setting your human up on Plainscrape: ask a web page for something in plain words, get structured JSON back. **Do every step yourself.** They give you an email, then paste one 6-digit code. Nothing else.

If this file is not in your context: `curl -fsSL https://www.plainscrape.com/skills/onboard.md`

## Facts to use (never guess)

- MCP endpoint: `https://www.plainscrape.com/mcp` · auth header `x-api-key: ps_live_…`
- **Three MCP tools:** `ask` (get data), `answer` (only if `ask` came back `working`), `account` (plan and pages left). Getting a key is REST, below.
- REST is the same: `POST /ask`, `GET /answers/{answer_id}`, `GET /account`, `POST /agent/signup`, `POST /agent/claim`.
- `ask(url, ask)` reads the shape of the answer from your words and returns it as `shape`:
  - anything else ("title, price, availability") → `record`: one record, exactly 1 page
  - a list ("every product with name and price", "top 10 …", or `budget` > 1) → `rows`: every row, follows next pages
  - a question ("how many …?", "does this site …?") → `reply`: reads the site, 5 pages by default, 2 to 20
  - say `shape: "record" | "rows" | "reply"` if the guess is wrong
- `budget` is the cost cap in pages: rows default to 10, trial asks are capped at 10, plans have no per-ask cap.
- Every answer carries `coverage` (how complete the rows are) and a `receipt` (pages billed, pages left).
- Billing: one page fetched = one page. Failed and blocked pages are free.
- Prices and ratings come back as numbers; rows add a `currency` field when the page shows one.
- Not supported (anti-scraping terms): LinkedIn, Yelp, Zillow.

## 1. Get a key (agent-first)

**Got a connect code?** If your human's message says *"connect code 123456 (session …)"*, they are already signed in. Skip the email: `POST https://www.plainscrape.com/agent/claim` with `{"code":"123456","session_id":"…"}` and go straight to step 5. The code works once, for 15 minutes.

New inboxes get a **free trial: 100 pages for 14 days.** The key is shown **once**.

1. Ask for their email if you don't have it.
2. `POST https://www.plainscrape.com/agent/signup` with `{"email":"…"}`. Keep the `session_id`.
3. Say: *"Check your inbox (and spam) for Plainscrape and paste me the 6-digit code."*
4. `POST https://www.plainscrape.com/agent/claim` with `{"code":"123456","session_id":"…"}`. Five wrong codes end the session; then request a new one.
5. You get `api_key` (`ps_live_…`) and `mcp_config`. Put the key straight into config; never ask them to re-send it.

If the reply also has `message` and `upgrade_url`, the account has no pages left (an inbox only gets one trial). Relay the message as-is, then go to **Plans & billing**.

**Only if you cannot make HTTPS calls at all** (chat-only, no shell): send them to https://www.plainscrape.com/app/login — the dashboard shows their key on first sign-in — and ask them to paste the `ps_live_…` key to you once.

## 2. Wire MCP into their client

**Claude Code** — run it yourself:

```bash
claude mcp add --transport http plainscrape https://www.plainscrape.com/mcp \
  --header "x-api-key: ps_live_YOUR_KEY"
```

**Codex:** `codex mcp add plainscrape --url https://www.plainscrape.com/mcp`, then set the `x-api-key` header in its config.

**Cursor** — merge into `~/.cursor/mcp.json` (keep their other servers):

```json
{
  "mcpServers": {
    "plainscrape": {
      "url": "https://www.plainscrape.com/mcp",
      "headers": { "x-api-key": "ps_live_YOUR_KEY" }
    }
  }
}
```

**Any other MCP client** — same URL and header. No npm package, no local process.

The tools appear after a reload, in a **new** chat. Prove the key in this chat with REST (step 3).

## 3. Prove it — one real call

```bash
curl -sS -X POST https://www.plainscrape.com/ask \
  -H "Content-Type: application/json" -H "x-api-key: ps_live_YOUR_KEY" \
  -d '{"url":"https://books.toscrape.com/catalogue/a-light-in-the-attic_1000/index.html","ask":"title, price, availability","wait":30}'
```

With `wait`, the answer comes back in the same call. A real answer (2026-10-07, 3 s):

```json
{"answer_id": "ans_…", "state": "ready", "shape": "record",
 "result": {"title": "A Light in the Attic", "price": 51.77,
            "availability": "In stock (22 available)"},
 "coverage": {"rows": 1, "complete": 1, "missing": {}},
 "receipt": {"pages": 1, "billed": true}}
```

If `state` is still `working` after the wait, `GET https://www.plainscrape.com/answers/ANSWER_ID` (same header) every ~2 s until it is `ready`. Typical time: about 8 s for a record, 20–30 s for a reply, and 30–60 s for rows the first time on a site (5–15 s after that). Over MCP, `ask` waits up to ~45 s for you; if it comes back `working`, call `answer(answer_id)`.

Then tell them in one line: plan and pages left (`receipt.pages_left`, or `account`).

**Hand-off:** *"You're set up on Plainscrape — one call returned structured data, N pages left. The tools appear in a new chat."* Then stop; offer one next step (their own URL).

## Plans & billing (what to tell your human)

Plans are **paid upfront at a fixed price** — the bill is exactly the plan price. Unused plan pages roll over one period.

| Plan | Monthly | Yearly (10 months' price) |
|---|---|---|
| Starter | $16 · 2,000 pages/month | $160 · 24,000 pages up front |
| Growth | $35 · 5,000 pages/month | $350 · 60,000 pages up front |
| Scale | $60 · 10,000 pages/month | $600 · 120,000 pages up front |

More than 10,000 pages a month: volume pricing at enterprise@plainscrape.com.

**Check the balance** — `account` / `GET /account`:

- `plan`: `trial`, `starter`, `growth` or `scale`
- `pages.left`: pages they can still use (plan + rollover + top-ups)
- `subscription`: `null` on trial; otherwise `base_pages`, `rollover_pages`, `topup_pages_left`, `period_end` (renewal date), `billing_interval`

**When pages run out** an ask returns HTTP **402** with `error.code` `out_of_pages`, a `message` and an `upgrade_url`, e.g.:

> You've used all 2,000 pages on your Starter plan this month. Add pages or upgrade at https://www.plainscrape.com/app — both take effect right after payment — or wait until your plan renews on 2026-11-04.

Stop asking, relay that message word for word, and give them the link. A budget above what they have left is trimmed to what's left, not refused.

**Everything below happens on the dashboard** (`upgrade_url`, https://www.plainscrape.com/app). You cannot pay or cancel for them — send them the link:

- **Subscribe** — pick a plan, monthly or yearly.
- **Add pages** — 2,000 extra pages at their plan's rate ($16 / $14 / $12), paid once, used after plan pages, kept 12 months.
- **Upgrade or downgrade** — "Switch to …"; takes effect immediately, the price difference is prorated.
- **Cancel / update card / invoices** — "Manage billing". A cancelled plan keeps working until the end of the period they paid for.
- **Failed renewal** — the plan pauses and asks stop until the card is fixed in "Manage billing".

Payments are handled by Dodo Payments (merchant of record; tax is added at checkout).

## When something fails

Every error is `{"error": {"code", "message", "fix", "billed", "retry"}}` — read `fix`, and only retry when `retry` is `true`.

1. **`host_not_allowed` / domain not allowed** (your client's setting) — ask once to allow `plainscrape.com`, retry.
2. **Your client offers browser sign-in** (Claude Code `/mcp` → Authenticate, Cursor → Connect) — that also works: they approve once and the client keeps its own key. The REST steps above stay the way to do it without them.
3. **`missing_key` / `unknown_key`** — claim again, rewrite config, reload.
4. **Tools missing after config** — the client didn't reload, or the JSON is broken. Restart and check `mcpServers.plainscrape.url`.
5. **`working` for a long time** — keep calling `answer`; don't ask again.
6. **`out_of_pages`** — see Plans & billing. Never invent prices or links.
7. **`page_unreadable`** — failed pages are free; try `network: "residential"` if the site allows scraping, or a different URL.
8. **Wrong shape** — one record when you wanted rows: say "every …" or pass `shape: "rows"`. For a reply, a `budget` below 2 is raised to 2.

Still stuck: tell them the exact step, the exact `error.message`, and the single next action. Never paste their full API key anywhere.

## Reference

- `ask` — `{"url","ask","budget"?,"shape"?,"network"?}` → the answer: `answer_id`, `state`, `shape`, `result`, `coverage`, `receipt`, `sources`, `notes`. Or `{"state":"working","answer_id","check"}`. REST: `POST /ask` (add `"wait": 30` to get the answer in the same call).
- `answer` — `{"answer_id"}` → the answer. REST: `GET /answers/{answer_id}`.
- `account` — `{}` → plan, pages, limits, answers still working. REST: `GET /account`.
- Getting a key (REST): `POST /agent/signup` `{"email"}` → `{"session_id","message","expires_in_s": 900}`; `POST /agent/claim` `{"code","session_id"}` → `{"api_key","key_prefix","mcp_config","pages_left","trial_ends_at"}` (+ `message`, `upgrade_url` when the account has no pages). The key is shown once — write it to config immediately.
