Focus 9

Focus 9 by Focus Softnet exposes a REST layer at /8API with an fSessionId header from a login call. No developer portal, but real endpoints are recoverable.

Focus 9 is an ERP from Focus Softnet, a vendor founded in 1992 with headquarters in Dubai and a large development and support operation in Hyderabad, selling across the GCC, India, South and South East Asia, Africa, the United Kingdom and North America. It is a full finance, inventory, distribution, manufacturing, CRM, HCM and point of sale suite, and in Indian and Gulf retail and distribution accounts it is the system of record for sales orders, invoices, item master, stock by warehouse, customers and receipts. Focus Softnet has since launched FocusX as its fourth-generation flagship, but Focus 9 is still marketed and still widely deployed, and both share the same underlying service layer.

Focus Softnet publishes no developer portal. However, unlike most vendors in this part of the catalogue, the actual contract is recoverable: a public Postman team workspace contains a collection called FocusAPI with ninety requests against a /8API/ path, complete with the login request, real session responses and real voucher bodies, and a second collection posting a sales voucher against a live customer tenant at ymt-9.focus9erp.com/focus8api/. Two independent artefacts agree on the path prefix and the header name, which is why this page can be specific where the Logic ERP page cannot.

At a glance

Warning

Provenance matters here. Everything technical on this page comes from a public Postman team workspace under the handle cloudy-star-9788 (team 247201), which is not badged as Focus Softnet. It is treated as credible because the collections reference a real Focus customer tenant (ymt-9.focus9erp.com), internal Focus development IP addresses, and a WCF service layer named Focus8Library that matches Focus's own product naming, and because two separately created collections agree on the 8API prefix and the fSessionId header. Confirm every endpoint with Focus Softnet before shipping. Do not treat the field lists as a contract.

What it is

Focus Softnet sells a family of products: FocusX, the current flagship described as a fourth-generation AI-enabled ERP; Focus 9, the previous flagship and still a live product page; and vertical products including Focus WMS, Focus MRP, Focus POS, Focus CRM, Focus HCM, Focus REMS for real estate and Focus CAFM. The company runs country sites for India, the United States, the United Arab Emirates, Saudi Arabia, Kuwait, Qatar, Oman, Bahrain, Kenya, Singapore, Malaysia, Indonesia, the Philippines, Bangladesh, Canada, Nigeria and the United Kingdom.

The stack is Microsoft: an ASP.NET application with a WCF service layer and a Microsoft SQL Server database. Deployments are either on the customer's own server or on a Focus-hosted cloud tenant, and the cloud tenants use a per-customer hostname pattern, <customer>-9.focus9erp.com, resolving to Azure. That pattern is the practical thing to know: the base URL is per customer, and "the Focus 9 API" is not a single shared host.

FocusX marketing lists "3rd Party Apps and RESTful APIs" as a feature and names Xero, Tally, QuickBooks, Stripe, PayPal, ICICI Bank, Pine Labs, Mada, Shopify, Magento, WooCommerce, Amazon, Twilio, Office 365, UPS, ShipStation, ShipHawk, MailChimp, SendGrid and Zapier as integrations. No Zapier app under any Focus Softnet name could be found on Zapier, so treat that list as a statement of capability rather than a list of shipped connectors.

API access

There is no developer console, no API key issuance page and no published plan. Access works like this:

  1. The customer already has a Focus 9 installation, either on-premise or on a Focus-hosted tenant.
  2. You obtain a Focus user account on that installation, with the company code for the company you are integrating against. The login request takes a user name, a password and a company code, so the credential is an ordinary ERP user, not a separate application credential.
  3. Your base URL is that installation's host plus the API path. The recovered collections use http://localhost/8API/... for a local install and https://ymt-9.focus9erp.com/focus8api/... for a hosted tenant. The casing of the prefix varies between 8API, 8api and focus8api in the same collection, which suggests the server is case-insensitive, but confirm rather than assume.

No API version number appears in any path or header. That is a risk: there is no visible contract for what breaks on upgrade.

No official SDK exists in any language. No first-party OpenAPI specification could be found. GitHub has no Focus Softnet owned organisation or repository.

Authentication

The model is a session token, not OAuth and not a static key.

  1. Call the login endpoint with the user name, password and company code. bfrommobile and bwithmobileappdata control whether the server also returns mobile application data, and both can be sent as false for a server-to-server integration.
  2. Read fSessionId from data[0].
  3. Send that value in an fSessionId header on every subsequent request, alongside Content-Type: application/json.

Token request:

POST /8api/MobileLogin HTTP/1.1
Host: yourtenant-9.focus9erp.com
Content-Type: application/json

{
  "data": [
    {
      "UserName": "su",
      "password": "su",
      "Companycode": "4p0",
      "bwithmobileappdata": false,
      "bfrommobile": true
    }
  ]
}

Response:

{
  "url": "http://localhost/8api/MobileLogin",
  "data": [
    {
      "iLoginId": 1,
      "Status": 4,
      "EmployeeId": 0,
      "iLogId": 4985,
      "LoginName": "SU",
      "AltLanguageId": 0,
      "fSessionId": "4P0-020120261239336460841",
      "EmployeeName": ""
    }
  ],
  "result": 1,
  "message": ""
}

Authenticated call:

GET /8API/List/Transactions/Sales%20Orders?pageNo=0&NoOfRows=2 HTTP/1.1
Host: yourtenant-9.focus9erp.com
fSessionId: 4P0-020120261239336460841
Content-Type: application/json

Notes on the token:

  • The fSessionId value is structured, not opaque: 4P0-020120261239336460841 begins with the company code in upper case, then what reads as a date and time stamp. Another sample, 0X0-0412202511332922111881, has the same shape. Do not parse it, but do expect it to be company-scoped, which means one session per company rather than one session across companies.
  • Lifetime is not published. Treat it as a server session that can expire, and build a re-login path triggered by an authentication failure rather than a fixed refresh timer.
  • Header capitalisation is inconsistent across the collections: fSessionId on most calls, fsessionId on the voucher post, fsessionid on the OTP call, and an empty fsession header on login. HTTP headers are case-insensitive so this should not matter, but it is a sign of a hand-built surface.
  • Multi-account: select the company at login with Companycode, and use GET /8API/List/Company to enumerate companies. Several utility calls take a CompanyId, for example GET /8API/List/Company/SupportedLanguage?where=CompanyId=36 and GET /8API/utility/companycodefromid/36. For multiple sellers on separate installations, keep a credential and base URL per installation.
  • An OTP login exists, POST /8api/LoginWithOTP, taking UserName, OTP, CompanyId and loginid. It is unsuitable for an unattended connector.
  • A licence check exists, POST /8api/GetLiceneceInfo, taking companyid. The endpoint name is misspelled on the server, which is worth remembering when it returns 404.

Objects we can read

Focus models almost everything as a voucher. The generic reading pattern is: list voucher types, list vouchers of one type, then fetch one voucher by id.

Orders

Sales orders are a voucher type named Sales Orders.

  • List: GET /8API/List/Transactions/Sales Orders?pageNo=0&NoOfRows=2. Pagination is page number plus rows per page, and the response carries EndOfFile as the stop condition.
  • Detail: GET /8API/Screen/Transactions/Sales%20Orders/{headerId}.
  • Field definitions for the type: GET /8API/Field/Transactions/Sales%20Orders. This returns the configured field list, which matters because Focus vouchers are user-configurable and two installations of the same version will not have the same fields.
  • Voucher settings: GET /8API/Screen/Transactions/VoucherSettings/{abbr}.
  • All voucher types: GET /8API/List/Transactions.
  • Classification helpers: GET /8API/utility/isitsales/Sales%20Orders, GET /8API/utility/IsItOrder/Sales%20Orders, GET /8API/utility/isitinwardvoucher/{abbr}, GET /8API/utility/isittransfer/{abbr}, GET /8API/utility/isitwms/{abbr}.

The list response is a grid, not a record set. Columns are described separately from the rows, so the connector has to zip Columns onto ColumnData by position.

{
  "data": [
    {
      "ColumnData": [
        [580, "Ajay", "1/191", "23/12/2025", "SU", "False", "Pending"]
      ],
      "TotalRow": 0,
      "EndOfFile": false,
      "Error": false,
      "Columns": [
        { "ColumnAlias": "HeaderId", "IsHidden": true },
        { "ColumnAlias": "Customer Account Name", "IsHidden": false },
        { "ColumnAlias": "Voucher Number", "IsHidden": false },
        { "ColumnAlias": "Date", "IsHidden": false },
        { "ColumnAlias": "Created by", "IsHidden": false },
        { "ColumnAlias": "Suspended", "IsHidden": false },
        { "ColumnAlias": "Authorization Status", "IsHidden": false }
      ]
    }
  ],
  "result": 1,
  "message": null
}

The detail response splits into Header, Body and Footer.

{
  "data": [
    {
      "Header": {
        "DocNo": "1",
        "Date": 132317982,
        "Time": 929590,
        "CustomerAC__Id": 19,
        "CustomerAC__Name": "Customer A",
        "CustomerAC__Code": "122-001",
        "Department__Id": 0,
        "sNarration": "su",
        "HeaderId": 585,
        "Net": 525,
        "TransactionNet": 525
      },
      "Body": [
        {
          "Item__Id": 1,
          "Item__Name": "item",
          "Item__Code": "0",
          "Unit__Id": 0,
          "Quantity": 5,
          "Rate": 105,
          "Gross": 525,
          "Discount": { "Input": 0, "FieldName": "Discount", "FieldId": 8, "Value": 0 },
          "TransactionId": 585,
          "BaseQuantity": -5
        }
      ],
      "Footer": [
        { "FieldId": 25, "FieldName": "FooterVal", "Input": 0, "Value": 0, "ColMap": 1 }
      ]
    }
  ]
}

Fields marked PII: CustomerAC__Name, CustomerAC__Code, any delivery address field, and the Signature field, which returns a base64 JPEG of a captured signature inline in the header. Strip it before storing.

Date is an integer date identifier in Focus's internal format, not Unix epoch seconds and not an ISO date. Differences between samples behave like minutes but the epoch is not published. Get the conversion from Focus Softnet rather than reverse engineering it, and never persist the raw integer as if it were a timestamp.

Order items

Body lines as shown above: Item__Id, Item__Name, Item__Code, Item__Alias, Unit__Id, Unit__Name, Quantity, Rate, Gross, Discount, TransactionId, BaseQuantity, BodyFlags. The double underscore is Focus's convention for a master reference expanded into id, name, code and alias.

Products and listings

  • GET /8API/List/Masters lists the master types available.
  • GET /8API/List/Masters/Core__Product/Group lists product groups.
  • GET /8API/List/Masters/Core__Account lists accounts, and GET /8API/List/Masters/Core__Account/{groupCode} lists within a group. Focus treats products and ledgers through the same master machinery, so the master name selects what you get.
  • GET /8api/Screen/Masters/Core__Account/{id} fetches one master record.
  • GET /8API/List/Masters/Core__Account/Trees, /TreeViews and /Leaf expose the master hierarchy.
  • POST /8API/Masters/masterdatacoloumnwise returns master data column-wise. The spelling is the server's.
  • GET /8API/Screen/CoreMasters/Units and GET /8API/List/CoreMasters/UnitConversion cover units and conversions, which matter for any retail catalogue with cases and pieces.
  • Price books: GET /8API/List/CoreMasters/BuyerPriceBook. A seller price book list uses the same path in the recovered collection, which looks like a copy and paste error in the collection rather than a real duplicate.
  • Rates for a product in context: POST /8API/Transactions/Rates with product__id, currency__id, date__id, pbabbr (price book abbreviation), pricebooktype, quantity, unit__code, customer__id. This is the call that answers "what price does this customer pay for this item today".
  • Tax rates: GET /8API/List/Transactions/Taxrates.
  • Product images: the WCF layer has POST /Focus8Library/MasterService.svc/GetProductImage.

Sample responses for the master endpoints are not published in the recovered material.

Inventory

  • POST /8API/Transactions/Stock with a body of {"data": [{"Product__Id": "2", "Date__Id": "132317971"}]} returns stock for a product as at a date. Sample response not published.
  • Batches: GET /8API/Transactions/Batches. Bins: GET /8API/Transactions/bins.
  • The WCF warehouse layer is richer: POST /Focus8Library/wms2service.svc/LoadProductInquiry for item enquiry, LoadBinInquiry for bin enquiry, LoadSkidInquiry for pallet enquiry, ValidateBin, ValidateSkid, LoadAllocationData and PostAutoAllocate. Warehouse is a separate service, not part of 8API.

Note that the stock read takes a single product id. There is no documented bulk stock endpoint in the recovered material, which is the single biggest practical problem for a catalogue sync. Ask Focus Softnet for a bulk stock or changed-since stock call before designing around per-product polling.

Shipments and tracking

Not present as an object. Focus is not a carrier and holds no AWB or carrier event. The warehouse layer has dispatch confirmation, POST /Focus8Library/wms2service.svc/SaveDispatchConfirm, and GetLogisticInfo, which is as close as it gets. Carrier data must come from the logistics provider.

Returns and cancellations

Returns are voucher types like any other, reachable through the same list and detail pattern once you know the type name and abbreviation for the installation. GET /8API/List/Transactions enumerates them. No dedicated returns endpoint exists.

Payments and settlements

  • Receipts are a voucher type: the authorisation endpoints use Receipts as their worked example, GET /8API/Screen/Transactions/AuthorizeVouchers/Receipts/Rct:1 and GET /8API/Screen/Transactions/RejectVouchers/Receipts/Rct:1.
  • POST /8API/Transactions/accountbalance with account__id, date__id and balancetype returns a ledger balance.
  • POST /8API/Transactions/ExchangeRateForLocalCurrency handles multi-currency, which matters for the Gulf deployments.
  • There is no marketplace settlement object. Settlements come from the channel, not from Focus.

Customers

Customers are accounts under the account master, read through GET /8API/List/Masters/Core__Account and GET /8api/Screen/Masters/Core__Account/{id}. Treat name, code, address and contact fields as PII.

Locations

Warehouses appear as an id on voucher lines (Warehouse in the posting body) and as bins and stores in the warehouse layer, for example GET /Focus8Library/TransactionService.svc/LoadDistributionStoreList?iASNId=123. There is no simple documented "list locations" call in 8API; enumerate through the master list.

Reports

Focus exposes its report engine over the API, which is often the fastest route to a bulk extract when no object endpoint fits.

  • GET /8API/List/Reports, GET /8API/List/Reports/ReportParameters/{reportName}, GET /8API/Screen/Reports/ReportLayouts/{reportName}/{id}.
  • POST /8API/Reports/pagedata with ReportId, LayoutId, StartingDate, EndingDate, RowsPerPage, CurrentPage, Masters and Inputs returns a page of report rows. POST /8API/Reports/pagedatahtml returns the same as HTML.
  • GET /8API/Screen/Reports/Closereport?where=UniqueId={guid} releases the server-side report session. Call it. Report sessions that are never closed are a classic way to exhaust a Focus server.
Warning

The collection also contains POST /8API/utility/ExecuteSqlQuery and POST /8API/utility/ExecuteNonQuery, which take a raw SQL string and run it against the ERP database. If that is enabled on a customer installation it is both the easiest extraction route and a serious security problem. Do not build on it. Raise it with the customer as a finding.

Writing back: listings, price and stock

For an ERP, read this as: can we push sales orders, invoices and stock adjustments in. The answer is yes, through one generic voucher endpoint.

Create a voucher:

POST /focus8api/Transactions/Vouchers/5634 HTTP/1.1
Host: yourtenant-9.focus9erp.com
fSessionId: 0V0-121220251194096311161
Content-Type: application/json

{
  "data": [
    {
      "Header": {
        "Salesman": "14",
        "bIsNew": true,
        "CustomerAC": "2966",
        "Department": "1",
        "Tax Code": "2",
        "OrderedBy": "",
        "DeliveryAddress": "",
        "DocNo": "",
        "Date": "132713484"
      },
      "Body": [
        {
          "Item": "5295",
          "Warehouse": "1",
          "Description": "OOTY GOLD PONNI PARBOILED RICE - 5KG X 6",
          "Rate": "10.00",
          "Quantity": "12.00",
          "Gross": "120.00",
          "Unit": "2",
          "TransactionId": "0"
        }
      ]
    }
  ]
}

Points that decide the design:

  • 5634 in the path is the numeric voucher type id. It is installation-specific, so resolve it from GET /8API/List/Transactions rather than hard-coding it. The same collection shows a second posting form, POST /8API/Transactions/{voucherTypeId}-{abbr}, for example /8API/Transactions/944-RTS, used for a richer body with batches and bins. Which form applies to which voucher type is not documented.
  • bIsNew: true marks a create. Setting it false is presumably an update, but no update example exists and this has not been verified.
  • DocNo is sent empty and the server allocates. If you need the number in advance, POST /8API/Transactions/NewVoucherNumber with {"data":[{"VoucherType":"Sales Orders"}]} returns one.
  • Every reference in the body is an internal id, not a code: CustomerAC, Item, Unit, Warehouse, Salesman, Department and Tax Code are all numeric. A write connector therefore needs a resolution step from your SKU and customer code to Focus ids, and a cache of that mapping. This is the main build cost.
  • There is no documented idempotency key and no documented external reference field. Duplicate protection has to be built on your side, most safely by writing your own reference into a user-defined field on the voucher and checking for it before posting. Focus vouchers are user-configurable so such a field can be added by the customer.
  • The richer posting form carries Batch, ExpDate__Id, Bins with Bins__Id and skidid, LPN_Line_No and Usecase, so batch and bin level stock movements are writable where the installation is configured for them.
  • Delete: DELETE /8API/Transactions/{voucherTypeId}/{voucherNumber}, for example /8API/Transactions/2564/SCN2026~~00001. Note the double tilde separator inside the voucher number.
  • Authorise or reject a posted voucher: GET /8API/Screen/Transactions/AuthorizeVouchers/{type}/{ref} and .../RejectVouchers/{type}/{ref}. Whether a created voucher lands authorised or pending depends on the installation's workflow, and the list response's Authorization Status column shows Pending in the sample, so do not assume a write is final.
  • Masters: POST /8API/Masters/CreateMaster defines a new master type, and POST /8API/Masters/{masterName} saves a record, for example POST /8API/Masters/Core__jobno with {"data":[{"sName":"j1","sCode":"j1"}]}. The warehouse layer also has POST /Focus8Library/MasterService.svc/SaveMasterByNames, which resolves references by name instead of id and is worth asking about, because it would remove most of the id resolution work.

Price and stock are not written directly. Price is a master and price book concern, stock changes only through vouchers. There is no "set stock to N" call, and there should not be.

No sample success or error response for a voucher post is published. The collection that contains the live posting is named "Ambikas Posting Null Return", which reads as a support ticket about a posting that returned null. Take that as a warning that error handling on this endpoint is not obvious and needs to be established empirically with the vendor.

Webhooks and notifications

None found. No subscription endpoint, no event catalogue, no signature header and no retry policy appears in any recovered material, and Focus Softnet publishes nothing on the subject.

There is an in-application alert system, GET /8API/Screen/Transactions/Alerts, GET /8API/Screen/Transactions/Alerts/{id} and GET /8API/Screen/Transactions/AlertApprovalCount, but it notifies Focus users inside the product, not external systems.

Poll instead. A workable cadence for a single installation, to be agreed with the customer because most installations are on hardware they own:

  • Sales and financial vouchers: every 15 minutes, using the voucher list with page numbers and stopping on EndOfFile. There is no documented modified-since filter, so either use the report engine with a date range or filter client side on the date column.
  • Masters: hourly for changed items, daily for a full refresh.
  • Stock: this is the problem case, because the only documented read is one product at a time. Prefer a stock report through POST /8API/Reports/pagedata over looping the stock endpoint, and agree the schedule with the customer.

Rate limits and pagination

No rate limit is published, no quota header appears in any sample, and for on-premise installations the binding constraint is the customer's own server rather than a vendor quota.

Pagination on list endpoints is page number and page size: ?pageNo=0&NoOfRows=2, with EndOfFile in the response as the terminator and TotalRow present but zero in the sample, so do not rely on a total. The report engine paginates with CurrentPage and RowsPerPage in the request body.

Practical guidance: keep concurrency at one request at a time per installation until the customer's IT contact agrees otherwise, always close report sessions with the close endpoint, and prefer the report engine over row-by-row endpoints for anything bulk.

Mapping to the unified model

Field names below are the ones observed in the recovered collections. They are configurable per installation, so verify against GET /8API/Field/Transactions/{voucherType} for the specific customer before relying on any of them.

Gaps and open questions

  • Provenance. Nothing here is vendor-published. The Postman workspace is public and internally consistent and matches a real customer tenant, but Focus Softnet has not confirmed any of it. Every endpoint needs confirming before a customer commitment.
  • Versioning. No version appears in any path or header, and the prefix is 8API, inherited from Focus 8. Whether FocusX keeps the same surface is unknown and is the first question to ask, since Focus is actively moving customers onto FocusX.
  • The date and time integer format is unresolved. This blocks any date filtering and is the highest priority item for a vendor conversation.
  • There is no documented modified-since filter on any list endpoint. Incremental sync may have to go through the report engine, which changes the whole design.
  • There is no documented bulk stock read. Per-product polling will not scale for a retail catalogue.
  • There is no documented idempotency mechanism on voucher creation. Double-posting an order is a real risk and needs a customer-side guard.
  • The two voucher posting path forms, /Transactions/Vouchers/{id} and /Transactions/{id}-{abbr}, are unexplained. Which to use when is unknown.
  • Rate limits, session lifetime, error codes and the response shape of a failed write are all unpublished.
  • ExecuteSqlQuery and ExecuteNonQuery accept raw SQL. If present and reachable on a customer installation, that is a security finding to escalate, not a feature to use.
  • Focus 9 versus FocusX. The Focus 9 product page is still live but Focus 9 is absent from the current product sitemap, which lists FocusX instead. Confirm which product a given customer runs before quoting this page.
  • Do not confuse this vendor with Focus POS, an unrelated United States restaurant point of sale company that also has a public Postman collection.

Sources

  • Focus Softnet Focus 9 product page, read 2026-09-22
  • Focus Softnet FocusX product page, read 2026-09-22, source of the "3rd Party Apps and RESTful APIs" claim and the integration list
  • Focus Softnet product sitemap, read 2026-09-22, showing the current product line and country sites
  • Public Postman collection FocusAPI, uid 5953841-6f16752e-8a88-4b5a-8836-615919f2b4f1, team 247201, publisher handle cloudy-star-9788, created 21 November 2025 and updated 5 May 2026, retrieved 2026-09-22. Ninety requests under /8API/, description: "Focus Softnet offer a robust REST API for developers a seamless integration platorm. It allows access to all the modules and nearly all the feautes available in Focus-ERP." Spelling as published
  • Public Postman collection Focus8Library, uid 5953841-13b75822-6e1d-4720-9197-f2e97913e7cc, same team, retrieved 2026-09-22. The WCF warehouse service layer
  • Public Postman collection Ambikas Posting Null Return, uid 5953841-c4947499-978c-4b53-90da-11cb28f55797, same team, retrieved 2026-09-22. Source of the live voucher posting request against ymt-9.focus9erp.com/focus8api/
  • DNS and HTTP probes on 2026-09-22: docs.focussoftnet.com, api.focussoftnet.com and developer.focussoftnet.com resolve through a wildcard but serve nothing; ymt-9.focus9erp.com resolves to an Azure address
  • Zapier app directory search for Focus Softnet, FocusX, Focus 9 and Focus ERP, run 2026-09-22: no matching app, despite the Zapier logo on the FocusX integrations panel
  • GitHub repository and code search for focussoftnet, run 2026-09-22: no vendor owned repository or SDK