# Uploading documents

> Learn how to upload, download, and combine documents using the Nexpay API.

Documents in Nexpay are used to attach supporting files to payments — such as payer identity documents, purpose proof, and invoices. You must upload documents before referencing them in payment or commission requests.

---

## Before you start

You will need a valid [API key](/docs/api-keys.md) to authenticate your requests.

> **Note — JSON examples show the unwrapped `data` payload**
>
> Every success response on the wire is `{ "data": { ... } }`. The JSON examples in this guide show the inner `data` value — what you get after `const { data } = await response.json()`. The upload endpoint returns `{ data: { documentId: "<uuid>" } }`; reuse that `documentId` when referencing the document elsewhere.

## Uploading a document

To upload a document, send a `POST` request with the file as `multipart/form-data`:

**cURL**

```bash
curl -X POST 'https://api.nexpay.com.au/v2/documents' \
  -H 'X-API-Key: nxp_ck_your-client-id:nxp_sk_your-secret' \
  -F 'file=@passport.jpg'
```

**JavaScript**

```javascript
try {
  const formData = new FormData();
  formData.append('file', fileInput.files[0]);

  const response = await fetch('https://api.nexpay.com.au/v2/documents', {
    method: 'POST',
    headers: {
      'X-API-Key': 'nxp_ck_your-client-id:nxp_sk_your-secret',
    },
    body: formData,
  });

  if (response.ok) {
    const { data } = await response.json();

    console.log(data);
  } else {
    throw new Error(`Request failed with status: ${response.status}`);
  }
} catch (error) {
  console.error(error);
}
```

**PHP (Laravel)**

```php
use Illuminate\Support\Facades\Http;

$response = Http::withHeaders([
    'X-API-Key' => 'nxp_ck_your-client-id:nxp_sk_your-secret',
])->attach('file', file_get_contents('passport.jpg'), 'passport.jpg')
  ->post('https://api.nexpay.com.au/v2/documents');

$data = $response->json('data');

echo $data['documentId'];
```

The response includes the document's `documentId` (a UUID), which you'll use when creating payments or commission requests. The field is named `documentId`, not `id` — pass it verbatim wherever an upstream call asks for `payerIdentityDocumentId`, `purposeProofDocumentId`, etc.

```javascript
{
  "documentId": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
}
```

> **Warning — File size limit**
>
> The maximum file size is **16 MB**. Requests exceeding this limit will be rejected.

### Where document IDs are used

Uploaded document IDs are referenced in other API calls:

| Context | Field | Description |
| --- | --- | --- |
| [Manual payment with splits](/docs/guides/payment-intents-manual.md) | `payerIdentityDocumentId` | Payer's identity document (e.g. passport, driver's license). |
| [Manual payment with splits](/docs/guides/payment-intents-manual.md) | `purposeProofDocumentId` | Proof of payment purpose (e.g. invoice, enrollment letter). **One per recipient on a split**, and every id on the intent must be unique (see "Document uniqueness within an intent" below). |
| [Commissions](/docs/guides/commissions.md) | `invoice` (file upload) | Invoice for commission withdrawal request. |
| [Conversations](/docs/guides/conversations.md) | `attachments` | File attachments in conversation messages. |

---

## Downloading a document

To download a previously uploaded document, use its `documentId`. The response is a binary file stream — do NOT call `response.json()`:

**cURL**

```bash
curl 'https://api.nexpay.com.au/v2/documents/f47ac10b-58cc-4372-a567-0e02b2c3d479' \
  -H 'X-API-Key: nxp_ck_your-client-id:nxp_sk_your-secret' \
  --output downloaded-file
```

**JavaScript**

```javascript
try {
  const response = await fetch('https://api.nexpay.com.au/v2/documents/f47ac10b-58cc-4372-a567-0e02b2c3d479', {
    method: 'GET',
    headers: {
      'X-API-Key': 'nxp_ck_your-client-id:nxp_sk_your-secret',
    },
  });

  if (response.ok) {
    const blob = await response.blob();

    // Save or display the file
    console.log('Downloaded file:', blob.size, 'bytes');
  } else {
    throw new Error(`Request failed with status: ${response.status}`);
  }
} catch (error) {
  console.error(error);
}
```

**PHP (Laravel)**

```php
use Illuminate\Support\Facades\Http;

$response = Http::withHeaders([
    'X-API-Key' => 'nxp_ck_your-client-id:nxp_sk_your-secret',
])->get('https://api.nexpay.com.au/v2/documents/f47ac10b-58cc-4372-a567-0e02b2c3d479');

// Binary file stream — do not call ->json()
$contents = $response->body();

echo 'Downloaded file: ' . strlen($contents) . ' bytes';
```

The response is a binary file stream with `application/octet-stream` content type.

---

## Combining documents

You can merge multiple uploaded documents into a single ZIP or PDF file. This is useful when you need to bundle several supporting documents together:

**cURL**

```bash
curl -X POST 'https://api.nexpay.com.au/v2/documents/combine' \
  -H 'X-API-Key: nxp_ck_your-client-id:nxp_sk_your-secret' \
  -H 'Content-Type: application/json' \
  -d '{
  "documentIds": [
    "f47ac10b-58cc-4372-a567-0e02b2c3d479",
    "a23bc45d-67ef-8901-b234-5c6d7e8f9012"
  ]
}'
```

**JavaScript**

```javascript
try {
  const response = await fetch('https://api.nexpay.com.au/v2/documents/combine', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-API-Key': 'nxp_ck_your-client-id:nxp_sk_your-secret',
    },
    body: JSON.stringify({
      "documentIds": [
        "f47ac10b-58cc-4372-a567-0e02b2c3d479",
        "a23bc45d-67ef-8901-b234-5c6d7e8f9012"
      ]
    }),
  });

  if (response.ok) {
    const { data } = await response.json();

    console.log(data);
  } else {
    throw new Error(`Request failed with status: ${response.status}`);
  }
} catch (error) {
  console.error(error);
}
```

**PHP (Laravel)**

```php
use Illuminate\Support\Facades\Http;

$response = Http::withHeaders([
    'X-API-Key' => 'nxp_ck_your-client-id:nxp_sk_your-secret',
])->post('https://api.nexpay.com.au/v2/documents/combine', [
    'documentIds' => [
        'f47ac10b-58cc-4372-a567-0e02b2c3d479',
        'a23bc45d-67ef-8901-b234-5c6d7e8f9012',
    ],
]);

$data = $response->json('data');

echo $data['id'];
```

The response returns a new document with its own `id`:

```javascript
{
  "id": "d89ef012-34ab-5678-cdef-901234567890",
  "fileName": "combined.pdf",
  "contentType": "application/pdf",
  "fileSize": 409600
}
```

### Combine fields

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `documentIds` | string[] | Yes | Array of document UUIDs to combine. Must contain at least 1 document. |

---

## Document uniqueness within an intent

A document can be referenced from multiple *contexts* (a payment, a conversation, a commission). But **within a single payment intent, every document id must be distinct** — each recipient's `purposeProofDocumentId` and the `payerIdentityDocumentId` must differ. Re-using one `documentId` across two recipients fails the `documents.uniqueDocumentIds` rule at submit (`[GEN9002]`). When the same underlying file legitimately backs two legs (for example a single enrolment letter), upload it once per leg so each gets its own `documentId`.

## Things to know

* Documents are scoped to your tenant and cannot be accessed across organizations.
* All document IDs are UUID v4 format.
* The combine endpoint creates a new document — the original documents remain available.
