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

# Create a Purchase Order

> Place a lumpsum purchase, track it through the exchange asynchronously, and settle payment.

A **purchase order** buys units of a mutual fund scheme for an investor. This
guide covers `LUMPSUM_PURCHASE`. Placing an order is a two-part flow:

<Steps>
  <Step title="Create the order">
    `POST /api/orders/v1/` — validated and persisted synchronously, then
    dispatched to the exchange in the background. Returns `202`.
  </Step>

  <Step title="Pay for it">
    `POST /api/payments/v1/` — reference one or more orders and a payment
    mode.
  </Step>
</Steps>

## Prerequisites

* The investor is `status: REGISTERED` (see [Create an investor](/create-investor)).
* An [investment account](/investment-accounts) exists for the investor.
* For mandate-funded payments, a `REGISTERED` [mandate](/mandates) exists.

## Supported order types

| `order_type`                             | Status                                                         |
| ---------------------------------------- | -------------------------------------------------------------- |
| `LUMPSUM_PURCHASE`                       | Live — covered here.                                           |
| `REDEMPTION`                             | Live.                                                          |
| `SWITCH`                                 | Live.                                                          |
| `SIP`, `XSIP`, `STP`, `SWP`, `SIP_TOPUP` | **Not yet available** — rejected with `400 VALIDATION_FAILED`. |

## Create the order

```http theme={null}
POST /api/orders/v1/
Authorization: Bearer <token>
Idempotency-Key: 7c3f1e6a-...
Content-Type: application/json
```

```json theme={null}
{
  "order_type": "LUMPSUM_PURCHASE",
  "investor_id": "I_SI_IND_000042",
  "investment_account_id": "A_RT_000001",
  "scheme_code": "128SDGP",
  "amount": 25000,
  "purchase_type": "FRESH",
  "bank_account_id": "bnk_9f21c8",
  "mandate_id": "mnd_4a1b2c",
  "euin": { "declaration": true, "euin": "E123456" },
  "client_ref": "ord-5521"
}
```

Response `202`:

```json theme={null}
{
  "success": true,
  "data": {
    "id": "ord_1a2b3c4d",
    "status": "PENDING",
    "order_type": "LUMPSUM_PURCHASE",
    "client_ref": "ord-5521",
    "created_at": "2026-09-06T10:20:00Z"
  }
}
```

### Fields

| Field                   | Required    | Format / values         | Notes                                                                                                        |
| ----------------------- | ----------- | ----------------------- | ------------------------------------------------------------------------------------------------------------ |
| `order_type`            | yes         | `LUMPSUM_PURCHASE`      |                                                                                                              |
| `investor_id`           | yes         | friendly investor ID    | Must be `REGISTERED`.                                                                                        |
| `investment_account_id` | yes         | friendly account ID     | Must belong to the investor.                                                                                 |
| `scheme_code`           | yes         | string                  | The exchange scheme code.                                                                                    |
| `amount`                | yes         | number                  | Purchase amount.                                                                                             |
| `purchase_type`         | no          | `FRESH` \| `ADDITIONAL` | Defaults to `FRESH`. `ADDITIONAL` requires `folio_no`.                                                       |
| `folio_no`              | conditional | string                  | Required for `ADDITIONAL` purchases.                                                                         |
| `bank_account_id`       | no          |                         | Must belong to the investor if supplied.                                                                     |
| `mandate_id`            | no          |                         | If supplied, checked for usability against `amount` (see [mandates](/mandates#using-a-mandate-for-payment)). |
| `euin`                  | no          | `{ declaration, euin }` | `euin` string is required when `declaration` is `true`.                                                      |
| `remarks`               | no          | string                  |                                                                                                              |
| `client_ref`            | no          | string                  | Your own reference.                                                                                          |

### Validation

Rejected synchronously before the `202`:

| Condition                                           | Response                                                        |
| --------------------------------------------------- | --------------------------------------------------------------- |
| Unsupported `order_type`                            | `400 VALIDATION_FAILED`                                         |
| Investor not found                                  | `404 NOT_FOUND`                                                 |
| Investor not `REGISTERED`                           | `422 KYC_INCOMPLETE`                                            |
| `investment_account_id` missing                     | `400 VALIDATION_FAILED` — "investment\_account\_id is required" |
| `investment_account_id` belongs to another investor | `400 VALIDATION_FAILED`                                         |
| `bank_account_id` belongs to another investor       | `400 VALIDATION_FAILED`                                         |
| `euin.declaration` true but `euin.euin` empty       | `400 VALIDATION_FAILED`                                         |
| `mandate_id` not usable for `amount`                | `422 MANDATE_NOT_ACTIVE` / `INSUFFICIENT_MANDATE_LIMIT`         |

<Note>
  Send an `Idempotency-Key` (UUID) on order creation. Replaying the same key +
  body returns the original response with `Idempotency-Replayed: true`; a
  different body with the same key is `409 IDEMPOTENCY_KEY_REUSED`.
</Note>

## Order lifecycle

```
PENDING ──▶ SUBMITTED ──▶ ACCEPTED ──▶ ALLOTTED
   │            │            │
   └──▶ REJECTED └──▶ REJECTED └──▶ REJECTED / CANCELLED
                └──▶ FAILED
```

| Status      | Meaning                                                                                |
| ----------- | -------------------------------------------------------------------------------------- |
| `PENDING`   | Persisted; queued for dispatch.                                                        |
| `SUBMITTED` | Sent to the exchange.                                                                  |
| `ACCEPTED`  | Exchange accepted the order.                                                           |
| `ALLOTTED`  | Units allotted; `nav`, `units`, `allotment_date` populated (set by a status-sync job). |
| `REJECTED`  | Exchange rejected it on business grounds; reason in `provider_remark`.                 |
| `FAILED`    | Transport failure reaching the exchange; safe to investigate/retry.                    |
| `CANCELLED` | Cancelled via the cancel endpoint.                                                     |

### Track progress

**Poll** `GET /api/orders/v1/:id`:

```json theme={null}
{
  "success": true,
  "data": {
    "id": "ord_1a2b3c4d",
    "order_type": "LUMPSUM_PURCHASE",
    "status": "ALLOTTED",
    "payment_status": "CAPTURED",
    "investor_id": "I_SI_IND_000042",
    "investment_account_id": "A_RT_000001",
    "scheme_code": "128SDGP",
    "amount": 25000,
    "units": 412.339,
    "nav": 60.63,
    "allotment_date": "2026-09-09",
    "provider_order_id": "8812345",
    "client_ref": "ord-5521",
    "created_at": "2026-09-06T10:20:00Z",
    "updated_at": "2026-09-09T21:30:00Z"
  }
}
```

`GET /api/orders/v1/:id/events` returns the full transition trail
(`{ from, to, at, remark }`).

**Or listen** for webhook events `order.accepted`, `order.rejected`,
`order.failed`.

### Cancel an order

```http theme={null}
POST /api/orders/v1/:id/cancel
{ "reason": "customer changed their mind" }
```

Allowed only from `PENDING`, `SUBMITTED` or `ACCEPTED` — otherwise
`409 INVALID_STATE`.

## Pay for the order

```http theme={null}
POST /api/payments/v1/
Authorization: Bearer <token>
Content-Type: application/json
```

```json theme={null}
{
  "investor_id": "I_SI_IND_000042",
  "investment_account_id": "A_RT_000001",
  "order_ids": ["ord_1a2b3c4d"],
  "mode": "MANDATE",
  "mandate_id": "mnd_4a1b2c"
}
```

Response `201`:

```json theme={null}
{
  "success": true,
  "data": {
    "id": "pay_77a1b2",
    "status": "INITIATED",
    "amount": 25000,
    "payment_url": "",
    "basket_id": "NSE-BASKET-9931"
  }
}
```

### Payment fields

| Field                   | Required    | Notes                                                                                |
| ----------------------- | ----------- | ------------------------------------------------------------------------------------ |
| `investor_id`           | yes         |                                                                                      |
| `investment_account_id` | no          | If set, every linked order must belong to it.                                        |
| `order_ids`             | yes         | 1–50 order IDs, same investor, purchase / XSIP only, sharing one investment account. |
| `mode`                  | yes         | `MANDATE` \| `CHEQUE` \| `UPI` \| `NETBANKING` \| `NEFT_RTGS`.                       |
| `mandate_id`            | conditional | Required for `MANDATE`.                                                              |
| `bank_account_id`       | conditional | Required for `CHEQUE`, `UPI`, `NETBANKING`.                                          |
| `vpa`                   | conditional | Required for `UPI`, `name@bank` form.                                                |
| `cheque`                | conditional | `{ number, date }` for `CHEQUE`; date within T-90 … T+3 days.                        |
| `utr`                   | conditional | Required for `NEFT_RTGS` (or submit later — see below).                              |

### How each mode settles

| Mode                 | Investor action                                                                                      |
| -------------------- | ---------------------------------------------------------------------------------------------------- |
| `MANDATE`            | None — the debit is auto-initiated against the registered mandate.                                   |
| `UPI` / `NETBANKING` | The response carries `payment_url` — hand it to the investor to complete payment on the hosted page. |
| `CHEQUE`             | Physical cheque details are registered; funds clear out-of-band.                                     |
| `NEFT_RTGS`          | The investor transfers funds out-of-band; you post the UTR.                                          |

### Payment status

`INITIATED → CAPTURED` (success) / `FAILED` / `REFUNDED`. Completion arrives
asynchronously via an exchange callback, which emits webhook events
`payment.captured`, `payment.failed`, `payment.refunded`. Each linked order's
`payment_status` is updated in step.

### Submit a UTR

```http theme={null}
POST /api/payments/v1/:id/utr
{ "utr": "AXISN02026090612345", "transfer_date": "2026-09-06" }
```

### Read endpoints

```http theme={null}
GET /api/payments/v1/:id
GET /api/payments/v1?investor_id=&status=
```

## Redemption and switch (brief)

Both use the same `POST /api/orders/v1/` endpoint:

* **Redemption** — `order_type: "REDEMPTION"`, plus `units` or
  `all_units: true` (demat), or `folio_no` for physical holdings. No payment
  step — proceeds are credited to the registered bank account.
* **Switch** — `order_type: "SWITCH"` with `switch: { "to_scheme_code": "..." }`.
