Wednesday, 14 September 2016

Acumatica REST API

HI All,

With Acumatica 6 release you can find (and actually use) new type of API - Rest API.
Acumatica 6 API

Acumatica Rest API is based on Contract based API, so here you have some important points:

  • You need to use existing or custom endpoint be able to send API calls
  • Field and container is available for REST API only if it is defined in contract. But you may extend existing contracts.
  • With REST API you have the same set of commands that you have with Contract Based API.
  • Acumatica uses Json format for transfer data between client and server
  • You still have to maintain session and authentication cookies.

URL:
http://<InstanceName>/entity/<EndpointName>/<EndpointVersion>/<Entity>
Example: http://acumatica.com/entity/Default/6.00.001/StockItem

Ok, lest try to do some examples. Here I will show you how to call Acumatica REST commands from Browser. By using this approach you can easily test functionality and just feel, how does it work.


To do so, we need a special tool. I will use PostMan extension for Google Chrome browser.
postman api builder
As we need to maintain session and cookies between calls, we also need to install Postman Interceptor extension.
postman api builder


Login
Now we actually can login. To do so, we need to send our credentials for the specific url:

URL: http://acumatica.com/entity/auth/login
JSON data:
{
    "name" : "admin",
    "password" : "123",
    "company" :  ""
}
Response should be 204 No Content.


Geting
Ok, authentication is done, lets try to select data.

URL: http://acumatica.com/entity/Default/6.00.001/StockItem



Filtering
If we want some filtering or conditions, we just can use "OData" like filters - $filter=ItemStatus eq 'Active'

URL: http://acumatica.com/entity/Default/6.00.001/StockItem?$filter=ItemStatus eq 'Active'&$top=9

Additional parameters that you can use together with URL when you retrieve records from Acumatica ERP:
  • $filter: To specify filtering conditions on the records to be returned
  • $skip: To specify the number of records to be skipped from the list of returned records
  • $top: To specify the number of records to be returned in the list
  • $expand: To specify the linked and detail entities to be expanded
  • $custom: To specify the fields that are not defined in the contract to be returned

Getting Single Record
If you know key, you can easily get details about single record - just add key field to the url string:

URL: http://acusea.acumatica.com/future/entity/Default/6.00.001/StockItem/AACOMPUT01


Puting
You also can create new entity using REST API, in this case you need to use PUT method and send item details using JSON format. Name of the fields and containers you can get from Contract definition.

URL: http://acusea.acumatica.com/future/entity/Default/6.00.001/StockItem

JSON:
{
"InventoryID" : {value : "Kettler" } ,
"Description" : {value : "New Cool Kettler" },
"ItemClass" : {value : "ELECCOMP" }
}


Documentation
Good news that the documentation on the REST API is included right within standard Acumatica Help. There you can find multiple examples and good code snippets that you can use from your favorite language/platform/code.


Have a nice integration!

35 comments:

Andrew Tainton said...

Hi,

Thanks for the article, could you provide an example using an action?

Sergey Marenich said...

Hi Andrew,

You can use it like this:
http://[Base endpoint URL]/[Top-level entity]/[Action name]

You use the POST HTTP method and pass the record to which the action should be applied and the parameters of the action in the request body in JSON format as follows:
{
"entity" : [record in JSON format],
"parameters" : [parameters in JSON format]
}

dkardell said...

How do you pass the filter? Your example has spaces in it?
http://acumatica.com/entity/Default/6.00.001/StockItem?$filter=ItemStatus eq 'Active'&$top=9

Sergey Marenich said...

Hi Dkardell,
You should pass filters with query URL. And yes, you should have spaces there, but in the end spaces should be encoded as accordingly to standard URL encoding rules

Tim said...

Hi Sergey,

I'm trying to work with the REST APIs using PHP cURL code.

I'm testing by doing to calls in Postman: Login, Create a Customer. It works fine.

Then I generate the PHP cURL code and run the code in PHP, but I get an error on the second call: {"message":"You are not logged in."}

I'm thinking that Postman automatically passes the session information between calls.

So, I added Postman Interceptor and now I get back cookie information like this: https://imgur.com/a/zejPA

But, when I generate the PHP cURL code, it still doesn't generate the cookie information:
"http://localhost/Acumatica/entity/auth/login",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "{\r\n \"name\": \"admin\",\r\n \"password\": \"mypassword\",\r\n \"company\": \"Company\"\r\n}",
CURLOPT_HTTPHEADER => array(
"cache-control: no-cache",
"content-type: application/json",
"postman-token: 3af0af99-e514-ab2e-4f69-7481a1b9c6ce"
),
));

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}

Do you know how to get the PHP cURL code that will pass the cookie information between API calls?

Tim said...

Hi Sergey,

Nevermind on this. I was able to get it to work.

I just had to figure out how to pass cookies between the requests. I couldn't figure out though how to post my successful PHP code sample here in the comments so I created a post on my blog instead.

You can go here for the successful PHP code that I used:
http://www.TimRodman.com/acumatica-rest-api-php-curl/

Sergey Marenich said...

Tim,
Thank you for confirmation!

Benjamin Cadoche said...

Hi Sergey,
I am trying to use the action Prepare Invoice for a sales order with the instructions from your post dating 26th January.
I can't get it to work, do you think it is possible to do it for this action ?

Thanks,

Benjamin Cadoche said...

Hi Sergey,

Nevermind on this. I was able to get it to work.
I needed to add the PrepareInvoice action in web service endpoint SalesOrder.

Sergey Marenich said...

Benjamin,
Thank you for confirmation. Ans sorry fore late replay, that is due to time-zones.

LAKKI said...

Hi Sergey,

Could you give an example of how the expand parameter can be used?
I tried the following but it did not work.
http://try.acumatica.com/isv/(W(4))/entity/Default/6.00.001/SalesOrder?$expand=SalesOrderDetail

VANNAK said...

Can we create record with custom field in rest api:
{
"OrderType": {value: "IN"},
"CustomerID" : {value : "1ARA" } ,
"Details" :[
{
"InventoryIDz": {value: "1BRO01"},
"InventoryID" : {value: "6DOC"}
}]
}

Sergey Marenich said...

Hi Lakki,
Try https://acusea.acumatica.com/entity/Default/6.00.001/SalesOrder?$expand=Details
Expand should add details records to result. In Expand you should pass name of container that exists in Acumatica Contract Endpoint.
But please note that it will significantly affect performance

Sergey Marenich said...

Vannak,
Yes, you can but you need to put custom fields in a special collection.
Please check here: https://help.acumatica.com/(W(5))/Main?ScreenId=ShowWiki&pageid=64daacf1-75c4-4bfa-b57b-36222020e7c9

LAKKI said...

Hi Sergey,

Thank you for the reply.

Regards
Lakki

Indranil Saha said...

Hi,

Can you tell me how to use date filter while fetching items. The filter mentioned in the documentation doesn't seem to work.

Regards,
Indranil Saha

LAKKI said...

Hi Indranil,

Try using no filters to find out the date field name for the specific object(endpoint) you are looking for. For example, the StockItem endpoint gives information about the items that are on stock. It has a field 'LastModified' that accepts date values as filters. Here is how the filter can be applied.
https:///StockItem?$filter=ItemStatus eq 'Active' and LastModified gt datetimeoffset'2017-10-01'
This URI would fetch all stock items that are in a status of 'Active' and modified after 01-OCT-2017.

Good luck.

Indranil Saha said...

Hi Lakki,

Thank you, that worked.

Regards,
Saha

Indranil Saha said...

Hi,

One more thing, can you please tell me how to fetch product price through API and add multiple addresses to a customer in POST request.

Regards,
Saha

LAKKI said...

I did think the best way to fetch a product price would be to use the endpoint for products(NonStockItem or StockItem I think) and then use the response JSON to extract the relevant information (in this case the price as you want it) from it.
As for the multiple addresses, I think these multiple addresses would need to be added as a JSON array onto the corresponding linked entity. I have not tried it myself though.

Cheers

Sergey Marenich said...

NonStockItem or StockItem are not the best place for price, as price may be different per customer/class/item/promotion and so on.
There is an example in our Acumatica I210 training guide (Contract-Based Web Serivices) that uses small csutomization fro that. Please check Lesson 3.4: Retrieving the Price of an Item
You can do the same with REST, but use PUT method

Rajasekaran Gopinathan said...

Hi, how do I select a value from a selector based on some condition? I'm creating an Inventory Adjustment transaction and I need to select a ReceiptNbr to associate with the adjustment transaction. I need to select the oldest receipt with a non zero qty. on hand from the selector. How do I perform this? Can you provide an example? Thank you.
-Raj

Sergey Marenich said...

Hi Rajasekaran,
I do not think you can do this in one request. I suggest you get oldest receipt from receipts screen or generic inquiries and than create a adjustment with second request.

Maurício said...

Hello,

I created a Production Order using the REST api in Postman. Now I need to release this production order.

I am POSTing to http://localhost/Development/entity/MANUFACTURING/17.200.001/ProductionOrder/ReleaseProductionOrder
{
"entity":{"ProductionNbr": {"value": "0000249"}},
"parameters": null
}

response:

{
"message": "The request is invalid.",
"modelState": {
"parameters": [
"Error reading JObject from JsonReader. Current JsonReader item is not an object: Null. Path 'parameters', line 3, position 22."
]
}
}

Any idea on why I am getting this message ?

Thanks,

Mauricio Camara

Sergey Marenich said...

Hi Maurício,
I see that you miss the second key of Production Order - OrderType. When you pass entity you need to pass all keys to find it.
Also make sure that your action is defined in the contract.

Maurício said...

Thanks for the response Sergey ...

I tried passing the OrderType as well. I tried using ProductionNbr as well as the DB field name ProdOrdID. The Action is defined in the web services endpoint ProductionOrder > Actions > ReleaseProductionOrder.

Passing the number and type my response was:
{
"message": "The request is invalid.",
"modelState": {
"parameters": [
"Error reading JObject from JsonReader. Current JsonReader item is not an object: Null. Path 'parameters', line 3, position 22."
]
}
}

Thanks a lot,

Mauricio

Sergey Marenich said...

Hi Maurício,
I'm really sorry for long reply. Could you please create a case with Acumatica team? It looks that we need to investigate your problem. Thank you!

Tourvi said...

Hi, where can I find a list of all the top-level entities that can be retrieved through the api?

Thanks

LAKKI said...

Hey Tourvi,

You should be able to get the list of available endpoints and its fields on the menu "System" >> "Integration" >> "Web Service Endpoints".

Goodluck!

Nick said...

Hi, How can I update the lines on a Shipment, such as the LotSerialNumber? do I need to supply a specific row number?

Sergey Marenich said...

Hi Nick,
You can use allocations popup on SO to put LotSerialNbr there:
new SalesOrderDetail()
{
InventoryID = new StringValue() { Value = "AAMACHINE1" },
Allocations = new SalesOrderDetailAllocation[]
{
new SalesOrderDetailAllocation()
{
Allocated = new BooleanValue() { Value = true },
LotSerNbr = new StringValue() { Value = "123" },
},
},

Or you can create shipment from shipment screen and link it with sales order. In second case you need to have 2 calls.

Nick said...

Hi Sergey,

I was referring to how use the rest api. I am using postman and I have been unable to submit a value to the detail array.

I was thinking it would look something like this:

{
ShipmentNbr: { value: "Shipment #" },
ShipmentDate: { value: "4/26/2018"},
Detail: [
{
Inventory: { value: "Some Item ID"},
ShippedQty: { value: "9" }
}
]

}

This does not seem to work.

Thanks in advance

Sergey Marenich said...

Nick,
One of the issues you have is "Inventory" -it should be "InventoryID"
But if that does not help, please connect me by skype and we can discuss it there. I'll need an error message.

Nick said...

HI Sergey,

The InventoryID does not work either.

I am trying to update the shipping lines (Lot Serial Numbers) via a put method, but it seems that the api method only attempts to add new line. is there some kind of identifier (like the @@ in import scenarios) so I can uniquely identify a specific shipping line.

What would be the best way to message you, so I can give you my skype info?

Thanks for all the help.

Sergey Marenich said...

Nick, sorry for later reply.
You can retrieve data by using record ID. Read about it here http://help.acumatica.com/Main?ScreenId=ShowWiki&pageid=bc9531b0-717b-4b2d-8899-ff7ca805ade1
Or you also can retrive and update record by keys
http://help.acumatica.com/Main?ScreenId=ShowWiki&pageid=52c97a83-1fa1-40e9-8219-52a89a91f2da
Id can be obtained from "ID" field when you retrieve records.

To contact me please message me in linkedin or thought google hangouts. For security reasons I do not want to publish my skype on blog.