Invoices – OptoSoft API
List GST invoices with paging and filters, pull the invoice-ready data for a sales order, and post a complete invoice back to OptoSoft.
6 endpoints in this module. Every request needs a JWT bearer token — see Authentication.
Invoices
An invoice is the GST tax document raised against a sales order. These six endpoints let you list invoices, discover which orders are still awaiting invoicing, pull the invoice-ready data for an order, and post a completed invoice back to OptoSoft.
In an optical store the order and the invoice are usually separated in time: the order is
taken (often with an advance), the lenses are fitted, and the invoice is raised on
delivery. That is why get-invoice-data-by-salesorder
exists — it hands you a pre-filled invoice built from the order, which you complete
and post back.
Indian GST split. Whether tax lands as CGST + SGST (intra-state) or
IGST (inter-state) is decided by the customer’s stateID
against the branch state. Set the customer’s state correctly on
customer upsert
and OptoSoft computes the split for you.
Listing invoices
POST /api/Invoice/List
Bearer token
Returns a paged, filtered list of invoices. This is the endpoint an accounting
integration polls to pull sales into Tally, Zoho Books or a GST return workflow.
Paging uses the fromPage /
toPage row-range style.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
| companyID | int64 | Required | Company (tenant). |
| branchID | int64 | Required | Store. |
| yearID | int64 | Required | Financial year — the GST return period you are reconciling. |
| invoiceNumber | string | Optional | Invoice number, exact or partial. |
| salesOrderNo | string | Optional | Find the invoice raised against a given order. |
| customerID | string | Optional | Encrypted customer ID. |
| customerName | string | Optional | Partial match on customer name. |
| customerMobile | string | Optional | Customer mobile number. |
| fromDate | string | Optional | Start of the invoice-date range. |
| toDate | string | Optional | End of the invoice-date range. |
| amount | string | Optional | Gross amount filter. |
| netAmount | string | Optional | Net amount filter. |
| totalRows | string | Optional | Echo field used by the OptoSoft grid. Leave unset. |
| orderBy | string | Optional | Sort column, optionally with DESC. |
| fromPage | int32 | Optional | First row of the range. |
| toPage | int32 | Optional | Last row of the range. |
Example request
curl -X POST https://api.opto-soft.com/api/Invoice/List \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"companyID": 18,
"branchID": 24,
"yearID": 9,
"fromDate": "2026-07-01",
"toDate": "2026-07-31",
"fromPage": 1,
"toPage": 50,
"orderBy": "Date DESC"
}'
Response
| Field | Type | Description |
|---|---|---|
| TotalRows | integer | Total matching invoices, before paging. |
| Data[].ID | string | Encrypted invoice ID. Use this on follow-up calls. |
| Data[].PlainId | integer | Numeric invoice ID, for your own reconciliation only. |
| Data[].InvoiceNo | string | Invoice number as printed. |
| Data[].SalesOrderNo | string | Originating order number. |
| Data[].Date | date-time | Invoice date. |
| Data[].CustomerName | string | Customer name. |
| Data[].CustomerMobileNo | string | Customer mobile number. |
| Data[].NetAmount | number | Invoice value including GST. |
Errors
| Status | Cause |
|---|---|
| 400 | Missing scoping field, or an unparseable date. |
| 401 | Token missing or expired. |
Example response — 200 OK
{
"TotalRows": 87,
"Data": [
{
"ID": "Q2ZQdGhSZVhBaVlXTnpaWFF3TWk0d01B",
"PlainId": 1042,
"InvoiceNo": "INV-2024-1042",
"SalesOrderNo": "SO-2024-0568",
"Date": "2024-01-20T00:00:00",
"CustomerName": "Rajesh Sharma",
"CustomerMobileNo": "9876543210",
"NetAmount": 8500.00
}
]
}
Preparing a new invoice
GET /api/Invoice/get-customers-for-invoice
Bearer tokenLists customers who currently have at least one order awaiting invoicing. Step one of the "raise an invoice" workflow — it narrows a large customer register down to just those with pending work.
Query parameters
None. Scope comes from the bearer token.
Example request
curl https://api.opto-soft.com/api/Invoice/get-customers-for-invoice \
-H "Authorization: Bearer YOUR_TOKEN"
Example response — 200 OK
{
"success": true,
"data": [
{
"ID": "Q2ZQdGhSZVhBaVlXTnpaWFF3TWk0d01B",
"CustomerName": "Rajesh Sharma"
},
{
"ID": "Q2ZQdGhSZVhBaVlXTnpaWFF3T0M0d01B",
"CustomerName": "Priya Patel"
}
]
}
GET /api/Invoice/get-sales-orders-for-invoice
Bearer tokenFor a chosen customer, lists their orders that are ready to be invoiced. Step two of the workflow.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
| customerId | string | Required | Encrypted customer ID from the previous call. |
Example request
curl "https://api.opto-soft.com/api/Invoice/get-sales-orders-for-invoice?customerId=ENCRYPTED_CUSTOMER_ID" \
-H "Authorization: Bearer YOUR_TOKEN"
Example response — 200 OK
{
"success": true,
"data": [
{
"SalesOrderID": "Q2ZQdGhSZVhBaVlXTnpaWFF3TWk0d01B",
"SalesOrderNO": "SO-2024-0568"
}
]
}
POST /api/Invoice/GetOrdersForNewInvoice
Bearer tokenReturns every order in the given company, branch and year that is still awaiting an invoice — the branch-wide view, rather than the per-customer view above. Use it to build a "pending invoicing" worklist.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
| companyID | int64 | Required | Company (tenant). |
| branchID | int64 | Required | Store. |
| yearID | int64 | Required | Financial year. |
Example request
curl -X POST https://api.opto-soft.com/api/Invoice/GetOrdersForNewInvoice \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "companyID": 18, "branchID": 24, "yearID": 9 }'
Example response — 200 OK
{
"OrderList": [
{
"SalesOrderID": "Q2ZQdGhSZVhBaVlXTnpaWFF3TWk0d01B",
"SalesOrderNO": "SO-2024-0568"
},
{
"SalesOrderID": "Q2ZQdGhSZVhBaVlXTnpaWFF3T0M0d01B",
"SalesOrderNO": "SO-2024-0569"
}
]
}
GET /api/Invoice/get-invoice-data-by-salesorder
Bearer token
The key endpoint of this module. Given a sales order, it returns a fully populated
invoice draft — customer, line items, GST breakdown and totals — ready to
be reviewed and posted back via
save-invoice-data.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
| salesOrderId | string | Required | Encrypted sales order ID. |
Example request
curl "https://api.opto-soft.com/api/Invoice/get-invoice-data-by-salesorder?salesOrderId=ENCRYPTED_SALES_ORDER_ID" \
-H "Authorization: Bearer YOUR_TOKEN"
The response is an OMFastWizardModel —
the same composite object used by
Quick Sale.
Round-trip it: take the returned object, adjust only what you need, and post it
to save-invoice-data. Do not attempt to
build one from scratch.
Example response — 200 OK
{
"success": true,
"data": {
"salesOrderID": "Q2ZQdGhSZVhBaVlXTnpaWFF3TWk0d01B",
"salesOrderDate": "20/01/2024",
"customerID": "Q2ZQdGhSZVhBaVlXTnpaWFF4TWk0d01B",
"salesRepresentative": "Amit Kumar",
"salesRepresentativeID": "Q2ZQdGhSZVhBaVlXTnpaWFF5TWk0d01B",
"taxTypeID": 1,
"taxType": "CGST + SGST",
"transport": null,
"totalQuantity": 3.00,
"totalPrice": 7200.00,
"totalMRP": 9600.00,
"totalSavings": 2400.00,
"totalDiscount": 1200.00,
"totalTaxableAmount": 6000.00,
"totalSGST": 540.00,
"totalCGST": 540.00,
"totalIGST": 0.00,
"shippingCharges": 0.00,
"otherCharges": 0.00,
"jobCharges": 200.00,
"serviceTax": 0.00,
"rounding": 0.00,
"netAmount": 8500.00,
"isForeigner": false,
"items": [
{
"id": "Q2ZQdGhSZVhBaVlXTnpaWFF6TWk0d01B",
"itemId": "Q2ZQdGhSZVhBaVlXTnpaWFF6T0M0d01B",
"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,
"pairNo": "1",
"spectacleLensType": "Single Vision",
"spherical": "-1.50",
"cylinder": "-0.50",
"axis": "180",
"addition": null,
"vision": "6/6"
}
]
}
}
Saving an invoice
POST /api/Invoice/save-invoice-data
Bearer token
Commits an invoice. The request body is the full composite
OMFastWizardModel (94 top-level properties,
most of them nested objects and UI display flags).
Top-level structure
| Field | Type | Description |
|---|---|---|
| customerModel | object | The customer the invoice is raised against. |
| salesorderModel | object | The order being invoiced, with its line items. |
| receiptModel | object | Payment taken at the time of invoicing, if any. |
| eyeInfoModel | object | The prescription dispensed against this invoice. |
| eyecheckupRecallModel | object | Next eye-test recall to schedule for the customer. |
| purchaseOrderModel | object | Linked lab or supplier purchase order. |
| autoItemTypeSectionList | array | Item-type sections shown in the OptoSoft wizard. |
| sessionID | string | Cart session ID, when the invoice is built from a Quick Sale session. |
| orderDate | date-time | Order date. |
| expectedDate | date-time | Expected delivery date. |
| supplierID | string | Encrypted supplier ID for lab work. |
| fitterID | string | Encrypted fitter ID. |
| priorityID | string | Encrypted job-priority ID. |
| isLensAdded | boolean | Whether a lens line is present. |
| isSpectacleLensInOrder | boolean | Whether a spectacle lens is on the order. |
| isExternal | boolean | Marks an externally originated transaction. |
| userId | int64 | Operator recording the invoice. |
| branchCity | string | Branch city, printed on the invoice. |
| visitorIP | string | Client IP recorded for audit. |
| showQuickSaleAdd…Section | boolean | Roughly 60 display flags controlling OptoSoft wizard sections (frames, sunglasses, lenses, contact lenses, accessories and so on). Preserve whatever the GET returned; they do not affect the saved invoice. |
Always round-trip this model. Call
get-invoice-data-by-salesorder first, modify
only the fields you need to change, and post the whole object back. Constructing
an OMFastWizardModel by hand is not
supported and will produce incorrect tax totals.
Example flow
# 1. Pull the pre-filled invoice draft for the order
curl "https://api.opto-soft.com/api/Invoice/get-invoice-data-by-salesorder?salesOrderId=ENCRYPTED_SALES_ORDER_ID" \
-H "Authorization: Bearer YOUR_TOKEN" -o draft.json
# 2. Adjust draft.json as needed (dates, payment, remarks)
# 3. Post it back to commit the invoice
curl -X POST https://api.opto-soft.com/api/Invoice/save-invoice-data \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
--data-binary @draft.json
Inspect the response body before treating the invoice as saved. A
200 here confirms the request was accepted,
not necessarily that a document number was allocated.
Printing
Rendered invoice PDFs and print-ready HTML come from the catalog module — see
Catalog & Master Data
for /api/Common/invoice/pdf and
/api/Common/invoice/pdf/html.
Example response — 200 OK
{
"success": true,
"data": {
"InvoiceId": 1042,
"EncryptedInvoiceId": "Q2ZQdGhSZVhBaVlXTnpaWFF3TWk0d01B",
"InvoiceNo": "INV-2024-1042"
}
}