ClearPact SDK Reference

The complete public surface for the ClearPact TypeScript / JavaScript SDK (v0.3.0) — installer, constructor, namespaces, methods, typed errors, and the settlement receipt shape.

This page consolidates the v0.3.0 SDK surface in one place. The v1.0 hardening arc — typed ClearPactError subclasses, retry/timeout options, the network mainnet flag, and platform-fee BPS — is documented here end-to-end so the agent-to-agent B2B pilot has a single reference for integration.

Install

npm
npm install clearpact
yarn
yarn add clearpact

The package ships a CommonJS index.js and a TypeScript declaration surface at clearpact/types. The same bundle is exposed as a browser global at https://clearpact.polsia.app/sdk for non-Node integrations.

new ClearPact(options)

The constructor reads six options, validates them, and wires the five exposed namespaces (jobs, escrow, x402, webhooks, keys) on the new instance.

OptionTypeDefaultDescription
apiKey string required ClearPact API key. Get one at /docs.
network 'testnet' | 'mainnet' 'testnet' Target network. Any other value throws ClearPactError.
baseUrl string 'https://clearpact.polsia.app' Override the API base URL (point at a self-hosted instance or staging).
timeoutMs number 30_000 Per-attempt timeout in ms. Triggers an AbortController abort.
retries number 3 Additional attempts after the first (so 4 total attempts by default).
platformFeeBps number 0 Platform fee in basis points (0–10000). Read from env PLATFORM_FEE_BPS if unset.
platformFeeRecipient string null Required when platformFeeBps > 0. Read from env PLATFORM_FEE_RECIPIENT.

The constructor validates each option at new ClearPact() time: apiKey must be truthy; network must be exactly 'testnet' or 'mainnet'; platformFeeBps must be an integer between 0 and 10000; platformFeeRecipient is required when platformFeeBps > 0 and must match /^0x[0-9a-fA-F]{40}$/. Any violation throws a ClearPactError synchronously — the HTTP client is never constructed.

Retry & Timeout Knobs

Four retry policy constants live on HttpClient (the same instance the constructor builds) and are inherited from TIMEOUT_DEFAULTS:

KnobDefaultMeaning
timeoutMs 30_000 Per-attempt timeout. AbortController cancels the fetch when this fires.
retries 3 Additional attempts after the initial (4 total when default). 0 disables retries.
baseDelayMs 200 Exponential backoff base.
jitterMs 100 Random jitter (0–100 ms) added to each backoff sleep.
backoffMaxMs 5_000 Cap on any single backoff sleep so a high retry count cannot queue a long wait.

The SDK retries retriable failures only. The rule from isRetriable(): NetworkError, ClearPactTimeoutError, HTTP 429, and any 5xx retry; everything else (including EscrowNotFoundError, ContractRevertedError, InsufficientFundsError, and InvalidAddressError) surfaces immediately — a retry on a 4xx risks double-submitting a mutation.

Namespaces

The constructor exposes five namespaces on the client instance. The jobs namespace is the ERC-8183 v2 surface; escrow is the v1 drainage alias (deprecated, removal in v0.4.0).

NamespacePurposeStatus
client.jobs ERC-8183 v2 job lifecycle: create / setProvider / setBudget / fund / submit / complete / reject / claimRefund / get / list v2 (current)
client.escrow Legacy v1 escrow API: create / fund / settle / cancel / get deprecated (v0.4.0)
client.x402 x402 payment protocol helpers: verify / settle / health / listTransactions / getTransaction current
client.webhooks Webhook registration: create / list / delete / deliveries current
client.keys API key observability: usage(keyId) current

client.jobs (ERC-8183 v2)

Ten methods on the jobs namespace implement the ERC-8183 v2 lifecycle. The state machine is Open → Funded → Submitted → Completed with a Rejected branch (E3: 3-path ACL, symmetric refund) and Expired (claimRefund after expiredAt).

jobs.create(provider, evaluator, expiredAt, description?, hook?)

Create a new job (ERC-8183 createJob). Self-service evaluator pattern — pass your own evaluator EOA; ClearPact never appoints or arbitrates evaluators. Only valid in Open state.

method create(provider, evaluator, expiredAt, description?, hook?) returns Promise<JobCreateResponse>

jobs.setProvider(jobId, provider)

Update the provider address (setProvider). Only valid in Open state.

method setProvider(jobId, provider) returns Promise<JobResponse>

jobs.setBudget(jobId, amount, opts?)

Set the budget (setBudget). Only valid in Open state. Phase 2bis extension: pass opts.conditionRef to emit ClearPactJobMetadata with the conditional reference hash; pass opts.token to override the per-job payment token (defaults to USDC).

method setBudget(jobId, amount, opts?) where opts = { token?: string, conditionRef?: string } returns Promise<JobResponse>

jobs.fund(jobId, opts?)

Fund the job on-chain (fund). Valid in Open state. Transitions to Funded. Optional opts.tx_hash records the on-chain funding transaction.

method fund(jobId, opts?) returns Promise<JobResponse>

jobs.submit(jobId, deliverable, opts?)

Submit the work deliverable (submit). Valid in Funded state. Transitions to Submitted. Provider calls this to signal the work is ready for evaluation.

method submit(jobId, deliverable, opts?) returns Promise<JobResponse>

jobs.complete(jobId, reason, opts?)

Approve and complete the job (complete). Valid in Submitted state. Transitions to Completed and releases payment to the provider. Decision 6: callable by the evaluator EOA or the client at Submitted state — both signatures call this method directly. Not via the Evaluator contract.

method complete(jobId, reason, opts?) returns Promise<JobResponse>

jobs.reject(jobId, reason, opts?)

Reject the job result (reject). Valid in Open, Funded, or Submitted state (E3: 3-path ACL). Transitions to Rejected; from Funded or Submitted, triggers a symmetric automatic refund. Decision 6: callable by the evaluator EOA or the client at Submitted state. Not resolveDispute — see Decision 7.

method reject(jobId, reason, opts?) returns Promise<JobRejectResponse>

jobs.claimRefund(jobId)

Claim refund after expiry (claimRefund). Valid in Funded or Submitted state, after expiredAt has passed. Unconditional, non-hookable, non-pausable — PAUSER_ROLE was renounced post-Phase 4.

method claimRefund(jobId) returns Promise<JobRefundResponse>

jobs.get(jobId)

Get the current job state, event log, and contract bindings (getJob).

method get(jobId) returns Promise<JobGetResponse>

jobs.list(filters?)

List jobs for the current API key. filters.status accepts the six JobStatus values (Open, Funded, Submitted, Completed, Rejected, Expired); filters.limit defaults to 20 and caps at 100; filters.offset paginates.

method list(filters?) returns Promise<JobListResponse>

client.x402

Five methods expose the x402 payment-protocol helpers — verify a signed payment payload, settle it on-chain, and inspect replays.

x402.verify(payload, options?)

Verify an x402 payment signature and replay protection. The payload argument may be a raw EIP-3009 signature string or an object containing the structured fields. options may carry expected_to, min_amount_raw, network, chain_id, and usdc_address.

method verify(payload, options?) returns Promise<X402VerifyResponse>

x402.settle(params?)

Settle a verified x402 payment on-chain. Requires DEPLOYER_PRIVATE_KEY on the server (see /api/x402/health for current settle_enabled status).

method settle(params?) returns Promise<X402SettleResponse>

x402.health()

Liveness + capability probe. Reports chain_id, USDC contract, and which phases (verify, settle) are currently enabled.

method health() returns Promise<X402HealthResponse>

x402.listTransactions(filters?)

List x402 transactions for the current API key. filters.status accepts verified | failed | replay_rejected | expired; filters.from and filters.network narrow the scope.

method listTransactions(filters?) returns Promise<X402TransactionsResponse>

x402.getTransaction(id)

Fetch a single x402 transaction record by id.

method getTransaction(id) returns Promise<X402TransactionResponse>

client.webhooks

Register outbound webhook endpoints, list them, and inspect delivery history. Webhook event types include escrow.created, escrow.funded, escrow.settled, escrow.cancelled, escrow.expired, plus the job.* counterparts during v1 drainage (dual-emit).

webhooks.create(params)

Register a new webhook. params.url is the receiving endpoint; params.event_types is the literal union of WebhookEvent strings. The response includes signing_secret — shown only once, store it immediately.

method create(params) returns Promise<WebhookCreateResponse>

webhooks.list()

List webhooks registered for the current API key.

method list() returns Promise<WebhookListResponse>

webhooks.delete(id)

Delete a webhook by id. No-op if the id is not registered to the current API key.

method delete(id) returns Promise<{ success: boolean }>

webhooks.deliveries(webhookId, opts?)

List delivery attempts for a single webhook. opts.from, opts.to, opts.status, opts.event_type, opts.limit, and opts.cursor paginate and narrow the result set.

method deliveries(webhookId, opts?) returns Promise<DeliveryResponse>

client.keys

One method on the keys namespace: usage stats for an API key. Used for observability and rate-limit dashboards.

keys.usage(keyId, opts?)

Fetch usage rows for an API key. opts.from, opts.to, opts.status, opts.endpoint, opts.limit, and opts.cursor paginate and narrow by endpoint shape. Caller must own the key — cross-key queries 403.

method usage(keyId, opts?) returns Promise<KeyUsageResponse>

Typed Errors — ClearPactError hierarchy

Every error thrown by the SDK extends ClearPactError. Catching the root class catches every subclass. Each carries a stable string err.code, the upstream err.status (HTTP code), and the original err.raw body.

ClassWhen throwncode
ClearPactError Base class — any unclassified 4xx, or constructor validation CLEAR_PACT_ERROR
NetworkError DNS / TLS / abort-before-fetch / non-JSON response body NETWORK_ERROR
EscrowNotFoundError HTTP 404 ESCROW_NOT_FOUND
ContractRevertedError HTTP 422, or body contains "revert" / "execution reverted" CONTRACT_REVERTED
InsufficientFundsError HTTP 402, or body contains "insufficient" / "balance" INSUFFICIENT_FUNDS
InvalidAddressError assertAddress() failure (provider / evaluator / hook / token / recipient) INVALID_ADDRESS
ClearPactTimeoutError Per-attempt timeoutMs reached (AbortController fires) CLEAR_PACT_TIMEOUT
ClearPactRetryExhaustedError Final attempt failed after retries + 1 tries extends ClearPactTimeoutError CLEAR_PACT_RETRY_EXHAUSTED

Common properties on ClearPactError

PropertyTypeDescription
namestringClass name (overridden per subclass).
codestringStable machine-readable code.
statusnumberHTTP status from the API response (or 0 for client-side failures).
messagestringHuman-readable error message.
errorstring | nullMachine-readable error code from the body, if present.
errorsstring[] | nullValidation errors array from 400 responses.
rawobject | nullFull raw response body.

ClearPactRetryExhaustedError extends ClearPactTimeoutError and adds two retry-aware fields:

FieldTypeDescription
attemptsnumberTotal attempts made (initial + retries).
lastErrorClearPactError | nullThe underlying error from the final attempt.

Recommended catch idiom

import {
  ClearPactError,
  NetworkError,
  EscrowNotFoundError,
  ContractRevertedError,
  InsufficientFundsError,
  ClearPactTimeoutError,
  ClearPactRetryExhaustedError,
} from 'clearpact';

try {
  const { job } = await client.jobs.get('not-a-real-id');
} catch (err) {
  if (err instanceof EscrowNotFoundError)          { return null; }
  if (err instanceof InsufficientFundsError)       { return 'top-up-wallet'; }
  if (err instanceof ClearPactRetryExhaustedError) { throw err.lastError; }
  if (err instanceof ClearPactTimeoutError)        { throw err; }   // single attempt timeout
  if (err instanceof ClearPactError)               { /* err.code + err.status */ }
  throw err;
}

Catching the root ClearPactError matches every subclass, and the retry-exhausted branch unwraps err.lastError so handlers see the underlying transport failure rather than the wrapper. Reaching for err.code lets you log by category without enumerating every subclass.

Settlement Receipt

client.escrow.settle() (the v1 drainage surface) returns a flat settlement object on the success response. The optional fee line is annotated by the SDK only when the client was constructed with platformFeeBps > 0 and a configured recipient; the on-chain payout goes entirely to the payee.

FieldTypeDescription
amountnumberGross amount (token units).
tokenstringToken symbol, e.g. "USDC".
fromstringPayer address.
tostringPayee address.
network'testnet' | 'mainnet'Network on which settlement settled.
conditionsConditionResult[]Per-condition results (index, type, met, details).
settled_atstringISO 8601 timestamp of the on-chain settlement.
settle_tx_hashstring | nullOn-chain transaction hash.
explorerstring | nullBaseScan URL for the settlement tx.
fee?PlatformFeeLineOptional — present only when platformFeeBps > 0.

When fee is present, it carries the BSP-based split computed as floor(amount * bps) / 10000:

FieldTypeDescription
fee.bpsnumberFee in basis points (0–10000).
fee.amountnumberFee slice in token units (same unit as settlement.amount).
fee.recipientstringEVM address that receives the fee slice.

Today this surface is documented on EscrowSettleResponse.settlement in sdk/types.ts. v1.0 will export this exact shape as a standalone SettlementReceipt TypeScript type for standalone typing — the runtime shape, field set, and semantics will not change.

Static Accessors

Two static getters are exposed on the ClearPact class constructor itself (not on instances):

AccessorTypeValue
ClearPact.version string '0.3.0'
ClearPact.contracts object Base Sepolia contract addresses (immutable, Phase 4 v2 deploy).
ClearPact.version      // '0.3.0'
ClearPact.contracts    // {
//   jobProxy:        '0x7CDB80e9B154c99354d66604103fAEb148c6f5A8',
//   evaluatorProxy:  '0x1DDefFED6a9e28C37e1E10c292F6774D837a7Ab6',
//   usdc:            '0x036CbD53842c5426634e7929541eC2318f3dCF7e',
// }

TypeScript Entry Point

TypeScript bindings are exported from clearpact/types for direct import without depending on the runtime bundle:

import type { ClearPactOptions, EscrowObject } from 'clearpact/types';

The set of exported types mirrors the runtime surface above — every namespace, request params, and response shape lives there. Importing from clearpact/types rather than the main entry point keeps type-only builds tree-shakable.

Pre-Deploy Readiness Polling

Before flipping CLEARPACT_ENABLE_MAINNET live, external monitors and CI can poll the preflight readiness endpoint for the same PASS/FAIL signal table that scripts/preflight-mainnet.js prints on the CLI. One JSON object per request, with a ready boolean and an entry per readiness signal (env flag, fee config, contract address, RPC reachability, chain id, USDC bytecode).

Auth is dual: pass an X-API-Key header (cpk_…) or an X-Admin-Secret header. Results are cached for 5 seconds on the server so dashboards can poll without hammering the external RPC.

curl -s -H "X-Admin-Secret: $ADMIN_SECRET" https://clearpact.polsia.app/api/preflight | jq
{
  "ready": false,
  "signals": [
    { "name": "env_flag_enabled",       "status": "FAIL", "detail": "..." },
    { "name": "fee_bps_set",            "status": "FAIL", "detail": "..." },
    { "name": "fee_recipient_valid",    "status": "FAIL", "detail": "..." },
    { "name": "platform_fee_active",    "status": "FAIL", "detail": "..." },
    { "name": "contract_address_set",   "status": "FAIL", "detail": "..." },
    { "name": "rpc_reachable",          "status": "FAIL", "detail": "..." },
    { "name": "chain_id_matches",       "status": "FAIL", "detail": "..." },
    { "name": "usdc_bytecode_present",  "status": "FAIL", "detail": "..." }
  ],
  "checked_at": "2026-08-06T12:00:00.000Z"
}