Developer Documentation



This documentation is designed to help developers get started communicating with the LOCATE Inventory RESTful API. Its interactive nature allows you to test requests against your instance of LOCATE. To take advantage of it's test requests you will need to enter your company domain alias below, followed by authenticating under Basic Authentication .

Set your Company Alias


Required Headers

On all requests to the LOCATE API it is required that you have the following headers.

Header KeyHeader Value


For a full list of RESTful API endpoints please visit our Swagger Docs

API Structure

LOCATE follows modern REST protocol. Primary endpoints are nouns, and actions are determined by HTTP request methods.
/salesorderRetrieve all salesordersCreate a new salesorderErrorError
/salesorder/1Retrieve the details for salesorder 1ErrorUpdate details for salesorder 1Deletes salesorder 1
/salesorder/1/line/4Retrieve the details for salesorder 1, line 4ErrorUpdate details for salesorder 1, line 4Deletes salesorder 1, line 4

For workflow operations on specific resources (e.g. Salesorders, Customers, Parts) the command is suffixed to the API request. Some of the workflow operations LOCATE uses are: Build, Clone, Commit, Deconstruct, Finish, Issue. A request to issue a salesorder would be:


HTTP Status Codes

  • 200 - OK - The request was successful
  • 201 - Created - A new record has been created
  • 204 - No Content - The server has successfully fulfilled your request and has nothing to return
  • 302 - Found - The server is attempting to redirect you. This status code commonly shows up when attempting to Login without the Required Headers .
  • 400 - Bad Request - The request body is improperly formatted. Be sure to compare the expected format against your request body.
  • 401 - Unauthorized - Session token is either expired, invalid or missing. See Basic Authentication
  • 422 - Unprocessable Entity - The request failed to pass validation. Check the response body or X-Error header for a failure reason.
  • 500 - Internal Server Error - The LOCATE server failed to process your request

Getting Connected

Basic Authentication

  • Build a login request containing a user's email and password


  • POST the login request to . On login success you will be provided a session_token in the login response.


  • The Basic Authentication header for subsequent requests consists of a Base64 encoded string containing session_token:session_token

    Authorization: {{data.basicAuthHeader}}

API Basics


For performance and scalability most LOCATE endpoints are paginated. This means that when you request a list endpoint such as the response will be limited to a subset of records. You can choose to specify the number of records that are returned per page using the perPage GET parameter. The current page you would like to request can be specified using the page GET parameter.

ParameterDescriptionDefault ValueMin ValueMax Value
perPageNumber of records to return per page1000100
pagePage to be returned by the API11Unbounded




The response of a paginated endpoint will be a JSON object containing your requested data along with various pagination details.

FieldData TypeDescription
totalIntegerTotal number of records across all pages
per_pageIntegerNumber of records per page
current_pageIntegerThe page number the current data set
last_pageIntegerThe last page available based on the total number of records
next_page_urlStringThe URL for the next page
prev_page_urlStringThe URL for the previous page
fromIntegerThe starting record count in the current data set
toIntegerThe ending record cound in the current data set
dataArrayArray of objects for the given endpoint


To filter the records of an endpoint you can simply pass a field name and value as a GET parameter. For example, if you would like to get a list of all active carriers you could submit a request to the endpoint with the GET parameter active=1




Advanced Filtering

[To Be Written]


To embed a related object on an endpoint you can use the embed GET parameter. A related object is commonly denoted by field name ending in _id. To embed multiple objects you can comma separate the related objects in the embed GET parameter.





To sort the records returned from an endpoint you can use the sort GET parameter. Supplying a field name such as sort=name will sort the returned records by the name field ascending. To sort descending add a minus in front of the field name sort=-name. Multi-sort can be performed by comma separating field names in the sort GET Parameter.




Limit Fields

To enhance performance of your request you can can limit the fields that are returned by the API. To limit the returned fields, specify a fields GET parameter with a comma separated list of fields you would like returned.

Note: Some fields are exempt from being eliminated and will always be returned. Embedded objects will always be returned in full.




Sales Orders

Creating a Salesorder

Lets start by creating a Salesorder for our customer, "Json Bourne", who has an id of 14. If you reference the list of salesorder parameters in our Endpoint Docs for creating a salesorder, you can see that the only required field is customer_id. In this case we know that the Id for "Json Bourne" is 14, but if we only had a customer name we could lookup Json's id with the following request.

Now that we have our customer_id, we can create our order.

    Body: {"customer_id": 14}

Adding Salesorder Lines

Lets add two Beanies to our order line. These beanies have a part id of 3, and a unit price of 8.00. To create a new line, we need to submit a salesorder_id, linetype_id, and part_id. The salesorder_id becomes a route parameter, and the rest of our information goes in our POST body. In this case our Linetype is Sale, which maps to 1. If we are unsure of our part_id or linetype_id, we can reference all parts and linetypes with a GET request to "/part" or "/linetype."

    Body: {"part_id": 3, "linetype_id": 1, "qty": 2, "unit_price": "8.00"}

Now that we've added a line to our salesorder, lets take a look at our current salesorder. We can get more information by embedding lines and status onto our salesorder.



Adding Discounts or Fees

Before we issue our salesorder, we can add an order discount. Discounts and Fees need to be created separately in LOCATE, or with an api call to "/discount" or "/fee". This same process can also be used to add line level discounts and fees.

Discounts can have rules that allow you to automatically apply them to orders, but in this case we will use a generic discount that subtracts a dollar from our order total.

    Body: {"discount_id": 3}

Issuing a salesorder

We can now issue our salesorder.


The status_id of our issued salesorder is 13, issued. To see a master list of all statuses we can make a GET request to "/status"; we could also just embed status when getting our salesorder records to see the status name.

Order Fulfillment

Fulfillment Basics

LOCATE's primary fulfillment process for an order consists of Pick, Pack, Ship, Pickup.

  • In the picking step, inventory for your order is committed from its storage location to a single moving location.
  • In the packing step, picked parts get placed into cartons.
  • In the shipping step, cartons can be palletized.
  • In the pickup step, completed cartons are picked up by the carriers.

LOCATE has a few different levels of automation for fulfilling an issued order. Orders can either be fully Autoworked, partially Autoworked, or processed manually. In the following section we will first explain how to manually work an order, to better understand each part of the process. However, feel free to jump ahead to Autoworking for a more automated look at order fulfillment.

We can always reference work done to a sales order by embedding fulfillment modules onto our salesorder request. This allows us to easily examine the state of an order, and perform necessary actions on it.


Our first three steps: Pick, Pack, and Ship, are most commonly driven by calls to {module}linebyorder with the salesorder id in the body. For example, to create a pick from an issued salesorder:

    Body: {"order_id": [1]}

Note: Order Id's are sent as an array, allowing operations to be created with multiple orders.


LOCATE has several methods of creating picks (From customer, destination, part, and order). Most commonly used is picking by order. We can create a pick for an order with the following call:

    Body: {"order_id": [1]}

Once we have our pick id, we can grab our related picklines by embedding lines on our pick call. In addition to the pickline_id, we will also need to know the location_id of the parts we are committing from. We can do that by embedding "qty_available_for_pick" on each of our parts:

This will return data about each pickline, and we can use the pickline id to commit each line.
We can see that there are 200 Beanies available to be picked at location 13. And we can use the location_id and quantity to commit our pickline inventory.

Note: When committing tracked inventory, LOCATE requires that tracking numbers be supplied when committing.
    Body: {"qty":"1", "subtract_location_id": 13}

After committing our line, we can finish our pick.


Note: Once a pick is finished, your inventory is transferred from your moving location to the default packing location.


After completing our pick, we can perform a similar process to pack our order.

    Body: {"order_id": ["1"]}

Next we will create a carton for our packlines. You can reference "/cartontype" for a comprehensive list of carton types. In this case we picked a medium flat rate box with a carton_id of 17.

    Body: {"cartontype_id": 17}

Next we use the newly created carton_id to move our packlines to our carton. In this example, add_carton_id is our newly created carton's id, and subtract_location_id is the location the items were picked to in the previous step.

    Body: {"add_carton_id": 48, "order_id": 1, "move_all": 1, "part_id": 2, "qty": 1, "subtract_location_id": 4}

Finally, we finish the packing step.


Note: LOCATE setting "start_in_carton" determines where your packing process starts. Typically, "start_in_carton" is disabled, and cartons need to be created for items to be moved into. When enabled all of your items are packed into a single carton upon pack creation. Items can then be moved to new cartons (if necessary) or the pack can be directly finished.

When the "start_in_carton" setting is disabled LOCATE provides the option of autopacking your order. Autopacking will use your item dimensions to pack your order into the most efficient carton size. To autopack, simply call:



After completing our pack, we can perform a similar process to ship our order.

    Body: {"order_id": ["1"]}

Simply post to your shipment with finish to complete the shipping step.



Once an order is packed into cartons and finishes the ship step, we can no longer run our workflow with just the order id. A carton can contain items from several salesorders, so instead, we allow pickup by carton or carrier, individually or in batches.

To find the pickuplines necessary to create a pickup, we can simply embed them on our salesorder.


Once the ship has been completed the pickup lines will automatically be generated. To get our pickuplines we can embed them onto our Saleorder


Once we have our pickupline_ids, marking lines as being picked up is simple.

    Body: {"pickupline_ids": [29], "site_id: 3"}


Autoworking allows you to generate and fulfill a set of tasks for all items on an order. LOCATE's autoworking process is non-blocking, and autoworked orders should be handled in a queue to check for order completion. Orders can be completely or partially autoworked.

To full autowork an order add autowork to your order call with the parameter full_auto. LOCATE will attempt to complete all possible work for an order without requiring you to provide specific tasks or steps.

    Body: {"full_auto": 1}

Partially autoworking an order gives you control over which tasks you want autoworked, but requires you to supply the task ids you want autoworked.

To view a list of all possible autowork tasks call:


LOCATE primarily uses autowork to pick, pack, and ship issued salesorders.

When a user autoworks an issued order in LOCATE, the following call is made:

    Body: {"task_ids": [2,3,4]}
This takes our order through pick, pack, and ship, leaving it ready for pickup. LOCATE's user run autowork stops short of the pickup step, since at that point work becomes difficult to reverse, and we let the user finish the pickup step.

To autowork with pickup simply include pickup in the list of "task_ids" (e.g. [2,3,4,6]).

Alternatively, You can Autowork single tasks, or different combinations of tasks if you want to do any fulfillment steps manually.

Tracking and Carton Details

At the start of packing, cartons are in an open status. Once packing finishes, cartons enter a closed status. Cartons stay in a closed status throughout the ship step. Finally, once pickup finishes, the cartons become locked.

Tracking and carton details can be posted back to the cartons anytime, up until the cartons become locked by pickup. Even if a pack has already been completed, carton, carrier and tracking information can still be posted back to it:

    Body: {"tracking_number": 123456, "carrier_id":4}

Edge Cases

Not all companies have a workflow that involves Pick, Pack, Ship, and Pickup. Pack or Ship steps can be skipped depending on your workflow, or carrier.

As an example of this, consider Will Call orders. Orders with a carrier of Will Call don't have a pack or ship step, and move directly to pickup after picking. Optionally, in the carrier settings module, Will Call can be set to include a pack step.

Consider also that several LOCATE settings can change parts of this workflow. For example the setting: "Auto-Create Pick By Order", automatically creates a pick whenever a salesoder is issued. Processing orders with "Auto-Create Pick By Order" enabled, allows you to start your process with a created pick.


Webhook Basics

To jumpstart your development process we recommend using a webhook tool like request bin to quickly verify data returned by the webhook.

Webhooks will Post to the given url with the model of the object that triggered the event. For example, setting a webhook to trigger whenever a customer is modified will result in the customer object being sent.

If the webhook request does not receive a 2xx response LOCATE will attempt to fire the webhook again at 5 minute intervals for a total of 3 times or until it gets a 2xx response.

Configure Webhooks in the App

  1. Log in to LOCATE
  2. Select Settings
  3. Select Webhooks at the bottom
  4. Enter the event that will trigger the webhook to fire, and the url to post to.

Configure Webhooks using the Api

Webhooks can be easily be programatically managed with standard CRUD calls to "/webhook" as listed in our Swagger Docs. Make a GET request to "/event" to see a list of events with associated event_ids.


Once we have an event_id we can create our webhook:

curl \   
-H "Content-type: application/json" \
-d '{
    "url": "",
    "event_id": "1"

Code Samples

PHP Code Sample

* Constants
define('LOCATE_BASE_URL', '');
define('LOCATE_USERNAME', '{{}}');
define('LOCATE_PASSWORD', '{{data.loginRequest.password}}');

function locateRequest($curlRequestType, $endpoint, $sessionToken = null, $postData = null) {

    // Create CURL Request
    $curlRequest = curl_init();

    // Set CURL Options
    curl_setopt($curlRequest, CURLOPT_CUSTOMREQUEST, $curlRequestType);
    curl_setopt($curlRequest, CURLOPT_URL, LOCATE_BASE_URL . $endpoint);
    curl_setopt($curlRequest, CURLOPT_HTTPHEADER, array('Content-Type: application/json', 'Accept: application/json'));
    curl_setopt($curlRequest, CURLOPT_RETURNTRANSFER, true);

    // Check for POST Data
    if($postData !== null) {
        curl_setopt($curlRequest, CURLOPT_POSTFIELDS, json_encode($postData));

    // BASIC Auth
    if($sessionToken !== null) {
        curl_setopt($curlRequest, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
        curl_setopt($curlRequest, CURLOPT_USERPWD, $sessionToken);
    // Execute CURL Request
    $response = curl_exec($curlRequest);
    $httpCode = curl_getinfo($curlRequest, CURLINFO_HTTP_CODE);

    // Check HTTP Status Code
    if($httpCode == 200 || $httpCode == 201) {
        return (json_decode($response));
    else {
        throw new Exception($httpCode . ' - ' . $response);

// Login
$loginRequest = array(
    'email' => LOCATE_USERNAME,
    'password' => LOCATE_PASSWORD
$loginResponse = locateRequest('POST', '/login', null, $loginRequest);
$sessionToken = $loginResponse->session_token;

// Get One Customer
$customerResponse = locateRequest('GET', '/customer?perPage=1', $sessionToken);