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

# Trade Endpoints

> Buy, buy exact out, and sell on behalf of a connected user, on the Trench bonding curve or the Raydium pool the token migrated to.

Three endpoints share a single shape. Each one resolves the token's current venue, builds a Solana transaction, signs it with the user's delegated wallet, and submits it.

|                |                                                                     |
| -------------- | ------------------------------------------------------------------- |
| **Scope**      | `trade:execute`                                                     |
| **Rate limit** | 60 / minute, within your [tier budget](/concepts/rate-limits)       |
| **Requires**   | `tradingEnabled: true` on [`GET /partner/v1/me`](/api-reference/me) |

All amounts are [integer strings in base units](/concepts/amounts).

## Shared Fields

Every trade takes the following fields.

| Field                 | Type             | Required | Constraint                                                         |
| --------------------- | ---------------- | -------- | ------------------------------------------------------------------ |
| `mint`                | string           | Yes      | Base58 Solana public key                                           |
| `priorityFeeLamports` | string           | Yes      | Integer, minimum `1000`                                            |
| `tipLamports`         | string           | Yes      | Integer, minimum `1000000`                                         |
| `slippageBps`         | number           | No       | Integer from 0 to 5000. Selects slippage mode                      |
| `partnerFeeBps`       | number or string | No       | Integer from 0 to 9999. See [Partner Fees](/concepts/partner-fees) |

## Request Modes

Each endpoint accepts two body shapes, chosen by whether `slippageBps` is present.

| Mode     | Trigger                   | Who computes the bound                  |
| -------- | ------------------------- | --------------------------------------- |
| Slippage | `slippageBps` in the body | Trench, from a quote taken at execution |
| Explicit | `slippageBps` absent      | You, as `minTokensOut` or `minSolOut`   |

The two are mutually exclusive. Sending `slippageBps` puts the request in slippage mode, and the explicit bound fields are then not read.

## Venue Routing

Tokens trade on the Trench bonding curve until they migrate to a Raydium pool. All three endpoints resolve the venue per request, so the same call works before and after migration.

| Lifecycle         | Behaviour                                        |
| ----------------- | ------------------------------------------------ |
| Bonding           | Executes against the Trench bonding curve        |
| Migration pending | Rejected with `409 migration_pending`, retryable |
| Migrated          | Executes against the Raydium pool                |

## POST /partner/v1/trades/buy

Spends up to a fixed amount of SOL.

| Field          | Required           | Constraint                            |
| -------------- | ------------------ | ------------------------------------- |
| `maxSolIn`     | Yes                | Integer string, greater than 0        |
| `minTokensOut` | Explicit mode only | Integer string, and `"0"` is accepted |

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST 'https://api.tren.ch/partner/v1/trades/buy' \
    -H "Authorization: Bearer $ACCESS_TOKEN" \
    -H 'Content-Type: application/json' \
    -d '{
      "mint": "…",
      "maxSolIn": "100000000",
      "slippageBps": 100,
      "priorityFeeLamports": "20000",
      "tipLamports": "1000000"
    }'
  ```

  ```javascript Node.js theme={null}
  const res = await fetch("https://api.tren.ch/partner/v1/trades/buy", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${accessToken}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      mint,
      maxSolIn: "100000000",
      slippageBps: 100,
      priorityFeeLamports: "20000",
      tipLamports: "1000000",
    }),
  });

  const { signature } = await res.json();
  ```

  ```python Python theme={null}
  import requests

  res = requests.post(
    "https://api.tren.ch/partner/v1/trades/buy",
    headers={"Authorization": f"Bearer {access_token}"},
    json={
      "mint": mint,
      "maxSolIn": "100000000",
      "slippageBps": 100,
      "priorityFeeLamports": "20000",
      "tipLamports": "1000000",
    },
  )

  signature = res.json()["signature"]
  ```
</CodeGroup>

The explicit form replaces `slippageBps` with a bound you calculated.

```json theme={null}
{
  "mint": "…",
  "maxSolIn": "100000000",
  "minTokensOut": "4950000000",
  "priorityFeeLamports": "20000",
  "tipLamports": "1000000"
}
```

## POST /partner/v1/trades/buy-exact-out

Buys an exact token amount while capping what you spend.

| Field       | Required           | Constraint                                                |
| ----------- | ------------------ | --------------------------------------------------------- |
| `tokensOut` | Yes                | Integer string, greater than 0                            |
| `maxSolIn`  | Explicit mode only | Integer string, greater than 0. Optional in slippage mode |

In slippage mode the quote produces a ceiling, so `maxSolIn` is optional. Sending it anyway applies whichever of the two values is lower, which lets you keep a hard spending limit regardless of what the quote returns.

```json theme={null}
{
  "mint": "…",
  "tokensOut": "1000000000",
  "slippageBps": 100,
  "priorityFeeLamports": "20000",
  "tipLamports": "1000000"
}
```

## POST /partner/v1/trades/sell

| Field       | Required           | Constraint                            |
| ----------- | ------------------ | ------------------------------------- |
| `tokensIn`  | Yes                | Integer string, greater than 0        |
| `minSolOut` | Explicit mode only | Integer string, and `"0"` is accepted |

```json theme={null}
{
  "mint": "…",
  "tokensIn": "1000000000",
  "slippageBps": 100,
  "priorityFeeLamports": "20000",
  "tipLamports": "1000000"
}
```

## Response

All three endpoints respond identically.

```json theme={null}
{ "signature": "…", "mint": "…", "status": "submitted" }
```

<Warning>
  A status of `submitted` means the network accepted the transaction, not that it landed. Confirm the signature against Solana through your own RPC before confirming anything to a user.
</Warning>

## Partner Fees

Sending a non-zero `partnerFeeBps` adds a SOL transfer to your registered fee wallet in the same transaction as the trade, which means the fee and the trade settle together. This requires the Partner tier.

The basis differs by endpoint, and rounding is always down.

| Endpoint               | Basis                                                        |
| ---------------------- | ------------------------------------------------------------ |
| `trades/buy`           | `maxSolIn`, deducted before the remainder reaches the venue  |
| `trades/buy-exact-out` | `maxSolIn`, whether you supplied it or the quote produced it |
| `trades/sell`          | `minSolOut`, so a `minSolOut` of `"0"` earns nothing         |

<Note>
  In slippage mode the fee is deducted before the quote is taken, so the derived bound already accounts for it. In explicit mode you have to quote `minTokensOut` against `maxSolIn` minus the fee yourself.
</Note>

See [Partner Fees](/concepts/partner-fees) for wallet registration and disclosure requirements.

## Retries and Duplicate Trades

The Partner API does not support idempotency keys on any write endpoint. If a trade times out at the network level you cannot ask Trench whether it landed, and resending the request may execute the trade a second time, since nothing deduplicates two identical buys.

<Warning>
  We recommend against retrying a trade automatically on a timeout or a `5xx`, because the transaction may already be in flight.

  Record each request before you send it. On an ambiguous failure, check the signature or the user's recent transactions on-chain, and otherwise surface the uncertainty to the user rather than resolving it by guessing.
</Warning>

A `429`, a `409 migration_pending`, and a `409 pool_not_open` are all safe to retry, because each is rejected before anything reaches the transaction builder.

## Errors

| Status | Error                        | Cause                                                        |
| ------ | ---------------------------- | ------------------------------------------------------------ |
| `400`  | `invalid_input`              | Body failed schema validation                                |
| `400`  | `invalid_partner_fee`        | `partnerFeeBps` is not an integer between 0 and 9999         |
| `400`  | `partner_fee_not_configured` | Your tier cannot charge fees, or no fee wallet is registered |
| `401`  | `invalid_token`              | Missing, expired, or revoked token                           |
| `403`  | `insufficient_scope`         | Token lacks `trade:execute`                                  |
| `403`  | `delegation_missing`         | User has not enabled 1-click trading                         |
| `409`  | `migration_pending`          | Curve complete, migration still finalizing. Retryable        |
| `409`  | `pool_not_open`              | Raydium pool is not open for trading yet. Retryable          |
| `422`  | `insufficient_sol`           | Adds `requiredLamports`, `availableLamports`                 |
| `422`  | `insufficient_tokens`        | Adds `requiredTokens`, `availableTokens`                     |
| `422`  | `missing_sell_token_account` | User holds none of this token                                |
| `422`  | `insufficient_liquidity`     | Venue cannot fill at this size                               |
| `422`  | `curve_complete`             | The curve completed while the trade was in flight            |
| `422`  | `slippage_exceeded`          | Price moved past the bound                                   |
| `502`  | `privy_sign_failed`          | Wallet signing service failed                                |
| `502`  | `rpc_unavailable`            | Solana RPC or the landing service is down                    |

The full catalog is on the [Error Handling](/concepts/errors) page.
