Invoice Statuses
Track invoice lifecycle from creation through payment with automatic status management
Invoice statuses represent the current state of an invoice in its lifecycle, from initial creation through final payment or closure. Payload automatically manages status transitions as invoices are created, published, paid, or closed, ensuring accurate tracking of accounts receivable and payment status. Understanding invoice statuses is essential for managing billing workflows, monitoring collections, and maintaining accurate financial records.
Invoice status transitions are automatic and based on invoice actions. When you create an
invoice, it starts as draft or you can set it to open to publish it. Setting status to
open automatically transitions the invoice to unpaid, partially_paid, or paid based on
the current balance. As payments are applied, the status updates accordingly. You can manually
close invoices that are cancelled or voided.
Prerequisites
Before working with invoice statuses, ensure you understand:
Create invoices
Learn how to create and configure invoices with line items and payment settings.
Process payments
Understand how payments affect invoice status and balance calculations.
Invoice Status Reference
Quick reference for all invoice statuses and their characteristics.
| Status | Description | Accepts Payments | Can Modify | Autopay |
|---|---|---|---|---|
| draft | Unpublished invoice being prepared | No | Yes | No |
| open | Publish trigger - automatically becomes unpaid/partially_paid/paid based on balance | - | - | - |
| unpaid | Published invoice with no payments applied | Yes | Yes | Yes (on due date) |
| partially_paid | Invoice with partial payment applied | Yes | Yes | No |
| paid | Fully paid invoice with zero balance | No | No | No |
| closed | Manually cancelled or voided invoice | No | No | No |
Automatic vs Manual Status: The partially_paid and paid statuses are automatically set
by the system based on payment allocations. You can only manually set draft, open, and
closed statuses via the API.
Understanding Invoice Statuses
Invoices move through different statuses as they progress from creation to payment.
Draft Status
Invoices in draft status are unpublished and not yet sent to customers.
Characteristics:
- Invoice is being prepared but not finalized
- Not visible to customers or included in receivables
- No autopay processing occurs
Transitioning from draft:
# Publish draft invoice to make it active
invoice = pl.Invoice.get("inv_draft123")
invoice.update(
status="open" # Moves to open status
)Draft Workflow: Use draft status when building complex invoices or coordinating with customers before publishing. Draft invoices don't appear in accounts receivable reports and won't trigger autopay or payment reminders.
Open Status (Publishing Trigger)
Setting an invoice to open status publishes the invoice and automatically transitions it based
on balance.
How it works:
- When you set
status: 'open', the system evaluates the invoice balance - Automatically becomes
unpaidif balance_due equals the full amount - Automatically becomes
partially_paidif some payments already exist (0 < balance < total) - Automatically becomes
paidif the balance is zero (rare but possible)
Example:
# Publish a draft invoice
invoice = pl.Invoice.get("inv_draft123")
invoice.update(
status="open" # Automatically becomes unpaid/partially_paid/paid
)
# After update, the invoice will have the appropriate status
updated_invoice = pl.Invoice.get("inv_draft123")
print(updated_invoice.status) # "unpaid" (if no payments exist)Publishing Trigger: Think of open as a "publish" command rather than a stable status.
When you set an invoice to open, it immediately evaluates the balance and transitions to the
appropriate status (unpaid, partially_paid, or paid).
Automatic Payment Requests: If enabled in your global settings, payment requests are
automatically sent to the payer when an invoice is opened or created with open status. This
provides customers with an immediate payment link without requiring manual payment link
creation.
Unpaid Status
Invoices in unpaid status are published and awaiting payment with no payments applied.
Characteristics:
- Invoice is active and visible to customers
- Autopay will process on due date (if enabled)
- Can send payment requests and reminders
- Can still be modified (add line items, adjust amounts)
Unpaid vs Partially Paid: An invoice is unpaid when no payments have been applied
(balance_due = total). Once any payment is received, even a small amount, the status
automatically transitions to partially_paid.
Partially Paid Status
Invoices in partially_paid status have received some payment but still have a remaining
balance.
Characteristics:
- One or more payments applied to invoice
-
totals.paidis greater than zero -
totals.balance_dueis greater than zero - Still eligible for additional payments
- Can send payment reminders for outstanding amount
Payment tracking:
invoice = pl.Invoice.get("inv_abc123")
print(f"Status: {invoice.status}") # "partially_paid"
print(f"Total: ${invoice.totals['total']}")
print(f"Paid: ${invoice.totals['paid']}")
print(f"Balance due: ${invoice.totals['balance_due']}")
# See all payment allocations
for payment in invoice.payments:
print(f"Payment: ${payment.amount} on {payment.created_at}")Partial Payment Strategy: Partial payments are useful for breaking large invoices into
manageable amounts, accepting deposits, or handling payment plan scenarios. Track each payment
allocation in the payments array for complete audit history.
Paid Status
Invoices in paid status have been fully paid with zero balance remaining.
Characteristics:
-
totals.balance_dueequals zero -
totals.paidequalstotals.total -
paid_timestamprecords when invoice was fully paid
Automatic transition:
# When final payment is applied, status automatically becomes "paid"
invoice = pl.Invoice.get("inv_abc123")
payment = pl.Transaction.create(
type="payment",
amount=invoice.totals["balance_due"], # Pay remaining balance
sender={
"account_id": invoice.payer["account_id"],
"method_id": "pm_card_456"
},
invoice_allocations=[
{
"invoice_id": invoice.id,
"amount": invoice.totals["balance_due"]
}
]
)
# Invoice automatically transitions to paid status
paid_invoice = pl.Invoice.get(invoice.id)
print(paid_invoice.status) # "paid"
print(paid_invoice.paid_timestamp) # Timestamp of full paymentPaid Invoice Immutability: Once an invoice reaches paid status, it cannot be modified.
To make corrections, issue a refund transaction or create a new invoice. This immutability
ensures accurate financial records and audit trails.
Closed Status
Invoices in closed status have been manually cancelled or voided.
Characteristics:
- Manually set by updating
status: 'closed' - Indicates invoice was cancelled, voided, or written off
- Cannot accept payments or be modified
Closing an invoice:
invoice = pl.Invoice.get("inv_abc123")
invoice.update(
status="closed",
attrs={
"closure_reason": "Customer cancelled service",
"closed_by": "[email protected]",
"closed_date": "2024-01-20"
}
)Close vs Delete: Use closed status instead of deleting invoices to maintain complete
audit trails. Closed invoices preserve the historical record while indicating the invoice is
no longer active. Store closure details in attrs for documentation.
Invoice Lifecycle
Invoices follow a standard lifecycle with automatic status transitions.
Standard Invoice Flow
The typical invoice lifecycle progresses through these stages:
draft → open (trigger) → unpaid → partially_paid → paid
↓ ↓ ↓
closed closed closedManual Payment Lifecycle
Invoices with manual payment collection follow this flow:
draft → open (trigger) → unpaid → (manual payment) → paid
↓ ↓ ↓
closed closed partially_paid → (additional payment) → paid
↓
closedManual payment process:
- Invoice created and set to
openwhich transitions tounpaid - Payment request automatically sent to payer (if global setting enabled)
- Customer makes payment via payment link, API, or external method
- First partial payment transitions from
unpaidtopartially_paid - Additional payments can be applied until invoice reaches
paidstatus - Invoice can be closed at any time if cancelled or written off
Multiple Payment Methods: Manual payment collection supports various methods including payment links, direct API payments, and external payments (checks, wire transfers). Each payment is tracked via payment allocations, allowing customers to pay invoices in installments.
Autopay Lifecycle
Invoices with autopay enabled follow a slightly different flow:
draft → open (trigger) → unpaid → (autopay on due_date) → paid
↓ ↓
closed closed (if autopay fails, stays unpaid)Autopay process:
- Invoice created with
autopay_settings.allowed: true - Set to
openwhich transitions tounpaid(assuming no prior payments) - Automatically charges payment method on
due_date - Successful payment transitions from
unpaidtopaid - Failed payment keeps invoice in
unpaidstatus for retry or manual payment
Autopay and Partial Payments: Autopay invoices attempt to charge the full amount on the
due date. If the charge succeeds, the invoice goes directly to paid. If it fails, the
invoice remains unpaid and can accept manual or partial payments.
Querying Invoices by Status
Filter and search invoices based on their current status.
Common Status Queries
Unpaid invoices:
# Get all invoices awaiting payment
unpaid = pl.Invoice.filter_by(
q='status == "unpaid"',
order_by="asc(due_date)"
).all()Overdue invoices:
# Find invoices past their due date
from datetime import date
today = date.today().isoformat()
overdue = pl.Invoice.filter_by(
q=f'status == "unpaid" && due_date < "{today}"',
order_by="asc(due_date)"
).all()Partially paid invoices:
# Track invoices with partial payments
partial = pl.Invoice.filter_by(
q='status == "partially_paid"',
order_by="desc(modified_at)"
).all()Recently paid invoices:
# Review successful payments
paid = pl.Invoice.filter_by(
q='status == "paid"',
order_by="desc(paid_timestamp)",
limit=50
).all()Draft invoices:
# Find unpublished invoices
drafts = pl.Invoice.filter_by(
q='status == "draft"',
order_by="desc(created_at)"
).all()Closed invoices:
# Review cancelled or voided invoices
closed = pl.Invoice.filter_by(
q='status == "closed"',
order_by="desc(modified_at)"
).all()Status-Based Reporting
Group invoices by status for reporting:
# Get invoice counts by status
status_breakdown = pl.Invoice.filter_by(
group_by="status"
).all()
print("Invoice Status Summary:")
for group in status_breakdown:
print(f" {group.status}: {group.count} invoices")Calculate accounts receivable:
# Sum all unpaid and partially paid invoices
sum_balance_due = pl.attr.totals.balance_due.sum()
receivables = (
pl.Invoice.select(sum_balance_due)
.filter_by(q='status == "unpaid" || status == "partially_paid"')
.all()
)
total_receivables = getattr(receivables[0], str(sum_balance_due))
print(f"Total accounts receivable: ${total_receivables}")Performance Optimization: Use the fields parameter to return only necessary fields when
querying large numbers of invoices. For example, fields=id,status,totals returns minimal
data for fast status reporting.
Managing Status Transitions
Control invoice status changes through API operations.
Publishing Draft Invoices
Move invoices from draft to open status:
# Single invoice
invoice = pl.Invoice.get("inv_draft123")
invoice.update(
status="open"
)
# Batch publish multiple drafts
drafts = pl.Invoice.filter_by(
q='status == "draft"'
).all()
for draft in drafts:
draft_invoice = pl.Invoice.get(draft.id)
draft_invoice.update(status="open")
print(f"Published {draft.number}")Closing Invoices
Mark invoices as closed or voided:
from datetime import date
invoice = pl.Invoice.get("inv_abc123")
invoice.update(
status="closed",
attrs={
"closure_reason": "Customer cancelled",
"closed_by": "[email protected]",
"closed_date": date.today().isoformat()
}
)Schema Reference
Fields relevant to invoice status management:
Invoice Status Fields
statusdraft, open, unpaid, partially_paid, paid, closed, syncpaid_timestamptotalsbalance_duepaidsubtotaltaxtotalNext Steps
Enhance your invoice management with additional features
Create and Manage Invoices
Build invoices with line items using Creating Invoices, automate recurring invoice generation with Billing Schedules, and attach supporting documents with Invoice Attachments.
Process Invoice Payments
Process invoice payments programmatically with Payment API, enable autopay for recurring billing with Automatic Payments, and send payment links to customers with Payment Requests.
Track and Report Status Changes
Monitor status transitions in real-time with Webhook Events, generate comprehensive financial reports with Reporting Overview, and analyze payment data with Transaction Reports.
Related articles
- Creating Invoices - Build detailed invoices with line items
- Payment API - Process payments that update invoice status
- Automatic Payments - Configure autopay for invoices
- Billing Schedules - Generate recurring invoices automatically
- Webhook Events - Monitor invoice status transitions
- Invoice API Reference - Complete invoice API documentation