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

# Prerequisites & Authentication

> Get your credentials, exchange them for a bearer token, and learn the request conventions every endpoint shares.

The Mutual Fund API is a whitelabelled service for transacting in Indian mutual
funds. This guide covers what you need before your first call, how
authentication works, and the conventions (envelopes, errors, idempotency,
pagination) that apply to every endpoint.

## What you get

When your account is provisioned you receive:

| Item            | Description                                                                                                  |
| --------------- | ------------------------------------------------------------------------------------------------------------ |
| `client_id`     | Public identifier for your integration.                                                                      |
| `client_secret` | Secret credential. Store it in a secret manager — it is shown only once and cannot be retrieved later.       |
| Environment     | `uat` for integration testing, `prod` for live money. Each has its own base URL and its own credential pair. |

<Note>
  Account provisioning (member code, exchange credentials, callback URL,
  scopes) is handled by your onboarding contact. This guide assumes you already
  have an active account.
</Note>

## Base URL

```
https://<env-host>/api/<resource>/v1
```

Every resource group is versioned independently (`/api/investors/v1`,
`/api/orders/v1`, …). Examples in this documentation use
`https://api.example.com` as a placeholder.

## Step 1 — Get a bearer token

```sh theme={null}
curl -sX POST https://api.example.com/api/auth/v1/token \
  -H 'Content-Type: application/json' \
  -d '{ "client_id": "b1f0...", "client_secret": "..." }'
```

```json theme={null}
{
  "success": true,
  "data": {
    "access_token": "mfa_9f2c...",
    "token_type": "Bearer",
    "expires_in": 3600,
    "scope": "investors:write mandates:write orders:write payments:write reports:read"
  }
}
```

* `expires_in` is in seconds. Request a fresh token before it expires; there is
  no refresh token — call `/token` again with your credentials.
* `scope` is the space-separated list of permissions your account holds. Calls
  that need a scope you don't have return `403 FORBIDDEN`.

<Warning>
  `POST /api/auth/v1/token` is rate limited to **8 failed attempts per
  `client_id` per 15-minute window**. Exceeding it returns `429 RATE_LIMITED`
  with a `Retry-After` header. A failed attempt means bad credentials — successful
  calls don't count.
</Warning>

### Auth failures

| Condition                                       | Response                                                           |
| ----------------------------------------------- | ------------------------------------------------------------------ |
| Bad credentials (or upstream identity disabled) | `401 UNAUTHORIZED` — "Invalid client credentials."                 |
| Credentials valid but no active account         | `403 NOT_PROVISIONED` — "No active account for these credentials." |
| Too many failed attempts                        | `429 RATE_LIMITED`                                                 |

## Step 2 — Call the API

Send the token as a bearer header on every other endpoint:

```sh theme={null}
curl -sX GET https://api.example.com/api/auth/v1/me \
  -H 'Authorization: Bearer mfa_9f2c...'
```

`GET /api/auth/v1/me` echoes back what your token can do:

```json theme={null}
{
  "success": true,
  "data": {
    "tenant_id": "...",
    "arn": "...",
    "member_code": "...",
    "environment": "uat",
    "scopes": ["investors:write", "orders:write", "payments:write"],
    "callback_url": "https://api.example.com/webhooks/nse/payments/...",
    "nse_credentials_configured": true
  }
}
```

`nse_credentials_configured: false` means the exchange credentials behind your
account are incomplete — orders and payments will fail until that is resolved
with your onboarding contact. Credential values are never returned, in any form.

### Revoking a token

```sh theme={null}
curl -sX POST https://api.example.com/api/auth/v1/revoke \
  -H 'Authorization: Bearer mfa_9f2c...'
```

Returns `204`. The presented token is invalidated immediately.

## Request conventions

Every endpoint in this API shares the following conventions.

### Response envelope

Success:

```json theme={null}
{ "success": true, "data": { }, "message": "" }
```

Errors:

```json theme={null}
{
  "success": false,
  "error": {
    "code": "VALIDATION_FAILED",
    "message": "Human-readable summary.",
    "details": [ { "field": "primary_holder.name", "issue": "required" } ],
    "provider_remark": "The exchange's own words, when the failure came from it.",
    "request_id": "req_01J8..."
  }
}
```

<Warning>
  `provider_remark` is informational only. **Never parse it** — branch on
  `code`. Its wording can change without notice.
</Warning>

### Error codes

| `code`                       | HTTP | Meaning                                                       |
| ---------------------------- | ---- | ------------------------------------------------------------- |
| `VALIDATION_FAILED`          | 400  | Request shape or business rule failed locally.                |
| `UNAUTHORIZED`               | 401  | Missing / invalid / expired token.                            |
| `FORBIDDEN`                  | 403  | Valid token, missing scope.                                   |
| `NOT_PROVISIONED`            | 403  | No active account for these credentials.                      |
| `NOT_FOUND`                  | 404  | Not found within your account.                                |
| `IDEMPOTENCY_KEY_REUSED`     | 409  | Same key, different body.                                     |
| `INVALID_STATE`              | 409  | Illegal state transition (e.g. cancelling an allotted order). |
| `INVALID_SCHEME`             | 422  | Scheme doesn't allow this transaction.                        |
| `KYC_INCOMPLETE`             | 422  | Investor is not `REGISTERED` yet.                             |
| `MANDATE_NOT_ACTIVE`         | 422  | Mandate isn't usable.                                         |
| `INSUFFICIENT_MANDATE_LIMIT` | 422  | Debit exceeds the mandate ceiling.                            |
| `UCC_REJECTED`               | 422  | The exchange rejected an investor/mandate registration.       |
| `ORDER_REJECTED`             | 422  | The exchange rejected the order.                              |
| `PAYMENT_REJECTED`           | 422  | The exchange rejected the payment.                            |
| `RATE_LIMITED`               | 429  | Back off; `Retry-After` is set.                               |
| `PROVIDER_UNAVAILABLE`       | 502  | Exchange unreachable or erroring; safe to retry.              |
| `INTERNAL`                   | 500  | Unexpected server error.                                      |

### Request ID

Send `X-Request-Id: <your-uuid>` and it is echoed back on the response and in
every error envelope's `request_id`. Omit it and the server generates one.
Quote this value in support requests.

### Idempotency

Send `Idempotency-Key: <uuid>` on unsafe (write) calls.

* First call: the response is stored against `(account, key, body fingerprint)`
  for 24 hours.
* Replay with the same body: the stored response is returned verbatim with
  `Idempotency-Replayed: true`.
* Same key, different body: `409 IDEMPOTENCY_KEY_REUSED`.

<Note>
  Idempotency is currently enforced on `POST /api/orders/v1/` — the call where
  it matters most. Sending the header on other writes is harmless and
  forward-compatible.
</Note>

### Pagination

List endpoints take `?limit=<n>&cursor=<opaque>` (max `limit` 200) and return:

```json theme={null}
{ "success": true, "data": [ ], "next_cursor": "..." }
```

Pass `next_cursor` back as `cursor` for the next page. Absence of
`next_cursor` means the last page.

### Types

* Amounts are JSON numbers, never strings.
* Dates are `YYYY-MM-DD`.
* Timestamps are RFC3339 UTC.
* Enums are `SCREAMING_SNAKE`.

## Next steps

<Steps>
  <Step title="Create an investor">
    [Create an investor](/create-investor) and wait for `status: REGISTERED`.
  </Step>

  <Step title="Create an investment account">
    [Create an investment account](/investment-accounts) to group the
    investor's products.
  </Step>

  <Step title="Register a mandate (optional)">
    [Create a mandate](/mandates) for auto-debit payments.
  </Step>

  <Step title="Place and pay for an order">
    [Create a purchase order](/purchase-orders) and settle it.
  </Step>
</Steps>
