# Subscription API (/docs/api/subscriptions)

These are the account-side tools and routes behind monthly subscriptions. For the
steps a person follows, see [Subscriptions and API keys](/docs/subscriptions).

> **Not open yet**
>
> Monthly subscriptions and API keys aren't open on quarry yet. This part describes how they will work. Today, agents pay per question from their own wallet, or use datasets their creators made free.

## Authentication [#authentication]

Send the account's OAuth token or a `qk_` API key as `Authorization: Bearer …`.
A regular quarry login token is not an agent token and is refused. Delegated access
can read subscriptions, use their quota, and prepare a Checkout link. It can't edit
datasets, manage payment details, or withdraw funds.

## MCP tools [#mcp-tools]

On the free address, `https://quarry.market/api/mcp`, these tools are listed for everyone. Calling one
without a connected account returns HTTP 401
with an OAuth challenge (`resource_metadata` points to
`/.well-known/oauth-protected-resource/api/mcp`), which clients use to start
sign-in. The sign-in address, `https://quarry.market/api/agent/mcp`, asks for sign-in right away and serves
the same tools, plus the older `search_datasets` and `query_dataset`.

| Tool                   | What it does                                                     |
| ---------------------- | ---------------------------------------------------------------- |
| `get_my_access`        | Your subscriptions and remaining quotas                          |
| `quote_access`         | Whether a question on a dataset is included, without using quota |
| `request_subscription` | Prepares a Checkout link for a monthly offer; doesn't charge     |

With an account connected, `ask` uses the subscription's quota before asking for
an x402 payment.

### `quote_access` statuses [#quote_access-statuses]

| Status              | Meaning                                                  |
| ------------------- | -------------------------------------------------------- |
| `included`          | A paid period has questions left; call `ask`             |
| `quota_exhausted`   | Paid access, but no questions left this period           |
| `approval_required` | No paid access; a monthly offer is available             |
| `payment_required`  | No paid access or offer; x402 is available               |
| `not_authorized`    | Neither a subscription nor a payment option is available |

The response includes `dataset_id`, `subscription_offer` (or `null`), `access`
(or `null`), and an `x402` alternative (or `null`). Pass `dataset_id` and
`subscription_offer.id` to `request_subscription` as `dataset_id` and `offer_id`.

## HTTP routes [#http-routes]

The same OAuth token or API key works on these routes:

| Route                          | Body                                         |
| ------------------------------ | -------------------------------------------- |
| `GET /api/agent/access`        | none                                         |
| `POST /api/agent/quote`        | `{ "username": "...", "slug": "..." }`       |
| `POST /api/agent/subscription` | `{ "dataset_id": "...", "offer_id": "..." }` |
| `POST /api/agent/query`        | the question object below                    |

```json
{
  "username": "publisher",
  "slug": "research",
  "query": "What changed in the latest report?",
  "top_k": 5,
  "request_id": "research-2026-09-10-001"
}
```

`request_id` is required on this route. You can send it in an `Idempotency-Key`
header instead, which wins over the body. The public dataset
`POST /{username}/{slug}` only takes x402 payments and never uses a subscription.

## Quota and request IDs [#quota-and-request-ids]

* One successful answer uses one question, even when the passages fit poorly, and
  `top_k` doesn't change the cost.
* Use a new `request_id` for each new question. To retry the identical request after
  a timeout, reuse the same ID: the saved answer is replayed for 24 hours without
  using more quota, even when the quota has run out since. IDs are shared by every
  connector of the account, per dataset.
* Over MCP, `request_id` is optional on `ask`. Without one, each call is a new
  question, so a retry after a timeout uses another one: pass an ID whenever your
  agent may retry.
* The same ID with a different `query` or `top_k` returns `IDEMPOTENCY_CONFLICT`.
  After 24 hours, that ID returns `RESULT_EXPIRED`.
* A request still running returns `REQUEST_IN_FLIGHT`. A failed search releases
  its reservation.
* Remaining quota is `quota - used - reserved`: questions in progress hold a unit
  until they finish. The quota resets with each paid month, and unused questions
  don't roll over.

When the quota runs out, `POST /api/agent/query` returns `QUOTA_EXHAUSTED` with the
access quote and any x402 option. Over MCP, `ask` falls back to an x402 payment
request instead, or returns `SUBSCRIPTION_REQUIRED` when the dataset only sells
subscriptions. quarry never buys extra questions on its own: paying per question
takes an explicit signed payment. See [Paying with x402](/docs/x402).

## Retiring datasets [#retiring-datasets]

If a seller stops selling a dataset, current subscribers keep access through these
routes until their paid period ends. The dataset leaves public discovery and can't
be bought during that time.

See [Error codes](/docs/api/errors#subscription-and-agent-apis) for every error code.
