Skip to main content
The zeroclick-sellers package implements the ZeroClick billing guard for Python backends: it verifies that each request came from ZeroClick, checks the buyer’s allowance before you do the work, returns the refusal responses ZeroClick expects, and settles or reports what was used. This page wires the guard into FastAPI, Flask, and Django. The integration overview describes the contract it implements.

Install

The package requires Python 3.10 or later and is fully typed (it ships py.typed, so type checkers see every signature). Its HTTP calls go through httpx.
You need three values from the dashboard: your signing secret (zcsec_…), its key id (hsec_…), and an API key (zc_…) with the usage:read and usage:write scopes. See keys and secrets.

Pick the right client

Use the async client in ASGI apps. The blocking client would stall the event loop during every allowance check. The two clients accept the same options and make the same decisions; they differ only in how they perform IO.

Guard a FastAPI route

The handler is four moves:
  1. Adapt. zc_request_from_asgi_scope turns the framework request into a ZcRequest built from the raw path and raw body bytes.
  2. Guard. guard verifies the zc-signature header before it calls the allowance API, and returns a decision, not an exception.
  3. Deny. A deny decision carries the exact response to return: 401 for a bad signature, the 402 payment_required body ZeroClick converts into a payment challenge, or 503 under a fail-closed outage policy. An allow decision carries the verified context: decision.context.zc_agent_id identifies the buyer.
  4. Settle. with_usage stamps the zc-usage response header with the actual usage. ZeroClick records that usage and strips the header before the agent sees the response.

Flask and Django

The handler keeps the same shape: create_seller, no await, and the WSGI adapter in place of the ASGI one.

Why the adapters exist

The signature covers the request target exactly as ZeroClick sent it, so ZcRequest.path_and_query must be the raw, percent-encoded path and query. Every framework hands you a decoded one. These are the observed values for GET /v1/items/a%2Fb%20c: Using the decoded path produces a different canonical string and fails verification. The adapters handle this. They also cover a spec difference between the two interfaces: ASGI’s raw_path excludes the query string (the adapter appends scope["query_string"]), while WSGI’s RAW_URI and REQUEST_URI already include it. The body has the same rule: pass the raw bytes exactly as received, before any framework parses or re-serializes them. Use await request.body() in FastAPI, request.get_data() in Flask, and request.body in Django.
On WSGI, if the server sets neither RAW_URI nor REQUEST_URI, the SDK cannot recover an encoded separator. WSGI decodes %2F to / before the SDK runs, and nothing can tell it from a literal /. gunicorn, werkzeug, uWSGI, and nginx all set one of them.

Test the guard

The guard must deny a plain request with no ZeroClick headers. Only signed requests from ZeroClick reach your handler:
The quickstart covers the rest of the loop: deploying the guarded route and running the dashboard’s API setup verification against it.

Next steps

Configuration

Every constructor option: secrets, split usage keys, outage policy, timeouts.

API reference

Every public export, from guard to the encryption helpers.

Errors

Decisions versus exceptions, and every ZCError code.

Charge up to a maximum

Bill work you cannot size up front, like output tokens.