402, settles the SPL transfer through a real
facilitator, credits your agent’s Asset Signer PDA treasury, and
writes a chained ReceiptV1 for every call — all visible on
explorer.leash.market.
Pick your integration shape
| Shape | Best when | You ship |
|---|---|---|
| Hosted payment link | Fixed or templated JSON response, zero new infra, sharable URL | POST /v1/payment-links → share share_url |
Sidecar (@leash/seller-kit) | Your API is TypeScript / Hono; you want the route inside your process | createSeller + env for receipt fan-out |
| Edge (any language) | Python / Go / Rust / Worker in front of your route | Buyer + seller HTTP utilities, or link + webhook |
Step 0 — Create your seller agent (once per mint)
Every monetised route needs an owner agent (one MPL Core mint). The x402payTo is derived from that asset; explorer and metrics key off
the same mint.
Create the mint + Agent Identity
- HTTP (any language):
POST /v1/agents/prepare→ signtransaction.base64→POST /v1/submit→ pollGET /v1/events/{id}untilphase: confirmed. The prepare path auto-watchlists the new agent for the indexer. - TypeScript:
createAgentorprepareAgentMint+sendPreparedAgentMintfrom@leash/registry-utils— same on-chain result as the API.
- Provision ATAs for every stable you list in
accepts(or the link will fail at settle when the treasury has no account for that mint):POST /v1/agents/{mint}/treasury/provision/prepare→ sign →POST /v1/submit(idempotent;no_opif already done). - Enrol the treasury for explorer fund events: call
GET /v1/agents/{mint}/treasury/balancesonce after provisioning. That registers the PDA + ATAs on the watchlist. See Explorer tracking for the full copy-paste sequence.
POST /v1/agents/{mint}/executive/register/prepare→ submitPOST /v1/agents/{mint}/executive/delegate/prepare→ submitPOST /v1/agents/{mint}/delegation/prepare(SPLApprovefrom treasury to executive) → submit
Try it in the playground (no API key in the browser)
The web app ships a Payment-Link Builder at/seller — same x402
semantics as the hosted API, useful before you script curl. Walkthrough:
Create a payment link.
Hosted payment links (fastest path)
OnePOST creates a public paywall on api.leash.market:
share_url (public paywall), pay_to, and
accepts[]. Buyers hit it, receive 402 + PAYMENT-REQUIRED, sign
TransferChecked, replay with X-PAYMENT, and get your configured
response after settlement. Full CRUD, hooks, and counters:
Payment links.
To proxy your existing SaaS after payment, set response to forward
to your backend (see that page for the proxy / template fields) so
you do not duplicate business logic in static JSON.
End-to-end flow (sidecar / edge)
When the paid response comes from a route you already run, the handshake is the same: The buyer does not reach your origin until they pay. Your origin does not need to speak x402 — Leash handles that at the edge or inside the hosted paywall.Sidecar mode (TypeScript / Hono)
@leash/seller-kit exposes createSeller(app, options) — real @x402/hono middleware, Asset Signer PDA as payTo,
and implicit receipt fan-out to the Leash API when both env vars are
set and you omit a custom onReceipt callback (same rules as
@leash/buyer-kit: a function passed as onReceipt
replaces the default sink entirely).
LEASH_SELLER_AGENT must be a mint that completed Step 0 (create +
provision + balances ping). Skipping provisioning does not block every
settle, but treasury fund lines on the explorer may never appear.
How forwarding works: when onReceipt is undefined (as in the
snippet above) and LEASH_API_URL plus LEASH_API_KEY are set, the
default receipt sink POSTs each settled ReceiptV1 to
POST /v1/receipts/{agent} on the API. That ingests the row, emits
receipt.published, and joins the explorer timeline. Omit those env
vars and nothing is forwarded to the API (only runner, if
LEASH_RUNNER_URL is set). Pass onReceipt: false to disable publishing
entirely, even when env vars are present.
Edge mode (any language)
Option A — payment link + webhook
Create a link withwebhook_url pointing at your backend. On settle,
Leash POSTs a payment_link.settled payload (see
Payment links) so you can flip entitlements,
enqueue jobs, or issue API keys without the buyer hitting your service
first.
Option B — buyer HTTP utilities
For a true 402 dance on a URL you control, the buyer endpoints mirror@leash/buyer-kit but execute on
Leash’s servers: your server calls api.leash.market with your API key,
and POST /v1/buyer/payment/execute finalises the spend receipt inside
the API — so explorer / receipt.published do not depend on
LEASH_API_URL in your buyer process (unlike a Node app that uses
createBuyer with the default sink and no custom onReceipt).
POST /v1/buyer/payment/execute replays the request,
parses PAYMENT-RESPONSE, finalises a spend receipt, and returns the
seller body plus tx_sig.
For seller-side helpers without Node (networks, facilitator,
parse-price, pay-to), use Seller utilities.
What you get
- Pay-per-call settlement — real SPL transfer, explorer
tx/link. - Hosted paywalls —
/x/{id}with counters and idempotent creates. - Receipt feed —
GET /v1/receipts/{agent}; verify with@leash/coreorPOST /v1/buyer/receipt/verify. - Treasury control — owner-driven withdraw via
/v1/agents/{mint}/treasury/.../prepare.
What you owe — the 1% Leash protocol fee
Every settlement routed through a Leash facilitator (devnet or mainnet hosted, or any self-hosted facilitator running@leash/facilitator)
adds a second TransferChecked for the same mint to the Leash fee
treasury. The math is unconditional and visible in the buyer’s signed
transaction:
- The seller is always quoted net. The price you set on the link or
in
createSelleris exactly what lands in yourpayToATA. - The buyer pays
gross = amount + fee, wherefee = ceil(amount * 100 / 10_000)—1.00%ceiling-rounded in atoms. - The fee leg targets a Leash-owned wallet identical on both clusters:
3DdcJkvjW7KLtMeko3Zr57jEJWhqRHuPsEBFm1XJYh7W. Verify it live onGET /v1/health(protocol_fee.authorities). - Receipts (
earnandspend) carry the breakdown:price.amount,price.fee,price.gross,price.feeBps,price.feeAuthority. The explorer surfaces this as a “Settlement breakdown” panel and aggregates collected fees per mint underprotocol.fee.collectedevents. - Sellers stamp
paymentRequirements.extra['leash.fee']on the 402 so buyers can verify the rate before signing. Vanilla x402 facilitators (no Leash semantics on/health) settle foramountflat — the fee leg is only enforced when the facilitator advertises it.
LEASH_FEE_* env knobs, see
Protocol fee.
Which shape should I pick?
| Question | Hosted link | Sidecar | Edge |
|---|---|---|---|
| Response is fixed JSON | Yes | Yes | Yes |
| API is TypeScript / Node | Yes | Yes | Possible |
| Zero new processes | Yes | Same as your server | Needs worker |
| Language must stay polyglot | Yes (curl) | No | Yes |
| Sharable URL out of the box | Yes | DIY | DIY |

