API reference
Boxes
A box is a storage unit you rent out: a size, a price and a number of identical units. Boxes live in a location.
Note
Boxes start as
DRAFT. The API can only
ask for a review (
submit); BoxLatvia staff publish. See
Publishing .
# The Box objectName Type Description iduuid BoxLatvia's identifier. external_idstring | null Your identifier. location_iduuid | null The location it belongs to. namestring Name. descriptionstring | null Description. statusstring DRAFT, REVIEW, PUBLISHED or UNPUBLISHED. Read-only: use submit and withdraw .price promotional_pricenumber | null Price per period, and the discounted price if there is one. price_period_1 _3 _6 _12number | null Fixed totals for renting 1, 3, 6 or 12 periods, if set. price_unitstring | null Price unit code. size size_length size_width size_height door_width door_heightnumber | null Floor area in m², inner dimensions and door size in metres. total_units available_unitsinteger | null Number of identical units, and how many are free to book. max_advance_daysinteger | null How many days ahead a customer may book. featuresstring[] Amenity codes. missingstring[] What is still needed before the box can be submitted for review: any of description, price, price_unit, size, features. created_at updated_atdatetime ISO 8601, UTC.
Example
Copy {
"id" : "c4883b4a-4e38-47bf-8a69-ef025917e022" ,
"external_id" : "SKU-3M" ,
"location_id" : "9d80d5f4-d17a-4f3e-99f6-4ab43b132bc3" ,
"name" : "Box 3 m²" ,
"description" : "Sausa un apsildāma telpa pirmajā stāvā" ,
"status" : "DRAFT" ,
"price" : 29.9 ,
"promotional_price" : null ,
"price_period_1" : null ,
"price_period_3" : 79 ,
"price_period_6" : null ,
"price_period_12" : 290 ,
"price_unit" : "menesi" ,
"size" : 3 ,
"size_length" : 2 ,
"size_width" : 1.5 ,
"size_height" : 2.4 ,
"door_width" : 0.9 ,
"door_height" : 2 ,
"total_units" : 10 ,
"available_units" : 10 ,
"max_advance_days" : 60 ,
"features" : [
"apkure" ,
"viedatslega"
],
"missing" : [],
"created_at" : "2026-11-07T09:30:12.418Z" ,
"updated_at" : "2026-11-07T09:30:12.418Z"
}
# Endpoints
# List boxes
GET /v1/boxesboxes:read
Your boxes, oldest first, one page at a time.
Query parameters Name Type Description limitinteger Page size, 1 to 200. Default 50. cursorstring The next_cursor of the previous page. Do not build cursors yourself. location_iduuid Only boxes of this location. external_idstring Only the box with this identifier of yours. statusstring DRAFT, REVIEW, PUBLISHED or UNPUBLISHED.
Example
Boxes of one location, one per page
cURL JavaScript Python
Copy curl "$API/v1/boxes?location_id=9d80d5f4-d17a-4f3e-99f6-4ab43b132bc3&limit=1" \
-u "$CLIENT_ID:$CLIENT_SECRET"
Copy const API = "https://api.boxlatvia.lv" ;
const auth = "Basic " + btoa(`${CLIENT_ID}:${CLIENT_SECRET}` );
const res = await fetch(`${API}/v1/boxes?location_id=9d80d5f4-d17a-4f3e-99f6-4ab43b132bc3&limit=1` , {
headers: { Authorization: auth },
});
console.log(res.status, await res.json());
Copy import requests
API = "https://api.boxlatvia.lv"
r = requests.get(
f"{API}/v1/boxes?location_id=9d80d5f4-d17a-4f3e-99f6-4ab43b132bc3&limit=1" ,
auth=(CLIENT_ID, CLIENT_SECRET),
)
print (r.status_code, r.json())
200 OK
Copy {
"data" : [
{
"id" : "c4883b4a-4e38-47bf-8a69-ef025917e022" ,
"external_id" : "SKU-3M" ,
"location_id" : "9d80d5f4-d17a-4f3e-99f6-4ab43b132bc3" ,
"name" : "Box 3 m²" ,
"description" : "Sausa un apsildāma telpa pirmajā stāvā" ,
"status" : "DRAFT" ,
"price" : 29.9 ,
"promotional_price" : null ,
"price_period_1" : null ,
"price_period_3" : 79 ,
"price_period_6" : null ,
"price_period_12" : 290 ,
"price_unit" : "menesi" ,
"size" : 3 ,
"size_length" : 2 ,
"size_width" : 1.5 ,
"size_height" : 2.4 ,
"door_width" : 0.9 ,
"door_height" : 2 ,
"total_units" : 10 ,
"available_units" : 10 ,
"max_advance_days" : 60 ,
"features" : [
"apkure" ,
"viedatslega"
],
"missing" : [],
"created_at" : "2026-11-07T09:30:12.418Z" ,
"updated_at" : "2026-11-07T09:30:12.418Z"
}
],
"next_cursor" : "WyIyMDI2LTExLTA2IDEwOjAwOjAwLjAwMDAxMyswMCIsIjdiOGEzODI3LTI2M2UtNGMyMC1iNDQ3LWFkMjE5YmU1NWE0YyJd"
}
Errors Status When 422 location_id or status is not valid.
# Create a box
POST /v1/boxesboxes:write
Creates a box as a DRAFT. It needs a name and a location; the rest can follow.
Query parameters Name Type Description validate_onlyboolean true runs every check and returns what would happen, but saves nothing. See dry runs .
Headers Name Type Description Idempotency-Keystring Optional. 1 to 200 printable characters. A retry with the same key returns the original result instead of creating a duplicate. See safe retries .
Request body Name Type Description name required string Up to 255 characters. Required when creating. location_iduuid The location the box is in. One of location_id or location_external_id is required when creating; later it moves the box to another of your locations. location_external_idstring The same, by your own id for the location. Not together with location_id. descriptionstring | null Up to 255 characters. external_idstring Your own identifier, up to 100 characters, unique among your boxes. Set once. pricenumber | null Price per period, more than 0, at most 2 decimals (never rounded for you). promotional_pricenumber | null Discounted price. Must be lower than price. price_period_1 _3 _6 _12number | null Optional fixed total for renting 1, 3, 6 or 12 periods. When set, the customer pays exactly this amount for that period. price_unitstring | null Price unit code (per month, per week), see Reference data . sizenumber | null Floor area in m², more than 0, rounded to 2 decimals. size_length size_width size_heightnumber | null Inner dimensions in metres. door_width door_heightnumber | null Door dimensions in metres. total_unitsinteger | null How many identical boxes you have, 0 or more. available_unitsinteger | null How many are free to rent right now. Cannot exceed total_units. max_advance_daysinteger | null How many days ahead a customer may book, 1 to 730. featuresstring[] Amenity codes, see Reference data . Replaces the whole set.
Examples
A complete box
cURL JavaScript Python
Copy curl -X POST "$API/v1/boxes" \
-u "$CLIENT_ID:$CLIENT_SECRET" \
-H "Content-Type: application/json" \
-d '{
"name": "Box 3 m²",
"description": "Sausa un apsildāma telpa pirmajā stāvā",
"location_external_id": "WH-IMANTA",
"external_id": "SKU-3M",
"price": 29.9,
"price_unit": "menesi",
"price_period_3": 79,
"price_period_12": 290,
"size": 3,
"size_length": 2,
"size_width": 1.5,
"size_height": 2.4,
"door_width": 0.9,
"door_height": 2,
"total_units": 10,
"available_units": 10,
"max_advance_days": 60,
"features": [
"apkure",
"viedatslega"
]
}'
Copy const API = "https://api.boxlatvia.lv" ;
const auth = "Basic " + btoa(`${CLIENT_ID}:${CLIENT_SECRET}` );
const res = await fetch(`${API}/v1/boxes` , {
method: "POST" ,
headers: { Authorization: auth, "Content-Type" : "application/json" },
body: JSON.stringify({
"name" : "Box 3 m²" ,
"description" : "Sausa un apsildāma telpa pirmajā stāvā" ,
"location_external_id" : "WH-IMANTA" ,
"external_id" : "SKU-3M" ,
"price" : 29.9,
"price_unit" : "menesi" ,
"price_period_3" : 79,
"price_period_12" : 290,
"size" : 3,
"size_length" : 2,
"size_width" : 1.5,
"size_height" : 2.4,
"door_width" : 0.9,
"door_height" : 2,
"total_units" : 10,
"available_units" : 10,
"max_advance_days" : 60,
"features" : [
"apkure" ,
"viedatslega"
]
}),
});
console.log(res.status, await res.json());
Copy import requests
API = "https://api.boxlatvia.lv"
r = requests.post(
f"{API}/v1/boxes" ,
auth=(CLIENT_ID, CLIENT_SECRET),
json={
"name" : "Box 3 m²" ,
"description" : "Sausa un apsildāma telpa pirmajā stāvā" ,
"location_external_id" : "WH-IMANTA" ,
"external_id" : "SKU-3M" ,
"price" : 29.9,
"price_unit" : "menesi" ,
"price_period_3" : 79,
"price_period_12" : 290,
"size" : 3,
"size_length" : 2,
"size_width" : 1.5,
"size_height" : 2.4,
"door_width" : 0.9,
"door_height" : 2,
"total_units" : 10,
"available_units" : 10,
"max_advance_days" : 60,
"features" : [
"apkure" ,
"viedatslega" ,
],
},
)
print (r.status_code, r.json())
201 Created ETag: W/"2daf00d492dd4f4cd82f2113"Location: https://api.boxlatvia.lv/v1/boxes/c4883b4a-4e38-47bf-8a69-ef025917e022
Copy {
"id" : "c4883b4a-4e38-47bf-8a69-ef025917e022" ,
"external_id" : "SKU-3M" ,
"location_id" : "9d80d5f4-d17a-4f3e-99f6-4ab43b132bc3" ,
"name" : "Box 3 m²" ,
"description" : "Sausa un apsildāma telpa pirmajā stāvā" ,
"status" : "DRAFT" ,
"price" : 29.9 ,
"promotional_price" : null ,
"price_period_1" : null ,
"price_period_3" : 79 ,
"price_period_6" : null ,
"price_period_12" : 290 ,
"price_unit" : "menesi" ,
"size" : 3 ,
"size_length" : 2 ,
"size_width" : 1.5 ,
"size_height" : 2.4 ,
"door_width" : 0.9 ,
"door_height" : 2 ,
"total_units" : 10 ,
"available_units" : 10 ,
"max_advance_days" : 60 ,
"features" : [
"apkure" ,
"viedatslega"
],
"missing" : [],
"created_at" : "2026-11-07T09:30:12.418Z" ,
"updated_at" : "2026-11-07T09:30:12.418Z"
}
Only a name and a location
missing lists what the box needs before it can be submitted.
cURL JavaScript Python
Copy curl -X POST "$API/v1/boxes" \
-u "$CLIENT_ID:$CLIENT_SECRET" \
-H "Content-Type: application/json" \
-d '{
"name": "Box 5 m²",
"location_id": "9d80d5f4-d17a-4f3e-99f6-4ab43b132bc3"
}'
Copy const API = "https://api.boxlatvia.lv" ;
const auth = "Basic " + btoa(`${CLIENT_ID}:${CLIENT_SECRET}` );
const res = await fetch(`${API}/v1/boxes` , {
method: "POST" ,
headers: { Authorization: auth, "Content-Type" : "application/json" },
body: JSON.stringify({
"name" : "Box 5 m²" ,
"location_id" : "9d80d5f4-d17a-4f3e-99f6-4ab43b132bc3"
}),
});
console.log(res.status, await res.json());
Copy import requests
API = "https://api.boxlatvia.lv"
r = requests.post(
f"{API}/v1/boxes" ,
auth=(CLIENT_ID, CLIENT_SECRET),
json={
"name" : "Box 5 m²" ,
"location_id" : "9d80d5f4-d17a-4f3e-99f6-4ab43b132bc3" ,
},
)
print (r.status_code, r.json())
201 Created ETag: W/"9c02eeec9c920097d539244a"Location: https://api.boxlatvia.lv/v1/boxes/9d7f53d3-a9cd-4ee1-b9cf-470a572f8b4e
Copy {
"id" : "9d7f53d3-a9cd-4ee1-b9cf-470a572f8b4e" ,
"external_id" : null ,
"location_id" : "9d80d5f4-d17a-4f3e-99f6-4ab43b132bc3" ,
"name" : "Box 5 m²" ,
"description" : null ,
"status" : "DRAFT" ,
"price" : null ,
"promotional_price" : null ,
"price_period_1" : null ,
"price_period_3" : null ,
"price_period_6" : null ,
"price_period_12" : null ,
"price_unit" : null ,
"size" : null ,
"size_length" : null ,
"size_width" : null ,
"size_height" : null ,
"door_width" : null ,
"door_height" : null ,
"total_units" : null ,
"available_units" : null ,
"max_advance_days" : null ,
"features" : [],
"missing" : [
"description" ,
"price" ,
"price_unit" ,
"size" ,
"features"
],
"created_at" : "2026-11-07T09:30:12.418Z" ,
"updated_at" : "2026-11-07T09:30:12.418Z"
}
Invalid values
cURL JavaScript Python
Copy curl -X POST "$API/v1/boxes" \
-u "$CLIENT_ID:$CLIENT_SECRET" \
-H "Content-Type: application/json" \
-d '{
"name": "Box 2 m²",
"location_id": "9d80d5f4-d17a-4f3e-99f6-4ab43b132bc3",
"price": 10.005,
"total_units": 1.5
}'
Copy const API = "https://api.boxlatvia.lv" ;
const auth = "Basic " + btoa(`${CLIENT_ID}:${CLIENT_SECRET}` );
const res = await fetch(`${API}/v1/boxes` , {
method: "POST" ,
headers: { Authorization: auth, "Content-Type" : "application/json" },
body: JSON.stringify({
"name" : "Box 2 m²" ,
"location_id" : "9d80d5f4-d17a-4f3e-99f6-4ab43b132bc3" ,
"price" : 10.005,
"total_units" : 1.5
}),
});
console.log(res.status, await res.json());
Copy import requests
API = "https://api.boxlatvia.lv"
r = requests.post(
f"{API}/v1/boxes" ,
auth=(CLIENT_ID, CLIENT_SECRET),
json={
"name" : "Box 2 m²" ,
"location_id" : "9d80d5f4-d17a-4f3e-99f6-4ab43b132bc3" ,
"price" : 10.005,
"total_units" : 1.5,
},
)
print (r.status_code, r.json())
422 Unprocessable Content
Copy {
"type" : "https://api.boxlatvia.lv/problems/validation-failed" ,
"title" : "Validation failed" ,
"status" : 422 ,
"detail" : "One or more fields are invalid." ,
"request_id" : "88e536cb-521e-4728-802c-8a7b667fa4af" ,
"errors" : [
{
"field" : "price" ,
"message" : "must have at most 2 decimals"
},
{
"field" : "total_units" ,
"message" : "Expected integer, received float"
}
]
}
Rules between fields and unknown codes
These checks look at the final values together: the promotional price, the stock figures, the codes.
cURL JavaScript Python
Copy curl -X POST "$API/v1/boxes" \
-u "$CLIENT_ID:$CLIENT_SECRET" \
-H "Content-Type: application/json" \
-d '{
"name": "Box 2 m²",
"location_id": "9d80d5f4-d17a-4f3e-99f6-4ab43b132bc3",
"price": 10,
"promotional_price": 12,
"total_units": 5,
"available_units": 8,
"price_unit": "gadsimts",
"features": [
"sauna"
]
}'
Copy const API = "https://api.boxlatvia.lv" ;
const auth = "Basic " + btoa(`${CLIENT_ID}:${CLIENT_SECRET}` );
const res = await fetch(`${API}/v1/boxes` , {
method: "POST" ,
headers: { Authorization: auth, "Content-Type" : "application/json" },
body: JSON.stringify({
"name" : "Box 2 m²" ,
"location_id" : "9d80d5f4-d17a-4f3e-99f6-4ab43b132bc3" ,
"price" : 10,
"promotional_price" : 12,
"total_units" : 5,
"available_units" : 8,
"price_unit" : "gadsimts" ,
"features" : [
"sauna"
]
}),
});
console.log(res.status, await res.json());
Copy import requests
API = "https://api.boxlatvia.lv"
r = requests.post(
f"{API}/v1/boxes" ,
auth=(CLIENT_ID, CLIENT_SECRET),
json={
"name" : "Box 2 m²" ,
"location_id" : "9d80d5f4-d17a-4f3e-99f6-4ab43b132bc3" ,
"price" : 10,
"promotional_price" : 12,
"total_units" : 5,
"available_units" : 8,
"price_unit" : "gadsimts" ,
"features" : [
"sauna" ,
],
},
)
print (r.status_code, r.json())
422 Unprocessable Content
Copy {
"type" : "https://api.boxlatvia.lv/problems/validation-failed" ,
"title" : "Validation failed" ,
"status" : 422 ,
"detail" : "One or more fields are invalid." ,
"request_id" : "ce26822d-d546-4254-a374-08549ad6e04b" ,
"errors" : [
{
"field" : "price_unit" ,
"message" : "is not a known price unit code"
},
{
"field" : "features" ,
"message" : "unknown or inactive feature code(s): sauna"
},
{
"field" : "promotional_price" ,
"message" : "must be lower than price"
},
{
"field" : "available_units" ,
"message" : "cannot be greater than total_units"
}
]
}
The status cannot be set
cURL JavaScript Python
Copy curl -X POST "$API/v1/boxes" \
-u "$CLIENT_ID:$CLIENT_SECRET" \
-H "Content-Type: application/json" \
-d '{
"name": "Box 2 m²",
"location_id": "9d80d5f4-d17a-4f3e-99f6-4ab43b132bc3",
"status": "PUBLISHED"
}'
Copy const API = "https://api.boxlatvia.lv" ;
const auth = "Basic " + btoa(`${CLIENT_ID}:${CLIENT_SECRET}` );
const res = await fetch(`${API}/v1/boxes` , {
method: "POST" ,
headers: { Authorization: auth, "Content-Type" : "application/json" },
body: JSON.stringify({
"name" : "Box 2 m²" ,
"location_id" : "9d80d5f4-d17a-4f3e-99f6-4ab43b132bc3" ,
"status" : "PUBLISHED"
}),
});
console.log(res.status, await res.json());
Copy import requests
API = "https://api.boxlatvia.lv"
r = requests.post(
f"{API}/v1/boxes" ,
auth=(CLIENT_ID, CLIENT_SECRET),
json={
"name" : "Box 2 m²" ,
"location_id" : "9d80d5f4-d17a-4f3e-99f6-4ab43b132bc3" ,
"status" : "PUBLISHED" ,
},
)
print (r.status_code, r.json())
422 Unprocessable Content
Copy {
"type" : "https://api.boxlatvia.lv/problems/validation-failed" ,
"title" : "Validation failed" ,
"status" : 422 ,
"detail" : "One or more fields are invalid." ,
"request_id" : "921b4ebd-4e17-4232-9475-c3e76ac97f43" ,
"errors" : [
{
"field" : "status" ,
"message" : "cannot be set directly; use POST /v1/boxes/{id}/submit (or /withdraw)"
}
]
}
Errors Status When 409 external_id is already used by another of your boxes.422 A value is invalid, a code is unknown, or the location is not yours.
# Retrieve a box
GET /v1/boxes/{id}boxes:read
One box, with the ETag you need to change or delete it.
Path parameters Name Type Description iduuid The box's BoxLatvia id.
Example
Retrieve
cURL JavaScript Python
Copy curl "$API/v1/boxes/c4883b4a-4e38-47bf-8a69-ef025917e022" \
-u "$CLIENT_ID:$CLIENT_SECRET"
Copy const API = "https://api.boxlatvia.lv" ;
const auth = "Basic " + btoa(`${CLIENT_ID}:${CLIENT_SECRET}` );
const res = await fetch(`${API}/v1/boxes/c4883b4a-4e38-47bf-8a69-ef025917e022` , {
headers: { Authorization: auth },
});
console.log(res.status, await res.json());
Copy import requests
API = "https://api.boxlatvia.lv"
r = requests.get(
f"{API}/v1/boxes/c4883b4a-4e38-47bf-8a69-ef025917e022" ,
auth=(CLIENT_ID, CLIENT_SECRET),
)
print (r.status_code, r.json())
200 OK ETag: W/"2daf00d492dd4f4cd82f2113"
Copy {
"id" : "c4883b4a-4e38-47bf-8a69-ef025917e022" ,
"external_id" : "SKU-3M" ,
"location_id" : "9d80d5f4-d17a-4f3e-99f6-4ab43b132bc3" ,
"name" : "Box 3 m²" ,
"description" : "Sausa un apsildāma telpa pirmajā stāvā" ,
"status" : "DRAFT" ,
"price" : 29.9 ,
"promotional_price" : null ,
"price_period_1" : null ,
"price_period_3" : 79 ,
"price_period_6" : null ,
"price_period_12" : 290 ,
"price_unit" : "menesi" ,
"size" : 3 ,
"size_length" : 2 ,
"size_width" : 1.5 ,
"size_height" : 2.4 ,
"door_width" : 0.9 ,
"door_height" : 2 ,
"total_units" : 10 ,
"available_units" : 10 ,
"max_advance_days" : 60 ,
"features" : [
"apkure" ,
"viedatslega"
],
"missing" : [],
"created_at" : "2026-11-07T09:30:12.418Z" ,
"updated_at" : "2026-11-07T09:30:12.418Z"
}
Errors Status When 404 No such box of yours.
# Update a box
PATCH /v1/boxes/{id}boxes:write
Changes only the fields you send, including stock (available_units) and prices. Changes to a published box are visible on the website straight away.
Path parameters Name Type Description iduuid The box's BoxLatvia id.
Query parameters Name Type Description validate_onlyboolean true runs every check and returns what would happen, but saves nothing. See dry runs .
Headers Name Type Description If-Match required string The ETag from a previous GET, exactly as received. 428 if missing, 412 if the object changed since. See conditional updates .
Request body (any of the fields) Name Type Description name required string Up to 255 characters. Required when creating. location_iduuid The location the box is in. One of location_id or location_external_id is required when creating; later it moves the box to another of your locations. location_external_idstring The same, by your own id for the location. Not together with location_id. descriptionstring | null Up to 255 characters. external_idstring Your own identifier, up to 100 characters, unique among your boxes. Set once. pricenumber | null Price per period, more than 0, at most 2 decimals (never rounded for you). promotional_pricenumber | null Discounted price. Must be lower than price. price_period_1 _3 _6 _12number | null Optional fixed total for renting 1, 3, 6 or 12 periods. When set, the customer pays exactly this amount for that period. price_unitstring | null Price unit code (per month, per week), see Reference data . sizenumber | null Floor area in m², more than 0, rounded to 2 decimals. size_length size_width size_heightnumber | null Inner dimensions in metres. door_width door_heightnumber | null Door dimensions in metres. total_unitsinteger | null How many identical boxes you have, 0 or more. available_unitsinteger | null How many are free to rent right now. Cannot exceed total_units. max_advance_daysinteger | null How many days ahead a customer may book, 1 to 730. featuresstring[] Amenity codes, see Reference data . Replaces the whole set.
Example
Update stock and price
cURL JavaScript Python
Copy curl -X PATCH "$API/v1/boxes/c4883b4a-4e38-47bf-8a69-ef025917e022" \
-u "$CLIENT_ID:$CLIENT_SECRET" \
-H "Content-Type: application/json" \
-H "If-Match: W/\"2daf00d492dd4f4cd82f2113\"" \
-d '{
"available_units": 7,
"price": 31.5
}'
Copy const API = "https://api.boxlatvia.lv" ;
const auth = "Basic " + btoa(`${CLIENT_ID}:${CLIENT_SECRET}` );
const res = await fetch(`${API}/v1/boxes/c4883b4a-4e38-47bf-8a69-ef025917e022` , {
method: "PATCH" ,
headers: { Authorization: auth, "Content-Type" : "application/json" , "If-Match" : "W/\"2daf00d492dd4f4cd82f2113\"" },
body: JSON.stringify({
"available_units" : 7,
"price" : 31.5
}),
});
console.log(res.status, await res.json());
Copy import requests
API = "https://api.boxlatvia.lv"
r = requests.patch(
f"{API}/v1/boxes/c4883b4a-4e38-47bf-8a69-ef025917e022" ,
auth=(CLIENT_ID, CLIENT_SECRET),
headers={"If-Match" : "W/\"2daf00d492dd4f4cd82f2113\"" },
json={
"available_units" : 7,
"price" : 31.5,
},
)
print (r.status_code, r.json())
200 OK ETag: W/"0a057478c432265c169ba82b"
Copy {
"id" : "c4883b4a-4e38-47bf-8a69-ef025917e022" ,
"external_id" : "SKU-3M" ,
"location_id" : "9d80d5f4-d17a-4f3e-99f6-4ab43b132bc3" ,
"name" : "Box 3 m²" ,
"description" : "Sausa un apsildāma telpa pirmajā stāvā" ,
"status" : "DRAFT" ,
"price" : 31.5 ,
"promotional_price" : null ,
"price_period_1" : null ,
"price_period_3" : 79 ,
"price_period_6" : null ,
"price_period_12" : 290 ,
"price_unit" : "menesi" ,
"size" : 3 ,
"size_length" : 2 ,
"size_width" : 1.5 ,
"size_height" : 2.4 ,
"door_width" : 0.9 ,
"door_height" : 2 ,
"total_units" : 10 ,
"available_units" : 7 ,
"max_advance_days" : 60 ,
"features" : [
"apkure" ,
"viedatslega"
],
"missing" : [],
"created_at" : "2026-11-07T09:30:12.418Z" ,
"updated_at" : "2026-11-07T14:05:41.092Z"
}
Errors Status When 404 No such box of yours. 412 The box changed since you read it. 422 A value is invalid or breaks a rule between fields. 428 If-Match is missing.
# Create or update by your own id
PUT /v1/boxes/by-external-id/{external_id}boxes:write
Creates the box if your id is new (then name and a location are required), otherwise updates the fields you send. No If-Match: ideal for stock and price feeds.
Path parameters Name Type Description external_idstring Your identifier for the box.
Query parameters Name Type Description validate_onlyboolean true runs every check and returns what would happen, but saves nothing. See dry runs .
Request body Name Type Description name required string Up to 255 characters. Required when creating. location_iduuid The location the box is in. One of location_id or location_external_id is required when creating; later it moves the box to another of your locations. location_external_idstring The same, by your own id for the location. Not together with location_id. descriptionstring | null Up to 255 characters. external_idstring Your own identifier, up to 100 characters, unique among your boxes. Set once. pricenumber | null Price per period, more than 0, at most 2 decimals (never rounded for you). promotional_pricenumber | null Discounted price. Must be lower than price. price_period_1 _3 _6 _12number | null Optional fixed total for renting 1, 3, 6 or 12 periods. When set, the customer pays exactly this amount for that period. price_unitstring | null Price unit code (per month, per week), see Reference data . sizenumber | null Floor area in m², more than 0, rounded to 2 decimals. size_length size_width size_heightnumber | null Inner dimensions in metres. door_width door_heightnumber | null Door dimensions in metres. total_unitsinteger | null How many identical boxes you have, 0 or more. available_unitsinteger | null How many are free to rent right now. Cannot exceed total_units. max_advance_daysinteger | null How many days ahead a customer may book, 1 to 730. featuresstring[] Amenity codes, see Reference data . Replaces the whole set.
Example
A stock update from your system
Send the full current figure each time, never “minus one”: customers may book while your feed runs.
cURL JavaScript Python
Copy curl -X PUT "$API/v1/boxes/by-external-id/SKU-3M" \
-u "$CLIENT_ID:$CLIENT_SECRET" \
-H "Content-Type: application/json" \
-d '{
"available_units": 6
}'
Copy const API = "https://api.boxlatvia.lv" ;
const auth = "Basic " + btoa(`${CLIENT_ID}:${CLIENT_SECRET}` );
const res = await fetch(`${API}/v1/boxes/by-external-id/SKU-3M` , {
method: "PUT" ,
headers: { Authorization: auth, "Content-Type" : "application/json" },
body: JSON.stringify({
"available_units" : 6
}),
});
console.log(res.status, await res.json());
Copy import requests
API = "https://api.boxlatvia.lv"
r = requests.put(
f"{API}/v1/boxes/by-external-id/SKU-3M" ,
auth=(CLIENT_ID, CLIENT_SECRET),
json={
"available_units" : 6,
},
)
print (r.status_code, r.json())
200 OK ETag: W/"52a099edb61a3e0853024325"
Copy {
"id" : "c4883b4a-4e38-47bf-8a69-ef025917e022" ,
"external_id" : "SKU-3M" ,
"location_id" : "9d80d5f4-d17a-4f3e-99f6-4ab43b132bc3" ,
"name" : "Box 3 m²" ,
"description" : "Sausa un apsildāma telpa pirmajā stāvā" ,
"status" : "DRAFT" ,
"price" : 31.5 ,
"promotional_price" : null ,
"price_period_1" : null ,
"price_period_3" : 79 ,
"price_period_6" : null ,
"price_period_12" : 290 ,
"price_unit" : "menesi" ,
"size" : 3 ,
"size_length" : 2 ,
"size_width" : 1.5 ,
"size_height" : 2.4 ,
"door_width" : 0.9 ,
"door_height" : 2 ,
"total_units" : 10 ,
"available_units" : 6 ,
"max_advance_days" : 60 ,
"features" : [
"apkure" ,
"viedatslega"
],
"missing" : [],
"created_at" : "2026-11-07T09:30:12.418Z" ,
"updated_at" : "2026-11-07T14:05:41.092Z"
}
Errors Status When 422 A value is invalid, or a new box is missing its name or location.
# Submit for review
POST /v1/boxes/{id}/submitboxes:write
Asks BoxLatvia to review and publish the box (DRAFT → REVIEW). The API never publishes: BoxLatvia staff do.
Submitting a box that is already in review changes nothing (200). If-Match is optional here.
Path parameters Name Type Description iduuid The box's BoxLatvia id.
Query parameters Name Type Description validate_onlyboolean true runs every check and returns what would happen, but saves nothing. See dry runs .
Examples
Not ready yet
cURL JavaScript Python
Copy curl -X POST "$API/v1/boxes/9d7f53d3-a9cd-4ee1-b9cf-470a572f8b4e/submit" \
-u "$CLIENT_ID:$CLIENT_SECRET"
Copy const API = "https://api.boxlatvia.lv" ;
const auth = "Basic " + btoa(`${CLIENT_ID}:${CLIENT_SECRET}` );
const res = await fetch(`${API}/v1/boxes/9d7f53d3-a9cd-4ee1-b9cf-470a572f8b4e/submit` , {
method: "POST" ,
headers: { Authorization: auth },
});
console.log(res.status, await res.json());
Copy import requests
API = "https://api.boxlatvia.lv"
r = requests.post(
f"{API}/v1/boxes/9d7f53d3-a9cd-4ee1-b9cf-470a572f8b4e/submit" ,
auth=(CLIENT_ID, CLIENT_SECRET),
)
print (r.status_code, r.json())
422 Unprocessable Content
Copy {
"type" : "https://api.boxlatvia.lv/problems/box-incomplete" ,
"title" : "Box is incomplete" ,
"status" : 422 ,
"detail" : "Fill in description, price, price_unit, size, features before submitting this box for review." ,
"request_id" : "f48c9ebd-fa4f-4b14-be21-b98154de2c1e" ,
"missing" : [
"description" ,
"price" ,
"price_unit" ,
"size" ,
"features"
]
}
Submitted
cURL JavaScript Python
Copy curl -X POST "$API/v1/boxes/c4883b4a-4e38-47bf-8a69-ef025917e022/submit" \
-u "$CLIENT_ID:$CLIENT_SECRET"
Copy const API = "https://api.boxlatvia.lv" ;
const auth = "Basic " + btoa(`${CLIENT_ID}:${CLIENT_SECRET}` );
const res = await fetch(`${API}/v1/boxes/c4883b4a-4e38-47bf-8a69-ef025917e022/submit` , {
method: "POST" ,
headers: { Authorization: auth },
});
console.log(res.status, await res.json());
Copy import requests
API = "https://api.boxlatvia.lv"
r = requests.post(
f"{API}/v1/boxes/c4883b4a-4e38-47bf-8a69-ef025917e022/submit" ,
auth=(CLIENT_ID, CLIENT_SECRET),
)
print (r.status_code, r.json())
200 OK ETag: W/"052468fbc2a710d35f026e30"
Copy {
"id" : "c4883b4a-4e38-47bf-8a69-ef025917e022" ,
"external_id" : "SKU-3M" ,
"location_id" : "9d80d5f4-d17a-4f3e-99f6-4ab43b132bc3" ,
"name" : "Box 3 m²" ,
"description" : "Sausa un apsildāma telpa pirmajā stāvā" ,
"status" : "REVIEW" ,
"price" : 31.5 ,
"promotional_price" : null ,
"price_period_1" : null ,
"price_period_3" : 79 ,
"price_period_6" : null ,
"price_period_12" : 290 ,
"price_unit" : "menesi" ,
"size" : 3 ,
"size_length" : 2 ,
"size_width" : 1.5 ,
"size_height" : 2.4 ,
"door_width" : 0.9 ,
"door_height" : 2 ,
"total_units" : 10 ,
"available_units" : 6 ,
"max_advance_days" : 60 ,
"features" : [
"apkure" ,
"viedatslega"
],
"missing" : [],
"created_at" : "2026-11-07T09:30:12.418Z" ,
"updated_at" : "2026-11-07T14:05:41.092Z"
}
Errors Status When 404 No such box of yours. 409 The box is already published: withdraw it first. 422 The box is incomplete: missing lists what it needs.
# Withdraw
POST /v1/boxes/{id}/withdrawboxes:write
Takes the box out of review or off the website and back to DRAFT. A draft stays a draft (200).
Path parameters Name Type Description iduuid The box's BoxLatvia id.
Query parameters Name Type Description validate_onlyboolean true runs every check and returns what would happen, but saves nothing. See dry runs .
Example
Back to draft
cURL JavaScript Python
Copy curl -X POST "$API/v1/boxes/c4883b4a-4e38-47bf-8a69-ef025917e022/withdraw" \
-u "$CLIENT_ID:$CLIENT_SECRET"
Copy const API = "https://api.boxlatvia.lv" ;
const auth = "Basic " + btoa(`${CLIENT_ID}:${CLIENT_SECRET}` );
const res = await fetch(`${API}/v1/boxes/c4883b4a-4e38-47bf-8a69-ef025917e022/withdraw` , {
method: "POST" ,
headers: { Authorization: auth },
});
console.log(res.status, await res.json());
Copy import requests
API = "https://api.boxlatvia.lv"
r = requests.post(
f"{API}/v1/boxes/c4883b4a-4e38-47bf-8a69-ef025917e022/withdraw" ,
auth=(CLIENT_ID, CLIENT_SECRET),
)
print (r.status_code, r.json())
200 OK ETag: W/"f1fa55a0c2e2fd1d06f83296"
Copy {
"id" : "c4883b4a-4e38-47bf-8a69-ef025917e022" ,
"external_id" : "SKU-3M" ,
"location_id" : "9d80d5f4-d17a-4f3e-99f6-4ab43b132bc3" ,
"name" : "Box 3 m²" ,
"description" : "Sausa un apsildāma telpa pirmajā stāvā" ,
"status" : "DRAFT" ,
"price" : 31.5 ,
"promotional_price" : null ,
"price_period_1" : null ,
"price_period_3" : 79 ,
"price_period_6" : null ,
"price_period_12" : 290 ,
"price_unit" : "menesi" ,
"size" : 3 ,
"size_length" : 2 ,
"size_width" : 1.5 ,
"size_height" : 2.4 ,
"door_width" : 0.9 ,
"door_height" : 2 ,
"total_units" : 10 ,
"available_units" : 6 ,
"max_advance_days" : 60 ,
"features" : [
"apkure" ,
"viedatslega"
],
"missing" : [],
"created_at" : "2026-11-07T09:30:12.418Z" ,
"updated_at" : "2026-11-07T14:05:41.092Z"
}
Errors Status When 404 No such box of yours.
# Delete a box
DELETE /v1/boxes/{id}boxes:write
Deletes the box and its pictures. A box that has orders cannot be deleted: set available_units to 0 to stop new bookings.
Path parameters Name Type Description iduuid The box's BoxLatvia id.
Query parameters Name Type Description validate_onlyboolean true runs every check and returns what would happen, but saves nothing. See dry runs .
Headers Name Type Description If-Match required string The ETag from a previous GET, exactly as received. 428 if missing, 412 if the object changed since. See conditional updates .
Examples
Deleted
cURL JavaScript Python
Copy curl -X DELETE "$API/v1/boxes/9d7f53d3-a9cd-4ee1-b9cf-470a572f8b4e" \
-u "$CLIENT_ID:$CLIENT_SECRET" \
-H "If-Match: W/\"9c02eeec9c920097d539244a\""
Copy const API = "https://api.boxlatvia.lv" ;
const auth = "Basic " + btoa(`${CLIENT_ID}:${CLIENT_SECRET}` );
const res = await fetch(`${API}/v1/boxes/9d7f53d3-a9cd-4ee1-b9cf-470a572f8b4e` , {
method: "DELETE" ,
headers: { Authorization: auth, "If-Match" : "W/\"9c02eeec9c920097d539244a\"" },
});
console.log(res.status); // 204, no body
Copy import requests
API = "https://api.boxlatvia.lv"
r = requests.delete(
f"{API}/v1/boxes/9d7f53d3-a9cd-4ee1-b9cf-470a572f8b4e" ,
auth=(CLIENT_ID, CLIENT_SECRET),
headers={"If-Match" : "W/\"9c02eeec9c920097d539244a\"" },
)
print (r.status_code) # 204, no body
204 No Content
No response body
The box has orders
cURL JavaScript Python
Copy curl -X DELETE "$API/v1/boxes/c4883b4a-4e38-47bf-8a69-ef025917e022" \
-u "$CLIENT_ID:$CLIENT_SECRET" \
-H "If-Match: W/\"f1fa55a0c2e2fd1d06f83296\""
Copy const API = "https://api.boxlatvia.lv" ;
const auth = "Basic " + btoa(`${CLIENT_ID}:${CLIENT_SECRET}` );
const res = await fetch(`${API}/v1/boxes/c4883b4a-4e38-47bf-8a69-ef025917e022` , {
method: "DELETE" ,
headers: { Authorization: auth, "If-Match" : "W/\"f1fa55a0c2e2fd1d06f83296\"" },
});
console.log(res.status, await res.json());
Copy import requests
API = "https://api.boxlatvia.lv"
r = requests.delete(
f"{API}/v1/boxes/c4883b4a-4e38-47bf-8a69-ef025917e022" ,
auth=(CLIENT_ID, CLIENT_SECRET),
headers={"If-Match" : "W/\"f1fa55a0c2e2fd1d06f83296\"" },
)
print (r.status_code, r.json())
409 Conflict
Copy {
"type" : "https://api.boxlatvia.lv/problems/box-has-orders" ,
"title" : "Conflict" ,
"status" : 409 ,
"detail" : "This box has orders and cannot be deleted. Set available_units to 0 to stop new bookings." ,
"request_id" : "d1f30eca-8286-4c7d-8244-c8f0cf9a56c3"
}
Errors Status When 404 No such box of yours. 409 The box has orders. 412 The box changed since you read it. 428 If-Match is missing.