# AUTOGROK Desk — agent quickstart

Hireable micro-jobs API. Agents and humans pay for discrete work (probe, extract, watch, shape, brief) via **USDC/x402** (per call), **Stripe packs** (prepaid credits, Checkout for humans) or **MPP card packs** (agents paying with a Stripe Shared Payment Token).

## Base URL

- **Public:** `https://desk.grokaiapps.com`
- **Health:** `GET /health`

## Discovery (free)

| Path | Purpose |
|------|---------|
| `GET /v1/discovery` | Full agent catalog + bazaar-shaped metadata (absolute **https** resource URLs) |
| `GET /v1/catalog` | Compact SKU list |
| `GET /.well-known/x402` | IETF-style capability manifest |
| `GET /AGENTS.md` | This file |
| `GET /docs/DISCOVERY.md` | Discovery checklist |
| `GET /docs/BAZAAR.md` | Bazaar / CDP indexing runbook |
| `POST /mcp` | MCP server (Streamable HTTP) — `initialize` / `tools/list` free |
| `GET /openapi.json` | MPP discovery (OpenAPI + `x-payment-info`) for the card-pack endpoints |
| `GET /llms.txt` | This file as plain text |

## MCP (Claude, Cursor, any MCP client)

Remote MCP server: **`https://desk.grokaiapps.com/mcp`** (Streamable HTTP, stateless, JSON responses).

| Tool | Price | Same as |
|------|------:|---------|
| `url_probe` | $0.03 | `url-probe` |
| `page_extract` | $0.05 | `page-extract` |
| `watch_once` | $0.10 | `watch-once` |
| `json_shape` | $0.15 | `json-shape` |
| `brief` | $0.25 | `brief` (async — see promise below) |
| `deep_brief` | $9.00 | `deep-brief` (async, ~1500 words, 8+ sources) |
| `brief_status` | free | `GET /v1/jobs/:id` |

Payment per `tools/call`:

- **x402 (USDC on Base):** an unpaid call returns a tool result with `isError: true` and `structuredContent` = x402 v2 `PaymentRequired` (same price + `payTo` as HTTP, bazaar extension `info.input.type: "mcp"`). Retry with the signed `PaymentPayload` in `params._meta["x402/payment"]`; the receipt comes back in `result._meta["x402/payment-response"]`. Easiest: `createx402MCPClient` from `@x402/mcp`. Failed jobs are not settled, so they cost nothing. Pack credits work the same way: a call that fails gives its credit back.
- **Pack credits:** send HTTP header `X-Entitlement-Key: <your secret pack key agk.…>` (Checkout or MPP) on the MCP connection; each call spends 1 credit of that job (`result._meta["autogrok/credits-remaining"]`).

Cursor (`~/.cursor/mcp.json`):

```json
{
  "mcpServers": {
    "autogrok-desk": {
      "url": "https://desk.grokaiapps.com/mcp",
      "headers": { "X-Entitlement-Key": "agk.cus_…your-pack-key…" }
    }
  }
}
```

Claude Code:

```bash
claude mcp add --transport http autogrok-desk https://desk.grokaiapps.com/mcp \
  --header "X-Entitlement-Key: agk.cus_…your-pack-key…"
```

Omit the header to pay per call with an x402-capable MCP client. Same SSRF guards and rate limits (30 req/min per IP / key) as `/v1/jobs/*`.

## Pay (agents) — USDC on Base via x402

1. `POST` a job path without payment → **HTTP 402** + `PAYMENT-REQUIRED` (base64 JSON).
2. `resource.url` is always `https://desk.grokaiapps.com/v1/jobs/...`.
3. Pay **exact** USDC on **Base** (`eip155:8453`) to `payTo` in the 402 body.
4. Retry with `PAYMENT-SIGNATURE` (CDP facilitator settles live when configured).
5. Network asset: USDC `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913`.

Facilitator: `https://api.cdp.coinbase.com/platform/v2/x402`.  
Validate (no key): `POST https://api.cdp.coinbase.com/platform/v2/x402/validate` with `{ "resource", "method" }`.

## Pay (agents) — MPP packs: card (Stripe SPT) or stablecoin on Tempo

Agents can buy a prepaid pack over the **Machine Payments Protocol**, paying either way from the same 402:

- **card** — Stripe Shared Payment Token (Link wallet or Cards API), settled as a PaymentIntent.
- **tempo** — stablecoin (USDC) on the **Tempo** chain, sent to a Stripe-issued deposit address. Stripe records the transfer as a crypto PaymentIntent, so it lands in the same Stripe balance. Desk holds no wallet and no key.

Both cost the same and credit the same pack. Cards have a $0.50 minimum, so this rail sells **packs only** ($0.50–$9.00, the top being `pack_deep_brief_1`); per-call jobs stay on x402. Each pack costs exactly the per-call price times its number of credits, so a pack is not a discount.

`POST https://desk.grokaiapps.com/v1/mpp/packs/{pack_id}` — packs: `pack_url_probe_17` ($0.51), `pack_page_extract_10` ($0.50), `pack_watch_5` ($0.50), `pack_json_4` ($0.60), `pack_brief_2` ($0.50), `pack_deep_brief_1` ($9.00).

1. Unpaid `POST` → **HTTP 402**. One `WWW-Authenticate: Payment` header carries **two** challenges:
   - `method="stripe", intent="charge"` — card. `request.amount` is USD cents (`"51"`), `request.methodDetails.networkId` is Desk's Stripe profile `profile_61VWqT6yAHyjIP6EJA6VWqT668SQipSDos48kYNEeAxc` (@autogrokdesk).
   - `method="tempo", intent="charge"` — stablecoin. `request.amount` is base units at 6 decimals (`"510000"`), `request.currency` is the Tempo USDC token address, `request.recipient` is the Stripe deposit address.

   Card comes first, so clients that read only the first challenge keep working.
2. Pay with whichever you can:
   - **card** — grant a Shared Payment Token for that `networkId` and amount.
   - **tempo** — send USDC on Tempo to `request.recipient` **carrying the challenge's attribution memo**, then retry with a hash credential for that transfer. Use `mppx/client` `tempo.charge({ account })`, which builds the memo and the credential; it is a `transferWithMemo` call, not a bare transfer. A plain transfer with no memo (an ordinary wallet send or exchange withdrawal) moves the money but is refused — the server cannot tell which 402 it paid. Money must reach the deposit address; a card-only client cannot pay this way.
   Then retry with `Authorization: Payment <credential>`.
3. `200` JSON: `{ entitlement_key: "agk.cus_….<secret>", credits, added, pack_id, payment_intent, method }` plus a `Payment-Receipt` header. `method` is `"stripe"` or `"tempo"`; `payment_intent` is the PaymentIntent id (card) or the on-chain transfer hash (tempo).
4. Spend: `X-Entitlement-Key: agk.…` on `POST /v1/jobs/*` or on the `/mcp` connection. Balance: `GET /v1/me/credits`. Keep the key secret.
5. A pack's own job uses one credit. A credit can also pay for one job priced at or below what you paid per credit (the difference stays as pack balance); credits never combine, so pricier jobs return 402 `needs_own_pack`. A failed call gives back exactly what it drew. `GET /v1/me/credits` shows `value_usd`, `balance_usd`, `unit_usd` and `can_pay` per job.
6. Top up: send your existing `X-Entitlement-Key` with the next pack purchase; credits are added to that key.

With Stripe's Link CLI:

```bash
link-cli spend-request create --credential-type shared_payment_token \
  --network-id profile_61VWqT6yAHyjIP6EJA6VWqT668SQipSDos48kYNEeAxc \
  --amount 51 --merchant-name "AUTOGROK Desk" --merchant-url https://desk.grokaiapps.com \
  --context "Buying 17 url-probe credits from AUTOGROK Desk to check link health for the user's site audit." \
  --request-approval
link-cli mpp pay https://desk.grokaiapps.com/v1/mpp/packs/pack_url_probe_17 \
  --spend-request-id lsrq_… --method POST
# → {"entitlement_key":"agk.cus_….…","credits":{"url-probe":17},...}
curl -X POST https://desk.grokaiapps.com/v1/jobs/url-probe \
  -H "X-Entitlement-Key: agk.cus_….…" -H "content-type: application/json" \
  -d '{"url":"https://example.com"}'
```

Programmatic: `mppx/client` `stripe.charge({ createToken })` for cards, `mppx/client` `tempo.charge({ account })` for stablecoin. Stripe API version pinned server-side: `2026-09-30.preview`. Discovery: `GET /openapi.json` (`x-payment-info.offers[]` has one entry per method), `GET /v1/discovery` → `mpp`.

**One payment, one credit.** Every settled reference (PaymentIntent id or transfer hash) is claimed in a durable table before crediting, so replaying a credential — even after a restart or deploy — returns `409 already_credited` with the key you already have and adds nothing. Set `MPP_TEMPO_ENABLED=0` to serve cards only; if Stripe declines to issue a deposit address the server logs `[mpp] tempo rail off` and serves cards automatically.

## Pay (humans) — Stripe packs

Card Checkout minimum is $0.50, so micro SKUs ship as packs.

1. `GET /v1/packs` — list packs.
2. `POST /v1/checkout/session` body `{"pack_id":"pack_page_extract_10"}` → `{ url, session_id }`.
3. Checkout asks for name and email. Each purchase gets its own secret **pack key** (`agk.cus_….<64 hex>`), shown on the return page (`/checkout/success` or `/hire/start`) and saved in that browser's HttpOnly cookie. The buyer can see it again in My stuff (`/hire/mine`) after signing in with the checkout email (emailed 6-digit code).
4. Agents/API: send header `X-Entitlement-Key: agk.…` to spend pack credits. Emails and Checkout session ids are **not** keys (they get `401 pack_key_required`): knowing someone's email never spends or shows their pack. Keep the key secret.
5. A pack's own job uses one credit. A credit can also pay for one job priced at or below what you paid per credit (the difference stays as pack balance); credits never combine, so pricier jobs return 402 `needs_own_pack`. A failed call gives back exactly what it drew. `GET /v1/me/credits` shows `value_usd`, `balance_usd`, `unit_usd` and `can_pay` per job.

**Tip what's left** (done with a pack): `GET /v1/me/tip-remaining` previews the total (pack value + signed-in dollar balance). `POST /v1/me/tip-remaining` with `X-Entitlement-Key` (or a signed-in session) and body `{"confirm": true, "expect_usd": "0.45"}` tips all of it to AUTOGROK: no new charge, the pack and balance go to zero, and it can't be undone. Errors: `400 confirm_required`, `409 amount_changed` (preview again), `409 nothing_to_tip`. MCP: tool `tip_remaining` (free; omit `confirm` to preview, then `confirm: true` with `expect_usd`).

**Help** (a person, not a bot): `POST /v1/support` with JSON `{"topic": "payment|job|credits|other", "message": "…", "email"?: "…", "job_id"?: "…"}` plus `X-Entitlement-Key` (or an email). Returns `201 {ticket: "HELP-XXXX", message, fallback}`. No automatic reply is sent; a person answers, usually within 1 business day. Rate-limited; `GET /v1/support` describes it. MCP: tool `support_request` (free). Operators: `GET /v1/ops/support`, `PATCH /v1/ops/support/:id`, `scripts/service-queue.mjs help|help-close`; open non-test tickets also appear in `/v1/ops/hire` (`support`) and `/v1/ops/services/requests` (`help_tickets`). The browser form posts to `/v1/hire/help`.

## SKUs (instant unless noted)

| id | Method + path | Price | Notes |
|----|---------------|------:|-------|
| `url-probe` | `POST /v1/jobs/url-probe` | $0.03 | Instant. Check if a URL is up: status code, final URL after redirects, content type, response time |
| `page-extract` | `POST /v1/jobs/page-extract` | $0.05 | Instant. Extract a web page's readable text as markdown, with title and description |
| `watch-once` | `POST /v1/jobs/watch-once` | $0.10 | Instant. Detect whether a page changed since the last check (sha256 of main text) |
| `json-shape` | `POST /v1/jobs/json-shape` | $0.15 | Instant. Turn messy text or key: value lines into JSON fields (rule-based, no LLM) |
| `brief` | `POST /v1/jobs/brief` | $0.25 | **Async, within 24h** — research brief with 3+ cited sources; returns `queued` + `dueBy`; poll `GET /v1/jobs/:id` |
| `deep-brief` | `POST /v1/jobs/deep-brief` | $9.00 | **Async, within 24h** — ~1500 words, executive summary, 8+ sources, operator-checked |

Bodies are JSON. Schemas and examples: `GET /v1/discovery` or `GET /v1/catalog`.

## Brief promise

- **Turnaround:** delivered within **24 hours** of the order (usually the same day). The job carries `dueBy`; `late: true` if we miss it.
- **Length:** ~500 words by default; `max_words` 50–1500.
- **Sources:** at least **3** cited public source links.
- **Delivery:** `GET /v1/jobs/{id}` (free) or MCP `brief_status` → `status: "done"` and `result: { title, summary, body_markdown, sources: [{title,url}], word_count, delivered_at }`.
- **Scope:** public web sources only — no paywalled, private, or login-only data. Price stays **$0.25**.
- **Free sample:** https://desk.grokaiapps.com/samples/brief-x402-bazaar-discovery.md (a real Brief: how agents find paid x402 APIs, Oct 2026).

## Deep brief ($9)

- `POST /v1/jobs/deep-brief` with `{"topic": "...", "focus": "optional angle"}` — x402 $9.00 USDC, or card/MPP pack `pack_deep_brief_1` ($9).
- **Turnaround:** within **24 hours**. **Length:** ~1500 words (1200–2000). **Structure:** 3–5 sentence executive summary + key findings + analysis. **Sources:** at least **8**, listed with title and URL, checked by the operator before delivery.
- Same delivery as Brief: `GET /v1/jobs/{id}` (free) or MCP `brief_status`; result `tier: "deep"`.

## Human hire UI

`https://desk.grokaiapps.com/` — paste a URL, run url-probe (first one free), buy a pack if unpaid; order a brief in the Brief lane. Agent API stays under `/v1/*`.

## Operator notes

- Env `PUBLIC_BASE_URL=https://desk.grokaiapps.com` pins absolute https URLs (do not trust bare `Host`).
- Bazaar indexing needs one **real settled** USDC payment per route that echoes `extensions.bazaar`. Desk cannot self-settle without funded buyer USDC.
