# Export reference

Executive summary
**This page defines the behaviors shared by every export: authentication, access control, output formats, financial precision, operational limits, and error responses.**

- Requests use bearer-token authentication, and the service authorizes them against the target domain only.
- Reports are returned as CSV or JSON, with all financial values as strings.
- Per-request limits protect the service and keep exports predictable.


## Authentication

Every request must include a valid JSON Web Token (JWT) in the `Authorization` header:


```
Authorization: Bearer <jwt>
```

The service uses a **user-based** authentication model. There's no separate machine credential flow: you must register even bot and service accounts as users with the appropriate roles in each domain they need to access. This ensures every export traces back to a specific, verifiable identity.

The token subject identifies the requesting user in `providerId:loginId` form. The service records this identity as the request author in every export's metadata.

## Access control

Authorization is **target-domain-only**. To generate a report for a domain, all of the following must be true:

1. You have a profile in the exact target domain.
2. Your user account is unlocked.
3. The target domain is unlocked.
4. One of your roles grants read access to the relevant entity type in that domain.


The entity type depends on the report:

| Report | Required read access |
|  --- | --- |
| Position Report | `accounts` |
| Movement Report | `transactions` |


The service verifies domain access *before* running any data query. If you lack access, it contacts neither the balance source nor the database.

### No inheritance from parent domains

A profile in a parent domain does **not** grant access to its children. You must hold a profile with a matching role in each domain you want to export. This prevents broad, unintended access through high-level parent profiles.

### Including child domains

When you set `includeChildDomains` to `true`, the service resolves the full domain tree and checks each child domain independently, using the same target-domain-only rule. It includes children you can access and silently omits children you can't access. You receive a valid report containing only the domains you're entitled to, with no error about the domains it excluded.

## Common behaviors

### Request method and content type

Both endpoints accept `POST` requests with a `Content-Type` of `application/json`.

### Response headers

| Header | Description |
|  --- | --- |
| `Content-Type` | `text/csv; charset=utf-8` for CSV, or `application/json` for JSON. |
| `Content-Disposition` | `attachment` with a generated filename based on the report type, domain, and range. |
| `X-Export-Id` | The UUID of the export, for support tracing and audit. |


### Duplicate request protection

If you submit the same parameters again within 60 seconds while a previous export is still processing, the service returns `429`. This prevents an accidental double-submission from triggering redundant work.

### Concurrency limit

You can have at most **2** exports processing at any time. A third concurrent request returns `429`. Wait for an in-flight export to finish, then retry.

## Output formats

Select the format with the `format` field in the request body.

### CSV

The service encodes CSV files as UTF-8 with a byte order mark (BOM) for spreadsheet compatibility, and uses `\r\n` (CRLF) line endings. The structure is:

| Line | Content |
|  --- | --- |
| 1 | The metadata header, prefixed with `# metadata:` followed by a JSON object. |
| 2 | The column headers, matching the report's column contract. |
| 3+ | One data row per record. |


All financial values are quoted strings (for example, `"0.000000000000000001"`). This prevents spreadsheet software and CSV parsers from silently truncating high-precision decimals.

### JSON

JSON responses wrap the data in a metadata envelope:


```json
{
  "metadata": {
    "exportId": "uuid",
    "requestAuthor": "providerId:loginId",
    "generatedAt": "ISO-8601",
    "filters": { },
    "recordCount": 1234,
    "controlTotals": { }
  },
  "data": [ ]
}
```

The metadata also carries the report's time parameters: `asOfTimestamp` for the Position Report, and `dateRangeStart` and `dateRangeEnd` for the Movement Report.

Financial values in JSON are strings, never bare numbers. Always parse them as strings.

## Financial precision

The service exports every financial value as a decimal string in the asset's native unit, with precision matching the asset's decimal places. It never exports raw blockchain denominations (except the Movement Report's explicit `rawTransactionValue` column) or scientific notation.

| Asset | Raw value (stored) | Decimals | Exported value |
|  --- | --- | --- | --- |
| XRP | `3000000` | 6 | `3.000000` |
| ETH | `50000000000000000` | 18 | `0.050000000000000000` |
| BTC | `100000000` | 8 | `1.00000000` |


Always parse financial values as strings
Financial values are strings in both CSV and JSON. Never parse them as floating-point numbers. Standard double-precision numbers lose accuracy beyond roughly 15 significant digits, and some assets carry many more decimal places—ETH has 18, and some ledgers support far more. Parsing as a number silently corrupts the value.

The service rounds down, consistent with banking practice: an export never reports more value than actually exists.

## Operational limits

| Limit | Value |
|  --- | --- |
| Maximum date range (Movement Report) | 30 days |
| Maximum filter array size (`vaultIds`, `tickerIds`, `accountIds`) | 1,000 entries per array |
| Maximum transfers per Movement Report | 100,000 rows |
| Duplicate detection window | 60 seconds |
| Maximum concurrent exports per user | 2 |
| Maximum rows per Position Report | 100,000 rows |
| Maximum accessible domains per request (with `includeChildDomains`) | 100 domains |
| Stale export release window | 5 minutes |


## Error responses

Errors return a standard HTTP status code with a client-safe JSON body. Error messages never expose internal details such as query fragments, stack traces, service names, or file paths. The service logs detailed diagnostics on the server against the export ID for correlation.

| Code | Meaning | When it occurs |
|  --- | --- | --- |
| `400` | Bad Request | A validation failure: a missing required field, a malformed UUID or timestamp, a future timestamp, a date range over 30 days, an end before start, an empty status array, or a filter array over 1,000 entries. |
| `401` | Unauthorized | The JWT is missing or invalid. |
| `403` | Forbidden | You lack access to the target domain: no profile, a locked user, a locked domain, or an insufficient role. |
| `413` | Payload Too Large | The request scope exceeds a report limit — either more than 100 accessible domains, or a result that would exceed 100,000 rows. Narrow the range or add filters and retry. |
| `429` | Too Many Requests | A duplicate request within 60 seconds while a previous export is still processing, or you already have 2 exports processing. |
| `502` | Bad Gateway | The upstream balance source returned an error (Position Report only). |
| `504` | Gateway Timeout | The export exceeded the service's internal time budget. |


### Error response body

Every error body carries a numeric `statusCode`, a short `error` label, and a `message`:


```json
{
  "message": "Access denied",
  "error": "Forbidden",
  "statusCode": 403
}
```

For `400` validation failures, `message` is an array of one or more client-safe validation strings that name the offending field:


```json
{
  "message": ["asOfTimestamp must not be in the future"],
  "error": "Bad Request",
  "statusCode": 400
}
```

Every `message` value is client-safe: it never exposes internal details such as query fragments, stack traces, service names, or file paths.

## Next steps

| Page | Description |
|  --- | --- |
| [Data export overview](/products/custody/data-export/overview) | Review report types, export metadata, and control totals. |
| [Position Report](/products/custody/data-export/position-report) | Generate a point-in-time balance snapshot. |
| [Movement Report](/products/custody/data-export/movement-report) | Generate a transaction history for a date range. |