Docs menu

POST /api/execute/plan

Turns a chosen venue quote into a one-call execution plan your wallet runs: typed steps, the price floor computed server-side, an expiry, and an idempotency key. Keyed only.

Request body

fieldrequiredmeaning
from, to, amountyesSame semantics as the quote endpoint.
venueIdyesThe venue you chose from the quote. v1 supports tradecraft.
takerPartyyesThe user's Canton party (hint::1220 plus 64 hex chars). Format-checked; no ledger lookup.
idempotencyKeyyesYour unique id for this plan request. Retries with the same key return the same plan.
slippageBpsnoInteger 0 to 5000; default 50. Sets the floor: minReceive = net x (1 - slippageBps/10000), floored.
feeBps + feeRecipientno, togetherYour fee, same rules and cap as the quote endpoint.

The plan shape

  • quoteRef pins the quote. Venue, pair, amount, net, minReceive, the full clientFees block, and appliedSchema. The numbers are computed by the same code path as the keyed quote, so what was quoted is what the plan carries.
  • steps are what your wallet performs. A venue-deposit step (target pool party, instrument, amount, and the memo carrying the price floor) and, when fees are collected, two independent fee-transfer legs: Synfin's flat service fee to the Synfin fee party, and your full integrator fee to your own recipient.
  • The memo floor is computed server-side. Your integration never hand-builds the venue's memo grammar; a malformed floor is how users lose money, so the server owns it.
  • expiry: a plan is valid for 90 seconds. After that, re-plan; prices have moved and the floor must be recomputed.
  • trustWindow states plainly what the user trusts and for how long during execution.

Example

curl -X POST "https://synfin.xyz/api/execute/plan" \
  -H "content-type: application/json" \
  -H "x-api-key: sk_live_..." \
  -d '{
    "from": "CC", "to": "USDCx", "amount": "250",
    "venueId": "tradecraft",
    "takerParty": "YOUR_USERS_PARTY",
    "feeBps": 30, "feeRecipient": "YOUR_PARTY",
    "slippageBps": 50,
    "idempotencyKey": "your-unique-id"
  }'
{
  "planId": "pl_6833f8c8-9d67-4796-95bb-281c9033915d",
  "quoteRef": {
    "venueId": "tradecraft",
    "pair": {
      "give": "Amulet",
      "want": "USDCx"
    },
    "amount": "250",
    "asOf": "2026-07-03T16:28:38.523Z",
    "net": "34.517390",
    "minReceive": "34.344803",
    "clientFees": {
      "integrator": {
        "bps": 10,
        "amount": "0.034518",
        "recipient": "demo-wallet::1220aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      "service": {
        "bps": 10,
        "amount": "0.034518"
      },
      "userReceives": "34.448354"
    },
    "appliedSchema": {
      "schemaId": "fs-2026-07-24",
      "overrideId": null
    }
  },
  "mode": "managed-deposit",
  "steps": [
    {
      "kind": "venue-deposit",
      "target": "tc-swp_CC-USDCx::122096fe076cc065af0cb38f94caa60e8ddfecbe8f0cfe10655ae7aa06fab99c66b7",
      "instrument": "Amulet",
      "amount": "250",
      "memo": "34.344803",
      "expiresAt": "2026-07-03T16:30:08.523Z"
    },
    {
      "kind": "fee-transfer",
      "target": "synfin-fees::1220...",
      "instrument": "USDCx",
      "amount": "0.034518",
      "beneficiary": "synfin",
      "expiresAt": "2026-07-03T16:30:08.523Z"
    },
    {
      "kind": "fee-transfer",
      "target": "demo-wallet::1220aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
      "instrument": "USDCx",
      "amount": "0.034518",
      "beneficiary": "integrator",
      "expiresAt": "2026-07-03T16:30:08.523Z"
    }
  ],
  "expiry": "2026-07-03T16:30:08.523Z",
  "idempotencyKey": "docs-demo-1",
  "trustWindow": "Funds are locked in a CIP-56 transfer proposal that stays in the taker wallet until the venue accepts, and auto-expire back on non-accept.",
  "collectsFees": true
}

Captured 2026-07-03T16:28Z from this release against live venue quotes.

Note steps[0].memo equals quoteRef.minReceive: the floor travels to the venue in the deposit memo, and Tradecraft enforces it at execution (see the per-venue guarantee table).

Idempotency

The same idempotencyKey from the same key returns the same plan (same planId, same numbers, same expiry) for 5 minutes. Safe to retry on timeouts; a retry never produces a second plan your wallet could accidentally execute twice.

Errors

statuscodemeaning
400missing_keyExecution planning requires an API key.
400bad_requestMissing or malformed fields (body must be JSON; required fields listed in the error).
400unknown_tokenUnknown from or to symbol.
400bad_partytakerParty is not a Canton party id.
400bad_fee_paramsFee params incomplete, over the cap, or a bad recipient.
400no_poolThe venue has no pool for this pair.
409venue_unavailableThe chosen venue is not quoting this pair right now. Re-quote and choose again.
502quote_failedThe upstream quote could not be built. Retry.
500internalUnexpected failure; nothing was created.

Example bodies and the shared 401/429/503 taxonomy are on the errors page.

The v1 boundary, honestly

  • Execution and tracking are client-side in v1. Your wallet performs the steps and observes its own ledger view to a terminal state; the SDK execution client drives both. Synfin holds no funds and no keys at any point.
  • A server-side status endpoint is planned. It will report the same lifecycle states the SDK derives today, so an integration written against the SDK states needs no rework.
  • Fee collection is flag-gated off. The plan discloses and pins the fee lines; when FEE_COLLECTION_ENABLED flips, it emits the two fee-transfer legs riding one atomic CIP-0112 batch with the deposit. Real on-ledger collection additionally needs a batch-capable wallet (Loop degrades fee-less today). Quotes and the disclosed fee lines are live now.
  • v1 venue support is Tradecraft, the venue whose swap execution is proven on the venue side by a mainnet canary and end to end on devnet. More venues follow as their capabilities are verified.