Finance API
API endpoints for the Finance module. All endpoints require JWT authentication and are prefixed with /finance.
Authentication
All endpoints require a valid JWT token:
Authorization: Bearer <token>Role-based access is enforced per endpoint (see the Auth column).
Invoices
| Method | Path | Description | Auth |
|---|---|---|---|
| GET | /finance/invoices | List invoices with filters | finance.read |
| GET | /finance/invoices/{id} | Get invoice details with items | finance.read |
| POST | /finance/invoices/generate | Generate invoice for a customer | finance.write |
| PUT | /finance/invoices/{id}/status | Update invoice status | finance.write |
| GET | /finance/invoices/{id}/pdf | Download invoice PDF | finance.read |
| POST | /finance/invoices/{id}/send | Send invoice by email | finance.write |
POST /finance/invoices/generate
Generates an invoice from unbilled transactions for a customer within a date range.
Request:
json
{
"customer_id": 1234,
"date_from": "2026-02-01",
"date_to": "2026-02-28",
"notes": "February 2026 billing"
}Response (201):
json
{
"id": 5678,
"invoice_number": "INV-2026-001234",
"customer_id": 1234,
"status": "draft",
"subtotal": 15000.00,
"tax_amount": 2400.00,
"total": 17400.00,
"items_count": 42,
"pdf_url": "https://s3.amazonaws.com/envia/invoices/INV-2026-001234.pdf",
"issued_at": null,
"due_date": null
}Errors:
| Code | Description |
|---|---|
| 400 | Invalid date range or missing fields |
| 404 | Customer not found |
| 409 | No unbilled transactions in the given range |
GET /finance/invoices
Query Parameters:
| Param | Type | Required | Description |
|---|---|---|---|
customer_id | number | No | Filter by customer |
status | string | No | Filter by status: draft, issued, paid, overdue, cancelled |
date_from | string | No | Issued date range start (YYYY-MM-DD) |
date_to | string | No | Issued date range end (YYYY-MM-DD) |
page | number | No | Page number (default: 1) |
limit | number | No | Items per page (default: 20, max: 100) |
Response (200):
json
{
"data": [
{
"id": 5678,
"invoice_number": "INV-2026-001234",
"customer_name": "Acme Corp",
"status": "issued",
"total": 17400.00,
"issued_at": "2026-03-01T06:00:00Z",
"due_date": "2026-03-15"
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 156
}
}Payments
| Method | Path | Description | Auth |
|---|---|---|---|
| GET | /finance/payments | List payments with filters | finance.read |
| GET | /finance/payments/{id} | Get payment details | finance.read |
| POST | /finance/payments | Register a new payment | finance.write |
| DELETE | /finance/payments/{id} | Cancel a payment (soft delete) | finance.admin |
POST /finance/payments
Registers a payment and updates the customer balance.
Request:
json
{
"customer_id": 1234,
"amount": 17400.00,
"method": "bank_transfer",
"reference": "TRF-20260301-ABC123",
"invoice_id": 5678,
"notes": "Payment for February invoice"
}Response (201):
json
{
"id": 9012,
"customer_id": 1234,
"amount": 17400.00,
"method": "bank_transfer",
"reference": "TRF-20260301-ABC123",
"invoice_id": 5678,
"balance_after": 0.00,
"created_at": "2026-03-01T14:30:00Z",
"created_by": {
"id": 42,
"name": "Admin User"
}
}Errors:
| Code | Description |
|---|---|
| 400 | Invalid amount or missing fields |
| 404 | Customer or invoice not found |
| 409 | Invoice already fully paid |
Transactions
| Method | Path | Description | Auth |
|---|---|---|---|
| GET | /finance/transactions | List transactions with filters | finance.read |
| GET | /finance/transactions/{id} | Get transaction details | finance.read |
| POST | /finance/transactions/adjustment | Create manual adjustment | finance.admin |
GET /finance/transactions
Query Parameters:
| Param | Type | Required | Description |
|---|---|---|---|
customer_id | number | No | Filter by customer |
type | string | No | Filter: charge, payment, refund, adjustment |
date_from | string | No | Date range start |
date_to | string | No | Date range end |
billed | boolean | No | true = billed, false = unbilled |
page | number | No | Page number (default: 1) |
limit | number | No | Items per page (default: 50, max: 200) |
Balances
| Method | Path | Description | Auth |
|---|---|---|---|
| GET | /finance/balances | List all customer balances | finance.read |
| GET | /finance/balances/{customer_id} | Get balance for a customer | finance.read |
| GET | /finance/balances/{customer_id}/summary | Balance summary with breakdown | finance.read |
GET /finance/balances/{customer_id}/summary
Returns a detailed balance breakdown for a customer.
Response (200):
json
{
"customer_id": 1234,
"customer_name": "Acme Corp",
"current_balance": -5200.00,
"breakdown": {
"total_charges": 125000.00,
"total_payments": 119800.00,
"total_adjustments": 0.00,
"total_refunds": 0.00
},
"unbilled_amount": 5200.00,
"overdue_invoices": 0,
"last_payment_date": "2026-02-28T10:15:00Z"
}Reports
| Method | Path | Description | Auth |
|---|---|---|---|
| GET | /finance/reports/revenue | Revenue report by period | finance.admin |
| GET | /finance/reports/aging | Accounts receivable aging report | finance.admin |
| GET | /finance/reports/export | Export transactions to Excel | finance.read |
GET /finance/reports/export
Query Parameters:
| Param | Type | Required | Description |
|---|---|---|---|
customer_id | number | No | Filter by customer |
date_from | string | Yes | Export range start |
date_to | string | Yes | Export range end |
format | string | No | xlsx (default) or csv |
Response: File download (Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet)
Common Error Responses
All errors follow the Boom format:
json
{
"statusCode": 400,
"error": "Bad Request",
"message": "Descriptive error message"
}| Status | When |
|---|---|
| 400 | Validation failed (Joi schema) |
| 401 | Missing or invalid JWT |
| 403 | Insufficient permissions for the action |
| 404 | Resource not found |
| 409 | Business rule conflict (e.g., duplicate payment, no unbilled transactions) |
