Skip to main content
An allowed request still has to settle: the payment finalizes against what the request actually used. Your revenue, credit burn, and analytics are computed from usage. The primary path is synchronous: stamp a zc-usage header on the successful response. ZeroClick records the usage as it returns the response to the agent. Reserve the asynchronous report for work that finishes after the response is gone.

Settle on the response

zc-usage is a response header carrying a JSON array of what the request used:
The SDK helpers build and validate it:
Three rules govern the header:
  • 2xx only. A 4xx is the buyer’s bad input and a 5xx is your failure; neither delivered anything, so neither bills. The Go middleware enforces this. In TypeScript and Python, call withUsage only on the success path.
  • ZeroClick strips it. The agent never sees zc-usage: the proxy consumes it and removes it from the response.
  • Settle the actual quantity. For a fixed quantity, that is what you declared. For a maxQuantity ceiling, it is anywhere from zero up to the ceiling. See charge up to a maximum. The platform accepts 0 (a delivered response that produced nothing settles the ceiling at $0). The TypeScript and Python helpers validate quantities as at least 1, so settle a zero-output ceiling by omitting that item.
A paid, delivered response always settles, even if your header is missing or malformed: fixed items settle at the authorized quantity, ceilings settle at zero. That guarantee protects the buyer’s payment from a garbage header. It does not excuse the report: an unreported ceiling settles as if nothing was produced.

Report usage

Some work outlives the response: a job that keeps extracting after you return 202, tokens streamed over a connection that has already closed, a worker that settles from a queue. Report that usage asynchronously with POST /v1/usage, through reportUsage, report_usage, or ReportUsage, using the usage:write key (split keys put it in the worker):
In Go, report with sellers.BackgroundContext(r), not r.Context(). Go cancels the request context the moment your handler returns, so a report on it is silently dropped. The work goes unbilled, with nothing in the logs.
Derive the idempotency key. Reports are idempotent per service on idempotencyKey, and the key is yours to construct: the SDKs never generate one. Derive it from stable facts, such as the request id plus the meter (zcreq_8h2m4x0q9k1f_output_tokens). A random key turns every retry into a double bill. duplicate: true means the key already landed and the stored event was replayed: a success, not an error. There are no automatic retries. Retry yourself with the same key. On the wire, the call and its response:
Request
Response
quantity is an integer of at least 1 (maximum 2,147,483,647); occurredAt is an optional ISO timestamp for when the usage happened. A report can fail with 402 (access_inactive, plan_expired, usage_exhausted), 409 (meter_not_priced), or otherwise 404 for a missing resource. The SDKs surface these as typed errors carrying the status and reason. See errors. A failed report after delivery means work went unbilled. Log it with the reason rather than failing a request the buyer already received.

Choose a path

  • Settle on the response when you know the quantity before you respond: most requests, including ceilings whose actual you know by the end of the handler.
  • Report asynchronously when the work finishes after the response, when settlement happens in a separate worker, or when there is no HTTP response to stamp (a connection upgraded away from HTTP, a queue consumer).
  • Combine them for work you can’t size up front: a fixed part settled on the response, the variable part reported when you know it. That is the pattern in charge up to a maximum.