Catalog & Master Data – OptoSoft API
Item types, categories and sub-categories, item search, the country/state/financial-year masters, and server-rendered invoice and receipt PDFs.
15 endpoints in this module. Every request needs a JWT bearer token — see Authentication.
Catalog & Master Data
This module holds the reference data every other module depends on: the product catalog hierarchy, the geography and financial-year masters, and the document renderer that turns an invoice or receipt into a printable PDF.
The catalog is a three-level tree — item type (frame, spectacle
lens, contact lens, sunglass, accessory) contains categories, which
contain sub-categories. Walk it top-down, or jump straight to
SearchItems if you already know what you are looking
for.
Cache this data. Item types, categories, countries, states and financial years change perhaps once a year. Fetch them at start-up and refresh daily — calling them per transaction wastes your rate-limit headroom.
Catalog hierarchy
GET /api/Common/GetItemTypes
No token requiredReturns the top level of the catalog. Start here, then drill into categories. This is one of the few endpoints whose response schema is declared in the specification.
Response
| Field | Type | Description |
|---|---|---|
| Value | string | Encrypted item-type ID. Pass this as itemTypeID downstream. |
| Name | string | Display name, for example "Frame" or "Spectacle Lens". |
Example request
curl https://api.opto-soft.com/api/Common/GetItemTypes
Example response — 200 OK
[
{
"Value": "1",
"Name": "Frames"
},
{
"Value": "2",
"Name": "Lenses"
},
{
"Value": "3",
"Name": "ContactLens"
},
{
"Value": "4",
"Name": "Accessories"
},
{
"Value": "5",
"Name": "Solutions"
}
]
POST /api/Common/GetCategoriesByItemType
Bearer tokenReturns the categories under one item type — the second level of the tree.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
| itemTypeID | string | Required | Encrypted item-type ID from GetItemTypes. |
Example request
curl -X POST https://api.opto-soft.com/api/Common/GetCategoriesByItemType \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "itemTypeID": "ENCRYPTED_ITEM_TYPE_ID" }'
Example response — 200 OK
[
{
"ID": "Q2ZQdGhSZVhBaVlXTnpaWFF3TWk0d01B",
"Name": "Single Vision",
"Code": "SV",
"ItemTypeID": "Q2ZQdGhSZVhBaVlXTnpaWFF4TWk0d01B"
},
{
"ID": "Q2ZQdGhSZVhBaVlXTnpaWFF3T0M0d01B",
"Name": "Bifocal",
"Code": "BF",
"ItemTypeID": "Q2ZQdGhSZVhBaVlXTnpaWFF4TWk0d01B"
}
]
POST /api/Common/GetSubCategoriesByCategory
Bearer tokenReturns the sub-categories under one category — the third and deepest level.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
| categoryID | string | Required | Encrypted category ID from GetCategoriesByItemType. |
Example request
curl -X POST https://api.opto-soft.com/api/Common/GetSubCategoriesByCategory \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "categoryID": "ENCRYPTED_CATEGORY_ID" }'
Example response — 200 OK
[
{
"ID": "Q2ZQdGhSZVhBaVlXTnpaWFF5TWk0d01B",
"Name": "CR39",
"Code": "CR39",
"CategoryID": "Q2ZQdGhSZVhBaVlXTnpaWFF3TWk0d01B"
},
{
"ID": "Q2ZQdGhSZVhBaVlXTnpaWFF5T0M0d01B",
"Name": "Polycarbonate",
"Code": "PC",
"CategoryID": "Q2ZQdGhSZVhBaVlXTnpaWFF3TWk0d01B"
}
]
POST /api/Common/SearchItems
Bearer token
The general-purpose catalog search: filter by any level of the tree, by free text,
or by all three. Paging uses the pageNumber /
pageSize style.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
| itemTypeID | string | Optional | Restrict to one item type. |
| categoryID | string | Optional | Restrict to one category. |
| subCategoryID | string | Optional | Restrict to one sub-category. |
| searchTerm | string | Optional | Free-text match on item name, code or brand. |
| pageNumber | int32 | Required | 1-based page index. |
| pageSize | int32 | Required | Rows per page. |
Example request
curl -X POST https://api.opto-soft.com/api/Common/SearchItems \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"itemTypeID": "ENCRYPTED_ITEM_TYPE_ID",
"searchTerm": "titanium",
"pageNumber": 1,
"pageSize": 25
}'
Example response — 200 OK
{
"Items": [
{
"ID": "Q2ZQdGhSZVhBaVlXTnpaWFF3TWk0d01B",
"Name": "Titan Rimless Frame",
"Code": "TIT-001",
"Price": 2500.00,
"MRP": 3200.00,
"Stock": 15.00,
"ItemTypeID": "Q2ZQdGhSZVhBaVlXTnpaWFF4TWk0d01B",
"CategoryID": "Q2ZQdGhSZVhBaVlXTnpaWFF4T0M0d01B",
"SubCategoryID": "Q2ZQdGhSZVhBaVlXTnpaWFF5TWk0d01B"
}
],
"Total": 142,
"PageNumber": 1,
"PageSize": 50
}
Module endpoints
OptoSoft groups item types into modules — optical, and optionally other retail verticals the same store runs. These four endpoints navigate that grouping.
POST /api/Module/GetItemTypesByModule
Bearer token
Returns the item types belonging to one module — the module-scoped equivalent
of GetItemTypes.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
| moduleID | string | Required | Encrypted module ID, available on GetItemTypes results. |
Example request
curl -X POST https://api.opto-soft.com/api/Module/GetItemTypesByModule \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "moduleID": "ENCRYPTED_MODULE_ID" }'
Example response — 200 OK
[
{
"Value": "Q2ZQdGhSZVhBaVlXTnpaWFF3TWk0d01B",
"Name": "Frames",
"ItemImagePath": "https://cdn.example.com/itemtypes/1/frame_icon.png",
"ModuleID": "Q2ZQdGhSZVhBaVlXTnpaWFF4TWk0d01B"
}
]
POST /api/Module/GetCategoryTreeByItemType
Bearer token
Returns the entire category and sub-category tree for an item type in one call.
Prefer this over calling GetCategoriesByItemType
and then GetSubCategoriesByCategory per node
— it saves a round trip per category.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
| itemTypeID | string | Required | Encrypted item-type ID. |
Example request
curl -X POST https://api.opto-soft.com/api/Module/GetCategoryTreeByItemType \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "itemTypeID": "ENCRYPTED_ITEM_TYPE_ID" }'
Example response — 200 OK
[
{
"ID": "Q2ZQdGhSZVhBaVlXTnpaWFF3TWk0d01B",
"Name": "Single Vision",
"Code": "SV",
"ItemTypeID": "Q2ZQdGhSZVhBaVlXTnpaWFF4TWk0d01B",
"SubCategories": [
{
"ID": "Q2ZQdGhSZVhBaVlXTnpaWFF5TWk0d01B",
"Name": "CR39",
"Code": "CR39",
"CategoryID": "Q2ZQdGhSZVhBaVlXTnpaWFF3TWk0d01B"
},
{
"ID": "Q2ZQdGhSZVhBaVlXTnpaWFF5T0M0d01B",
"Name": "Polycarbonate",
"Code": "PC",
"CategoryID": "Q2ZQdGhSZVhBaVlXTnpaWFF3TWk0d01B"
}
]
}
]
POST /api/Module/GetQuickSaleTypes
Bearer tokenReturns the sale types available in the Quick Sale screen for a module — which product sections the counter operator can bill from.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
| moduleID | string | Required | Encrypted module ID. |
| value | string | Optional | Encrypted ID, when narrowing to one type. |
| name | string | Optional | Display name filter. |
| itemImagePath | string | Optional | Image path. Not normally sent on a request. |
Example request
curl -X POST https://api.opto-soft.com/api/Module/GetQuickSaleTypes \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "moduleID": "ENCRYPTED_MODULE_ID" }'
Example response — 200 OK
[
{
"Value": "Q2ZQdGhSZVhBaVlXTnpaWFF3TWk0d01B",
"Name": "Frames",
"ItemImagePath": "https://cdn.example.com/itemtypes/1/frame_icon.png",
"ModuleID": "1"
},
{
"Value": "Q2ZQdGhSZVhBaVlXTnpaWFF3T0M0d01B",
"Name": "Lenses",
"ItemImagePath": "https://cdn.example.com/itemtypes/2/lens_icon.png",
"ModuleID": "1"
}
]
POST /api/Module/SaveSale
Bearer tokenA module-scoped sale commit, parallel to /api/quickSale/SaveSale.
Prefer /api/quickSale/SaveSale.
The OpenAPI specification declares this endpoint’s request body as an
untyped object with no schema, so its contract is not verifiable from the spec.
If your integration needs this specific endpoint,
contact the OptoSoft team
for the expected payload before building against it.
Request body
Untyped JSON object. Not specified.
Example response — 200 OK
{
"Success": true,
"Message": "Sale saved successfully",
"Data": {
"CustomerID": "Q2ZQdGhSZVhBaVlXTnpaWFF3TWk0d01B",
"SalesOrderID": "Q2ZQdGhSZVhBaVlXTnpaWFF4TWk0d01B",
"InvoiceID": "Q2ZQdGhSZVhBaVlXTnpaWFF5TWk0d01B",
"EncryptedInvoiceID": "Q2ZQdGhSZVhBaVlXTnpaWFF5TWk0d01B",
"IsInvoiceGenerated": true,
"UsedDefaultCustomer": false
}
}
Geography and financial years
GET /api/Common/GetCountries
No token required
Returns the country master with encrypted IDs, for
countryID on customer and doctor records.
Query parameters
None.
Example request
curl https://api.opto-soft.com/api/Common/GetCountries \
-H "Authorization: Bearer YOUR_TOKEN"
Example response — 200 OK
[
{
"ID": "Q2ZQdGhSZVhBaVlXTnpaWFF3TWk0d01B",
"Name": "India"
},
{
"ID": "Q2ZQdGhSZVhBaVlXTnpaWFF3T0M0d01B",
"Name": "United States"
},
{
"ID": "Q2ZQdGhSZVhBaVlXTnpaWFF5TWk0d01B",
"Name": "United Kingdom"
}
]
GET /api/Common/GetStates/{countryId}
No token requiredReturns the states within a country. For India this is the list that drives the GST place-of-supply rule, so setting a customer’s state correctly is what makes CGST + SGST versus IGST come out right on their invoices.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
| countryId | string | Required | Encrypted country ID from GetCountries. |
Example request
curl https://api.opto-soft.com/api/Common/GetStates/ENCRYPTED_COUNTRY_ID \
-H "Authorization: Bearer YOUR_TOKEN"
Example response — 200 OK
[
{
"ID": "Q2ZQdGhSZVhBaVlXTnpaWFF5T0M0d01B",
"Name": "Maharashtra",
"CountryID": "Q2ZQdGhSZVhBaVlXTnpaWFF3TWk0d01B"
},
{
"ID": "Q2ZQdGhSZVhBaVlXTnpaWFF6TWk0d01B",
"Name": "Gujarat",
"CountryID": "Q2ZQdGhSZVhBaVlXTnpaWFF3TWk0d01B"
},
{
"ID": "Q2ZQdGhSZVhBaVlXTnpaWFF6T0M0d01B",
"Name": "Karnataka",
"CountryID": "Q2ZQdGhSZVhBaVlXTnpaWFF3TWk0d01B"
}
]
GET /api/Common/GetYears
No token required
Returns the financial years configured for the company. Almost every search
endpoint takes a yearID, so resolve the current
year from here at start-up rather than hard-coding it — it rolls over every
1 April in India.
Query parameters
None.
Example request
curl https://api.opto-soft.com/api/Common/GetYears \
-H "Authorization: Bearer YOUR_TOKEN"
Example response — 200 OK
[
{
"YearID": 4,
"YearName": "2023-2024",
"StartDate": "2023-04-01T00:00:00",
"EndDate": "2024-03-31T00:00:00",
"IsCurrentYear": false
},
{
"YearID": 5,
"YearName": "2024-2025",
"StartDate": "2024-04-01T00:00:00",
"EndDate": "2025-03-31T00:00:00",
"IsCurrentYear": true
}
]
Document rendering
Four endpoints render printable documents. Each has a
PDF variant returning binary bytes and an
HTML variant returning markup you can restyle or embed. All four are
POST, and all four take the branch and company as
query parameters while the document payload goes in the body.
The specification types the request body of all four as a bare
string rather than a structured object. In practice
this is the serialised document model. Round-trip it from
get-invoice-data-by-salesorder
rather than constructing it by hand.
POST /api/Common/invoice/pdf
Bearer tokenRenders a GST invoice as a PDF. Returns binary bytes, not JSON.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
| CompanyID | string | Required | Encrypted company ID. Note the capitalised name. |
| BranchID | string | Required | Encrypted branch ID, for the letterhead. |
Example request
curl -X POST "https://api.opto-soft.com/api/Common/invoice/pdf?CompanyID=ENCRYPTED_COMPANY_ID&BranchID=ENCRYPTED_BRANCH_ID" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
--data-binary @invoice.json \
-o invoice.pdf
Example response — 200 OK
"<binary PDF — Content-Type: application/pdf, Content-Disposition: attachment; filename=<invoiceId>.pdf>"
POST /api/Common/invoice/pdf/html
Bearer tokenThe HTML variant of the invoice renderer. Use it to preview an invoice in a browser or an in-app web view, or to apply your own print stylesheet.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
| CompanyID | string | Required | Encrypted company ID. |
| BranchID | string | Required | Encrypted branch ID. |
Example request
curl -X POST "https://api.opto-soft.com/api/Common/invoice/pdf/html?CompanyID=ENCRYPTED_COMPANY_ID&BranchID=ENCRYPTED_BRANCH_ID" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
--data-binary @invoice.json \
-o invoice.html
Example response — 200 OK
"<binary PDF — Content-Type: application/pdf>"
POST /api/Common/receipt/pdf
Bearer token
Renders a payment receipt as a PDF. Unlike the invoice renderer, this one can
locate the receipt itself from receiptNo.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
| receiptNo | string | Required | Receipt number to render. |
| external | boolean | Optional | Set true to use the external-facing receipt template rather than the internal one. |
| CompanyID | string | Required | Encrypted company ID. |
| BranchID | string | Required | Encrypted branch ID. |
Example request
curl -X POST "https://api.opto-soft.com/api/Common/receipt/pdf?receiptNo=RCP%2F2026%2F002210&external=true&CompanyID=ENCRYPTED_COMPANY_ID&BranchID=ENCRYPTED_BRANCH_ID" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-o receipt.pdf
Example response — 200 OK
"<binary PDF — Content-Type: application/pdf, Content-Disposition: attachment; filename=<receiptNo>.pdf>"
POST /api/Common/receipt/pdf/html
Bearer tokenThe HTML variant of the receipt renderer — ideal for thermal printers driven from a browser, or for emailing a receipt as inline HTML.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
| receiptNo | string | Required | Receipt number to render. |
| external | boolean | Optional | Use the external-facing template. |
| CompanyID | string | Required | Encrypted company ID. |
| BranchID | string | Required | Encrypted branch ID. |
Example request
curl -X POST "https://api.opto-soft.com/api/Common/receipt/pdf/html?receiptNo=RCP%2F2026%2F002210&CompanyID=ENCRYPTED_COMPANY_ID&BranchID=ENCRYPTED_BRANCH_ID" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-o receipt.html
Example response — 200 OK
"<binary PDF — Content-Type: application/pdf>"