Create and update customers, search the customer register, detect duplicates, and manage spectacle and contact-lens prescriptions (SPH, CYL, AXIS, ADD, PD) plus the referring-doctor master.
13 endpoints in this module. Every request needs a JWT bearer token — see Authentication.
The customer module is the entry point for almost every OptoSoft workflow. It holds the customer register, the full optical prescription history (spectacle powers and contact-lens parameters), and the referring-doctor master used on prescriptions and orders.
A customer in OptoSoft is more than a contact record: it carries the latest recorded
prescription inline (the lefT_* /
righT_* fields), membership and loyalty flags,
marketing consent (DND) flags, and GST details for business customers.
/api/Customer/list by mobile number./api/Customer/check-duplicate, then create via /api/Customer/upsert./api/Customer/save-prescription.
Mobile number is the practical natural key. OptoSoft does not enforce
uniqueness on it, so always search before creating and always run
check-duplicate in an automated import, or you
will fragment a customer’s prescription and purchase history across duplicates.
Six endpoints on this page share one request model. Every field is a filter: supply only
the ones you want to narrow by and omit the rest. companyID
and branchID are numeric here, unlike most of the API.
| Field | Type | Required | Description |
|---|---|---|---|
| companyID | int64 | Required | Your OptoSoft company (tenant). |
| branchID | int64 | Required | The store to search within. |
| yearID | int64 | Optional | Financial year. Resolve from GET /api/Common/GetYears. |
| customerID | string | Optional | Encrypted customer ID. Set this to fetch one specific customer. |
| firstName | string | Optional | Partial match on given name. |
| lastName | string | Optional | Partial match on surname. |
| mobile | string | Optional | Mobile number. The most reliable lookup. |
| phoneNO | string | Optional | Landline number. |
| string | Optional | Email address. | |
| city | string | Optional | City filter. |
| isActive | string | Optional | Active flag, sent as a string ("true" / "false"). |
| customerSourceStringID | string | Optional | Acquisition source. Values from GET /api/quickSale/getSources. |
| typeID | string | Optional | Customer category / type. |
| orderBy | string | Optional | Sort column, optionally with DESC. |
| fromPage | int32 | Optional | First row of the range (row-range paging). |
| toPage | int32 | Optional | Last row of the range. |
Searches the customer register and returns a paged list. This is the endpoint you use to find a walk-in customer by mobile number.
The shared search model described above.
curl -X POST https://api.opto-soft.com/api/Customer/list \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"companyID": 18,
"branchID": 24,
"mobile": "9876543210",
"fromPage": 1,
"toPage": 20,
"orderBy": "FirstName"
}'
Returns the complete record for a single customer, including address, membership
and the inline latest prescription. Set customerID
to the encrypted ID returned by /api/Customer/list.
The shared search model, with
customerID set.
curl -X POST https://api.opto-soft.com/api/Customer/detail \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"companyID": 18,
"branchID": 24,
"customerID": "ENCRYPTED_CUSTOMER_ID"
}'
Tests whether a customer matching the supplied mobile, email or name already
exists, without creating anything. Call this before
upsert in any bulk import.
The shared search model. Populate the identifying fields you are about to insert.
curl -X POST https://api.opto-soft.com/api/Customer/check-duplicate \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"companyID": 18,
"branchID": 24,
"mobile": "9876543210",
"firstName": "Anita",
"lastName": "Shah"
}'
Creates a new customer, or updates an existing one when
id is supplied. This is the largest model in
the customer module (77 fields), but only a handful are needed to create a usable
record.
| Field | Type | Required | Description |
|---|---|---|---|
| id | string | Optional | Encrypted customer ID. Omit to create; supply to update. |
| companyID | string | Required | Encrypted company ID — a string here, unlike the search model. |
| branchID | string | Required | Encrypted branch ID. |
| yearID | string | Required | Encrypted financial-year ID. |
| firstName | string | Required | Given name. |
| lastName | string | Optional | Surname. |
| mobile | string | Required | Mobile number. Used for WhatsApp and SMS recalls. |
| emailId | string | Optional | Email address. |
| title | string | Optional | Salutation (Mr, Mrs, Ms, Dr). |
| genderID | string | Optional | Encrypted gender ID from GET /api/quickSale/getGenders. |
| birthDate | date-time | Optional | Drives birthday marketing campaigns. |
| address | string | Optional | Free-text address. Structured parts are also available (see full list). |
| city | string | Optional | City. |
| zipCode | string | Optional | PIN code. |
| stateID | string | Optional | Encrypted state ID from GET /api/Common/GetStates/{countryId}. Drives CGST/SGST vs IGST. |
| countryID | string | Optional | Encrypted country ID from GET /api/Common/GetCountries. |
| gstno | string | Optional | GSTIN for B2B invoices. |
| customerSourceID | string | Optional | How the customer found you. |
| isActive | boolean | Optional | Defaults to active. |
| dndForPromotionalMessage | boolean | Optional | Suppress marketing messages. Set from your own consent capture. |
| dndForTransactionalMessage | boolean | Optional | Suppress order and receipt notifications. |
| Field | Type |
|---|---|
| id | string |
| branchID | string |
| stateID | string |
| countryID | string |
| faceShapeID | string |
| genderID | string |
| companyID | string |
| customerID | string |
| yearID | string |
| title | string |
| firstName | string |
| middleName | string |
| lastName | string |
| isMarried | boolean |
| reference | string |
| birthDate | date-time |
| remarks | string |
| mobile | string |
| emailId | string |
| city | string |
| zipCode | string |
| address | string |
| houseNumber | string |
| societyName | string |
| street | string |
| area | string |
| landmark | string |
| isActive | boolean |
| createdBy | string |
| createdDate | date-time |
| createdIP | string |
| modifiedBy | string |
| modifiedDate | date-time |
| modifiedIP | string |
| newCustomer | boolean |
| isNRI | boolean |
| marriageDate | date-time |
| existingCustomerReference | string |
| phoneNO | string |
| gstno | string |
| customerSourceID | string |
| customerCategoryID | string |
| isStoreCustomer | boolean |
| storeID | string |
| isMember | boolean |
| membershipID | string |
| age | int32 |
| bloodGroup | string |
| self | string |
| isBlackList | boolean |
| dndForPromotionalMessage | boolean |
| dndForTransactionalMessage | boolean |
| clientConsentImage | string |
| clientConsentDisplayName | string |
| prescriptionDocument | string |
| prescriptionDocumentDisplayName | string |
| isForeigner | boolean |
| lefT_SphericalWithSign | string |
| righT_SphericalWithSign | string |
| righT_CylinderWithSign | string |
| lefT_CylinderWithSign | string |
| lefT_AxisWithSign | string |
| righT_AxisWithSign | string |
| righT_AdditionWithSign | string |
| lefT_AdditionWithSign | string |
| leftVision | string |
| rightVision | string |
| leftvn | string |
| rightvn | string |
| rightpd | string |
| leftpd | string |
| leftbc | string |
| rightbc | string |
| leftdia | string |
| rightdia | string |
| prescriptionRemarks | string |
| salesOrderID | string |
curl -X POST https://api.opto-soft.com/api/Customer/upsert \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"companyID": "ENCRYPTED_COMPANY_ID",
"branchID": "ENCRYPTED_BRANCH_ID",
"yearID": "ENCRYPTED_YEAR_ID",
"title": "Ms",
"firstName": "Anita",
"lastName": "Shah",
"mobile": "9876543210",
"emailId": "anita.shah.example.in",
"city": "Ahmedabad",
"zipCode": "380015",
"isActive": true,
"dndForPromotionalMessage": false
}'
The scoping fields are encrypted strings on this endpoint, not
the numeric values used by the search model. Take them from a prior response
(for example /api/Customer/detail) rather
than converting the numbers yourself.
A prescription in OptoSoft is an eye info record identified by
customerEyeInfoID, holding one row per lens type
(distance, near, contact lens) with the standard optical parameters.
| Parameter | Optical meaning |
|---|---|
| sph | Sphere — myopia or hyperopia correction, in dioptres. |
| cyl | Cylinder — astigmatism correction. |
| axis | Axis of the cylinder, 0–180 degrees. |
| add | Addition — near power for bifocal and progressive lenses. |
| va | Visual acuity, distance. |
| vn | Visual acuity, near. |
| pd | Pupillary distance, in millimetres. |
| bc | Base curve — contact lenses. |
| dia | Diameter — contact lenses. |
| fh | Fitting height for progressive lenses. |
| th | Total height of the lens. |
| lensTypeID | Encrypted lens-type ID identifying which row this is. |
Lists a customer’s prescription history, newest first. Use it to show previous eye tests before recording a new one.
The shared search model, with
customerID set.
curl -X POST https://api.opto-soft.com/api/Customer/prescription-list \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"companyID": 18,
"branchID": 24,
"customerID": "ENCRYPTED_CUSTOMER_ID",
"fromPage": 1,
"toPage": 10
}'
Returns the summary view of one prescription.
| Field | Type | Required | Description |
|---|---|---|---|
| customerEyeInfoID | string | Required | Encrypted prescription ID from prescription-list. |
curl -X POST https://api.opto-soft.com/api/Customer/GetPrescription \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "customerEyeInfoID": "ENCRYPTED_EYE_INFO_ID" }'
Returns the complete prescription, including every lens-type row and the
associated doctor and remarks. Use this when reprinting an Rx or pre-filling a
reorder; GetPrescription returns the lighter
summary.
| Field | Type | Required | Description |
|---|---|---|---|
| customerEyeInfoID | string | Required | Encrypted prescription ID. |
curl -X POST https://api.opto-soft.com/api/Customer/prescription-full-detail \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "customerEyeInfoID": "ENCRYPTED_EYE_INFO_ID" }'
Records a new eye test against a customer. The customer identity fields sit at the
top level and the optical powers go in
prescriptionList, one entry per lens type.
| Field | Type | Required | Description |
|---|---|---|---|
| companyID | int64 | Required | Company (tenant). |
| branchID | int64 | Required | Store. |
| yearID | int64 | Required | Financial year. |
| customerID | string | Required | Encrypted customer ID the prescription belongs to. |
| id | string | Optional | Encrypted prescription ID. Omit to create a new one. |
| firstName | string | Optional | Customer name, echoed onto the Rx printout. |
| lastName | string | Optional | Customer surname. |
| mobile | string | Optional | Contact number. |
| string | Optional | Email address. | |
| birthDate | string | Optional | Date of birth. |
| genderID | string | Optional | Encrypted gender ID. |
| doctorID | string | Optional | Encrypted referring-doctor ID. See doctor endpoints. |
| presripionTypeID | string | Optional | Prescription type. Note the spelling in the API. |
| visitingDate | string | Optional | Date of the eye test. |
| sourceID | string | Optional | Encrypted acquisition-source ID. |
| houseNO | string | Optional | Address — house number. |
| societyName | string | Optional | Address — society or building. |
| street | string | Optional | Address — street. |
| area | string | Optional | Address — area. |
| city | string | Optional | Address — city. |
| prescriptionRemarks | string | Optional | Free-text clinical notes. |
| prescriptionList | array | Required | One entry per lens type — see the parameter table above. |
curl -X POST https://api.opto-soft.com/api/Customer/save-prescription \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"companyID": 18,
"branchID": 24,
"yearID": 9,
"customerID": "ENCRYPTED_CUSTOMER_ID",
"doctorID": "ENCRYPTED_DOCTOR_ID",
"visitingDate": "2026-07-26",
"prescriptionRemarks": "Progressive lenses advised.",
"prescriptionList": [
{
"lensTypeID": "ENCRYPTED_LENS_TYPE_ID",
"sph": "-1.75",
"cyl": "-0.50",
"axis": "90",
"add": "+2.00",
"va": "6/6",
"vn": "N6",
"pd": "62"
}
]
}'
Removes a prescription record. Note this is a POST,
not DELETE.
| Field | Type | Required | Description |
|---|---|---|---|
| customerEyeInfoID | string | Required | Encrypted prescription ID to delete. |
curl -X POST https://api.opto-soft.com/api/Customer/delete-prescription \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "customerEyeInfoID": "ENCRYPTED_EYE_INFO_ID" }'
A prescription already referenced by a dispensed order should normally be superseded by saving a new one rather than deleted, so the order keeps its clinical audit trail.
Prescriptions can cite a referring optometrist or ophthalmologist. These four endpoints maintain that master list.
Returns the full doctor list for the branch, unpaged. Suitable for populating a dropdown at start-up.
The shared search model.
curl -X POST https://api.opto-soft.com/api/Customer/doctor-list \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "companyID": 18, "branchID": 24 }'
The paged equivalent of doctor-list, for a
searchable admin grid. Use the fromPage and
toPage row range.
The shared search model, with paging fields set.
curl -X POST https://api.opto-soft.com/api/Customer/doctor-list-page \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"companyID": 18,
"branchID": 24,
"firstName": "Mehta",
"fromPage": 1,
"toPage": 25,
"orderBy": "Name"
}'
Returns one doctor record by encrypted ID.
| Name | Type | Required | Description |
|---|---|---|---|
| id | string | Required | Encrypted doctor ID from doctor-list. |
curl https://api.opto-soft.com/api/Customer/get-doctor/ENCRYPTED_DOCTOR_ID \
-H "Authorization: Bearer YOUR_TOKEN"
Creates a doctor, or updates an existing one when
id is supplied.
| Field | Type | Required | Description |
|---|---|---|---|
| id | string | Optional | Encrypted doctor ID. Omit to create. |
| name | string | Required | Doctor’s full name as it should print on the Rx. |
| mobile | string | Optional | Contact number. |
| string | Optional | Email address. | |
| address | string | Optional | Clinic address. |
| city | string | Optional | City. |
| stateID | string | Optional | Encrypted state ID. |
| countryID | string | Optional | Encrypted country ID. |
| zipCode | string | Optional | PIN code. |
| remarks | string | Optional | Internal notes. |
| isActive | boolean | Optional | Set false to retire without deleting. |
curl -X POST https://api.opto-soft.com/api/Customer/save-doctor \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Dr. R. Mehta",
"mobile": "9825012345",
"city": "Ahmedabad",
"isActive": true
}'