Finance
Procurement (suppliers, purchase orders, receipts), supplier bills and payments, COD receivables, cash accounts, expenses and cash-basis reports.
The finance module is a lightweight ERP attached to your stores: procurement, accounts payable,
COD receivables, cash accounts and reports. Everything lives under /v1/finance and requires a
bearer token.
- Project-scoped. Every request carries a
projectId(query param on reads, body field on writes). A project groups one or more stores (store.projectId) and is shared with inventory; list yours viaGET /v1/inventory/projects. - Permissions. The store owner can do everything. Members need the
finance.readpermission for reads andfinance.writefor writes (enforced per store role). The finance module must also be enabled for the store (billing entitlement). - Lists accept
page,limit,searchand the filterator, and return the standard paginator envelope. Batch endpoints (:batchDelete,:batchPay, …) take{ projectId, names, returnPartialSuccess? }and return the partial-success envelope.
Purchase-to-pay flow
Posting a receipt is one transaction with three effects: receipt lines become
inventory objects (batch buckets carrying unit_cost in metadata), a reason: "receive"
movement is recorded per line, and an open supplier bill is created for the total. Posting is
idempotent. Rules that follow from this:
- Batch-cost invariant — an existing batch only accepts stock-in at the same
unitCost; receiving at a different cost fails withBATCH_COST_MISMATCH(use a differentbatch). - Voiding a posted receipt reverses the stock-in. It fails with
RECEIPT_REVERSE_BLOCKEDif quantity was already reserved or sold, and withBILL_NOT_OPENif the bill has payments — pass{ "voidPayments": true }to void those payments in the same transaction. - Purchase orders move through
draft,sent,partial,received,cancelled; invalid transitions fail withINVALID_STATE_TRANSITION. Receipts don't require a PO (purchaseOrderIdis optional).
At a glance
All endpoints require Bearer auth.
Suppliers
| Method | Path | Description | Auth |
|---|---|---|---|
GET | /v1/finance/suppliers | List suppliers | Bearer |
POST | /v1/finance/suppliers | Create a supplier | Bearer |
GET | /v1/finance/suppliers/{id} | Get one | Bearer |
PUT | /v1/finance/suppliers/{id} | Update | Bearer |
DELETE | /v1/finance/suppliers/{id} | Delete | Bearer |
POST | /v1/finance/suppliers:batchDelete | Delete many | Bearer |
Purchase orders
| Method | Path | Description | Auth |
|---|---|---|---|
GET | /v1/finance/purchase-orders | List POs | Bearer |
POST | /v1/finance/purchase-orders | Create (status draft) | Bearer |
GET | /v1/finance/purchase-orders/{id} | Get one | Bearer |
PUT | /v1/finance/purchase-orders/{id} | Update reference/notes/currency/expectedAt/items | Bearer |
POST | /v1/finance/purchase-orders/{id}/send | Transition to sent | Bearer |
POST | /v1/finance/purchase-orders/{id}/cancel | Transition to cancelled | Bearer |
POST | /v1/finance/purchase-orders/{id}/status | Set explicit status (state machine enforced) | Bearer |
POST | /v1/finance/purchase-orders:batchSend | Send many drafts | Bearer |
POST | /v1/finance/purchase-orders:batchCancel | Cancel many | Bearer |
GET | /v1/finance/purchase-orders/{id}/pdf | PDF (returns fileUrl) | Bearer |
POST | /v1/finance/purchase-orders:batchPdf | Merged PDF for many | Bearer |
Purchase receipts
| Method | Path | Description | Auth |
|---|---|---|---|
GET | /v1/finance/purchase-receipts | List receipts (with lines + supplier) | Bearer |
POST | /v1/finance/purchase-receipts | Create draft receipt with lines | Bearer |
GET | /v1/finance/purchase-receipts/{id} | Get one | Bearer |
PUT | /v1/finance/purchase-receipts/{id} | Update notes/reference/attachments only | Bearer |
POST | /v1/finance/purchase-receipts/{id}/post | Post: stock-in + create bill (idempotent) | Bearer |
POST | /v1/finance/purchase-receipts/{id}/void | Void a posted receipt (voidPayments optional) | Bearer |
POST | /v1/finance/purchase-receipts:batchPost | Post many drafts | Bearer |
POST | /v1/finance/purchase-receipts:batchVoid | Void many (always voids payments) | Bearer |
GET | /v1/finance/purchase-receipts/{id}/pdf | PDF (returns fileUrl) | Bearer |
POST | /v1/finance/purchase-receipts:batchPdf | Merged PDF for many | Bearer |
Supplier bills & payments (AP)
| Method | Path | Description | Auth |
|---|---|---|---|
GET | /v1/finance/supplier-bills | List bills | Bearer |
POST | /v1/finance/supplier-bills | Create an ad-hoc bill (not from a receipt) | Bearer |
GET | /v1/finance/supplier-bills/{id} | Get one (with payments + receipt lines) | Bearer |
POST | /v1/finance/supplier-bills/{id}/pay | Record a (partial) payment — returns bill + payment | Bearer |
POST | /v1/finance/supplier-bills:batchPay | Pay the full remaining balance on each bill | Bearer |
POST | /v1/finance/supplier-bills:batchVoidPayments | Void all payments on each bill | Bearer |
GET | /v1/finance/supplier-bills/{id}/pdf | PDF (returns fileUrl) | Bearer |
POST | /v1/finance/supplier-bills:batchPdf | Merged PDF for many | Bearer |
GET | /v1/finance/supplier-payments | List payments (supplierBillId filter) | Bearer |
GET | /v1/finance/supplier-payments/{id} | Get one | Bearer |
POST | /v1/finance/supplier-payments/{id}/void | Void a payment, recompute the bill | Bearer |
Bills have no update or delete — totals are managed through payments and voids.
Receivables & customer payments (AR)
Receivables are derived from open COD orders — read-only rows keyed by order id.
| Method | Path | Description | Auth |
|---|---|---|---|
GET | /v1/finance/receivables | Open order receivables (q, codInTransit filters) | Bearer |
GET | /v1/finance/receivables/{orderId} | Receivable detail + linked order | Bearer |
POST | /v1/finance/receivables:batchCollect | Collect the full balance due on each order | Bearer |
GET | /v1/finance/customer-payments | List collected payments (orderId filter) | Bearer |
POST | /v1/finance/orders/{orderId}/collect | Record a customer/COD payment | Bearer |
POST | /v1/finance/customer-payments/{id}/void | Void a customer payment | Bearer |
Financial accounts & transfers
| Method | Path | Description | Auth |
|---|---|---|---|
GET | /v1/finance/financial-accounts | List accounts with computed balances | Bearer |
POST | /v1/finance/financial-accounts | Create (name, type, openingBalance, isDefault) | Bearer |
GET | /v1/finance/financial-accounts/{id} | Get one with balance | Bearer |
PUT | /v1/finance/financial-accounts/{id} | Update | Bearer |
DELETE | /v1/finance/financial-accounts/{id} | Delete | Bearer |
POST | /v1/finance/financial-accounts:batchDelete | Delete many | Bearer |
GET | /v1/finance/transfers | Money timeline (voided hidden by default) | Bearer |
POST | /v1/finance/transfers | Record money in/out/between accounts | Bearer |
GET | /v1/finance/transfers/{id} | Get one | Bearer |
POST | /v1/finance/transfers/{id}/void | Soft-void (excluded from balances) | Bearer |
Expenses & other income
Both follow the same CRUD shape; categories are simple named trees (parentId).
| Method | Path | Description | Auth |
|---|---|---|---|
GET / POST | /v1/finance/expenses | List / record expenses | Bearer |
GET / PUT / DELETE | /v1/finance/expenses/{id} | Get / update (set status: "voided" to void) / delete | Bearer |
POST | /v1/finance/expenses:batchDelete | Delete many | Bearer |
GET / POST | /v1/finance/expense-categories | List / create categories | Bearer |
GET / PUT / DELETE | /v1/finance/expense-categories/{id} | Get / update / delete | Bearer |
POST | /v1/finance/expense-categories:batchDelete | Delete many | Bearer |
GET / POST | /v1/finance/other-incomes | List / record other income | Bearer |
GET / PUT / DELETE | /v1/finance/other-incomes/{id} | Get / update / delete | Bearer |
POST | /v1/finance/other-incomes:batchDelete | Delete many | Bearer |
GET / POST | /v1/finance/other-income-categories | List / create categories | Bearer |
GET / PUT / DELETE | /v1/finance/other-income-categories/{id} | Get / update / delete | Bearer |
POST | /v1/finance/other-income-categories:batchDelete | Delete many | Bearer |
Reports & reset
| Method | Path | Description | Auth |
|---|---|---|---|
GET | /v1/finance/reports/overview | Consolidated bundle (from, to) | Bearer |
GET | /v1/finance/reports/cash-position | Balances across financial accounts | Bearer |
GET | /v1/finance/reports/ap-aging | Payables aging buckets | Bearer |
GET | /v1/finance/reports/ar-aging | Receivables aging buckets | Bearer |
GET | /v1/finance/reports/pnl | Cash-basis P&L (from, to, groupBy=day) | Bearer |
POST | /v1/finance/reset | Wipe project finance data — owner only, body confirm: "RESET" | Bearer |
Reports are cached server-side and invalidated on writes.
Examples
Create a supplier:
curl -X POST "https://api.feeef.org/v1/finance/suppliers" \
-H "Authorization: Bearer $FEEEF_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "projectId": "PROJECT_ID", "name": "Atlas Textiles", "paymentTerms": "NET30" }'Create a purchase order and send it:
curl -X POST "https://api.feeef.org/v1/finance/purchase-orders" \
-H "Authorization: Bearer $FEEEF_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"projectId": "PROJECT_ID",
"supplierId": "SUPPLIER_ID",
"items": [{ "sku": "tshirt/red/m", "qtyOrdered": 50, "unitCost": 950 }]
}'
curl -X POST "https://api.feeef.org/v1/finance/purchase-orders/PO_ID/send?projectId=PROJECT_ID" \
-H "Authorization: Bearer $FEEEF_TOKEN"Receive the goods — create a receipt and post it (stock-in + bill):
curl -X POST "https://api.feeef.org/v1/finance/purchase-receipts" \
-H "Authorization: Bearer $FEEEF_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"projectId": "PROJECT_ID",
"supplierId": "SUPPLIER_ID",
"purchaseOrderId": "PO_ID",
"lines": [{ "sku": "tshirt/red/m", "qtyReceived": 50, "unitCost": 950, "batch": "B-2026-08" }]
}'
curl -X POST "https://api.feeef.org/v1/finance/purchase-receipts/RECEIPT_ID/post?projectId=PROJECT_ID" \
-H "Authorization: Bearer $FEEEF_TOKEN"Pay the supplier bill from a financial account:
curl -X POST "https://api.feeef.org/v1/finance/supplier-bills/BILL_ID/pay" \
-H "Authorization: Bearer $FEEEF_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "projectId": "PROJECT_ID", "financialAccountId": "ACCOUNT_ID", "amount": 47500 }'Collect a COD payment against an order:
curl -X POST "https://api.feeef.org/v1/finance/orders/ORDER_ID/collect" \
-H "Authorization: Bearer $FEEEF_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "projectId": "PROJECT_ID", "financialAccountId": "ACCOUNT_ID", "amount": 3200 }'Notes
- PDF endpoints respond with
{ "fileUrl": "https://…" }— a hosted PDF, not a binary body. POST /v1/finance/expensesis soft-deprecated: it records the expense as a native finance transfer (type: "expense") and returns that transfer row. Prefer/v1/finance/transfersfor new integrations.- Payments (supplier and customer) are immutable — correct mistakes by voiding, never editing.
- The general-ledger endpoints (
gl-accounts,journal-entries,reports/trial-balance,reports/balance-sheet,accounting-periods) are retired; the routes are disabled even though older SDK builds still expose wrapper methods for them.