Skip to main content

Invoiceninja

@shrug/invoiceninjav2026.07.22.1· 3d agoMODELSREPORTS
01README

Invoice Ninja v5 (/api/v1) model — read-side snapshots (invoices, payments, expenses, vendors, expense categories) plus an idempotent, gated write path that syncs bank transactions into Invoice Ninja as expense records.

Status: live in production, promoted to stable, driving the mercury-expense-sync / ar-nightly workflows.

Methods

  • ReadlistInvoices, getInvoice, listPayments, listExpenses, expenseCategories, vendors. All paginate to completion.
  • Write (gated behind dryRun, default true) — upsertExpenses (check-then-create per BankTxn, dedup on custom_value1), upsertVendors / upsertExpenseCategories (auto-create reference data for candidate transactions), buildMapping (pure derive of the frozen vendor/category Mapping; under dryRun it also projects would-create vendor/category names onto a sentinel id so an all-dry-run preview resolves them instead of reporting a phantom unmatched vendor/category).

Dedup contract

Bank txn id → Invoice Ninja custom_value1 — the only field both writable and ?filter=-searchable. Check-then-create; no upsert; single-writer fan-out.

Curated overlays

vendorOverlay / categoryOverlay translate raw vendor/category names; vendorCategoryOverlay hard-overrides a vendor's category assignment, winning over the bank's raw category.

Header token auth (vaulted), X-Requested-With mandatory on every request.

02Models1
@shrug/invoiceninjav2026.07.22.1invoiceninja.ts
fn listInvoices(clientId?: string, clientStatus?: string)
GET /invoices (paginated, include=client) → invoices snapshot with
ArgumentTypeDescription
clientId?string
clientStatus?stringe.g. "1,2,3,4"
fn getInvoice(id: string)
GET /invoices/{id}?include=client,payments,activities → invoice.
ArgumentTypeDescription
idstring
fn listPayments(clientId?: string)
GET /payments (paginated, include=invoices,client) → payments
ArgumentTypeDescription
clientId?string
fn listExpenses(clientId?: string, vendorId?: string, categoryId?: string, filter?: string)
GET /expenses (paginated, include=vendor,category,client) → expenses
ArgumentTypeDescription
clientId?string
vendorId?string
categoryId?string
filter?string?filter= (searches custom_value1)
fn expenseCategories()
GET /expense_categories (paginated) → expenseCategories mapping snapshot.
fn vendors()
GET /vendors (paginated) → vendors mapping snapshot.
fn upsertExpenses(txns: array, mapping: record, dryRun?: boolean)
Check-then-create expenses for a BankTxn[] (dedup on custom_value1).
ArgumentTypeDescription
txnsarrayBankTxn[] (§6)
mappingrecordvendor/category/currency mapping tables (§7.3)
dryRun?booleanOverrides cfg.dryRun; default cfg.dryRun (true).
fn upsertVendors(txns: array, dryRun?: boolean)
Auto-create IN vendors for the distinct counterparties of candidate
ArgumentTypeDescription
txnsarrayBankTxn[] (§6)
dryRun?booleanOverrides cfg.dryRun; default cfg.dryRun (true).
fn upsertExpenseCategories(txns: array, dryRun?: boolean, fallbackName?: string)
Auto-create IN expense categories for the distinct rawCategories of
ArgumentTypeDescription
txnsarrayBankTxn[] (§6)
dryRun?booleanOverrides cfg.dryRun; default cfg.dryRun (true).
fallbackName?stringFallback category name always ensured; default 'Uncategorized'.
fn buildMapping(currencyId: string, fallbackCategoryName?: string, dryRun?: boolean)
PURE derive (no IN fetch): read the latest vendors + expenseCategories
ArgumentTypeDescription
currencyIdstringIN currency hashid (USD-only v1, §12.4).
fallbackCategoryName?stringCategory whose id becomes fallbackCategoryId; default 'Uncategorized'.
dryRun?booleanOverrides cfg.dryRun; default cfg.dryRun (true). Gates the §7.6

Resources

invoices(infinite)— An invoice list snapshot (§4.3)
invoice(infinite)— A single detailed invoice (`/invoices/{id}`)
payments(infinite)— A payment list snapshot (§4.3)
expenses(infinite)— An expense list snapshot (§4.3; carries custom_value1)
expenseCategories(infinite)— Expense-category mapping list (`/expense_categories`)
vendors(infinite)— Vendor mapping list (`/vendors`)
expenseSync(infinite)— One row per txn processed by upsertExpense (§4.4)
expenseSyncErrors(infinite)— Durable audit of failed write attempts (additive, WSPEC-04) — one row
vendorSync(infinite)— One row per distinct vendor processed by upsertVendors (§7.4).
vendorSyncErrors(infinite)— Durable audit of failed vendor creates (§7.4) — flushed even when empty.
categorySync(infinite)— One row per distinct category processed by upsertExpenseCategories (§7.4).
categorySyncErrors(infinite)— Durable audit of failed category creates (§7.4) — flushed even when empty.
mapping(infinite)— The derived Mapping (§7.3) emitted by buildMapping; the sync consumes it
03Reports3
@shrug/invoiceninja/ar-agingmodel
ar_aging.ts

Outstanding invoice balance bucketed by age past due (current/1-30/31-60/61-90/90+), per client and total

invoiceninjaaccountingar
@shrug/invoiceninja/expense-summarymodel
expense_summary.ts

Expenses grouped by category and vendor over a period, plus billable-but-uninvoiced backlog

invoiceninjaaccountingexpenses
@shrug/invoiceninja/reconcileworkflow
reconcile.ts

Gap / mismatch / duplicate report: source posted debits vs expenses recorded in Invoice Ninja — the 'am I caught up?' answer

invoiceninjaaccountingreconcile
04Previous Versions2
2026.07.21.1
2026.07.19.2
05Stats
A
100 / 100
Downloads
0
Archive size
68.5 KB
  • Has README or module doc2/2earned
  • README has a code example1/1earned
  • README is substantive1/1earned
  • Most symbols documented1/1earned
  • No slow types (deprecated)1/1earned
  • Dependencies pass trust audit2/2earned
  • Has description1/1earned
  • Platform support declared (or universal)2/2earned
  • License declared1/1earned
  • Verified public repository2/2earned
06Platforms
07Labels