Inventory & Products | Products | Stock | Get Stock Transfer

Getting Product Stock Transfer Data

This document outlines the procedure for making API calls to retrieve product stock transfer data from https://easycms.fi/public_api/get_products_stocktransfer/.

Important Note

The pid (Product ID) parameter is MANDATORY for this endpoint. You must provide a valid product ID to retrieve stock transfer data.

Product Stock Transfer Data Retrieval

Fetch stock transfer entries for a product — these are records in the stock movement table (tbl_stockcoming) where a transfer_id is set, representing stock that has been or is being transferred between locations. Use this endpoint to track product-level stock movement across warehouses, retail stores, or any inventory locations.

Endpoint and Method

  • Endpoint: https://easycms.fi/public_api/get_products_stocktransfer/
  • Method: GET or POST

Parameters | Payload

  • TOKEN (api_key): Your unique API key for authentication. How to Create API Credentials.
  • username: Your login username.
  • password: Your login password.
  • account: Your specific account ID.

Required Parameter

  • pid: Product ID (MANDATORY) - The ID of the product to retrieve stock transfer data for. Must be an integer.

Optional Filter Parameters

  • location_id: Filter results by a specific location ID. When provided, returns transfer entries where the location is either the source or destination.
  • status: Filter by transfer status. Must be a valid integer:
    • 1 = Draft
    • 100 = Sent
    • 110 = Preparing
    • 120 = Shipped
    • 200 = Received
    • -1 = Cancelled

Pagination Parameters

  • start - Specify the starting point of the row from which to begin fetching stock transfer data (default: 0).
  • limit - Control the number of records returned in a single request (default: 50, max: 50).



Call Examples in Different Languages


# Get all stock transfer entries for a product
curl -X POST 'https://easycms.fi/public_api/get_products_stocktransfer' \
-H 'Authorization1: TOKEN' \
-d 'username=USERNAME&password=PASSWORD&account=ACCOUNT_ID&pid=12345'

# Get stock transfer entries filtered by location
curl -X POST 'https://easycms.fi/public_api/get_products_stocktransfer' \
-H 'Authorization1: TOKEN' \
-d 'username=USERNAME&password=PASSWORD&account=ACCOUNT_ID&pid=12345&location_id=1'

# Get stock transfer entries filtered by status (shipped only)
curl -X POST 'https://easycms.fi/public_api/get_products_stocktransfer' \
-H 'Authorization1: TOKEN' \
-d 'username=USERNAME&password=PASSWORD&account=ACCOUNT_ID&pid=12345&status=120'

$curl = curl_init();
curl_setopt_array($curl, array(
  CURLOPT_URL => "https://easycms.fi/public_api/get_products_stocktransfer",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_POST => true,
  CURLOPT_POSTFIELDS => http_build_query([
    'username' => 'USERNAME', 
    'password' => 'PASSWORD', 
    'account' => 'ACCOUNT_ID',
    'pid' => 12345,
    'location_id' => 1,  // optional
    'status' => 120      // optional, e.g. 120 = shipped
  ]),
  CURLOPT_HTTPHEADER => array("Authorization1: TOKEN"),
));
$response = curl_exec($curl);
curl_close($curl);
echo $response;

import requests
url = "https://easycms.fi/public_api/get_products_stocktransfer"
headers = {"Authorization1": "TOKEN"}
payload = {
    'username': 'USERNAME', 
    'password': 'PASSWORD', 
    'account': 'ACCOUNT_ID',
    'pid': 12345,
    'location_id': 1,  # optional
    'status': 120      # optional, e.g. 120 = shipped
}
response = requests.post(url, headers=headers, data=payload)
print(response.text)

HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://easycms.fi/public_api/get_products_stocktransfer"))
    .headers("Authorization1", "TOKEN")
    .POST(HttpRequest.BodyPublishers.ofString(
        "username=USERNAME&password=PASSWORD&account=ACCOUNT_ID&pid=12345&location_id=1&status=120"))
    .build();
HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());

const https = require('https');
const data = new URLSearchParams({ 
  username: 'USERNAME', 
  password: 'PASSWORD', 
  account: 'ACCOUNT_ID',
  pid: 12345,
  location_id: 1,  // optional
  status: 120      // optional
}).toString();
const options = {
  hostname: 'easycms.fi',
  path: '/public_api/get_products_stocktransfer',
  method: 'POST',
  headers: {
    'Authorization1': 'TOKEN',
    'Content-Type': 'application/x-www-form-urlencoded',
    'Content-Length': data.length
  }
};
const req = https.request(options, (res) => {
  let data = '';
  res.on('data', (chunk) => { data += chunk; });
  res.on('end', () => { console.log(data); });
});
req.on('error', (e) => { console.error(e); });
req.write(data);
req.end();

import React, { useEffect, useState } from 'react';
function App() {
  const [transferData, setTransferData] = useState('');
  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch('https://easycms.fi/public_api/get_products_stocktransfer', {
          method: 'POST',
          headers: {'Authorization1': 'TOKEN', 'Content-Type': 'application/x-www-form-urlencoded'},
          body: new URLSearchParams({
            username: 'USERNAME', 
            password: 'PASSWORD', 
            account: 'ACCOUNT_ID',
            pid: 12345
          }).toString()
        });
        const data = await response.text();
        setTransferData(data);
      } catch (error) {
        console.error(error);
      }
    };
    fetchData();
  }, []);
  return (
{transferData}
); } export default App;

import okhttp3.OkHttpClient
import okhttp3.FormBody
import okhttp3.Request

fun main() {
    val client = OkHttpClient()

    val formBody = FormBody.Builder()
        .add("username", "USERNAME")
        .add("password", "PASSWORD")
        .add("account", "ACCOUNT_ID")
        .add("pid", "12345")
        .add("location_id", "1")  // optional
        .add("status", "120")     // optional
        .build()

    val request = Request.Builder()
        .url("https://easycms.fi/public_api/get_products_stocktransfer")
        .post(formBody)
        .addHeader("Authorization1", "TOKEN")
        .build()

    client.newCall(request).execute().use { response ->
        if (!response.isSuccessful) throw IOException("Unexpected code $response")
        println(response.body?.string())
    }
}

using System;
using System.Net.Http;
using System.Threading.Tasks;
class Program
{
    static async Task Main()
    {
        var token = "TOKEN";
        var content = new FormUrlEncodedContent(new[]
        {
            new KeyValuePair("username", "USERNAME"),
            new KeyValuePair("password", "PASSWORD"),
            new KeyValuePair("account", "ACCOUNT_ID"),
            new KeyValuePair("pid", "12345"),
            new KeyValuePair("location_id", "1"),  // optional
            new KeyValuePair("status", "120")      // optional
        });
        using (var httpClient = new HttpClient())
        {
            httpClient.DefaultRequestHeaders.Add("Authorization1", token);
            var response = await httpClient.PostAsync("https://easycms.fi/public_api/get_products_stocktransfer", content);
            if (response.IsSuccessStatusCode)
            {
                var responseData = await response.Content.ReadAsStringAsync();
                Console.WriteLine(responseData);
            }
            else
            {
                Console.WriteLine($"Error: {response.StatusCode}");
            }
        }
    }
}




Handling Endpoint Results

When you make a request to the endpoint, you receive a JSON response containing various keys and values. Here's an explanation of the response keys and their meanings:


- `start`: This represents the starting index for the data. In this case, it's set to 0, indicating that the data starts from the first item.

- `limit`: The maximum number of items returned in the response. In this example, the limit is set to 50, meaning that the response will include up to 50 items.

- `count`: The number of items included in the current response.

- `total_count`: The total count of stock transfer entries available for this product.

- `pid`: The product ID that was queried.

- `total_in_transfer`: The total quantity of this product currently in transfer (sum of all pending/in-transit quantities).

    

- `transfer_id`: The unique identifier for the stock transfer order this entry belongs to.
- `stock`: The quantity of the product being transferred.
- `from_location_id`: The source location ID where the stock is being transferred from.
- `from_location_name`: The name of the source location.
- `to_location_id`: The destination location ID where the stock is being transferred to.
- `to_location_name`: The name of the destination location.
- `status`: The transfer status code:
  - 1 = Draft
  - 100 = Sent
  - 110 = Preparing
  - 120 = Shipped
  - 200 = Received
  - -1 = Cancelled
- `status_title`: Human-readable status text.
- `coming_to_stock_date`: The expected date when the stock will arrive at the destination (YYYY-MM-DD format, null if not set).
- `best_before_date`: The best before date for the transferred stock (YYYY-MM-DD format, null if not set).
- `reason`: Stock reason code (3 = Movement/transfer).
- `price`: The sell price for the transferred stock.
- `price_buy`: The buy price for the transferred stock (null if not set).
- `datenew`: The date when this transfer entry was created (YYYY-MM-DD HH:MM:SS format).

These key-value pairs provide detailed information about product stock movement between locations and can be used for tracking in-transit inventory in your application.
    

{
  "INFO": {
    "start": 0,
    "limit": 50,
    "count": 2,
    "total_count": 2,
    "pid": 12345,
    "total_in_transfer": 150.0
  },
  "OUTPUT": [
    {
      "transfer_id": 789,
      "stock": 100.0,
      "from_location_id": 1,
      "from_location_name": "Main Warehouse",
      "to_location_id": 4,
      "to_location_name": "Retail Store",
      "status": 120,
      "status_title": "transfer_shipped",
      "coming_to_stock_date": "2026-04-10",
      "best_before_date": "2027-06-30",
      "reason": 3,
      "price": 15.50,
      "price_buy": 9.00,
      "datenew": "2026-03-28 09:15:00"
    },
    {
      "transfer_id": 801,
      "stock": 50.0,
      "from_location_id": 2,
      "from_location_name": "Secondary Warehouse",
      "to_location_id": 4,
      "to_location_name": "Retail Store",
      "status": 100,
      "status_title": "transfer_sent",
      "coming_to_stock_date": "2026-04-15",
      "best_before_date": null,
      "reason": 3,
      "price": 15.50,
      "price_buy": null,
      "datenew": "2026-04-01 11:30:00"
    }
  ]
}
      

Error Handling

Here are the possible error messages and their meanings:

Error Code HTTP Status Description
MISSING_PID 400 The pid parameter is required
INVALID_PID 400 The pid must be a valid integer
PRODUCT_NOT_FOUND 404 The product with the specified pid does not exist
INVALID_LOCATION_ID 400 The location_id must be a valid integer
INVALID_STATUS 400 The status must be a valid transfer status code (1, 100, 110, 120, 200, or -1)
UN-AUTHORIZED 401 Incorrect username or password
this_account_does_not_exist_or_your_credentials_do_not_match_this_account 401 The account doesn't exist or mismatched credentials
Maximum query size is 50 rows per query 400 Exceeded maximum limit of 50 rows per query

Error Response Example

{
  "status": "error",
  "error_code": "MISSING_PID",
  "message": "The pid parameter is required"
}

Empty Stock Transfer Response

When a product exists but has no stock transfer entries:

{
  "INFO": {
    "start": 0,
    "limit": 50,
    "count": 0,
    "total_count": 0,
    "pid": 12345,
    "total_in_transfer": 0
  },
  "OUTPUT": []
}

Use Cases

Get Total In-Transfer Quantity

Use the total_in_transfer field from the INFO object to quickly see how much of the product is currently in transit:

// Example in JavaScript
const response = await getProductsStockTransfer(pid);
console.log(`Total in transfer: ${response.INFO.total_in_transfer}`);

Filter Transfers by Status

Use the status filter to see only shipped transfers:

POST /get_products_stocktransfer?pid=12345&status=120

Filter by Location

Use the location_id filter to see transfers involving a specific location (as source or destination):

POST /get_products_stocktransfer?pid=12345&location_id=4

Combine with Current Stock for Full Inventory Picture

Use this endpoint together with get_products_stock and get_products_incoming_stock to get a complete view of current, incoming, and in-transit stock:

const [currentStock, incomingStock, transferStock] = await Promise.all([
  getProductsStock(pid),
  getProductsIncomingStock(pid),
  getProductsStockTransfer(pid)
]);
console.log(`Current: ${currentStock.OUTPUT.reduce((s,i) => s+i.stock, 0)} units`);
console.log(`Incoming: ${incomingStock.OUTPUT.reduce((s,i) => s+i.stock, 0)} units`);
console.log(`In transfer: ${transferStock.INFO.total_in_transfer} units`);

Related Endpoints

  • get_products_stock - Get current stock levels for a product across all locations and shelves.
  • get_products_incoming_stock - Get all incoming stock data for a product (purchase orders, returns, and transfers).
  • get_stock_transfers - Get formal stock transfer orders with line items, filtering by reference, locations, status, and date range.
  • set_stock_transfer - Create or update stock transfer orders between locations.