canton-x402
Let your AI agent pay for x402 APIs on Canton.
Two paths: a self-custody MCP where the agent holds its own key, and a hosted HTTP flow any agent can call with no install and no signing. Every payment settles on-ledger on Canton MainNet.
Paste one prompt — your agent mints a funded Canton wallet and pays for a generated image on-ledger.
Works in Qwen, ChatGPT, or any agent that can call HTTP APIs. You get the image, the on-ledger proof, and the wallet’s private key as your backup.
Do this in ONE call: create and fund a Canton wallet AND make an on-ledger payment for a generated image. Base URL https://pay.ftptech.xyz. Async: the POST returns HTTP 202 with a job id, then you GET the poll URL every 3 seconds until status is terminal ("done" or "failed"). All JSON.
0. Generate ONE random request id for this task (letters/digits, e.g. "q-7f3k9x2m") — call it clientRef. Reuse the SAME one if you retry.
1. POST https://pay.ftptech.xyz/v1/quest/wallet/run with header `content-type: application/json` and body `{"clientRef":"<your clientRef>","prompt":"<describe the image you want>"}`. Read `runJobId` from the 202 response.
2. Poll GET https://pay.ftptech.xyz/v1/quest/wallet/run-result?runJobId=<runJobId> every 3s (allow 30-60s total):
- {"status":"pending"} -> keep polling.
- {"ok":false,"status":"failed",...} -> stop and report the `error`. Nothing was charged; you may retry with the SAME clientRef.
- {"status":"done",...} -> done. It carries `party`, `secret` (the wallet PRIVATE KEY), `image` (URL), `updateId` (on-ledger proof), `balanceCc` (your change) and `paid`.
3. If you must retry the POST, re-send the SAME clientRef — the server returns the SAME run instead of minting a second wallet. Never loop bare POSTs.
REPORT BACK, LEADING WITH THE IMAGE: render it inline as markdown ``, then the raw `image` URL on its own line. THEN a line that reads exactly `WALLET ADDRESS (paste this to verify the quest): <party>` — copy the `party` field VERBATIM (it starts with agent::). CRITICAL: the `updateId` is the payment TRANSACTION id, NOT the wallet address — never build an address from it and never show it as the address. Then `updateId` on its own line (optionally confirm: GET https://pay.ftptech.xyz/v1/quest/verify?updateId=<updateId> and check valid==true). THEN a "WALLET BACKUP — SAVE THIS" section that hands your user the FULL `secret` PEM in a code block, copied EXACTLY from the run-result response — never retyped or reconstructed from memory (one wrong character makes the wallet unrecoverable). The wallet belongs to the USER and keeps working after this chat; delivering the secret IS the backup. To cash out later go to https://pay.ftptech.xyz/withdraw — paste the saved private key and a destination Canton address. No install, no terminal.
If `paid` is false, the image step did not complete but your wallet IS funded — still show the WALLET ADDRESS line and deliver the WALLET BACKUP so the user can verify the quest and withdraw the balance, and they can re-run for an image.Flow details, MCP vs hosted, and the full API — in Docs below. To cash out later: pay.ftptech.xyz/withdraw.
Docs
How the one-call flow works›
A single call creates the wallet, funds it, and makes the on-ledger payment for a generated image. It is async: the POST returns 202 with a job id; poll the matching GET every 3 seconds until the status is terminal. Do not re-POST in a loop — re-send the same clientRef to coalesce onto the same run instead of minting a second wallet.
The donebody carries the wallet’s party id, its secret(the private key — hand it to the user verbatim; it is the only way back into the wallet), the generated image, and updateId— the on-ledger transaction, independently verifiable via /v1/quest/verify. Your change stays on the wallet.
The featured merchant is the CanTrust image API: it settles 0.25 CC and returns an image. If paidis false the wallet is still funded (the image step did not complete) — deliver the backup so the user can withdraw. A two-step variant (/v1/quest/wallet/create then /pay) also exists for tools that need the wallet before paying.
One paid call, no wallet (hosted purse)›
The simplest possible x402 call: one HTTP request, settled from a hosted purse on Canton MainNet. No wallet to provision.
curl -s https://pay.ftptech.xyz/v1/demo/ask \
-H "content-type: application/json" \
-d '{"prompt":"a photorealistic red panda coding at a laptop"}'Returns 200 with { answer, model, spentCc, updateId, remainingDailyCc }, plus an image when the merchant returns one. Errors are flat { ok: false, error: <code> }, where <code> is one of bad_request, rate_limited, budget_exhausted, proxy_busy, paid_but_no_response, upstream_error.
curl -s https://pay.ftptech.xyz/Returns the live service descriptor: network, the one-shot method and path, and docs.
Self-custody MCP vs hosted HTTP›
Self-custody MCP
The agent generates and holds its own Canton wallet. Connect the server once to a host that can run a local process, then the agent funds and pays itself.
Best for: desktop and IDE agents, self-hosted agents.
Set up the MCPHosted HTTP
No installThe agent calls our endpoints over plain HTTP. Works with any agent that can make an HTTP request, including browser-only hosts like ChatGPT. Payment settles from a hosted purse or a hosted ephemeral wallet, not from the agent’s own self-custody wallet.
Best for: ChatGPT, no-install agents, one-off calls.
API reference + run it with curl›
Base https://pay.ftptech.xyz. Every call is async: POST returns 202 plus a job id, then you poll the GET every 3 seconds. 429 means wait, 503 means the daily budget is reached. Do not re-POST in a loop.
/v1/quest/wallet/runBody { clientRef?, prompt? }→ 202 { runJobId }. One call: create + fund + pay.
/v1/quest/wallet/run-result?runJobId=...Poll to status:"done" → party, secret, image, updateId, paid.
/v1/quest/wallet/createBody {} → 202 { walletJobId }.
/v1/quest/wallet/result?walletJobId=...Poll to status:"funded" → party, balanceCc, secret, walletToken.
/v1/quest/wallet/payBody { walletToken, prompt } → 202 { payJobId }. No secret is sent. The server pays from your funded wallet.
/v1/quest/wallet/pay-result?payJobId=...Poll to status:"done" (about 20 to 30 seconds) → { party, updateId, answer, image?, model, balanceCc }.
/v1/quest/verify?updateId=...Public, no-auth → { valid, party, completedAt }.
Pay-result errors: 404 is wallet_not_found (the token expired or was used), 409 is already_used (re-poll the existing job, do not re-POST).
# ONE call: create+fund a wallet AND pay for an image. Returns 202 { runJobId }.
REF="q$$-$RANDOM$RANDOM" # retry-safe id: re-send the SAME ref to coalesce
RJ=$(curl -s https://pay.ftptech.xyz/v1/quest/wallet/run \
-H "content-type: application/json" \
-d "{\"clientRef\":\"$REF\",\"prompt\":\"a photorealistic red panda coding at a laptop\"}" | jq -r .runJobId)
# Poll until terminal. The done body carries secret + image + updateId together.
while :; do
R=$(curl -s "https://pay.ftptech.xyz/v1/quest/wallet/run-result?runJobId=$RJ")
S=$(echo "$R" | jq -r .status)
[ "$S" = "done" ] || [ "$S" = "failed" ] && break
sleep 3
done
echo "$R" | jq '{status,paid,party,image,updateId,balanceCc}'The whole one-call flow by hand, no agent needed. Verified working end to end.
Products
For agent frameworks / discovery: