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

# Check allowances

> Guard before work: declare what a request will use, let ZeroClick decide whether the buyer's plan covers it, and handle denials and allowance-API outages.

After a signature verifies, and before your API does any work, ask ZeroClick whether the buyer's plan covers what this request will use. That is the allowance check: `POST /v1/usage/check` with the request's `zcRequestId` and the declared usage. Checking records nothing and burns no credit; it is a pre-gate, not a charge. For the model behind it, see [usage and allowances](/concepts/usage-and-allowances).

The SDK `guard` runs the check for you, immediately after verification:

<CodeGroup>
  ```ts TypeScript theme={null}
  const decision = await zeroClick.guard(request, {
    serviceSlug: "product-watch",
    usage: [{ meterSlug: "requests", quantity: 1 }],
  });
  if (decision.action === "deny") return decision.response;

  // decision.context.zcRequestId, decision.context.zcAgentId
  // decision.allowance.status === "allowed" | "unavailable"
  ```

  ```python Python theme={null}
  decision = await zeroclick.guard(
      zc_request,
      service_slug="product-watch",
      usage=[UsageItem(meter_slug="requests", quantity=1)],
  )
  if decision.action == "deny":
      return to_fastapi(decision.response)

  # decision.context.zc_request_id, decision.context.zc_agent_id
  # decision.allowance == "allowed" | "unavailable"
  ```

  ```go Go theme={null}
  result, err := seller.Guard(r.Context(), sellers.FromHTTP(r, body),
  	"product-watch", []sellers.UsageItem{{MeterSlug: "requests", Quantity: 1}}, "")
  if err != nil {
  	// The usage list is malformed, or the outage policy is Throw.
  	http.Error(w, `{"error":"internal_error"}`, http.StatusInternalServerError)
  	return
  }
  if !result.OK {
  	result.Response.WriteTo(w)
  	return
  }
  // result.Context.RequestID, result.Context.AgentID
  // result.Outcome == sellers.OutcomeAllowed | sellers.OutcomeUnavailable
  ```
</CodeGroup>

In Go, the [`Meter` middleware](/sdks/go/middleware) wraps `Guard` and the usage settlement in one. When you need the decision in your own handler, call `Guard` directly.

## Declare what the request will use

Each usage item names a meter and takes one of three forms:

| Form          | Meaning                                                                                                                                                                    |
| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `quantity`    | An exact amount, known up front.                                                                                                                                           |
| `maxQuantity` | A ceiling for work you can't size yet; the payment settles at what you [report](/integrate/settle-usage). See [charge up to a maximum](/integrate/charge-up-to-a-maximum). |
| Neither       | Defer to the meter's configured **Max usage per request** (`defaultMaxQuantity`). If the meter has none, the check denies with `meter_not_priced`.                         |

Before any network call, the SDK rejects an item that declares both `quantity` and `maxQuantity`. A check takes 1 to 20 items, one per meter. The SDK rejects duplicate meters too.

## Read the decision

`guard` returns a decision, not an exception. A buyer who can't pay is an expected event:

* **Deny** carries a ready-to-return response: the `401` for a failed signature, the exact `402 payment_required` body for a business denial, or the `503` under a fail-closed outage policy. Return it unchanged (`decision.response` in TypeScript and Python, `result.Response` in Go). Do no work. The `reason` field carries one of the seven denial reasons, listed at [errors](/resources/errors).
* **Allow** carries the verified context (`zcRequestId`, `zcAgentId`) and an allowance status:

| Status         | Meaning                                                                                                                                                         |
| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `allowed`      | The buyer's plan covers the declared usage.                                                                                                                     |
| `unavailable`  | The allowance API gave no answer and your policy chose to serve anyway: possibly unbilled work.                                                                 |
| `not_required` | Identity was proven and no allowance was needed. Only `guardIdentity` produces this. See [free and identity endpoints](/integrate/free-and-identity-endpoints). |

## When the allowance API gives no answer

The check sits in front of every billable request, so its timeout is short: 1.5 seconds by default (`checkTimeoutMs` / `check_timeout_seconds` / `CheckTimeout`). When it expires, or the API is unreachable or failing, your configured policy decides:

| Policy            | Behavior                                                                                                         |
| ----------------- | ---------------------------------------------------------------------------------------------------------------- |
| `allow` (default) | Serve the request with allowance status `unavailable`. A ZeroClick outage should not take your API down with it. |
| `deny`            | Refuse with the `503`. When unbilled work costs more than a failed request, choose this policy.                  |
| `throw`           | Surface the typed error and decide in application code.                                                          |

The policy applies only after a signature has verified: a fail-open allowance policy never becomes a fail-open signature policy. The Go SDK further restricts the policy to genuine no-answers:

| Condition                                                 | Go SDK behavior                                                                                    |
| --------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| Timeout, DNS failure, connection refused, 5xx, 429, 408   | The policy applies.                                                                                |
| Any other 4xx (revoked or wrong API key, unknown service) | Always an error, never fail-open: it is permanent and would give your product away indefinitely.   |
| `allowed: false` with a reason the SDK does not recognize | Always a `402`. The SDK honors the denial and passes the unfamiliar reason through for you to log. |
| The caller's context was cancelled                        | Not an outage. The check runs anyway on its own timeout, and its real answer stands.               |

<Note>
  The TypeScript and Python SDKs route every allowance API error through the policy, including 4xx responses and unrecognized denial reasons. Under the default `allow` policy, monitor `onAllowanceUnavailable` for repeated incidents: a persistent 4xx such as a revoked key means you are serving every request unbilled. Each SDK's errors page documents its exact classification.
</Note>

Set `onAllowanceUnavailable` (and, in Go, `Logger`) to record every incident the policy absorbs. A fail-open that is also silent is invisible unbilled traffic:

<CodeGroup>
  ```ts TypeScript theme={null}
  const zeroClick = createSeller({
    signingSecrets: { [kid]: secret },
    apiKey: process.env.ZEROCLICK_API_KEY!,
    allowanceUnavailable: "allow",
    onAllowanceUnavailable: (error) => logger.warn("allowance outage", error.context),
  });
  ```

  ```python Python theme={null}
  zeroclick = create_async_seller(
      signing_secrets={kid: secret},
      api_key=os.environ["ZEROCLICK_API_KEY"],
      allowance_unavailable_policy="allow",
      on_allowance_unavailable=lambda error: logger.warning("allowance outage: %s", error),
  )
  ```

  ```go Go theme={null}
  seller, err := sellers.New(sellers.Config{
  	APIKey:                 os.Getenv("ZEROCLICK_API_KEY"),
  	ServiceSlug:            "product-watch",
  	SigningSecrets:         secrets,
  	Policy:                 sellers.PolicyAllow,
  	Logger:                 log.Default(),
  	OnAllowanceUnavailable: func(err error) { alert("allowance outage", err) },
  })
  ```
</CodeGroup>

## Call the allowance API directly

For flows where verification and the check live in different layers, call the check yourself with a `zcRequestId` from an already-verified context. The response is `{ "allowed": boolean, "reason": <denial reason or null> }`. Building the `402` from a denial is then on you (`paymentRequired` / `payment_required` / `PaymentRequired` construct the exact body).

<CodeGroup>
  ```ts TypeScript theme={null}
  const decision = await zeroClick.checkAllowance({
    zcRequestId: context.zcRequestId,
    serviceSlug: "product-watch",
    usage: [{ meterSlug: "output_tokens", maxQuantity: 100_000 }],
  });
  ```

  ```python Python theme={null}
  decision = await zeroclick.check_allowance(
      zc_request_id=context.zc_request_id,
      service_slug="product-watch",
      usage=[UsageItem(meter_slug="output_tokens", max_quantity=100_000)],
  )
  ```

  ```go Go theme={null}
  decision, err := seller.CheckAllowance(r.Context(), zc.RequestID,
  	"product-watch", []sellers.UsageItem{{MeterSlug: "output_tokens", MaxQuantity: 100_000}})
  ```
</CodeGroup>

Unlike `guard`, the direct check surfaces outages as errors instead of applying the policy. It runs under your own cancellation (an optional `{ signal }` in TypeScript, the caller's context in Go). Once a request is allowed, serve it. Then [settle the usage](/integrate/settle-usage).
