HSP. Developer Guide static copy
Draft Protocol · Pre-1.0

Verify the settlement,
not the promise.

HSP is a verifiable settlement layer for stablecoin payments. Instead of trusting a payment processor’s “paid” flag, every payment carries cryptographic evidence that anyone can re-check. A payer signs a Mandate; an adapter settles it and emits a signed Receipt; a verifier checks the pair (plus any compliance Attestations) against one rule:

ACCEPT requiredCapabilities satisfiedCapabilities (proof ∪ attestations) Everything the payer’s mandate required is satisfied by the receipt’s proof and the attestations. That single subset check is the whole trust model. ACCEPT means ship.
signed by the payer
Mandate
pay this much of this token to this recipient on this chain, requiring capabilities Z
signed by an adapter
Receipt
I observed the settlement that satisfies this mandate
signed by an issuer
Attestation
I vouch the subject is KYC’d / not sanctioned — optional, per policy
VERIFIER
pure function · runs anywhere
same inputs ⇒ same decision
ACCEPT

You do not need to run any infrastructure. The organizer hosts this shared sandbox; you point the SDK at it and pay. Values marked <…> below (Coordinator URL, API key, faucet, repo) are shared once the sandbox is live.

01

What is HSP

Three moves, three objects, one rule

Payment rails move money; almost none give the relying party a cryptographic answer to “did the thing I authorized actually happen, under the conditions I demanded?” HSP is that answer layer. The flow is always the same three moves:

1Intent

The payer signs a mandate — an EIP-712 message: pay this much of this token to this recipient on this chain, meeting these requirements.

2Settlement

The payer’s own wallet broadcasts the on-chain transfer. HSP is zero-custody: no service ever holds your funds.

3Verification

A verifier independently checks the receipt against the mandate and a pinned trust policy. ACCEPT means ship — and never requires trusting the Coordinator.

Three wire objects carry the evidence — and that is the entire protocol surface:

ObjectSigned bySays
Mandatethe payer“I intend to pay X to Y, and I require capabilities Z”
Receiptan adapter“I observed the settlement that satisfies this mandate”
Attestationan issuer“I vouch the subject is KYC’d / not sanctioned / …”

Compliance is not a bolt-on: KYC and sanctions are ordinary capabilities in those sets, toggled per payment. The protocol is a pre-1.0 draft — these concepts are stable; wire details may still change.

02

What you can build

Four scenarios, end-to-end today

Every scenario below is implemented, tested against real chains, and inspectable in the Explorer — the decision trace shows exactly which capability was required and what satisfied it. The matrix is two settlement paths × two policies:

#ScenarioWhat it demonstratesRequires
1Public · evm-transfera plain ERC-20 stablecoin transfer, verified — the hello-world
2Public · x402paying via real Coinbase x402 v2 (HTTP-native, client-pull)
3Compliant · evm-transfera transfer that also carries KYC + sanctions attestationsattests:kyc + attests:sanctions
4Compliant · x402KYC / sanctions enforced over the x402 pathattests:kyc + attests:sanctions

Build agentic commerce, paid APIs (x402 paywalls), compliant settlement flows — or your own settlement adapter (§07).

Coming soon — private payments. A later release will add and open up privacy-preserving settlement: hide the recipient (stealth addresses) or the amount (a shielded pool), each with optional view-key disclosure to a regulator. They slot in as adapters — the capability model already reserves room for them, so nothing else on this page changes when they ship.

03

The tool stack

Pick your layer

You consume HSP through a small layered stack — each layer is independently usable, and each lower layer is what the one above is built on. All of it runs from TypeScript source; no build step.

skills/hsp-payAI skill — teach an agent how & when to pay (safety rails)
@hsp/mcpMCP server — agents pay via tools: quote / pay / status / verify
@hsp/sdkone-call pay() + independent verify()most developers start here
@hsp/devkitbuild & conformance-test your own settlement adapter
@hsp/coreprotocol primitives — types, capabilities, verifier, signer, adapters
Coordinatorthe hosted hub you point your SDK at (+ Explorer + this portal)

The Coordinator is the only service you talk to over the network. It registers mandates, observes your on-chain settlement, runs the verifier, stores the (mandate, receipt, attestations) triple, and serves status, a web Explorer, and this portal. It is custody-free — it signs observations with an adapter key, never moves money.

04

Quickstart — your first payment in five minutes

hashkey-testnet

The SDK is distributed as a repository for the hackathon (not yet on npm). The organizer shares the repo link together with the sandbox details. Then:

  1. Install.
    git clone <REPO_URL> && cd hsp
    # everything runs in Docker (no node on your host) — see the repo README,
    # or use your own Node 20+ toolchain:
    npm install
  2. Get testnet funds.

    Open the faucet page (/faucet/) and paste your address — you receive gas + test USDC, rate-limited per address & IP. Or by API:

    curl -X POST -H 'content-type: application/json' \
      -d '{"address":"0xYOUR_ADDRESS"}' <FAUCET_URL>/faucet
  3. Send a payment.
    import { HSPClient } from '@hsp/sdk';
    import { resolveChain } from '@hsp/core/chains/index';
    
    const chain = resolveChain('hashkey-testnet');            // pinned testnet USDC
    const hsp = new HSPClient({
      coordinatorUrl: process.env.HSP_COORDINATOR_URL,       // <COORDINATOR_URL>
      apiKey:         process.env.HSP_API_KEY,               // <API_KEY>
      signer:         { kind: 'privateKey', privateKey: process.env.HSP_PRIVATE_KEY },
      chain,
    });
    
    // USDC has 6 decimals → 1 USDC = 1_000_000 base units
    const handle = await hsp.pay({ to: '0xRecipient', amount: 1_000_000n });
    await handle.awaitSettled();                            // → "SETTLED"

    That single pay() call builds the mandate, signs it, registers it, broadcasts the ERC-20 transfer from your own wallet, asks the Coordinator to observe it, and returns a handle.

  4. Watch the decision.

    Open /explorer and paste the paymentId — see the mandate, the receipt, and the subset chain that produced ACCEPT.

05

Core concepts you’ll meet

Capabilities · verifier · signer

Capabilities

Typed requirements, written verb:object:version. proves:… — the settlement structurally proves something. attests:… — an issuer vouches for something.

public = (trivially satisfied) · compliant = attests:kyc + attests:sanctions

The verifier

A pure function of (mandate, receipt, attestations, policy). ACCEPT iff requiredCapabilities ⊆ satisfiedCapabilities. You can run it yourself — you never have to trust the Coordinator’s word.

same triple + same pinned policy ⇒ same decision, anywhere

Signer

Three backends, one wire signature. The signing account is also the settling account (wallet-settling: Transfer.from must equal the mandate signer).

privateKey (scripts) · viemAccount · eip1193 (browser/wallet — key never leaves it)

paymentId

Equals the mandateHash. Use it to query status, deep-link the Explorer, and de-duplicate: one on-chain transfer settles at most one payment.

idempotent: re-registering the same mandate returns the same payment

06

Recipes

Compliant · x402 · verify

Compliant payment (KYC + sanctions)

The SDK fetches matching attestations from the issuer and registers them alongside your mandate; the verifier credits them.

const hsp = new HSPClient({ /* …as above… */ issuerUrl: process.env.HSP_ISSUER_URL });

await hsp.pay({
  to: '0xRecipient', amount: 1_000_000n,
  profile: { compliance: ['kyc', 'sanctions'] },  // signs the caps in
});

Pay via x402 (real Coinbase x402 v2)

You sign an HSP mandate and an EIP-3009 authorization; the facilitator settles on-chain (you pay no gas) and bridges it to a verifiable HSP receipt.

await hsp.payX402({
  merchant:       '0xMerchant',
  facilitatorUrl: process.env.HSP_FACILITATOR_URL,
  amount:         1_000_000n,
  // profile: { compliance: ['kyc','sanctions'] }  // optional
});

to charge for an HTTP resource (a paywall), use x402Gate / fetchWithX402

Verify a payment you received — don’t trust the Coordinator

Pin the adapter’s observation address once (out-of-band, from /chains) and verify locally — the proof is yours to check.

import { HSPVerifier } from '@hsp/sdk';

const verifier = new HSPVerifier({ chain, adapterAddress: PINNED });
const d = await verifier.verify(mandate, receipt, attestations);
if (d.ok && d.outcomeClass === 'ACCEPT') ship();

re-fetching the pin from the party you’re trying not to trust defeats the point

07

Build your own settlement adapter

@hsp/devkit

Want to settle a different way — a new rail, a new proof? @hsp/devkit gives you a template plus a conformance runner that checks your adapter against the protocol’s generic obligations using the real verifier. The verifier never changes; only your proof schema does.

# 1. start from a template that already passes conformance
cp packages/devkit/template/my-adapter.ts        my-team/adapter.ts
cp packages/devkit/template/run-conformance.ts   my-team/run-conformance.ts
# 2. make it settle YOUR way, then self-test:
npx tsx my-team/run-conformance.ts
# → forged signatures, replays, deadline, observation reuse… all exercised

Then submit (adapterId, instanceKey, signing address, reorgPolicy) to the organizer to be registered in the sandbox Coordinator’s trust set — payers can settle through you. No protocol changes, no permission from the spec.

08

AI agents

Give an agent a wallet, safely

Two pieces let an AI agent pay safely — the agent never sees a private key beyond a small demo wallet you scope, and hsp_pay is bounded by hard server-side guardrails.

@hsp/mcp

An MCP server exposing hsp_quote / hsp_pay / hsp_status / hsp_verify. hsp_pay requires explicit confirm: true and is bounded by a SpendGuard: per-tx cap, daily cap, optional recipient allowlist.

// .mcp.json env
"HSP_COORDINATOR_URL": "<COORDINATOR_URL>",
"HSP_API_KEY": "<API_KEY>",
"HSP_CHAIN": "hashkey-testnet",
"HSP_AGENT_PRIVATE_KEY": "0xDEMO_SMALL_AMOUNT_ONLY",
"HSP_MAX_AMOUNT_BASE_UNITS": "10000000",
"HSP_DAILY_CAP_BASE_UNITS": "50000000"

skills/hsp-pay

A skill that teaches the agent the safe flow — always quote → get user approval → pay — and routes intents to the right tool. Drop it into your agent’s skills directory and point the MCP server at <COORDINATOR_URL> with your scoped demo key.

flow: quote → explicit user approval → pay{confirm:true} → track

09

Coordinator endpoints

Write endpoints need a Bearer key
MethodPathPurpose
POST/paymentsregister a signed mandate (+ attestations)
POST/payments/:id/observeask the Coordinator to observe your settlement tx
GET/payments/:idpayment status + the stored triple
GET/payments/:id/explainlabel-resolved decision trace (what the Explorer shows)
GET/requirements?chain=the deployment’s requirement advertisement
GET/chainschain registry + the adapter address to pin
GET/statspublic aggregate dashboard (JSON)
GET/docs, /explorerthis portal + the decision-trace UI

Write endpoints need Authorization: Bearer <API_KEY>. paymentId = mandateHash (idempotent); a 202 from observe means the tx is still confirming — the SDK retries for you.

10

Outcomes & error handling

Branch on outcomeClass

Every verifier decision carries an outcomeClass — the HTTP-status-code of settlement. Branch on the class; log the errorCode.

ClassMeaningWhat to do
ACCEPTsettled and verified under your policyship
RETRYABLEtransient — not observable yet, stale stateretry
POLICYa policy / requirement isn’t metfix the mandate / attestations
PERMANENTstructurally invalid — contradicts the mandategive up / debug
Frequent codeIt means
HSP-MAND-EXPIREDsettled after the mandate deadline (an on-time settlement stays verifiable later)
HSP-RCPT-SIGreceipt not signed by a trusted adapter — check your pinned address
HSP-RCPT-PROOFamount / recipient / token mismatch — exact-amount only; no fee-on-transfer or multi-log txs on the public path
HSP-RCPT-OBS-REUSEDthis on-chain transfer already settled another mandate (one settlement, one payment)
HSP-MAND-REQ-INSUFFICIENTmandate doesn’t require everything the deployment’s policy floor demands
11

Trust model & safety

One paragraph, three rules

HSP is zero-custody: your wallet settles; the Coordinator only signs observations with an adapter key — it cannot move funds. A relying party (a merchant, an auditor, a platform) pins the adapter’s address once (from GET /chains, out-of-band) and runs the verifier itself. If the Coordinator lied, your independent verification fails — that is the whole design.

Pin, don’t fetch-and-trust

Record the adapter address once and hardcode it. Never re-fetch it from the party you are trying not to trust.

Demo keys are demo keys

Example and MCP agent keys are for small testnet amounts. Anything that matters belongs in a real wallet — use the eip1193 path; the key stays in the wallet.

Caps on, always

Set HSP_MAX_AMOUNT_BASE_UNITS and HSP_DAILY_CAP_BASE_UNITS for any agent-held key; the faucet rate-limits per address & IP; team API keys are per-team — don’t share.

12

This sandbox

What the organizer gives you

The hackathon sandbox runs the full stack on HashKey Chain testnet so you run nothing yourself. One HTTPS host fronts every service; point your SDK at these base URLs:

You setValueWhat it is
HSP_COORDINATOR_URL<COORDINATOR_URL>the hub — register, observe, verify, Explorer
HSP_ISSUER_URL<COORDINATOR_URL>/issuermock compliance issuer (kyc + sanctions)
HSP_FACILITATOR_URL<COORDINATOR_URL>/facilitatorx402 v2 facilitator (for scenarios 2 & 4)
HSP_API_KEY<API_KEY>your team’s write key (Bearer)
HSP_CHAINhashkey-testnetchain name + the pinned stablecoin

Faucet (testnet gas + USDC): /faucet/ — rate-limited per address & IP. The adapter address to pin for independent verification is published at /chains.

No external accounts to register. The organizer gives you the API key and the URLs above; you only generate your own testnet wallet (a private key) and fund it from the faucet. No Circle, Coinbase, KYC-provider, RPC, or npm sign-up — the mock issuer and the x402 facilitator are part of this sandbox. The Explorer is public: looking up a payment by its id needs no key (an API key is only for browsing the full payment list).

Chains

NameChain IDStablecoinNote
hashkey-testnet133USDC · 6 decfaucet-friendly — start here · RPC testnet.hsk.xyz
anvil-dev31337per-run MockERC20local development
hashkey177USDC.e · 6 decmainnet — real money
ethereum1USDC · 6 decmainnet — real money

Registry defaults shown; when this page is served by a live Coordinator the table reflects its actual configuration.