A self-hosted appointment server with x402 micropayments: $0.001 to read the open grid, a $0.01 refundable hold to claim a time. Confirmation, meeting link, cancel token, cancellation policy and a calendar invite come back in the same HTTP response. Pay in USDC on Base or Solana — your client picks the rail.
| Route | Price | The artifact you get back |
|---|---|---|
GET /slots | $0.001 | Open start times per service — date, time, endsAt, plus duration, mode and the cancellation policy |
POST /appointments | $0.01 (refundable) | Confirmed appointment: time, meeting link, cancelPolicy, cancelToken, ledger entry, base64 .ics invite, HMAC signature |
POST /cancel/:id | free | Cancellation record + refund ledger entry (auth by cancelToken) |
GET /services | free | Service catalogue, hours, cancellation policy, prices, payment rails |
GET /.well-known/x402 | free | Machine-readable payment manifest for agents and indexers |
Every paid route returns the thing you bought in the 200 body. No pay-now-deliver-later.
POST /appointments with no payment. Server replies 402 with an accepts[] holding both rails — amount, token address and recipient on Base and on Solana.
Base: sign an EIP-3009 USDC authorization. Solana: sign an SPL transferChecked. Retry with the X-PAYMENT header; that rail's facilitator verifies and settles on the chain you chose.
200 with the confirmed appointment in the body and a settlement receipt in X-PAYMENT-RESPONSE, naming the rail. Settlement only runs on success.
import { wrapFetchWithPayment } from "x402-fetch";
import { privateKeyToAccount } from "viem/accounts";
const payFetch = wrapFetchWithPayment(fetch, privateKeyToAccount(PRIVATE_KEY));
const { services } = await payFetch("http://localhost:4022/slots?service=consult-30").then(r => r.json());
const slot = services[0].slots[0];
const appt = await payFetch("http://localhost:4022/appointments", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ service: "consult-30", date: slot.date, time: slot.time, name: "Agent Ada" }),
}).then(r => r.json());
console.log(appt.appointmentId, appt.meetingLink, appt.cancelToken, appt.ics);
Calendly-style flow with the drop-in payment modal — pick a service, tap a time, pay with Phantom (Solana) or MetaMask (Base), download the invite.
public/index.html →skill.md + .well-known/x402 discovery, MCP tool wrapper for Claude, listing on x402scan / Bazaar.
agents.md →Every endpoint with params, prices, schemas, and error cases. OpenAPI 3.1 included.
api.md →