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.
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
Initialize the sync timestamp
Same approach as client invoices: load the last successful sync timestamp from your database and capture the current time.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")
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();
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 invoicesOAuth scope required: supplier_invoice.read.
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].
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")
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`);
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 attachmentOAuth scope required: attachment.read.
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']}")
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}`);
}
Store the sync timestamp
Persist the new sync timestamp only after full success.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}")
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
We now have an up-to-date local copy of all supplier invoices and their attachments for the given time window.