> ## 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.

# Sandbox Simulation

> Drive an investor, order, payment, or mandate through every outcome yourself, with no live exchange or bank in the loop.

Every resource in this API eventually advances on its own, driven by the
exchange, a bank, or a payment gateway: NSE registers a UCC, accepts or
rejects an order and later allots it, a gateway captures a payment, a bank
approves a mandate. In **sandbox** environments, none of that happens for
real — instead, each resource's create/get response carries a
`simulation_links` object you call yourself to move the resource to its next
state, with the exact same status changes and [webhook](/api-documentation/webhooks)
events a real exchange/bank/gateway response would produce.

<Note>
  `simulation_links` only ever appears in a sandbox environment. In production
  the field is always absent and the links themselves don't exist — calling
  one returns `404`.
</Note>

## How it works

1. Create a resource normally (`POST /api/investors/v1`, `/api/orders/v1`,
   `/api/payments/v1`, `/api/mandates/v1`) — the request/response shape is
   identical to production.
2. Read `simulation_links` off the create response, or a later `GET` of the
   same resource (some links only appear once the resource reaches a later
   status — see each section below).
3. `GET` the `accept` or `reject` link — no `Authorization` header needed,
   so you can open it directly in a browser or `curl` it. It's scoped by an
   unguessable token in the query string, and safe to call more than once.
4. Poll the resource, or listen for the webhook event, exactly as you would
   in production.

```json theme={null}
{
  "id": "pay_9c21a8",
  "status": "INITIATED",
  "simulation_links": {
    "accept": "https://api-sandbox.mf-atlas.space/api/simulation/payments/v1/pay_9c21a8/accept?token=…",
    "reject": "https://api-sandbox.mf-atlas.space/api/simulation/payments/v1/pay_9c21a8/reject?token=…"
  }
}
```

## Investor

`simulation_links` appears while the investor is `PENDING` or `PROCESSING`
(right after `POST /api/investors/v1`, or shortly after — provisioning is
asynchronous, so `GET` again if it isn't there yet).

| Link     | Effect                                                                                              |
| -------- | --------------------------------------------------------------------------------------------------- |
| `accept` | Advances to `REGISTERED`, mirroring a successful NSE UCC registration. Emits `investor.registered`. |
| `reject` | Advances to `REJECTED`. Emits `investor.rejected`.                                                  |

## Purchase order

An order has **two** independent simulated stages, matching its real
lifecycle (`PENDING → SUBMITTED → ACCEPTED → ALLOTTED`):

**Dispatch** — appears while the order is `PENDING`/`SUBMITTED`:

| Link              | Effect                                                                         |
| ----------------- | ------------------------------------------------------------------------------ |
| `dispatch.accept` | Advances to `ACCEPTED`, mirroring exchange acceptance. Emits `order.accepted`. |
| `dispatch.reject` | Advances to `REJECTED`. Emits `order.rejected`.                                |

**Allotment** — appears only once the order is `ACCEPTED`:

| Link               | Effect                                                                               |
| ------------------ | ------------------------------------------------------------------------------------ |
| `allotment.accept` | Advances to `ALLOTTED`, mirroring the allotment report sync. Emits `order.allotted`. |
| `allotment.reject` | Advances to `REJECTED`. Emits `order.rejected`.                                      |

The allotment `accept` link accepts optional query parameters —
`units`, `nav`, `folio_no`, `allotment_date` — if you want specific values
in the response; omit any of them for a deterministic default.

## Payment

`simulation_links` appears while the payment is `INITIATED` (immediately on
`POST /api/payments/v1` in a sandbox environment — no real gateway/bank round
trip happens).

| Link     | Effect                                                                                                                                         |
| -------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `accept` | Advances to `CAPTURED`, mirroring a successful settlement callback. Emits `payment.captured`, and moves every linked order's `payment_status`. |
| `reject` | Advances to `FAILED`. Emits `payment.failed`.                                                                                                  |

## Mandate

`simulation_links` appears only for an `ENACH` mandate while it is `PENDING`.
A `PHYSICAL` mandate has no simulation link — in a sandbox environment,
`POST /api/mandates/v1/:id/image` registers it directly.

| Link     | Effect                                                                                            |
| -------- | ------------------------------------------------------------------------------------------------- |
| `accept` | Advances to `REGISTERED`, mirroring the investor's own bank approval. Emits `mandate.registered`. |
| `reject` | Advances to `REJECTED`. Emits `mandate.rejected`.                                                 |

## Next step

Wire up [webhooks](/api-documentation/webhooks) so your integration reacts
to these simulated outcomes the same way it will react in production.
