Payments
Two endpoints, called in order. Both follow x402: call once to get the terms, sign, then call again with the payment.
| Method | POST |
| Body | { "packageId": 322 } |
| Payment header | payment-signature (the x-payment header is also accepted) |
| Token | USDG on Robinhood Chain (eip155:4663) |
packageId is the onchain identity id from the package list, not the database id. It goes in the JSON body. There are no query or path parameters.
Pay the platform fee
POST /api/pay/treasuryCharges 10% of the package's priceUnits, rounded down, to the treasury.
Without a payment → 402 with the terms.
With a valid payment → 200
{ "paid": true, "tx": "0x…" }If this wallet already paid the fee for this package, the payment is not settled again:
{ "paid": true, "tx": "0x…", "already": true }Pay the seller
POST /api/pay/sellerCharges the remaining 90% to the wallet that currently owns the package identity, read from the chain at request time.
With a valid payment → 200
{ "paid": true, "tx": "0x…", "body": "The package…", "filename": "SKILL.md" }| Field | Meaning |
|---|---|
paid | true |
tx | The settlement transaction |
body | The package itself |
filename | The file to save it as: SKILL.md, prompt.md, mcp.json, workflow.md or package.txt |
The response also sets a session cookie for the paying wallet, so a browser can open the package again later without paying.
| Status | When |
|---|---|
409 "Pay the platform fee first." | No fee payment is recorded for this wallet and package |
409 "You've already bought this package. Sign in to open it." | This wallet already bought it. Nothing is charged |
Errors on both endpoints
| Status | When |
|---|---|
404 "Package not found." | No live package has that packageId |
503 "Payments aren't open yet." | Payments are switched off |
Headers
| Header | Direction | Content |
|---|---|---|
payment-required | Response, on 402 | Base64 JSON of the terms (same as the body) |
payment-signature | Request | Base64 JSON of the signed payment |
payment-response | Response, on 200 | Base64 JSON receipt: { success, transaction, network, payer } |
What the server checks
Before settling anything, the signed terms must match what the server charges right now: same network, token, amount and recipient. A payment signed for a different amount or a different recipient is refused with a fresh 402.
Settlement happens before the response. When you get 200, the USDG has already moved onchain, and tx is the transaction.
More errors
See the response table in the payment guide.