The six endpoints that issue, read and edit invoices and give the revenue summary.
Overview
These six endpoints deal with the invoice itself: listing, issuing, reading, editing its header fields and deleting it. A revenue summary giving a period total sits alongside them.
An invoice carries a snapshot: the client's name, address and tax details are held inside the record as they stood at issue. A client changing their address later leaves the invoice untouched, because a document once issued tells the story of a moment.
The money has three layers: the lines give the subtotal, the tax what sits on top of it, and the commission and surcharge what the payment method brought. Together the three make the grand total.
Reference
Listing the Invoices
get/api/v1/admin/invoices
Invoices/GetInvoices admin
Returns invoices, filtered and paged.
Query 15
pageintWhich page.
limitintRecords per page. A value out of range falls back to the default.
searchstringSearches the invoice number and the client details.
statusstringFilters by status: waiting, unpaid, paid, cancelled, refund. It also takes derived filters such as overdue, upcoming, formalised and not formalised.
client_idintFilters by client.
currency_idintFilters by currency.
taxedboolSeparates the formalised ones.
amountstringFilters by amount.
amount_opstringWhich way the amount comparison runs.
item_descriptionstringSearches the line descriptions.
cdatestringFilters by the date issued.
cdate_opstringWhich way that comparison runs.
duedatestringFilters by the due date.
duedate_opstringWhich way that comparison runs.
daterangestringFilters between two dates. Its direction comes from its own comparison field.
Response fields data[] — 14 + meta — 4
idintThe invoice id.
numberstring | nullThe invoice number. Empty until a number is given.
// Draft and system invoices NEVER enter this list; the count will not match the table.
$rows = Api::Invoices()->GetInvoices([], ['status' => 'unpaid'])['data'];
Issuing an Invoice
post/api/v1/admin/invoices
Invoices/CreateInvoice admin the notice is optional
Issues an invoice to a client by hand and writes its lines.
Body 8
client_idintreqThe client the invoice goes to.
itemsarrayreqThe invoice lines. At least one line is needed.
currency_idintThe invoice currency. Left out, the client's own is used.
statusstringThe status it opens with. Unpaid by default.
payment_methodstringThe payment method.
created_atstringThe date issued. Left out, the moment of the call is used.
due_datestringThe due date. Left out, the end of today is used.
send_notificationboolSends the client the notice that suits the status.
Line fields items[] — 8
descriptionstringreqWhat the line is for.
quantityintreqHow many. One at the least.
amountfloatreqThe unit amount.
discountfloatThe discount value.
discount_typestringWhether the discount is an amount or a share.
tax_ratefloatA tax rate for this line.
tax_exemptboolKeeps the line free of tax.
user_pidintThe service it belongs to. Zero says the line stands alone.
Response fields 201 — data
dataobjectThe invoice issued. Same shape as the detail endpoint.
Errors 6
invalid_client422No client was given.
not_found404No such client.
currency_required422No currency could be resolved.
items_required422No line was given.
create_failed422The invoice could not be issued.
insufficient_scope403The key lacks the required scope.
// The notice is OPTIONAL: leave it out and the client never hears about the invoice.
Api::Invoices()->CreateInvoice([
'client_id' => $uid,
'items' => $lines,
'send_notification' => true,
]);
Reading the Revenue Summary
get/api/v1/admin/invoices/stats
Invoices/GetInvoiceStats admin
Returns the total for the type and period you pick.
Query 2
typestringWhat gets added up: unpaid, paid, overdue or tax. The unpaid ones by default.
periodstringWhich period: the last seven days, the last fifteen, this month, last month, this year or last year. This month by default.
Response fields data — 5
typestringThe type applied.
periodstringThe period applied.
amountfloatThe total. A raw number, with other currencies converted into the local one before adding.
countintHow many invoices went into it.
currency_idintThe currency the total is in.
Errors 1
insufficient_scope403The key lacks the required scope.
// The amount is a CONVERTED total, worked out at the day's rate, and you format it yourself.
$s = Api::Invoices()->GetInvoiceStats([], ['type' => 'unpaid'])['data'];
$sum = Money::formatter_symbol($s['amount'], $s['currency_id']);
Reading One Invoice
get/api/v1/admin/invoices/{id}
Invoices/GetInvoice admin
Returns an invoice with its lines, payments and the client snapshot.
// When REBUILDING the total, add the two taxes yourself; the tax field leaves them out.
$inv = Api::Invoices()->GetInvoice(['id' => $id])['data'];
$sum = $inv['subtotal'] + $inv['tax']
+ $inv['payment_method_commission'] + $inv['payment_method_commission_tax']
+ $inv['installment_surcharge'] + $inv['installment_surcharge_tax'];
Editing an Invoice
patch/api/v1/admin/invoices/{id}
Invoices/UpdateInvoice admin
Changes an invoice's header fields, recomputing the totals when needed.
Body 13
numberstringThe invoice number.
payment_methodstringThe payment method. Changing it on an unpaid invoice that carries no payment recomputes the commission at the new method's current rate, along with the totals. An invoice that was paid on, is paid or awaits confirmation keeps its amount and only takes the new method. On a paid invoice the cash record for the part settled without a payment row moves to the new method; payment rows keep their own cash records. It turns into Balance on an open invoice. On a paid, cancelled or refunded invoice it does so only when a payment row taken from the balance exists, and only the name changes; otherwise the request is refused and nothing is written. When omitted, the stored method stays as it is, an empty one included.
taxation_typestringWhether tax sits inside the amount. Changing it recomputes the totals.
currency_idintThe invoice currency. Changing it converts the line amounts, discounts and totals to the new currency at the system's exchange rate, whatever the status.
tax_ratefloatThe tax rate. Changing it recomputes tax, commission and total only while the invoice is unpaid. In any other status the rate is stored and the amounts stay.
formalizedboolTurns it into a formal invoice. The first time it does, the client is told.
taxfreeboolMakes the invoice free of tax. It takes precedence over formalising. A change recomputes the amounts only while the invoice is unpaid, and is written to the invoice history in every status.
created_atstringThe date issued. All four dates are read to the minute. A value within the stored minute keeps the stored value, seconds included. A move to another minute is written to the invoice history.
due_datestringThe due date.
paid_atstringThe date paid. On a paid invoice a value in another minute moves the cash-book income entry of the part settled without a payment row to that date in the same request; entries of recorded payments keep their own dates.
refund_datestringThe date refunded.
remove_formalize_fileboolRemoves the formalization file kept on the server. Uploading one is not offered here.
admin_notesstringThe admin-only note, at most 10000 characters. An empty string clears it; when omitted it is left untouched.
Response fields data
dataobjectThe invoice as it now stands. Same shape as the detail endpoint.
Errors 4
not_found404No such invoice.
balance_requires_open_invoice422The method would turn into Balance on an invoice that is not open and holds no payment row taken from the balance. Nothing would be taken from the account balance there. The details carry the invoice status; no field is written.
admin_notes_too_long422The admin note is longer than 10000 characters.
insufficient_scope403The key lacks the required scope.
// Making it tax-free OVERRIDES formalising; sending both together makes no sense.
Api::Invoices()->UpdateInvoice(['id' => $id, 'formalized' => true]);
Deleting an Invoice
delete/api/v1/admin/invoices/{id}
Invoices/DeleteInvoice admin unwinds a chain of records
Removes an invoice and every record hanging off it.
Response fields data — 2
deletedboolWhether the delete ran.
idintThe id of the invoice removed.
Errors 3
not_found404No such invoice.
blocked_by_gate422A hook refused the delete.
insufficient_scope403The key lacks the required scope.
// Cancel it rather than delete: the accounting trail stays and it leaves what is owed.
Api::Invoices()->SetInvoiceStatus(['id' => $id, 'status' => 'cancelled']);
Pitfalls
The tax field does not hold every tax
The taxes on the payment commission and the instalment surcharge enter the grand total and not the tax field. Add the subtotal to the tax expecting the grand total and the difference has no obvious source. Rebuilding the total means adding those two taxes from their own fields.
Draft invoices are not in the list
The listing endpoint hides draft and system invoices. That matches what the panel shows, yet the count from the list will not match the rows in the database. Weigh that when writing an accounting report; adding a filter does not bring the hidden ones back.
Formal and tax-free share one field
An invoice's tax state is one field taking one of three values: normal, formal or tax-free. Making it tax-free overrides formalising, and sending both together means nothing. The first time formalising is applied, the client is told as well.
A delete unwinds the chain
Deleting an invoice does more than remove the record: it also unwinds the lines, the income and expense entries, the links to services and any pending metered rows. A hook can refuse the delete, because some invoices leaving would break the books. To back out, cancel rather than delete: the trail stays and the debt goes.
Empty dates come back empty
Fields for things that have not happened, such as the paid and refund dates, come back empty. The database keeps placeholder dates there and the response clears them. Check a date exists before reading it, or you end up with a screen treating a placeholder as real.