FTPFTP Tech LLC

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.

Start hereOne prompt · no install · ~60s

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.

Copy this prompt into your agent
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 `![generated image](<the image URL>)`, 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.

POST /v1/demo/ask
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.

GET / to self-discover
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 MCP

Hosted HTTP

No install

The 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.

Self-custody MCPHosted HTTP
InstallOwner runs one connect commandNone
TransportLocal stdioHTTP
Works in ChatGPT webNoYes
Who holds the keyThe agent, locallyFTP Tech (hosted)
Whose fundsThe agent's own walletHosted purse or hosted ephemeral wallet
Spend capsOwner-set (daily cap, allowlist, funded ceiling)Server-side daily budget
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.

POST/v1/quest/wallet/run

Body { clientRef?, prompt? }→ 202 { runJobId }. One call: create + fund + pay.

GET/v1/quest/wallet/run-result?runJobId=...

Poll to status:"done" party, secret, image, updateId, paid.

POST/v1/quest/wallet/create

Body {} → 202 { walletJobId }.

GET/v1/quest/wallet/result?walletJobId=...

Poll to status:"funded" party, balanceCc, secret, walletToken.

POST/v1/quest/wallet/pay

Body { walletToken, prompt } → 202 { payJobId }. No secret is sent. The server pays from your funded wallet.

GET/v1/quest/wallet/pay-result?payJobId=...

Poll to status:"done" (about 20 to 30 seconds) → { party, updateId, answer, image?, model, balanceCc }.

GET/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).

Run it yourself (curl + jq)
# 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:

Back to ftptech.xyz