POST /v2/swap-plans returns a quote-only plan or wallet-bound VeChain clauses. It does not sign or submit. GET /v2/swap-plans describes the request contract.
First quote
Prerequisites: curl and the mainnet addresses of both supported tokens. This runnable request uses 1 native VET as input and B3TR as output. No wallet or key is required for quote_only.
Units beside the request: HTTP amountIn is an integer string in raw token units. VET has 18 decimals: "1000000000000000000" means 1 VET. slippageBps: 100 means 1%, not 100%.
curl -fsS https://vetrade.vet/v2/swap-plans \
-H 'Content-Type: application/json' \
--data-binary @- <<'JSON'
{
"mode": "quote_only",
"inputToken": "0x0000000000000000000000000000000000000000",
"outputToken": "0x5ef79995fe8a89e0812330e4378eb2660cede699",
"amountIn": "1000000000000000000",
"slippageBps": 100
}
JSON
Expected: quote, routePlan, warnings, and bundle: null. A route may be unavailable. The following successful-response excerpt is illustrative, with made-up raw output amounts; it is not a live quote or a complete response:
{
"apiVersion": "v2",
"mode": "quote_only",
"quote": {
"amountIn": "1000000000000000000",
"amountOut": "2000000000000000000",
"amountOutMin": "1980000000000000000",
"slippageBps": 100
},
"bundle": null,
"simulation": null,
"warnings": []
}
Read quote.generatedAt and quote.validUntil in the full response. Refresh the plan when expired; an old quote is not an available trade.
Amount units
| Entry point | Amount field | Meaning |
|---|---|---|
HTTP swap plans and hosted quote_swap | amountIn | Raw integer units of the input token |
Hosted quote | amount | Human decimal string; token selectors accept symbols or addresses |
Hosted swap | amount | Human decimal string such as "1.5" |
Local helper quote-swap and SDK quote_swap | amount | Human decimal string |
| Quote response | amountOut, amountOutMin | Raw integer units of the output token |
Use the token registry decimals for each side. Do not assume every token has 18 decimals. Convert using exact decimal/integer arithmetic, not JavaScript floating-point multiplication. Keep raw amounts as strings.
Wallet-bound plan
Use mode: "wallet_bound" and the actual signer-bound recipient. Request include.simulation: true. All other fields use the same raw units as the first example.
{
"mode": "wallet_bound",
"inputToken": "0x0000000000000000000000000000000000000000",
"outputToken": "0x5ef79995fe8a89e0812330e4378eb2660cede699",
"amountIn": "1000000000000000000",
"recipient": "0x1111111111111111111111111111111111111111",
"slippageBps": 100,
"include": { "simulation": true }
}
The recipient above is an example address. Replace it before using the plan. For a different final recipient, use transfer plans instead of changing a signer-bound local swap.
Correct a failed request
amountIn: "1.5" is invalid here. To request 1.5 VET, use amountIn: "1500000000000000000". For wallet_bound, a missing recipient is also invalid: add the actual wallet address, or use quote_only when no clauses are needed.
Inspect error.details.issues and error.action; correct the named fields before retrying. UPSTREAM_QUOTE_API_ERROR can indicate quote-service or route failure. Recheck pair and amount, then retry after the underlying issue changes. Error reference.
Review and settlement
Check minimum received, route, permissions, costs and freshness before local signing. Simulation is a preflight result. Only the receipt and actual settlement establish the outcome; failure may consume gas. Continue with MCP, the skill or SDK.