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

# Portfolio and Transactions

> Read a customer's holdings, totals per currency, and the transactions on each holding.

## Portfolio

```
GET /api/v3/partners/{shortName}/customers/{externalUserId}/portfolio
```

The customer's holdings, one per plan, and their totals per currency.

```bash cURL theme={null}
curl https://be-dev-1.dev.ndovu.co/api/v3/partners/YOUR_SHORT_NAME/customers/cust_8842/portfolio \
  -H "Authorization: Bearer $ACCESS_TOKEN"
```

```javascript Node.js theme={null}
const res = await fetch(
  `https://be-dev-1.dev.ndovu.co/api/v3/partners/${shortName}/customers/${externalUserId}/portfolio`,
  { headers: { Authorization: `Bearer ${accessToken}` } }
);

const { data: portfolio } = await res.json();
```

```python Python theme={null}
res = requests.get(
    f"https://be-dev-1.dev.ndovu.co/api/v3/partners/{short_name}/customers/{external_user_id}/portfolio",
    headers={"Authorization": f"Bearer {access_token}"},
)
res.raise_for_status()
portfolio = res.json()["data"]
```

### Response

```json theme={null}
{
  "message": "Request executed successfully",
  "data": {
    "holdings": [
      {
        "plan": { "id": 12, "identifier": "NDV-MMF", "name": "Ndovu Money Market Fund", "currency": "KES" },
        "units": 41.2,
        "currentValue": 5100,
        "investedToDate": 4950,
        "withdrawnToDate": 0,
        "pendingAmount": 0,
        "reconciledAmount": 0,
        "allTimeReturnValue": 150,
        "allTimeReturnPercentage": 3.03
      }
    ],
    "totals": [
      {
        "currency": "KES",
        "currentValue": 5100,
        "investedToDate": 4950,
        "allTimeReturnValue": 150,
        "allTimeReturnPercentage": 3.03
      }
    ]
  }
}
```

### Holding

All amounts are in the plan's currency.

| Field | Type | Description |
| - | - | - |
| `plan` | object | `id`, `identifier`, `name` and `currency` of the plan. |
| `units` | number | Units held. |
| `currentValue` | number | What the holding is worth now, including money received but not yet invested. |
| `investedToDate` | number | Total invested, after fees. |
| `withdrawnToDate` | number | Total withdrawn. |
| `pendingAmount` | number | Payments started but not yet received. |
| `reconciledAmount` | number | Money received, waiting to be invested. |
| `allTimeReturnValue` | number | Gain or loss since the first investment. |
| `allTimeReturnPercentage` | number | The same, as a percentage of what is invested. |

A holding appears once the customer has value, pending money or received money in the plan.

### Totals

One entry per currency the customer holds. Totals are **never** added up across currencies: a customer with KES and USD holdings has two totals.

| Field | Type | Description |
| - | - | - |
| `currency` | string | The currency. |
| `currentValue` | number | Sum of `currentValue` for this currency's holdings. |
| `investedToDate` | number | Sum of `investedToDate` for this currency's holdings. |
| `allTimeReturnValue` | number | Gain or loss across this currency's holdings. |
| `allTimeReturnPercentage` | number | The same, as a percentage. |

## Transactions

A **Transaction** is one investment instruction on a holding: a deposit being invested, or a fee taken from the holding. A deposit's Transaction is created when its [Payment](https://docs.ndovu.co/payments.md) completes, so a payment that is still pending or has failed has no Transaction.

### List transactions

```
GET /api/v3/partners/{shortName}/customers/{externalUserId}/portfolio/transactions
```

```bash theme={null}
curl "https://be-dev-1.dev.ndovu.co/api/v3/partners/YOUR_SHORT_NAME/customers/cust_8842/portfolio/transactions?limit=20" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
```

| Parameter | Description |
| - | - |
| `planId` | Only transactions on this plan. |
| `limit` | Up to 50. Default 20. |
| `cursor` | The `nextCursor` from the previous page. |

```json theme={null}
{
  "message": "Request executed successfully",
  "data": {
    "data": [
      {
        "transactionId": 48213,
        "type": "DEPOSIT",
        "status": "COMPLETED",
        "reason": null,
        "plan": { "id": 12, "name": "Ndovu Money Market Fund" },
        "amount": 5000,
        "currency": "KES",
        "fee": 50,
        "feePercentage": 1,
        "investedAmount": 4950,
        "units": 41.2,
        "paymentReference": "ACM7F3K9Q",
        "payment": {
          "reference": "ACM7F3K9Q",
          "channel": "BANK_TRANSFER",
          "amount": 5000,
          "finalAmount": 5000,
          "currency": "KES",
          "status": "COMPLETED",
          "mnoReference": null,
          "bankReference": "FT26296ABCD",
          "phoneNumber": null,
          "plan": { "id": 12, "name": "Ndovu Money Market Fund" },
          "transactionId": 48213,
          "createdAt": "2026-10-23T08:00:00.000Z",
          "completedAt": "2026-10-23T11:42:10.000Z"
        },
        "requestedAt": "2026-10-23T11:42:10.000Z"
      }
    ],
    "nextCursor": null
  }
}
```

Newest first. `nextCursor` is `null` on the last page; otherwise pass it as `cursor`.

### Get a transaction

```
GET /api/v3/partners/{shortName}/customers/{externalUserId}/portfolio/transactions/{transactionId}
```

The transaction plus one field:

| Field | Type | Description |
| - | - | - |
| `estimatedArrival` | string | When the investment is expected to complete (`YYYY-MM-DD`). |

### The transaction object

| Field | Type | Description |
| - | - | - |
| `transactionId` | integer | Our ID for the transaction. |
| `type` | string | `DEPOSIT` or `FEE`. See below. |
| `status` | string | See below. |
| `reason` | string \| null | Why an `ARCHIVED` transaction won't complete. `null` otherwise. |
| `plan` | object | `id` and `name` of the plan. |
| `amount` | number | The amount before the fee. |
| `currency` | string | The currency. |
| `fee` | number \| null | The fee taken. |
| `feePercentage` | number \| null | The fee rate, as a percentage. |
| `investedAmount` | number | The amount invested after the fee. |
| `units` | number \| null | Units allocated. `null` until `COMPLETED`. |
| `paymentReference` | string \| null | The funding Payment's reference. `null` for a fee. |
| `payment` | object \| null | The [Payment](https://docs.ndovu.co/payments.md#the-payment-object) that funded it, including its `channel`, `bankReference` (bank transfer) and `mnoReference` (M-Pesa receipt). `null` for a fee. |
| `requestedAt` | string | When the transaction was created. |

### Type

| Type | Meaning |
| - | - |
| `DEPOSIT` | Money invested into the plan. |
| `FEE` | Units taken from the holding to pay Ndovu's management fee. It has no payment. Showing it lets your records add up to the holding's value. |

### Status

| Status | Meaning |
| - | - |
| `PENDING` / `TRANSFER_INCOMPLETE` | Waiting for the money. Rare for your customers, since a deposit's transaction is created once its payment completes. |
| `RECONCILED` | The money arrived and is waiting to be invested. Usually the next business day or two. |
| `COMPLETED` | Invested. `units` is set. |
| `ARCHIVED` | Will never complete. `reason` says why: `PENDING_PAYMENT`, `PENDING_INVESTOR_INFORMATION`, `TRADE_CANCELLED`, `CLIENT_RECALL` or `DISINVESTMENT_CANCELLED_BY_CLIENT`. |

## Errors

| Status | Code | Cause |
| - | - | - |
| `400` | `BAD_REQUEST` | An invalid `planId`, `limit` or `cursor`, or a non-numeric `transactionId`. |
| `404` | `CUSTOMER_NOT_FOUND` | You have no customer with this `externalUserId`. |
| `404` | `TRANSACTION_IS_NOT_FOUND` | No transaction with this ID for this customer. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.