> ## 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`

<Steps>
  <Step title="Initialize the sync timestamp" titleSize="h3">
    Same approach as client invoices: load the last successful sync timestamp from your database and capture the current time.

    <Tabs>
      <Tab title="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")
        ```
      </Tab>

      <Tab title="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();
        ```
      </Tab>
    </Tabs>
  </Step>

  <Step title="Fetch all supplier invoices updated in the time window" titleSize="h3">
    List supplier invoices updated between your last sync and now, sorted chronologically and paginated.

    Endpoint: [List supplier invoices](/api-reference/business-api/expense-management/supplier-invoices/list-supplier-invoices)

    <Info>
      **OAuth scope required:** `supplier_invoice.read`.
    </Info>

    <Info>
      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]`.
    </Info>

    <Tabs>
      <Tab title="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")
        ```
      </Tab>

      <Tab title="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`);
        ```
      </Tab>
    </Tabs>
  </Step>

  <Step title="Retrieve and download attachments" titleSize="h3">
    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](/api-reference/business-api/expense-management/attachments/retrieve-an-attachment)

    <Info>
      **OAuth scope required:** `attachment.read`.
    </Info>

    <Tabs>
      <Tab title="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']}")
        ```
      </Tab>

      <Tab title="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}`);
        }
        ```
      </Tab>
    </Tabs>
  </Step>

  <Step title="Store the sync timestamp" titleSize="h3">
    Persist the new sync timestamp only after full success.

    <Tabs>
      <Tab title="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}")
        ```
      </Tab>

      <Tab title="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}`);
        ```
      </Tab>
    </Tabs>
  </Step>
</Steps>

**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
```

<Check>
  We now have an up-to-date local copy of all supplier invoices and their attachments for the given time window.
</Check>

***
