Invoice Lifecycle¶
A complete guide to the invoice state machine — from draft to paid, with every transition explained.
Status Overview¶
Every managed invoice has a status field that controls what actions are available.
draft --finalize--> finalized --mark-sent--> sent --mark-paid--> paid
| \ ^ \ |
| \--- mark-paid ---|---\----> paid |
| | |
| +--- mark-unpaid --+
|
void: finalized, sent --void--> void
archive: finalized, sent, paid, void --archive--> archived --restore--> finalized
delete: draft only --DELETE--> (removed)
Note: mark-paid is allowed from both finalized and sent. mark-unpaid
moves paid back to sent. void is only allowed from finalized or sent.
Statuses¶
| Status | Description | Editable? |
|---|---|---|
draft |
Invoice is being prepared. Fully editable. | Yes |
finalized |
Locked and ready to send. Calculations are frozen. | No |
sent |
Delivered to the customer (via email or manually marked). | No |
paid |
Payment received. | No |
void |
Cancelled. Cannot be un-voided. | No |
archived |
Hidden from default views. Can be restored. | No |
Transitions¶
Draft to Finalized¶
Finalizing an invoice:
- Locks all fields (line items, discounts, taxes, etc.)
- Freezes the calculation — totals won't change
- Makes the invoice available for rendering and sending
Irreversible
Once finalized, an invoice cannot be returned to draft. If you need to make changes, void the invoice and create a new one.
When to finalize: After all line items, discounts, taxes, and details have been reviewed and confirmed.
Finalized to Sent¶
Only POST .../mark-sent moves the status to sent. It is allowed only from
finalized.
Use this to record that you delivered the invoice — whether InvoicePDFs emailed it or you delivered it through your own channels (printed, uploaded to a portal, etc.).
send does not change the status
{
"to": ["client@example.com"],
"subject": "Invoice INV-2026-001",
"message": "Please find your invoice attached.",
"attach_pdf": true
}
The send endpoint emails the document and creates a delivery record, but it
does not transition the document's status. It works from any non-draft
status (finalized, sent, or paid) and returns 409 Conflict on a draft.
If attach_pdf is true, a render must already exist (render the document
first). To move the status to sent, call mark-sent explicitly.
Marking as Paid¶
Mark an invoice as paid when you've received payment. This is a manual status update
— InvoicePDFs does not process payments. mark-paid is allowed from finalized
or sent (you do not have to mark it sent first).
Paid to Unpaid (Reversal)¶
If a payment bounces or is reversed, you can move the invoice back to sent status.
Voiding an Invoice¶
Voiding permanently cancels an invoice. You can void an invoice that is finalized or sent. A paid invoice cannot be voided.
Use cases:
- Invoice was sent in error
- Customer dispute — issuing a corrected invoice
- Duplicate invoice
Note
Voided invoices remain in the system for audit purposes. They cannot be deleted or un-voided.
Archiving and Restoring¶
Archiving hides an invoice from default list views. Archiving is allowed from finalized, sent, paid, or void (a draft cannot be archived — delete it instead).
Restoring brings it back to finalized status, regardless of what status it was in before archiving.
Allowed Transitions Table¶
| From | To | Endpoint |
|---|---|---|
draft |
finalized |
POST .../finalize |
draft |
(deleted) | DELETE .../ |
finalized |
sent |
POST .../mark-sent |
finalized |
paid |
POST .../mark-paid |
finalized |
void |
POST .../void |
finalized |
archived |
POST .../archive |
sent |
paid |
POST .../mark-paid |
sent |
void |
POST .../void |
sent |
archived |
POST .../archive |
paid |
sent |
POST .../mark-unpaid |
paid |
archived |
POST .../archive |
void |
archived |
POST .../archive |
archived |
finalized |
POST .../restore |
POST .../send emails the document and creates a delivery record but is not a
status transition — it does not appear in this table because it never changes the
document's status.
Common Workflows¶
Standard Flow¶
1. Create draft invoice
2. Add line items, discounts, taxes
3. Finalize
4. Render PDF
5. Send via email
6. Mark as paid when payment received
Quick Render (No Lifecycle)¶
If you don't need status tracking, use the Documents API for stateless rendering:
Correction Flow¶
1. Void the incorrect invoice
2. Create a new draft with corrected data
3. Reference the voided invoice in notes
4. Finalize and send the new invoice
Batch Processing¶
import httpx
client = httpx.Client(
base_url="https://invoicepdfs.com",
headers={"Authorization": "Bearer ip_live_..."},
)
# Create and finalize invoices in batch
for invoice_data in monthly_invoices:
resp = client.post("/api/v1/documents", json=invoice_data)
invoice_id = resp.json()["data"]["id"]
client.post(f"/api/v1/documents/{invoice_id}/finalize")
# Note: send emails the PDF but does not change status; call mark-sent to
# record the document as sent. A render must exist before send with attach_pdf.
client.post(f"/api/v1/documents/{invoice_id}/renders", json={"template_id": "tpl_modern"})
client.post(f"/api/v1/documents/{invoice_id}/send", json={
"to": [invoice_data["customer_email"]],
"attach_pdf": True,
})
client.post(f"/api/v1/documents/{invoice_id}/mark-sent")
Error Handling¶
Invalid transitions return 409 Conflict:
{
"error": {
"status": 409,
"code": "conflict",
"message": "Cannot finalize a document in 'sent' status",
"request_id": "38f24dafcf099a38a8eb287afef6b3c0"
}
}
Only draft invoices can be edited. Attempting to PATCH a non-draft invoice returns: