Skip to main content
Everything on this page imports from zeroclick_sellers, except the framework adapters, which live in zeroclick_sellers.adapters. See configuration for constructor options and errors for error codes. create_seller(**options) returns a SellerClient (blocking, for WSGI); create_async_seller(**options) returns an AsyncSellerClient (non-blocking, for ASGI). The SDK exports both classes for type annotations. The methods below have identical signatures and semantics on both clients; on AsyncSellerClient, guard, check_allowance, and report_usage are coroutines you await. guard_identity and verify_request make no network call, so they are synchronous on both clients.

Guard methods

guard

The whole billing guard in one call. It verifies the zc-signature header first, and only then checks the declared usage against the buyer’s allowance with POST /v1/usage/check. The SDK never consults the allowance API for an unverified request. The result is a decision, not an exception:
  • Deny, 401: a missing, malformed, stale, or invalid signature.
  • Deny, 402: the allowance API answered allowed: false. The response is the exact payment_required body ZeroClick converts into a payment challenge, built from service_slug, usage, and plan_slug.
  • Deny, 503: the allowance API gave no answer and the client’s policy is "deny".
  • Allow: carries the verified context and an allowance of "allowed" or "unavailable".
usage must contain at least one UsageItem and no duplicate meters; the allowance API accepts at most 20 items per check. When set, plan_slug appears in a denial’s 402 body as planSlug. guard raises ZCError only for malformed input, a failing secret resolver, or an allowance outage under the "throw" policy.

guard_identity

Guards a free, identity-scoped endpoint: one that costs nothing but must know which buyer is calling. It verifies the signature exactly like guard, makes no allowance call (so it is synchronous on both clients), and:
  • denies a bad signature with the 401 response,
  • denies a verified request without a buyer identity (reason identity_required) with the 402 payment_required body carrying usage: [], which ZeroClick answers with a free identity challenge,
  • allows an identified buyer with allowance == "not_required", and in that case decision.context.zc_agent_id is always non-empty.

verify_request

Signature verification alone, using the client’s secrets, tolerance, and clock. This is the first half of guard, for when you want to run the allowance check separately or not at all. It returns VerifyOk with the proven context, or VerifyFailure with a reason and the ready-to-return 401 response. The failure reasons are missing_signature, malformed_signature, stale_timestamp, missing_request_id, unknown_kid, and invalid_signature; every one maps to the same 401 {"error":"invalid_zeroclick_signature"} body, so the reason is for your logs, not the caller. The HMAC comparison is constant-time.
verify_request is also a standalone function for use without a client. It takes the same request plus its own configuration. Provide exactly one of signing_secrets or resolve_signing_secret, and optional tolerance_seconds (default 300) and clock:

Usage API methods

check_allowance

The allowance check alone, without verification or response-building. Use it for a second check partway through a multi-stage request, or when you have already verified. zc_request_id must be the id from the verified request’s context. Checking records nothing and burns no credit. Unlike guard, any API failure raises ZCError: the outage policy applies only inside guard.

report_usage

Reports usage asynchronously with POST /v1/usage, for work that finishes after the response has gone out. This is the alternative to settling with the zc-usage header. All arguments are keyword-only. quantity is an integer of at least 1 (at most 2,147,483,647); occurred_at is an optional ISO 8601 timestamp. Reporting is idempotent per service on idempotency_key. Derive the key from stable facts (zcreq_8h2m4x0q9k1f_output_tokens, not a random value) so a retry of your own job cannot double-bill. report_usage does not generate idempotency keys and does not retry. A result with duplicate=True means the key already landed and the API returned the stored event. That is a success, not an error. A rejected report raises ZCError; see errors for reading the status and reason.

Response builders

payment_required

Builds the exact 402 refusal ZeroClick converts into a payment challenge, the same response a guard denial carries. Use it to deny for business reasons of your own. An empty usage is meaningful, not a mistake: it is the free identity-scoped refusal, which ZeroClick answers with a $0 identity challenge. It is available as a static method on both clients and as a top-level import.

with_usage

Returns a copy of the response with the zc-usage header attached: the synchronous settlement path. Attach it to 2xx responses only; ZeroClick records the usage and strips the header before the agent sees the response. It is available as a static method on both clients and as a top-level import.

Other builders

  • usage_header(usage: Iterable[SyncUsageItem]) -> str: the zc-usage header value alone, for stamping a framework response directly instead of going through ZcResponse.
  • invalid_zeroclick_signature() -> ZcResponse: the 401 {"error":"invalid_zeroclick_signature"} response. Deny decisions already carry it; the export is for manual flows.
  • allowance_unavailable() -> ZcResponse: the 503 {"error":"allowance_unavailable"} response the fail-closed outage policy uses.

Request and response types

All SDK types are frozen dataclasses: immutable after construction, validated in __post_init__, and comparable by value.

ZcRequest

The framework-neutral view of an inbound request that every verification entry point consumes. Two rules matter:
  • body must be the raw bytes exactly as received. Passing anything else raises ZCError (malformed_input, “body must be bytes; decode nothing before verifying”).
  • path_and_query must be the raw, percent-encoded request target. Decoded paths fail verification; the adapters recover the raw target for you.
The constructor lowercases header names, and request.header(name) looks up case-insensitively:

ZcResponse

The framework-neutral response the SDK hands back; your adapter turns it into a real framework response. ZcResponse.json(payload, *, status=200, headers=None) builds one with compact JSON and a content-type: application/json header; response.json_body() parses the body back.

Decision types

GuardResult

Discriminate on action (or match on the class). Allow.allowance tells you how the guard cleared the request:
  • "allowed": the allowance API confirmed coverage.
  • "unavailable": the allowance API gave no answer and the "allow" policy let the request through.
  • "not_required": guard_identity allowed the request and made no allowance check.
Deny.reason is one of the six verification failures (response 401); one of the seven allowance denial reasons (service_not_found, access_not_found, access_inactive, plan_expired, meter_not_found, meter_not_priced, usage_exhausted) or allowance_denied when the API denies without a reason (response 402); allowance_unavailable under the fail-closed policy (response 503); or identity_required from guard_identity (response 402). Deny.response is always ready to return as-is.

VerifyResult

verify_request returns this type. Discriminate on ok.

ZeroClickContext

Proven facts about a verified request: the correlation id (zcreq_…), the id of the agent that made the call (agt_…), the signature’s timestamp in Unix seconds, and the key id that signed it. zc_anonymous_id repeats zc_agent_id under the name that will eventually replace zc-agent-id. zc_buyer_id (byr_…) is the buyer that agent belongs to, and is None for an anonymous agent; unlike the fields above it is not covered by the signature. See agents and access.
A verified request without a buyer identity is a signed anonymous probe. ZeroClick forwards one to price a pay-as-you-go 402, so it is valid, expected traffic. Test for it with truthiness, not is None: an absent zc-agent-id header gives None, but a present-but-empty one gives "", and both mean the same thing.
guard_identity already handles both.

AllowanceDecision

check_allowance returns this type. reason is None when allowed, otherwise one of the seven allowance denial reasons.

ReportUsageResult

report_usage returns this type. usage_event is the API’s recorded event: id, zcAgentId, zcRequestId, idempotencyKey, meterSlug, quantity, totalCostUsd, source, and occurredAt.

Usage items

UsageItem

What a request will be charged for, used by guard, check_allowance, and payment_required. Declare at most one of quantity (a known amount) or max_quantity (a ceiling for work you cannot size up front). Declaring both raises ZCError. Declaring neither defers to the meter’s configured per-request ceiling; if the meter has none, the API denies the check with meter_not_priced. Quantities must be positive integers.
The buyer authorizes up to a ceiling and settles at the actual amount you report, so a ceiling never overcharges.

SyncUsageItem

Actual usage settled alongside a successful response, used by with_usage and usage_header. All fields are required; quantity must be a positive integer.

Standalone verification

canonical_string builds the exact string ZeroClick signs: six fields joined by newlines. You need it only to debug a verification mismatch or implement verification elsewhere; the signature spec specifies the format.

Framework adapters

  • zc_request_from_asgi_scope(scope, body) -> ZcRequest: builds a request from an ASGI scope and the already-read raw body (FastAPI, Starlette).
  • zc_request_from_wsgi_environ(environ, body) -> ZcRequest: the same for a WSGI environ (Flask, Django).
  • asgi_path_and_query(scope) -> str and wsgi_path_and_query(environ) -> str: the raw request-target recovery alone, for custom integrations.
The adapters’ whole job is recovering the raw, percent-encoded request target that the signature covers; see why the adapters exist. In both cases body must be the raw bytes, read before any framework parses or re-serializes them.

Module defaults

Encryption

For services that opt into body encryption, the request body arrives as a Compact JWE and the reply goes back the same way. The signature covers the ciphertext, so guard runs first and unchanged; decrypt only after an allow decision.
resolve_private_key receives the kid from the JWE protected header and returns that private key (a JWK mapping or a joserfc ECKey), or None if it is unknown. DecryptedEnvelope carries the plaintext bytes plus what encrypt_response needs to encrypt the reply (protected_header, cty, reply_jwk). encrypt_response returns the response unchanged when the request carried no reply key, so the same handler serves encrypted and plaintext buyers.
The suite is fixed at ECDH-ES+A256KW key management with A256GCM content encryption; the SDK rejects anything else. If a reply key arrives with private key material, the SDK rejects it outright rather than using it. Failures raise ZCError with JWE-specific string codes.

Errors

ZCError and is_zc_error(error, code=None) are the last two exports. Guard outcomes are decisions; ZCError is reserved for malformed input and ZeroClick API failures. The errors page covers every code and how to handle them.