List the invoices due this week

The approved supplier invoices to pay in the next days, overdue ones first, with the total still due per currency.

See which approved supplier invoices you have to pay in the next seven days — and which are already overdue — with the amount still due and the total per currency.

2026-09-26 (overdue)  Office Supplies Ltd            FA-2026-3391          1,944.00 EUR
2026-10-02            Cloud Hosting Inc              INV-88412             2,490.00 EUR

2 invoices due by 2026-10-06: 4,434.00 EUR

Scopes: experimental:invoice:read, and user:read to find your company ID with a company-level key. Set-up: Running the recipes.

📘

Experimental

Invoice endpoints are experimental: their scope is granted on request, and they may still change. See Versioning & Deprecation.

With Postman

In the Invoices and intakes folder, open List invoices and set these query parameters — change the date to seven days from today:

ParameterValue
dueBefore2026-10-06
paymentStatusunpaid, and a second paymentStatus row with partiallyPaid
approvalStatusapproved
sortBydueDate
sortasc

Each invoice shows its supplier.name, invoiceNumber, dueDate and amountDue — what remains to pay, in cents. The company ID in the path comes from your environment's companyId; with a company-level key, Get Users shows it as companyId on every user.

With an AI assistant

Which approved supplier invoices are due in the next 7 days or already overdue? Give me the supplier, the amount still due and the due date, and the total per currency.

With a script

Save it as due_invoices.py, then run python3 due_invoices.py — or python3 due_invoices.py 14 for the next 14 days.

"""List the supplier invoices to pay in the next N days (default 7), overdue ones included.

Usage: python3 due_invoices.py [DAYS]
Needs SPENDESK_CLIENT_ID and SPENDESK_CLIENT_SECRET; SPENDESK_COMPANY_ID with an organisation-level key.
Scopes: experimental:invoice:read, user:read (to find your company ID with a company-level key).
"""
import base64, datetime as dt, json, os, sys, urllib.parse, urllib.request

BASE = os.environ.get("SPENDESK_BASE_URL", "https://public-api.spendesk.com")
COMPANY = os.environ.get("SPENDESK_COMPANY_ID")


def token():
    creds = base64.b64encode(f"{os.environ['SPENDESK_CLIENT_ID']}:{os.environ['SPENDESK_CLIENT_SECRET']}".encode()).decode()
    req = urllib.request.Request(BASE + "/v1/auth/token", data=b"grant_type=client_credentials",
                                 headers={"Authorization": "Basic " + creds, "Content-Type": "application/x-www-form-urlencoded"})
    return json.load(urllib.request.urlopen(req))["access_token"]


TOKEN = token()


def api(method, path, query=None, body=None):
    url = BASE + path + ("?" + urllib.parse.urlencode(query, doseq=True) if query else "")
    headers = {"Authorization": "Bearer " + TOKEN, "Content-Type": "application/json"}
    if COMPANY:
        headers["X-Company-Id"] = COMPANY
    req = urllib.request.Request(url, method=method, headers=headers, data=json.dumps(body).encode() if body else None)
    return json.load(urllib.request.urlopen(req))


# 1. The company ID goes in the path of v2 endpoints. A company-level key reads it from any user.
company = COMPANY or api("GET", "/v1/users", {"pageSize": 1})["data"][0]["companyId"]

# 2. Approved invoices not fully paid, due before the cutoff, earliest first.
days = int(sys.argv[1]) if len(sys.argv) > 1 else 7
cutoff = dt.date.today() + dt.timedelta(days=days)
query = {"dueBefore": str(cutoff), "paymentStatus": ["unpaid", "partiallyPaid"], "approvalStatus": ["approved"],
         "sortBy": "dueDate", "sort": "asc", "limit": 100}
invoices, cursor = [], None
while True:
    page = api("GET", f"/v2/companies/{company}/invoices", dict(query, **({"cursor": cursor} if cursor else {})))
    invoices += page["data"]
    cursor = page["meta"]["pagination"]["nextCursor"]
    if not cursor:
        break

# 3. Print them, then the total still due per currency. Amounts are in cents: divide by 100.
totals = {}
for inv in invoices:
    late = " (overdue)" if inv["dueDate"] < str(dt.date.today()) else ""
    print(f"{inv['dueDate']}{late}  {inv['supplier']['name']:<30} {inv['invoiceNumber']:<20} "
          f"{inv['amountDue'] / 100:>12,.2f} {inv['currency']}")
    totals[inv["currency"]] = totals.get(inv["currency"], 0) + inv["amountDue"]
print(f"\n{len(invoices)} invoices due by {cutoff}:",
      ", ".join(f"{amount / 100:,.2f} {currency}" for currency, amount in sorted(totals.items())) or "nothing")

Good to know

  • Only approved invoices are listed. Remove the approvalStatus filter to also see those still waiting for approval.
  • Partially paid invoices are included, with only what remains in amountDue.
  • Overdue invoices are included: dueBefore has no lower bound. Add dueAfter with today's date to leave them out.
  • Several companies: with an organisation-level key, run it once per company, changing SPENDESK_COMPANY_ID.

The request is List invoices; the company in the path works as described in Organisation-level access.