This document outlines the procedure for making API calls to retrieve product stock transfer data from https://easycms.fi/public_api/get_products_stocktransfer/.
The pid (Product ID) parameter is MANDATORY for this endpoint. You must provide a valid product ID to retrieve stock transfer data.
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.
https://easycms.fi/public_api/get_products_stocktransfer/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. pid: Product ID (MANDATORY) - The ID of the product to retrieve stock transfer data for. Must be an integer.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 = Draft100 = Sent110 = Preparing120 = Shipped200 = Received-1 = Cancelledstart - 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).
# 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}");
}
}
}
}
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"
}
]
}
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 |
{
"status": "error",
"error_code": "MISSING_PID",
"message": "The pid parameter is required"
}
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 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}`);
Use the status filter to see only shipped transfers:
POST /get_products_stocktransfer?pid=12345&status=120
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
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`);