ZeroClick::Sellers.create
Builds a client. See configuration for every option.
ZeroClick::Sellers::Error with code malformed_input at construction, not at the first request.
#guard
Verifies the request, then checks the buyer can pay before you do the work.
Allow or Deny — never raises for an unpayable or unsigned request, because both are expected events.
#guard_identity
Guards a free endpoint that must still know the caller. Makes no network call.
Allow#allowance is "not_required". A signed anonymous probe is denied with reason identity_required and a 402 carrying empty usage.
#verify_request
Signature verification alone, with no allowance check.
zc_agent_id is nil is a signed anonymous probe — valid, not a failure.
#check_allowance
The allowance check on its own, without verification.
guard, this raises on an API failure rather than applying the outage policy — it has no verified context to fall back to.
#report_usage
Reports usage asynchronously, after the work is done.
duplicate? == true. That is a success, not a failure.
Response helpers
ZeroClick::Sellers::Responses exposes the same functions plus invalid_zeroclick_signature, allowance_unavailable and usage_header.
Value types
UsageItem
What a request will be charged for.
quantity or max_quantity; neither defers to the meter’s configured per-request ceiling. Declaring both raises.
A fixed quantity lets ZeroClick offer the exact x402 scheme. A max_quantity forces upto, which fewer clients can pay — prefer a fixed charge and report variable work afterwards.
The ceiling never overcharges: ZeroClick authorises up to it and bills what you settle. But it does gate the buyer’s authorisation, which is easy to miss. A ceiling of 50 units at $0.002 plus a $0.01 request charge makes every call reserve $0.11, and a buyer whose per-call cap is below that is refused before any work happens — a client capped at $0.10 is simply turned away. The unspent remainder never leaves their wallet, but an ambitious ceiling still prices out small callers, so keep it near a realistic worst case.
Middleware::Meter accepts only items with an explicit quantity, because it settles usage automatically. The other two forms are fine for guard, which only asks whether the buyer could pay.
SyncUsageItem
Actual usage, reported alongside a successful response via the zc-usage header.
Request
A framework-neutral inbound request. Build one from a Rack env with Middleware.request_from_env(env), or directly:
path_and_query must be the raw, percent-encoded target, and body the raw bytes. Decode nothing before verifying — the signature covers exactly what arrived.
Response
ZeroClickContext
Module helpers
Encrypted request bodies
A buyer can encrypt its request body end to end with your public key. The buyer can also request an encrypted reply. The helpers load only after this separate require:ECDH-ES+A256KW key management, A256GCM content encryption — so a sender cannot negotiate something weaker.
Two ordering rules are worth stating outright, because both are silently wrong in a way that still returns 200:
- Attach usage before encrypting.
zc-usageis a header, which is what lets an encrypted call settle through a proxy that cannot read the body. - Encrypt refusals too. A buyer who asked for confidentiality does not stop wanting it because the answer is a 4xx.
Unlike the Go SDK, there is no empty-plaintext limitation here: an encrypted request whose plaintext is empty decrypts normally, matching the TypeScript and Python SDKs.
The stateful helpers
require "zeroclick/sellers/stateful" implements the account routes for sellers whose purchases create accounts and API keys:
ZeroClick::Sellers::Stateful.handle_access_request(request, on_write:, on_mint:, base_path:, remint_policy:, ...): routes, verifies, and validates both account calls, then hands off to your two handlers. Returnsnilfor anything that is not one of its routes.Stateful::Entitlement.should_apply?,.derive_credit_delta,.period_advanced?, and.parse: the dedup and never-regress arithmetic an account write must be applied with.Stateful::Entitlement.serviceable?(state, now = Time.now): the one request-time gate —trueonly while the account isactiveand its period, when it has anend, has not lapsed.Stateful::Money.parse_money_usdand.format_money_usd: six-decimal money strings to integer micros and back, so no floating-point math ever touches money.Stateful::Verify.verify_access_signature,.canonical_access_string, and.sign_access_request: the purpose-carrying access signature on its own, plus a signer for integration tests.Stateful::Contracts: the wire shapes — parsed entitlements exposelifetime_credit_granted_usdandlifetime_credit_reversed_usd(plus_microsvariants) alongside the period and status.
#guard or #verify_request: account calls carry a different, purpose-separated signature by design. The account and key reference documents every field, status, and retry rule.