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

# Direct Order Execution

> Place a market order on a specific venue in one call, without a prior quote

`POST /execution/orders` places a market order on a single named venue and returns one
order. You do not need to call the quote endpoint first — the route is priced and funded
server-side.

Use it when your backend already knows which venue it wants and how much to spend. If you
want to see a price before committing, call `GET /orderbook/{venueMarketOutcomeId}/route`
first and execute with `POST /execution/fill` instead.

## Request

```bash theme={null}
curl -X POST https://api.agg.market/execution/orders \
  -H "x-app-id: $AGG_APP_ID" \
  -H "Authorization: Bearer $USER_JWT" \
  -H "content-type: application/json" \
  -d '{
    "venue": "polymarket",
    "venueMarketOutcomeId": "vmo_abc123",
    "side": "buy",
    "maxSpend": 25,
    "externalId": "your-trade-id-0001"
  }'
```

| Field                  | Type                | Notes                                                                                        |
| ---------------------- | ------------------- | -------------------------------------------------------------------------------------------- |
| `venue`                | string              | The venue to trade on. This endpoint never splits an order across venues.                    |
| `venueMarketOutcomeId` | string              | The outcome to trade, on the venue you named. See [Naming the outcome](#naming-the-outcome). |
| `side`                 | `"buy"` \| `"sell"` |                                                                                              |
| `maxSpend`             | number              | Buy only. Maximum all-in USD spend, inclusive of fees. You cannot be charged more than this. |
| `sellShares`           | number              | Sell only. Number of contracts to sell.                                                      |
| `externalId`           | string              | **Required.** Your trade id, max 36 characters, unique within your app.                      |
| `slipCapBps`           | number              | Optional slippage cap in basis points. Defaults to `500` (5%).                               |

With the SDK:

```ts theme={null}
import { createAggClient } from "@agg-build/sdk";

const client = createAggClient({ baseUrl: AGG_API_URL, appId: AGG_APP_ID });

const order = await client.placeOrder({
  venue: "polymarket",
  venueMarketOutcomeId: "vmo_abc123",
  side: "buy",
  maxSpend: 25,
  externalId: "your-trade-id-0001",
});
```

## Response

```json theme={null}
{
  "orderId": "ord_xyz789",
  "externalId": "your-trade-id-0001",
  "venue": "polymarket",
  "status": "pending",
  "quoteId": "qte_abc123",
  "quotedPriceRaw": "0.53",
  "quotedCostRaw": "25000000",
  "quotedSharesRaw": "47169811"
}
```

`status` is always `pending` — the order has been accepted and is executing.

`quotedPriceRaw`, `quotedCostRaw`, and `quotedSharesRaw` can be `null` in the rare case the
order row doesn't carry a quoted value yet. The order has still been placed — never treat a
`null` as zero.

## Tracking the order

Prefer push over polling. Both the `trades.filled` webhook and the `order_event` websocket
message carry your `externalId`, so you can route them straight into your own ledger without
holding any state between the request and the fill.

When you do poll, pick by what you are asking:

| You want                               | Use                                 | Keyed on                    |
| -------------------------------------- | ----------------------------------- | --------------------------- |
| Live progress while the order executes | `GET /execution/status?quoteId=`    | `quoteId` from the response |
| The finished order, or reconciliation  | `GET /execution/orders?externalId=` | your `externalId`           |

`GET /execution/status` returns quote-scoped execution progress and DAG step state, so you
can show a partner-facing status — bridging, submitting, confirming — instead of a spinner.
On a cross-chain fill that window is tens of seconds.

`GET /execution/orders?externalId=` is the one to reach for **after a timeout**. If the
request never returned, you never saw the `quoteId` — your `externalId` is the only handle
you have on that trade, which is the whole reason to send one.

## Retries are safe

`externalId` is unique within your app — two different users of the same app cannot share
one. If a request times out and you retry with the same `externalId`, you get `409 Conflict`
instead of a second trade. Generate one id per intended trade and reuse it across retries.

## Price protection

There is no separate price-guard parameter, because two bounds already apply:

* **On buys, `maxSpend` is a hard ceiling.** The app fee is reserved out of it before
  routing, so your all-in spend can never exceed it. The worst case is fewer shares.
* **`slipCapBps` bounds how far down the book we fill**, defaulting to 5%.

**On sells, set `slipCapBps` explicitly.** `sellShares` bounds the number of contracts sold,
not the proceeds you receive — a large sell into a thin book can fill well below the top of
book at the default cap.

## Naming the outcome

Pass the `venueMarketOutcomeId` that belongs to the `venue` you named. That is the only form
that unambiguously identifies what trades.

Ids from other venues are resolved through our matched-outcome graph rather than rejected,
so a mismatched id can still place a real order — on an outcome our matching chose rather
than one you named. If several outcomes on the requested venue match and none is the one you
named, the request fails with `400` and `code: "outcome_ambiguous"`, listing the candidates.

On sells this matters more: resolution runs over the positions you hold on that venue, not
over its outcomes, so the id you pass does not on its own pin which position closes. Read
`GET /execution/positions` first when it matters which one goes.

## Venue availability

Venues that appear in our catalog but do not yet support order execution return `400` with
`code: "venue_not_executable"`. This is checked against execution support, not against
whether the venue is otherwise live — a venue whose markets you can browse and quote may
still be rejected here. The code is terminal: retrying the same venue will not start
working, so treat it differently from `quote_unfillable`, which is worth re-quoting.

## Related

<CardGroup cols={2}>
  <Card title="Place Order API" icon="book-open" href="/api-reference/execution/place-order">
    Full request and response schema for `POST /execution/orders`.
  </Card>

  <Card title="Execution Status API" icon="book-open" href="/api-reference/execution/get-execution-status">
    Quote-scoped progress and DAG step state while an order executes.
  </Card>

  <Card title="Limit Orders" icon="cube" href="/recipes/limit-orders">
    Resting orders at a price you choose, when you do not want an immediate fill.
  </Card>

  <Card title="Funding & Withdrawals" icon="cube" href="/recipes/deposits">
    Prepare balances before placing live buy orders.
  </Card>
</CardGroup>
