Transactions (POS)
All routes under /api/v1/pos require authentication.
Complete a Sale
POST /pos/sale
Complete a sale with multiple line items. Automatically decrements stock.
Auth: Required
Request Body:
{
"lines": [
{ "product_id": "uuid", "qty": 2 },
{ "product_id": "uuid", "qty": 1 }
],
"payment_method": "Cash",
"payment_reference": "REF123",
"discount_pct": 5.0,
"customer_name": "John Doe",
"customer_phone": "+255712345678",
"customer_id": "uuid",
"till_number": "TILL-001",
"notes": "Birthday discount applied"
}Payment Methods: Cash, M-Pesa, Tigo Pesa, Bank, Credit
Response (201): Transaction object.
Side Effects:
- Decrements stock for each line item
- Fires
transaction.createdevent (updates customer stats, auto-creates credit tab if Credit payment)
GET /pos/transactions
List transactions with filters and pagination.
Auth: Required
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
status | string | completed, refunded, void |
method | string | Cash, M-Pesa, etc. |
search | string | Search by reference |
customer_id | uuid | Filter by customer |
date_from | string | ISO date (YYYY-MM-DD) |
date_to | string | ISO date (YYYY-MM-DD) |
page | int | Page number |
page_size | int | Items per page |
Response: Paginated transaction list.
GET /pos/transactions/:id
Get a single transaction with line items.
Auth: Required
Response: Transaction object with lines array:
{
"id": "uuid",
"status": "completed",
"payment_method": "Cash",
"subtotal": 4500,
"tax": 810,
"discount": 225,
"total": 5085,
"lines": [
{
"product_id": "uuid",
"product_name": "Pepsi 500ml",
"qty": 2,
"unit_price": 1500,
"line_total": 3000
}
],
"created_at": "2024-01-15T10:30:00Z"
}POST /pos/transactions/:id/refund
Refund a completed transaction.
Auth: Required
Request Body:
{
"reason": "Customer returned damaged product"
}Response: Transaction with status: "refunded".
Side Effects:
- Fires
transaction.refundedevent (restores stock, settles credit tab if applicable)
POST /pos/transactions/:id/void
Void a transaction.
Auth: Required
Request Body:
{
"reason": "Incorrect amount entered"
}Response: Transaction with status: "void".
Side Effects:
- Fires
transaction.voidedevent
GET /pos/summary
Daily sales summary KPIs.
Auth: Required
Query Parameters:
| Parameter | Type | Default |
|---|---|---|
date_from | string | Start of today |
date_to | string | End of today |
Response (200):
{
"success": true,
"data": {
"total_sales": 525000,
"transaction_count": 45,
"average_ticket": 11667,
"items_sold": 120,
"cash_total": 300000,
"mpeca_total": 200000,
"credit_total": 25000,
"refund_count": 2,
"refund_total": 5000
}
}GET /pos/summary/hourly
Hourly sales breakdown for a specific date.
Auth: Required
Query: date (YYYY-MM-DD, default: today)
Response: Array of hourly data:
{
"success": true,
"data": [
{ "hour": 8, "sales": 15000, "count": 3 },
{ "hour": 9, "sales": 45000, "count": 8 },
...
]
}