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

# How to bulk upload supplier invoices

When your platform receives bills or purchase invoices, pushing them directly into Qonto saves your team from re-uploading files manually and keeps expense tracking centralised. In this guide, we will upload a supplier invoice file via the bulk endpoint and verify the response to confirm it was ingested successfully.

> **Example:** A procurement platform forwards vendor invoices received by email directly into Qonto as soon as they arrive, ready for approval without any manual upload.

**Prerequisites:**

* A PDF invoice file ready to upload (max 10 MB).
* Scope: `supplier_invoice.write`

### 1. Upload the invoice file

Send the PDF using `POST /v2/supplier_invoices/bulk`. You can upload the raw file binary or reference an existing attachment by its ID.

Endpoint: [Create supplier invoices](https://docs.qonto.com/api-reference/business-api/spend-management/supplier-invoices/create-supplier-invoices.md)

> [!NOTE]
>
> **OAuth scope required:** `supplier_invoice.write`.

> [!NOTE]
>
> Maximum **20 invoices per request**. Each invoice requires a unique `idempotency_key` (UUID recommended) to prevent duplicate uploads. See [Idempotent requests](https://docs.qonto.com/api-reference/business-api/introduction/idempotency.md).

> [!WARNING]
>
> This endpoint returns HTTP 200 even when individual invoices fail to process. Always inspect the `errors` array in the response body for each invoice when you receive a `200`.
>
> *Authentication failures still return `401`, permission issues return `403`, and server-side errors return `500`.*

> [!WARNING]
>
> You must provide **either** `file` or `attachment_id` for each invoice, not both. Including both will result in an error.

**Python**

```python theme={null}
import uuid
import requests

BASE_URL = "https://thirdparty.qonto.com/v2"
headers = {"Authorization": "Bearer {your_access_token}"}

with open("./invoice.pdf", "rb") as f:
    response = requests.post(
        f"{BASE_URL}/supplier_invoices/bulk",
        headers=headers,
        data={
            "supplier_invoices[][idempotency_key]": str(uuid.uuid4()),
            "source": "integration",
        },
        files={
            "supplier_invoices[][file]": ("invoice.pdf", f, "application/pdf"),
        },
    )

# Always 200 — check errors in body
result = response.json()
```

**Node.js**

```javascript theme={null}
import { readFile } from "node:fs/promises";
import { randomUUID } from "node:crypto";

const BASE_URL = "https://thirdparty.qonto.com/v2";
const headers = { Authorization: "Bearer {your_access_token}" };

const fileBuffer = await readFile("./invoice.pdf");
const formData = new FormData();
formData.append(
  "supplier_invoices[][file]",
  new Blob([fileBuffer], { type: "application/pdf" }),
  "invoice.pdf"
);
formData.append("supplier_invoices[][idempotency_key]", randomUUID());
formData.append("source", "integration");

const response = await fetch(`${BASE_URL}/supplier_invoices/bulk`, {
  method: "POST",
  headers, // Do NOT set Content-Type — fetch sets it automatically with the boundary
  body: formData,
});

// Always 200 — check errors in body
const result = await response.json();
```

### 2. Check the response for errors

Inspect the response to confirm each invoice was processed successfully.

**Python**

```python theme={null}
supplier_invoices = result.get("supplier_invoices", [])

for invoice in supplier_invoices:
    errors = invoice.get("errors", [])
    if errors:
        print(f"Failed: {errors}")
    else:
        print(f"Uploaded successfully: {invoice.get('id')}")
```

**Node.js**

```javascript theme={null}
const supplierInvoices = result.supplier_invoices ?? [];

for (const invoice of supplierInvoices) {
  if (invoice.errors?.length) {
    console.error("Failed:", invoice.errors);
  } else {
    console.log(`Uploaded successfully: ${invoice.id}`);
  }
}
```

Common error codes to handle:

| Code | Cause |
| - | - |
| `invalid` | File too large or unsupported content type |
| `required` | Missing `idempotency_key` |
| `limit_reached` | More than 20 invoices in the request |
| `internal_server_error` | Processing failed on Qonto's side |

**Example output:**

```
[Step 1] POST /supplier_invoices/bulk  (file: sample_invoice.pdf)
  HTTP status: 200

[Step 2] Check response
  Uploaded successfully: 019d0705-xxxx-xxxx-xxxx-xxxxxxxxxxxx
```

> [!TIP]
>
> We now have the supplier invoice uploaded to Qonto and queued for review. It will appear in the "Supplier invoices" section of the Qonto app with status `to_review`.

***
