---
title: "Billing Invoices"
method: GET
path: "/api/billing/invoices"
tags: ["billing", "team"]
---

# Billing Invoices

`GET /api/billing/invoices`

## Request

- Base URL: `https://www.oxfordledge.com` (the document declares no server; this is the origin it was published from)
- URL: `https://www.oxfordledge.com/api/billing/invoices`
- Auth: none declared

List recent Stripe invoices for the authenticated user.

Up to 20 most recent invoices, newest first. Returns empty list +
has_stripe_customer=False for free-tier users (no Stripe customer
record yet) — the FE branches on this for a friendly empty state.

503 on Stripe API outage (`reason='unavailable'`); the FE wraps
that with a 'Try again in a moment' toast rather than treating it
as a billing error.

## Response `200`

Successful Response

- BillingInvoicesResponse: Wire shape for GET /api/billing/invoices. `has_stripe_customer` distinguishes "no invoices yet" (free tier or just-paid first invoice not yet generated) from "we can't reach Stripe" (which uses 503 instead).
  - `invoices` InvoiceItem[], required: Up to 20 most recent invoices, newest first. Empty list when the user has no Stripe customer record yet (free tier) or no invoices have been generated.
    - `id` string, required: Stripe invoice ID (in_xxx).
    - `number` string, nullable: Human-readable invoice number (Stripe assigns; absent for drafts).
    - `amount_paid_cents` integer, required: Amount paid by the user, in cents. Use over amount_due since paid is the post-payment authoritative value.
    - `currency` string, required: ISO 4217 currency code (e.g., 'usd').
    - `status` string, required: Invoice status: 'draft', 'open', 'paid', 'void', 'uncollectible'. UI hides drafts; renders the rest with status-specific styling.
    - `hosted_invoice_url` string, nullable: Stripe-hosted page where the user can view + pay the invoice. Absent on void or uncollectible invoices.
    - `invoice_pdf` string, nullable: Direct PDF download URL. Absent on drafts.
    - `created_iso` string, required: Invoice creation timestamp, ISO-8601 UTC.
  - `count` integer, required: Length of `invoices` (frontend uses for empty-state branching).
  - `has_stripe_customer` boolean, required: True iff the user has a stripe_customer_id in user_billing. STRIPE-LINKAGE signal ONLY — NOT a tier signal. A comped / admin / founder account is a high tier with no Stripe customer. The FE branches the empty state on account_tier, not this field (CHAOS audit 2026-05-17).
  - `account_tier` string, nullable: Canonical users.tier for the authenticated user (e.g. 'free', 'plus', 'power', 'plus_trial'). None when unresolved (no row / NULL / PG-down / transient error) — the honest 'unknown' the FE renders as a neutral state, NEVER as 'Learner (Free)'. display-only, NOT an authz signal: server gates resolve session->users.tier independently of this body.

---

[API](https://skmtc.dev/oxfordledge/apis/oxford-ledge-api.md) · [All operations](https://skmtc.dev/oxfordledge/apis/oxford-ledge-api/llms.txt) · [OpenAPI document](https://skmtc.dev/oxfordledge/apis/oxford-ledge-api/revisions/e832a1260f0b?raw)
