Skip to Content
APIQuote

Quote

POST /api/external/intents/quote

Creates an intent session from the user’s selection and returns a routed, economically normalized plan. The request body is the user’s intent; there is no intentSessionId before this call — Sweep creates it in the response.

All identifiers and amounts in the examples are synthetic except documented chain and token identifiers.

Request

A user swept 10 USDC on Base into USDC on Arbitrum:

{ "walletAddress": "0x1111111111111111111111111111111111111111", "recipient": "0x1111111111111111111111111111111111111111", "sources": [ { "chainId": 8453, "address": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913", "amount": "10000000", "symbol": "USDC", "decimals": 6, "usdValue": "10.00" } ], "target": { "chainId": 42161, "address": "0xaf88d065e77c8cc2239327c5edb3a432268e5831", "targetToken": { "address": "0xaf88d065e77c8cc2239327c5edb3a432268e5831", "symbol": "USDC", "decimals": 6, "priceUSD": "1" } }, "slippageBps": 50, "allowPartial": false }
FieldRequiredMeaning
walletAddressYesEVM account that signs and executes the prepared source-chain actions
recipientYesWallet that receives the target token
sources[]Yes1–50 selected assets; see field rules below
targetYesDestination chain and token; target.address and target.targetToken.address must match
slippageBpsNoInteger 1–10,000; omission means 50. 0 is rejected — omit the field instead
allowPartialNoWhether Sweep may return a plan covering fewer than all selected sources
allowedProvidersNoPer-request provider subset; omit the field to allow all providers. See Providers
maxWaitMsNoQuote time budget, 1,000–18,000 ms
feeBpsNoInteger integrator markup; must not exceed the partner-configured cap (300 bps default)
callbackUrlNoHTTP(S) webhook URL; must equal the partner-configured callback URL

Source field rules:

  • amount is a positive base-10 raw-unit string. Convert display amounts without floating-point arithmetic: 0.1 USDC with 6 decimals is "100000".
  • decimals is required for every source.
  • Token identity is the canonical pair (chainId, address); duplicates are rejected. Use the zero address for a native asset.
  • usdValue is optional for a single-target intent, required positive for multi-target intents.
  • Exactly one of target or targets[] is present.

Providers

allowedProviders accepts the names relay, across, mayan, symbiosis and fly. There is no all value: omitting the field means all providers enabled for the partner.

  • Only relay, across and mayan can currently produce an external route: symbiosis is excluded from external traffic and fly is not evaluated by external route selection.
  • [] is valid and intentionally produces no provider candidates; an allowlist containing only excluded names behaves like [].
  • The partner-level provider policy configured during onboarding still applies on top of any per-request subset.

Response

{ "apiVersion": "1", "quoteStatus": "ready", "intentSessionId": "intent_00000000000000000001", "expiresAt": "2030-01-01T00:00:30.000Z", "coverageMode": "full", "selectedPlan": { "isFullPlan": true, "coveredSliceCount": 1, "totalSliceCount": 1, "providerSet": ["relay"], "legs": [ { "outputTargetId": "output-1", "routeId": "relay", "provider": "relay", "coveredSliceCount": 1, "totalSliceCount": 1, "requiredQuoteCount": 1, "noopSliceCount": 0, "isFullRoute": true, "summary": { "expectedOutputRaw": "9950000", "minimumOutputRaw": "9900000", "expectedOutputAmount": "9.95", "minimumOutputAmount": "9.90", "output": { "chainId": 42161, "tokenAddress": "0xaf88d065e77c8cc2239327c5edb3a432268e5831", "symbol": "USDC", "decimals": 6 }, "totalOutputUsd": 9.95, "totalFeeUsd": 0.03, "totalLossUsd": 0.05, "timeEstimate": 2 }, "failedSources": [] } ], "summary": { "totalOutputUsd": 9.95, "totalFeeUsd": 0.03, "totalLossUsd": 0.05, "timeEstimate": 2 } }, "failedSources": [] }

Responses can contain additional informational fields. Tolerate unknown fields and do not build logic on undocumented provider data.

Quote states

quoteStatusMeaningIntegrator behavior
readyA full executable plan is selectedShow preview; allow confirmation before expiresAt
partial_onlyOnly part of the requested sources can be coveredShow failedSources; require an explicit new partial quote if the product allows it
unavailableNo acceptable plan existsDo not call prepare; show failedSources
expiredReplay only: the stored quote’s expiresAt has passedDo not call prepare; start a new quote with a new idempotency key

coverageMode is full, partial or none.

Rendering economics

Render only Sweep-normalized fields from selectedPlan:

  • expected receive: legs[].summary.expectedOutputAmount
  • minimum receive: legs[].summary.minimumOutputAmount
  • output value: summary.totalOutputUsd
  • reported fees: summary.totalFeeUsd
  • total value loss: summary.totalLossUsd
  • ETA seconds: summary.timeEstimate

expectedOutputAmount is already the expected net receive — never subtract fee or loss from it again. Fee and loss are different concepts; do not add them together. Do not read provider-specific raw fields from routeEvaluations or rebuild provider economics.

Expiry and replay

expiresAt is the deadline for calling prepare. Do not prepare in the background while the user is still reviewing.

Retrying an ambiguous quote failure with the same idempotency key does not re-run routing: Sweep replays the stored session with its current status. A replay landing after expiresAt returns quoteStatus: "expired" with the original intentSessionId. Treat it as dead: prepare would fail with 409 intent_session_expired; build a new intent with a new idempotency key — reusing the expired key replays the same dead session forever.

Code

import crypto from 'node:crypto'; import { SweepExternalClient } from 'sweep-external-sdk/server'; const client = new SweepExternalClient({ sweepApiUrl: process.env.SWEEP_API_URL, partnerId: process.env.SWEEP_PARTNER_ID, secret: process.env.SWEEP_PARTNER_SECRET, }); const idempotencyKey = `quote-${crypto.randomUUID()}`; // Persist { idempotencyKey, body } BEFORE transmission. const result = await client.quote(quoteRequestBody, idempotencyKey); if (result.reachable && result.ok && result.response.quoteStatus === 'ready') { // Persist intentSessionId and expiresAt; render selectedPlan. }

Validation and route failure codes: Errors.

Last updated on