---
title: "API basics · Nook docs"
canonical: https://www.checkoutbench.ai/docs/api
last_updated: 2026-11-03
---

# API basics

> As of Nov 3, 2026, 2:45 PM PST, from Nook main 680daa2f.

This page only repeats what Nook's published agent guide documents. Paths are relative to Nook's base URL. Field lists are abbreviated; the agent guide is the source of truth.

## Basics

- **Auth:** `Authorization: Bearer nk_…`, the agent's partner key. A missing or wrong key returns `401 invalid_partner_key`. Keep the key on the server; never show it to the person.
- **Bodies:** JSON, at most 4096 characters (`413 too_large` above that).
- **Errors:** `{ "error": "<code>", "details": { … } }`.
- **Retries:** quotes and purchases take an `Idempotency-Key` header (1 to 128 printable characters). The same key and body return the same result; the same key with a different body returns `409 idempotency_key_reused`.

## Endpoints

| Method and path | What it does |
|---|---|
| `POST /v3/api/agent/register` | Register a partner key the agent generated itself (`nk_` plus 32 random bytes in base64url). Body: `id`, `name`, `host`, optional `about`. Returns `201` with the partner record. |
| `GET /v3/api/agent/me` | The partner record, the connections people granted it, and counts. |
| `POST /v3/api/agent/connect-links` | Ask for a link. `{ "merchants": "all" }`, a list, or `{ "merchant": "amazon" }`. Returns `201` with `url`, `status: "waiting_for_person"`, `expires_at`. |
| `GET /v3/api/agent/connect-links/<id>` | Link status: `waiting_for_person`, `connecting`, `connected` or `failed`. Once connected, `connection.id` is the `connection_id`. |
| `POST /v3/api/agent/quotes` | Start a quote. Returns `202` with `status: "preparing"`. |
| `GET /v3/api/agent/quotes/<id>` | Read the quote. Poll every few seconds. |
| `POST /v3/api/agent/connections/<connection_id>/address` | Amazon only: save a new delivery address the person asked for. Returns `202`; read it at `GET /v3/api/agent/address-operations/<id>` (`preparing`, `saved`, `needs_input` with Amazon's suggested versions, or `blocked`). Not a purchase. |
| `GET /v3/api/agent/connections/<connection_id>/preferences` | The person's saved preferences for that store (place names, usual address and card, budget, notes). |
| `POST /v3/api/agent/connections/<connection_id>/preferences/places` | Save a place name, only as the answer to the agent's own `destination_choice_needed` quote. |
| `POST /v3/api/agent/purchases` | Buy a ready quote. Body: `quote_id` and, if the person said yes in chat, `confirmation` (their words). Returns `202`. |
| `GET /v3/api/agent/purchases/<id>` | Read the purchase. |
| `GET /v3/api/agent/connections/<connection_id>/operations` (and `.md`) | Named operations a connected store offers beyond quotes, such as searching flights or managing price alerts: each one's name, read or write, approval rule, input schema and output. |
| `POST /v3/api/agent/connections/<connection_id>/operations` | Run one: `{ "operation", "input", "confirmation" }`. A write that changes the person's account needs `confirmation.text`, the person's own words. Returns `202`; read it at `GET /v3/api/agent/operations/<id>`. |
| `GET /v3/api/agent/sites`, `GET` / `POST /v3/api/agent/sites/<site>/operations` | Public reads that need no account (for example movie showtimes), run in a fresh browser with nothing signed in. Any partner key can call them. |
| `POST /v3/api/agent/site-jobs` | Add a website by its address: Nook connects the person's account there (or builds public reads only, with `"mode": "public"`) and generates its operations. Open only to pilot partners for now (`site_builder_access_required` otherwise). |
| `GET /v3/api/connections/health` | Public: Nook's status and the stores and sites a person can connect, with any operations each offers. |
| `POST` / `GET /v3/api/agent/purchases/<id>/order-status` | Refresh (`202`, `status: checking`) and read the status of a confirmed order. The guide documents it for Uber Eats; Nook's Oct 2 changelog adds Instacart to the same order reader. GET only reads the last snapshot. |

## Quote request

```json
{ "connection_id": "<connection.id>",
  "request": {
    "items": [{ "query": "<the product's own words>", "size": "<pack or size>", "quantity": 1,
                "options": [{ "name": "<what the user asked about>", "value": "<their words>" }] }],
    "destination": { "kind": "saved", "label": "<text in a saved address>" },
    "payment": { "kind": "saved", "network": "amex | visa | mastercard | discover", "last4": "<optional>" },
    "constraints": { "tip": { "amount_cents": 400, "currency": "USD" } },
    "merchant_data": { "ubereats": { "restaurant": "<its name>" } } } }
```

- `query` holds only the product's own words. Everything else goes in its own field. A requirement no field can express must be raised with the person, never dropped.
- A budget is sent only when the person states one: `"budget": { "amount_cents": 1000, "currency": "USD", "kind": "hard" }` inside `constraints`. `hard` marks a quote above it `over_budget`; `target` only reports it.
- `"preferences": "apply"` or `"ignore"` sits next to `connection_id`.

## Quote response

```json
{ "id", "status": "preparing | ready | blocked | cancelled", "merchant", "connection_id",
  "quote": { "items": [{ "title", "quantity", "unit_price_cents", "seller", "selected_variants", "availability" }], "lines": [{ "label", "cents" }],
             "total_cents", "currency", "destination": { "matched", "label", "place" },
             "payment": { "matched", "brand", "last4" }, "tip": { "cents", "requested", "matched" },
             "delivery", "expires_at" },
  "blocker": { "code", "message", "choices", "listings": [{ "product_id", "title", "price", "image", "url", "role" }] },
  "warnings": [{ "code", "message" }],
  "cleanup": "running | done | unconfirmed", "stages": [{ "stage", "ms" }], "preferences": { … },
  "created_at", "finished_at" }
```

- `lines` are the store's own charge rows and add up to `total_cents`.
- `selected_variants` names the color, size or pack the store's page actually selected (Amazon may pick one on a product-family page); `availability` is that page's own text, not a stock promise.
- When a blocker carries `listings`, show the person each one's picture, link, title and price. Never ask the person for a product ID: the listings carry them.
- Read the quote once more after `cleanup` is `done`; warnings about things Nook could not put back arrive late.
- Common blocker codes: `quote_failed` (Nook's own failure), `quote_check_failed` (checkout didn't match the request), `product_choice_requires_user` (the person must pick), `product_mismatch` (the chosen listing and the checkout disagree), `over_budget`.

## Purchase response

```json
{ "id", "quote_id", "connection_id", "merchant", "status", "quote", "approval_url", "approved_by", "confirmation",
  "receipt": { "order_id", "merchant_confirmed": true, "observed_at" },
  "failure", "detail", "warnings": [], "created_at", "updated_at" }
```

Statuses: `awaiting_approval`, `approved`, `submitting`, `confirmed`, `blocked`, `cancelled`, `uncertain`. Claim an order only from `confirmed`.

## Purchase rules

- `confirmation` is the person's reply to this quote's total, in their own words, 1 to 500 characters. Without it, the purchase waits until the person presses Buy on `approval_url`.
- Before its one click, Nook reads the checkout again and buys at the quote's total or less. It buys nothing if the total went up (`price_increased`, with `detail.was_cents` and `detail.now_cents`) or the item, address or card changed.
- A store that keeps no saved card (many Shopify stores) quotes the card as "Card you enter at purchase". The person types it on `approval_url` next to Buy; Nook enters it into that one checkout and keeps nothing. `confirmation` alone can't buy these.
- `failure: merchant_order_cancelled` (Uber Eats): Uber cancelled an identified order after it was placed; Nook did not cancel it. Check refunds with Uber and don't reorder automatically.
- `uncertain` means Nook pressed the store's final button but couldn't read the result, so the order may exist. Never buy it again; a new purchase of the same quote gets `409 purchase_uncertain`.
- `blocked` / `cancelled`: nothing was bought; the same quote can be bought again with a new request and a new `Idempotency-Key`.
- `quote_not_ready`: the quote is blocked, still cleaning up, or carries warnings.
- `409 in_use`: the store account is busy with another run; retry shortly. `404 not_found`: the quote or purchase belongs to another partner or doesn't exist.

## Quote refusals

Refused before any browser opens: `404 not_found` (no connection you hold), `400 quotes_unavailable`, `400 invalid_input` (`path`, `rule`), `400 unsupported_request` (`paths`: what this store's quotes can't honour yet, never dropped), `409 in_use`.

## Not documented here

- **TBD:** a public base URL for these docs. The agent guide names one; these docs leave it out until we decide what to publish.
- **TBD:** rate limits, pricing and SLAs. The sources don't state them.
