Provides a simple interface to theRESTFul API for interfacing with aXcelerate.
This library is being initially developed for use with the aXcelerate RESTFul Service API, so API methods will likely be implemented in the order that they are needed by any project that accesses this service.
Some endpoints the library wraps are live but missing from aXcelerate's published docs:
GET /domains—Accounting.Domains(): every domain id and name (135 on staging and production, 29 Sep 2026). aXcelerate stores anyDOMAINIDon an invoice line without checking it exists, so this list is the only way to validate one before writing it.
CreateInvoice, AddInvoiceLines, ApproveInvoice, ApplyPayment and
Transactions enforce these before a request is sent. The order aXcelerate
requires is create → add lines → approve → pay:
a := client.Accounting
inv, _, err := a.CreateInvoice(axcelerate.InvoiceRequest{
ContactID: 14518907, FirstName: "Chris", LastName: "Joyce",
Lines: []axcelerate.InvoiceLine{{Description: "Pocket Mask Training", Qty: 1,
UnitPriceGross: "19.00", TaxPercent: "0.00", DomainID: axcelerate.Int(5566)}},
})
_, err = a.AddInvoiceLines(inv.InvoiceID, axcelerate.InvoiceLine{Description: "Afterpay transaction fee",
Qty: 1, UnitPriceGross: "2.00", TaxPercent: "0.00", DomainID: axcelerate.Int(5494)})
_, err = a.ApproveInvoice(inv.InvGUID) // a draft cannot take a payment
txn, _, err := a.ApplyPayment(axcelerate.InvoicePayment{
ContactID: 14518907, InvoiceID: inv.InvoiceID, Amount: "21.00"}) // dollarsThe full, runnable version is invoiceFlow in example/.
Each rule below was established against the live API (most of them in
production):
- The Bill To surname is
lastname. The docs saysurname; aXcelerate ignores it and storesLASTNAMEnull.firstnameandlastnameare both required ("The Bill To Given Name and Surname fields cannot be empty"). invoiceDateandorderDateare required ("The invoice order date is out of bounds"); the client defaults both to today.ITEMCODEmust be on every line, even as""— a line without the key fails the whole request with "The invoice items are invalid."- The invoice PUT's
aItemreplaces every line.AddInvoiceLinesreads the invoice and re-sends the existing lines with theirITEMID; sending only the new line deletes the rest. The JSON goes in the form body — a PUT's query string does not survive&,#or+in a description. - A draft invoice cannot take a payment — and a payment against one still
records the money, as an unassigned receipt, while answering HTTP 400.
Retrying after that 400 pays twice.
ApplyPaymentrefuses a draft before sending anything;ApproveInvoice(by GUID) issues it first. - Amounts are dollars, as decimal strings — not a gateway's cents.
DOMAINIDis not validated by aXcelerate; check ids againstDomains().- A draft cannot be voided — only an issued invoice.
Transactiondecoding:ORGANISATIONis a name,ORGIDa quoted id andUNASSIGNEDAMOUNTa decimal string. Before these were typed correctly a payment that succeeded returned a decode error.
I would like to cover the entire aXcelerate RESTFul Service API and contributions are of course always welcome. See CONTRIBUTING.md for details.
## Secrets (SOPS)
`.env` is gitignored and never committed. The encrypted copy `.env.enc` **is**
committed and is the portable source of truth — clone the repo on any machine and
decrypt, rather than copying `.env` around by hand.
```sh
# after cloning or pulling
sops -d --input-type dotenv --output-type dotenv .env.enc > .env
# after changing .env — re-encrypt and commit the .enc file
sops -e --input-type dotenv --output-type dotenv .env > .env.enc
Requires the personal age key at ~/.config/sops/age/keys.txt (0600). On macOS
sops looks in ~/Library/Application Support/sops/age/keys.txt, so that path must
symlink to it. Recipient: age1yngetl7fdm7m0dlycfn3mfrgcvnj6ezeyjan2dppla78ndf7lsts3l5wve.
Note: sops's dotenv parser drops blank lines, so a decrypted .env may differ
cosmetically from the original. Keys and values are preserved exactly.