This document provides guidance on using the set_stock API endpoint for comprehensive stock management operations. Unlike Set Products Stock which handles simple add/deduct operations, this endpoint supports transfers, absolute value adjustments, BBD changes, location moves, and more — all with full stockdiary audit trail.
action parameter determines the operationaction=adjust) or Set Products Stocktransfer action handles source deduction and destination addition in a single call| Action | Description | Diary Entries |
|---|---|---|
adjust |
Add/deduct stock by quantity with reason code. Use stock=0 to reset to zero (reason auto-detected). |
1 entry |
set |
Set stock to an absolute value | 1 entry (or 0 if no change) |
transfer |
Move stock between locations/shelves atomically | 2 entries (OUT + IN) |
modify_bbd |
Change best-before date on existing stock | 2 entries (OUT old + IN new) |
move |
Change location/shelf while keeping quantity | 2 entries (OUT + IN) |
set_default_location |
Set default location flag for a product | 0 entries |
delete |
Remove a stock entry entirely | 1 entry (OUT) |
https://easycms.fi/public_api/set_stock/| Parameter | Type | Description |
|---|---|---|
TOKEN (api_key) |
string | Your unique API key in Authorization1 header. How to Create API Credentials |
username |
string | Your login username |
password |
string | Your login password |
account |
integer | Your specific account ID |
adjust — Add/Deduct Stock by QuantitySame functionality as Set Products Stock but through this unified endpoint.
| Parameter | Type | Description |
|---|---|---|
action |
string | Must be "adjust" |
pid |
integer | Product ID |
location_id |
integer | Location ID |
shelf_id |
integer | Shelf ID |
stock |
decimal | Quantity to add (positive) or deduct (negative). Use 0 to reset stock to zero (reason auto-detected). |
reason |
integer | Reason code (see Reason Codes table below) |
| Parameter | Type | Description |
|---|---|---|
best_before_date |
date | YYYY-MM-DD format |
price |
decimal | Sell price at time of movement |
price_buy |
decimal | Buy price at time of movement |
supplier_id |
integer | Supplier ID |
reference |
string | Reference text (max 255 chars) |
| Code | Constant | Description |
|---|---|---|
1 |
IN_PURCHASE | Stock received from purchase order |
2 |
IN_REFUND | Stock returned from customer refund |
3 |
IN_MOVEMENT | Stock moved in from another location |
4 |
IN_SALE_CORRECTION | Sale cancelled, stock returned |
9 |
IN_ADJUSTMENT | Stock adjustment (addition / correction) |
| Code | Constant | Description |
|---|---|---|
-1 |
OUT_SALE | Stock sold to customer |
-2 |
OUT_REFUND | Stock refunded to supplier |
-3 |
OUT_BREAK | Stock broken/damaged |
-4 |
OUT_MOVEMENT | Stock moved to another location |
-5 |
OUT_SAMPLE | Stock used as sample |
-6 |
OUT_FREE | Stock given for free |
-7 |
OUT_USED | Stock used internally |
-8 |
OUT_SUBTRACT | Stock adjustment (deduction) |
-9 |
OUT_ADJUSTMENT | Stock adjustment (removal / correction) |
When stock=0 is passed to the adjust action:
| Scenario | Behavior |
|---|---|
No reason provided, current stock > 0 |
Auto-sets reason to -9 (OUT_ADJUSTMENT) — resets to 0 |
No reason provided, current stock < 0 |
Auto-sets reason to 9 (IN_ADJUSTMENT) — resets to 0 |
No reason provided, current stock = 0 |
Returns success with "No change — stock is already at 0" |
reason explicitly provided |
Uses the provided reason code (respects user intent) |
set — Set Stock to Absolute ValueSets the stock quantity at a location/shelf to an exact value. The system calculates the difference and creates an adjustment diary entry (reason=9 for increase, reason=-9 for decrease).
| Parameter | Type | Required | Description |
|---|---|---|---|
action |
string | YES | Must be "set" |
pid |
integer | YES | Product ID |
location_id |
integer | YES | Location ID |
shelf_id |
integer | YES | Shelf ID |
stock |
decimal | YES | The new absolute stock value (must be >= 0) |
best_before_date |
date | NO | YYYY-MM-DD format |
reference |
string | NO | Reference text |
transfer — Move Stock Between Locations/ShelvesAtomically transfers a specified quantity of stock from one location/shelf to another. This is a single API call that handles both the source deduction and destination addition, including merge detection when the destination already has stock for the product.
| Parameter | Type | Required | Description |
|---|---|---|---|
action |
string | YES | Must be "transfer" |
pid |
integer | YES | Product ID |
location_id |
integer | YES | SOURCE Location ID |
shelf_id |
integer | YES | SOURCE Shelf ID |
dest_location_id |
integer | YES | DESTINATION Location ID |
dest_shelf_id |
integer | YES | DESTINATION Shelf ID |
stock |
decimal | YES | Quantity to transfer (must be > 0) |
best_before_date |
date | NO | New BBD for destination (YYYY-MM-DD) |
reference |
string | NO | Reference text |
Logic:
modify_bbd — Change Best-Before DateChanges the best-before date on an existing stock entry. Creates two diary entries following the admin pattern: OUT with old BBD + IN with new BBD.
| Parameter | Type | Required | Description |
|---|---|---|---|
action |
string | YES | Must be "modify_bbd" |
pid |
integer | YES | Product ID |
location_id |
integer | YES | Location ID |
shelf_id |
integer | YES | Shelf ID |
best_before_date |
date | YES | New BBD (YYYY-MM-DD) |
reference |
string | NO | Reference text |
move — Change Location/Shelf Keeping QuantityMoves stock to a different location and/or shelf while keeping the quantity intact. If the destination already has stock for this product, the quantities are merged (combined) and the source row is removed.
| Parameter | Type | Required | Description |
|---|---|---|---|
action |
string | YES | Must be "move" |
pid |
integer | YES | Product ID |
location_id |
integer | YES | CURRENT Location ID |
shelf_id |
integer | YES | CURRENT Shelf ID |
new_location_id |
integer | NO* | NEW Location ID (defaults to current if not provided) |
new_shelf_id |
integer | NO* | NEW Shelf ID (required if new_location_id not changing) |
reference |
string | NO | Reference text |
*At least one of new_location_id or new_shelf_id must be different from the current values.
Logic:
set_default_location — Set Default Location FlagSets a specific location/shelf as the default for a product. All other locations for this product are reset to default_location=0.
| Parameter | Type | Required | Description |
|---|---|---|---|
action |
string | YES | Must be "set_default_location" |
pid |
integer | YES | Product ID |
location_id |
integer | YES | Location ID to set as default |
shelf_id |
integer | YES | Shelf ID to set as default |
delete — Remove Stock Entry EntirelyDeletes a stock entry from a location/shelf. Creates an OUT_MOVEMENT diary entry with the full stock quantity before removing the row.
| Parameter | Type | Required | Description |
|---|---|---|---|
action |
string | YES | Must be "delete" |
pid |
integer | YES | Product ID |
location_id |
integer | YES | Location ID |
shelf_id |
integer | YES | Shelf ID |
reference |
string | NO | Reference text |
The following examples demonstrate the transfer action. Change the action and parameters for other operations.
# Transfer 50 units from location 1/shelf 5 to location 2/shelf 3
curl -X POST 'https://easycms.fi/public_api/set_stock' \
-H 'Authorization1: TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"username": "USERNAME",
"password": "PASSWORD",
"account": "ACCOUNT_ID",
"action": "transfer",
"pid": 12345,
"location_id": 1,
"shelf_id": 5,
"dest_location_id": 2,
"dest_shelf_id": 3,
"stock": 50,
"reference": "TRANSFER-2024-001"
}'
# Set stock to absolute value of 200
curl -X POST 'https://easycms.fi/public_api/set_stock' \
-H 'Authorization1: TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"username": "USERNAME",
"password": "PASSWORD",
"account": "ACCOUNT_ID",
"action": "set",
"pid": 12345,
"location_id": 1,
"shelf_id": 5,
"stock": 200,
"reference": "STOCKTAKE-2024"
}'
# Add stock via adjust action
curl -X POST 'https://easycms.fi/public_api/set_stock' \
-H 'Authorization1: TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"username": "USERNAME",
"password": "PASSWORD",
"account": "ACCOUNT_ID",
"action": "adjust",
"pid": 12345,
"location_id": 1,
"shelf_id": 5,
"stock": 100,
"reason": 1,
"best_before_date": "2025-12-31",
"price_buy": 15.50,
"reference": "PO-2024-001"
}'
# Modify best-before date
curl -X POST 'https://easycms.fi/public_api/set_stock' \
-H 'Authorization1: TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"username": "USERNAME",
"password": "PASSWORD",
"account": "ACCOUNT_ID",
"action": "modify_bbd",
"pid": 12345,
"location_id": 1,
"shelf_id": 5,
"best_before_date": "2026-06-30",
"reference": "BBD-UPDATE"
}'
# Move stock to a different shelf (keep quantity)
curl -X POST 'https://easycms.fi/public_api/set_stock' \
-H 'Authorization1: TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"username": "USERNAME",
"password": "PASSWORD",
"account": "ACCOUNT_ID",
"action": "move",
"pid": 12345,
"location_id": 1,
"shelf_id": 5,
"new_shelf_id": 10,
"reference": "SHELF-REARRANGE"
}'
# Set default location
curl -X POST 'https://easycms.fi/public_api/set_stock' \
-H 'Authorization1: TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"username": "USERNAME",
"password": "PASSWORD",
"account": "ACCOUNT_ID",
"action": "set_default_location",
"pid": 12345,
"location_id": 1,
"shelf_id": 5
}'
# Delete a stock entry
curl -X POST 'https://easycms.fi/public_api/set_stock' \
-H 'Authorization1: TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"username": "USERNAME",
"password": "PASSWORD",
"account": "ACCOUNT_ID",
"action": "delete",
"pid": 12345,
"location_id": 1,
"shelf_id": 5,
"reference": "REMOVE-EMPTY"
}'
# Reset stock to zero (reason auto-detected from current stock direction)
curl -X POST 'https://easycms.fi/public_api/set_stock' \
-H 'Authorization1: TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"username": "USERNAME",
"password": "PASSWORD",
"account": "ACCOUNT_ID",
"action": "adjust",
"pid": 12345,
"location_id": 1,
"shelf_id": 5,
"stock": 0,
"reference": "STOCKTAKE-RESET"
}'
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => "https://easycms.fi/public_api/set_stock",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode([
'username' => 'USERNAME',
'password' => 'PASSWORD',
'account' => 'ACCOUNT_ID',
'action' => 'transfer',
'pid' => 12345,
'location_id' => 1,
'shelf_id' => 5,
'dest_location_id' => 2,
'dest_shelf_id' => 3,
'stock' => 50,
'reference' => 'TRANSFER-2024-001'
]),
CURLOPT_HTTPHEADER => array(
"Authorization1: TOKEN",
"Content-Type: application/json"
),
));
$response = curl_exec($curl);
curl_close($curl);
echo $response;
import requests
import json
url = "https://easycms.fi/public_api/set_stock"
headers = {
"Authorization1": "TOKEN",
"Content-Type": "application/json"
}
payload = {
'username': 'USERNAME',
'password': 'PASSWORD',
'account': 'ACCOUNT_ID',
'action': 'transfer',
'pid': 12345,
'location_id': 1,
'shelf_id': 5,
'dest_location_id': 2,
'dest_shelf_id': 3,
'stock': 50,
'reference': 'TRANSFER-2024-001'
}
response = requests.post(url, headers=headers, data=json.dumps(payload))
print(response.text)
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
HttpClient client = HttpClient.newHttpClient();
String jsonPayload = """
{
"username": "USERNAME",
"password": "PASSWORD",
"account": "ACCOUNT_ID",
"action": "transfer",
"pid": 12345,
"location_id": 1,
"shelf_id": 5,
"dest_location_id": 2,
"dest_shelf_id": 3,
"stock": 50,
"reference": "TRANSFER-2024-001"
}
""";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://easycms.fi/public_api/set_stock"))
.headers("Authorization1", "TOKEN", "Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(jsonPayload))
.build();
HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
const https = require('https');
const payload = JSON.stringify({
username: 'USERNAME',
password: 'PASSWORD',
account: 'ACCOUNT_ID',
action: 'transfer',
pid: 12345,
location_id: 1,
shelf_id: 5,
dest_location_id: 2,
dest_shelf_id: 3,
stock: 50,
reference: 'TRANSFER-2024-001'
});
const options = {
hostname: 'easycms.fi',
path: '/public_api/set_stock',
method: 'POST',
headers: {
'Authorization1': 'TOKEN',
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(payload)
}
};
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(payload);
req.end();
import okhttp3.OkHttpClient
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
fun main() {
val client = OkHttpClient()
val jsonPayload = """
{
"username": "USERNAME",
"password": "PASSWORD",
"account": "ACCOUNT_ID",
"action": "transfer",
"pid": 12345,
"location_id": 1,
"shelf_id": 5,
"dest_location_id": 2,
"dest_shelf_id": 3,
"stock": 50,
"reference": "TRANSFER-2024-001"
}
""".trimIndent()
val requestBody = jsonPayload.toRequestBody("application/json".toMediaType())
val request = Request.Builder()
.url("https://easycms.fi/public_api/set_stock")
.post(requestBody)
.addHeader("Authorization1", "TOKEN")
.build()
client.newCall(request).execute().use { response ->
if (!response.isSuccessful) throw IOException("Unexpected code $response")
println(response.body?.string())
}
}
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:
{
"INFO": {
"start": 0,
"limit": 50,
"count": 0,
"total_count": 0,
"docs": "https://cms.helpostikotisivut.fi/DOCUMENTATION/bt5/set_stock.php"
},
"OUTPUT": {
"response_type": "success",
"action": "transfer",
"status": "success",
"message": "Stock transferred successfully",
"data": {
"pid": 12345,
"source": {
"location_id": 1,
"shelf_id": 5,
"previous_stock": 100,
"new_stock": 50
},
"destination": {
"location_id": 2,
"shelf_id": 3,
"previous_stock": 20,
"new_stock": 70,
"is_new_entry": false
},
"transferred_qty": 50,
"best_before_date": null,
"stockdiary": {
"out_id": "uuid-out-entry",
"in_id": "uuid-in-entry"
}
}
}
}
{
"INFO": { ... },
"OUTPUT": {
"response_type": "success",
"action": "set",
"status": "success",
"message": "Stock set to absolute value",
"data": {
"pid": 12345,
"location_id": 1,
"shelf_id": 5,
"previous_stock": 85,
"new_stock": 200,
"stock_change": 115,
"reason": 9,
"reason_text": "ADJUSTMENT_IN",
"stockdiary_id": "uuid-diary-entry",
"best_before_date": null,
"is_new_entry": false
}
}
}
{
"INFO": { ... },
"OUTPUT": {
"response_type": "success",
"action": "adjust",
"status": "success",
"message": "Stock updated successfully",
"data": {
"pid": 12345,
"location_id": 1,
"shelf_id": 5,
"previous_stock": 100,
"stock_change": 50,
"new_stock": 150,
"reason": 1,
"reason_text": "IN_PURCHASE",
"stockdiary_id": "uuid-diary-entry",
"best_before_date": "2025-12-31",
"is_new_entry": false
}
}
}
{
"INFO": { ... },
"OUTPUT": {
"response_type": "success",
"action": "delete",
"status": "success",
"message": "Stock entry deleted successfully",
"data": {
"pid": 12345,
"location_id": 1,
"shelf_id": 5,
"deleted_stock": 75,
"best_before_date": "2025-06-30",
"stockdiary_id": "uuid-diary-entry"
}
}
}
{
"INFO": { ... },
"OUTPUT": {
"response_type": "error",
"action": "transfer",
"message": "Insufficient stock at source. Current: 30, Requested: 50",
"error_code": "INSUFFICIENT_STOCK",
"data": {
"pid": 12345,
"source": {
"location_id": 1,
"shelf_id": 5,
"current_stock": 30,
"requested_transfer": 50
}
}
}
}
| Error Code | Description |
|---|---|
MISSING_ACTION |
The action parameter is required |
INVALID_ACTION |
Unknown action. Valid: adjust, set, transfer, modify_bbd, move, set_default_location, delete |
MISSING_PID |
pid parameter is required |
INVALID_PID |
pid must be a positive integer |
PRODUCT_NOT_FOUND |
Product with specified pid does not exist |
MISSING_LOCATION_ID |
location_id is required for this action |
INVALID_LOCATION_ID |
location_id must be a positive integer |
INVALID_LOCATION |
Location does not exist |
MISSING_SHELF_ID |
shelf_id is required for this action |
INVALID_SHELF_ID |
shelf_id must be a non-negative integer |
INVALID_SHELF |
Shelf does not exist or doesn't belong to this location |
MISSING_STOCK |
stock parameter is required for this action |
STOCK_MUST_BE_POSITIVE |
Transfer qty must be positive |
MISSING_REASON |
reason parameter is required for adjust action |
INVALID_REASON |
Invalid reason code |
MISSING_DEST_LOCATION |
dest_location_id is required for transfer action |
MISSING_DEST_SHELF |
dest_shelf_id is required for transfer action |
INVALID_DEST_LOCATION |
Destination location does not exist |
INVALID_DEST_SHELF |
Destination shelf does not exist |
SOURCE_EQUALS_DESTINATION |
Source and destination location/shelf cannot be the same |
INSUFFICIENT_STOCK |
Not enough stock at source for transfer/deduction |
STOCK_ENTRY_NOT_FOUND |
No stock entry exists at specified location/shelf |
MISSING_BBD |
best_before_date is required for modify_bbd action |
INVALID_BBD_FORMAT |
best_before_date must be in YYYY-MM-DD format |
MISSING_NEW_LOCATION |
At least one of new_location_id or new_shelf_id must differ from current |
INVALID_NEW_LOCATION |
New location does not exist |
INVALID_NEW_SHELF |
New shelf does not exist or doesn't belong to new location |
INTERNAL_ERROR |
An unexpected error occurred |
stock >= transfer_qtytbl_stockcurrent rowtbl_stockcurrent.stock to new absolute valuetbl_stockcurrent.best_before_date to new valuetransfer instead of two separate calls — the atomic transfer action ensures data integrityset for stocktaking — set stock to exact counted values rather than calculating differences manuallyreference — include transfer IDs, adjustment notes, or reasons for traceabilityadjust for transactional movements — sales, purchases, returns with appropriate reason codesget_products_stock or get_stockcurrent to verify available stockset_products_stock| Feature | set_products_stock |
set_stock |
|---|---|---|
| Add/deduct by reason | ✅ | ✅ (action=adjust) |
| Set absolute value | ❌ | ✅ (action=set) |
| Transfer (atomic) | ❌ (needs 2 calls) | ✅ (action=transfer) |
| Modify BBD | ❌ | ✅ (action=modify_bbd) |
| Move location/shelf | ❌ | ✅ (action=move) |
| Set default location | ❌ | ✅ (action=set_default_location) |
| Delete stock entry | ❌ | ✅ (action=delete) |
| Stock diary audit | ✅ | ✅ (all actions) |
| New entry auto-create | ✅ | ✅ (adjust only) |
Note: Set Products Stock remains unchanged and fully functional. The set_stock endpoint is a superset that provides all advanced stock management operations.