Search sales orders with paging, read a single order and its line items, list orders carrying an outstanding balance, change order status and cancel an order with a reason.
10 endpoints in this module. Every request needs a JWT bearer token — see Authentication.
A sales order is the central transactional record in OptoSoft: what the customer ordered, what it costs, when it is due for delivery, how much has been paid, and where it currently sits in the fulfilment workflow.
In an optical store an order usually cannot be delivered immediately — lenses go to a lab for surfacing and fitting. The order therefore moves through a status lifecycle and accrues receipts (advances and final payments) along the way. These endpoints let you read that state and drive it forward.
Order paths are not consistently typed. Most take an
encrypted string ID, but /api/Orders/{salesOrderId}/items
takes a numeric int64. Others key on the
human-readable salesOrderNo rather than an ID at
all. Each endpoint below states which it expects — check before wiring it up.
The primary order-listing endpoint. Every field is an optional filter, and paging
uses the pageNumber /
pageSize style. This is the endpoint to poll
on a date range if you need to detect new orders.
| Field | Type | Required | Description |
|---|---|---|---|
| pageNumber | int32 | Required | 1-based page index. |
| pageSize | int32 | Required | Rows per page. Keep to 50 or below. |
| yearID | int64 | Optional | Financial year. Strongly recommended — without it you scan every year. |
| customerID | string | Optional | Encrypted customer ID, to list one customer’s orders. |
| salesOrderNo | string | Optional | Order number, exact or partial. |
| customerName | string | Optional | Partial match on customer name. |
| customerMobile | string | Optional | Customer mobile number. |
| branchName | string | Optional | Filter a multi-store chain by branch name. |
| date | string | Optional | Single order date. |
| fromDate | string | Optional | Start of a date range. Use with toDate. |
| toDate | string | Optional | End of a date range. |
| status | string | Optional | Order status filter. |
| acceptedBy | string | Optional | Staff member who took the order. |
| deliveryMode | string | Optional | Collection or delivery mode. |
| deliveryDateTime | string | Optional | Promised delivery date. |
| netAmount | string | Optional | Exact net amount. |
| discount | string | Optional | Discount amount. |
| orderBy | string | Optional | Sort column, optionally with DESC. |
curl -X POST https://api.opto-soft.com/api/Orders/search \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"pageNumber": 1,
"pageSize": 25,
"yearID": 9,
"fromDate": "2026-07-01",
"toDate": "2026-07-26",
"orderBy": "Date DESC"
}'
A paged envelope: Items plus
Total, PageNumber
and PageSize. Note the response fields are
PascalCase, unlike the camelCase request body above.
| Field | Type | Description |
|---|---|---|
| Items[].ID | string | Encrypted sales order ID. Use this on follow-up calls. |
| Items[].SalesOrderNo | string | Human-readable order number shown to the customer. |
| Items[].CustomerName | string | Customer name. |
| Items[].CustomerMobileNo | string | Customer mobile number. |
| Items[].BranchName | string | Originating store. |
| Items[].Date | date-time | Order date. |
| Items[].AcceptedBy | string | Staff member who took the order. |
| Items[].DeliveryModeName | string | Delivery or collection mode. |
| Items[].DeliveryDateTime | string | Promised delivery date. |
| Items[].NetAmount | number | Order value after discount and tax. |
| Items[].StatusID | integer | Numeric status. Pass this to the status-change endpoint. |
| Items[].Status | string | Display label for the status. |
| Items[].ItemCount | integer | Number of line items. |
| Items[].TotalReceipt | number | Amount received so far. Balance is NetAmount - TotalReceipt. |
| Items[].TotalDiscount | number | Total discount applied. |
| Total | integer | Total matching orders before paging. |
| PageNumber | integer | Echo of the requested page index. |
| PageSize | integer | Echo of the requested page size. |
{
"Items": [
{
"ID": "Q2ZQdGhSZVhBaVlXTnpaWFF3TWk0d01B",
"CustomerName": "Rajesh Sharma",
"CustomerMobileNo": "9876543210",
"BranchName": "Main Branch",
"SalesOrderNo": "SO-2024-0568",
"Date": "2024-01-15T00:00:00",
"AcceptedBy": "John Doe",
"DeliveryModeName": "Store Pickup",
"DeliveryDateTime": "22/01/2024 10:00 AM",
"NetAmount": 8500.00,
"StatusID": 2,
"Status": "Processing",
"ItemCount": 3,
"TotalReceipt": 3000.00,
"TotalDiscount": 1200.00
}
],
"Total": 245,
"PageNumber": 1,
"PageSize": 20
}
Fetches a complete order by its human-readable order number — not by ID. This is what you call when a customer quotes their order number at the counter or over the phone.
| Name | Type | Required | Description |
|---|---|---|---|
| salesOrderNo | string | Required | Order number as printed on the customer’s copy. URL-encode any slashes. |
curl "https://api.opto-soft.com/api/Orders/SO%2F2026%2F001842" \
-H "Authorization: Bearer YOUR_TOKEN"
| Status | Cause |
|---|---|
| 404 | No order with that number in the token’s company and branch scope. |
| 500 | Server error. Retry with backoff. |
{
"SalesOrderID": "Q2ZQdGhSZVhBaVlXTnpaWFF3TWk0d01B",
"SalesOrderNO": "SO-2024-0568",
"SalseOrderDate": "2024-01-15T00:00:00",
"DeliveryDate": "2024-01-22T00:00:00",
"CustomerID": "Q2ZQdGhSZVhBaVlXTnpaWFF4TWk0d01B",
"CustomerFullName": "Rajesh Sharma",
"HouseNumber": "A-12",
"SocietyName": "Green Gardens",
"Street": "MG Road",
"Area": "Fort",
"Landmark": "Near Post Office",
"EmailId": "rajesh.sharma@email.com",
"CustomerMobile": "9876543210",
"CustomerGSTNO": null,
"BranchID": "Q2ZQdGhSZVhBaVlXTnpaWFF5TWk0d01B",
"BranchName": "Main Branch",
"BranchAddress": "123 MG Road",
"BranchCity": "Mumbai",
"BrachEmailId": "main@visionoptics.com",
"BranchMobile": "9876500000",
"CompanyName": "Vision Optics Pvt Ltd",
"GSTNO": "27ABCDE1234F1Z5",
"IsCompositionScheme": false,
"SalesRepresentativeID": "Q2ZQdGhSZVhBaVlXTnpaWFF6TWk0d01B",
"SRName": "Amit Kumar",
"YearID": "Q2ZQdGhSZVhBaVlXTnpaWFF6T0M0d01B",
"YearName": "2024-2025",
"TaxTypeID": "1",
"TaxType": "CGST + SGST",
"StatusID": "2",
"Transport": null,
"TotalQuantity": 3.00,
"ITotalPrice": 7200.00,
"TotalDiscount": 1200.00,
"TotalTaxableAmount": 6000.00,
"TotalSGST": 540.00,
"TotalCGST": 540.00,
"TotalIGST": 0.00,
"JobCharges": 200.00,
"ServiceTax": 0.00,
"ShippingCharges": 0.00,
"OtherCharges": 0.00,
"Rounding": 0.00,
"NetAmount": 8500.00,
"ITotlaMRP": 9600.00,
"TotalSavings": 2400.00,
"AcceptedBy": "John Doe",
"Remarks": null,
"SalesOrderDetailID": "Q2ZQdGhSZVhBaVlXTnpaWFF7TWk0d01B",
"ItemID": "Q2ZQdGhSZVhBaVlXTnpaWFF7T0M0d01B",
"ItemName": "Titan Rimless Frame",
"HSNCode": "90041000",
"ItemTypeName": "Frames",
"Qty": 1.00,
"MRP": 3200.00,
"Price": 2500.00,
"IDTotalMRP": 3200.00,
"IDTotalPrice": 2500.00,
"DiscountRate": 12.50,
"DiscountAmount": 400.00,
"TaxableAmount": 2100.00,
"SGSTRate": 9.00,
"SGSTAmount": 189.00,
"CGSTRate": 9.00,
"CGSTAmount": 189.00,
"IGSTRate": 0.00,
"IGSTAmount": 0.00,
"TotalAmount": 2478.00,
"Spherical_Right": "-2.00",
"Cylinder_Right": "-0.75",
"AXIS_Right": "175",
"ADD_Right": null,
"VA_Right": "6/9",
"VN_Right": null,
"PD_Right": "32",
"Spherical_Left": "-1.50",
"Cylinder_Left": "-0.50",
"AXIS_Left": "180",
"ADD_Left": null,
"VA_Left": "6/6",
"VN_Left": null,
"PD_Left": "32",
"Conditions": null,
"ShowPrescriptionOnSalesOrderPrint": 1,
"NameOfSalesOrderPrint": "Sales Order"
}
Resolves an order number into the encrypted IDs the rest of the API needs. This is the bridge between the number a human knows and the identifiers the API expects.
| Name | Type | Required | Description |
|---|---|---|---|
| salesOrderNo | string | Required | The order number. |
{
"salesOrderID": "ENCRYPTED_SALES_ORDER_ID",
"customerID": "ENCRYPTED_CUSTOMER_ID"
}
curl "https://api.opto-soft.com/api/Orders/SO%2F2026%2F001842/ids" \
-H "Authorization: Bearer YOUR_TOKEN"
{
"SalesOrderID": "Q2ZQdGhSZVhBaVlXTnpaWFF3TWk0d01B",
"CustomerID": "Q2ZQdGhSZVhBaVlXTnpaWFF4TWk0d01B"
}
Returns the line items on an order — frames, lenses, contact lenses and accessories, with quantity, price, discount and GST breakdown.
| Name | Type | Required | Description |
|---|---|---|---|
| salesOrderId | int64 | Required | Numeric sales order ID — not the encrypted string used elsewhere. |
This is the one order endpoint that takes a plain numeric ID. Passing the
encrypted id from
/api/Orders/search here will fail to bind.
curl https://api.opto-soft.com/api/Orders/104822/items \
-H "Authorization: Bearer YOUR_TOKEN"
[
{
"SalesOrderDetailID": "Q2ZQdGhSZVhBaVlXTnpaWFF3TWk0d01B",
"ItemID": "Q2ZQdGhSZVhBaVlXTnpaWFF4TWk0d01B",
"Name": "Titan Rimless Frame",
"Code": "TIT-001",
"Qty": 1.00,
"MRP": 3200.00,
"Price": 2500.00,
"DiscountAmount": 400.00,
"DiscountRate": 12.50,
"TaxableAmount": 2100.00,
"SGSTRate": 9.00,
"SGSTAmount": 189.00,
"CGSTRate": 9.00,
"CGSTAmount": 189.00,
"IGSTRate": 0.00,
"IGSTAmount": 0.00,
"TotalAmount": 2478.00
}
]
Returns everything needed to take an advance payment against an order: the customer, the order total, what has already been received and the balance still outstanding. Call this before recording a receipt.
| Name | Type | Required | Description |
|---|---|---|---|
| salesOrderId | string | Required | Encrypted sales order ID. |
| Field | Type | Description |
|---|---|---|
| CustomerID | string | Encrypted customer ID. |
| CustomerName | string | Full customer name. |
| CustomerFirstName | string | Given name, for greetings on the receipt. |
| CustomerMobileNo | string | Mobile number. |
| CustmoerEmail | string | Email address. Note the spelling in the API. |
| Balance | number | Amount still outstanding. |
| ReceiptTotalAmount | number | Total already received. |
| SOTotalAmount | number | Order total. |
| SOTotalDiscountAmount | number | Discount on the order. |
| SalesOrderID | string | Encrypted order ID, echoed back. |
| StatusID | integer | Current order status. |
| MembershipID | string | Encrypted membership ID, if the customer is a member. |
curl https://api.opto-soft.com/api/Orders/ENCRYPTED_SALES_ORDER_ID/advance-receipt-data \
-H "Authorization: Bearer YOUR_TOKEN"
| Status | Cause |
|---|---|
| 404 | Order not found in the token’s scope. |
{
"CustomerID": "Q2ZQdGhSZVhBaVlXTnpaWFF3TWk0d01B",
"CustomerName": "Rajesh Sharma",
"CustomerFirstName": "Rajesh",
"CustomerMobileNo": "9876543210",
"CustmoerEmail": "rajesh.sharma@email.com",
"Balance": 5500.00,
"ReceiptTotalAmount": 3000.00,
"SOTotalAmount": 8500.00,
"SOTotalDiscountAmount": 1200.00,
"SalesOrderID": "Q2ZQdGhSZVhBaVlXTnpaWFF4TWk0d01B",
"StatusID": 2,
"MembershipID": "Q2ZQdGhSZVhBaVlXTnpaWFF5TWk0d01B"
}
Lists every order in a financial year that still has money outstanding. This is the receivables view — useful for a collections report or a payment-reminder campaign.
| Name | Type | Required | Description |
|---|---|---|---|
| yearId | int64 | Required | Financial year to report on. |
curl "https://api.opto-soft.com/api/Orders/with-balance?yearId=9" \
-H "Authorization: Bearer YOUR_TOKEN"
[
{
"BranchID": "Q2ZQdGhSZVhBaVlXTnpaWFF3TWk0d01B",
"SalesOrderIDInReceipt": "Q2ZQdGhSZVhBaVlXTnpaWFF4TWk0d01B",
"SONetAmount": 8500.00,
"ReceiptTotal": 3000.00,
"BalanceAmount": 5500.00,
"CustomerName": "Rajesh Sharma",
"MobileNumber": "9876543210",
"FullSalesOrderNo": "SO-2024-0568",
"ShortSalesOrderNo": "0568",
"CombinedSalesOrderNo": "SO-2024-0568 | Rajesh Sharma"
}
]
A lightweight list of one customer’s orders with their balances — designed to populate a picker when taking a payment, so the cashier can see which order to apply it to.
| Name | Type | Required | Description |
|---|---|---|---|
| customerId | string | Required | Encrypted customer ID. |
| yearId | int64 | Required | Financial year. |
| Field | Type | Description |
|---|---|---|
| SalesOrderID | string | Encrypted order ID. |
| SalesOrderNO | string | Order number. |
| SONetAmount | number | Order total. |
| ReceiptTotal | number | Received so far. |
| BalanceAmount | number | Outstanding balance. |
curl "https://api.opto-soft.com/api/Orders/dropdown?customerId=ENCRYPTED_CUSTOMER_ID&yearId=9" \
-H "Authorization: Bearer YOUR_TOKEN"
[
{
"SalesOrderID": "Q2ZQdGhSZVhBaVlXTnpaWFF3TWk0d01B",
"SalesOrderNO": "SO-2024-0568",
"SONetAmount": 8500.00,
"ReceiptTotal": 3000.00,
"BalanceAmount": 5500.00
}
]
Returns the configured cancellation reasons. Fetch this once and cache it to populate a reason picker.
| Field | Type | Description |
|---|---|---|
| ID | integer | Reason ID. |
| CancellationReason | string | Display text. |
| IsActive | boolean | Only offer active reasons. |
curl https://api.opto-soft.com/api/Orders/cancellation-reasons \
-H "Authorization: Bearer YOUR_TOKEN"
[
{
"ID": 1,
"CancellationReason": "Customer Request",
"IsActive": true
},
{
"ID": 2,
"CancellationReason": "Out of Stock",
"IsActive": true
},
{
"ID": 3,
"CancellationReason": "Duplicate Order",
"IsActive": true
}
]
Moves an order to a new status — for example from In Process to Ready for Delivery when the lab returns the job. This is the endpoint a lab-management or workflow integration drives.
| Name | Type | Required | Description |
|---|---|---|---|
| salesOrderId | string | Required | Encrypted sales order ID. |
| Field | Type | Required | Description |
|---|---|---|---|
| statusId | int64 | Required | Target status ID. Read current values from the statusID field on /api/Orders/search results. |
curl -X PUT https://api.opto-soft.com/api/Orders/ENCRYPTED_SALES_ORDER_ID/status \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "statusId": 3 }'
| Status | Cause |
|---|---|
| 400 | Invalid status transition, or an unrecognised statusId. |
| 401 | Token missing or expired. |
| 500 | Server error. |
{
"success": true,
"message": "Sales order status updated successfully."
}
Cancels an order and simultaneously settles the treatment of any receipts already taken against it. Because it touches money, it takes both an order status and a receipt status.
| Name | Type | Required | Description |
|---|---|---|---|
| salesOrderId | string | Required | Encrypted sales order ID. |
| Field | Type | Required | Description |
|---|---|---|---|
| statusId | int32 | Required | Cancellation status to apply to the order. |
| receiptStatusId | int32 | Required | How to treat existing receipts (for example refunded or retained as credit). |
| date | date-time | Required | Cancellation date. |
| transactionDate | date-time | Required | Accounting date the cancellation posts against. |
curl -X POST https://api.opto-soft.com/api/Orders/ENCRYPTED_SALES_ORDER_ID/cancel \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"statusId": 5,
"receiptStatusId": 2,
"date": "2026-07-26T00:00:00",
"transactionDate": "2026-07-26T00:00:00"
}'
Cancellation is not reversible through the API. There is no un-cancel endpoint. Confirm the reason and the receipt treatment with the operator before calling this.
| Status | Cause |
|---|---|
| 400 | Order already cancelled or invoiced, or an invalid status combination. |
| 401 | Token missing or expired. |
| 500 | Server error. |
{
"success": true,
"message": "Sales order cancelled successfully."
}