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

> ## Agent Instructions
> Soroswap Aggregator API: base URL https://api.soroswap.finance, pick the network with ?network=mainnet or ?network=testnet (omitted means mainnet).
> Auth: Authorization: Bearer sk_... on every call. Get a key at https://api.soroswap.finance/register. A missing or invalid key returns 403, never 401. Free plan 1 request/s; see https://docs.soroswap.finance/api/plans.md.
> Flow: POST /quote (assetIn, assetOut as C... contract addresses, amount as an integer string in the smallest unit, tradeType, protocols) -> POST /quote/build with the unchanged quote and from -> the user signs the XDR -> POST /send. Build right after quoting: a Soroban XDR is valid for 180 seconds. Submit it as is, never re-simulate it.
> AI tools: docs MCP at https://docs.soroswap.finance/mcp (claude mcp add --transport http soroswap-docs https://docs.soroswap.finance/mcp), skill with npx skills add https://docs.soroswap.finance. Other clients: https://docs.soroswap.finance/api/build-with-ai.md.
> Units and Stellar pitfalls: https://docs.soroswap.finance/api/stellar-gotchas.md. Errors and retries: https://docs.soroswap.finance/api/errors.md.

# Stellar gotchas for integrators

> Amounts are integer strings in stroops (7 decimals for XLM, USDC, EURC, AQUA), assets are C... contract addresses, a built Soroban XDR expires in 180 s, and a missing trustline is a 428.

The things that trip up a first integration on Stellar, in one place. Each one was checked
against the live API on mainnet.

## Amounts are integer strings in the smallest unit

Every `amount` you send to the API is an integer number of the asset's smallest unit (stroops
for XLM), written as a string: `"100000000"` is 10 XLM. Convert from what the user types with
`raw = human × 10^decimals`, using integer or decimal arithmetic, never floats.

| Asset | Decimals | 1 unit in the API | Mainnet contract |
| - | - | - | - |
| XLM | 7 | `"10000000"` | `CAS3J7GYLGXMF6TDJBBYYSE3HQ6BBSMLNUQ34T6TZMYMW2EVH34XOWMA` |
| USDC | 7 | `"10000000"` | `CCW67TSZV3SSS2HXMBQ5JFGCKJNXKZM7UQUWUZPUTHXSTZLEO7SJMI75` |
| EURC | 7 | `"10000000"` | `CDTKPWPLOURQA2SGTKTUQOWRCBZEORB4BWBOMJ3D3ZTQQSGE5F6JBQLV` |
| AQUA | 7 | `"10000000"` | `CAUIKL3IYGMERDRUN6YSCLWVAKIFG5Q4YJHUKM4S4NJZQIA3BAS6OJPK` |

Every classic Stellar asset has 7 decimals. Tokens issued directly on Soroban choose their own:
the Soroswap token list has some with 18. Read `decimals` from
`GET /asset-list?name=SOROSWAP` or from the token contract's `decimals()` instead of assuming 7.

* `"0"` and negative amounts return **400** `amount must be a positive integer number of stroops`.
* An amount with a decimal point (`"10.5"`) currently returns **500**, not 400. Never send one.
* A JSON number is accepted, but a string is the only form that survives values above 2^53 in
  JavaScript.

Not every field is in stroops. Read the unit of each response field before you display it:

| Field | Unit |
| - | - |
| `amountIn`, `amountOut`, `otherAmountThreshold` on any quote | Smallest unit, integer string |
| `rawTrade` of an `aggregator` or `router` quote | Smallest unit |
| `rawTrade` of an `sdex` quote (`source_amount`, `destination_amount`...) | Decimal units with 7 places, for example `"0.5000000"` |
| `GET /balances/{address}`: `amount` and `available` | Decimal units |
| `GET /balances/{address}`: `rawAmount` and `availableRaw` | Smallest unit |
| `GET /price`: `price` | A number in the reference currency (USD by default) |

## Assets are contract addresses

The API identifies every asset, classic or Soroban, by its contract address (`C...`). The
`CODE:ISSUER` form and the issuer's `G...` address are not accepted:

* `"assetIn": "USDC:GA5Z..."` returns **400** `Invalid Stellar address`.
* `"assetIn": "GA5Z..."` (the issuer) returns **503** `Routing unavailable`, which reads like an
  outage but is a wrong input. Check your addresses before you retry it.

A classic asset reaches Soroban through its **Stellar Asset Contract (SAC)**, whose address is
derived from the asset and the network. To get it:

```bash theme={null}
stellar contract id asset --asset USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN --network mainnet
# CCW67TSZV3SSS2HXMBQ5JFGCKJNXKZM7UQUWUZPUTHXSTZLEO7SJMI75
```

```javascript theme={null}
import { Asset, Networks } from '@stellar/stellar-sdk'
new Asset('USDC', 'GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN').contractId(Networks.PUBLIC)
Asset.native().contractId(Networks.PUBLIC) // XLM: CAS3J7GY...OWMA
```

The same asset has a different contract address on testnet: XLM is
`CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC` there. Never reuse mainnet addresses
with `?network=testnet`.

Accounts are a separate question. `from` and `to` are the wallets that sign and receive: a
classic account (`G...`) or a contract account such as a smart wallet (`C...`). See
[Smart wallets](#smart-wallets-and-passkeys) below.

## A missing trustline

A classic account cannot receive a classic asset (anything but XLM) until it has a trustline for
it. Tokens issued on Soroban need no trustline. When the receiving account lacks one:

* `POST /quote/build` returns **428** with `action: "CREATE_TRUSTLINE"`. `actionData.xdr` is a
  `changeTrust` transaction for `actionData.signer`, with `assetCode` and `assetIssuer`. Sign it,
  submit it, then call `/quote/build` again with the same quote (quote again if it is no longer
  fresh).
* `POST /swap` returns **200** with the same `action` instead of an error, so one call carries
  the prerequisite. With `inlinePrerequisites: true` it also returns the swap XDR, with the
  trustline transaction in a `prerequisites` array to submit first.
* On a mixed-venue route the missing trustline can be for an intermediate asset, not the output
  one. Read `assetCode` from the response instead of assuming it is `assetOut`.
* A user with no XLM to pay for the trustline can still receive the asset in one swap through
  [Gasless Trustline](https://docs.soroswap.finance/api/gasless-trustline.md), on SDEX routes, with a sponsor account you control.

## The XLM reserve

Each account keeps a minimum XLM balance it cannot spend: 0.5 XLM per base reserve, two for the
account itself plus one per trustline, offer or other subentry. Opening a trustline locks another
0.5 XLM. A user who swaps "all" their XLM fails: size XLM swaps from what is spendable.
`GET /balances/{address}` returns it as `available` / `availableRaw`, already net of the
reserve.

## A built transaction expires

The XDR from `/quote/build` and `/swap` has a time bound. Past it, the network rejects the
transaction with `tx_too_late`.

| Route (`platform`) | Valid for |
| - | - |
| `aggregator`, `router` (Soroban) | 180 seconds from the build |
| `sdex` | About an hour |

The quote inside it ages faster than that: prices move and the minimum output was set at quote
time. If the user has not signed within the window, quote and build again. Never re-sign or
resubmit an expired XDR, and never edit its time bounds.

## Simulation: the API has done it

A Soroban transaction must be simulated before it is signed, to get its footprint, resource fee
and authorization entries. `/quote/build` already did that and sized the result for the route.
Sign it and send it as is. Do not run `prepareTransaction` or another simulation on it:
simulating again drops the margins the API adds for Aquarius and SushiSwap routes, and the swap
can fail on chain.

The one exception is a swap XDR that comes back with `simulated: false` from `/swap` with
`inlinePrerequisites: true`. It could not be simulated because its prerequisites did not exist
yet: submit them first, then simulate and assemble it yourself.

A simulation that fails while building returns **400** `Simulation Failed` with a contract
`errorCode`, for example `10` (`TokenError.InsufficientBalance`): the account cannot cover the
amount. Fix the account state and build again.

## Signing: envelope, not auth entries

The XDR the API builds uses the `from` account as transaction source, and its Soroban
authorization is source-account credentials. A classic wallet (Freighter, Stellar Wallets Kit, a
keypair on your server) signs the transaction envelope once and that covers everything. You do
not sign authorization entries separately. The one exception to a single signer is
[Gasless Trustline](https://docs.soroswap.finance/api/gasless-trustline.md), where the sponsor signs too.

## Smart wallets and passkeys

A contract account (`C...`), such as a passkey smart wallet, cannot be the source of a
transaction, so it cannot use `/quote/build`: a `C...` in `from` returns **400**
`invalid version byte. expected 48, got 16`. Smart wallets take the
[contract path](https://docs.soroswap.finance/aggregator/technical-reference/cross-contract-integration.md):

1. Quote with `/quote`, leaving `sdex` out of `protocols` (the classic DEX cannot pay a
   contract; `/swap` drops it for you and returns 400 if it was the only venue).
2. Build the aggregator `swap_exact_in` / `swap_exact_out` invocation yourself from
   `rawTrade.distribution`.
3. Simulate it with a fee-paying `G...` account as source, have the smart wallet sign the
   **authorization entry** for its own address, then assemble and submit.

Soroban routes deliver to the signing account only: on `/quote/build`, `to` must equal `from` or
be left out.

## Testnet is a different world

`?network=testnet` uses the same base URL and key. Testnet quotes come only from Soroswap pools,
carry no fee and use testnet contract addresses. Fund an account with Friendbot and get test
tokens at [faucet.soroswap.finance](https://faucet.soroswap.finance). Leaving out `?network`
quotes **mainnet**, so always send it.

## Responses you might misread

* A successful `POST` returns **201**, not 200. Treat any 2xx as success.
* A missing or invalid key returns **403** `Forbidden resource`, never 401. See [Errors](https://docs.soroswap.finance/api/errors.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.