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
npmnpm 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.
| Option | Type | Default | Description |
|---|---|---|---|
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:
| Knob | Default | Meaning |
|---|---|---|
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).
| Namespace | Purpose | Status |
|---|---|---|
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.
| Class | When thrown | code |
|---|---|---|
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
| Property | Type | Description |
|---|---|---|
name | string | Class name (overridden per subclass). |
code | string | Stable machine-readable code. |
status | number | HTTP status from the API response (or 0 for client-side failures). |
message | string | Human-readable error message. |
error | string | null | Machine-readable error code from the body, if present. |
errors | string[] | null | Validation errors array from 400 responses. |
raw | object | null | Full raw response body. |
ClearPactRetryExhaustedError extends
ClearPactTimeoutError and adds two retry-aware fields:
| Field | Type | Description |
|---|---|---|
attempts | number | Total attempts made (initial + retries). |
lastError | ClearPactError | null | The 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.
| Field | Type | Description |
|---|---|---|
amount | number | Gross amount (token units). |
token | string | Token symbol, e.g. "USDC". |
from | string | Payer address. |
to | string | Payee address. |
network | 'testnet' | 'mainnet' | Network on which settlement settled. |
conditions | ConditionResult[] | Per-condition results (index, type, met, details). |
settled_at | string | ISO 8601 timestamp of the on-chain settlement. |
settle_tx_hash | string | null | On-chain transaction hash. |
explorer | string | null | BaseScan URL for the settlement tx. |
fee? | PlatformFeeLine | Optional — present only when platformFeeBps > 0. |
When fee is present, it carries the BSP-based split
computed as floor(amount * bps) / 10000:
| Field | Type | Description |
|---|---|---|
fee.bps | number | Fee in basis points (0–10000). |
fee.amount | number | Fee slice in token units (same unit as settlement.amount). |
fee.recipient | string | EVM 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):
| Accessor | Type | Value |
|---|---|---|
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"
}