> 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/transactions-wip.md).

# Transactions \[WIP]

Orders are how investors subscribe into a share class. Placing an order is a **two-step flow**: first you create the order on the network (which validates the request, prices it against the live NAV, and lands it in `created` state), and then you push it through to the supply-side platform for execution (which transitions it to `submitted`). Splitting placement from push gives your application a clean audit point — you can inspect the priced order, share it with the investor for confirmation, or cancel it before any settlement work begins.

This page covers all five subscription-order endpoints, the parameters distributors supply, the response shapes (including the rich pricing detail returned at placement), and the patterns we recommend for the lifecycle. 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.

### Prerequisites for placing an order

Before a subscription order can be created for an investor, the following must already be in place:

1. The investor is registered under your distributor — see Investors.
2. The investor has a wallet configured for the share class's default chain.
3. The investor is granted access to the share class.
4. The share class has a live NAV published.

Wallet setup and share-class grants are handled out-of-band by the Synthesys operations team. If a prerequisite is missing, `POST /orders/subscribe` will fail with a clear `errorMsg` (e.g. *"Investor not granted access to SC000001"* or *"No wallet configured for investor on chain 1"*).

### Order lifecycle

A subscription moves through three observable states:

| State             | Set by                                     | Means                                                                                                                     |
| ----------------- | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------- |
| `created`         | `POST /orders/subscribe`                   | The order is priced and parked on the network. Nothing has been pushed downstream yet. Cancellable. Pushable              |
| `submitted`       | `POST /orders/subscriptions/push/:orderId` | The order has been forwarded to the supply-side platform and accepted into its queue. No longer cancellable from this API |
| (terminal states) | downstream                                 | Acceptance, rejection, and settlement happen on the supply-side platform and are reflected back into `statusHistory`      |

The `statusHistory` map on every order records the timestamp, status, and the actor (the value of the `x-actor` header, or `"Network"` for system transitions) for every state change. Use it to render a timeline in your back-office UI.

***

### Create subscription order

Place a subscription. The order is priced immediately against the live NAV; the response includes the full pricing breakdown so you can present it to the investor for confirmation.

**`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######`                                      |
| `amountSubscription` | number | Yes      | Fiat amount of the subscription (e.g. `100000` for 100,000 USD)                               |
| `currency`           | string | No       | ISO currency code. Defaults to the share class's denomination currency                        |
| `externalRef`        | string | No       | Your own correlation identifier for this order. Stored verbatim and echoed back on every read |

The destination chain and wallet are resolved automatically from the share class's `defaultChainId` and the investor's wallet on that chain. You do **not** pass `chainId` or `walletAddress` in the request body.

#### Response

The successful response carries the full priced order:

json

```json
{
  "isError": false,
  "errorMsg": "NA",
  "data": {
    "orderId": "OR000001",
    "orderType": "SUBSCRIPTION",
    "status": "created",
    "distributorId": "DT000001",
    "investorId": "IN000002",
    "shareClassId": "SC000003",
    "amountSubscription": 1000,
    "amountFeesTotal": 1,
    "amountPayable": 1001,
    "amountTokensEstimated": "0.9108",
    "feesSubBps": 10,
    "currency": "USD",
    "deliveryMode": "passthrough",
    "deliveryWallet": "0xAAAA000000000000000000000000000000000001",
    "nav": "1098.0000",
    "navPublishedAt": 1761807929692,
    "externalRef": "first test txn of subscription through network",
    "createdAt": 1761808000000,
    "createdBy": "Network",
    "statusHistory": {
      "1761808000000": { "status": "created", "by": "Network" }
    }
  }
}
```

| Field                   | Notes                                                                                                                                                                     |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `orderId`               | Synthesys-side order identifier, format `OR######`. Persist this immediately                                                                                              |
| `orderType`             | Always `"SUBSCRIPTION"` for this endpoint                                                                                                                                 |
| `status`                | Starts at `"created"`. Will not change until you call the push endpoint                                                                                                   |
| `amountFeesTotal`       | Fee in the order's currency, derived from `amountSubscription × feesSubBps / 10000`                                                                                       |
| `amountPayable`         | The total the investor must transfer: `amountSubscription + amountFeesTotal`                                                                                              |
| `amountTokensEstimated` | Estimated tokens at the current NAV. String (decimal precision). Final tokens are computed at settlement against the NAV in effect then                                   |
| `feesSubBps`            | Subscription fee in basis points, copied from the share class at order time (10 bps = 0.1%)                                                                               |
| `deliveryMode`          | `"passthrough"` (tokens go to the investor's wallet) or `"hold"` (tokens go to the distributor's wallet). Determined by how the share class is granted to the distributor |
| `deliveryWallet`        | The actual address where tokens will land — derived from `deliveryMode`                                                                                                   |
| `nav`                   | The NAV used for pricing, as a decimal string                                                                                                                             |
| `navPublishedAt`        | Epoch ms at which that NAV was struck                                                                                                                                     |
| `statusHistory`         | Map keyed by epoch-ms timestamp. Every state transition appends a new entry                                                                                               |

If no live NAV is published when the order is created, `nav` will be `-1` and `amountTokensEstimated` will be `"-1"`. The order is still valid; it just hasn't been priced.

#### Examples

**curl:**

bash

```bash
curl -sX POST $BASE/orders/subscribe \
  -H 'Content-Type: application/json' \
  -H 'x-actor: apex-orders' \
  -d '{
    "distributorId": "DT000001",
    "investorId": "IN000002",
    "shareClassId": "SC000003",
    "amountSubscription": 1000,
    "currency": "USD",
    "externalRef": "first test txn of subscription through network"
  }'
```

**Node.js:**

javascript

```javascript
async function createSubscription({ distributorId, investorId, shareClassId, amountSubscription, currency, externalRef }) {
  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, amountSubscription, currency, externalRef }),
  });
  const payload = await res.json();
  if (payload.isError) throw new Error(payload.errorMsg);
  return payload.data;
}

const order = await createSubscription({
  distributorId: 'DT000001',
  investorId: 'IN000002',
  shareClassId: 'SC000003',
  amountSubscription: 1000,
  currency: 'USD',
  externalRef: 'first test txn of subscription through network',
});
console.log(`${order.orderId}: ${order.amountPayable} ${order.currency}, est ${order.amountTokensEstimated} tokens`);
```

**Python:**

python

```python
import os, requests

def create_subscription(distributor_id, investor_id, share_class_id, amount_subscription, *,
                        currency=None, external_ref=None):
    body = {
        "distributorId": distributor_id,
        "investorId": investor_id,
        "shareClassId": share_class_id,
        "amountSubscription": amount_subscription,
    }
    if currency:      body["currency"] = currency
    if external_ref:  body["externalRef"] = external_ref

    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",
    "IN000002",
    "SC000003",
    1000,
    currency="USD",
    external_ref="first test txn of subscription through network",
)
print(f"{order['orderId']}: {order['amountPayable']} {order['currency']}")
```

***

### Get order by ID

Read a single order, including its full `statusHistory`. Use this to refresh state for an order your application already knows the ID for.

**`GET /orders/:orderId`**

#### Path parameters

| Param     | Type   | Description                                           |
| --------- | ------ | ----------------------------------------------------- |
| `orderId` | string | The order ID returned at placement, format `OR######` |

#### Response

json

```json
{
  "isError": false,
  "errorMsg": "NA",
  "data": {
    "orderId": "OR000001",
    "orderType": "SUBSCRIPTION",
    "status": "submitted",
    "distributorId": "DT000001",
    "investorId": "IN000001",
    "shareClassId": "SC000001",
    "amountSubscription": 100000,
    "amountPayable": 100100,
    "supplyRefs": {
      "mint": { "orderReference": "SUB000123", "submittedAt": 1761808129700 }
    },
    "statusHistory": {
      "1761808000000": { "status": "created", "by": "Network" },
      "1761808129700": { "status": "submitted", "by": "Network", "note": "pushed to mint" }
    }
  }
}
```

The `supplyRefs` block is present once an order has been pushed to a supply-side platform. It carries the supply-side platform's own reference for the order — useful for cross-system reconciliation.

#### Examples

**curl:**

bash

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

**Node.js:**

javascript

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

**Python:**

python

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

***

### Push subscription order

Forward a `created` order to the supply-side platform for execution. On success, the order's status transitions to `submitted` and `supplyRefs` is populated with the supply-side reference.

**`POST /orders/subscriptions/push/:orderId`**

#### Path parameters

| Param     | Type   | Description                             |
| --------- | ------ | --------------------------------------- |
| `orderId` | string | The order ID to push, format `OR######` |

#### Request body

None. The push endpoint takes no body; everything it needs is on the order.

#### Headers

The optional `x-actor` header stamps `updatedBy` on the order for audit attribution. If omitted, `updatedBy` defaults to `"Network"`.

#### Response

json

```json
{
  "isError": false,
  "errorMsg": "NA",
  "data": {
    "orderId": "OR000001",
    "status": "submitted",
    "updatedAt": 1761808129700,
    "updatedBy": "Network",
    "supplyRefs": {
      "mint": {
        "orderReference": "SUB000123",
        "submittedAt": 1761808129700,
        "raw": { "request": {}, "response": {} }
      }
    },
    "statusHistory": {
      "1761808000000": { "status": "created", "by": "Network" },
      "1761808129700": { "status": "submitted", "by": "Network", "note": "pushed to mint" }
    }
  }
}
```

The `supplyRefs.<provider>.raw` block records the literal request and response exchanged with the supply-side platform for forensic purposes. Most applications can ignore it.

#### Idempotency

Push is **not** idempotent today. Calling it a second time on an already-submitted order returns:

json

```json
{
  "isError": true,
  "errorMsg": "Cannot push subscription in status \"submitted\" (only \"created\")",
  "data": {}
}
```

This is intentional — push is a state-mutating action that may take side effects downstream, and the API protects against accidental double-pushes.

#### Examples

**curl:**

bash

```bash
curl -sX POST $BASE/orders/subscriptions/push/OR000001 \
  -H 'x-actor: apex-orders'
```

**Node.js:**

javascript

```javascript
const result = await call('POST', `/orders/subscriptions/push/${encodeURIComponent(orderId)}`);
console.log(result.supplyRefs?.mint?.orderReference);
```

**Python:**

python

```python
result = call("POST", f"/orders/subscriptions/push/{order_id}")
print(result.get("supplyRefs", {}).get("mint", {}).get("orderReference"))
```

***

### Cancel subscription order

Cancel a `created` order. Once an order has been pushed (`submitted`), cancellation through this API is no longer possible — the supply-side platform owns that state and any cancellation will need to happen there.

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

#### Path parameters

| Param     | Type   | Description                               |
| --------- | ------ | ----------------------------------------- |
| `orderId` | string | The order ID to cancel, format `OR######` |

#### Request body

| Field  | Type   | Required | Description                                             |
| ------ | ------ | -------- | ------------------------------------------------------- |
| `note` | string | No       | Optional free-form reason recorded into `statusHistory` |

#### Response

json

```json
{
  "isError": false,
  "errorMsg": "NA",
  "data": {
    "orderId": "OR000001",
    "status": "cancelled",
    "updatedAt": 1761808200000,
    "statusHistory": {
      "1761808000000": { "status": "created", "by": "Network" },
      "1761808200000": { "status": "cancelled", "by": "Network", "note": "investor changed their mind" }
    }
  }
}
```

Calling cancel on an already-submitted order returns:

json

```json
{
  "isError": true,
  "errorMsg": "Cannot cancel subscription in status \"submitted\" (only \"created\")",
  "data": {}
}
```

#### Examples

**curl:**

bash

```bash
curl -sX POST $BASE/orders/subscriptions/cancel/OR000001 \
  -H 'Content-Type: application/json' \
  -d '{ "note": "investor changed their mind" }'
```

**Node.js:**

javascript

```javascript
await call('POST', `/orders/subscriptions/cancel/${encodeURIComponent(orderId)}`, {
  note: 'investor changed their mind',
});
```

**Python:**

python

```python
call("POST", f"/orders/subscriptions/cancel/{order_id}", { "note": "investor changed their mind" })
```

***

### List subscriptions by distributor

Return every subscription order placed under a given distributor. Useful for back-office views and reconciliation.

**`GET /orders/subscriptions/distributor/:distributorId`**

#### Path parameters

| Param           | Type   | Description       |
| --------------- | ------ | ----------------- |
| `distributorId` | string | Format `DT######` |

#### Query parameters

| Param    | Type   | Required | Description                                                 |
| -------- | ------ | -------- | ----------------------------------------------------------- |
| `status` | string | No       | Filter by status (e.g. `created`, `submitted`, `cancelled`) |

#### Response

json

```json
{
  "isError": false,
  "errorMsg": "NA",
  "data": {
    "items": [
      {
        "orderId": "OR000001",
        "orderType": "SUBSCRIPTION",
        "status": "submitted",
        "investorId": "IN000001",
        "shareClassId": "SC000001",
        "amountSubscription": 100000,
        "amountPayable": 100100,
        "createdAt": 1761808000000
      }
    ],
    "count": 1
  }
}
```

#### Examples

**curl:**

bash

```bash
curl -s "$BASE/orders/subscriptions/distributor/DT000001?status=submitted" | jq .
```

**Node.js:**

javascript

```javascript
async function listByDistributor(distributorId, { status } = {}) {
  const url = new URL(`${BASE}/orders/subscriptions/distributor/${encodeURIComponent(distributorId)}`);
  if (status) url.searchParams.set('status', status);
  const res = await fetch(url);
  const payload = await res.json();
  if (payload.isError) throw new Error(payload.errorMsg);
  return payload.data;
}
```

**Python:**

python

```python
def list_by_distributor(distributor_id, status=None):
    params = {"status": status} if status else None
    r = requests.get(f"{BASE}/orders/subscriptions/distributor/{distributor_id}", params=params, timeout=10)
    payload = r.json()
    if payload["isError"]:
        raise RuntimeError(payload["errorMsg"])
    return payload["data"]
```

***

### List subscriptions by investor

Return every subscription order placed for a specific investor. Use this to render an investor's order history.

**`GET /orders/subscriptions/investor/:investorId`**

#### Path parameters

| Param        | Type   | Description       |
| ------------ | ------ | ----------------- |
| `investorId` | string | Format `IN######` |

#### Query parameters

| Param    | Type   | Required | Description      |
| -------- | ------ | -------- | ---------------- |
| `status` | string | No       | Filter by status |

#### Response

Same shape as the by-distributor list — `{ items: [], count: N }`.

#### Examples

**curl:**

bash

```bash
curl -s $BASE/orders/subscriptions/investor/IN000001 | jq .
```

**Node.js:**

javascript

```javascript
const { items, count } = await call('GET', `/orders/subscriptions/investor/${encodeURIComponent(investorId)}`);
```

**Python:**

python

```python
result = call("GET", f"/orders/subscriptions/investor/{investor_id}")
items, count = result["items"], result["count"]
```

***

### Common errors

* **`Investor not granted access to <shareClassId>`** — The investor is not granted the requested share class. Granting is handled by Synthesys operations.
* **`No wallet configured for investor on chain <chainId>`** — The investor has no wallet on the share class's default chain. Wallet setup is handled by Synthesys operations.
* **`Cannot push subscription in status "<status>" (only "created")`** — You tried to push an order that has already been pushed or cancelled.
* **`Cannot cancel subscription in status "<status>" (only "created")`** — Cancellation is only valid before the push.
* **Push fails with a supply-side error** — The supply-side platform rejected the order (e.g. *"fund not found"*). The order stays in `created` and `errorMsg` carries the upstream reason.

### Patterns

* **Persist `orderId` immediately at placement.** The order's lifecycle is long-running; logging the ID against the user-visible context at placement is the single most useful debugging artefact you can produce.
* **Surface the pricing detail to investors before pushing.** The `created` state exists specifically to let your UI show the investor the priced order — `amountPayable`, `amountTokensEstimated`, the NAV — and obtain confirmation before any downstream commitment.
* **Always pass `x-actor`.** Both the placement and the push will populate audit fields from this header. Audit data is useless if every order is attributed to `"Network"`.
* **Validate inputs in your application first.** Negative or zero `amountSubscription`, missing IDs, wrong format — catch these client-side for faster feedback to the end-user.
* **Use `externalRef` to make reconciliation trivial.** Pass your own order ID as `externalRef` on every subscription; it's stored verbatim and surfaces in every read, so cross-system reconciliation is a one-field join.


---

# 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 by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

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

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

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.
