Invoices
Billing for completed flights.
15 endpoints · base URL https://api.aerscheduler.com
/invoicesList invoices
Every invoice in the organization. Administrators only , this is the whole membership's payment history plus revenue aggregates, so it is treated as a financial surface rather than an operational one. Dispatchers get the per-reservation invoice instead.
Query parameters
startDate | string | Start of the window, ISO 8601. Include an offset or Z. |
endDate | string | End of the window, ISO 8601. |
q | string | Free-text filter. Matches the fields a person would search on. |
paid | boolean | Filter on paid state. |
voided | boolean | Filter on voided state. paid=false on its own still includes voided invoices, which nobody owes; pass voided=false for outstanding. |
overdue | boolean | Only invoices that are past due: unpaid, not voided, and carrying a dueAt in the past. An invoice with no due date is never overdue. Matches the Overdue status of the revenue report. |
limit | integer | Rows to return, 1,1000. Defaults to 1000; a larger value is clamped rather than rejected. |
offset | integer | Rows to skip, for paging. Defaults to 0. |
sort | string | Field to order by before paging, as a dot path into the row , total, user.firstName. Omit to keep the endpoint's own order. Numbers and ISO timestamps order as numbers and instants, not as text, and empty values always sort last regardless of direction. Ordering happens before the page is cut, so it orders the whole collection rather than the page you are holding. |
order | "asc" | "desc" | asc (default) or desc. Only meaningful with sort. |
Responses
200 | OK | The invoices.→ { data: Invoice[] } |
400 | Bad Request | The date range is unparseable. |
401 | Unauthorized | No token, an expired token, a malformed one, or a session token naming a membership that is no longer yours ("Your session is out of date"). Sign in again. |
403 | Forbidden | Authenticated, but not allowed to do this. |
429 | Too Many Requests | Rate limited. Retry-After says how long to wait. |
curl https://api.aerscheduler.com/invoices \
-H "Authorization: Bearer $AERSCHEDULER_KEY"/invoicesCreate an invoice
Bills a member for something that isn't a flight , a checkout fee, a headset, a late cancellation. Line items are validated against the organization before anything is written.
For billing a completed flight, use POST /reservations/{id}/invoices instead: it prices the booking from the meters you recorded.
Request body
The invoice. Exactly one of customer or guest is required.
customer | object | Bill a member. Mutually exclusive with guest. |
guest | object | Bill a non-member by name and email. Mutually exclusive with customer. |
memo | string | Optional note shown on the invoice. |
itemsrequired | object[] | Line items. unitPrice is in cents, before tax.
Sales tax follows the organization's own setup (see GET /organizations/salesTax): a line in a category the organization taxes is taxed at that category's rate, taxable overrides that for one line, and a customer marked exempt is never taxed. A line that sends neither category nor taxable is never taxed, so clients written before sales tax existed keep raising the bills they show. |
dueAt | string | When payment is due. |
dueIn | integer | Days until due, used when Stripe needs a relative due. |
serviceFeeTaxable | boolean | Tax the organization's service fee line (or not), whatever the organization's rule for Fees says. Absent follows the rule. |
expectedTotal | integer | Optional. The total from POST /invoices/preview. When it no longer matches (a tax rate, rule or exemption changed in between), nothing is raised and the answer is 409 with the new total. |
Responses
200 | OK | Success.→ { data: Invoice } |
201 | Created | The invoice, with Stripe's tax on each line and total = subtotal + tax.→ { data: Invoice } |
400 | Bad Request | A line item or the customer failed validation, a line is taxable but the organization has no rate, or a taxed charge would go on a member's account balance (ledger mode), which cannot hold sales tax. |
401 | Unauthorized | No token, an expired token, a malformed one, or a session token naming a membership that is no longer yours ("Your session is out of date"). Sign in again. |
403 | Forbidden | Authenticated, but not allowed to do this. |
409 | expectedTotal no longer matches: the bill changed since it was priced. | |
429 | Too Many Requests | Rate limited. Retry-After says how long to wait. |
curl -X POST https://api.aerscheduler.com/invoices \
-H "Authorization: Bearer $AERSCHEDULER_KEY" \
-H "Content-Type: application/json" \
-d '{"items":[]}'/invoices/{invoiceId}Get an invoice
One invoice with its line items. An aircraft owner from outside the organization reads only their own bills, and gets what they need to read and pay one: its number, dates, status, memo, lines with their tax, totals, how a desk payment came in (paymentMethod, checkNumber, paymentReceivedOn), the payment and PDF links, and the work order it is for. Not the organization's QuickBooks sync state, its note about the payment, or which of its staff raised, voided or marked it paid. billTo (name, billing address, phone, email) is on the bill for the person billed and for administrators.
Path parameters
invoiceIdrequired | integer | The invoice id. |
Responses
200 | OK | Success.→ { data: Invoice } |
401 | Unauthorized | No token, an expired token, a malformed one, or a session token naming a membership that is no longer yours ("Your session is out of date"). Sign in again. |
403 | Forbidden | Authenticated, but not allowed to do this. |
404 | Not Found | No such invoice. |
429 | Too Many Requests | Rate limited. Retry-After says how long to wait. |
curl https://api.aerscheduler.com/invoices/:invoiceId \
-H "Authorization: Bearer $AERSCHEDULER_KEY"/invoices/{invoiceId}Mark an invoice paid, or void it
Send markPaid: true to record a payment taken outside the card flow, optionally saying how: paymentMethod, checkNumber (only with check), paymentReceivedOn (the day it arrived, YYYY-MM-DD; today in the organization's calendar when left out, never in the future) and paymentNote. Stripe is told the invoice was paid outside it, and the person billed is sent a receipt. Or send markVoided: true. Payment fields without markPaid are refused. Requires the admin role.
Path parameters
invoiceIdrequired | integer | The invoice id. |
Request body
What to do.
markPaid | boolean | Record it paid. |
markVoided | boolean | Void it. |
paymentMethod | string | null | |
checkNumber | string | null | Only with paymentMethod: check. |
paymentReceivedOn | string | null | The day the money arrived. Not in the future. |
paymentNote | string | null |
Responses
200 | OK | Success.→ { data: Invoice } |
400 | Bad Request | Already voided, a field is not valid, or payment fields without markPaid. |
401 | Unauthorized | No token, an expired token, a malformed one, or a session token naming a membership that is no longer yours ("Your session is out of date"). Sign in again. |
403 | Forbidden | Authenticated, but not allowed to do this. |
429 | Too Many Requests | Rate limited. Retry-After says how long to wait. |
curl -X PATCH https://api.aerscheduler.com/invoices/:invoiceId \
-H "Authorization: Bearer $AERSCHEDULER_KEY" \
-H "Content-Type: application/json" \
-d '{"markPaid":true,"markVoided":true}'/invoices/previewPrice an invoice without raising it
Takes exactly the body of POST /invoices and answers what that call would charge: each line with its sales tax, the organization's service fee line, tax per rate, and the total. Nothing is written and Stripe is not called. The same permission as creating the invoice.
Send the total back as expectedTotal on the create to be sure nothing moved in between.
Responses
200 | OK | Success.→ { data: InvoicePreview } |
400 | Bad Request | The body would fail POST /invoices for the same reason. |
401 | Unauthorized | No token, an expired token, a malformed one, or a session token naming a membership that is no longer yours ("Your session is out of date"). Sign in again. |
403 | Forbidden | Authenticated, but not allowed to do this. |
429 | Too Many Requests | Rate limited. Retry-After says how long to wait. |
curl -X POST https://api.aerscheduler.com/invoices/preview \
-H "Authorization: Bearer $AERSCHEDULER_KEY"/invoices/summarySummarise invoices
Revenue and outstanding totals for a window, in cents, with the counts behind them. Administrators only, like the list.
Takes the same startDate, endDate and q as GET /invoices, so it describes exactly the invoices that endpoint would return , except paid, which narrows the list but not this. Use it instead of adding up a list you fetched: lists are capped at 1,000 rows, so summing what you received would give you the total of an arbitrary thousand invoices for any organization with more than that.
Query parameters
startDate | string | Start of the window, ISO 8601. Include an offset or Z. |
endDate | string | End of the window, ISO 8601. |
q | string | Free-text filter. Matches the fields a person would search on. |
voided | boolean | Filter on voided state. outstanding already excludes voided invoices; this narrows revenue too. |
Responses
200 | OK | The totals.→ { data: object } |
400 | Bad Request | The date range is unparseable. |
401 | Unauthorized | No token, an expired token, a malformed one, or a session token naming a membership that is no longer yours ("Your session is out of date"). Sign in again. |
403 | Forbidden | Authenticated, but not allowed to do this. |
429 | Too Many Requests | Rate limited. Retry-After says how long to wait. |
curl https://api.aerscheduler.com/invoices/summary \
-H "Authorization: Bearer $AERSCHEDULER_KEY"/invoices/{invoiceId}/filesList an invoice's files
The vendor's invoice, a receipt, an 8130-3 attached to the bill. url works for 15 minutes. Administrators read every file; the person billed reads only the ones shown to them (visibility: owner). Anybody else is answered 403, as for an id that is not this organization's.
Path parameters
invoiceIdrequired | integer | The invoice id. |
Query parameters
limit | integer | Rows to return, 1,1000. Defaults to 1000; a larger value is clamped rather than rejected. |
offset | integer | Rows to skip, for paging. Defaults to 0. |
sort | string | Field to order by before paging, as a dot path into the row , total, user.firstName. Omit to keep the endpoint's own order. Numbers and ISO timestamps order as numbers and instants, not as text, and empty values always sort last regardless of direction. Ordering happens before the page is cut, so it orders the whole collection rather than the page you are holding. |
order | "asc" | "desc" | asc (default) or desc. Only meaningful with sort. |
Responses
200 | OK | The files, oldest first.→ { data: InvoiceFile[] } |
401 | Unauthorized | No token, an expired token, a malformed one, or a session token naming a membership that is no longer yours ("Your session is out of date"). Sign in again. |
403 | Forbidden | Not yours, or not this organization's. |
429 | Too Many Requests | Rate limited. Retry-After says how long to wait. |
curl https://api.aerscheduler.com/invoices/:invoiceId/files \
-H "Authorization: Bearer $AERSCHEDULER_KEY"/invoices/{invoiceId}/filesAttach files to an invoice
Send fileNames (up to 5 per request; jpg, png, heic or pdf), visibility (shop by default, owner to show them to the person billed) and a label for a single file. The answer has the new rows in data and, in the same order, signedUrlData: a presigned POST for each file, which the client posts the bytes to. An invoice holds up to 30 files. Requires the admin role. Refused in the demo.
Path parameters
invoiceIdrequired | integer | The invoice id. |
Request body
The files to attach.
fileNames | string[] | |
visibility | "shop" | "owner" | |
label | string | null |
Responses
201 | Created | The rows and where to upload each file. |
400 | Bad Request | A file name or the count is not valid. |
401 | Unauthorized | No token, an expired token, a malformed one, or a session token naming a membership that is no longer yours ("Your session is out of date"). Sign in again. |
403 | Forbidden | Not allowed, not this organization's, or the demo. |
429 | Too Many Requests | Rate limited. Retry-After says how long to wait. |
curl -X POST https://api.aerscheduler.com/invoices/:invoiceId/files \
-H "Authorization: Bearer $AERSCHEDULER_KEY" \
-H "Content-Type: application/json" \
-d '{"fileNames":[],"visibility":"shop"}'/invoices/{invoiceId}/remindSend a payment reminder
Emails the member about an outstanding invoice. Requires the admin role.
Path parameters
invoiceIdrequired | integer | The invoice id. |
Responses
200 | OK | Success.→ { data: object } |
204 | No Content | Success. No body. |
400 | Bad Request | Already paid, or reminded too recently. |
401 | Unauthorized | No token, an expired token, a malformed one, or a session token naming a membership that is no longer yours ("Your session is out of date"). Sign in again. |
403 | Forbidden | Authenticated, but not allowed to do this. |
429 | Too Many Requests | Rate limited. Retry-After says how long to wait. |
curl -X POST https://api.aerscheduler.com/invoices/:invoiceId/remind \
-H "Authorization: Bearer $AERSCHEDULER_KEY"/invoices/orgUsers/{orgUserId}List a member's invoices
Your own, or anyone's if you are an administrator.
Path parameters
orgUserIdrequired | integer | The membership id. |
Query parameters
startDate | string | Start of the window, ISO 8601. Include an offset or Z. |
endDate | string | End of the window, ISO 8601. |
paid | boolean | Filter on paid state. |
voided | boolean | Filter on voided state. paid=false on its own still includes voided invoices, which nobody owes; pass voided=false for outstanding. |
overdue | boolean | Only invoices that are past due: unpaid, not voided, and carrying a dueAt in the past. An invoice with no due date is never overdue. Matches the Overdue status of the revenue report. |
limit | integer | Rows to return, 1,1000. Defaults to 1000; a larger value is clamped rather than rejected. |
offset | integer | Rows to skip, for paging. Defaults to 0. |
sort | string | Field to order by before paging, as a dot path into the row , total, user.firstName. Omit to keep the endpoint's own order. Numbers and ISO timestamps order as numbers and instants, not as text, and empty values always sort last regardless of direction. Ordering happens before the page is cut, so it orders the whole collection rather than the page you are holding. |
order | "asc" | "desc" | asc (default) or desc. Only meaningful with sort. |
Responses
200 | OK | The invoices.→ { data: Invoice[] } |
400 | Bad Request | Not your invoices, and you are not an administrator. |
401 | Unauthorized | No token, an expired token, a malformed one, or a session token naming a membership that is no longer yours ("Your session is out of date"). Sign in again. |
403 | Forbidden | Authenticated, but not allowed to do this. |
429 | Too Many Requests | Rate limited. Retry-After says how long to wait. |
curl https://api.aerscheduler.com/invoices/orgUsers/:orgUserId \
-H "Authorization: Bearer $AERSCHEDULER_KEY"/invoices/reservation/{reservationId}Get a reservation's invoice
The bill for one flight. Dispatchers can read this even though they cannot list the organization's invoices.
Path parameters
reservationIdrequired | integer | The reservation id. |
Responses
200 | OK | Success.→ { data: Invoice } |
401 | Unauthorized | No token, an expired token, a malformed one, or a session token naming a membership that is no longer yours ("Your session is out of date"). Sign in again. |
403 | Forbidden | Authenticated, but not allowed to do this. |
404 | Not Found | That flight has not been invoiced. |
429 | Too Many Requests | Rate limited. Retry-After says how long to wait. |
curl https://api.aerscheduler.com/invoices/reservation/:reservationId \
-H "Authorization: Bearer $AERSCHEDULER_KEY"/invoices/{invoiceId}/files/{fileId}Change an invoice's file
Its label, or who it is shown to (visibility). Requires the admin role.
Path parameters
invoiceIdrequired | integer | The invoice id. |
fileIdrequired | integer | The file id. |
Request body
The fields to change.
label | string | null | |
visibility | "shop" | "owner" |
Responses
200 | OK | The file.→ { data: InvoiceFile } |
400 | Bad Request | A field is not valid. |
401 | Unauthorized | No token, an expired token, a malformed one, or a session token naming a membership that is no longer yours ("Your session is out of date"). Sign in again. |
403 | Forbidden | Not allowed. |
404 | Not Found | File not found. |
429 | Too Many Requests | Rate limited. Retry-After says how long to wait. |
curl -X PATCH https://api.aerscheduler.com/invoices/:invoiceId/files/:fileId \
-H "Authorization: Bearer $AERSCHEDULER_KEY" \
-H "Content-Type: application/json" \
-d '{"label":"…","visibility":"shop"}'/invoices/{invoiceId}/files/{fileId}Remove an invoice's file
It stops listing; the stored object is kept. Requires the admin role.
Path parameters
invoiceIdrequired | integer | The invoice id. |
fileIdrequired | integer | The file id. |
Responses
204 | No Content | Success. No body. |
401 | Unauthorized | No token, an expired token, a malformed one, or a session token naming a membership that is no longer yours ("Your session is out of date"). Sign in again. |
403 | Forbidden | Not allowed. |
404 | Not Found | File not found. |
429 | Too Many Requests | Rate limited. Retry-After says how long to wait. |
curl -X DELETE https://api.aerscheduler.com/invoices/:invoiceId/files/:fileId \
-H "Authorization: Bearer $AERSCHEDULER_KEY"/invoices/{invoiceId}/files/from-jobAdd a job's files to its invoice
Copies the work order's files onto the invoice raised for it: every one not already there, or those named in fileIds. Each keeps its label and who it is shown to. Nothing is uploaded. Requires the admin role. Refused in the demo.
Path parameters
invoiceIdrequired | integer | The invoice id. |
Request body
Which files, or none for all.
fileIds | integer[] |
Responses
201 | Created | The files added; empty when every one was already there.→ { data: InvoiceFile[] } |
400 | Bad Request | Not a work order's invoice, or a file is not on its job. |
401 | Unauthorized | No token, an expired token, a malformed one, or a session token naming a membership that is no longer yours ("Your session is out of date"). Sign in again. |
403 | Forbidden | Not allowed, or not this organization's. |
429 | Too Many Requests | Rate limited. Retry-After says how long to wait. |
curl -X POST https://api.aerscheduler.com/invoices/:invoiceId/files/from-job \
-H "Authorization: Bearer $AERSCHEDULER_KEY" \
-H "Content-Type: application/json" \
-d '{"fileIds":[]}'/invoices/reservation/{reservationId}/invoiceTotalsGet a reservation's totals
What the flight comes to, broken down, without creating an invoice.
Path parameters
reservationIdrequired | integer | The reservation id. |
Responses
200 | OK | Success.→ { data: object } |
401 | Unauthorized | No token, an expired token, a malformed one, or a session token naming a membership that is no longer yours ("Your session is out of date"). Sign in again. |
403 | Forbidden | Authenticated, but not allowed to do this. |
404 | Not Found | No such reservation. |
429 | Too Many Requests | Rate limited. Retry-After says how long to wait. |
curl https://api.aerscheduler.com/invoices/reservation/:reservationId/invoiceTotals \
-H "Authorization: Bearer $AERSCHEDULER_KEY"