Customers API
Endpoints the POS app uses to search, create and edit customers, manage their addresses, read their ledger and look up location lists.
These endpoints power the Customers screen, the customer picker in Billing (F3 in Finalize Order), the Add New Customer form and the Customer Profile tabs.
All paths are relative to {{server}} (your tenant domain). Every call sends:
Header | Value |
|---|---|
|
|
|
|
|
|
|
|
Most calls also add store_id = the active store (for example 2 for "متجر النجمة Store").
See API overview for the envelope, pagination and error codes.
Endpoint summary
Method | Path | Used by |
|---|---|---|
GET |
| Customers list, customer picker, phone lookup |
GET |
| Lightweight paginated list (not used by the POS screens) |
POST |
| Add New Customer form |
POST |
| Customer Profile > Edit Details |
POST |
| Customer Profile > Customer Address (add) |
POST |
| Customer Profile > Customer Address (edit) |
POST |
| Billing cart history icon (last purchase price) |
GET |
| Load one customer by id |
GET |
| Paginated list of user accounts |
GET |
| State picker in the customer form |
GET |
| City (district) picker |
GET |
| Pincode picker |
GET |
| Customer Profile > Transactions, Customer Transactions report |
GET /api/v1/customer/customer-searchbar
Returns the customers of the company with their current balance, loyalty card, KYC (CR/VAT numbers) and addresses.
Used by: Customers list (search as you type), the customer picker in Billing, and the app's offline customer cache (Settings > Offline Data > Customers).
Query parameters
Name | Type | Notes |
|---|---|---|
| string | Partial match on the customer name |
| string | Partial match on phone or alternate phone |
| string | Partial match on email |
| string |
|
| string | Accepted by the server |
| integer | Return one customer |
| boolean |
|
| integer | The app sends |
| integer | Active store |
Response
data is a flat array (not a paginator). balance is computed from the ledger at request time. A positive balance means the customer owes the store.
{
"status": "success",
"message": "success",
"data": [
{
"id": 2767,
"user_id": 1497,
"name": "Layla Al-Harbi",
"email": "layla.demo@funzcart.in",
"phone": "0551234567",
"alt_phone": null,
"dob": null,
"gender": null,
"profile_image": null,
"balance": 0,
"payment_type": "to_receive",
"customer_type": "B2C",
"profile_verified": false,
"store_id": 2,
"store_name": "متجر النجمة Store ",
"company_id": 2,
"card_number": null,
"loyalty_points": null,
"valid_from": null,
"valid_until": null,
"card_status": null,
"membership_name": null,
"membership_code": null,
"min_redeemable_points": null,
"price_per_point": null,
"kyc": [],
"addresses": [],
"transactions": [],
"orders": [],
"created_at": "2026-10-27 15:10:42",
"updated_at": "2026-10-27 15:10:42"
}
]
}*Shape from a captured response; the demo customer's values are filled in. The real list has many more rows.*
Notes
- A B2B customer's CR and VAT numbers come back inside
kyc(key/value rows). - When the app is offline, the customer list and picker read the last cached copy of this response.
GET /api/v1/customer/customer-list
A lighter, paginated list (15 per page by default, maximum 100) with only name, email, balance, phone, alt phone and type. The POS screens use customer-searchbar instead; this endpoint is useful for integrations.
Name | Type | Notes |
|---|---|---|
| string | Partial matches |
| integer | Limit to one store |
| integer | Default 15, maximum 100 |
| integer | Page number |
{
"status": "success",
"message": "success",
"data": {
"current_page": 1,
"data": [
{
"name": "Layla Al-Harbi",
"email": "layla.demo@funzcart.in",
"balance": 0,
"phone": "0551234567",
"alt_phone": null,
"customerType": "B2C"
}
],
"per_page": 15,
"last_page": 15,
"total": 225
}
}*Trimmed: paginator link fields removed.*
POST /api/v1/customer/add-customer
Creates a customer (and its user account) in the company.
Used by: Add New Customer (from the Customers list, from the customer picker in Billing, and from Finalize Order). After success the app selects the new customer automatically.
Request body
Field | Required | Notes |
|---|---|---|
| yes | Digits only, 8–15 digits. Must be unique within the company. The form auto-formats |
| no | The form joins first and last name |
| no | Valid email |
| no | Active store; the first store of the company if missing |
| yes |
|
| yes | Opening balance, number (send |
| yes |
|
| no | Business (B2B) registration numbers, stored as KYC |
| no | 8–15 digits |
| no |
|
| no |
|
| no | The form joins building, street and area into one line |
| no | The app sends the district (city) id as a string |
| no | The app sends the state id as a string |
| no | Pincode text |
| no | Country name |
Example (the demo customer):
{
"phone": "0551234567",
"name": "Layla Al-Harbi",
"email": "layla.demo@funzcart.in",
"store_id": "2",
"address": "",
"pin_code": "21442",
"city": "<district-id-for-Jeddah>",
"state": "<state-id-for-Makkah>",
"country": "Saudi Arabia",
"balance": "0",
"payment_type": "to_receive",
"customer_type": "B2C"
}Response
{
"status": "success",
"message": "Customer added successfully",
"data": {
"user": { "id": 1497, "name": "Layla Al-Harbi", "email": "layla.demo@funzcart.in", "phone": "0551234567" },
"customer": { "id": 2767, "user_id": 1497, "store_id": 2, "customer_type": "B2C", "payment_type": "to_receive", "balance": 0 },
"address": null
}
}*Trimmed to the main fields.*
Rules and side effects
- Opening balance sign.
to_receivestores the balance as a negative number (the store owes the customer);to_paystores it as positive (the customer owes the store). - If
balance> 0 an "Initial balance" ledger line is created:to_pay→ Credit / Receipt,to_receive→ Debit / Voucher. - Duplicate phone in the same company → 422
"This phone already exists in this company." - Validation errors → 422
{"status":"error","errors":{...}}.
POST /api/v1/customer/customer-edit
Updates a customer. The app sends only the fields that changed plus customer_id. An empty string clears a field.
Used by: Customer Profile > Edit Details.
{
"customer_id": 2767,
"email": "layla.demo@funzcart.in",
"customer_type": "B2B",
"cr_number": "<cr-number>",
"vat_number": "<vat-number>"
}Accepted fields are the same as add-customer (name, email, phone, alt_phone, gender, dob, store_id, balance, payment_type, customer_type, cr_number, vat_number, address fields), all optional. If nothing changed the app does not send a request.
Response: {"status":"success","message":"...","data":{...customer}}. Unknown customer → "Customer not found".
POST /api/v1/customer/executive-add-address
Adds a delivery address to a customer.
Used by: Customer Profile > Customer Address > add.
Field | Required | Notes |
|---|---|---|
| yes | Customer id |
| yes | Defaults to the customer's name |
| yes | Defaults to the customer's phone |
| yes | Street address |
| yes | Id from |
| yes |
|
| no | City text |
| no | Ids from the location lists |
| no | Landmark |
{
"customer_id": 2767,
"name": "Layla Al-Harbi",
"phone": "0551234567",
"address": "King Abdulaziz Road",
"city": "Jeddah",
"state_id": "<state-id>",
"district_id": "<district-id>",
"pincode_id": "<pincode-id>",
"landmark": "Near Al Hamra Mall",
"type": "Home"
}POST /api/v1/customer/executive-update-address/{id}
Same body as add, for the address with id {id}. Used when you edit an address on the Customer Address tab.
POST /api/v1/customer/customer-last-purchases
Returns the last 5 orders in which this customer bought this product, newest first.
Used by: the history icon on a cart line in Billing (shows the customer's last purchase price). The icon appears when the website setting SHOW_CUSTOMER_LAST_BUYED_PRICE_LIST is on.
{ "customer_id": 2767, "product_id": 15485 }{
"success": true,
"data": [
{ "price": 220.0, "quantity": 2, "total": 440.0, "date": "2026-10-27", "order_number": "ORD-004851" }
]
}*Illustrative values from the demo order.* Missing ids → 400 {"success":false,"message":"customer_id and product_id are required"}; unknown customer → 404.
GET /api/v1/user/get-user-details/{customerId}
Loads one customer by customer id (despite the path name).
{
"status": "success",
"message": "Customer details fetched successfully.",
"data": {
"id": 2767,
"customer_id": 2767,
"user_id": 1497,
"store_id": 2,
"name": "Layla Al-Harbi",
"email": "layla.demo@funzcart.in",
"phone": "0551234567",
"alt_phone": null,
"customer_type": "B2C",
"payment_type": "to_receive",
"addresses": []
}
}*Trimmed.* Not found → 404 "Customer not found".
GET /api/v1/user/users
Paginated list of user accounts in the tenant (20 per page). Returns id, name, email, phone, phone_verified, is_admin, created_at.
{
"status": "success",
"message": "Success",
"data": {
"current_page": 1,
"data": [
{ "id": 1497, "name": "Layla Al-Harbi", "email": "layla.demo@funzcart.in", "phone": "0551234567", "phone_verified": 1, "is_admin": false }
],
"per_page": 20,
"last_page": 60,
"total": 1192
}
}*Shape from a captured response; row values replaced with demo data.*
GET /api/v1/location/get-states
Returns all states as an id → name map. Used by the State picker in Add New Customer (for example "Makkah").
{ "status": "success", "data": { "45": "Riyadh", "46": "Makkah" } }*Illustrative ids.*
GET /api/v1/location/get-district
Name | Required | Notes |
|---|---|---|
| yes | State id |
| no | Sent by the app |
Returns districts (the form calls them City, for example "Jeddah") as id → name.
{ "status": "success", "data": { "629": "Jeddah" } }GET /api/v1/location/list-pincodes
Name | Required | Notes |
|---|---|---|
| no | Limit to one district (the app always sends it) |
Each label is "<area name> (<pin code>)".
{ "status": "success", "message": "Pincodes list", "data": { "4858": "Al Hamra (21442)" } }*Illustrative ids.* When you use Pick on Map, the app matches the picked place name to these lists (ignoring words like "province" or "district").
GET /api/v1/transaction/customer-transactions
The customer ledger: invoices (debit), receipts (credit), sales returns, vouchers, COGS lines and opening balances.
Used by: Customer Profile > Transactions and the Customer Transactions Report detail.
Name | Type | Notes |
|---|---|---|
| integer | One customer |
| string |
|
| string |
|
| date |
|
| integer | App default 20 |
| integer | Page number |
| integer | Active store |
{
"status": "success",
"message": "Customer transactions listed successfully",
"data": {
"current_page": 1,
"data": [
{
"id": 20039,
"order_id": 7036,
"order_number": "ORD-004850",
"payment_method": "7973",
"date": "2026-10-27",
"type": "Credit",
"reference_id": "RCP1001952",
"transaction_type": "Receipt",
"amount": "200.000",
"currency": "SAR",
"reference": "249567",
"transaction_comment": "Receipt #RCP1001952",
"tax_amount": "30.510",
"balance": "-1887.040",
"status": "SUCC",
"store_id": 2,
"customer_name": "Test Default",
"customer_id": 2527
},
{
"id": 20038,
"order_number": "ORD-004850",
"date": "2026-10-27",
"type": "Debit",
"reference_id": "INV-OR1002059",
"transaction_type": "COGS",
"amount": "200.000",
"transaction_comment": "Cost of Goods Sold — Order #ORD-004850"
}
],
"per_page": 20,
"last_page": 699,
"total": 13972
}
}*Trimmed from a captured response (walk-in "Test Default" customer). payment_method here is the master-data id of the method.*