Kinetic REST API headaches

Hi all - I’m pretty new to Epicor, admittedly. I’ve been in my position for about a year now. But I am not new to programming - I’m a 25 year I.T. journeyman coder; I’m pretty familiar with relational data and RESTful APIs.

I don’t understand what the point is of many of these Kinetic REST API routes that suggest they will retrieve collections for you, but you end up getting nothing back. Please note that I am talking about running these routes in the REST API Help pages using the OpenAPI “Try it Out” functionality, so none of this has to do with coding issues on my part.

Here is an example:
I have a Sales Order Number. I want to retrieve the OrderHed record, all the OrderDtls records, and all the OrderRel records in as efficient and performant a manner as possible using the REST APIs. The Erp.BO.SalesOrderSvc API has ostensibly convenient routes that would suggest this is no problem, e.g.:
GET /{currentCompany}/Erp.BO.SalesOrderSvc/SalesOrders
It’s an OData route, so you set a $select list to pare down the inordinate number of fields, and set the $filter to:
“OrderNum eq {someOrderNumber}”

This works just fine. But then to get OrderDtls, you perform the same operation using:
GET /{currentCompany}/Erp.BO.SalesOrderSvc/OrderDtls.
This route’s description says it “Calls GetRows to retrieve OrderDtl items.” So you think this is the same deal - set the parameters, define a $select list, and set a $filter to:
“OrderNum eq {someOrderNumber}”

Except this time the response is:
{ "@odata.context": "https://{someEpicorInstance}/api/v2/odata/LBS/Erp.BO.SalesOrderSvc/$metadata#OrderDtls(OrderNum,OrderLine,OpenLine,PartNum)", "value": [] }

Why? I thought the idea behind the OData routes was that you have great flexibility between $select, $filter, and $expand to specify which data you want, how it’s presented, and even to pull child information. Except the reality I am running into here is that a simple, straightforward query - what would be a 1 line SELECT in a SQL context - requires multiple classes to operate through REST and gets you either no data or, in the case of using $expand, creates an unacceptable processing bottleneck (pulling just the OrderDtls along with the Order header for even a moderately sized order of ~100 sales order lines effectively crashes the call. I waited over five minutes and gave up - no user will - or should! - tolerate that.)

The only way I have found for this to work is to call an additional route from a completely different API - the List route from the Erp.BO.OrderDtlSearchSvc - just to get a list of OrderDtl keys that I can then turn around to retrieve individually. And that’s just one child collection. I still need to get OrderRels, JobProds, JobHeads, JobOpers, JobMtls, POHeaders, PODetails, and PORels. I can code this in async using parallel HTTP calls, but is this really the only way to retrieve these child records?

There has to be a better way. Can anyone explain what I am missing here?

Use the custom (RPC) ones. The ODATA ones are not easy to use.

1 Like

Some people will disagree with this, but when it comes to the Kinetic API, I use a BAQ endpoint for any GET call I need and use a Function endpoint for any POST.

Yes, you have to create BAQs and Functions, but for me that is easier than all the API interaction you’re going through.

1 Like

I partially agree. Some endpoints are great though and don’t need that extra fuss.

2 Likes

I have to be honest, at this point it seems a whole heck of a lot easier to just hit the database for retrievals. I just have an aversion to in-line SQL in C# APIs and writing my own SPs is not an option in our implementation. So, it seems I have some homework to do on BAQs and Functions.

2 Likes

The GetById route gets me the SalesOrder, but I still need the child objects - this route does not retrieve those keys, so I need a second call to get the OrderLine numbers, then one GetById call apiece for each OrderLine. Same process for the releases for each OrderLine.

That route returns the whole shebang.

2 Likes

Easier, but not secure or scalable. How does one secure that connection? Basic Auth? ActiveDirectory trusted connections?

How does that work with other environments? Test, Pilot, Education, etc.?

Over time, many find that using the framework does indeed get easier. Hang in there.

3 Likes

You may be right. I am not able to verify, however…

1 Like

Try the regular swagger page if that one is too intense.
https://yourserveraddress/server/api/help/v2/

Edit: Or open something with less data lol, I’ve seen people with 10000 line sales orders.

2 Likes

The security piece is definitely a concern, that is true.

Scalability is not as much for me. I’ve worked on an API hitting a SQL server DB that handled 40,000 requests a minute; our retrievals from a table that had 300 million rows were under 10ms - that’s total web call time, not just the SQL retrieval. It can be done if its designed correctly. I don’t need anywhere near that kind of performance here, but retrieving one SalesOrder taking so long that Edge asks me if I want to abort does not meet my standard of acceptable performance.

When you say “using the framework” - what is the “framework”? Are you just referring to Kinetic’s system of REST APIs? (Again, I’m still pretty new to Epicor.)

1 Like

That’s edge, not the api.

Pull it in postman.

2 Likes

Interesting. Didn’t know a regular Swagger page existed. I’ll give that a shot.

This SalesOrder only has about 100 lines, so unless I am vastly misunderstanding the volume of data involved I feel like it should at least open before the Edge mercy message comes up.

Poor choice of words on my part: managibility is what I meant. Hard coding databases to database servers, etc.

But I can see, I don’t have to sell you on the idea of an API! Why didn’t you just let all those users hit the database directly? :wink: So, yes. Just as you would prefer your app to use your company’s API, I would recommend the same when interfacing with Epicor.

If you’re a stickler for reducing dependencies in your clients, you should check out Epicor/Kinetic Functions. @klincecum has MANY very useful examples here on the list. They save your clients from all the dirty details of Kinetic and will upgrade smoother.

5 Likes

I have had both great, and crap performance from those pages. YMMV.

Just don’t judge the API performance from them. Do it in code or postman for a better test.

We were all new, and many of us are happy to teach you the ins and outs of Epicor’s api.

ODATA is made for machines, not people. The custom methods are usually where you want to be.

Ask away.

2 Likes

:+1: I can definitely switch my exploration over to Postman. I’ll give that a shot as well.

Epicor’s helper page has issues with big datasets so use the Swagger version or Postman like @klincecum mentioned.

1 Like

And if you want shape the data to reduce the size, check out Epicor Functions.

taylor swift hint GIF

4 Likes

FWIW, you can do nested $expand($select) on regular OData endpoints like so:

$select=CustNum,OrderNum,OrderDate&$expand=OrderDtls($select=OrderNum,OrderLine,OpenLine,PartNum;$expand=OrderRels($select=OrderNum,OrderLine,OrderRelNum,ReqDate))

{
  "@odata.context": "https://xxx.epicorsaas.com/xxx/api/v2/odata/xxx/Erp.BO.SalesOrderSvc/$metadata#SalesOrders(CustNum,OrderNum,OrderDate,OrderDtls(OrderNum,OrderLine,OpenLine,PartNum,OrderRels(OrderNum,OrderLine,OrderRelNum,ReqDate)))/$entity",
  "OrderNum": 24528,
  "CustNum": 32,
  "OrderDate": "2025-06-24T00:00:00-05:00",
  "OrderDtls": [
    {
      "OpenLine": true,
      "OrderNum": 24528,
      "OrderLine": 1,
      "PartNum": "MOD 4 TANK INSTALL",
      "OrderRels": [
        {
          "OrderNum": 24528,
          "OrderLine": 1,
          "OrderRelNum": 1,
          "ReqDate": "2025-10-10T00:00:00-05:00"
        }
      ]
    }
  ]
}
7 Likes