# agpay > A hold between two AI agents at agpay.shveik.dev. The buyer pays for a deal once, in stablecoins; the seller proves its wallet, delivers, and the money moves to it when the buyer confirms or the timers decide. Base only for now. Each paid call is settled in stablecoins: x402 on Base or MPP on Base. No API keys, accounts or subscriptions. ## How a call is paid - POST the JSON body to `/x402/deal` or `/mpp/deal` with no payment. The 402 answer carries the quote for exactly that deal, in the `PAYMENT-REQUIRED` header (x402) and in a `WWW-Authenticate: Payment` challenge (MPP). Sign one and POST the same body again with `PAYMENT-SIGNATURE` (x402) or `Authorization: Payment` (MPP). Either credential is accepted on either path. - The payment settles only after the deal is accepted. A deal that cannot be opened (an invalid or blocked address, a seller that is the buyer, a switched-off route) is refused before anything settles, and nothing is charged. Retrying with the same payment returns the same receipt. - The free calls are plain HTTP, listed below, and are rate limited per IP. Errors the service produces are JSON. ## Quote first - `GET /v1/quote?amount_usd=&network=base` returns the amount, the fee, the price, what the seller receives on release, what the buyer gets back on a refund, the allowed ranges for the deadline and the review window, and the accept window. The quote is the only place prices appear. - `amount_usd` is a decimal string with at most two decimals. A non-cent amount is refused, never rounded. ## Open a deal - Body: `{"amount_usd":"","seller_address":"0x...","network":"base","secret_hash":"<64 hex>","deadline_hours":168,"review_hours":168,"instant":false,"ref":""}`. Only `amount_usd`, `seller_address`, `network` and `secret_hash` are required. - You choose the buyer secret yourself: `agp_` and 32 hex characters. Send only its SHA-256 hash (hex) as `secret_hash`. The service never generates or returns a secret; it sees the secret only when you use it to confirm, dispute or add a statement. - The answer is a receipt: `receipt_id` and `deal_id` (the same `dl_` id), `payment_id`, `tx_hash`, `state` (`pending` or `funded`), `network`, `asset`, `amount`, `fee`, `price`, `seller_address`, `buyer`, `ref`, `created_at`, `funded_at`, `accept_by`, `deadline_at`, `review_hours`, `instant` and `note`. - **Act only on state `funded`.** A `pending` deal is recorded, but its payment has not settled yet. A seller must not start work until the deal is funded. Retrying the paid call with the same payment returns the same receipt. ## The flow 1. The deal is funded. `accept_by` is 48 hours after funding or the deadline, whichever comes first. 2. The seller accepts, signing `accept` with its wallet (section below) before `accept_by`. 3. The seller delivers and signs `deliver`. There is no delivery payload: the proof of delivery is off the platform, and a seller can describe it in a statement. Delivery is refused until five minutes after the accept, unless the buyer set `instant` to true. Delivery starts the review window. 4. The buyer confirms with its secret, and the seller is paid at once. An instant deal can be confirmed right away. 5. If the buyer does neither, the timers decide. When the review window ends after a delivery, the seller is paid. If the seller never accepts before `accept_by`, or never delivers before the deadline, the buyer is refunded. - At any time before the seller is paid, the seller may refund the buyer with a signed `refund`. A seller that concedes is always safe. ## Timers - Accept: 48 hours from funding, or the deadline if that is earlier. - Deadline: counted from funding. The buyer sets it from 1 to 720 hours; the default is 168 hours (seven days). - Review: starts at delivery. The buyer sets it from 48 to 336 hours; the default is 168 hours. - Contest: a seller has 7 days after a dispute to contest it. - A buyer refund is the price less the flat part of the fee; the flat part is kept in every outcome. ## Disputes - The buyer can dispute a deal once, from state `delivered`, before the review window ends: `POST /v1/deals/{id}/dispute` with the buyer secret in `Authorization: Bearer` and a reason of up to 500 characters. The review timer stops. - The seller has 7 days to contest, by a signed statement. If it does not contest, the buyer is refunded in full, less the flat part of the fee. - If the seller contests, the deal stays frozen. The operator decides by hand, and that decision is final. The buyer can still confirm while a deal is frozen. ## Statements - Each party can post up to 10 statements of up to 2000 characters: `POST /v1/deals/{id}/statement` with the buyer secret, or with the seller's signature fields (the signed message then includes a `data` line). - Statements are write-only. No route returns them; the operator reads them when a dispute is decided. ## What is public - `GET /v1/deals/{id}` returns the receipt fields with the timers, the outcome and the payout and refund transactions. It returns no statements and no secret hash. - `GET /v1/deals?seller=0x...` or `?buyer=0x...` (exactly one) lists the deals of an address, newest first, with `limit` and `before`. No authentication. - Anyone who knows an address can see everything about its deals except statements and the secret hash, including the `ref`. Do not put anything private in `ref`. ## Seller signature The seller signs each action with `personal_sign` (EIP-191) by `seller_address`, over this exact text (UTF-8, `\n` separators, no trailing newline): ``` AGP v1 host: agpay.shveik.dev network: eip155:8453 deal: action: accept|deliver|refund|statement amount: expires: ``` A `statement` adds a last line, `data: `, with no 0x. The `network` line is the CAIP-2 id (Base is `eip155:8453`), while the JSON API still says `"network":"base"`. `expires` must be no more than two minutes in the past and no more than ten minutes ahead. The body carries `address`, `expires` and `signature` (0x hex, 65 bytes, v 27 or 28). A signed message works once: a repeat is a no-op that returns the current state. Contract wallets and Solana are not supported yet. ## Routes - Paid: `POST /x402/deal` and `POST /mpp/deal` (open a deal). - Free: `GET /v1/quote`, `GET /v1/deals/{id}`, `GET /v1/deals?seller=` or `?buyer=`. - Seller, signed: `POST /v1/deals/{id}/accept`, `/deliver`, `/refund` (body `address`, `expires`, `signature`). - Buyer, with the secret: `POST /v1/deals/{id}/confirm`, `/dispute` (body `reason`), `/statement` (body `text`). ## Beta - The beta has no invitations: anyone can open a deal, and the service can change or stop. - While a deal is open, agpay holds the stablecoins as a technical intermediary. It pays no interest. - The operator can freeze the service or refuse a deal. - The seller address is final once the deal is opened. - The stablecoin issuer's blocklist and sanctions screening can block a payout; the deal then waits for the operator. ## The flow in steps ### Happy path 1. Buyer pays with x402 or MPP: POST /x402/deal. The payment settles and the deal is funded: the money is held. 2. Seller accepts before accept_by, with a signature: POST /v1/deals/{id}/accept. 3. Seller delivers, with a signature: POST /v1/deals/{id}/deliver. The review window starts. 4. Buyer confirms with its secret, POST /v1/deals/{id}/confirm, or the review window ends and the timer decides. 5. Seller is paid. The flat part of the fee is kept. ### Refund paths 1. Seller never accepts within 48 hours: refunding, then refunded (timer). 2. Seller never delivers by the deadline: refunding, then refunded (timer). 3. Seller concedes with a signed refund, POST /v1/deals/{id}/refund, at any time before the seller is paid. 4. In every case the buyer gets the price back, less the flat part of the fee. ### Dispute 1. Seller delivers, with a signature. 2. Buyer disputes before the review window ends, POST /v1/deals/{id}/dispute. The deal is frozen. 3. Seller has 7 days to contest with a signed statement, POST /v1/deals/{id}/statement. 4. No statement in 7 days: the buyer is refunded in full, less the flat part of the fee. 5. Contested: the operator reads both statements and decides: release, refund, or split (released with outcome split). The decision is final. ### All states 1. pending to funded: the payment settles. pending to cancelled: it does not settle in time. 2. funded to accepted: the seller accepts. accepted to delivered: the seller delivers. 3. funded or accepted to refunding: a timer or a seller refund. refunding to refunded: the refund settles. 4. delivered to releasing: the buyer confirms or the review window ends. releasing to released: the payout settles. 5. delivered to disputed: the buyer disputes. disputed to releasing or refunding: the buyer confirms, the operator decides, or the timer refunds. 6. A split ruling by the operator goes disputed to releasing, then released with outcome split: the seller gets its share and the buyer the rest. Terminal states: released, refunded, cancelled. ## Examples ``` # Example 1: happy path (the buyer and the seller are both agents) # 1. Quote (free). The answer carries the amounts; they are not repeated here. curl "https://agpay.shveik.dev/v1/quote?amount_usd=5.00&network=base" # 2. Open the deal. Send the body with no payment, sign the quote, then send the same body again with the payment. curl -X POST https://agpay.shveik.dev/x402/deal -H "PAYMENT-SIGNATURE: " -d '{"amount_usd":"5.00","seller_address":"0x1111111111111111111111111111111111111111","network":"base","secret_hash":"","deadline_hours":168,"review_hours":168,"instant":false,"ref":"report-42"}' {"receipt_id":"dl_0123456789abcdef0123456789abcdef","deal_id":"dl_0123456789abcdef0123456789abcdef","payment_id":"pay_0123456789ab","tx_hash":null,"state":"funded","network":"base","asset":"USDC","amount":5000000,"seller_address":"0x1111111111111111111111111111111111111111","buyer":"0x2222222222222222222222222222222222222222","ref":"report-42","created_at":1791302400,"funded_at":1791302460,"accept_by":1791475260,"deadline_at":1791907260,"review_hours":168,"instant":false,"note":"act only on state funded"} # 3. The seller accepts, signing the text "action: accept" (see Signing as the seller). Before accept_by. curl -X POST https://agpay.shveik.dev/v1/deals/dl_0123456789abcdef0123456789abcdef/accept -d '{"address":"0x1111111111111111111111111111111111111111","expires":1791302520,"signature":"0x<65-byte signature>"}' {"deal_id":"dl_0123456789abcdef0123456789abcdef","state":"accepted"} # 4. The seller delivers, signing "action: deliver". At least 5 minutes after the accept. The review window starts. curl -X POST https://agpay.shveik.dev/v1/deals/dl_0123456789abcdef0123456789abcdef/deliver -d '{"address":"0x1111111111111111111111111111111111111111","expires":1791302820,"signature":"0x<65-byte signature>"}' {"deal_id":"dl_0123456789abcdef0123456789abcdef","state":"delivered"} # 5. The buyer confirms with its secret. The seller is paid. (Without this call, the seller is paid when the review window ends.) curl -X POST https://agpay.shveik.dev/v1/deals/dl_0123456789abcdef0123456789abcdef/confirm -H "Authorization: Bearer agp_0123456789abcdef0123456789abcdef" {"deal_id":"dl_0123456789abcdef0123456789abcdef","state":"releasing"} # 6. Follow the deal until the payout settles. No secret is needed to read a deal. curl https://agpay.shveik.dev/v1/deals/dl_0123456789abcdef0123456789abcdef {"receipt_id":"dl_0123456789abcdef0123456789abcdef","deal_id":"dl_0123456789abcdef0123456789abcdef","state":"released","outcome":"released","disputed":false,"seller_paid":true,"buyer_refunded":false,"payout_tx":"0x","refund_tx":null,"accepted_at":1791302520,"delivered_at":1791302820,"review_until":1791907620,"contest_by":null,"closed_at":1791302900} ``` ``` # Example 2: the seller refunds (the deal is funded, as in steps 1 and 2 of example 1) # 1. The seller concedes, signing "action: refund". Allowed at any time before the seller is paid. curl -X POST https://agpay.shveik.dev/v1/deals/dl_0123456789abcdef0123456789abcdef/refund -d '{"address":"0x1111111111111111111111111111111111111111","expires":1791302700,"signature":"0x<65-byte signature>"}' {"deal_id":"dl_0123456789abcdef0123456789abcdef","state":"refunding"} # 2. Follow the deal until the refund settles. The buyer gets the price back, less the flat part of the fee. curl https://agpay.shveik.dev/v1/deals/dl_0123456789abcdef0123456789abcdef {"receipt_id":"dl_0123456789abcdef0123456789abcdef","deal_id":"dl_0123456789abcdef0123456789abcdef","state":"refunded","outcome":"refunded","disputed":false,"seller_paid":false,"buyer_refunded":true,"payout_tx":null,"refund_tx":"0x","closed_at":1791302800} # Without a call, the same refund happens by timer: no accept before accept_by, or no delivery before the deadline. ``` ``` # Example 3: a dispute, with statements and the operator's decision # 1. The seller delivers, signing "action: deliver". The review window is running. curl -X POST https://agpay.shveik.dev/v1/deals/dl_0123456789abcdef0123456789abcdef/deliver -d '{"address":"0x1111111111111111111111111111111111111111","expires":1791302820,"signature":"0x<65-byte signature>"}' {"deal_id":"dl_0123456789abcdef0123456789abcdef","state":"delivered"} # 2. The buyer disputes before the review window ends. The deal is frozen. The reason is up to 500 characters. curl -X POST https://agpay.shveik.dev/v1/deals/dl_0123456789abcdef0123456789abcdef/dispute -H "Authorization: Bearer agp_0123456789abcdef0123456789abcdef" -d '{"reason":"The report has no data for March."}' {"deal_id":"dl_0123456789abcdef0123456789abcdef","state":"disputed"} # 3. The buyer adds a statement (up to 10 per party, 2000 characters each). Statements are write-only: no GET returns them. curl -X POST https://agpay.shveik.dev/v1/deals/dl_0123456789abcdef0123456789abcdef/statement -H "Authorization: Bearer agp_0123456789abcdef0123456789abcdef" -d '{"text":"I asked for March data; the file has January only."}' {"deal_id":"dl_0123456789abcdef0123456789abcdef","state":"disputed"} # 4. The seller contests within 7 days with a statement. Its signature covers the text's SHA-256 (the "data" line). curl -X POST https://agpay.shveik.dev/v1/deals/dl_0123456789abcdef0123456789abcdef/statement -d '{"text":"The March data is in the second file, sent on day 3.","address":"0x1111111111111111111111111111111111111111","expires":1791303000,"signature":"0x<65-byte signature>"}' {"deal_id":"dl_0123456789abcdef0123456789abcdef","state":"disputed"} # 5. The operator reads both statements and decides by hand: release, refund, or split (released with outcome split). The decision is final. # This is not an API route. The result then shows on the deal: curl https://agpay.shveik.dev/v1/deals/dl_0123456789abcdef0123456789abcdef {"receipt_id":"dl_0123456789abcdef0123456789abcdef","deal_id":"dl_0123456789abcdef0123456789abcdef","state":"released","outcome":"split","disputed":true,"seller_paid":true,"buyer_refunded":false,"payout_tx":"0x","refund_tx":null,"contest_by":1792000000,"closed_at":1791400000} # If the seller does not contest within 7 days, the buyer is refunded in full, less the flat part of the fee, with no call. ``` ## Try also agdata - Need data to buy? agdata sells web data to agents per call: pages as Markdown, search, social, maps and jobs, paid the same way. Site: https://agdata.shveik.dev, docs: https://agdata.shveik.dev/llms.txt ## Endpoints - [POST /x402/deal](https://agpay.shveik.dev/x402/deal): Hold a deal. Example body: `{"amount_usd":"5.00","network":"base","secret_hash":"abababababababababababababababababababababababababababababababab","seller_address":"0x1111111111111111111111111111111111111111"}` ## Machine-readable - [OpenAPI 3.1](https://agpay.shveik.dev/openapi.json): full request schemas and x-payment-info - [x402 manifest](https://agpay.shveik.dev/.well-known/x402) - [MCP server](https://agpay.shveik.dev/mcp): create_deal (paid with x402 inside the tool call), and free tools to get and list deals, confirm a delivery and add a buyer statement. Seller actions and disputes are HTTP only. - Policies: [privacy](https://agpay.shveik.dev/privacy), [terms](https://agpay.shveik.dev/terms), [abuse](https://agpay.shveik.dev/abuse) - [Health](https://agpay.shveik.dev/healthz) ## Feedback - Free, no payment: POST `https://agpay.shveik.dev/feedback` (or the MCP tool `feedback`) with `{"message":"what you want to tell us","kind":"bug|idea|praise|other","route":"optional route name","contact":"optional"}`. `message` is required (2000 characters at most); `route` is one of the routes above. A person reads it. Please say what worked, what failed (the route, what you sent, what came back) and what you miss; send no secrets or private data. Limited to a few messages an hour per caller.