Reference
Invoices
Create, list, fetch, and update sales invoices with automatic GL posting.
Invoices are the main retail-sales record in Finora Business. Creating one through the API posts a draft invoice, and when the invoice is sent, accepted, or marked paid, journal entries post automatically — you don't call a separate GL endpoint.
All monetary amounts are dual-written: amountKobo / totalKobo (integer,
source of truth) and amount / total (naira, convenience). Always compute on
kobo.
List invoices
/v1/invoicesReturns the most recent invoices for a business, newest first.
Query parameters
| Field | Type | Required | Notes |
|---|---|---|---|
businessId | string | yes | Business whose invoices you want. |
limit | integer | no | Default 50, max 100. |
status | string | no | Filter by draft, unsent, sent, partially_paid, paid, overdue, or cancelled. |
Example
curl "https://api.finorabusiness.com/v1/invoices?businessId=$BID&limit=10&status=sent" \
-H "Authorization: Bearer $FINORA_API_KEY"Response — 200
{
"success": true,
"data": {
"invoices": [
{
"id": "abc123",
"businessId": "biz_001",
"invoiceNumber": "INV-2026-001",
"customerId": "cust_xyz",
"invoiceDate": "2026-04-10T00:00:00.000Z",
"dueDate": "2026-04-24T00:00:00.000Z",
"lineItems": [
{
"description": "Website development",
"quantity": 1,
"unitPrice": 500000,
"amountKobo": 50000000,
"amount": 500000
}
],
"subtotalKobo": 50000000,
"vatRate": 7.5,
"vatAmountKobo": 3750000,
"totalKobo": 53750000,
"total": 537500,
"amountDueKobo": 53750000,
"status": "sent",
"createdAt": "2026-04-10T09:15:00.000Z"
}
],
"count": 1
},
"meta": {
"requestId": "req_a1b2c3d4e5f6g7h8",
"rateLimit": { "limit": 60, "remaining": 58 }
}
}Get a single invoice
/v1/invoices/{id}Fetches one invoice by ID.
Example
curl "https://api.finorabusiness.com/v1/invoices/abc123?businessId=$BID" \
-H "Authorization: Bearer $FINORA_API_KEY"Response — 200
Same invoice shape as list items, wrapped in data.
Errors
| Code | When |
|---|---|
NOT_FOUND | Invoice doesn't exist or belongs to another business. |
FORBIDDEN | The key's user isn't the business owner. |
Create an invoice
/v1/invoicesCreates a draft invoice. GL posting happens automatically when you mark it sent.
Query parameters
| Field | Type | Required | Notes |
|---|---|---|---|
businessId | string | yes | Business to create under. May also be passed in body. |
Body
| Field | Type | Required | Notes |
|---|---|---|---|
customerId | string | yes | Must be an existing customer in this business. |
lineItems | array | yes | 1–100 items. Each has description, quantity, unitPrice. |
lineItems[].description | string | yes | |
lineItems[].quantity | number | yes | 0 < quantity <= 1,000,000. |
lineItems[].unitPrice | number | yes | In naira. 0 <= unitPrice <= 100,000,000. |
invoiceDate | string (ISO 8601) | no | Defaults to today. |
dueDate | string (ISO 8601) | no | Defaults to today + 14 days. |
vatRate | number | no | Defaults to 7.5. Nigerian standard VAT. Pass 0 for zero-rated items. |
notes | string | no | Free-form note shown on the invoice PDF. |
Example
curl -X POST "https://api.finorabusiness.com/v1/invoices?businessId=$BID" \
-H "Authorization: Bearer $FINORA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"customerId": "cust_xyz",
"dueDate": "2026-05-01",
"lineItems": [
{
"description": "Monthly retainer — April",
"quantity": 1,
"unitPrice": 500000
}
]
}'Response — 201
{
"success": true,
"data": {
"id": "abc123",
"invoiceNumber": "INV-2026-001",
"total": 537500,
"amountDue": 537500,
"status": "draft",
"createdAt": "2026-04-17T09:15:00.000Z"
}
}Validation errors
Invalid bodies come back as 400 VALIDATION_ERROR with a field hint:
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "unitPrice must be between 0 and 100,000,000",
"field": "lineItems[0].unitPrice",
"requestId": "req_..."
}
}Update an invoice
/v1/invoices/{id}Edits a draft invoice. Finalised invoices are immutable — void and recreate instead.
Only four fields can be updated: customerId, dueDate, notes, lineItems.
Updating line items recalculates totals from scratch.
Example
curl -X PUT "https://api.finorabusiness.com/v1/invoices/abc123?businessId=$BID" \
-H "Authorization: Bearer $FINORA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"dueDate": "2026-05-15",
"notes": "Net 15 from approval date"
}'Response — 200
{
"success": true,
"data": {
"id": "abc123",
"dueDate": "2026-05-15T00:00:00.000Z",
"notes": "Net 15 from approval date",
"updatedAt": "2026-04-17T10:30:00.000Z"
}
}Life cycle
draft ─► (sent via email) ─► unsent / sent
│
├─► partially_paid ─► paid
├─► overdue
└─► cancelled (via void)- Draft invoices have no GL impact and can be edited or deleted freely.
- Sent invoices post a journal entry: DR Trade Debtors, CR Revenue, CR VAT Provision. They can be voided but not edited — voiding reverses the JE.
- Paid invoices (fully or partially) trigger receipt creation; the VAT provision transfers to VAT payable at that point.
Common flows
Record a sale in Finora Business from a WooCommerce order
POST /v1/customers/find-or-create— resolve the shopper to a Finora Business customer.POST /v1/invoices— create the invoice with line items matching the order.- When the Paystack payment confirms, hit
POST /v1/receiptsto record the payment.
Three API calls per order. After your trial, that's ₦300 per order. See billing for credit management.
Read reports based on API data
Once invoices exist, reports are a read away:
GET /v1/reports/profit-loss?businessId=X&startDate=...&endDate=...GET /v1/reports/ar-aging?businessId=X
See the reports module for all options.
Related endpoints
- Receipts — record payments against invoices
- Customers — the
customerIdyou need - Journal Entries — inspect the auto-posted GL entries
Reference index
Back to all modules