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
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 answeredallowed: false. The response is the exactpayment_requiredbody ZeroClick converts into a payment challenge, built fromservice_slug,usage, andplan_slug. - Deny,
503: the allowance API gave no answer and the client’s policy is"deny". - Allow: carries the verified
contextand anallowanceof"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
guard, makes no allowance call (so it is synchronous on both clients), and:
- denies a bad signature with the
401response, - denies a verified request without a buyer identity (reason
identity_required) with the402 payment_requiredbody carryingusage: [], which ZeroClick answers with a free identity challenge, - allows an identified buyer with
allowance == "not_required", and in that casedecision.context.zc_agent_idis always non-empty.
verify_request
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
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
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
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
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: thezc-usageheader value alone, for stamping a framework response directly instead of going throughZcResponse.invalid_zeroclick_signature() -> ZcResponse: the401 {"error":"invalid_zeroclick_signature"}response. Deny decisions already carry it; the export is for manual flows.allowance_unavailable() -> ZcResponse: the503 {"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
bodymust be the raw bytes exactly as received. Passing anything else raisesZCError(malformed_input, “body must be bytes; decode nothing before verifying”).path_and_querymust be the raw, percent-encoded request target. Decoded paths fail verification; the adapters recover the raw target for you.
request.header(name) looks up case-insensitively:
ZcResponse
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
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_identityallowed 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
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.
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
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.
SyncUsageItem
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) -> strandwsgi_path_and_query(environ) -> str: the raw request-target recovery alone, for custom integrations.
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, soguard 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.
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.