Products
Every operation below is generated from the OpenAPI v2 specification and includes parameters, request fields, response fields, errors, and examples.
Documents, lots and physical stock
| stocks.physical / stocks.aggregate | Arrays of warehouse totals: warehouse_id, warehouse_name, stockTotal, stockReserved, stockAvailable. stocks.available_by_warehouse is an object keyed by warehouse ID; these are not identical shapes. |
|---|---|
| GET /api/v2/products/{id}/stock-lots | Physically placed quantities by lot and dates. An unassigned reservation cannot be subtracted from an arbitrary lot. Check quantity units and warehouse in the response. |
| accepted_lots | Actual receipt lots. Expected, accepted and placed quantities are distinct measures; a difference does not itself indicate an API error. |
| requested_lot / fulfillment_lots | Order requested_lot expresses a requirement; fulfillment_lots records actual picking and picked_quantity the picked amount. Multiple actual lots cannot be reduced to one date. |
| movement_type / operation | Picking can emit a reservation-release and physical-issue pair. Distinguish operation instead of counting two physical issues. Transfers correlate through movement_group_id; transfer_kind specifies the kind. |
| GET /api/v2/closed-boxes | Actual sealed boxes with a unique SSCC: balance=1 and balance_unit=box. A packaging-type barcode from products is not a unique SSCC. Opened, shipped and expected-only boxes are excluded. |
| acceptance_date | For standard/return creation (type_id=1/2), the current handler assigns server now() even when acceptance_date is supplied. Updating an existing acceptance accepts the date. Type 3 uses a separate cross-dock service. created_at/updated_at are technical timestamps, not the document date. |
| Lifecycle | Acceptance cancellation is a separate cancel command with state checks, not deletion. Prefer /lines; deprecated /offers remain for compatibility. See operation-specific constraints. |
Products
List products
| operationId | listProducts |
|---|---|
| Tag | Products |
| Security | bearerAuth |
Returns products visible to the authenticated account. Use documented query parameters for filtering, sorting, and pagination.
For unplaced consumable usage, stockTotal does not include a permanent physical write-off and must not be used as available packaging inventory. Addressed consumable write-offs remain the existing warehouse process.
Unknown read parameters are ignored. per_page is silently capped; use meta.per_page. Date-only boundaries follow the installation's stored calendar dates; ISO timestamps are serialized in UTC. The default general API limit is 600/minute; a user-specific override may apply. Its bucket is shared by authenticated user, not by shop/domain; Retry-After is returned on throttling.
For unplaced consumable usage, stockTotal does not include a permanent physical write-off and must not be used as available packaging inventory. Addressed consumable write-offs remain the existing warehouse process.
Unknown read parameters are ignored. per_page is silently capped; use meta.per_page. Date-only boundaries follow the installation's stored calendar dates; ISO timestamps are serialized in UTC. The default general API limit is 600/minute; a user-specific override may apply. Its bucket is shared by authenticated user, not by shop/domain; Retry-After is returned on throttling.
For unplaced consumable usage, stockTotal does not include a permanent physical write-off and must not be used as available packaging inventory. Addressed consumable write-offs remain the existing warehouse process.
Unknown read parameters are ignored. per_page is silently capped; use meta.per_page. Date-only boundaries follow the installation's stored calendar dates; ISO timestamps are serialized in UTC. The default general API limit is 600/minute; a user-specific override may apply. Its bucket is shared by authenticated user, not by shop/domain; Retry-After is returned on throttling.
For unplaced consumable usage, stockTotal does not include a permanent physical write-off and must not be used as available packaging inventory. Addressed consumable write-offs remain the existing warehouse process.
Unknown read parameters are ignored. per_page is silently capped; use meta.per_page. Date-only boundaries follow the installation's stored calendar dates; ISO timestamps are serialized in UTC. The default general API limit is 600/minute; a user-specific override may apply. Its bucket is shared by authenticated user, not by shop/domain; Retry-After is returned on throttling.
For unplaced consumable usage, stockTotal does not include a permanent physical write-off and must not be used as available packaging inventory. Addressed consumable write-offs remain the existing warehouse process.
Unknown read parameters are ignored. per_page is silently capped; use meta.per_page. Date-only boundaries follow the installation's stored calendar dates; ISO timestamps are serialized in UTC. The default general API limit is 600/minute; a user-specific override may apply. Its bucket is shared by authenticated user, not by shop/domain; Retry-After is returned on throttling.
For unplaced consumable usage, stockTotal does not include a permanent physical write-off and must not be used as available packaging inventory. Addressed consumable write-offs remain the existing warehouse process.
Unknown read parameters are ignored. per_page is silently capped; use meta.per_page. Date-only boundaries follow the installation's stored calendar dates; ISO timestamps are serialized in UTC. The default general API limit is 600/minute; a user-specific override may apply. Its bucket is shared by authenticated user, not by shop/domain; Retry-After is returned on throttling.
HTTP request example (cURL)
Set your server and token. Replace sample IDs with accessible IDs. For requests with a body, save the applicable JSON example below as request.json and replace sample values before sending. Idempotency-Key identifies one logical operation.
curl --globoff --request GET "${NWMS_BASE_URL}/api/v2/products" \
--header "Accept: application/json" \
--header "Authorization: Bearer ${NWMS_TOKEN}"Parameters
| Name | In | Required | Type | Style | Explode | Constraints | Description | Example |
|---|---|---|---|---|---|---|---|---|
page |
query | no | integer:int64 |
form | true | min: 1 | Page number. The same filter may also be sent as filter[page] where supported by the Laravel query adapter. |
1 |
per_page |
query | no | integer:int64 |
form | true | min: 1 max: 250 |
Number of records per page. Default is 50; maximum is 250. The same filter may also be sent as filter[per_page] where supported by the Laravel query adapter. |
50 |
sort |
query | no | string |
form | false | pattern: ^-?[A-Za-z0-9_]+(,-?[A-Za-z0-9_]+)*$ | Comma-separated sort fields. Prefix with - for descending order. Allowed fields: id, name, article, sku, price, created_at, updated_at, inventory_updated_at. Default: -id. The same filter may also be sent as filter[sort] where supported by the Laravel query adapter. |
"-id" |
id |
query | no | integer:int64 |
form | true | min: 1 | Filter by id using exact matching. The same filter may also be sent as filter[id] where supported by the Laravel query adapter. |
1001 |
external_id |
query | no | string |
form | true | Filter by external_id using exact matching. The same filter may also be sent as filter[external_id] where supported by the Laravel query adapter. |
"EXT-10045" |
|
shop_id |
query | no | integer:int64 |
form | true | min: 1 | Filter by shop_id using exact matching. The same filter may also be sent as filter[shop_id] where supported by the Laravel query adapter. |
12 |
status_id |
query | no | ProductStatusId |
form | true | Filter by status_id using exact matching. The same filter may also be sent as filter[status_id] where supported by the Laravel query adapter. |
|
|
type_id |
query | no | integer:int64 |
form | true | min: 1 | Filter by type_id using exact matching. The same filter may also be sent as filter[type_id] where supported by the Laravel query adapter. |
1 |
package_kind |
query | no | string |
form | true | Filter by package_kind using exact matching. The same filter may also be sent as filter[package_kind] where supported by the Laravel query adapter. |
"unit" |
|
parent_product_id |
query | no | integer:int64 |
form | true | min: 1 | Filter by parent_product_id using exact matching. The same filter may also be sent as filter[parent_product_id] where supported by the Laravel query adapter. |
1200 |
base_product_id |
query | no | integer:int64 |
form | true | min: 1 | Filter by base_product_id using exact matching. The same filter may also be sent as filter[base_product_id] where supported by the Laravel query adapter. |
1200 |
datamatrix_required |
query | no | boolean |
form | true | Filter by datamatrix_required using exact matching. The same filter may also be sent as filter[datamatrix_required] where supported by the Laravel query adapter. |
false |
|
expiration_required |
query | no | boolean |
form | true | Filter by expiration_required using exact matching. The same filter may also be sent as filter[expiration_required] where supported by the Laravel query adapter. |
false |
|
name |
query | no | string |
form | true | Filter by name using partial text matching. The same filter may also be sent as filter[name] where supported by the Laravel query adapter. |
"Coffee" |
|
article |
query | no | string |
form | true | Filter by article using partial text matching. The same filter may also be sent as filter[article] where supported by the Laravel query adapter. |
"ART-001245" |
|
sku |
query | no | string |
form | true | Filter by sku using partial text matching. The same filter may also be sent as filter[sku] where supported by the Laravel query adapter. |
"SKU-001245" |
|
comment |
query | no | string |
form | true | Filter by comment using partial text matching. The same filter may also be sent as filter[comment] where supported by the Laravel query adapter. |
"Priority" |
|
created_at_from |
query | no | string:date |
form | true | Lower inclusive date boundary for created_at. The same filter may also be sent as filter[created_at_from] where supported by the Laravel query adapter. |
"2026-07-14" |
|
created_at_to |
query | no | string:date |
form | true | Upper inclusive date boundary for created_at. The same filter may also be sent as filter[created_at_to] where supported by the Laravel query adapter. |
"2026-07-14" |
|
updated_at_from |
query | no | string:date |
form | true | Lower inclusive date boundary for updated_at. The same filter may also be sent as filter[updated_at_from] where supported by the Laravel query adapter. |
"2026-07-14" |
|
updated_at_to |
query | no | string:date |
form | true | Upper inclusive date boundary for updated_at. The same filter may also be sent as filter[updated_at_to] where supported by the Laravel query adapter. |
"2026-07-14" |
|
inventory_updated_at_from |
query | no | string:date |
form | true | Lower inclusive date boundary for inventory_updated_at. The same filter may also be sent as filter[inventory_updated_at_from] where supported by the Laravel query adapter. |
"2026-07-14" |
|
inventory_updated_at_to |
query | no | string:date |
form | true | Upper inclusive date boundary for inventory_updated_at. The same filter may also be sent as filter[inventory_updated_at_to] where supported by the Laravel query adapter. |
"2026-07-14" |
|
price_from |
query | no | number:float |
form | true | Lower inclusive numeric boundary for price. The same filter may also be sent as filter[price_from] where supported by the Laravel query adapter. |
19.99 |
|
price_to |
query | no | number:float |
form | true | Upper inclusive numeric boundary for price. The same filter may also be sent as filter[price_to] where supported by the Laravel query adapter. |
19.99 |
|
estimated_price_from |
query | no | number:float |
form | true | Lower inclusive numeric boundary for estimated_price. The same filter may also be sent as filter[estimated_price_from] where supported by the Laravel query adapter. |
19.99 |
|
estimated_price_to |
query | no | number:float |
form | true | Upper inclusive numeric boundary for estimated_price. The same filter may also be sent as filter[estimated_price_to] where supported by the Laravel query adapter. |
19.99 |
Request body
No request body.
Responses
200 Successful response.
Successful response.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
data |
yes | array<Product> |
|
|||
data[] |
no | Product |
Product catalog item exposed by API v2. Product barcodes identify a product or package type and are not unique SSCC values. Use /api/v2/closed-boxes to read individual sealed boxes and their current box balance. |
|
||
data[].id |
yes | integer:int64 |
readOnly | min: 1 | 1001 |
|
data[].created_at |
no | string:date-time | null |
"2026-07-14T10:30:00Z" |
|||
data[].updated_at |
no | string:date-time | null |
"2026-07-14T10:30:00Z" |
|||
data[].external_id |
no | string | null |
"EXT-10045" |
|||
data[].shop_id |
no | integer:int64 | null |
min: 1 | 12 |
||
data[].domain_id |
no | integer:int64 | null |
min: 1 | 3 |
||
data[].status_id |
no | integer:int64 | null |
1 |
|||
data[].type_id |
no | integer:int64 | null |
1 |
|||
data[].name |
yes | string |
"Coffee 250g" |
|||
data[].article |
no | string | null |
"ART-001245" |
|||
data[].sku |
no | string | null |
"SKU-001245" |
|||
data[].price |
no | number:float | null |
19.99 |
|||
data[].estimated_price |
no | number:float | null |
21.5 |
|||
data[].image_url |
no | string:uri | null |
"https://betawms.ffcdek.ru/offer-images/products/coffee-250g.jpg" |
|||
data[].dimensions |
no | Dimensions |
|
|||
data[].dimensions.x |
no | number:float | null |
min: 0 | 105 |
||
data[].dimensions.y |
no | number:float | null |
min: 0 | 200 |
||
data[].dimensions.z |
no | number:float | null |
min: 0 | 32 |
||
data[].dimensions.unit |
yes | enum |
enum: mm | "mm" |
||
data[].weight |
no | Weight |
|
|||
data[].weight.value |
no | number:float | null |
min: 0 | 250 |
||
data[].weight.unit |
yes | enum |
enum: g | "g" |
||
data[].datamatrix_required |
yes | boolean |
false |
|||
data[].expiration_required |
yes | boolean |
true |
|||
data[].comment |
no | string | null |
"Keep dry" |
|||
data[].description |
no | string | null |
"Ground coffee package" |
|||
data[].inventory_updated_at |
no | string:date-time | null |
"2026-07-14T10:30:00Z" |
|||
data[].barcodes |
no | array<ProductBarcode> |
|
|||
data[].barcodes[] |
no | ProductBarcode |
|
|||
data[].barcodes[].barcode |
yes | string |
minLength: 1 maxLength: 64 |
"4601234567890" |
||
data[].barcodes[].is_main |
yes | boolean |
true |
|||
data[].stocks |
no | ProductStocks |
|
|||
data[].stocks.available_by_warehouse |
no | object |
Map keyed by warehouse ID. Values are quantities. | {"42":18.0,"43":3.0} |
||
data[].stocks.physical |
no | array<ProductStockSummary> |
Physical stock rows for the product itself. | |
||
data[].stocks.physical[] |
no | ProductStockSummary |
Stock totals for one warehouse. Quantities may be negative when reservations exceed physical stock. | |
||
data[].stocks.physical[].warehouse_id |
yes | integer:int64 |
min: 1 | 42 |
||
data[].stocks.physical[].warehouse_name |
yes | string | null |
"Warehouse Sofia" |
|||
data[].stocks.physical[].stockTotal |
yes | number:float |
24.0 |
|||
data[].stocks.physical[].stockReserved |
yes | number:float |
6.0 |
|||
data[].stocks.physical[].stockAvailable |
yes | number:float |
18.0 |
|||
data[].stocks.aggregate |
no | array<ProductStockSummary> |
Aggregated base-unit stock rows for the product and its descendant quants. | |
||
data[].stocks.aggregate[] |
no | ProductStockSummary |
Stock totals for one warehouse. Quantities may be negative when reservations exceed physical stock. | |
||
data[].stocks.aggregate[].warehouse_id |
yes | integer:int64 |
min: 1 | 42 |
||
data[].stocks.aggregate[].warehouse_name |
yes | string | null |
"Warehouse Sofia" |
|||
data[].stocks.aggregate[].stockTotal |
yes | number:float |
24.0 |
|||
data[].stocks.aggregate[].stockReserved |
yes | number:float |
6.0 |
|||
data[].stocks.aggregate[].stockAvailable |
yes | number:float |
18.0 |
|||
data[].package |
no | object | null |
|
|||
data[].package.kind |
no | string | null |
"unit" |
|||
data[].package.parent_product_id |
no | integer:int64 | null |
min: 1 | 1201 |
||
data[].package.base_product_id |
no | integer:int64 | null |
min: 1 | 1200 |
||
data[].package.multiplicity |
no | number:float | null |
min: 0 | 6 |
||
links |
yes | PaginationLinks |
|
|||
links.first |
yes | string:uri | null |
"https://betawms.ffcdek.ru/api/v2/orders?page=1" |
|||
links.last |
yes | string:uri | null |
"https://betawms.ffcdek.ru/api/v2/orders?page=2" |
|||
links.prev |
yes | string:uri | null |
|
|||
links.next |
yes | string:uri | null |
"https://betawms.ffcdek.ru/api/v2/orders?page=2" |
|||
meta |
yes | PaginationMeta |
|
|||
meta.api_version |
yes | enum |
enum: v2 | "v2" |
||
meta.current_page |
yes | integer:int32 |
min: 1 | 1 |
||
meta.from |
yes | integer:int32 | null |
min: 1 | 1 |
||
meta.last_page |
yes | integer:int32 |
min: 1 | 2 |
||
meta.path |
yes | string:uri |
"https://betawms.ffcdek.ru/api/v2/orders" |
|||
meta.per_page |
yes | integer:int32 |
min: 1 | 50 |
||
meta.to |
yes | integer:int32 | null |
min: 1 | 50 |
||
meta.total |
yes | integer:int32 |
min: 0 | 73 |
Response examples
success
{
"data": [
{
"id": 1201,
"external_id": "EXT-10045",
"shop_id": 12,
"domain_id": 3,
"status_id": 1,
"type_id": 1,
"name": "Coffee 250g",
"article": "ART-001245",
"sku": "SKU-001245",
"price": 19.99,
"estimated_price": 21.5,
"image_url": "https://betawms.ffcdek.ru/offer-images/products/coffee-250g.jpg",
"dimensions": {
"x": 105,
"y": 200,
"z": 32,
"unit": "mm"
},
"weight": {
"value": 250,
"unit": "g"
},
"datamatrix_required": false,
"expiration_required": true,
"comment": "Keep dry",
"description": "Ground coffee package",
"inventory_updated_at": "2026-07-14T08:30:00Z",
"barcodes": [
{
"barcode": "4601234567890",
"is_main": true
}
],
"stocks": {
"available_by_warehouse": {
"42": 18
},
"physical": [
{
"warehouse_id": 42,
"warehouse_name": "Warehouse Sofia",
"stockTotal": 24,
"stockReserved": 6,
"stockAvailable": 18
}
],
"aggregate": [
{
"warehouse_id": 42,
"warehouse_name": "Warehouse Sofia",
"stockTotal": 30,
"stockReserved": 7,
"stockAvailable": 23
}
]
},
"package": {
"kind": "unit",
"parent_product_id": null,
"base_product_id": null,
"multiplicity": null
},
"created_at": "2026-07-14T09:00:00Z",
"updated_at": "2026-07-14T09:30:00Z"
}
],
"links": {
"first": "https://betawms.ffcdek.ru/api/v2/products?page=1",
"last": "https://betawms.ffcdek.ru/api/v2/products?page=2",
"prev": null,
"next": "https://betawms.ffcdek.ru/api/v2/products?page=2"
},
"meta": {
"api_version": "v2",
"current_page": 1,
"from": 1,
"last_page": 2,
"path": "https://betawms.ffcdek.ru/api/v2/products",
"per_page": 50,
"to": 50,
"total": 73
}
}
401 Authentication required
Unauthorized
Authentication is required.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
message |
yes | string |
"The request could not be processed." |
|||
error |
yes | string |
"BAD_REQUEST" |
Response examples
AUTH_REQUIRED
{
"message": "Authentication is required.",
"error": "AUTH_REQUIRED"
}
403 Forbidden
Forbidden
The authenticated account is not allowed to perform this action.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
message |
yes | string |
"The request could not be processed." |
|||
error |
yes | string |
"BAD_REQUEST" |
Response examples
FORBIDDEN
{
"message": "The authenticated account is not allowed to perform this action.",
"error": "FORBIDDEN"
}
429 Rate limit exceeded
TooManyRequests
Rate limit exceeded.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
message |
yes | string |
"Too Many Attempts." |
|||
error |
yes | string |
"RATE_LIMITED" |
500 Internal server error
InternalServerError
An unexpected server error occurred.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
message |
yes | string |
"The request could not be processed." |
|||
error |
yes | string |
"BAD_REQUEST" |
Response examples
INTERNAL_SERVER_ERROR
{
"message": "An unexpected server error occurred.",
"error": "INTERNAL_SERVER_ERROR"
}
Create product
| operationId | createProducts |
|---|---|
| Tag | Products |
| Security | bearerAuth |
Creates one product or imports multiple products. Barcodes, dimensions, weights, marking requirements, expiration requirements, and package metadata are accepted in public v2 field names.
For unplaced consumable usage, stockTotal does not include a permanent physical write-off and must not be used as available packaging inventory. Addressed consumable write-offs remain the existing warehouse process.
For unplaced consumable usage, stockTotal does not include a permanent physical write-off and must not be used as available packaging inventory. Addressed consumable write-offs remain the existing warehouse process.
For unplaced consumable usage, stockTotal does not include a permanent physical write-off and must not be used as available packaging inventory. Addressed consumable write-offs remain the existing warehouse process.
For unplaced consumable usage, stockTotal does not include a permanent physical write-off and must not be used as available packaging inventory. Addressed consumable write-offs remain the existing warehouse process.
For unplaced consumable usage, stockTotal does not include a permanent physical write-off and must not be used as available packaging inventory. Addressed consumable write-offs remain the existing warehouse process.
For unplaced consumable usage, stockTotal does not include a permanent physical write-off and must not be used as available packaging inventory. Addressed consumable write-offs remain the existing warehouse process.
Optional type_id accepts integer codes 1 ordinary, 2 composite, 3 component, 4 consumable, 5 packaging. Omission preserves the existing/default type; explicit null, empty, fractional or unknown values are rejected. Types 2/3 require composites enabled in the shop and permission to manage composition, in addition to product create/update rights. Quant packaging and links, existing composition links and dependent warehouse documents can prevent a type transition. This field does not create or replace a composition. Product and component prices are independent. Each item commits separately: a later rule failure may leave earlier items applied. Read back affected products before retrying a failed bulk request.
HTTP request example (cURL)
Set your server and token. Replace sample IDs with accessible IDs. For requests with a body, save the applicable JSON example below as request.json and replace sample values before sending. Idempotency-Key identifies one logical operation.
curl --globoff --request POST "${NWMS_BASE_URL}/api/v2/products" \
--header "Accept: application/json" \
--header "Authorization: Bearer ${NWMS_TOKEN}" \
--header "Idempotency-Key: ${NWMS_REQUEST_KEY}" \
--header "Content-Type: application/json" \
--data-binary @request.jsonParameters
| Name | In | Required | Type | Style | Explode | Constraints | Description | Example |
|---|---|---|---|---|---|---|---|---|
Idempotency-Key |
header | no | string |
maxLength: 191 | Optional idempotency key for API v2 write requests. Successful 2xx/3xx responses are replayed for the same key and identical payload for one day. The same key with a different payload returns 409. |
"ord-create-20260714-001" |
Request body
BulkCreateProductsRequest required
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
data |
yes | CreateProductPayload |
|
|||
data |
yes | array<CreateProductPayload> |
minItems: 1 | |
Request examples
default
{
"data": [
{
"external_id": "EXT-10045",
"shop_id": 12,
"name": "Coffee 250g",
"barcodes": [
{
"barcode": "4601234567890",
"is_main": true
}
],
"type_id": 3
}
]
}
Responses
201 Created.
Created.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
(root) |
no | ProductMutationResponse |
Single-object product writes return ProductResponse; array product writes return ProductCollectionResponse in the same input order for successfully processed items. |
|
||
data |
yes | Product |
Product catalog item exposed by API v2. Product barcodes identify a product or package type and are not unique SSCC values. Use /api/v2/closed-boxes to read individual sealed boxes and their current box balance. |
|
||
data.id |
yes | integer:int64 |
readOnly | min: 1 | 1001 |
|
data.created_at |
no | string:date-time | null |
"2026-07-14T10:30:00Z" |
|||
data.updated_at |
no | string:date-time | null |
"2026-07-14T10:30:00Z" |
|||
data.external_id |
no | string | null |
"EXT-10045" |
|||
data.shop_id |
no | integer:int64 | null |
min: 1 | 12 |
||
data.domain_id |
no | integer:int64 | null |
min: 1 | 3 |
||
data.status_id |
no | integer:int64 | null |
1 |
|||
data.type_id |
no | integer:int64 | null |
1 |
|||
data.name |
yes | string |
"Coffee 250g" |
|||
data.article |
no | string | null |
"ART-001245" |
|||
data.sku |
no | string | null |
"SKU-001245" |
|||
data.price |
no | number:float | null |
19.99 |
|||
data.estimated_price |
no | number:float | null |
21.5 |
|||
data.image_url |
no | string:uri | null |
"https://betawms.ffcdek.ru/offer-images/products/coffee-250g.jpg" |
|||
data.dimensions |
no | Dimensions |
|
|||
data.dimensions.x |
no | number:float | null |
min: 0 | 105 |
||
data.dimensions.y |
no | number:float | null |
min: 0 | 200 |
||
data.dimensions.z |
no | number:float | null |
min: 0 | 32 |
||
data.dimensions.unit |
yes | enum |
enum: mm | "mm" |
||
data.weight |
no | Weight |
|
|||
data.weight.value |
no | number:float | null |
min: 0 | 250 |
||
data.weight.unit |
yes | enum |
enum: g | "g" |
||
data.datamatrix_required |
yes | boolean |
false |
|||
data.expiration_required |
yes | boolean |
true |
|||
data.comment |
no | string | null |
"Keep dry" |
|||
data.description |
no | string | null |
"Ground coffee package" |
|||
data.inventory_updated_at |
no | string:date-time | null |
"2026-07-14T10:30:00Z" |
|||
data.barcodes |
no | array<ProductBarcode> |
|
|||
data.barcodes[] |
no | ProductBarcode |
|
|||
data.barcodes[].barcode |
yes | string |
minLength: 1 maxLength: 64 |
"4601234567890" |
||
data.barcodes[].is_main |
yes | boolean |
true |
|||
data.stocks |
no | ProductStocks |
|
|||
data.stocks.available_by_warehouse |
no | object |
Map keyed by warehouse ID. Values are quantities. | {"42":18.0,"43":3.0} |
||
data.stocks.physical |
no | array<ProductStockSummary> |
Physical stock rows for the product itself. | |
||
data.stocks.physical[] |
no | ProductStockSummary |
Stock totals for one warehouse. Quantities may be negative when reservations exceed physical stock. | |
||
data.stocks.physical[].warehouse_id |
yes | integer:int64 |
min: 1 | 42 |
||
data.stocks.physical[].warehouse_name |
yes | string | null |
"Warehouse Sofia" |
|||
data.stocks.physical[].stockTotal |
yes | number:float |
24.0 |
|||
data.stocks.physical[].stockReserved |
yes | number:float |
6.0 |
|||
data.stocks.physical[].stockAvailable |
yes | number:float |
18.0 |
|||
data.stocks.aggregate |
no | array<ProductStockSummary> |
Aggregated base-unit stock rows for the product and its descendant quants. | |
||
data.stocks.aggregate[] |
no | ProductStockSummary |
Stock totals for one warehouse. Quantities may be negative when reservations exceed physical stock. | |
||
data.stocks.aggregate[].warehouse_id |
yes | integer:int64 |
min: 1 | 42 |
||
data.stocks.aggregate[].warehouse_name |
yes | string | null |
"Warehouse Sofia" |
|||
data.stocks.aggregate[].stockTotal |
yes | number:float |
24.0 |
|||
data.stocks.aggregate[].stockReserved |
yes | number:float |
6.0 |
|||
data.stocks.aggregate[].stockAvailable |
yes | number:float |
18.0 |
|||
data.package |
no | object | null |
|
|||
data.package.kind |
no | string | null |
"unit" |
|||
data.package.parent_product_id |
no | integer:int64 | null |
min: 1 | 1201 |
||
data.package.base_product_id |
no | integer:int64 | null |
min: 1 | 1200 |
||
data.package.multiplicity |
no | number:float | null |
min: 0 | 6 |
||
meta |
yes | ResponseMeta |
|
|||
meta.api_version |
yes | enum |
enum: v2 | "v2" |
||
meta.deleted |
no | boolean |
true |
|||
meta.id |
no | integer:int64 | null |
min: 1 | 1001 |
||
meta.command_replayed |
no | boolean |
True when an idempotent cross-dock command returned the original successful result. | false |
||
meta.crossdock_configuration |
no | object | null |
|
|||
meta.crossdock_configuration.physical_warehouse_id |
yes | integer:int64 |
min: 1 | 42 |
||
meta.crossdock_configuration.enabled |
yes | boolean |
true |
|||
meta.crossdock_configuration.process_version |
yes | string |
"direct_order_v1" |
|||
data |
yes | array<Product> |
|
|||
data[] |
no | Product |
Product catalog item exposed by API v2. Product barcodes identify a product or package type and are not unique SSCC values. Use /api/v2/closed-boxes to read individual sealed boxes and their current box balance. |
|
||
data[].id |
yes | integer:int64 |
readOnly | min: 1 | 1001 |
|
data[].created_at |
no | string:date-time | null |
"2026-07-14T10:30:00Z" |
|||
data[].updated_at |
no | string:date-time | null |
"2026-07-14T10:30:00Z" |
|||
data[].external_id |
no | string | null |
"EXT-10045" |
|||
data[].shop_id |
no | integer:int64 | null |
min: 1 | 12 |
||
data[].domain_id |
no | integer:int64 | null |
min: 1 | 3 |
||
data[].status_id |
no | integer:int64 | null |
1 |
|||
data[].type_id |
no | integer:int64 | null |
1 |
|||
data[].name |
yes | string |
"Coffee 250g" |
|||
data[].article |
no | string | null |
"ART-001245" |
|||
data[].sku |
no | string | null |
"SKU-001245" |
|||
data[].price |
no | number:float | null |
19.99 |
|||
data[].estimated_price |
no | number:float | null |
21.5 |
|||
data[].image_url |
no | string:uri | null |
"https://betawms.ffcdek.ru/offer-images/products/coffee-250g.jpg" |
|||
data[].dimensions |
no | Dimensions |
|
|||
data[].dimensions.x |
no | number:float | null |
min: 0 | 105 |
||
data[].dimensions.y |
no | number:float | null |
min: 0 | 200 |
||
data[].dimensions.z |
no | number:float | null |
min: 0 | 32 |
||
data[].dimensions.unit |
yes | enum |
enum: mm | "mm" |
||
data[].weight |
no | Weight |
|
|||
data[].weight.value |
no | number:float | null |
min: 0 | 250 |
||
data[].weight.unit |
yes | enum |
enum: g | "g" |
||
data[].datamatrix_required |
yes | boolean |
false |
|||
data[].expiration_required |
yes | boolean |
true |
|||
data[].comment |
no | string | null |
"Keep dry" |
|||
data[].description |
no | string | null |
"Ground coffee package" |
|||
data[].inventory_updated_at |
no | string:date-time | null |
"2026-07-14T10:30:00Z" |
|||
data[].barcodes |
no | array<ProductBarcode> |
|
|||
data[].barcodes[] |
no | ProductBarcode |
|
|||
data[].barcodes[].barcode |
yes | string |
minLength: 1 maxLength: 64 |
"4601234567890" |
||
data[].barcodes[].is_main |
yes | boolean |
true |
|||
data[].stocks |
no | ProductStocks |
|
|||
data[].stocks.available_by_warehouse |
no | object |
Map keyed by warehouse ID. Values are quantities. | {"42":18.0,"43":3.0} |
||
data[].stocks.physical |
no | array<ProductStockSummary> |
Physical stock rows for the product itself. | |
||
data[].stocks.physical[] |
no | ProductStockSummary |
Stock totals for one warehouse. Quantities may be negative when reservations exceed physical stock. | |
||
data[].stocks.physical[].warehouse_id |
yes | integer:int64 |
min: 1 | 42 |
||
data[].stocks.physical[].warehouse_name |
yes | string | null |
"Warehouse Sofia" |
|||
data[].stocks.physical[].stockTotal |
yes | number:float |
24.0 |
|||
data[].stocks.physical[].stockReserved |
yes | number:float |
6.0 |
|||
data[].stocks.physical[].stockAvailable |
yes | number:float |
18.0 |
|||
data[].stocks.aggregate |
no | array<ProductStockSummary> |
Aggregated base-unit stock rows for the product and its descendant quants. | |
||
data[].stocks.aggregate[] |
no | ProductStockSummary |
Stock totals for one warehouse. Quantities may be negative when reservations exceed physical stock. | |
||
data[].stocks.aggregate[].warehouse_id |
yes | integer:int64 |
min: 1 | 42 |
||
data[].stocks.aggregate[].warehouse_name |
yes | string | null |
"Warehouse Sofia" |
|||
data[].stocks.aggregate[].stockTotal |
yes | number:float |
24.0 |
|||
data[].stocks.aggregate[].stockReserved |
yes | number:float |
6.0 |
|||
data[].stocks.aggregate[].stockAvailable |
yes | number:float |
18.0 |
|||
data[].package |
no | object | null |
|
|||
data[].package.kind |
no | string | null |
"unit" |
|||
data[].package.parent_product_id |
no | integer:int64 | null |
min: 1 | 1201 |
||
data[].package.base_product_id |
no | integer:int64 | null |
min: 1 | 1200 |
||
data[].package.multiplicity |
no | number:float | null |
min: 0 | 6 |
||
meta |
yes | ResponseMeta |
|
|||
meta.api_version |
yes | enum |
enum: v2 | "v2" |
||
meta.deleted |
no | boolean |
true |
|||
meta.id |
no | integer:int64 | null |
min: 1 | 1001 |
||
meta.command_replayed |
no | boolean |
True when an idempotent cross-dock command returned the original successful result. | false |
||
meta.crossdock_configuration |
no | object | null |
|
|||
meta.crossdock_configuration.physical_warehouse_id |
yes | integer:int64 |
min: 1 | 42 |
||
meta.crossdock_configuration.enabled |
yes | boolean |
true |
|||
meta.crossdock_configuration.process_version |
yes | string |
"direct_order_v1" |
Response examples
success
{
"data": {
"id": 1201,
"external_id": "EXT-10045",
"shop_id": 12,
"domain_id": 3,
"status_id": 1,
"type_id": 1,
"name": "Coffee 250g",
"article": "ART-001245",
"sku": "SKU-001245",
"price": 19.99,
"estimated_price": 21.5,
"image_url": "https://betawms.ffcdek.ru/offer-images/products/coffee-250g.jpg",
"dimensions": {
"x": 105,
"y": 200,
"z": 32,
"unit": "mm"
},
"weight": {
"value": 250,
"unit": "g"
},
"datamatrix_required": false,
"expiration_required": true,
"comment": "Keep dry",
"description": "Ground coffee package",
"inventory_updated_at": "2026-07-14T08:30:00Z",
"barcodes": [
{
"barcode": "4601234567890",
"is_main": true
}
],
"stocks": {
"available_by_warehouse": {
"42": 18
},
"physical": [
{
"warehouse_id": 42,
"warehouse_name": "Warehouse Sofia",
"stockTotal": 24,
"stockReserved": 6,
"stockAvailable": 18
}
],
"aggregate": [
{
"warehouse_id": 42,
"warehouse_name": "Warehouse Sofia",
"stockTotal": 30,
"stockReserved": 7,
"stockAvailable": 23
}
]
},
"package": {
"kind": "unit",
"parent_product_id": null,
"base_product_id": null,
"multiplicity": null
},
"created_at": "2026-07-14T09:00:00Z",
"updated_at": "2026-07-14T09:30:00Z"
},
"meta": {
"api_version": "v2"
}
}
400 Bad request
BadRequest
Bad request.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
message |
yes | string |
"The request could not be processed." |
|||
error |
yes | string |
"BAD_REQUEST" |
Response examples
BAD_REQUEST
{
"message": "Bad request.",
"error": "BAD_REQUEST"
}
401 Authentication required
Unauthorized
Authentication is required.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
message |
yes | string |
"The request could not be processed." |
|||
error |
yes | string |
"BAD_REQUEST" |
Response examples
AUTH_REQUIRED
{
"message": "Authentication is required.",
"error": "AUTH_REQUIRED"
}
403 Forbidden
Forbidden
The authenticated account is not allowed to perform this action.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
message |
yes | string |
"The request could not be processed." |
|||
error |
yes | string |
"BAD_REQUEST" |
Response examples
FORBIDDEN
{
"message": "The authenticated account is not allowed to perform this action.",
"error": "FORBIDDEN"
}
409 Conflict
Conflict
The request conflicts with the current resource state or idempotency key state.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
message |
yes | string |
"The request could not be processed." |
|||
error |
yes | string |
"BAD_REQUEST" |
Response examples
idempotencyConflict
{
"message": "The idempotency key is already used with a different payload.",
"error": "IDEMPOTENCY_KEY_CONFLICT"
}
422 Validation error
ValidationError
Validation failed.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
message |
yes | string |
"The given data was invalid." |
|||
error |
no | string |
"VALIDATION_ERROR" |
|||
errors |
yes | object |
{"warehouse_id":["The selected warehouse is invalid."]} |
Response examples
validationError
{
"message": "The given data was invalid.",
"error": "VALIDATION_ERROR",
"errors": {
"warehouse_id": [
"The selected warehouse is invalid."
]
}
}
429 Rate limit exceeded
TooManyRequests
Rate limit exceeded.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
message |
yes | string |
"Too Many Attempts." |
|||
error |
yes | string |
"RATE_LIMITED" |
500 Internal server error
InternalServerError
An unexpected server error occurred.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
message |
yes | string |
"The request could not be processed." |
|||
error |
yes | string |
"BAD_REQUEST" |
Response examples
INTERNAL_SERVER_ERROR
{
"message": "An unexpected server error occurred.",
"error": "INTERNAL_SERVER_ERROR"
}
Update product
| operationId | replaceProducts |
|---|---|
| Tag | Products |
| Security | bearerAuth |
Bulk updates products identified by id or by external_id plus shop_id. When barcodes are supplied, the barcode set is replaced according to existing product rules.
Current backend behavior is legacy partial-update semantics: omitted fields are left unchanged.
For unplaced consumable usage, stockTotal does not include a permanent physical write-off and must not be used as available packaging inventory. Addressed consumable write-offs remain the existing warehouse process.
For unplaced consumable usage, stockTotal does not include a permanent physical write-off and must not be used as available packaging inventory. Addressed consumable write-offs remain the existing warehouse process.
For unplaced consumable usage, stockTotal does not include a permanent physical write-off and must not be used as available packaging inventory. Addressed consumable write-offs remain the existing warehouse process.
For unplaced consumable usage, stockTotal does not include a permanent physical write-off and must not be used as available packaging inventory. Addressed consumable write-offs remain the existing warehouse process.
For unplaced consumable usage, stockTotal does not include a permanent physical write-off and must not be used as available packaging inventory. Addressed consumable write-offs remain the existing warehouse process.
For unplaced consumable usage, stockTotal does not include a permanent physical write-off and must not be used as available packaging inventory. Addressed consumable write-offs remain the existing warehouse process.
Optional type_id accepts integer codes 1 ordinary, 2 composite, 3 component, 4 consumable, 5 packaging. Omission preserves the existing/default type; explicit null, empty, fractional or unknown values are rejected. Types 2/3 require composites enabled in the shop and permission to manage composition, in addition to product create/update rights. Quant packaging and links, existing composition links and dependent warehouse documents can prevent a type transition. This field does not create or replace a composition. Product and component prices are independent. Each item commits separately: a later rule failure may leave earlier items applied. Read back affected products before retrying a failed bulk request.
HTTP request example (cURL)
Set your server and token. Replace sample IDs with accessible IDs. For requests with a body, save the applicable JSON example below as request.json and replace sample values before sending. Idempotency-Key identifies one logical operation.
curl --globoff --request PUT "${NWMS_BASE_URL}/api/v2/products" \
--header "Accept: application/json" \
--header "Authorization: Bearer ${NWMS_TOKEN}" \
--header "Idempotency-Key: ${NWMS_REQUEST_KEY}" \
--header "Content-Type: application/json" \
--data-binary @request.jsonParameters
| Name | In | Required | Type | Style | Explode | Constraints | Description | Example |
|---|---|---|---|---|---|---|---|---|
Idempotency-Key |
header | no | string |
maxLength: 191 | Optional idempotency key for API v2 write requests. Successful 2xx/3xx responses are replayed for the same key and identical payload for one day. The same key with a different payload returns 409. |
"ord-create-20260714-001" |
Request body
BulkReplaceProductsRequest required
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
data |
yes | ReplaceProductPayload |
|
|||
data |
yes | array<ReplaceProductPayload> |
minItems: 1 | |
Request examples
default
{
"data": [
{
"id": 1201,
"name": "Coffee 250g",
"price": 19.99,
"type_id": 3
}
]
}
Responses
200 Successful response.
Successful response.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
(root) |
no | ProductMutationResponse |
Single-object product writes return ProductResponse; array product writes return ProductCollectionResponse in the same input order for successfully processed items. |
|
||
data |
yes | Product |
Product catalog item exposed by API v2. Product barcodes identify a product or package type and are not unique SSCC values. Use /api/v2/closed-boxes to read individual sealed boxes and their current box balance. |
|
||
data.id |
yes | integer:int64 |
readOnly | min: 1 | 1001 |
|
data.created_at |
no | string:date-time | null |
"2026-07-14T10:30:00Z" |
|||
data.updated_at |
no | string:date-time | null |
"2026-07-14T10:30:00Z" |
|||
data.external_id |
no | string | null |
"EXT-10045" |
|||
data.shop_id |
no | integer:int64 | null |
min: 1 | 12 |
||
data.domain_id |
no | integer:int64 | null |
min: 1 | 3 |
||
data.status_id |
no | integer:int64 | null |
1 |
|||
data.type_id |
no | integer:int64 | null |
1 |
|||
data.name |
yes | string |
"Coffee 250g" |
|||
data.article |
no | string | null |
"ART-001245" |
|||
data.sku |
no | string | null |
"SKU-001245" |
|||
data.price |
no | number:float | null |
19.99 |
|||
data.estimated_price |
no | number:float | null |
21.5 |
|||
data.image_url |
no | string:uri | null |
"https://betawms.ffcdek.ru/offer-images/products/coffee-250g.jpg" |
|||
data.dimensions |
no | Dimensions |
|
|||
data.dimensions.x |
no | number:float | null |
min: 0 | 105 |
||
data.dimensions.y |
no | number:float | null |
min: 0 | 200 |
||
data.dimensions.z |
no | number:float | null |
min: 0 | 32 |
||
data.dimensions.unit |
yes | enum |
enum: mm | "mm" |
||
data.weight |
no | Weight |
|
|||
data.weight.value |
no | number:float | null |
min: 0 | 250 |
||
data.weight.unit |
yes | enum |
enum: g | "g" |
||
data.datamatrix_required |
yes | boolean |
false |
|||
data.expiration_required |
yes | boolean |
true |
|||
data.comment |
no | string | null |
"Keep dry" |
|||
data.description |
no | string | null |
"Ground coffee package" |
|||
data.inventory_updated_at |
no | string:date-time | null |
"2026-07-14T10:30:00Z" |
|||
data.barcodes |
no | array<ProductBarcode> |
|
|||
data.barcodes[] |
no | ProductBarcode |
|
|||
data.barcodes[].barcode |
yes | string |
minLength: 1 maxLength: 64 |
"4601234567890" |
||
data.barcodes[].is_main |
yes | boolean |
true |
|||
data.stocks |
no | ProductStocks |
|
|||
data.stocks.available_by_warehouse |
no | object |
Map keyed by warehouse ID. Values are quantities. | {"42":18.0,"43":3.0} |
||
data.stocks.physical |
no | array<ProductStockSummary> |
Physical stock rows for the product itself. | |
||
data.stocks.physical[] |
no | ProductStockSummary |
Stock totals for one warehouse. Quantities may be negative when reservations exceed physical stock. | |
||
data.stocks.physical[].warehouse_id |
yes | integer:int64 |
min: 1 | 42 |
||
data.stocks.physical[].warehouse_name |
yes | string | null |
"Warehouse Sofia" |
|||
data.stocks.physical[].stockTotal |
yes | number:float |
24.0 |
|||
data.stocks.physical[].stockReserved |
yes | number:float |
6.0 |
|||
data.stocks.physical[].stockAvailable |
yes | number:float |
18.0 |
|||
data.stocks.aggregate |
no | array<ProductStockSummary> |
Aggregated base-unit stock rows for the product and its descendant quants. | |
||
data.stocks.aggregate[] |
no | ProductStockSummary |
Stock totals for one warehouse. Quantities may be negative when reservations exceed physical stock. | |
||
data.stocks.aggregate[].warehouse_id |
yes | integer:int64 |
min: 1 | 42 |
||
data.stocks.aggregate[].warehouse_name |
yes | string | null |
"Warehouse Sofia" |
|||
data.stocks.aggregate[].stockTotal |
yes | number:float |
24.0 |
|||
data.stocks.aggregate[].stockReserved |
yes | number:float |
6.0 |
|||
data.stocks.aggregate[].stockAvailable |
yes | number:float |
18.0 |
|||
data.package |
no | object | null |
|
|||
data.package.kind |
no | string | null |
"unit" |
|||
data.package.parent_product_id |
no | integer:int64 | null |
min: 1 | 1201 |
||
data.package.base_product_id |
no | integer:int64 | null |
min: 1 | 1200 |
||
data.package.multiplicity |
no | number:float | null |
min: 0 | 6 |
||
meta |
yes | ResponseMeta |
|
|||
meta.api_version |
yes | enum |
enum: v2 | "v2" |
||
meta.deleted |
no | boolean |
true |
|||
meta.id |
no | integer:int64 | null |
min: 1 | 1001 |
||
meta.command_replayed |
no | boolean |
True when an idempotent cross-dock command returned the original successful result. | false |
||
meta.crossdock_configuration |
no | object | null |
|
|||
meta.crossdock_configuration.physical_warehouse_id |
yes | integer:int64 |
min: 1 | 42 |
||
meta.crossdock_configuration.enabled |
yes | boolean |
true |
|||
meta.crossdock_configuration.process_version |
yes | string |
"direct_order_v1" |
|||
data |
yes | array<Product> |
|
|||
data[] |
no | Product |
Product catalog item exposed by API v2. Product barcodes identify a product or package type and are not unique SSCC values. Use /api/v2/closed-boxes to read individual sealed boxes and their current box balance. |
|
||
data[].id |
yes | integer:int64 |
readOnly | min: 1 | 1001 |
|
data[].created_at |
no | string:date-time | null |
"2026-07-14T10:30:00Z" |
|||
data[].updated_at |
no | string:date-time | null |
"2026-07-14T10:30:00Z" |
|||
data[].external_id |
no | string | null |
"EXT-10045" |
|||
data[].shop_id |
no | integer:int64 | null |
min: 1 | 12 |
||
data[].domain_id |
no | integer:int64 | null |
min: 1 | 3 |
||
data[].status_id |
no | integer:int64 | null |
1 |
|||
data[].type_id |
no | integer:int64 | null |
1 |
|||
data[].name |
yes | string |
"Coffee 250g" |
|||
data[].article |
no | string | null |
"ART-001245" |
|||
data[].sku |
no | string | null |
"SKU-001245" |
|||
data[].price |
no | number:float | null |
19.99 |
|||
data[].estimated_price |
no | number:float | null |
21.5 |
|||
data[].image_url |
no | string:uri | null |
"https://betawms.ffcdek.ru/offer-images/products/coffee-250g.jpg" |
|||
data[].dimensions |
no | Dimensions |
|
|||
data[].dimensions.x |
no | number:float | null |
min: 0 | 105 |
||
data[].dimensions.y |
no | number:float | null |
min: 0 | 200 |
||
data[].dimensions.z |
no | number:float | null |
min: 0 | 32 |
||
data[].dimensions.unit |
yes | enum |
enum: mm | "mm" |
||
data[].weight |
no | Weight |
|
|||
data[].weight.value |
no | number:float | null |
min: 0 | 250 |
||
data[].weight.unit |
yes | enum |
enum: g | "g" |
||
data[].datamatrix_required |
yes | boolean |
false |
|||
data[].expiration_required |
yes | boolean |
true |
|||
data[].comment |
no | string | null |
"Keep dry" |
|||
data[].description |
no | string | null |
"Ground coffee package" |
|||
data[].inventory_updated_at |
no | string:date-time | null |
"2026-07-14T10:30:00Z" |
|||
data[].barcodes |
no | array<ProductBarcode> |
|
|||
data[].barcodes[] |
no | ProductBarcode |
|
|||
data[].barcodes[].barcode |
yes | string |
minLength: 1 maxLength: 64 |
"4601234567890" |
||
data[].barcodes[].is_main |
yes | boolean |
true |
|||
data[].stocks |
no | ProductStocks |
|
|||
data[].stocks.available_by_warehouse |
no | object |
Map keyed by warehouse ID. Values are quantities. | {"42":18.0,"43":3.0} |
||
data[].stocks.physical |
no | array<ProductStockSummary> |
Physical stock rows for the product itself. | |
||
data[].stocks.physical[] |
no | ProductStockSummary |
Stock totals for one warehouse. Quantities may be negative when reservations exceed physical stock. | |
||
data[].stocks.physical[].warehouse_id |
yes | integer:int64 |
min: 1 | 42 |
||
data[].stocks.physical[].warehouse_name |
yes | string | null |
"Warehouse Sofia" |
|||
data[].stocks.physical[].stockTotal |
yes | number:float |
24.0 |
|||
data[].stocks.physical[].stockReserved |
yes | number:float |
6.0 |
|||
data[].stocks.physical[].stockAvailable |
yes | number:float |
18.0 |
|||
data[].stocks.aggregate |
no | array<ProductStockSummary> |
Aggregated base-unit stock rows for the product and its descendant quants. | |
||
data[].stocks.aggregate[] |
no | ProductStockSummary |
Stock totals for one warehouse. Quantities may be negative when reservations exceed physical stock. | |
||
data[].stocks.aggregate[].warehouse_id |
yes | integer:int64 |
min: 1 | 42 |
||
data[].stocks.aggregate[].warehouse_name |
yes | string | null |
"Warehouse Sofia" |
|||
data[].stocks.aggregate[].stockTotal |
yes | number:float |
24.0 |
|||
data[].stocks.aggregate[].stockReserved |
yes | number:float |
6.0 |
|||
data[].stocks.aggregate[].stockAvailable |
yes | number:float |
18.0 |
|||
data[].package |
no | object | null |
|
|||
data[].package.kind |
no | string | null |
"unit" |
|||
data[].package.parent_product_id |
no | integer:int64 | null |
min: 1 | 1201 |
||
data[].package.base_product_id |
no | integer:int64 | null |
min: 1 | 1200 |
||
data[].package.multiplicity |
no | number:float | null |
min: 0 | 6 |
||
meta |
yes | ResponseMeta |
|
|||
meta.api_version |
yes | enum |
enum: v2 | "v2" |
||
meta.deleted |
no | boolean |
true |
|||
meta.id |
no | integer:int64 | null |
min: 1 | 1001 |
||
meta.command_replayed |
no | boolean |
True when an idempotent cross-dock command returned the original successful result. | false |
||
meta.crossdock_configuration |
no | object | null |
|
|||
meta.crossdock_configuration.physical_warehouse_id |
yes | integer:int64 |
min: 1 | 42 |
||
meta.crossdock_configuration.enabled |
yes | boolean |
true |
|||
meta.crossdock_configuration.process_version |
yes | string |
"direct_order_v1" |
Response examples
success
{
"data": {
"id": 1201,
"external_id": "EXT-10045",
"shop_id": 12,
"domain_id": 3,
"status_id": 1,
"type_id": 1,
"name": "Coffee 250g",
"article": "ART-001245",
"sku": "SKU-001245",
"price": 19.99,
"estimated_price": 21.5,
"image_url": "https://betawms.ffcdek.ru/offer-images/products/coffee-250g.jpg",
"dimensions": {
"x": 105,
"y": 200,
"z": 32,
"unit": "mm"
},
"weight": {
"value": 250,
"unit": "g"
},
"datamatrix_required": false,
"expiration_required": true,
"comment": "Keep dry",
"description": "Ground coffee package",
"inventory_updated_at": "2026-07-14T08:30:00Z",
"barcodes": [
{
"barcode": "4601234567890",
"is_main": true
}
],
"stocks": {
"available_by_warehouse": {
"42": 18
},
"physical": [
{
"warehouse_id": 42,
"warehouse_name": "Warehouse Sofia",
"stockTotal": 24,
"stockReserved": 6,
"stockAvailable": 18
}
],
"aggregate": [
{
"warehouse_id": 42,
"warehouse_name": "Warehouse Sofia",
"stockTotal": 30,
"stockReserved": 7,
"stockAvailable": 23
}
]
},
"package": {
"kind": "unit",
"parent_product_id": null,
"base_product_id": null,
"multiplicity": null
},
"created_at": "2026-07-14T09:00:00Z",
"updated_at": "2026-07-14T09:30:00Z"
},
"meta": {
"api_version": "v2"
}
}
400 Bad request
BadRequest
Bad request.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
message |
yes | string |
"The request could not be processed." |
|||
error |
yes | string |
"BAD_REQUEST" |
Response examples
BAD_REQUEST
{
"message": "Bad request.",
"error": "BAD_REQUEST"
}
401 Authentication required
Unauthorized
Authentication is required.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
message |
yes | string |
"The request could not be processed." |
|||
error |
yes | string |
"BAD_REQUEST" |
Response examples
AUTH_REQUIRED
{
"message": "Authentication is required.",
"error": "AUTH_REQUIRED"
}
403 Forbidden
Forbidden
The authenticated account is not allowed to perform this action.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
message |
yes | string |
"The request could not be processed." |
|||
error |
yes | string |
"BAD_REQUEST" |
Response examples
FORBIDDEN
{
"message": "The authenticated account is not allowed to perform this action.",
"error": "FORBIDDEN"
}
409 Conflict
Conflict
The request conflicts with the current resource state or idempotency key state.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
message |
yes | string |
"The request could not be processed." |
|||
error |
yes | string |
"BAD_REQUEST" |
Response examples
idempotencyConflict
{
"message": "The idempotency key is already used with a different payload.",
"error": "IDEMPOTENCY_KEY_CONFLICT"
}
422 Validation error
ValidationError
Validation failed.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
message |
yes | string |
"The given data was invalid." |
|||
error |
no | string |
"VALIDATION_ERROR" |
|||
errors |
yes | object |
{"warehouse_id":["The selected warehouse is invalid."]} |
Response examples
validationError
{
"message": "The given data was invalid.",
"error": "VALIDATION_ERROR",
"errors": {
"warehouse_id": [
"The selected warehouse is invalid."
]
}
}
429 Rate limit exceeded
TooManyRequests
Rate limit exceeded.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
message |
yes | string |
"Too Many Attempts." |
|||
error |
yes | string |
"RATE_LIMITED" |
500 Internal server error
InternalServerError
An unexpected server error occurred.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
message |
yes | string |
"The request could not be processed." |
|||
error |
yes | string |
"BAD_REQUEST" |
Response examples
INTERNAL_SERVER_ERROR
{
"message": "An unexpected server error occurred.",
"error": "INTERNAL_SERVER_ERROR"
}
Update product
| operationId | patchProducts |
|---|---|
| Tag | Products |
| Security | bearerAuth |
Partially bulk updates products. Only supplied product fields are changed.
Current backend behavior is legacy partial-update semantics: omitted fields are left unchanged.
For unplaced consumable usage, stockTotal does not include a permanent physical write-off and must not be used as available packaging inventory. Addressed consumable write-offs remain the existing warehouse process.
For unplaced consumable usage, stockTotal does not include a permanent physical write-off and must not be used as available packaging inventory. Addressed consumable write-offs remain the existing warehouse process.
For unplaced consumable usage, stockTotal does not include a permanent physical write-off and must not be used as available packaging inventory. Addressed consumable write-offs remain the existing warehouse process.
For unplaced consumable usage, stockTotal does not include a permanent physical write-off and must not be used as available packaging inventory. Addressed consumable write-offs remain the existing warehouse process.
For unplaced consumable usage, stockTotal does not include a permanent physical write-off and must not be used as available packaging inventory. Addressed consumable write-offs remain the existing warehouse process.
For unplaced consumable usage, stockTotal does not include a permanent physical write-off and must not be used as available packaging inventory. Addressed consumable write-offs remain the existing warehouse process.
Optional type_id accepts integer codes 1 ordinary, 2 composite, 3 component, 4 consumable, 5 packaging. Omission preserves the existing/default type; explicit null, empty, fractional or unknown values are rejected. Types 2/3 require composites enabled in the shop and permission to manage composition, in addition to product create/update rights. Quant packaging and links, existing composition links and dependent warehouse documents can prevent a type transition. This field does not create or replace a composition. Product and component prices are independent. Each item commits separately: a later rule failure may leave earlier items applied. Read back affected products before retrying a failed bulk request.
HTTP request example (cURL)
Set your server and token. Replace sample IDs with accessible IDs. For requests with a body, save the applicable JSON example below as request.json and replace sample values before sending. Idempotency-Key identifies one logical operation.
curl --globoff --request PATCH "${NWMS_BASE_URL}/api/v2/products" \
--header "Accept: application/json" \
--header "Authorization: Bearer ${NWMS_TOKEN}" \
--header "Idempotency-Key: ${NWMS_REQUEST_KEY}" \
--header "Content-Type: application/json" \
--data-binary @request.jsonParameters
| Name | In | Required | Type | Style | Explode | Constraints | Description | Example |
|---|---|---|---|---|---|---|---|---|
Idempotency-Key |
header | no | string |
maxLength: 191 | Optional idempotency key for API v2 write requests. Successful 2xx/3xx responses are replayed for the same key and identical payload for one day. The same key with a different payload returns 409. |
"ord-create-20260714-001" |
Request body
BulkPatchProductsRequest required
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
data |
yes | PatchProductPayload |
|
|||
data |
yes | array<PatchProductPayload> |
minItems: 1 | |
Request examples
default
{
"data": [
{
"external_id": "EXT-10045",
"shop_id": 12,
"price": 18.99,
"type_id": 3
}
]
}
Responses
200 Successful response.
Successful response.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
(root) |
no | ProductMutationResponse |
Single-object product writes return ProductResponse; array product writes return ProductCollectionResponse in the same input order for successfully processed items. |
|
||
data |
yes | Product |
Product catalog item exposed by API v2. Product barcodes identify a product or package type and are not unique SSCC values. Use /api/v2/closed-boxes to read individual sealed boxes and their current box balance. |
|
||
data.id |
yes | integer:int64 |
readOnly | min: 1 | 1001 |
|
data.created_at |
no | string:date-time | null |
"2026-07-14T10:30:00Z" |
|||
data.updated_at |
no | string:date-time | null |
"2026-07-14T10:30:00Z" |
|||
data.external_id |
no | string | null |
"EXT-10045" |
|||
data.shop_id |
no | integer:int64 | null |
min: 1 | 12 |
||
data.domain_id |
no | integer:int64 | null |
min: 1 | 3 |
||
data.status_id |
no | integer:int64 | null |
1 |
|||
data.type_id |
no | integer:int64 | null |
1 |
|||
data.name |
yes | string |
"Coffee 250g" |
|||
data.article |
no | string | null |
"ART-001245" |
|||
data.sku |
no | string | null |
"SKU-001245" |
|||
data.price |
no | number:float | null |
19.99 |
|||
data.estimated_price |
no | number:float | null |
21.5 |
|||
data.image_url |
no | string:uri | null |
"https://betawms.ffcdek.ru/offer-images/products/coffee-250g.jpg" |
|||
data.dimensions |
no | Dimensions |
|
|||
data.dimensions.x |
no | number:float | null |
min: 0 | 105 |
||
data.dimensions.y |
no | number:float | null |
min: 0 | 200 |
||
data.dimensions.z |
no | number:float | null |
min: 0 | 32 |
||
data.dimensions.unit |
yes | enum |
enum: mm | "mm" |
||
data.weight |
no | Weight |
|
|||
data.weight.value |
no | number:float | null |
min: 0 | 250 |
||
data.weight.unit |
yes | enum |
enum: g | "g" |
||
data.datamatrix_required |
yes | boolean |
false |
|||
data.expiration_required |
yes | boolean |
true |
|||
data.comment |
no | string | null |
"Keep dry" |
|||
data.description |
no | string | null |
"Ground coffee package" |
|||
data.inventory_updated_at |
no | string:date-time | null |
"2026-07-14T10:30:00Z" |
|||
data.barcodes |
no | array<ProductBarcode> |
|
|||
data.barcodes[] |
no | ProductBarcode |
|
|||
data.barcodes[].barcode |
yes | string |
minLength: 1 maxLength: 64 |
"4601234567890" |
||
data.barcodes[].is_main |
yes | boolean |
true |
|||
data.stocks |
no | ProductStocks |
|
|||
data.stocks.available_by_warehouse |
no | object |
Map keyed by warehouse ID. Values are quantities. | {"42":18.0,"43":3.0} |
||
data.stocks.physical |
no | array<ProductStockSummary> |
Physical stock rows for the product itself. | |
||
data.stocks.physical[] |
no | ProductStockSummary |
Stock totals for one warehouse. Quantities may be negative when reservations exceed physical stock. | |
||
data.stocks.physical[].warehouse_id |
yes | integer:int64 |
min: 1 | 42 |
||
data.stocks.physical[].warehouse_name |
yes | string | null |
"Warehouse Sofia" |
|||
data.stocks.physical[].stockTotal |
yes | number:float |
24.0 |
|||
data.stocks.physical[].stockReserved |
yes | number:float |
6.0 |
|||
data.stocks.physical[].stockAvailable |
yes | number:float |
18.0 |
|||
data.stocks.aggregate |
no | array<ProductStockSummary> |
Aggregated base-unit stock rows for the product and its descendant quants. | |
||
data.stocks.aggregate[] |
no | ProductStockSummary |
Stock totals for one warehouse. Quantities may be negative when reservations exceed physical stock. | |
||
data.stocks.aggregate[].warehouse_id |
yes | integer:int64 |
min: 1 | 42 |
||
data.stocks.aggregate[].warehouse_name |
yes | string | null |
"Warehouse Sofia" |
|||
data.stocks.aggregate[].stockTotal |
yes | number:float |
24.0 |
|||
data.stocks.aggregate[].stockReserved |
yes | number:float |
6.0 |
|||
data.stocks.aggregate[].stockAvailable |
yes | number:float |
18.0 |
|||
data.package |
no | object | null |
|
|||
data.package.kind |
no | string | null |
"unit" |
|||
data.package.parent_product_id |
no | integer:int64 | null |
min: 1 | 1201 |
||
data.package.base_product_id |
no | integer:int64 | null |
min: 1 | 1200 |
||
data.package.multiplicity |
no | number:float | null |
min: 0 | 6 |
||
meta |
yes | ResponseMeta |
|
|||
meta.api_version |
yes | enum |
enum: v2 | "v2" |
||
meta.deleted |
no | boolean |
true |
|||
meta.id |
no | integer:int64 | null |
min: 1 | 1001 |
||
meta.command_replayed |
no | boolean |
True when an idempotent cross-dock command returned the original successful result. | false |
||
meta.crossdock_configuration |
no | object | null |
|
|||
meta.crossdock_configuration.physical_warehouse_id |
yes | integer:int64 |
min: 1 | 42 |
||
meta.crossdock_configuration.enabled |
yes | boolean |
true |
|||
meta.crossdock_configuration.process_version |
yes | string |
"direct_order_v1" |
|||
data |
yes | array<Product> |
|
|||
data[] |
no | Product |
Product catalog item exposed by API v2. Product barcodes identify a product or package type and are not unique SSCC values. Use /api/v2/closed-boxes to read individual sealed boxes and their current box balance. |
|
||
data[].id |
yes | integer:int64 |
readOnly | min: 1 | 1001 |
|
data[].created_at |
no | string:date-time | null |
"2026-07-14T10:30:00Z" |
|||
data[].updated_at |
no | string:date-time | null |
"2026-07-14T10:30:00Z" |
|||
data[].external_id |
no | string | null |
"EXT-10045" |
|||
data[].shop_id |
no | integer:int64 | null |
min: 1 | 12 |
||
data[].domain_id |
no | integer:int64 | null |
min: 1 | 3 |
||
data[].status_id |
no | integer:int64 | null |
1 |
|||
data[].type_id |
no | integer:int64 | null |
1 |
|||
data[].name |
yes | string |
"Coffee 250g" |
|||
data[].article |
no | string | null |
"ART-001245" |
|||
data[].sku |
no | string | null |
"SKU-001245" |
|||
data[].price |
no | number:float | null |
19.99 |
|||
data[].estimated_price |
no | number:float | null |
21.5 |
|||
data[].image_url |
no | string:uri | null |
"https://betawms.ffcdek.ru/offer-images/products/coffee-250g.jpg" |
|||
data[].dimensions |
no | Dimensions |
|
|||
data[].dimensions.x |
no | number:float | null |
min: 0 | 105 |
||
data[].dimensions.y |
no | number:float | null |
min: 0 | 200 |
||
data[].dimensions.z |
no | number:float | null |
min: 0 | 32 |
||
data[].dimensions.unit |
yes | enum |
enum: mm | "mm" |
||
data[].weight |
no | Weight |
|
|||
data[].weight.value |
no | number:float | null |
min: 0 | 250 |
||
data[].weight.unit |
yes | enum |
enum: g | "g" |
||
data[].datamatrix_required |
yes | boolean |
false |
|||
data[].expiration_required |
yes | boolean |
true |
|||
data[].comment |
no | string | null |
"Keep dry" |
|||
data[].description |
no | string | null |
"Ground coffee package" |
|||
data[].inventory_updated_at |
no | string:date-time | null |
"2026-07-14T10:30:00Z" |
|||
data[].barcodes |
no | array<ProductBarcode> |
|
|||
data[].barcodes[] |
no | ProductBarcode |
|
|||
data[].barcodes[].barcode |
yes | string |
minLength: 1 maxLength: 64 |
"4601234567890" |
||
data[].barcodes[].is_main |
yes | boolean |
true |
|||
data[].stocks |
no | ProductStocks |
|
|||
data[].stocks.available_by_warehouse |
no | object |
Map keyed by warehouse ID. Values are quantities. | {"42":18.0,"43":3.0} |
||
data[].stocks.physical |
no | array<ProductStockSummary> |
Physical stock rows for the product itself. | |
||
data[].stocks.physical[] |
no | ProductStockSummary |
Stock totals for one warehouse. Quantities may be negative when reservations exceed physical stock. | |
||
data[].stocks.physical[].warehouse_id |
yes | integer:int64 |
min: 1 | 42 |
||
data[].stocks.physical[].warehouse_name |
yes | string | null |
"Warehouse Sofia" |
|||
data[].stocks.physical[].stockTotal |
yes | number:float |
24.0 |
|||
data[].stocks.physical[].stockReserved |
yes | number:float |
6.0 |
|||
data[].stocks.physical[].stockAvailable |
yes | number:float |
18.0 |
|||
data[].stocks.aggregate |
no | array<ProductStockSummary> |
Aggregated base-unit stock rows for the product and its descendant quants. | |
||
data[].stocks.aggregate[] |
no | ProductStockSummary |
Stock totals for one warehouse. Quantities may be negative when reservations exceed physical stock. | |
||
data[].stocks.aggregate[].warehouse_id |
yes | integer:int64 |
min: 1 | 42 |
||
data[].stocks.aggregate[].warehouse_name |
yes | string | null |
"Warehouse Sofia" |
|||
data[].stocks.aggregate[].stockTotal |
yes | number:float |
24.0 |
|||
data[].stocks.aggregate[].stockReserved |
yes | number:float |
6.0 |
|||
data[].stocks.aggregate[].stockAvailable |
yes | number:float |
18.0 |
|||
data[].package |
no | object | null |
|
|||
data[].package.kind |
no | string | null |
"unit" |
|||
data[].package.parent_product_id |
no | integer:int64 | null |
min: 1 | 1201 |
||
data[].package.base_product_id |
no | integer:int64 | null |
min: 1 | 1200 |
||
data[].package.multiplicity |
no | number:float | null |
min: 0 | 6 |
||
meta |
yes | ResponseMeta |
|
|||
meta.api_version |
yes | enum |
enum: v2 | "v2" |
||
meta.deleted |
no | boolean |
true |
|||
meta.id |
no | integer:int64 | null |
min: 1 | 1001 |
||
meta.command_replayed |
no | boolean |
True when an idempotent cross-dock command returned the original successful result. | false |
||
meta.crossdock_configuration |
no | object | null |
|
|||
meta.crossdock_configuration.physical_warehouse_id |
yes | integer:int64 |
min: 1 | 42 |
||
meta.crossdock_configuration.enabled |
yes | boolean |
true |
|||
meta.crossdock_configuration.process_version |
yes | string |
"direct_order_v1" |
Response examples
success
{
"data": {
"id": 1201,
"external_id": "EXT-10045",
"shop_id": 12,
"domain_id": 3,
"status_id": 1,
"type_id": 1,
"name": "Coffee 250g",
"article": "ART-001245",
"sku": "SKU-001245",
"price": 19.99,
"estimated_price": 21.5,
"image_url": "https://betawms.ffcdek.ru/offer-images/products/coffee-250g.jpg",
"dimensions": {
"x": 105,
"y": 200,
"z": 32,
"unit": "mm"
},
"weight": {
"value": 250,
"unit": "g"
},
"datamatrix_required": false,
"expiration_required": true,
"comment": "Keep dry",
"description": "Ground coffee package",
"inventory_updated_at": "2026-07-14T08:30:00Z",
"barcodes": [
{
"barcode": "4601234567890",
"is_main": true
}
],
"stocks": {
"available_by_warehouse": {
"42": 18
},
"physical": [
{
"warehouse_id": 42,
"warehouse_name": "Warehouse Sofia",
"stockTotal": 24,
"stockReserved": 6,
"stockAvailable": 18
}
],
"aggregate": [
{
"warehouse_id": 42,
"warehouse_name": "Warehouse Sofia",
"stockTotal": 30,
"stockReserved": 7,
"stockAvailable": 23
}
]
},
"package": {
"kind": "unit",
"parent_product_id": null,
"base_product_id": null,
"multiplicity": null
},
"created_at": "2026-07-14T09:00:00Z",
"updated_at": "2026-07-14T09:30:00Z"
},
"meta": {
"api_version": "v2"
}
}
400 Bad request
BadRequest
Bad request.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
message |
yes | string |
"The request could not be processed." |
|||
error |
yes | string |
"BAD_REQUEST" |
Response examples
BAD_REQUEST
{
"message": "Bad request.",
"error": "BAD_REQUEST"
}
401 Authentication required
Unauthorized
Authentication is required.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
message |
yes | string |
"The request could not be processed." |
|||
error |
yes | string |
"BAD_REQUEST" |
Response examples
AUTH_REQUIRED
{
"message": "Authentication is required.",
"error": "AUTH_REQUIRED"
}
403 Forbidden
Forbidden
The authenticated account is not allowed to perform this action.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
message |
yes | string |
"The request could not be processed." |
|||
error |
yes | string |
"BAD_REQUEST" |
Response examples
FORBIDDEN
{
"message": "The authenticated account is not allowed to perform this action.",
"error": "FORBIDDEN"
}
409 Conflict
Conflict
The request conflicts with the current resource state or idempotency key state.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
message |
yes | string |
"The request could not be processed." |
|||
error |
yes | string |
"BAD_REQUEST" |
Response examples
idempotencyConflict
{
"message": "The idempotency key is already used with a different payload.",
"error": "IDEMPOTENCY_KEY_CONFLICT"
}
422 Validation error
ValidationError
Validation failed.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
message |
yes | string |
"The given data was invalid." |
|||
error |
no | string |
"VALIDATION_ERROR" |
|||
errors |
yes | object |
{"warehouse_id":["The selected warehouse is invalid."]} |
Response examples
validationError
{
"message": "The given data was invalid.",
"error": "VALIDATION_ERROR",
"errors": {
"warehouse_id": [
"The selected warehouse is invalid."
]
}
}
429 Rate limit exceeded
TooManyRequests
Rate limit exceeded.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
message |
yes | string |
"Too Many Attempts." |
|||
error |
yes | string |
"RATE_LIMITED" |
500 Internal server error
InternalServerError
An unexpected server error occurred.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
message |
yes | string |
"The request could not be processed." |
|||
error |
yes | string |
"BAD_REQUEST" |
Response examples
INTERNAL_SERVER_ERROR
{
"message": "An unexpected server error occurred.",
"error": "INTERNAL_SERVER_ERROR"
}
Get product
| operationId | getProducts |
|---|---|
| Tag | Products |
| Security | bearerAuth |
Returns one product when it exists and is visible to the authenticated account.
HTTP request example (cURL)
Set your server and token. Replace sample IDs with accessible IDs. For requests with a body, save the applicable JSON example below as request.json and replace sample values before sending. Idempotency-Key identifies one logical operation.
curl --globoff --request GET "${NWMS_BASE_URL}/api/v2/products/1001" \
--header "Accept: application/json" \
--header "Authorization: Bearer ${NWMS_TOKEN}"Parameters
| Name | In | Required | Type | Style | Explode | Constraints | Description | Example |
|---|---|---|---|---|---|---|---|---|
id |
path | yes | integer:int64 |
min: 1 | Product ID. | 1001 |
Request body
No request body.
Responses
200 Successful response.
Successful response.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
data |
yes | Product |
Product catalog item exposed by API v2. Product barcodes identify a product or package type and are not unique SSCC values. Use /api/v2/closed-boxes to read individual sealed boxes and their current box balance. |
|
||
data.id |
yes | integer:int64 |
readOnly | min: 1 | 1001 |
|
data.created_at |
no | string:date-time | null |
"2026-07-14T10:30:00Z" |
|||
data.updated_at |
no | string:date-time | null |
"2026-07-14T10:30:00Z" |
|||
data.external_id |
no | string | null |
"EXT-10045" |
|||
data.shop_id |
no | integer:int64 | null |
min: 1 | 12 |
||
data.domain_id |
no | integer:int64 | null |
min: 1 | 3 |
||
data.status_id |
no | integer:int64 | null |
1 |
|||
data.type_id |
no | integer:int64 | null |
1 |
|||
data.name |
yes | string |
"Coffee 250g" |
|||
data.article |
no | string | null |
"ART-001245" |
|||
data.sku |
no | string | null |
"SKU-001245" |
|||
data.price |
no | number:float | null |
19.99 |
|||
data.estimated_price |
no | number:float | null |
21.5 |
|||
data.image_url |
no | string:uri | null |
"https://betawms.ffcdek.ru/offer-images/products/coffee-250g.jpg" |
|||
data.dimensions |
no | Dimensions |
|
|||
data.dimensions.x |
no | number:float | null |
min: 0 | 105 |
||
data.dimensions.y |
no | number:float | null |
min: 0 | 200 |
||
data.dimensions.z |
no | number:float | null |
min: 0 | 32 |
||
data.dimensions.unit |
yes | enum |
enum: mm | "mm" |
||
data.weight |
no | Weight |
|
|||
data.weight.value |
no | number:float | null |
min: 0 | 250 |
||
data.weight.unit |
yes | enum |
enum: g | "g" |
||
data.datamatrix_required |
yes | boolean |
false |
|||
data.expiration_required |
yes | boolean |
true |
|||
data.comment |
no | string | null |
"Keep dry" |
|||
data.description |
no | string | null |
"Ground coffee package" |
|||
data.inventory_updated_at |
no | string:date-time | null |
"2026-07-14T10:30:00Z" |
|||
data.barcodes |
no | array<ProductBarcode> |
|
|||
data.barcodes[] |
no | ProductBarcode |
|
|||
data.barcodes[].barcode |
yes | string |
minLength: 1 maxLength: 64 |
"4601234567890" |
||
data.barcodes[].is_main |
yes | boolean |
true |
|||
data.stocks |
no | ProductStocks |
|
|||
data.stocks.available_by_warehouse |
no | object |
Map keyed by warehouse ID. Values are quantities. | {"42":18.0,"43":3.0} |
||
data.stocks.physical |
no | array<ProductStockSummary> |
Physical stock rows for the product itself. | |
||
data.stocks.physical[] |
no | ProductStockSummary |
Stock totals for one warehouse. Quantities may be negative when reservations exceed physical stock. | |
||
data.stocks.physical[].warehouse_id |
yes | integer:int64 |
min: 1 | 42 |
||
data.stocks.physical[].warehouse_name |
yes | string | null |
"Warehouse Sofia" |
|||
data.stocks.physical[].stockTotal |
yes | number:float |
24.0 |
|||
data.stocks.physical[].stockReserved |
yes | number:float |
6.0 |
|||
data.stocks.physical[].stockAvailable |
yes | number:float |
18.0 |
|||
data.stocks.aggregate |
no | array<ProductStockSummary> |
Aggregated base-unit stock rows for the product and its descendant quants. | |
||
data.stocks.aggregate[] |
no | ProductStockSummary |
Stock totals for one warehouse. Quantities may be negative when reservations exceed physical stock. | |
||
data.stocks.aggregate[].warehouse_id |
yes | integer:int64 |
min: 1 | 42 |
||
data.stocks.aggregate[].warehouse_name |
yes | string | null |
"Warehouse Sofia" |
|||
data.stocks.aggregate[].stockTotal |
yes | number:float |
24.0 |
|||
data.stocks.aggregate[].stockReserved |
yes | number:float |
6.0 |
|||
data.stocks.aggregate[].stockAvailable |
yes | number:float |
18.0 |
|||
data.package |
no | object | null |
|
|||
data.package.kind |
no | string | null |
"unit" |
|||
data.package.parent_product_id |
no | integer:int64 | null |
min: 1 | 1201 |
||
data.package.base_product_id |
no | integer:int64 | null |
min: 1 | 1200 |
||
data.package.multiplicity |
no | number:float | null |
min: 0 | 6 |
||
meta |
yes | ResponseMeta |
|
|||
meta.api_version |
yes | enum |
enum: v2 | "v2" |
||
meta.deleted |
no | boolean |
true |
|||
meta.id |
no | integer:int64 | null |
min: 1 | 1001 |
||
meta.command_replayed |
no | boolean |
True when an idempotent cross-dock command returned the original successful result. | false |
||
meta.crossdock_configuration |
no | object | null |
|
|||
meta.crossdock_configuration.physical_warehouse_id |
yes | integer:int64 |
min: 1 | 42 |
||
meta.crossdock_configuration.enabled |
yes | boolean |
true |
|||
meta.crossdock_configuration.process_version |
yes | string |
"direct_order_v1" |
Response examples
success
{
"data": {
"id": 1201,
"external_id": "EXT-10045",
"shop_id": 12,
"domain_id": 3,
"status_id": 1,
"type_id": 1,
"name": "Coffee 250g",
"article": "ART-001245",
"sku": "SKU-001245",
"price": 19.99,
"estimated_price": 21.5,
"image_url": "https://betawms.ffcdek.ru/offer-images/products/coffee-250g.jpg",
"dimensions": {
"x": 105,
"y": 200,
"z": 32,
"unit": "mm"
},
"weight": {
"value": 250,
"unit": "g"
},
"datamatrix_required": false,
"expiration_required": true,
"comment": "Keep dry",
"description": "Ground coffee package",
"inventory_updated_at": "2026-07-14T08:30:00Z",
"barcodes": [
{
"barcode": "4601234567890",
"is_main": true
}
],
"stocks": {
"available_by_warehouse": {
"42": 18
},
"physical": [
{
"warehouse_id": 42,
"warehouse_name": "Warehouse Sofia",
"stockTotal": 24,
"stockReserved": 6,
"stockAvailable": 18
}
],
"aggregate": [
{
"warehouse_id": 42,
"warehouse_name": "Warehouse Sofia",
"stockTotal": 30,
"stockReserved": 7,
"stockAvailable": 23
}
]
},
"package": {
"kind": "unit",
"parent_product_id": null,
"base_product_id": null,
"multiplicity": null
},
"created_at": "2026-07-14T09:00:00Z",
"updated_at": "2026-07-14T09:30:00Z"
},
"meta": {
"api_version": "v2"
}
}
401 Authentication required
Unauthorized
Authentication is required.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
message |
yes | string |
"The request could not be processed." |
|||
error |
yes | string |
"BAD_REQUEST" |
Response examples
AUTH_REQUIRED
{
"message": "Authentication is required.",
"error": "AUTH_REQUIRED"
}
403 Forbidden
Forbidden
The authenticated account is not allowed to perform this action.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
message |
yes | string |
"The request could not be processed." |
|||
error |
yes | string |
"BAD_REQUEST" |
Response examples
FORBIDDEN
{
"message": "The authenticated account is not allowed to perform this action.",
"error": "FORBIDDEN"
}
404 Not found
NotFound
The requested resource was not found or is outside the authenticated account scope.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
message |
yes | string |
"The request could not be processed." |
|||
error |
yes | string |
"BAD_REQUEST" |
Response examples
NOT_FOUND
{
"message": "The requested resource was not found or is outside the authenticated account scope.",
"error": "NOT_FOUND"
}
429 Rate limit exceeded
TooManyRequests
Rate limit exceeded.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
message |
yes | string |
"Too Many Attempts." |
|||
error |
yes | string |
"RATE_LIMITED" |
500 Internal server error
InternalServerError
An unexpected server error occurred.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
message |
yes | string |
"The request could not be processed." |
|||
error |
yes | string |
"BAD_REQUEST" |
Response examples
INTERNAL_SERVER_ERROR
{
"message": "An unexpected server error occurred.",
"error": "INTERNAL_SERVER_ERROR"
}
Delete product
| operationId | deleteProducts |
|---|---|
| Tag | Products |
| Security | bearerAuth |
Deletes the resource when the authenticated account has permission and the current lifecycle state allows deletion. If deletion is blocked by related records or document state, the API returns a validation error.
HTTP request example (cURL)
Set your server and token. Replace sample IDs with accessible IDs. For requests with a body, save the applicable JSON example below as request.json and replace sample values before sending. Idempotency-Key identifies one logical operation.
curl --globoff --request DELETE "${NWMS_BASE_URL}/api/v2/products/1001" \
--header "Accept: application/json" \
--header "Authorization: Bearer ${NWMS_TOKEN}" \
--header "Idempotency-Key: ${NWMS_REQUEST_KEY}"Parameters
| Name | In | Required | Type | Style | Explode | Constraints | Description | Example |
|---|---|---|---|---|---|---|---|---|
id |
path | yes | integer:int64 |
min: 1 | Product ID. | 1001 |
||
Idempotency-Key |
header | no | string |
maxLength: 191 | Optional idempotency key for API v2 write requests. Successful 2xx/3xx responses are replayed for the same key and identical payload for one day. The same key with a different payload returns 409. |
"ord-create-20260714-001" |
Request body
No request body.
Responses
200 Successful response.
Successful response.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
data |
yes | object | null |
|
|||
meta |
yes | object |
|
|||
meta.api_version |
yes | enum |
enum: v2 | "v2" |
||
meta.deleted |
yes | boolean |
true |
|||
meta.id |
yes | integer:int64 |
min: 1 | 1001 |
Response examples
success
{
"data": null,
"meta": {
"api_version": "v2",
"deleted": true,
"id": 1001
}
}
400 Bad request
BadRequest
Bad request.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
message |
yes | string |
"The request could not be processed." |
|||
error |
yes | string |
"BAD_REQUEST" |
Response examples
BAD_REQUEST
{
"message": "Bad request.",
"error": "BAD_REQUEST"
}
401 Authentication required
Unauthorized
Authentication is required.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
message |
yes | string |
"The request could not be processed." |
|||
error |
yes | string |
"BAD_REQUEST" |
Response examples
AUTH_REQUIRED
{
"message": "Authentication is required.",
"error": "AUTH_REQUIRED"
}
403 Forbidden
Forbidden
The authenticated account is not allowed to perform this action.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
message |
yes | string |
"The request could not be processed." |
|||
error |
yes | string |
"BAD_REQUEST" |
Response examples
FORBIDDEN
{
"message": "The authenticated account is not allowed to perform this action.",
"error": "FORBIDDEN"
}
404 Not found
NotFound
The requested resource was not found or is outside the authenticated account scope.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
message |
yes | string |
"The request could not be processed." |
|||
error |
yes | string |
"BAD_REQUEST" |
Response examples
NOT_FOUND
{
"message": "The requested resource was not found or is outside the authenticated account scope.",
"error": "NOT_FOUND"
}
409 Conflict
Conflict
The request conflicts with the current resource state or idempotency key state.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
message |
yes | string |
"The request could not be processed." |
|||
error |
yes | string |
"BAD_REQUEST" |
Response examples
idempotencyConflict
{
"message": "The idempotency key is already used with a different payload.",
"error": "IDEMPOTENCY_KEY_CONFLICT"
}
422 Validation error
ValidationError
Validation failed.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
message |
yes | string |
"The given data was invalid." |
|||
error |
no | string |
"VALIDATION_ERROR" |
|||
errors |
yes | object |
{"warehouse_id":["The selected warehouse is invalid."]} |
Response examples
validationError
{
"message": "The given data was invalid.",
"error": "VALIDATION_ERROR",
"errors": {
"warehouse_id": [
"The selected warehouse is invalid."
]
}
}
429 Rate limit exceeded
TooManyRequests
Rate limit exceeded.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
message |
yes | string |
"Too Many Attempts." |
|||
error |
yes | string |
"RATE_LIMITED" |
500 Internal server error
InternalServerError
An unexpected server error occurred.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
message |
yes | string |
"The request could not be processed." |
|||
error |
yes | string |
"BAD_REQUEST" |
Response examples
INTERNAL_SERVER_ERROR
{
"message": "An unexpected server error occurred.",
"error": "INTERNAL_SERVER_ERROR"
}
List physical stock lots
| operationId | getProductStockLots |
|---|---|
| Tag | Products |
| Security | bearerAuth |
Read physical placed stock per warehouse, place and lot in this product's units. Unplaced reservations are excluded because they have no actual lot yet. Aggregate physical_quantity by warehouse and lot for reconciliation. Results use current product visibility and container rules, standard pagination (maximum 500) and no snapshot isolation.
Unknown read parameters are ignored. per_page is silently capped; use meta.per_page. Date-only boundaries follow the installation's stored calendar dates; ISO timestamps are serialized in UTC. The default general API limit is 600/minute; a user-specific override may apply. Its bucket is shared by authenticated user, not by shop/domain; Retry-After is returned on throttling.
HTTP request example (cURL)
Set your server and token. Replace sample IDs with accessible IDs. For requests with a body, save the applicable JSON example below as request.json and replace sample values before sending. Idempotency-Key identifies one logical operation.
curl --globoff --request GET "${NWMS_BASE_URL}/api/v2/products/1001/stock-lots" \
--header "Accept: application/json" \
--header "Authorization: Bearer ${NWMS_TOKEN}"Parameters
| Name | In | Required | Type | Style | Explode | Constraints | Description | Example |
|---|---|---|---|---|---|---|---|---|
id |
path | yes | integer:int64 |
min: 1 | Product ID. | 1001 |
||
page |
query | no | integer:int64 |
form | true | min: 1 | Page number. The same filter may also be sent as filter[page] where supported by the Laravel query adapter. |
1 |
per_page |
query | no | integer:int64 |
form | true | min: 1 max: 500 |
Number of records per page. Default is 100; maximum is 500. The same filter may also be sent as filter[per_page] where supported by the Laravel query adapter. |
100 |
sort |
query | no | string |
form | false | pattern: ^-?[A-Za-z0-9_]+(,-?[A-Za-z0-9_]+)*$ | Comma-separated sort fields. Prefix with - for descending order. Allowed fields: id, warehouse_id, expiration_date. Default: id. The same filter may also be sent as filter[sort] where supported by the Laravel query adapter. |
"id" |
warehouse_id |
query | no | integer:int64 |
form | true | min: 1 | Filter by warehouse_id using exact matching. The same filter may also be sent as filter[warehouse_id] where supported by the Laravel query adapter. |
42 |
batch |
query | no | string |
form | true | Filter by batch using partial text matching. The same filter may also be sent as filter[batch] where supported by the Laravel query adapter. |
"LOT-2026" |
|
production_date_from |
query | no | string:date |
form | true | Lower inclusive date boundary for production_date. The same filter may also be sent as filter[production_date_from] where supported by the Laravel query adapter. |
"2026-07-14" |
|
production_date_to |
query | no | string:date |
form | true | Upper inclusive date boundary for production_date. The same filter may also be sent as filter[production_date_to] where supported by the Laravel query adapter. |
"2026-07-14" |
|
expiration_date_from |
query | no | string:date |
form | true | Lower inclusive date boundary for expiration_date. The same filter may also be sent as filter[expiration_date_from] where supported by the Laravel query adapter. |
"2026-07-14" |
|
expiration_date_to |
query | no | string:date |
form | true | Upper inclusive date boundary for expiration_date. The same filter may also be sent as filter[expiration_date_to] where supported by the Laravel query adapter. |
"2026-07-14" |
Request body
No request body.
Responses
200 Successful response.
Successful response.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
data |
yes | array<StockLot> |
|
|||
data[] |
no | StockLot |
One active positive placed stock slice in product units, using the same container visibility rules as product stock. Does not allocate unplaced reservations to lots. Not a snapshot cursor or append-only feed. | |
||
data[].batch |
yes | string | null |
"LOT-2026" |
|||
data[].production_date |
yes | string:date | null |
"2026-01-15" |
|||
data[].expiration_date |
yes | string:date | null |
"2027-01-15" |
|||
data[].id |
yes | integer:int64 |
min: 1 | 5001 |
||
data[].product_id |
yes | integer:int64 |
min: 1 | 1201 |
||
data[].warehouse_id |
yes | integer:int64 |
min: 1 | 734 |
||
data[].place_id |
yes | integer:int64 |
min: 1 | 501 |
||
data[].physical_quantity |
yes | number:float |
min: 0 | 12 |
||
links |
yes | PaginationLinks |
|
|||
links.first |
yes | string:uri | null |
"https://betawms.ffcdek.ru/api/v2/orders?page=1" |
|||
links.last |
yes | string:uri | null |
"https://betawms.ffcdek.ru/api/v2/orders?page=2" |
|||
links.prev |
yes | string:uri | null |
|
|||
links.next |
yes | string:uri | null |
"https://betawms.ffcdek.ru/api/v2/orders?page=2" |
|||
meta |
yes | PaginationMeta |
|
|||
meta.api_version |
yes | enum |
enum: v2 | "v2" |
||
meta.current_page |
yes | integer:int32 |
min: 1 | 1 |
||
meta.from |
yes | integer:int32 | null |
min: 1 | 1 |
||
meta.last_page |
yes | integer:int32 |
min: 1 | 2 |
||
meta.path |
yes | string:uri |
"https://betawms.ffcdek.ru/api/v2/orders" |
|||
meta.per_page |
yes | integer:int32 |
min: 1 | 50 |
||
meta.to |
yes | integer:int32 | null |
min: 1 | 50 |
||
meta.total |
yes | integer:int32 |
min: 0 | 73 |
Response examples
success
{
"data": [
{
"batch": "LOT-2026",
"production_date": "2026-01-15",
"expiration_date": "2027-01-15",
"id": 5001,
"product_id": 1201,
"warehouse_id": 734,
"place_id": 501,
"physical_quantity": 12
}
],
"links": {
"first": "https://betawms.ffcdek.ru/api/v2/products/5101/stock-lots?page=1",
"last": "https://betawms.ffcdek.ru/api/v2/products/5101/stock-lots?page=2",
"prev": null,
"next": "https://betawms.ffcdek.ru/api/v2/products/5101/stock-lots?page=2"
},
"meta": {
"api_version": "v2",
"current_page": 1,
"from": 1,
"last_page": 2,
"path": "https://betawms.ffcdek.ru/api/v2/products/5101/stock-lots",
"per_page": 100,
"to": 100,
"total": 123
}
}
401 Authentication required
Unauthorized
Authentication is required.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
message |
yes | string |
"The request could not be processed." |
|||
error |
yes | string |
"BAD_REQUEST" |
Response examples
AUTH_REQUIRED
{
"message": "Authentication is required.",
"error": "AUTH_REQUIRED"
}
403 Forbidden
Forbidden
The authenticated account is not allowed to perform this action.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
message |
yes | string |
"The request could not be processed." |
|||
error |
yes | string |
"BAD_REQUEST" |
Response examples
FORBIDDEN
{
"message": "The authenticated account is not allowed to perform this action.",
"error": "FORBIDDEN"
}
404 Not found
NotFound
The requested resource was not found or is outside the authenticated account scope.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
message |
yes | string |
"The request could not be processed." |
|||
error |
yes | string |
"BAD_REQUEST" |
Response examples
NOT_FOUND
{
"message": "The requested resource was not found or is outside the authenticated account scope.",
"error": "NOT_FOUND"
}
429 Rate limit exceeded
TooManyRequests
Rate limit exceeded.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
message |
yes | string |
"Too Many Attempts." |
|||
error |
yes | string |
"RATE_LIMITED" |
500 Internal server error
InternalServerError
An unexpected server error occurred.
| Field | Required | Type | Flags | Constraints | Description | Example |
|---|---|---|---|---|---|---|
message |
yes | string |
"The request could not be processed." |
|||
error |
yes | string |
"BAD_REQUEST" |
Response examples
INTERNAL_SERVER_ERROR
{
"message": "An unexpected server error occurred.",
"error": "INTERNAL_SERVER_ERROR"
}