> ## 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 sync supplier invoices and attachments

Automatically pulling supplier invoices into your system removes the need for manual data entry and gives your finance team real-time visibility into what is owed and when. In this guide, we will fetch all supplier invoices updated since the last sync, paginate through results, and download their attachments; using the same polling pattern as client invoices, with different endpoints and filters.

> **Example:** A spend management tool syncs supplier invoices from Qonto nightly to match them against purchase orders and flag any discrepancies before payment.

**Prerequisites:**

* At least one supplier invoice exists in Qonto.
* Scopes: `supplier_invoice.read`, `attachment.read`

### 1. Initialize the sync timestamp

Same approach as client invoices: load the last successful sync timestamp from your database and capture the current time.

**Python**

```python theme={null}
from datetime import datetime, timezone

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

last_sync_at = "2026-01-01T00:00:00Z"  # Load from your database
current_sync_at = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
```

**Node.js**

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

const lastSyncAt = "2026-01-01T00:00:00Z"; // Load from your database
const currentSyncAt = new Date().toISOString();
```

### 2. Fetch all supplier invoices updated in the time window

List supplier invoices updated between your last sync and now, sorted chronologically and paginated.

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

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

> [!NOTE]
>
> Additional filters are available: `filter[status]` (e.g., `to_review`, `to_pay`, `paid`), `filter[due_date]`, and `filter[created_at_from]` / `filter[created_at_to]`.

**Python**

```python theme={null}
def fetch_all_supplier_invoices(last_sync_at, current_sync_at):
    invoices = []
    page = 1

    while True:
        response = requests.get(
            f"{BASE_URL}/supplier_invoices",
            headers=headers,
            params={
                "filter[updated_at_from]": last_sync_at,
                "filter[updated_at_to]": current_sync_at,
                "sort_by": "updated_at:asc",  # Process chronologically
                "per_page": 100,
                "page": page,
            },
        )
        response.raise_for_status()
        data = response.json()

        invoices.extend(data["supplier_invoices"])

        if data["meta"]["next_page"] is None:
            break
        page = data["meta"]["next_page"]

    return invoices

invoices = fetch_all_supplier_invoices(last_sync_at, current_sync_at)
print(f"Fetched {len(invoices)} supplier invoices")
```

**Node.js**

```javascript theme={null}
async function fetchAllSupplierInvoices(lastSyncAt, currentSyncAt) {
  const invoices = [];
  let page = 1;

  while (true) {
    const params = new URLSearchParams({
      "filter[updated_at_from]": lastSyncAt,
      "filter[updated_at_to]": currentSyncAt,
      sort_by: "updated_at:asc", // Process chronologically
      per_page: 100,
      page,
    });
    const response = await fetch(`${BASE_URL}/supplier_invoices?${params}`, { headers });
    if (!response.ok) throw new Error(`HTTP ${response.status}`);

    const data = await response.json();
    invoices.push(...data.supplier_invoices);

    if (data.meta.next_page === null) break;
    page = data.meta.next_page;
  }

  return invoices;
}

const invoices = await fetchAllSupplierInvoices(lastSyncAt, currentSyncAt);
console.log(`Fetched ${invoices.length} supplier invoices`);
```

### 3. Retrieve and download attachments

For each invoice with an `attachment_id`, fetch the attachment and download the file. The attachment URL expires in 30 minutes — download immediately.

Endpoint: [Retrieve an attachment](https://docs.qonto.com/api-reference/business-api/transactions-statements/attachments/retrieve-an-attachment.md)

> [!NOTE]
>
> **OAuth scope required:** `attachment.read`.

**Python**

```python theme={null}
import os

for invoice in invoices:
    if not invoice.get("attachment_id"):
        continue

    attachment_response = requests.get(
        f"{BASE_URL}/attachments/{invoice['attachment_id']}",
        headers=headers,
    )
    attachment_response.raise_for_status()
    attachment = attachment_response.json()["attachment"]

    file_response = requests.get(attachment["url"])
    file_response.raise_for_status()

    os.makedirs("./supplier_invoices", exist_ok=True)
    with open(f"./supplier_invoices/{attachment['file_name']}", "wb") as f:
        f.write(file_response.content)

    print(f"Downloaded: {attachment['file_name']}")
```

**Node.js**

```javascript theme={null}
import { writeFile, mkdir } from "node:fs/promises";

for (const invoice of invoices) {
  if (!invoice.attachment_id) continue;

  const attachmentResponse = await fetch(
    `${BASE_URL}/attachments/${invoice.attachment_id}`,
    { headers }
  );
  if (!attachmentResponse.ok) throw new Error(`HTTP ${attachmentResponse.status}`);

  const { attachment } = await attachmentResponse.json();

  const fileResponse = await fetch(attachment.url);
  await mkdir("./supplier_invoices", { recursive: true });
  const buffer = Buffer.from(await fileResponse.arrayBuffer());
  await writeFile(`./supplier_invoices/${attachment.file_name}`, buffer);

  console.log(`Downloaded: ${attachment.file_name}`);
}
```

### 4. Store the sync timestamp

Persist the new sync timestamp only after full success.

**Python**

```python theme={null}
save_to_db("last_supplier_invoice_sync_at", current_sync_at) # Use your own tools here
print(f"Sync complete. Next run will start from: {current_sync_at}")
```

**Node.js**

```javascript theme={null}
await saveToDB("last_supplier_invoice_sync_at", currentSyncAt); // Use your own tools here
console.log(`Sync complete. Next run will start from: ${currentSyncAt}`);
```

**Example output:**

```
[Step 1] Initialize sync timestamps
  lastSyncAt:    2026-03-18T00:00:00Z
  currentSyncAt: 2026-03-19T16:50:12.227Z

[Step 2] GET /supplier_invoices (paginated)
  Fetched 2 supplier invoices

[Step 3] Download attachments
  Downloaded: supplier_invoice_1.pdf
  Downloaded: supplier_invoice_2.pdf

[Step 4] Store sync timestamp
  Sync complete. Next run will start from: 2026-03-19T16:50:12.22
```

> [!TIP]
>
> We now have an up-to-date local copy of all supplier invoices and their attachments for the given time window.

***
