> ## Documentation Index
> Fetch the complete documentation index at: https://trench-446767c3.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Trade on Behalf of a User

> Execute buys, exact-out buys, and sells on behalf of a connected Trench user, set slippage, handle migrated tokens, and charge your own fee.

Trench builds each trade transaction, signs it with the user's delegated wallet, and submits it, so your application never handles key material.

## Prerequisites

Before starting, make sure you have:

* An access token carrying the `trade:execute` scope.
* A user with 1-click trading enabled.
* Amounts prepared as [integer strings in base units](/concepts/amounts).

## Endpoints Used

* [`POST /partner/v1/trades/buy`](/api-reference/trades)
* [`POST /partner/v1/trades/buy-exact-out`](/api-reference/trades)
* [`POST /partner/v1/trades/sell`](/api-reference/trades)

## Before You Trade

Check `tradingEnabled` on [`GET /partner/v1/me`](/api-reference/me). A user can revoke wallet delegation after connecting, and once they do, every trade will fail with `403 delegation_missing`.

```javascript theme={null}
const { user } = await trench.get("/partner/v1/me");
if (!user.tradingEnabled) return promptReconnect();
```

## Setting Slippage

Every trade needs a bound on how far the price may move between quoting and execution, and there are two ways to give one. Which one you get is decided by whether the body contains `slippageBps`.

### Slippage Mode

Send `slippageBps` and Trench quotes the trade at execution time and derives the bound for you. This is the simpler option, and it is the one to reach for unless you are running your own pricing.

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

The value is in basis points, so `100` is 1%, and the accepted range is 0 to 5000.

### Explicit Mode

Omit `slippageBps` and you supply the bound yourself as `minTokensOut` on a buy or `minSolOut` on a sell. Both are then required.

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

<Warning>
  In explicit mode the API accepts `"0"`, which disables slippage protection entirely and fills at whatever price the curve or pool offers. Derive the bound from a recent quote plus your own tolerance rather than hardcoding a value.
</Warning>

When the bound cannot be met, the trade fails with `slippage_exceeded` rather than filling at a worse price. That is true in both modes.

## Buy

A standard buy spends up to a fixed amount of SOL.

<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",
    }),
  });
  ```

  ```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",
    },
  )
  ```
</CodeGroup>

## Buy Exact Out

An exact-out buy purchases a specific token amount while capping what you spend. This is the right endpoint when the user picks a quantity rather than a SOL budget.

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

In slippage mode `maxSolIn` becomes optional here, since the quote produces one. You may still send it as a hard ceiling, in which case whichever value is lower applies.

## Sell

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

Full field constraints are listed on the [Trade endpoints](/api-reference/trades) reference.

## Migrated Tokens

A Trench token starts on the bonding curve and later migrates to a Raydium pool. All three endpoints handle both, so you send the same request either way and Trench routes it to whichever venue the token currently trades on.

There is a brief window between the curve completing and the pool being ready, and trades during it fail with a retryable `409`.

| Error               | Meaning                                                 |
| ------------------- | ------------------------------------------------------- |
| `migration_pending` | The curve is complete and migration is still finalizing |
| `pool_not_open`     | The pool exists but is not open for trading yet         |

Both clear on their own, so retrying after a short delay is the right response to either.

## The 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 before telling a user their trade went through.
</Warning>

## Confirming

Whether a transaction succeeded is public on-chain state, so you read it from your own RPC using the signature Trench returned rather than asking Trench for it.

```javascript Node.js theme={null}
import { Connection } from "@solana/web3.js";

const connection = new Connection(process.env.SOLANA_RPC_URL, "confirmed");

const { value } = await connection.getSignatureStatuses([signature]);
const status = value[0];
```

A confirmed transaction carrying an `err` is a real economic outcome rather than a transport problem, since the user paid fees and the trade did not execute. Treat it as a failed trade and surface it as one.

To report what the trade actually filled at, read the token balance changes recorded on the transaction.

```javascript Node.js theme={null}
const tx = await connection.getParsedTransaction(signature, {
  maxSupportedTransactionVersion: 0,
});

const { preTokenBalances, postTokenBalances } = tx.meta;
```

## Retries

<Warning>
  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 it a second time.

  We recommend recording each request before you send it. On an ambiguous failure, check the user's recent transactions on-chain if you received a signature, and otherwise surface the uncertainty to the user rather than guessing.
</Warning>

A `429` is the one case where retrying is safe, because the request was rejected before reaching the transaction builder and nothing was submitted.

## Network Fees

Both fee fields are required on every trade.

| Field                 | Minimum   | Purpose                                                   |
| --------------------- | --------- | --------------------------------------------------------- |
| `priorityFeeLamports` | `1000`    | Prioritizes the transaction with Solana validators        |
| `tipLamports`         | `1000000` | Pays the landing service that submits with MEV protection |

Raising the priority fee helps during periods of congestion. Both fees are spent whether or not the trade succeeds.

## Charging Your Own Fee

Adding `partnerFeeBps` to any of the three endpoints pays you a cut of the trade, transferred to a wallet you register with Trench in the same transaction.

```json theme={null}
{
  "mint": "…",
  "maxSolIn": "100000000",
  "slippageBps": 100,
  "partnerFeeBps": 100,
  "priorityFeeLamports": "20000",
  "tipLamports": "1000000"
}
```

The rate travels with each request rather than being stored on your app, so per-user tiering is yours to design. Charging a fee requires the Partner tier and a registered fee wallet.

<Tip>
  In slippage mode the fee is deducted before the quote is taken, so the bound already accounts for it. This is the main practical reason to prefer slippage mode when you charge a fee: in explicit mode you have to quote `minTokensOut` against `maxSolIn` minus the fee yourself, and quoting against the full amount will trip your own guard.
</Tip>

[Partner Fees](/concepts/partner-fees) covers the basis for each endpoint, the wallet requirements, and your disclosure obligations.

## Volume Caps

Neither tier sets a cap by default, but Trench can configure them per app. When set, they apply to `maxSolIn` on buys only, and sells are never capped.

| Error                    | Extra fields                                   |
| ------------------------ | ---------------------------------------------- |
| `403 trade_cap_exceeded` | `maxTradeLamports`                             |
| `403 daily_cap_exceeded` | `dailyVolumeCapLamports`, `spentTodayLamports` |

The daily figure is tracked per user and resets at 00:00 UTC. See [Registration](/concepts/registration).

## Common Failures

| Error                                       | What to do                                                                   |
| ------------------------------------------- | ---------------------------------------------------------------------------- |
| `delegation_missing`                        | Send the user back through authorize to enable 1-click trading               |
| `insufficient_sol` or `insufficient_tokens` | Show required against available, using the fields in the error body          |
| `insufficient_liquidity`                    | Retry at a smaller size                                                      |
| `migration_pending` or `pool_not_open`      | The token is mid-migration, so retry shortly                                 |
| `slippage_exceeded`                         | Widen `slippageBps`, or refresh the quote and recompute your bound           |
| `missing_sell_token_account`                | The user holds none of this token                                            |
| `partner_fee_not_configured`                | Your app cannot charge fees on its current tier, or has no wallet registered |

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