# Position Report

Executive summary
**The Position Report is a point-in-time balance snapshot. Each row reports one account-and-asset balance as of a timestamp you choose.**

- Send a `POST` request to `/v1/exports/position` with your target domain and an `asOfTimestamp`.
- Filter by vault, ticker, and account, and choose whether to include child domains and zero balances.
- The service returns 18 fixed columns in CSV or JSON, plus balance control totals.


Use the Position Report to verify end-of-day balances against your core systems, calculate regulatory capital, and produce tax reporting. Each row represents the balance of one account for one asset (a single account-and-ticker pair) at the point in time you request.

## Prerequisites

To generate a Position Report, you need the following:

| Prerequisite | Additional information |
|  --- | --- |
| A valid bearer token (JWT) | See [Authentication](/products/custody/data-export/reference#authentication). |
| A profile in the target domain with a role that grants read access to accounts | See [Access control](/products/custody/data-export/reference#access-control). |
| The target domain's ID | The domain you want to report on. |


## Endpoint


```
POST /v1/exports/position
```

The service generates a balance snapshot for every account in the target domain as of `asOfTimestamp`.

## Request fields

Send the request body as `application/json`.

| Field | Type | Required | Default | Description |
|  --- | --- | --- | --- | --- |
| `domainId` | string (UUID) | Yes | — | The target domain. You must have read access to accounts in this domain. |
| `asOfTimestamp` | string (ISO 8601) | Yes | — | The point in time for the balance snapshot. Can't be in the future. |
| `includeChildDomains` | boolean | No | `false` | Include balances from descendant domains. The service access-checks each child individually. |
| `vaultIds` | string[] (UUID) | No | — | Include only the listed vaults. Maximum 1,000 entries. |
| `tickerIds` | string[] (UUID) | No | — | Include only the listed assets. Maximum 1,000 entries. |
| `accountIds` | string[] (UUID) | No | — | Include only the listed accounts. Maximum 1,000 entries. |
| `includeZeroBalances` | boolean | No | `false` | Include accounts that hold a zero balance. |
| `format` | `"CSV"` | `"JSON"` | Yes | — | The output format. |


## Example request


```bash
curl -X POST "https://{host}/v1/exports/position" \
  -H "Authorization: Bearer <jwt>" \
  -H "Content-Type: application/json" \
  -d '{
    "domainId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "asOfTimestamp": "2026-06-15T23:59:59Z",
    "includeChildDomains": true,
    "includeZeroBalances": false,
    "format": "CSV"
  }'
```

The response is returned as a single file with a `Content-Disposition` attachment header and an `X-Export-Id` header for tracing. For the response structure of each format, see [Output formats](/products/custody/data-export/reference#output-formats).

## Output columns

Each row represents one account-and-asset balance. Column order is a fixed contract. The `domainId` and `domainName` columns are per-balance: they identify the domain that owns each balance, which can differ from the domain you requested when you include child domains.

| # | Column | Type | Nullable | Description |
|  --- | --- | --- | --- | --- |
| 1 | `domainId` | string (UUID) | No | The domain that owns this balance. |
| 2 | `domainName` | string | No | The domain's display name. |
| 3 | `vaultType` | string | No | The vault's backing type, passed through from the core system, such as `MPC` or `HSM`. Not a fixed enumeration. |
| 4 | `vaultId` | string (UUID) | No | The vault identifier. |
| 5 | `vaultName` | string | No | The vault's display name. |
| 6 | `accountId` | string (UUID) | No | The account identifier. |
| 7 | `accountName` | string | No | The account's display name. |
| 8 | `accountType` | string | No | The account type, currently `vault` or `gas station`. Additional types are planned for later phases. |
| 9 | `ledgerName` | string | No | The blockchain network name as configured by the operator, such as `XRPL Testnet August 2024` or `Ethereum Mainnet`. |
| 10 | `addressOnLedger` | string | No | The on-chain address for this account. |
| 11 | `tickerId` | string (UUID) | No | The asset identifier. |
| 12 | `tickerKind` | string | No | The asset kind. One of `Native`, `Token`, or `Contract`. |
| 13 | `tickerName` | string | No | The full asset name, such as `Ether`. |
| 14 | `tickerSymbol` | string | Yes | The asset symbol, such as `ETH`. Empty if not set. |
| 15 | `reservedBalance` | string (decimal) | No | The reserved balance at `asOfTimestamp`. |
| 16 | `quarantineBalance` | string (decimal) | No | The quarantined balance at `asOfTimestamp`. |
| 17 | `totalBalance` | string (decimal) | No | The total balance at `asOfTimestamp`. |
| 18 | `ledgerBalanceTimestamp` | string (ISO 8601) | Yes | When the service last wrote the balance. Empty on zero-balance rows (no contributing entries as of the timestamp). This is a database write time, not the on-chain block time. |


Balance columns (`reservedBalance`, `quarantineBalance`, `totalBalance`) are decimal strings in the asset's native unit. In CSV, the service quotes them to preserve precision. Always parse them as strings. See [Financial precision](/products/custody/data-export/reference#financial-precision).

## Control totals

The metadata header includes the following control totals, each a sum across every row in the report. Use them to verify that your ingested file is complete and uncorrupted. For how to verify, see [Control totals](/products/custody/data-export/overview#control-totals).

| Field | Description |
|  --- | --- |
| `balanceTotalAmount` | The sum of all `totalBalance` values. |
| `balanceReservedAmount` | The sum of all `reservedBalance` values. |
| `balanceQuarantinedAmount` | The sum of all `quarantineBalance` values. |


## Errors

If the service returns an error, the response body is a client-safe JSON object and never exposes internal details. Common cases for the Position Report include:

- **`400`**: A required field is missing, a UUID or timestamp is malformed, `asOfTimestamp` is in the future, or a filter array exceeds 1,000 entries.
- **`401`**: The JWT is missing or invalid.
- **`403`**: You lack access to the target domain.
- **`413`**: The request scope exceeds a report limit — either more than 100 accessible domains, or a result that would exceed 100,000 rows.
- **`429`**: A duplicate request within 60 seconds while a previous export is still processing, or you already have 2 exports processing.
- **`502`**: The upstream balance source is temporarily unavailable.


For the full list, see [Error responses](/products/custody/data-export/reference#error-responses).

## Next steps

| Page | Description |
|  --- | --- |
| [Movement Report](/products/custody/data-export/movement-report) | Generate a transaction history for a date range. |
| [Export reference](/products/custody/data-export/reference) | Look up output formats, financial precision rules, access control, operational limits, and error responses. |
| [Data export overview](/products/custody/data-export/overview) | Review report types, export metadata, and control totals. |