If you're sending invoices through Stripe, you need visibility into what's paid, what's overdue, and what's outstanding. Stripe's Invoices API and webhook system let you track this in real time, whether you're managing a handful of invoices or thousands.
Find Your Invoices in Stripe
Start with the Dashboard to understand where invoice data lives.
Navigate to the Invoices section
Log into your Stripe Dashboard and go to Invoicing > Invoices. You'll see all invoices with their status, customer, amount, and payment date. Filter by status, customer, or date range to find what you need.
Understand invoice statuses
Stripe invoices have five statuses: draft (not sent), open (sent, awaiting payment), paid (payment collected), void (cancelled), and uncollectible (failed after retries). Focus on open and uncollectible — those need action.
// Invoice statuses in Stripe
const invoiceStatuses = {
draft: 'not yet sent',
open: 'sent, waiting for payment',
paid: 'payment received',
void: 'cancelled',
uncollectible: 'payment failed'
};Query Invoices via the API
To track invoicing at scale, use the Invoices API directly.
List invoices for a customer
Call stripe.invoices.list() with a customer filter to retrieve all invoices for that customer. Check the status and paid fields to see what's outstanding.
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const invoices = await stripe.invoices.list({
customer: 'cus_12345',
limit: 100
});
invoices.data.forEach(invoice => {
console.log(`${invoice.id}: ${invoice.status}, Paid: ${invoice.paid}`);
});Filter invoices by status
Add a status filter to pull only the invoices that matter. For unpaid, use open. For failed payments, use uncollectible. You can also sort by date to find recent invoices.
// Get all open (unpaid) invoices
const openInvoices = await stripe.invoices.list({
status: 'open',
limit: 100
});
// Get paid invoices from last 30 days
const paidInvoices = await stripe.invoices.list({
status: 'paid',
created: {
gte: Math.floor(Date.now() / 1000) - (30 * 24 * 60 * 60)
}
});Calculate unpaid and overdue totals
Sum the amount_due field across invoices to find total unpaid. Check the due_date field to identify overdue invoices. Remember: amounts are in cents, so divide by 100 for display.
const invoices = await stripe.invoices.list({ status: 'open' });
let totalUnpaid = 0;
let totalOverdue = 0;
const now = Math.floor(Date.now() / 1000);
invoices.data.forEach(invoice => {
totalUnpaid += invoice.amount_due;
if (invoice.due_date && invoice.due_date < now) {
totalOverdue += invoice.amount_due;
}
});
console.log(`Unpaid: $${(totalUnpaid / 100).toFixed(2)}`);
console.log(`Overdue: $${(totalOverdue / 100).toFixed(2)}`);
Monitor Changes with Webhooks
For real-time tracking, listen to invoice events as they happen.
Create a webhook endpoint in Stripe
Go to Developers > Webhooks in your Stripe Dashboard and create an endpoint pointing to your server. Subscribe to invoice.created, invoice.payment_succeeded, invoice.payment_failed, and invoice.marked_uncollectible events.
const express = require('express');
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const app = express();
const endpointSecret = process.env.STRIPE_WEBHOOK_SECRET;
app.post('/webhook', express.raw({type: 'application/json'}), (req, res) => {
const sig = req.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(req.body, sig, endpointSecret);
} catch (err) {
return res.status(400).send(`Webhook Error: ${err.message}`);
}
res.json({received: true});
});
app.listen(3000, () => console.log('Listening for webhooks'));
Handle invoice events
In your webhook handler, check the event.type and act on invoice changes. Log the invoice ID, customer, amount, and status to your own database for historical tracking.
if (event.type === 'invoice.payment_succeeded') {
const invoice = event.data.object;
console.log(`Invoice ${invoice.id} paid by ${invoice.customer}`);
// Log to your database
}
if (event.type === 'invoice.payment_failed') {
const invoice = event.data.object;
console.log(`Invoice ${invoice.id} payment failed`);
// Trigger a retry or send a reminder
}
if (event.type === 'invoice.marked_uncollectible') {
const invoice = event.data.object;
console.log(`Invoice ${invoice.id} marked uncollectible`);
// Flag for review
}
Common Pitfalls
- Forgetting Stripe amounts are in cents. Divide by 100 before displaying as dollars to users.
- Counting draft invoices as revenue. Draft invoices aren't sent — filter by open, paid, or uncollectible only.
- Not deduplicating webhook events. Stripe retries for 3 days; track by invoice ID and timestamp to avoid double-counting.
- Assuming unpaid means overdue. Always check the
due_datefield — an unpaid invoice is only overdue if that date has passed.
Wrapping Up
You now have three ways to track your Stripe invoices: the Dashboard for quick checks, the API for programmatic queries, and webhooks for real-time updates. This setup lets you identify unpaid and overdue invoices, calculate total outstanding amounts, and trigger actions automatically. If you want to track this automatically across tools, Product Analyst can help.