Sales & Transactions API
The Sales API is the core transactional engine of Tracepos. When an order is submitted through this endpoint, Tracepos executes an atomic database transaction that:
- Decrements Stock: Deducts inventory from the warehouse using FIFO batch allocation (
product_batches). - Generates Invoices: Issues a sequence-controlled company invoice number (
SAL-XXXXX). - Processes Payments: Immediately settles full or partial payments if the
all_paymentsarray is provided. - Posts Journal Entries: Creates a balanced double-entry accounting record (Debiting Cash/Receivables, Crediting Sales Revenue and Sales Tax).
- Fires Webhooks: Dispatches
order.createdororder.paidreal-time events to all registered webhook endpoints.
1. List Sales Orders
Retrieve a paginated list of sales orders created in the authorized warehouse.
/api/v1/public/salesQuery Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
dates | String | - | Date range filter in YYYY-MM-DD,YYYY-MM-DD format (e.g. 2026-09-01,2026-09-30) |
limit | Integer | 20 | Items per page (maximum: 100) |
page | Integer | 1 | Pagination page number |
Code Examples
:::: code-group ::: code-group-item cURL
curl -X GET "https://api.tracepos.com/api/v1/public/sales?dates=2026-09-01,2026-09-30&limit=20" \
-H "X-Tracepos-Public-Key: tp_pub_live_7f8a9c2d1e" \
-H "X-Tracepos-Secret-Key: tp_sec_live_9b3e1f7a4c" \
-H "Accept: application/json"
::: ::: code-group-item Node.js (Axios)
const axios = require('axios');
async function listSales(startDate, endDate) {
const response = await axios.get('https://api.tracepos.com/api/v1/public/sales', {
params: {
dates: `${startDate},${endDate}`,
limit: 20
},
headers: {
'X-Tracepos-Public-Key': process.env.TRACEPOS_PUBLIC_KEY,
'X-Tracepos-Secret-Key': process.env.TRACEPOS_SECRET_KEY,
'Accept': 'application/json'
}
});
return response.data.data;
}
::: ::: code-group-item Python (Requests)
import os
import requests
headers = {
"X-Tracepos-Public-Key": os.getenv("TRACEPOS_PUBLIC_KEY"),
"X-Tracepos-Secret-Key": os.getenv("TRACEPOS_SECRET_KEY"),
"Accept": "application/json"
}
response = requests.get(
"https://api.tracepos.com/api/v1/public/sales",
params={"dates": "2026-09-01,2026-09-30", "limit": 20},
headers=headers
)
sales = response.json().get("data", {})
::: ::: code-group-item PHP (Guzzle)
use GuzzleHttp\Client;
$client = new Client(['base_uri' => 'https://api.tracepos.com/api/v1/public/']);
$response = $client->get('sales', [
'query' => [
'dates' => '2026-09-01,2026-09-30',
'limit' => 20
],
'headers' => [
'X-Tracepos-Public-Key' => getenv('TRACEPOS_PUBLIC_KEY'),
'X-Tracepos-Secret-Key' => getenv('TRACEPOS_SECRET_KEY'),
'Accept' => 'application/json'
]
]);
$sales = json_decode($response->getBody()->getContents(), true)['data'];
::: ::::
Response (200 OK)
{
"status": "success",
"data": {
"current_page": 1,
"data": [
{
"id": 182,
"unique_id": "ord_9182ab3c4d5e",
"invoice_number": "SAL-00451",
"order_type": "sales",
"order_date": "2026-09-30",
"subtotal": "5500.00",
"tax_amount": "412.50",
"discount": "0.00",
"shipping": "0.00",
"total": "5912.50",
"paid_amount": "5912.50",
"due_amount": "0.00",
"order_status": "delivered",
"payment_status": "paid",
"user": {
"id": 105,
"name": "Adeola Adeleke"
},
"staff_member": {
"id": 2,
"name": "POS Terminal 01 Key"
}
}
],
"total": 450
}
}
2. Get Single Sale Order
Retrieve complete line items, applied product taxes, and customer details for a single sale.
/api/v1/public/sales/{id}Response (200 OK)
{
"status": "success",
"data": {
"id": 182,
"unique_id": "ord_9182ab3c4d5e",
"invoice_number": "SAL-00451",
"order_date": "2026-09-30",
"subtotal": "5500.00",
"total": "5912.50",
"paid_amount": "5912.50",
"due_amount": "0.00",
"payment_status": "paid",
"user": {
"id": 105,
"name": "Adeola Adeleke",
"phone": "+2348031234567"
},
"items": [
{
"id": 412,
"product_id": 45,
"quantity": 2,
"unit_price": "2750.00",
"subtotal": "5500.00",
"product": {
"id": 45,
"name": "Premium Basmati Rice 5kg",
"item_code": "RICE-5KG-01"
}
}
]
}
}
3. Create Sale (POS Checkout)
Record a completed or credit sale from a smart POS terminal, self-checkout kiosk, or eCommerce gateway.
/api/v1/public/salesRequest Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
user_id | Integer / String | Yes | Customer ID (e.g. 105 or Walk-in customer ID 106) |
order_date | Date (YYYY-MM-DD) | Yes | Transaction date |
product_items | Array | Yes | Line items array (minimum 1 item) |
product_items[].xid | String / Integer | Yes | Product hashed ID (xid) or numeric ID |
product_items[].quantity | Numeric | Yes | Units purchased (e.g. 1, 2.5) |
product_items[].unit_price | Numeric | Yes | Sales price per unit |
product_items[].selected_unit_id | String / Integer | No | Unit of measure hash ID if multi-unit product |
product_items[].tax_id | String / Integer | No | Applied tax rule ID |
product_items[].discount_rate | Numeric | No | Line-item discount percentage |
discount | Numeric | No | Order-level discount amount |
shipping | Numeric | No | Shipping or delivery fee |
invoice_number | String | No | Custom invoice number (auto-generated if omitted) |
all_payments | Array | No | Immediate payment breakdown (Cash, Card, Transfer) |
all_payments[].amount | Numeric | Yes (if payment) | Payment tender amount |
all_payments[].payment_mode_id | String / Integer | Yes (if payment) | Payment mode ID (e.g. 1 for Cash, 2 for POS Card) |
Request Payload Example
{
"user_id": 105,
"order_date": "2026-09-30",
"product_items": [
{
"xid": "J1rA2l6xN4",
"quantity": 2,
"unit_price": 2750.00
},
{
"xid": "B9mP8k3vL0",
"quantity": 1,
"unit_price": 1200.00
}
],
"discount": 0.00,
"shipping": 0.00,
"all_payments": [
{
"amount": 6700.00,
"payment_mode_id": 2
}
]
}
Code Examples
:::: code-group ::: code-group-item cURL
curl -X POST "https://api.tracepos.com/api/v1/public/sales" \
-H "X-Tracepos-Public-Key: tp_pub_live_7f8a9c2d1e" \
-H "X-Tracepos-Secret-Key: tp_sec_live_9b3e1f7a4c" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"user_id": 105,
"order_date": "2026-09-30",
"product_items": [
{
"xid": "J1rA2l6xN4",
"quantity": 2,
"unit_price": 2750.00
}
],
"all_payments": [
{
"amount": 5500.00,
"payment_mode_id": 2
}
]
}'
::: ::: code-group-item Node.js (Axios)
const axios = require('axios');
async function createSale(orderPayload) {
const response = await axios.post(
'https://api.tracepos.com/api/v1/public/sales',
orderPayload,
{
headers: {
'X-Tracepos-Public-Key': process.env.TRACEPOS_PUBLIC_KEY,
'X-Tracepos-Secret-Key': process.env.TRACEPOS_SECRET_KEY,
'Content-Type': 'application/json',
'Accept': 'application/json'
}
}
);
return response.data;
}
::: ::: code-group-item Python (Requests)
import os
import requests
headers = {
"X-Tracepos-Public-Key": os.getenv("TRACEPOS_PUBLIC_KEY"),
"X-Tracepos-Secret-Key": os.getenv("TRACEPOS_SECRET_KEY"),
"Content-Type": "application/json",
"Accept": "application/json"
}
payload = {
"user_id": 105,
"order_date": "2026-09-30",
"product_items": [
{"xid": "J1rA2l6xN4", "quantity": 2, "unit_price": 2750.00}
],
"all_payments": [
{"amount": 5500.00, "payment_mode_id": 2}
]
}
response = requests.post("https://api.tracepos.com/api/v1/public/sales", json=payload, headers=headers)
result = response.json()
::: ::: code-group-item PHP (Guzzle)
use GuzzleHttp\Client;
$client = new Client(['base_uri' => 'https://api.tracepos.com/api/v1/public/']);
$response = $client->post('sales', [
'headers' => [
'X-Tracepos-Public-Key' => getenv('TRACEPOS_PUBLIC_KEY'),
'X-Tracepos-Secret-Key' => getenv('TRACEPOS_SECRET_KEY'),
'Content-Type' => 'application/json',
'Accept' => 'application/json'
],
'json' => [
'user_id' => 105,
'order_date' => '2026-09-30',
'product_items' => [
['xid' => 'J1rA2l6xN4', 'quantity' => 2, 'unit_price' => 2750.00]
],
'all_payments' => [
['amount' => 5500.00, 'payment_mode_id' => 2]
]
]
]);
$result = json_decode($response->getBody()->getContents(), true);
::: ::::
Response (200 OK)
{
"status": "success",
"order_id": 182,
"invoice_number": "SAL-00451",
"total_amount": 5500.00
}
Split Tender Payments
Tracepos supports multi-tender transactions directly within all_payments. For example, a customer paying part Cash and part Card:
{
"user_id": 105,
"order_date": "2026-09-30",
"product_items": [
{ "xid": "J1rA2l6xN4", "quantity": 4, "unit_price": 2500.00 }
],
"all_payments": [
{ "amount": 4000.00, "payment_mode_id": 1 },
{ "amount": 6000.00, "payment_mode_id": 2 }
]
}
Tracepos automatically creates two Payment records and two OrderPayment ledger entries, marking the order as payment_status = "paid".
