> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zeroclick.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# TypeScript SDK API

> Every public export of @zeroclickai/sellers: client methods, standalone functions, result shapes, and the contracts and encryption subpaths.

Everything in `@zeroclickai/sellers` comes two ways: as bound methods on the client `createSeller(config)` returns, and as standalone named exports. The standalone exports are for applications that place verification, allowance checking, and response construction in separate middleware layers. All public functions validate their inputs and throw a typed [`ZCError`](/sdks/typescript/errors) for malformed input or API failure. Signature failures and business denials are returned decisions, never exceptions.

## Client methods

`createSeller(config)` (see the [configuration reference](/sdks/typescript/configuration)) returns these bound methods:

| Method                            | Purpose                                                                                            |
| --------------------------------- | -------------------------------------------------------------------------------------------------- |
| `guard(request, input)`           | Verify the request, check the allowance, and return an allow or deny decision.                     |
| `guardIdentity(request, input)`   | Verify the request and require a proven buyer for a free, identity-scoped call. No allowance call. |
| `verifyRequest(request)`          | Verify only the ZeroClick request signature without consuming the original body.                   |
| `checkAllowance(input, options?)` | Call `POST /v1/usage/check` directly.                                                              |
| `paymentRequired(input)`          | Construct the exact seller `402 payment_required` response.                                        |
| `withUsage(response, usage)`      | Set the validated `zc-usage` header without consuming the response body.                           |
| `reportUsage(input, options?)`    | Call `POST /v1/usage` for asynchronous usage.                                                      |

### guard

```ts theme={null}
guard(request: Request, input: {
  serviceSlug: string;
  planSlug?: string;
  usage: UsageItem[]; // at least one item
}): Promise<GuardResult>
```

Verifies the signature first, then checks the allowance under the request's own `zcRequestId` and returns a [decision](#guardresult). It never consults the allowance API for an unverified request. Each `UsageItem` names a `meterSlug` and declares the charge one of three ways: an exact `quantity`, a `maxQuantity` ceiling, or neither. A ceiling covers work you cannot size up front; it settles later at actual usage, so it never overcharges. An item with neither defers to the meter's configured max usage per request. The SDK rejects an item that declares both. It also rejects duplicate meters and an empty `usage` array: an accidentally empty computed array must fail rather than silently skip the check.

```ts theme={null}
const decision = await zeroClick.guard(request, {
  serviceSlug: "product-watch",
  usage: [
    { meterSlug: "requests", quantity: 1 },
    { meterSlug: "output_tokens", maxQuantity: 100_000 },
  ],
});
```

On a business denial, the SDK builds the carried response from your `input`: the exact `402 payment_required` body with the declared usage and, if you passed one, the `planSlug`. See [charge up to a maximum](/integrate/charge-up-to-a-maximum) for the ceiling pattern.

### guardIdentity

```ts theme={null}
guardIdentity(request: Request, input: { serviceSlug: string }): Promise<GuardResult>
```

The guard for free, identity-scoped endpoints: calls that cost nothing but serve only the buyer that owns the underlying records, such as polling a job the buyer created. It verifies the signature exactly like `guard`. When `zc-agent-id` is present, it allows with `allowance: { status: "not_required" }`. When it is absent, it denies with `reason: "identity_required"` and a `402` whose body carries `usage: []`. ZeroClick answers that with a free identity challenge, and the retry arrives with the buyer's `zc-agent-id` attached. `guardIdentity` makes no allowance call, and no `zc-usage` belongs on the response: free means free.

```ts theme={null}
export async function GET(request: Request) {
  const decision = await zeroClick.guardIdentity(request, {
    serviceSlug: "product-watch",
  });
  if (decision.action === "deny") return decision.response;

  const job = await findJob(jobId, { owner: decision.context.zcAgentId });
  if (!job) return Response.json({ error: "not_found" }, { status: 404 });
  return Response.json(job);
}
```

Identity alone is not billability: a buyer that has only proven identity has no active access, so `reportUsage` against it fails with `access_not_found`. Gate billable work with `guard`. See [free and identity endpoints](/integrate/free-and-identity-endpoints).

### verifyRequest

```ts theme={null}
verifyRequest(request: Request): Promise<VerifyRequestResult>
```

Verifies the `zc-signature` header and nothing else: parses `t`, `kid`, and `v1`, enforces the timestamp tolerance, resolves the signing secret by `kid`, recomputes the HMAC-SHA256 over the canonical string, and compares in constant time. It reads a clone, so the original body stays readable. The result is a decision:

```ts theme={null}
type VerifyRequestResult =
  | { ok: true; context: ZeroClickContext }
  | { ok: false; reason: VerifyFailureReason; response: Response };
```

`reason` is one of `missing_signature`, `malformed_signature`, `stale_timestamp`, `missing_request_id`, `unknown_kid`, or `invalid_signature`; `response` is the ready-to-return `401 {"error":"invalid_zeroclick_signature"}`. Use it when verification and allowance checking live in different layers:

```ts theme={null}
const verification = await zeroClick.verifyRequest(request);
if (!verification.ok) return verification.response;
// verification.context: zcRequestId, zcAgentId, timestamp, kid
```

The [signature spec](/integrate/signature-spec) specifies the canonical string and header format.

### checkAllowance

```ts theme={null}
checkAllowance(
  input: { zcRequestId: string; serviceSlug: string; usage: UsageItem[] },
  options?: { signal?: AbortSignal },
): Promise<{ allowed: boolean; reason: UsageDenialReason | null }>
```

Calls `POST /v1/usage/check` with the verified request's `zcRequestId`. Checking records nothing and burns no credit: it answers whether the declared usage is covered right now. The SDK rejects an empty or duplicate-metered `usage` array before calling; the API accepts up to 20 items. Unlike `guard`, `checkAllowance` throws on a failed call: no outage policy applies here, so the caller decides.

```ts theme={null}
const allowance = await zeroClick.checkAllowance({
  zcRequestId: verification.context.zcRequestId,
  serviceSlug: "product-watch",
  usage: [{ meterSlug: "requests", quantity: 1 }],
});
if (!allowance.allowed) {
  // allowance.reason: "usage_exhausted", "access_not_found", …
}
```

See [check allowances](/integrate/check-allowances) for what each denial reason means.

### paymentRequired

```ts theme={null}
paymentRequired(input: {
  serviceSlug: string;
  planSlug?: string;
  usage: UsageItem[]; // [] is the free identity refusal
}): Response
```

Constructs the exact `402` refusal ZeroClick expects from a seller: `{"error":"payment_required","serviceSlug":"…","usage":[…]}`. For pay-as-you-go pricing, ZeroClick re-prices the refusal an anonymous probe earns into the priced challenge the agent sees. An empty `usage` array is valid here: it is the refusal for a free, identity-scoped endpoint, and ZeroClick answers it with a \$0 identity challenge.

```ts theme={null}
return zeroClick.paymentRequired({
  serviceSlug: "product-watch",
  usage: [{ meterSlug: "output_tokens", maxQuantity: 100_000 }],
});
```

### withUsage

```ts theme={null}
withUsage(response: Response, usage: {
  serviceSlug: string;
  meterSlug: string;
  quantity: number; // positive integer
}[]): Response
```

Sets the validated `zc-usage` header for synchronous settlement without consuming the response body: it requires an unread `Response` and rebuilds it around the same body stream. Every item declares an exact quantity: this is where ceilings settle at actual usage. The header belongs on successful (`2xx`) responses only, and ZeroClick strips it before the agent sees the response.

```ts theme={null}
return zeroClick.withUsage(response, [
  { serviceSlug: "product-watch", meterSlug: "requests", quantity: 1 },
  { serviceSlug: "product-watch", meterSlug: "output_tokens", quantity: 4187 },
]);
```

See [settle usage](/integrate/settle-usage) for choosing between synchronous and asynchronous settlement.

### reportUsage

```ts theme={null}
reportUsage(
  input: {
    zcAgentId: string;
    idempotencyKey: string;
    serviceSlug: string;
    meterSlug: string;
    quantity: number;    // positive integer
    occurredAt?: string; // ISO timestamp
  },
  options?: { signal?: AbortSignal },
): Promise<ReportUsageResult>
```

Calls `POST /v1/usage` for work that finishes after the response is gone. The idempotency key is seller-owned: derive it from stable facts (`zcreq_8h2m4x0q9k1f_output_tokens`), never random. Reporting is idempotent per service on that key, and `reportUsage` does not generate keys or retry automatically.

```ts theme={null}
const result = await zeroClick.reportUsage({
  zcAgentId: "agt_x7f2kq93bh0d",
  idempotencyKey: "zcreq_8h2m4x0q9k1f_output_tokens",
  serviceSlug: "product-watch",
  meterSlug: "output_tokens",
  quantity: 4200,
});

console.log(result.recorded, result.duplicate);
```

The result is `{ recorded: true, duplicate: boolean, usageEvent }`. `duplicate: true` replays the stored event: a success, not an error. A business denial at reporting time throws an `api_status_error` whose `context.reason` carries the denial reason; see [errors](/sdks/typescript/errors).

## Result shapes

### GuardResult

`guard` and `guardIdentity` return the same union:

```ts theme={null}
type GuardResult =
  | {
      action: "allow";
      context: ZeroClickContext;
      allowance: { status: "allowed" | "unavailable" | "not_required" };
    }
  | { action: "deny"; reason: GuardDenyReason; response: Response };
```

The allow branch's `context` is the verified ZeroClick envelope:

| Field           | Value                                                                                                                                                           |
| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `zcRequestId`   | The correlation id (`zcreq_…`) ZeroClick threads through the challenge, the paid retry, and the usage record.                                                   |
| `zcAgentId`     | The id (`agt_…`) of the agent that made this call, or `null` on a signed anonymous probe.                                                                       |
| `zcAnonymousId` | The same value as `zcAgentId`, read from `zc-anonymous-id`: the name that will eventually replace `zc-agent-id`.                                                |
| `zcBuyerId`     | The buyer (`byr_…`) the agent belongs to, or `null` for an anonymous agent. Not covered by the signature. See [agents and access](/concepts/agents-and-access). |
| `timestamp`     | The signature timestamp, in Unix seconds.                                                                                                                       |
| `kid`           | The id of the signing secret that verified the request.                                                                                                         |

`allowance.status` is `"allowed"` when the API said yes, `"unavailable"` when the fail-open outage policy admitted a verified request without an answer, and `"not_required"` from `guardIdentity`, which makes no allowance call.

### Deny reasons

| `reason`                                                                                                                             | Response carried                                     | Source                                                      |
| ------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------- | ----------------------------------------------------------- |
| `missing_signature`, `malformed_signature`, `stale_timestamp`, `missing_request_id`, `unknown_kid`, `invalid_signature`              | `401` with `{"error":"invalid_zeroclick_signature"}` | Signature verification, in every guard                      |
| `service_not_found`, `access_not_found`, `access_inactive`, `plan_expired`, `meter_not_found`, `meter_not_priced`, `usage_exhausted` | `402` with the `payment_required` body               | The allowance check in `guard`                              |
| `allowance_denied`                                                                                                                   | `402` with the `payment_required` body               | `guard`, when the API denies without naming a reason        |
| `identity_required`                                                                                                                  | `402` with `usage: []`                               | `guardIdentity`, for a signed request without `zc-agent-id` |
| `allowance_unavailable`                                                                                                              | `503` with `{"error":"allowance_unavailable"}`       | `guard` under the `"deny"` outage policy                    |

## Standalone exports

The same primitives are named exports for applications that spread the flow across middleware layers. The guards take an options argument in place of client configuration; the API calls take [per-call options](/sdks/typescript/configuration#standalone-function-options).

* `guard(request, input, options)`: options are a signing-secret source (`signingSecrets` or `resolveSigningSecret`), a required `apiKey`, and the guard settings (`apiBaseUrl`, `fetch`, `clock`, `toleranceSeconds`, `allowanceUnavailable`, `checkTimeoutMs`, `onAllowanceUnavailable`).
* `guardIdentity(request, input, options)` and `verifyRequest(request, options)`: options are a signing-secret source plus optional `clock` and `toleranceSeconds`.
* `checkAllowance(input, options)` and `reportUsage(input, options)`: options are `{ apiKey, apiBaseUrl?, fetch?, signal?, timeoutMs? }`.
* `paymentRequired(input)` and `withUsage(response, usage)`: identical to the bound methods.
* `invalidZeroClickSignature()` and `allowanceUnavailable()`: construct the bare `401` and `503` refusal responses.
* `ZCError` and `isZCError`: the typed error and its narrowing helper; see [errors](/sdks/typescript/errors).

```ts theme={null}
import { verifyRequest } from "@zeroclickai/sellers";

const verification = await verifyRequest(request, {
  signingSecrets: {
    hsec_k5nq0v7m3d8p: process.env.ZEROCLICK_SIGNING_SECRET!,
  },
});
```

The package root exports TypeScript types for every input and result: `SellerConfig`, `GuardInput`, `GuardResult`, `ZeroClickContext`, `UsageItem`, `SyncUsageItem`, `CheckAllowanceInput`, `AllowanceDecision`, `UsageDenialReason`, `ReportUsageInput`, `ReportUsageResult`, and the rest.

## The contracts subpath

`@zeroclickai/sellers/contracts` exports the seller-facing Zod schemas and their inferred types: guard input, usage items, allowance requests, responses, and denial reasons, the `payment_required` body, `zc-usage` items, signature headers, the verified context, report inputs and results, seller configuration, and API call options.

```ts theme={null}
import {
  checkAllowanceInputSchema,
  paymentRequiredBodySchema,
  reportUsageInputSchema,
  syncUsageSchema,
  zeroClickContextSchema,
} from "@zeroclickai/sellers/contracts";

const usage = syncUsageSchema.parse([
  { serviceSlug: "product-watch", meterSlug: "requests", quantity: 1 },
]);
```

Public SDK methods already validate their inputs, so reach for the schemas only to validate data at an earlier boundary: a queue message that will become a `reportUsage` call, a stored usage draft, a test fixture.

## Encryption

Helpers for encrypted request and response bodies are opt-in through the `@zeroclickai/sellers/encryption` subpath: `decryptRequest(request, { resolvePrivateKey })` and `encryptResponse(response, envelope)`. Always run `guard` or `verifyRequest` against the original encrypted request before decrypting it, so the signature covers the Compact JWE bytes. The suite is fixed: `ECDH-ES+A256KW` key management with `A256GCM` content encryption. `decryptRequest` resolves the private key by the protected header's `kid`, which supports rotation. Private encryption keys remain seller-owned: the SDK does not generate, upload, persist, or rotate them.
