Invoices
Invoices represent accounts receivable — money owed to your business by customers.
Invoice Status Flow
draft → open (finalized) → partially_paid → manual_paid / stripe_paid
↘ void / uncollectible| Status | Description |
|---|---|
draft |
Editable, not yet issued. No journal entry, nothing in Stripe |
open |
Finalized, awaiting payment |
partially_paid |
Some payment received |
stripe_paid |
Fully paid through the Stripe payment link |
manual_paid |
Fully paid via linked bank transaction(s) |
void |
Cancelled after finalization |
uncollectible |
Written off as bad debt |
Create an Invoice
curl -X POST "https://api.ondayzero.com/api/v1/invoices" \
-H "Authorization: Bearer dz_your_token_here" \
-H "x-business-id: YOUR_BUSINESS_ID" \
-H "Content-Type: application/json" \
-d '{
"customer_id": "019ab37c-c057-7000-8000-000000000001",
"due_date": "2026-11-01",
"description": "October retainer",
"line_items": [
{
"description": "Monthly retainer",
"quantity": 1,
"unit_price": 250000
},
{
"variant_id": "019ab37c-0a71-7000-8000-000000000001",
"quantity": 3
}
]
}'
unit_priceis in cents.250000= $2,500.00.
Two kinds of line item:
- Custom — omit
variant_id(or send"custom");descriptionandunit_priceare required,quantitydefaults to1. - Catalog — pass a product
variant_id(or acatalog_item_id) and aquantity; the catalog price applies unless you sendunit_priceto override it.
Optional fields: issue_date, invoice_number (otherwise assigned on finalize), currency, project_id, fulfillment_location_id (warehouse to deduct inventory from), historical: true (import an already-issued invoice without side effects), and recurring (see Recurring Templates).
The invoice is created as a draft. Edit it with PUT /api/v1/invoices/{invoice_id} (send the fields you want to change plus id); delete a draft with DELETE /api/v1/invoices/{invoice_id}.
Finalize an Invoice
Draft invoices must be finalized before they can be sent or paid:
curl -X PUT "https://api.ondayzero.com/api/v1/invoices/{invoice_id}/finalize" \
-H "Authorization: Bearer dz_your_token_here" \
-H "x-business-id: YOUR_BUSINESS_ID"Finalization moves the status to open, assigns the invoice number if it was blank, posts the AR journal entry, and — when Stripe is connected — creates the Stripe invoice so a hosted payment link exists. Only draft invoices can be finalized.
Send an Invoice
curl -X POST "https://api.ondayzero.com/api/v1/invoices/{invoice_id}/send" \
-H "Authorization: Bearer dz_your_token_here" \
-H "x-business-id: YOUR_BUSINESS_ID" \
-H "Content-Type: application/json" \
-d '{"attach_pdf": true, "cc_sender": false, "custom_message": "Thanks for your business!"}'GET /api/v1/invoices/{invoice_id}/pdf returns a fresh PDF URL; PUT /api/v1/invoices/{invoice_id}/delivered marks an invoice delivered when you send it yourself.
List Invoices
curl "https://api.ondayzero.com/api/v1/invoices?status=open&sort_by=due_date&descending=false&limit=50" \
-H "Authorization: Bearer dz_your_token_here" \
-H "x-business-id: YOUR_BUSINESS_ID"Filters: status, customer_id, number, search, due_date_from / due_date_to, product_id / variant_id, plus the standard pagination parameters. GET /api/v1/invoices/aging returns the AR aging summary and GET /api/v1/invoices/metrics the headline numbers.
Stripe Payment Links
If Stripe is connected, finalized invoices carry a hosted_invoice_url for customers to pay online; GET /api/v1/invoices/{invoice_id}/payment-link returns it on demand. Payments made there are recorded automatically via Stripe webhooks and move the invoice to stripe_paid. POST /api/v1/invoices/{invoice_id}/stripe/sync re-checks Stripe if a payment seems to be missing.
Record a Manual Payment
Link a bank transaction to the invoice:
curl -X POST "https://api.ondayzero.com/api/v1/invoices/{invoice_id}/payments" \
-H "Authorization: Bearer dz_your_token_here" \
-H "x-business-id: YOUR_BUSINESS_ID" \
-H "Content-Type: application/json" \
-d '{"transaction_id": "TRANSACTION_UUID", "amount": 250000, "paid_on": "2026-10-28"}'Partial amounts move the invoice to partially_paid; paying the balance moves it to manual_paid. Payments can be updated (PATCH …/payments/{payment_id}), removed (DELETE), or moved to another invoice (POST …/payments/{payment_id}/move). GET /api/v1/invoices/suggestions lists AI-matched transaction/invoice pairs you can accept or reject, and POST /api/v1/invoices/bulk-payments records many payments at once.
If you don't have a transaction to link, PUT /api/v1/invoices/{invoice_id}/paid marks the invoice paid outright (and /unpaid reverses that).
Void an Invoice
curl -X PUT "https://api.ondayzero.com/api/v1/invoices/{invoice_id}/void" \
-H "Authorization: Bearer dz_your_token_here" \
-H "x-business-id: YOUR_BUSINESS_ID"Voiding reverses the AR journal entry and voids the Stripe invoice if there is one. Only open invoices can be voided: drafts should be deleted instead, and paid invoices cannot be voided (remove the payments first, or write the balance off with PUT /api/v1/invoices/{invoice_id}/uncollectible).