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

# TypeScript SDK configuration

> Every createSeller option, signing-secret rotation, split usage keys, the allowance outage policy, and per-call overrides.

`createSeller(config)` validates its configuration up front and returns a frozen client. Invalid configuration throws a `ZCError` with code `malformed_input` at construction, not at request time. Construction enforces two constraints:

* **Exactly one signing-secret source.** Provide `signingSecrets` or `resolveSigningSecret`: not both, not neither.
* **Both usage directions covered.** Allowance checks (`guard`, `checkAllowance`) use `usageReadKey`. Usage reporting (`reportUsage`) uses `usageWriteKey`. A single `apiKey` with both scopes backfills either side. `createSeller` rejects a config that leaves a direction without a key; `context.issues` names the missing side.

## Options

| Option | Required | Default | Description |
| - | - | - | - |
| `signingSecrets` | One secret source | None | Record from ZeroClick signing-secret `kid` (`hsec_…`) to secret value (`zcsec_…`). Keep the current and previous keys in the record during rotation. |
| `resolveSigningSecret` | One secret source | None | Async resolver `({ kid }) => secret`, returning `null` for an unknown `kid`. For a secret manager or other dynamic store. |
| `apiKey` | Key coverage | None | API key with both the `usage:read` and `usage:write` scopes. Backfills whichever scoped key below is absent. |
| `usageReadKey` | Key coverage | None | API key with `usage:read`, used for the allowance checks behind `guard` and `checkAllowance`. |
| `usageWriteKey` | Key coverage | None | API key with `usage:write`, used for `reportUsage`. |
| `apiBaseUrl` | No | `https://api.zeroclick.io` | Absolute API base URL, as a string or `URL`. |
| `fetch` | No | `globalThis.fetch` | Injected Fetch-compatible implementation, useful for private deployments and tests. |
| `toleranceSeconds` | No | `300` | Maximum absolute age of a request signature, in seconds. |
| `clock` | No | `Date.now` | Millisecond clock function, primarily for deterministic tests. |
| `checkTimeoutMs` | No | `1500` | Allowance-check timeout in milliseconds. |
| `allowanceUnavailable` | No | `"allow"` | Outage policy when the allowance API gives no answer: `"allow"`, `"deny"`, or `"throw"`. |
| `onAllowanceUnavailable` | No | None | Callback receiving the sanitized `ZCError` when the allowance API is unavailable. |

## Usage keys

One API key with both usage scopes covers everything. To separate concerns (a request path that only checks, a worker that only reports), pass scoped keys:

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

const zeroClick = createSeller({
  signingSecrets: {
    hsec_k5nq0v7m3d8p: process.env.ZEROCLICK_SIGNING_SECRET!,
  },
  usageReadKey: process.env.ZEROCLICK_USAGE_READ_KEY!,
  usageWriteKey: process.env.ZEROCLICK_USAGE_WRITE_KEY!,
});
```

A scoped key can also override one direction while `apiKey` backfills the other. See [keys and secrets](/docs/integrate/keys-and-secrets.md) for minting scoped keys.

## Signing-secret rotation

ZeroClick identifies each signature with a `kid`: the signing secret's id, carried in every `zc-signature` header. Keep every key that may still sign an in-flight request available to the SDK:

```ts theme={null}
const zeroClick = createSeller({
  signingSecrets: {
    hsec_k5nq0v7m3d8p: process.env.ZEROCLICK_SIGNING_SECRET_CURRENT!,
    hsec_j2rw8t4c6y1z: process.env.ZEROCLICK_SIGNING_SECRET_PREVIOUS!,
  },
  apiKey: process.env.ZEROCLICK_API_KEY!,
});
```

If a request's `kid` is missing from the record, the SDK denies it with a `401` (reason `unknown_kid`). Never log or return a signing secret. Revoke an old key only after requests signed by it can no longer be in flight.

## Resolving secrets dynamically

For managed secret storage, resolve by `kid` instead of holding secrets in memory:

```ts theme={null}
const zeroClick = createSeller({
  resolveSigningSecret: async ({ kid }) => secretStore.get(kid),
  apiKey: process.env.ZEROCLICK_API_KEY!,
});
```

The resolver runs on every verification with the `kid` from the request's signature header. Return the secret, or `null` for a `kid` you do not recognize. The SDK denies that request as `unknown_kid`. A resolver that throws surfaces as a `ZCError` with code `signing_secret_resolution_failed` rather than a deny. An outage in your secret store shows up as an error instead of silently rejecting traffic.

## Allowance outage policy

The default policy is fail-open after the 1.5-second check timeout: the SDK allows a verified request with `allowance.status === "unavailable"`, and `onAllowanceUnavailable` receives the sanitized error. Configure `"deny"` to return `503 {"error":"allowance_unavailable"}` instead, or `"throw"` to handle the typed error in application code:

```ts theme={null}
const zeroClick = createSeller({
  signingSecrets: {
    hsec_k5nq0v7m3d8p: process.env.ZEROCLICK_SIGNING_SECRET!,
  },
  apiKey: process.env.ZEROCLICK_API_KEY!,
  allowanceUnavailable: "deny",
  onAllowanceUnavailable: (error) => {
    console.error("allowance check unavailable", error.code, error.context);
  },
});
```

The policy applies when the allowance check produces no usable answer: the call failed or timed out (`api_transport_error`), the API answered with an unsuccessful status (`api_status_error`), or the response body did not validate (`api_response_invalid`). It applies only after the signature verifies: the SDK never allows an unverified request because the allowance API is unavailable. A definite `allowed: false` answer is never subject to the policy; it always denies with the `402`.

## Per-call cancellation

The bound `checkAllowance` and `reportUsage` methods accept an optional `{ signal }`:

```ts theme={null}
await zeroClick.reportUsage(input, { signal: request.signal });
```

The signal combines with the built-in timeout; whichever fires first aborts the call. `guard` already ties its allowance check to the incoming request's own `AbortSignal`, so an agent that disconnects does not leave a check running.

Through the client, the allowance check times out after `checkTimeoutMs`, and `reportUsage` after the fixed 1,500 ms default. To raise the reporting timeout, call the standalone export with `timeoutMs`.

## Standalone function options

The `checkAllowance` and `reportUsage` named exports hold no client state. Each call carries its own options, for middleware layers or workers that never construct a client:

| Option | Required | Default | Description |
| - | - | - | - |
| `apiKey` | Yes | None | API key with the scope the call needs: `usage:read` for `checkAllowance`, `usage:write` for `reportUsage`. |
| `apiBaseUrl` | No | `https://api.zeroclick.io` | Absolute API base URL, as a string or `URL`. |
| `fetch` | No | `globalThis.fetch` | Injected Fetch-compatible implementation. |
| `signal` | No | None | `AbortSignal` combined with the timeout. |
| `timeoutMs` | No | `1500` | Per-call timeout in milliseconds. |

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

const result = await reportUsage(
  {
    zcAgentId: "agt_x7f2kq93bh0d",
    idempotencyKey: "zcreq_8h2m4x0q9k1f_output_tokens",
    serviceSlug: "product-watch",
    meterSlug: "output_tokens",
    quantity: 4200,
  },
  { apiKey: process.env.ZEROCLICK_USAGE_WRITE_KEY!, timeoutMs: 5000 },
);
```

The standalone `guard`, `guardIdentity`, and `verifyRequest` exports take the options a client would otherwise hold; the [API reference](/docs/sdks/typescript/api.md#standalone-exports) lists their shapes.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.