Quickstart
Two integration paths. Pick the one that matches what you’re already doing:
- You speak x402 / MPP (or use pay.sh).
Use
pact pay <tool> <url>— drop-in forpay, works on any 402-gated endpoint. → Calling with pay.sh. - You just want to call a curated provider over plain HTTP. Use
Market host-swap (no CLI) or
pact <url>(with CLI). → Market host swap / CLI gateway.
Both paths assume an allowlisted Solana wallet — see Private beta.
Calling with pay.sh — the 60-second flow
Section titled “Calling with pay.sh — the 60-second flow”pact pay curl is a drop-in for pay curl: same args, x402 + MPP wire
format, exit semantics — plus on-chain refund on 5xx, timeout, or
truncated/malformed body.
Works on any 402-gated endpoint. Every call gets a classifier
verdict + coverage record; on-chain refund settlement currently runs
through the pay-default pool, a subsidized launch float covering
pay.sh-wrapped calls during private beta. For curated onboarded
providers, the gateway flow (pact <url>, below) is the fully insured
alternative.
pact pay and pact approve sign with pay.sh’s wallet directly. No
separate pact-managed keypair, no pact init required for this flow —
fund pay’s account once and both legs draw from it.
-
Set the closed-beta gate.
Terminal window export PACT_MAINNET_ENABLED=1 -
Install and set up pay.sh.
pact payreads pay’s active account from~/.config/pay/accounts.ymland shells out topay account export <name> -for the keypair. Install pay, create or import an account, confirmpay account listshows an active entry. -
Install the pact CLI. See Install — one
npm install -g @q3labs/pact-cli, Node 18+. -
Fund pay’s account with mainnet USDC. The address
payshows for the active account signs both the merchant payment and Pact’s settlement authorization. Top it up externally — neitherpaynorpactmoves USDC. Confirm with:Terminal window pact balance --json# { "status": "ok",# "body": { "wallet": "...", "ata": "...", "ata_balance_usdc": 0,# "allowance_usdc": 0, "eligible": false, "reason": "no_allowance" } } -
Grant a settlement allowance. SPL Token
Approve(Solana’s standard way to delegate spending authority on a token account) authorizing the protocol’sSettlementAuthorityPDA (a program-derived account, not a wallet) to debit up to<usdc>from your USDC ATA (associated token account) at settlement. Funds stay in your ATA — the protocol doesn’t custody.Terminal window pact approve 5 -
Swap
payforpact pay. Same args, exit code, stdout.Terminal window # Before — pay.sh handles the payment, you eat any 5xx.pay curl https://flaky.example/api/v1/quote/AAPL# After — same args, same exit code, plus on-chain refund on 5xx.pact pay curl https://flaky.example/api/v1/quote/AAPLAuto-pay is silent by default (matches
pay). Pass--jsonfor the structured envelope:Terminal window pact pay --json curl https://flaky.example/api/v1/quote/AAPL# { "status": "x402_payment_made",# "body": { "tool_exit_code": 0, "payment": {...}, "response_body": "..." } }On every payment attempt
pact payprints[pact] wallet: pay/<name> (xxxx…yyyy)to stderr so the signing account is visible. macOS may trigger one Touch ID prompt per session to unlock pay’s keypair (cached ~5 min). For CI, setPACT_PRIVATE_KEY. -
Check the refund. A failed call settles into an on-chain refund record at the next batch window. Refund credits land back on the same USDC ATA that pay.sh’s active account holds (the wallet that originally paid).
Terminal window pact agents show --json | jq '.body.recent_refunds'
Calling curated providers via the CLI
Section titled “Calling curated providers via the CLI”Skip pay’s 402-discovery and call any hostname in Pact’s discovery cache directly. The gateway looks up the slug, takes a premium, and proxies to upstream — no x402 challenge, no retry round-trip.
This path uses a separate per-project keypair at
~/.config/pact/<project>/wallet.json (not pay’s account), provisioned
by pact init:
pact initpact approve 5pact --json https://api.helius.xyz/v0/addresses/<addr>/balancespact agents show --jsonno_provider (exit 20) means the hostname isn’t onboarded; in private
beta that’s terminal — fall back to pact pay <tool> <url> or --raw.
Calling Market via host swap
Section titled “Calling Market via host swap”The CLI-free path. Swap the upstream host for
market.pactnetwork.io/<endpoint>/; same path, same body. Refund
coverage is automatic. The <endpoint> identifier and
PACT_MARKET_KEY are issued at onboarding (see
Private beta).
curl -X POST https://upstream.example/v1/<path> \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $UPSTREAM_KEY"curl -X POST https://market.pactnetwork.io/<endpoint>/v1/<path> \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $PACT_MARKET_KEY"Success → you pay. Failure (5xx, timeout, or truncated/malformed body — see the classifier) → principal + premium refunded at the next settlement window.
Full Market spec (headers, error shapes, idempotency): Calling Market.
Agent integration
Section titled “Agent integration”The CLI ships with a skill — a Markdown file that tells Claude Code,
Codex, and other skill-aware runners when to reach for pact instead
of curl / fetch / Bash.
What pact init does
Section titled “What pact init does”Two things, both idempotent:
- Installs the skill at
.claude/skills/pact/SKILL.md(if absent). The skill’s frontmatterdescriptionlists curated provider hostnames so the agent only loads it when calling those. - Appends a snippet to
CLAUDE.md/AGENTS.mdunder## Paid API calls. Skipped if the marker is already there.
pact init does not create the gateway wallet itself. The
per-project state directory (wallet.json, policy.yaml,
endpoints-cache.json) is provisioned on the first pact <url> call
— see Install.
pact pay doesn’t need pact init — it uses pay.sh’s active account
directly. Run pact init only when you want the skill / CLAUDE.md
snippet, or you’re about to call curated providers via pact <url>.
For an aggressive substitute-every-pay-call policy, see the standalone
pact-pay skill.
Status → action table
Section titled “Status → action table”The skill instructs the agent to pass --json and parse .status
first, then map to action:
| Status | What the agent should do |
|---|---|
ok | call succeeded — use body |
client_error | YOUR request was wrong (4xx) — don’t retry, surface to user |
server_error | upstream failed — refund auto-issued, retry once if idempotent |
needs_funding | run pact approve <usdc> if cap allows; else surface |
auto_deposit_capped | hit policy cap — surface session_used_usdc / session_max_usdc |
endpoint_paused | provider disabled — pick another or wait |
no_provider | hostname unsupported — fall back to pact pay <tool> <url> |
discovery_unreachable | gateway unreachable — surface and stop |
signature_rejected | clock skew — tell user to sync NTP |
Status values map 1:1 to exit codes (see Commands → Status taxonomy) so an agent can branch on either.
Self-funding policy
Section titled “Self-funding policy”The agent may run pact approve <amount> on its own if
<amount> ≤ per_deposit_max_usdc and the session total stays under
session_total_max_usdc. Caps live in
~/.config/pact/<project>/policy.yaml:
per_deposit_max_usdc: 5session_total_max_usdc: 20On auto_deposit_capped the agent surfaces to the user, never retries
with a different cap.
Runner compatibility
Section titled “Runner compatibility”| Runner | Skill picked up from | Notes |
|---|---|---|
| Claude Code | .claude/skills/pact/SKILL.md | Loaded automatically; description gate ensures activation only on listed hostnames. |
| Codex CLI | .claude/skills/pact/SKILL.md + AGENTS.md snippet | pact init writes the snippet to AGENTS.md when present. |
| Custom agent | direct read | The body shape and status taxonomy are stable per-version; pin to pact --version and treat unknown statuses as cli_internal_error. |
Common envelope outcomes
Section titled “Common envelope outcomes”| Status | Meaning | What to do |
|---|---|---|
ok | gateway call (pact <url>) succeeded | use body |
x402_payment_made / mpp_payment_made | pact pay succeeded via 402 retry | use body.response_body; body.tool_exit_code is the wrapped tool’s exit |
client_error | your request was wrong (4xx) | fix the request; don’t retry |
server_error | upstream failed | refund queued; retry once if idempotent |
payment_failed | pact pay parsed a 402 but the retry was rejected | inspect body.scheme + body.reason |
needs_funding | ATA / allowance insufficient | fund the wallet, run pact approve <usdc> |
auto_deposit_capped | hit the policy cap | raise the cap or run approve manually |
no_provider | hostname not in discovery cache | use pact pay <tool> <url> or --raw |
endpoint_paused | provider disabled | pick another or wait |
unsupported_tool | pact pay <tool> where <tool> isn’t curl | use pact pay curl … |
tool_missing | <tool> is supported but not on $PATH | install it |
Full taxonomy and exit codes: Commands → Status taxonomy.
Common gotchas
Section titled “Common gotchas”- Refunds aren’t instant. Settlement is batched — expect refunds within one window. Cadence: Status.
client_erroris on you. Bad keys, malformed payloads, expired tokens don’t get refunded.- Retries don’t double-refund. Each retry is a separate call, independently classified.
- Allowlist enforced at Market. Without a key, you get 401; the upstream never sees the call.
What’s next
Section titled “What’s next”- Commands — every subcommand with envelope examples.
- Troubleshooting — failure modes and fixes.
- Classifier & refund policy — how Pact decides what’s a refund-eligible failure and the underlying math.