> ## Documentation Index
> Fetch the complete documentation index at: /docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors

> Every machine error code ZeroClick returns, by surface: the REST API, the usage endpoints, the responses your API must return, and the agent-facing pay URL.

ZeroClick returns errors on four surfaces: the REST API at `https://api.zeroclick.io`, the usage endpoints under `/v1/usage`, your own API (which returns three fixed bodies to ZeroClick), and the agent-facing pay URL (`https://acme.pay.zeroclick.io`). This page lists every machine code per surface.

## The error envelope

Every error body is JSON with a machine code in `error`:

```json theme={null}
{ "error": "usage_exhausted" }
```

Some errors carry additional fields: pay URL errors may add a `reason` (and `settlementReason`), and `amount_below_minimum` adds `minimumPurchaseUsd`. Two cases fall outside the machine-code convention:

* Request validation failures return `400` with a human-readable message in `error` describing the invalid field.
* Server errors return `500 { "error": "Internal server error" }`. The body never carries internals.

## REST API errors

Management endpoints (sellers, services, meters, plans, prices, signing secrets, API keys, analytics) authenticate with `Authorization: Bearer zc_…` and share these codes:

| Code | Status | Meaning |
| - | - | - |
| `missing_bearer_token` | 401 | The request has no `Authorization: Bearer` header. |
| `invalid_token` | 401 | The API key or session token did not verify. |
| `auth_type_not_allowed` | 403 | The endpoint does not accept this credential type. For example, API keys cannot manage API keys (session-only), and the usage endpoints accept API keys only. |
| `insufficient_scope` | 403 | The API key lacks a scope the endpoint requires. See [keys and secrets](/docs/integrate/keys-and-secrets.md). |
| `organization_required` | 403 | The caller has no current organization. |
| `organization_permission_required` | 403 | The caller may not act on the selected organization. |
| `seller_not_found` | 404 | No such seller in your organization. |
| `service_not_found` | 404 | No such service. |
| `meter_not_found` | 404 | No such meter. |
| `plan_not_found` | 404 | No such plan. |
| `plan_meter_price_not_found` | 404 | No such plan meter price. |
| `signing_secret_not_found` | 404 | No such signing secret. |
| `duplicate_slug` | 409 | A seller, service, or plan with this slug already exists in that scope. |
| `duplicate_key` | 409 | A meter with this key already exists on the service. |
| `duplicate_plan_meter_price` | 409 | The plan already prices this meter. |
| `payg_price_not_whole_cents` | 422 | Pay-as-you-go rates are charged per call, so `priceUsd` must be a whole-cent amount. Rates on credit and subscription plans keep full 6-decimal precision. |

> [!NOTE]
>
> 404 is the default for anything that does not resolve. A resource that exists but belongs to another organization is also a 404, never a 403.

### Agentify errors (`GET /v1/agentify/markdown`)

The [Agentify](/docs/agentify/overview.md) endpoint requires an API key with the `agentify:convert` scope; a wrong scope is `403 insufficient_scope`, a non-API-key credential is `403 auth_type_not_allowed`, and requests beyond 60 per minute per organization answer `429`. Its own codes:

| Code | Status | Meaning |
| - | - | - |
| `invalid_url` | 400 | The `url` is not a public `http(s)` page: IP literals, credentials, non-standard ports, and non-public hostnames are rejected. |
| `seller_required` | 400 | The organization has zero or several active sellers, or the `seller` parameter matched none of them. Pass `?seller=sel_…`. |
| `page_not_found` | 404 | The page answered `404` or `410`. The SDK middleware falls through, so your site serves its own not-found page. |
| `content_too_thin` | 422 | The page converted to too little readable content, usually a JavaScript app serving an empty shell. See [SPA setup](/docs/agentify/spa-setup.md). |
| `fetch_failed` | 502 | The page could not be fetched: DNS, TLS, a network failure, too many redirects, or an oversized body. |

## Usage endpoint errors

Both usage endpoints live at `https://api.zeroclick.io` and require an API key: `POST /v1/usage/check` needs scope `usage:read`, `POST /v1/usage` needs `usage:write`. A wrong scope is `403 insufficient_scope`; a non-API-key credential is `403 auth_type_not_allowed`.

### Allowance denials (`POST /v1/usage/check`)

A denial is not an HTTP error. The check returns `200` with the decision, and checking records nothing and burns no credit:

```json theme={null}
{ "allowed": false, "reason": "usage_exhausted" }
```

`reason` is `null` when `allowed` is `true`, and exactly one of these seven codes otherwise:

| Reason | Meaning | Fix |
| - | - | - |
| `service_not_found` | No service with this slug under the organization your API key belongs to. | Send the service slug as configured in the dashboard, with a key from the owning organization. |
| `access_not_found` | The `zcRequestId` does not match a live ZeroClick request for this service, or the request has no purchase or payment behind it. | Pass the `zc-request-id` header value through unchanged. For unpaid traffic, this is the expected denial: return the `402 payment_required` body, and ZeroClick prices the challenge. |
| `access_inactive` | The buyer's access is not active: canceled, or its period has not started. | Deny with the `402` body; the agent must purchase again. |
| `plan_expired` | The access period has ended. | Deny with the `402` body; the agent re-purchases. |
| `meter_not_found` | No meter with this slug exists on the service. | Use the meter key exactly as configured on the service. |
| `meter_not_priced` | The buyer's plan does not price this meter. Also returned when an item declares neither `quantity` nor `maxQuantity` and the price has no `defaultMaxQuantity` to gate against. | Price the meter on the plan, or set the price's `defaultMaxQuantity` (or declare an explicit `maxQuantity` per request). |
| `usage_exhausted` | The plan cannot cover this usage: credit or included allowance has run out. | Deny with the `402` body; ZeroClick challenges the agent to pay or top up. |

See [check allowances](/docs/integrate/check-allowances.md) for the request shape and [usage and allowances](/docs/concepts/usage-and-allowances.md) for how ZeroClick decides coverage.

### Report errors (`POST /v1/usage`)

A replayed `idempotencyKey` is a success (`200` with `duplicate: true`), not an error. Failures map to:

| Status | Codes | Meaning |
| - | - | - |
| 402 | `access_inactive`, `plan_expired`, `usage_exhausted` | The access exists but can no longer be billed. |
| 409 | `meter_not_priced` | The buyer's plan does not price this meter. |
| 404 | `service_not_found`, `access_not_found`, `meter_not_found` | The slug or agent does not resolve (the default for anything missing). |

## Errors your API returns to ZeroClick

Your API answers ZeroClick with exactly three non-2xx bodies. The [seller SDKs](/docs/sdks/overview.md) build all of them; if you integrate over [REST](/docs/integrate/rest-walkthrough.md), return them byte-for-byte.

An invalid, missing, or stale `zc-signature`, before any other work:

```json 401 theme={null}
{ "error": "invalid_zeroclick_signature" }
```

A business denial (no allowance, unpaid probe). ZeroClick reads the `usage` list, prices it, and issues the buyer a payment challenge. The buyer never sees this body. `planSlug` is optional; `usage: []` means an identity challenge for a [free identity-scoped endpoint](/docs/integrate/free-and-identity-endpoints.md):

```json 402 theme={null}
{
  "error": "payment_required",
  "serviceSlug": "product-watch",
  "usage": [{ "meterSlug": "requests", "quantity": 1 }]
}
```

The allowance API unreachable under a fail-closed outage policy:

```json 503 theme={null}
{ "error": "allowance_unavailable" }
```

## Errors agents see at your pay URL

Buyers transact at your pay URL (`https://acme.pay.zeroclick.io`). Once ZeroClick forwards a paid request, agents receive your API's own status codes and bodies unchanged. The codes below are ZeroClick's own, issued before or instead of the forward:

| Code | Status | Meaning |
| - | - | - |
| `invalid_request` | 400 | Malformed purchase or top-up body. |
| `amount_required` | 400 | A credit plan purchase without `amountUsd`. |
| `payg_not_purchasable` | 400 | Purchase attempt on a pay-as-you-go plan: payg is called, never purchased. |
| `amount_not_cent_increment` | 400 | The `amountUsd` is not a whole-cent value; buyer-chosen amounts settle on-chain in whole cents. |
| `amount_below_minimum` | 400 | The `amountUsd` is under the plan's minimum purchase; the body carries `minimumPurchaseUsd`. |
| `encryption_not_configured` | 400 | An `application/jose` body was sent but the seller publishes no encryption keys. |
| `invalid_compact_jwe` | 400 | The body is not a five-segment Compact JWE. |
| `unsupported_jwe_suite` | 400 | The JWE does not use the seller's fixed `alg`/`enc` suite. |
| `jwe_kid_required` | 400 | The JWE protected header names no `kid`. |
| `unknown_jwe_kid` | 400 | The `kid` is not in the seller's published JWKS. |
| `private_reply_jwk` | 400 | The reply JWK carries private key material. |
| `invalid_reply_jwk` | 400 | The reply JWK is not a public P-256 key. |
| `bearer_required` | 401 | The call needs a registered agent credential: buying or extending a plan, or a free-mode call covered by included units (the free case also requires the credential to be claimed by a human with a verified email). The body carries an `auth` block with the registration recipe. Pay-as-you-go per-call payments need no credential. |
| `payment_required` | 402 | The payment challenge: the one 402 an agent pays. A `reason` field distinguishes the cases below. |
| `plan_required` | 403 | A registered agent's plan does not include a meter the call uses (`reason: not_included_in_plan`, with the meters named), or the agent holds no plan and the call has no per-request price (`reason: access_not_found`). The body's `plans` lists only the plans that cover the call, each with its `purchase.url`. Buy one with the bearer, then retry. |
| `seller_not_found` | 404 | The host does not map to a seller. |
| `plan_not_found` | 404 | Unknown plan id; agents use ids from `GET /manifest.json`. |
| `signing_secret_required` | 409 | The seller has no active signing secret: setup is incomplete. |
| `payment_request_mismatch` | 409 | The paid retry differs from the challenged request: method, path, or body bytes changed. The payment is bound to the request's body digest, so the retry must be byte-identical. |
| `payment_request_already_completed` | 409 | A retry of a request that already delivered. Each payment forwards its request once. |
| `payment_request_in_flight` | 409 | A concurrent retry while another attempt for the same request is still running. |
| `payment_amount_mismatch` | 409 | A purchase or top-up retry carried a different `amountUsd` than the challenge that was paid. |
| `payment_rail_unavailable` | 409 | No payment method the seller accepts can settle this charge. The usual case is a card-only seller (no stablecoin payments) and a per-call charge or top-up below the seller's card minimum (\$0.50, or a little more for a seller paid out in another currency): buy credits or a plan by card, then retry with the bearer. |
| `usage_not_priced` | 409 | The upstream's refusal names usage no plan can price, so ZeroClick cannot mint a per-call challenge. When the gap is the calling agent's own plan, it receives `plan_required` instead. |
| `entitlements_not_available` | 501 | This storefront does not sell plans or top-ups. Pay-as-you-go per call still works. |
| `capped_pricing_unavailable` | 503 | The request needs ceiling pricing but no reserve-and-pay-actual rail is currently available. |

The seven `400` JWE codes apply only to requests sent with `Content-Type: application/jose` (end-to-end encrypted bodies). ZeroClick returns them before it asks for any payment.

> [!NOTE]
>
> A billing failure detected after your API already delivered a 2xx does not become an error status. ZeroClick returns the delivered response and surfaces the failure in the `zc-usage-error` response header. See [headers](/docs/resources/headers.md).

### `402 payment_required` reasons

| Reason | Meaning |
| - | - |
| *absent* | The normal per-call charge: the body carries a `payment` block with the exact `amountUsd` and a signable challenge per protocol. The agent pays and retries. |
| `identity_invalid` | The identity proof did not verify. The agent signs a fresh challenge. |
| `access_not_found` | The agent has no active plan to draw from or extend. It purchases one first. |
| `usage_exhausted` | The plan's credit ran out. The agent tops up or purchases again. |
| `settlement_failed` | The signed payment did not settle; `settlementReason` says why (for example `insufficient_funds`). The agent fixes its wallet and restarts for a fresh challenge. |

A priced challenge looks like this (the same challenge also rides in the `payment-required` and `www-authenticate` response headers):

```json 402 theme={null}
{
  "error": "payment_required",
  "zcAgentId": "agt_x7f2kq93bh0d",
  "zcRequestId": "zcreq_8h2m4x0q9k1f",
  "serviceSlug": "product-watch",
  "usage": [{ "meterSlug": "requests", "quantity": 1 }],
  "plan": { "id": "pln_...", "slug": "metered", "billingMode": "payg" },
  "payment": {
    "id": "apay_...",
    "amountUsd": "0.010000",
    "rail": "base_usdc",
    "network": "base"
  },
  "protocols": {
    "x402": {
      "x402Version": 2,
      "network": "eip155:8453",
      "scheme": "exact",
      "amount": "10000",
      "asset": "0x..."
    },
    "mpp": { "challengeId": "...", "method": "tempo", "intent": "charge" }
  }
}
```

The body also carries the standard x402 document fields (`x402Version`, `resource`, `accepts`) at the top level for body-reading clients. ZeroClick omits `rail` and `network` on anonymous challenges, where the agent picks a protocol by paying. The full lifecycle around this challenge is on [how ZeroClick works](/docs/concepts/how-zeroclick-works.md); the protocols themselves are on [payment protocols](/docs/concepts/payment-protocols.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.