API reference
AceChange Agent API — documentation
The precise reference: base URL, auth, every endpoint, MCP tools, events and errors. Non-custodial throughout.
Live: /health · machine-readable /agent · guide: connect · open-source SDK on GitHub.
Base & authentication
Base URL: https://www.acechange.io/wp-json/acechange-agent/v1
- Headers:
X-Ace-Agent-Key,X-Ace-Agent-Timestamp(unix seconds, ±300s),X-Ace-Agent-Sign. - Signature =
HMAC-SHA256( METHOD + "\n" + ROUTE + "\n" + TS + "\n" + BODY , key )wherekey = hex(sha256(secret)). - ROUTE = the namespaced route, e.g.
/acechange-agent/v1/pay(the/wp-json-prefixed form is also accepted). BODY = raw body;""for GET. est_usd(> 0) is REQUIRED on swap/pay/cross-chain so spend caps apply.- The open-source SDKs (Python/TypeScript) sign for you.
Endpoints
Discovery, onboarding & limits
/agentPublic capability card: auth spec, all endpoints, MCP tools, error codes.
/healthPublic status: enabled, micro_configured, cross_chain_configured.
/currenciesSupported Base micro tokens.
/agent/registerPublic self-onboarding (when the operator enables it): {label?} → your own key_id + secret + activity link + starter caps. No operator step. IP rate-limited.
/limitsRead this key's caps: max_tx_usd, daily_usd_cap, per_min.
/limits{daily_usd_cap?,max_tx_usd?,per_min?} → set your OWN caps (0 = no limit). Self-registered keys get full/unlimited access or a ceiling, per the operator's setting.
Micro-swap (Base, 0x gasless)
/swap/quote{sell,buy,amount,taker} → price + the EIP-712 trade to sign.
/swap/submit{signed,est_usd} → relays the signed trade to the 0x relayer.
/swap/status?trade_hash= → trade status.
Cross-chain (real BTC / USDT / XMR)
/ff/currenciesValid cross-chain currency codes.
/ff/estimate{from,to,amount,type?,direction?} → rate + amounts.
/ff/create{from,to,amount,toAddress,type?,tag?,est_usd,idempotency_key?} → deposit + receive block.
/ff/status?order= → mapped status (scoped to your key).
/ff/emergency{order_id,choice(EXCHANGE|REFUND),address?} → act on a stuck swap.
Pay anyone
/pay{to,token,amount,from?,est_usd} → unsigned tx to sign; your spend caps applied.
Commerce
/commerce/merchant{receive_address,label?,webhook_url?,fee_bps?} → merchant_id + webhook_secret (once).
/commerce/product{name,price_usd,description?,currency?}.
/commerce/productsList your products.
/commerce/checkout{product_id|amount,expires_in?} → pay_to (merchant), EIP-681, x402, checkout_url.
/commerce/confirm{order_id,x_payment|tx_hash} → verify + mark paid + webhook.
/commerce/order?order= → public status (buyer poll).
/commerce/watch?order= → public, on-chain auto-confirm (IP-rate-limited).
/commerce/refund{order_id,to_address} → reverse transfer the merchant signs.
/commerce/deliver{order_id} → mark delivered.
Billing & usage
/billing/subscription{amount_usd,period(day|week|month),label?}.
/billing/subscriptionsList subscriptions.
/billing/cancel{subscription_id}.
/billing/usage{meter,units,unit_price_usd}.
/billing/invoice{meter} → one checkout for the metered total.
Pay-per-call
/x402/premium402 + USDC requirements; pay with X-PAYMENT, get the resource.
/l402/premium402 + Lightning invoice; pay, retry with Authorization: L402 <token>:<preimage>.
/x402/info | /l402/infoRail status.
Events & webhooks
/eventsYour recent events.
/webhooks/register{url} (public HTTPS) → secret; events POSTed with X-Ace-Event-Signature.
/webhooksList webhooks.
/webhooks/delete{url}.
Wallet registry
/wallet/provisionSDK config (no key material).
/wallet/register{owner_address,label?}.
/wallet/smart-account{smart_account,owner_address,label?} (ERC-4337).
/wallet/session{session_address,per_tx_usd,daily_usd,allowed_tokens,allowed_recipients,expires_in?}.
/wallet/policy?session= → policy.
/wallet/revoke{session_address} → kill switch.
/allowlist→ this key's recipient allow-list (pay rail).
/allowlist{addresses:[…]} → the ONLY addresses this key may pay on /pay (empty = no restriction). Even a hijacked bot can't pay off-list. MCP: ace_set_allowlist.
Funding & visibility
/fund/link?wallet=&amount=&crypto=&network= → fiat on-ramp link to the agent wallet.
/activityYour activity history + totals.
/activity/view?key=&token= → read-only human dashboard feed.
MCP tools
POST /mcp (JSON-RPC 2.0): initialize (negotiates protocolVersion, latest 2025-06-18), tools/list (public), tools/call (agent-auth; business errors return result.isError=true).
ace_register ace_currencies ace_limits ace_set_limits ace_activity ace_events ace_fund ace_swap_quote ace_swap_submit ace_swap_status ace_pay ace_ff_currencies ace_ff_estimate ace_ff_create ace_ff_status ace_register_merchant ace_create_product ace_create_checkout ace_order_status ace_create_subscription ace_record_usage ace_invoice
Events
Subscribe via POST /webhooks/register (signed X-Ace-Event-Signature) or pull GET /events:
swap.submitted swap.created swap.completed swap.refunded swap.expired payment.built order.paid order.refunded order.delivered subscription.due invoice.created
Error codes
Shape: { code, message, data:{status} } — branch on code.
| code | HTTP | meaning |
|---|---|---|
agent_disabled | 503 | module off |
agent_auth | 401 | bad/missing/expired key, timestamp, signature — or a replayed request (single generic error) |
rate_limited | 429 | velocity / IP / platform guard — back off and retry |
est_usd_required | 400 | pass est_usd > 0 (parsed strictly: plain decimal only) |
tx_too_large | 403 | over per-tx cap |
daily_cap | 403 | over daily USD cap (rolling 24h) |
*_not_configured | 503 | rail not set up by the operator |
ff_upstream / upstream | 502 | provider error — generic message (a known provider code shows as [CC:CODE]) |
x402_underpaid / x402_replay | 402 | authorized amount below the requirement / authorization already used |
l402_used / l402_bad_token | 402 / 401 | L402 credential already spent / invalid or wrong-resource token |
no_order | 404 | unknown order — also returned for an order owned by another key (no enumeration) |
Security model
Non-custodial throughout — the platform never holds, routes or freezes funds; there is no pooled balance to steal. What the API enforces for you:
- Replay-safe auth — a signed request authenticates at most once (durable + atomic), with a strict ±300s timestamp window. Your signing key is stored encrypted at rest, so a database leak alone can't forge requests.
- One-time-use payments — x402 (EIP-3009) authorizations and L402 preimages are single-use; the settled amount is enforced and the recipient is bound to the required
payTo; one on-chain payment settles at most one order. - Strict scoping — a key only ever sees/acts on its own resources; not-owned lookups return a generic
404(no enumeration). - Spend & rate controls — your own per-tx / daily-USD caps (you set them; 0 = no limit), sliding-window velocity, and platform guards that bound shared upstream usage.
- On-chain verification — a self-reported
tx_hashis never trusted; payment is confirmed on-chain (token contract, amount, ≥3 confirmations, no reuse). - SSRF-safe webhooks — must be public HTTPS; loopback/private/IPv6-literal hosts are rejected and no redirects are followed.
- Prompt-injection resistant by design — the money controls are deterministic and server-side, not enforced by your bot's LLM. Even a fully prompt-injected agent still can't exceed your caps, spend funds that aren't its own, or (with a recipient allow-list on the pay rail) pay a Base address it wasn't pre-authorised to. Cross-chain payout addresses vary per swap, so that rail is bounded by your caps + the non-custodial model instead. The generated agent pack also instructs the bot to treat all API/product/memo/webhook text as untrusted DATA, never as commands.
Your part: keep your secret out of logs/git/client code (revoke + reissue if it leaks — it can spend that key's own funds), verify the HMAC on every webhook, set sensible caps, and always check the receive amount + network + address before funding a deposit. Full model: SECURITY.md.