> ## Documentation Index
> Fetch the complete documentation index at: https://xoxno.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Quote endpoint — forward (given input, maximize output) or reverse
(given target output, minimize input).

> When `sender` is set, the response gains a ready-to-sign `transaction`
XDR envelope. With `simulate=true` (the default whenever `sender` is set)
the envelope is server-side-prepared (resource-fee + footprint attached).

`platform=all` runs both Aggregator (Soroban) and SDEX (Classic) and
returns the winning quote only; fire a second request with an explicit
`platform` to fetch the loser if you need a side-by-side comparison.



## OpenAPI

````yaml /api-reference/stellar-aggregator-openapi.json get /api/v1/quote
openapi: 3.1.0
info:
  title: Stellar DEX Aggregator API
  description: >-
    DEX aggregator and routing engine for Stellar across Soroswap, Aquarius,
    Phoenix, native AMM (SDEX), and static SAC bridges.


    ## Features

    - Forward & reverse quotes (given input → output, or target output → input)

    - Multi-path split allocation across pools to minimize price impact

    - Optional server-side simulated `TransactionEnvelope` ready to sign

    - Run-and-pick across Aggregator (Soroban) and SDEX (Classic) planes

    - Auto-fallback when the on-chain budget rejects the requested config

    - Echoes USD value + protocol/referral fees
  license:
    name: MIT
    identifier: MIT
  version: 0.1.0
servers: []
security: []
tags:
  - name: Health
    description: Liveness and readiness probes
  - name: Tokens
    description: Routable token catalog
  - name: Quote
    description: Routing, quoting, and transaction-envelope construction
paths:
  /api/v1/quote:
    get:
      tags:
        - Quote
      summary: |-
        Quote endpoint — forward (given input, maximize output) or reverse
        (given target output, minimize input).
      description: >-
        When `sender` is set, the response gains a ready-to-sign `transaction`

        XDR envelope. With `simulate=true` (the default whenever `sender` is
        set)

        the envelope is server-side-prepared (resource-fee + footprint
        attached).


        `platform=all` runs both Aggregator (Soroban) and SDEX (Classic) and

        returns the winning quote only; fire a second request with an explicit

        `platform` to fetch the loser if you need a side-by-side comparison.
      operationId: quote_handler
      parameters:
        - name: from
          in: query
          description: |-
            Token identifier. Accepts:
            - `XLM` / `native`: native lumen
            - `CODE`: first Classic asset matching code (e.g. "USDC")
            - `CODE-SAC`: Soroban Asset Contract form of that Classic asset
            - `XLM-SAC`: the SAC form of native XLM
            - `CODE:GISSUER...`: exact Classic in SEP-11 form
            - `C...` (56 chars): Soroban contract address directly
          required: true
          schema:
            type: string
        - name: to
          in: query
          required: true
          schema:
            type: string
        - name: amount_in
          in: query
          description: |-
            Forward mode: input amount in RAW atomic units.
            Exactly ONE of `amount_in` or `amount_out` must be set.
            Accepts `amount_in` (snake) or `amountIn` (camel).
          required: false
          schema:
            type: string
        - name: amount_out
          in: query
          description: |-
            Reverse mode: target output in RAW atomic units. The server computes
            the minimum `amount_in` that delivers at least this output.
            Accepts `amount_out` or `amountOut`.
          required: false
          schema:
            type: string
        - name: max_hops
          in: query
          required: false
          schema:
            type: integer
            minimum: 0
        - name: max_splits
          in: query
          description: |-
            Split across up to N paths to reduce slippage. Default 1 (no split).
            Only applies to forward quotes; reverse quotes use one path.
          required: false
          schema:
            type: integer
            minimum: 0
        - name: slippage
          in: query
          description: |-
            Slippage tolerance as decimal (e.g. `0.01` = 1%). When set, response
            populates `amountOutMin` at `amountOut * (1 - slippage)`.
          required: false
          schema:
            type: number
            format: double
        - name: includePaths
          in: query
          description: |-
            Opt in to the per-path breakdown (`paths[]`). Defaults to `false` —
            the response then carries totals + the prepared transaction only,
            even when the allocator splits across multiple pools. Set to `true`
            for debugging, analytics, or UIs that render the route.
          required: false
          schema:
            type: boolean
        - name: sender
          in: query
          description: |-
            Sender G-strkey. When combined with `router`, the response gains
            `transaction` with a ready-to-sign envelope.
          required: false
          schema:
            type: string
        - name: router
          in: query
          description: >-
            Router Soroban contract C-strkey. Optional — defaults to the

            configured network router (see
            `Network::default_aggregator_router`).

            Override only when targeting a non-standard router (tests, preview).
          required: false
          schema:
            type: string
        - name: referral_id
          in: query
          description: |-
            Referral identifier for fee attribution. `0` (default) means "no
            referral / no fee". Non-zero IDs MUST be registered on-chain via
            `add_referral` or the contract reverts at execution. Threaded into
            the BatchSwap so the on-chain router can charge admin + referral
            fees and accumulate them per (referral_id, token).
          required: false
          schema:
            type: integer
            format: int64
            minimum: 0
        - name: simulate
          in: query
          description: |-
            When `true` (default whenever `sender` is set), the server runs
            `simulateTransaction` itself and returns a *prepared* envelope —
            Soroban data + resource-fee already attached, total fee accurate.
            The caller skips its own simulate call before signing.

            Set `simulate=false` to opt out (faster — saves ~150-300 ms per
            quote — at the cost of the caller running its own simulate
            before signing). When the simulator reports an error (typically
            `Budget, ExceededLimit` for over-large multi-path routes), the
            server returns HTTP 422 with the error message so the caller
            can retry with smaller `maxSplits` / `maxHops`.
          required: false
          schema:
            type: boolean
        - name: platform
          in: query
          description: |-
            Execution-plane filter:
            - `aggregator` (default): Soroswap + Aquarius + Phoenix only.
              Produces a `batch_execute` envelope safe for composable
              contract-to-contract use (Lending → Aggregator).
            - `sdex`: Native AMM only. Today returns the route; envelope
              construction (Classic `PathPaymentStrictReceive`) lands in a
              follow-up.
            - `all`: runs both and returns whichever produces the larger
              `amountOut` (gas tiebreak: fewer hops). The losing platform's
              quote is dropped — fire a second request with the explicit
              `platform=...` if you need a side-by-side comparison.
          required: false
          schema:
            $ref: '#/components/schemas/Platform'
        - name: fresh
          in: query
          description: >-
            If `true`, compare the snapshot's `last_applied_ledger` against the

            network's live `getLatestLedger` before answering. If the snapshot
            is

            behind by more than `MAX_FRESH_LEDGER_DRIFT` ledgers, return HTTP
            409

            (Conflict) with a `snapshot_stale` body so the caller can retry.

            Costs one extra RPC round-trip; off by default.
          required: false
          schema:
            type: boolean
      responses:
        '200':
          description: Route plus optional prepared transaction envelope
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/QuoteResponse'
        '400':
          description: >-
            Bad request — unknown token, invalid amount, or both/neither amount
            given
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Router found no path between tokens for the requested platform
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: >-
            Snapshot stale beyond `MAX_FRESH_LEDGER_DRIFT` (only when
            `?fresh=true`)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: >-
            Server-side simulator rejected the envelope (e.g. budget exceeded).
            Lower `maxSplits` / `maxHops` and retry.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '502':
          description: Upstream RPC unreachable while preparing the transaction
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
components:
  schemas:
    Platform:
      type: string
      description: |-
        Which execution plane the quote should be valid for.

        Stellar has two execution planes: Soroban (smart contracts) and Classic
        (SDEX orderbook + native AMM pools). A composable contract chain
        (Lending → Aggregator) MUST stay in Soroban — Classic ops can only be
        composed at envelope-construction time, before the user signs. A
        direct-user flow can use either plane independently.

        `Platform` filters which `PoolSource` values beam search is allowed to
        expand. The default is `Aggregator` (Soroban-only) — that's the safer
        choice because every existing caller of the routing engine pre-platform
        expected Soroban results.
      enum:
        - aggregator
        - sdex
        - all
    QuoteResponse:
      type: object
      description: |-
        Top-level response carrying the route, prepared envelope (when
        `sender` is set), and enrichment fields (USD value, fee breakdown,
        auto-fallback metadata).
      required:
        - mode
        - from
        - tokenInKind
        - to
        - tokenOutKind
        - amountIn
        - amountOut
        - amountInShort
        - amountOutShort
        - rate
        - rateInverse
        - decimalsIn
        - decimalsOut
        - platform
      properties:
        amountIn:
          type: string
          description: Raw u128 string (precision-safe).
        amountInShort:
          type: number
          format: double
          description: f64 convenience for display (loses precision above 2^53).
        amountInUsd:
          type:
            - number
            - 'null'
          format: double
          description: |-
            USD value of `amountIn` (None when the input token has no
            Reflector oracle price on the active network).
        amountOut:
          type: string
        amountOutMin:
          type:
            - string
            - 'null'
          description: |-
            Minimum output after slippage (raw u128 string). Set only when
            `slippage` was provided in the request.
        amountOutMinShort:
          type:
            - number
            - 'null'
          format: double
        amountOutMinUsd:
          type:
            - number
            - 'null'
          format: double
          description: |-
            USD value of `amountOutMin` (only set when `slippage` was set on
            the request and the output token has a price).
        amountOutShort:
          type: number
          format: double
        amountOutUsd:
          type:
            - number
            - 'null'
          format: double
          description: USD value of `amountOut`.
        decimalsIn:
          type: integer
          format: int32
          minimum: 0
        decimalsOut:
          type: integer
          format: int32
          minimum: 0
        degraded:
          oneOf:
            - type: 'null'
            - $ref: '#/components/schemas/DegradedQuote'
              description: |-
                Set when the server-side simulator rejected the requested
                configuration (typically `Budget, ExceededLimit`) and the
                quote was re-built with reduced `maxSplits` / `maxHops` to fit.
                Carries the actual config used so the caller can surface a
                "we routed through fewer paths" hint to the user.
        feeAmount:
          type:
            - string
            - 'null'
          description: |-
            Fee amount the router will deduct (raw u128 string). Charged on
            the input token when `feeOnInput=true`, otherwise on the output
            token. The `amountOut` / `amountOutMin` already reflect this
            deduction.
        feeAmountShort:
          type:
            - number
            - 'null'
          format: double
          description: |-
            Display value of `feeAmount` (decimals applied; loses precision
            above 2^53).
        feeBps:
          type:
            - integer
            - 'null'
          format: int32
          description: |-
            Combined static + referral fee in basis points (1 bps = 0.01%) the
            router will charge for this swap. `None` when `referralId == 0`
            or unknown — no referral means no fee.
          minimum: 0
        feeOnInput:
          type:
            - boolean
            - 'null'
          description: |-
            `true` if the fee is charged on the input token, `false` if
            charged on the output. Mirrors the contract's
            `fee_on_input = !out_whitelisted || in_whitelisted` rule. Absent
            when no fee applies.
        from:
          type: string
          description: |-
            Canonical token identifier. "XLM" for native, "C..." for Soroban
            contracts, "CODE:GISSUER..." for Classic.
        mode:
          type: string
          description: '`"forward"` or `"reverse"`.'
          example: forward
        paths:
          type:
            - array
            - 'null'
          items:
            $ref: '#/components/schemas/QuotePathResponse'
          description: >-
            Per-path breakdown. Populated when the request includes

            `includePaths=true`. Even on multi-path splits the field stays
            absent

            by default — clients that need the route detail must opt in.
        platform:
          $ref: '#/components/schemas/Platform'
          description: >-
            Which execution plane produced this quote. `"aggregator"` for

            Soroban-side routes (Soroswap / Aquarius / Phoenix / static
            bridges);

            `"sdex"` for Classic-plane routes (native AMM pools). The API
            returns

            this discriminator so consumers can branch on envelope type without

            inspecting the routes.
        priceImpact:
          type:
            - number
            - 'null'
          format: double
          description: |-
            Price impact as decimal: `1 - executed_rate / spot_rate`.
            `0.0` = no slippage, `0.02` = 2% below mid-market. Absent when
            spot rate can't be determined (no alternative depth info).
        rate:
          type: number
          format: double
          description: Output per 1 unit of input (f64).
        rateInverse:
          type: number
          format: double
          description: Input per 1 unit of output.
        slippage:
          type:
            - number
            - 'null'
          format: double
          description: Slippage tolerance as decimal (echoed from request).
        to:
          type: string
        tokenInKind:
          type: string
          description: |-
            Stellar extension: which token family was resolved. One of
            `"native" | "classic" | "soroban"`. Matches the `kind` field in
            `/api/v1/tokens`.
        tokenOutKind:
          type: string
        transaction:
          oneOf:
            - type: 'null'
            - $ref: '#/components/schemas/TransactionPayload'
              description: >-
                Unsigned transaction envelope ready for the sender to sign +
                submit.

                Populated when the request included both `sender=G...` and

                `router=C...` query params. The envelope calls

                `router.batch_execute(batch)` where `batch` is derived from the

                paths in this response. Caller MUST:
                  1. Populate `seqNum` from the sender's account state.
                  2. Run `simulateTransaction` to attach Soroban resource fees.
                  3. Sign with sender's Ed25519 key.
                  4. Submit via `sendTransaction`.
    ErrorResponse:
      type: object
      required:
        - error
      properties:
        error:
          type: string
          description: >-
            Human-readable error message. Match against the substrings
            documented

            in the error reference (e.g. `"unknown token_in"`, `"no path"`).
          example: unknown token_in 'INVALID-123'
    DegradedQuote:
      type: object
      description: |-
        Detail of a server-side fallback when the original quote config
        exceeded the Soroban transaction budget.
      required:
        - requestedMaxSplits
        - effectiveMaxSplits
        - requestedMaxHops
        - effectiveMaxHops
        - fallbackAttempts
        - reason
      properties:
        effectiveMaxHops:
          type: integer
          description: '`maxHops` the returned quote actually uses.'
          minimum: 0
        effectiveMaxSplits:
          type: integer
          description: '`maxSplits` the returned quote actually uses.'
          minimum: 0
        fallbackAttempts:
          type: integer
          description: |-
            How many simulation attempts the server made before settling on
            this configuration (1 = first try succeeded after the first
            fallback step, etc.).
          minimum: 0
        reason:
          type: string
          description: Short human-readable reason ("budget_exceeded").
        requestedMaxHops:
          type: integer
          description: '`maxHops` the request asked for.'
          minimum: 0
        requestedMaxSplits:
          type: integer
          description: '`maxSplits` the request asked for.'
          minimum: 0
    QuotePathResponse:
      type: object
      description: Single path in a split-aware quote.
      required:
        - amountIn
        - amountOut
        - amountInShort
        - amountOutShort
        - splitPpm
        - swaps
      properties:
        amountIn:
          type: string
        amountInShort:
          type: number
          format: double
        amountOut:
          type: string
        amountOutShort:
          type: number
          format: double
        splitPpm:
          type: integer
          format: int32
          description: |-
            Parts-per-million of total input routed through this path.
            1_000_000 = 100%.
          minimum: 0
        swaps:
          type: array
          items:
            $ref: '#/components/schemas/QuoteSwapResponse'
    TransactionPayload:
      type: object
      description: |-
        Stellar-specific envelope payload. The envelope XDR carries all
        transaction data; the other fields are convenience echoes for
        clients that don't decode XDR.

        When the server-side simulator runs (default whenever `sender` is
        provided), `envelope_xdr` is the *prepared* envelope — Soroban
        transaction data + resource-fee-adjusted `tx.fee` already attached.
        The caller only needs to set the sequence number and sign before
        submitting via `sendTransaction`. `simulated` indicates which mode
        the response is in.
      required:
        - envelopeXdr
        - sourceAccount
        - routerContract
        - baseFee
        - networkPassphrase
        - notes
      properties:
        baseFee:
          type: integer
          format: int64
          description: |-
            Total fee in stroops. When `simulated=true` this equals
            `base_fee + min_resource_fee`. Otherwise it's the placeholder
            `base_fee` and the caller must run `simulateTransaction` to get
            the real number.
          minimum: 0
        envelopeXdr:
          type: string
          description: Base64 XDR of the (optionally prepared) `TransactionEnvelope`.
        latestLedger:
          type:
            - integer
            - 'null'
          format: int32
          description: |-
            Live ledger the simulation observed. Lets callers detect a
            stale snapshot if their own `getLatestLedger` shows newer data.
          minimum: 0
        minResourceFee:
          type:
            - string
            - 'null'
          description: |-
            Soroban resource-fee component when `simulated=true` (stroops,
            stringified to preserve precision in JS clients).
        networkPassphrase:
          type: string
          description: Network passphrase the envelope is tied to.
        notes:
          type: string
          description: Human-readable pre-signing checklist.
        routerContract:
          type: string
          description: Router contract C-strkey (echo of request).
        simulated:
          type: boolean
          description: |-
            `true` when the envelope was simulated server-side and the
            resource-fee + footprint were stitched onto the envelope. The
            caller can skip its own `simulateTransaction` call in that case.
        sourceAccount:
          type: string
          description: Sender G-strkey (echo of request).
    QuoteSwapResponse:
      type: object
      description: |-
        Single swap / hop on a route. Carries the source DEX, pool shape,
        canonical pool address, fee, and per-token kinds (`native`, `classic`,
        `soroban`) for the input and output sides.
      required:
        - dex
        - kind
        - address
        - feeBps
        - from
        - tokenInKind
        - to
        - tokenOutKind
        - amountIn
        - amountOut
        - amountInShort
        - amountOutShort
      properties:
        address:
          type: string
          description: |-
            Canonical pool identifier. For Soroban pools the C-strkey; for
            native AMM pools the L-strkey.
        amountIn:
          type: string
        amountInShort:
          type: number
          format: double
        amountOut:
          type: string
        amountOutShort:
          type: number
          format: double
        dex:
          type: string
          description: 'DEX source: `"NativeAmm" | "Soroswap" | "Aquarius" | "Phoenix"`.'
        feeBps:
          type: integer
          format: int32
          minimum: 0
        from:
          type: string
        kind:
          type: string
          description: 'Pool shape: `"ConstantProduct" | "Stable" | "Static"`.'
        to:
          type: string
        tokenInKind:
          type: string
        tokenOutKind:
          type: string

````