This document outlines the procedure for making API calls to retrieve upcoming/incoming stock data for a product from https://easycms.fi/public_api/get_products_incoming_stock/.
The pid (Product ID) parameter is MANDATORY for this endpoint. You must provide a valid product ID to retrieve incoming stock data.
Fetch upcoming stock that is expected to arrive for a product. This data comes from purchase orders, stock transfers, and other stock addition sources that have not yet been received into current stock.
https://easycms.fi/public_api/get_products_incoming_stock/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 incoming stock for. Must be an integer.location_id: Filter results by a specific location ID. When provided, returns incoming stock only for this location.shelf_id: Filter results by a specific shelf ID. Note: Requires location_id to be provided as well.start - Specify the starting point of the row from which to begin fetching incoming stock data (default: 0).limit - Control the number of records returned in a single request (default: 50, max: 50).
# Get all incoming stock for a product
curl -X POST 'https://easycms.fi/public_api/get_products_incoming_stock' \
-H 'Authorization1: TOKEN' \
-d 'username=USERNAME&password=PASSWORD&account=ACCOUNT_ID&pid=12345'
# Get incoming stock for a specific location
curl -X POST 'https://easycms.fi/public_api/get_products_incoming_stock' \
-H 'Authorization1: TOKEN' \
-d 'username=USERNAME&password=PASSWORD&account=ACCOUNT_ID&pid=12345&location_id=1'
# Get incoming stock for a specific location and shelf
curl -X POST 'https://easycms.fi/public_api/get_products_incoming_stock' \
-H 'Authorization1: TOKEN' \
-d 'username=USERNAME&password=PASSWORD&account=ACCOUNT_ID&pid=12345&location_id=1&shelf_id=5'
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => "https://easycms.fi/public_api/get_products_incoming_stock",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query([
'username' => 'USERNAME',
'password' => 'PASSWORD',
'account' => 'ACCOUNT_ID',
'pid' => 12345,
'location_id' => 1, // optional
'shelf_id' => 5 // optional, requires location_id
]),
CURLOPT_HTTPHEADER => array("Authorization1: TOKEN"),
));
$response = curl_exec($curl);
curl_close($curl);
echo $response;
import requests
url = "https://easycms.fi/public_api/get_products_incoming_stock"
headers = {"Authorization1": "TOKEN"}
payload = {
'username': 'USERNAME',
'password': 'PASSWORD',
'account': 'ACCOUNT_ID',
'pid': 12345,
'location_id': 1, # optional
'shelf_id': 5 # optional, requires location_id
}
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_incoming_stock"))
.headers("Authorization1", "TOKEN")
.POST(HttpRequest.BodyPublishers.ofString(
"username=USERNAME&password=PASSWORD&account=ACCOUNT_ID&pid=12345&location_id=1&shelf_id=5"))
.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
shelf_id: 5 // optional
}).toString();
const options = {
hostname: 'easycms.fi',
path: '/public_api/get_products_incoming_stock',
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 [incomingData, setIncomingData] = useState('');
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch('https://easycms.fi/public_api/get_products_incoming_stock', {
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();
setIncomingData(data);
} catch (error) {
console.error(error);
}
};
fetchData();
}, []);
return ({incomingData});
}
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("shelf_id", "5") // optional
.build()
val request = Request.Builder()
.url("https://easycms.fi/public_api/get_products_incoming_stock")
.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("shelf_id", "5") // optional
});
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Add("Authorization1", token);
var response = await httpClient.PostAsync("https://easycms.fi/public_api/get_products_incoming_stock", 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 incoming stock entries available for this product.
- `WHERE`: (Optional) The filter condition that was applied. For example, "location_id = 1" when filtered by location.
- `stock`: The upcoming stock quantity expected to arrive.
- `location_id`: The unique identifier for the location/warehouse where stock will arrive.
- `location_name`: The name of the location/warehouse.
- `shelf_id`: The unique identifier for the shelf within the location.
- `shelf_name`: The name of the shelf.
- `coming_to_stock_date`: The expected date when the stock will arrive (YYYY-MM-DD format, null if not set).
- `best_before_date`: The best before date for the upcoming stock (YYYY-MM-DD format, null if not set).
- `reason`: Stock reason code indicating why this stock is coming:
- `1` = Purchase (import from supplier)
- `2` = Refund (return from customer)
- `3` = Movement (transfer in)
- `4` = Sale correction
- `price`: The sell price for the upcoming stock.
- `price_buy`: The buy price for the upcoming stock (null if not set).
- `supplier_id`: The supplier ID associated with this upcoming stock.
- `purchase_id`: The linked purchase order ID (0 if not linked to a purchase order).
- `datenew`: The date when this upcoming stock record was created (YYYY-MM-DD HH:MM:SS format).
These key-value pairs provide information about pending stock arrivals and can be used for inventory planning and forecasting in your application.
{
"INFO": {
"start": 0,
"limit": 50,
"count": 2,
"total_count": 2,
"WHERE": "pid = 12345"
},
"OUTPUT": [
{
"stock": 100.0,
"location_id": 1,
"location_name": "Main Warehouse",
"shelf_id": 5,
"shelf_name": "Shelf A1",
"coming_to_stock_date": "2026-08-15",
"best_before_date": "2027-08-15",
"reason": 1,
"price": 10.5,
"price_buy": 6.0,
"supplier_id": 3,
"purchase_id": 456,
"datenew": "2026-03-27 10:00:00"
},
{
"stock": 50.0,
"location_id": 2,
"location_name": "Secondary Warehouse",
"shelf_id": 3,
"shelf_name": "Shelf B2",
"coming_to_stock_date": "2026-09-01",
"best_before_date": null,
"reason": 3,
"price": 10.5,
"price_buy": null,
"supplier_id": 1,
"purchase_id": 0,
"datenew": "2026-03-25 14: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_SHELF_ID |
400 | The shelf_id must be a valid integer |
SHELF_REQUIRES_LOCATION |
400 | shelf_id cannot be used without location_id |
LOCATION_NOT_FOUND |
404 | The specified location does not exist |
SHELF_NOT_FOUND |
404 | The specified shelf does not exist |
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 incoming stock:
{
"INFO": {
"start": 0,
"limit": 50,
"count": 0,
"total_count": 0,
"pid": 12345
},
"OUTPUT": []
}
To calculate the total incoming stock for a product across all locations, sum the stock values from all items in the OUTPUT array.
// Example in JavaScript
const response = await getProductsIncomingStock(pid);
const totalIncoming = response.OUTPUT.reduce((sum, item) => sum + item.stock, 0);
console.log(`Total incoming stock: ${totalIncoming}`);
Use the location_id filter to check incoming stock at a specific warehouse:
POST /get_products_incoming_stock?pid=12345&location_id=1
Use both location_id and shelf_id to check incoming stock at a specific shelf:
POST /get_products_incoming_stock?pid=12345&location_id=1&shelf_id=5
Sort the results by coming_to_stock_date to find the earliest expected stock arrival:
const response = await getProductsIncomingStock(pid);
const sorted = response.OUTPUT.sort((a, b) =>
new Date(a.coming_to_stock_date) - new Date(b.coming_to_stock_date)
);
const nextArrival = sorted[0];
console.log(`Next arrival: ${nextArrival.stock} units on ${nextArrival.coming_to_stock_date}`);
Use this endpoint together with get_products_stock to get a complete picture of both current and upcoming stock:
const [currentStock, incomingStock] = await Promise.all([
getProductsStock(pid),
getProductsIncomingStock(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`);