> For the complete documentation index, see [llms.txt](https://synthesys-2.gitbook.io/synthesys-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://synthesys-2.gitbook.io/synthesys-docs/network-overview/api-reference/orders-wip.md).

# Orders \[WIP]

Orders are how investors move money in and out of a share class. The Synthesys network models two primary order types — **subscriptions** (an investor buying into a share class with fiat) and **redemptions** (an investor selling tokens back for fiat) — plus two state-management endpoints for inspecting and cancelling orders after they're placed.

This page covers the contract for all four order endpoints, the parameters distributors need to supply, the response shapes, and the patterns we recommend for order lifecycle handling. Orders are placed by a distributor's backend on behalf of a registered investor — they should never be issued directly from a browser or mobile client.

### The order lifecycle

A subscription or redemption order moves through a small set of states from placement to settlement. The exact state machine is owned by the fund administrator, but at the API surface you'll see three observable phases:

1. **Placement.** A `POST` to `/orders/subscribe` or `/orders/redeem` creates the order and returns its identifier. The order is now in a *pending* state, awaiting the fund administrator's review.
2. **Acceptance or rejection.** The fund administrator's back-office accepts or rejects the order out-of-band. The API surfaces this transition through the corresponding webhook events (`order.accepted`, `order.rejected`) — see Webhooks.
3. **Settlement.** Accepted orders settle into the on-chain token via a transfer. The corresponding token events (`token.transfer_completed`, `token.transfer_failed`) signal settlement.

Cancellation is only possible before acceptance. Once the fund administrator accepts an order, the cancellation endpoint will reject the request and you'll need to handle the workflow downstream.

***

### Create subscription order

Place a buy order for a share class. The investor purchases new tokens at the current NAV; the resulting token balance lands at `walletAddress` once the order settles.

**`POST /orders/subscribe`**

#### Request body

| Field           | Type   | Required | Description                                                                                                                            |
| --------------- | ------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| `distributorId` | string | Yes      | The distributor placing the order. Format `DT######`                                                                                   |
| `investorId`    | string | Yes      | The investor on whose behalf the order is placed. Format `IN######`                                                                    |
| `shareClassId`  | string | Yes      | The share class being subscribed into. Format `SC######`                                                                               |
| `amount`        | number | Yes      | Fiat amount of the subscription. Must be ≥ 0. The number of tokens issued is derived from this against the live NAV at acceptance time |
| `currency`      | string | No       | ISO currency code (e.g. `USD`). Defaults to the share class's denomination currency                                                    |
| `chainId`       | string | No       | Numeric EVM chain ID — the network the resulting tokens will be issued on                                                              |
| `walletAddress` | string | No       | Destination wallet address for the issued tokens                                                                                       |

The `chainId` and `walletAddress` fields are optional at placement time but must be present (either on the order itself or on the investor's profile) before the order can settle. Most distributors send them on every subscription to keep the flow simple.

#### Response

Returns the new order's identifier and current state:

json

```json
{
  "isError": false,
  "errorMsg": null,
  "data": {
    "orderId": "ord_01HF8K2WJYZN3X8M5R6V7P9Q0T",
    "status": "pending",
    "type": "subscribe",
    "shareClassId": "SC000001",
    "investorId": "IN000001",
    "distributorId": "DT000001",
    "amount": 1000,
    "currency": "USD",
    "createdAt": 1761807929692
  }
}
```

The `orderId` is a UUID-style opaque identifier — persist it against the request payload on your side so you can correlate later state transitions.

#### Examples

**curl:**

bash

```bash
curl -sX POST $BASE/orders/subscribe \
  -H 'Content-Type: application/json' \
  -H 'x-actor: apex-orders' \
  -d '{
    "distributorId": "DT000001",
    "investorId": "IN000001",
    "shareClassId": "SC000001",
    "amount": 1000,
    "currency": "USD",
    "chainId": "1",
    "walletAddress": "0xabc..."
  }'
```

**Node.js:**

javascript

```javascript
async function createSubscription({ distributorId, investorId, shareClassId, amount, currency, chainId, walletAddress }) {
  const res = await fetch(`${BASE}/orders/subscribe`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'x-actor': process.env.ACTOR ?? 'system',
    },
    body: JSON.stringify({ distributorId, investorId, shareClassId, amount, currency, chainId, walletAddress }),
  });
  const payload = await res.json();
  if (payload.isError) throw new Error(payload.errorMsg);
  return payload.data;
}

const order = await createSubscription({
  distributorId: 'DT000001',
  investorId: 'IN000001',
  shareClassId: 'SC000001',
  amount: 1000,
  currency: 'USD',
});
```

**Python:**

python

```python
import os, requests

def create_subscription(distributor_id, investor_id, share_class_id, amount, *,
                        currency=None, chain_id=None, wallet_address=None):
    body = {
        "distributorId": distributor_id,
        "investorId": investor_id,
        "shareClassId": share_class_id,
        "amount": amount,
    }
    if currency:        body["currency"] = currency
    if chain_id:        body["chainId"] = chain_id
    if wallet_address:  body["walletAddress"] = wallet_address

    r = requests.post(
        f"{BASE}/orders/subscribe",
        json=body,
        headers={"x-actor": os.getenv("ACTOR", "system")},
        timeout=10,
    )
    payload = r.json()
    if payload["isError"]:
        raise RuntimeError(payload["errorMsg"])
    return payload["data"]

order = create_subscription("DT000001", "IN000001", "SC000001", 1000, currency="USD")
```

***

### Create redemption order

Place a sell order. The investor's tokens are burned and the corresponding fiat is returned through the fund administrator's settlement flow.

**`POST /orders/redeem`**

#### Request body

| Field           | Type   | Required | Description                                                                              |
| --------------- | ------ | -------- | ---------------------------------------------------------------------------------------- |
| `distributorId` | string | Yes      | The distributor placing the order. Format `DT######`                                     |
| `investorId`    | string | Yes      | The investor on whose behalf the order is placed. Format `IN######`                      |
| `shareClassId`  | string | Yes      | The share class being redeemed. Format `SC######`                                        |
| `tokenCount`    | number | Yes      | Number of tokens to redeem. Must be ≥ 0 and ≤ the investor's holding in this share class |
| `chainId`       | string | No       | Numeric EVM chain ID the tokens are being redeemed from                                  |
| `walletAddress` | string | No       | The wallet address holding the tokens to be burned                                       |

Note that redemptions are denominated in **token count**, not fiat amount — this is the inverse of subscriptions. The fiat payout is computed at acceptance time against the live NAV.

#### Response

json

```json
{
  "isError": false,
  "errorMsg": null,
  "data": {
    "orderId": "ord_01HF8K3X9P2NV7K4B6Q5R8M2D1",
    "status": "pending",
    "type": "redeem",
    "shareClassId": "SC000001",
    "investorId": "IN000001",
    "distributorId": "DT000001",
    "tokenCount": 500,
    "createdAt": 1761807929700
  }
}
```

#### Examples

**curl:**

bash

```bash
curl -sX POST $BASE/orders/redeem \
  -H 'Content-Type: application/json' \
  -d '{
    "distributorId": "DT000001",
    "investorId": "IN000001",
    "shareClassId": "SC000001",
    "tokenCount": 500
  }'
```

**Node.js:**

javascript

```javascript
const order = await call('POST', '/orders/redeem', {
  distributorId: 'DT000001',
  investorId: 'IN000001',
  shareClassId: 'SC000001',
  tokenCount: 500,
});
```

**Python:**

python

```python
order = call("POST", "/orders/redeem", {
    "distributorId": "DT000001",
    "investorId": "IN000001",
    "shareClassId": "SC000001",
    "tokenCount": 500,
})
```

***

### Get order by ID

Retrieve a single order by its identifier. Use this to poll order state if you can't subscribe to webhooks, or to confirm the persisted state of an order you just placed.

**`GET /orders/:id`**

#### Path parameters

| Param | Type   | Description                                             |
| ----- | ------ | ------------------------------------------------------- |
| `id`  | string | The order's opaque identifier, as returned at placement |

#### Response

json

```json
{
  "isError": false,
  "errorMsg": null,
  "data": {
    "orderId": "ord_01HF8K2WJYZN3X8M5R6V7P9Q0T",
    "status": "accepted",
    "type": "subscribe",
    "shareClassId": "SC000001",
    "investorId": "IN000001",
    "distributorId": "DT000001",
    "amount": 1000,
    "currency": "USD",
    "createdAt": 1761807929692,
    "updatedAt": 1761808129700
  }
}
```

#### Examples

**curl:**

bash

```bash
curl -s $BASE/orders/ord_01HF8K2WJYZN3X8M5R6V7P9Q0T | jq .
```

**Node.js:**

javascript

```javascript
const order = await call('GET', `/orders/${encodeURIComponent(orderId)}`);
```

**Python:**

python

```python
order = call("GET", f"/orders/{order_id}")
```

***

### Cancel order

Cancel a pending order. Cancellation is only valid before the fund administrator has accepted the order; once accepted, this endpoint will reject the request.

**`POST /orders/cancel/:orderId`**

#### Path parameters

| Param     | Type   | Description         |
| --------- | ------ | ------------------- |
| `orderId` | string | The order to cancel |

#### Response

json

```json
{
  "isError": false,
  "errorMsg": null,
  "data": {
    "success": true,
    "message": "Order cancelled successfully"
  }
}
```

#### Examples

**curl:**

bash

```bash
curl -sX POST $BASE/orders/cancel/ord_01HF8K2WJYZN3X8M5R6V7P9Q0T
```

**Node.js:**

javascript

```javascript
await call('POST', `/orders/cancel/${encodeURIComponent(orderId)}`);
```

**Python:**

python

```python
call("POST", f"/orders/cancel/{order_id}")
```

***

### Common errors

* **`404` "investorId not found" / "distributorId not found" / "shareClassId not found"** — a referenced ID doesn't exist in this environment. Verify with the corresponding read endpoint.
* **`400` "amount must be ≥ 0" / "tokenCount must be ≥ 0"** — a numeric field is negative.
* **`400` "tokenCount exceeds holdings"** — the investor doesn't hold enough tokens for the requested redemption.
* **`409` "order is no longer cancellable"** — the order has progressed past the cancellable state (typically because it was already accepted).
* **`404` "order not found"** — the order ID doesn't exist or belongs to a different distributor.

### Patterns

* **Persist the `orderId` immediately.** The order's lifecycle is long-running (often minutes for live acceptance, longer for settlement). Logging the ID against the user-visible context at placement gives you something to grep for when things go wrong.
* **Prefer webhooks for state changes.** Polling `GET /orders/:id` for every order to detect acceptance is wasteful. Register a webhook for `order.accepted` and `order.rejected` and react event-driven instead. See Webhooks.
* **Always pass `x-actor`.** Audit fields on the order record will be populated from this header.
* **Validate amounts on your side first.** The API will reject negative or oversized values, but doing the check in your application gives the end-user a faster, more helpful error than an API round-trip would.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://synthesys-2.gitbook.io/synthesys-docs/network-overview/api-reference/orders-wip.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
