API reference
Pictures
Pictures belong to a box or a location. Upload them as files; BoxLatvia stores them and returns their public address.
#The Picture object
| Name | Type | Description |
|---|---|---|
id | uuid | Use it to delete the picture. |
url | string | Public address of the picture. |
created_at | datetime | When it was created (UTC). |
Example
{
"id": "eb28418e-6710-4ba2-aee5-f15b53c02d8f",
"url": "https://gnfvochksxcodgoqdgxx.supabase.co/storage/v1/object/public/box-images/sup-1/c4883b4a-4e38-47bf-8a69-ef025917e022/e8219951-043a-45bd-a9ee-0c75a6819510.png",
"created_at": "2026-11-07T09:30:12.418Z"
}#Endpoints
#List pictures
GET
/v1/boxes/{id}/imagesboxes:readThe pictures of a box, oldest first.
The same three endpoints exist for locations under /v1/locations/{id}/images; they need locations:read instead of boxes:read for listing.
Path parameters
| Name | Type | Description |
|---|---|---|
id | uuid | The box's id (or the location's, under /v1/locations/{id}/images). |
Example
Pictures of a box
curl "$API/v1/boxes/c4883b4a-4e38-47bf-8a69-ef025917e022/images" \
-u "$CLIENT_ID:$CLIENT_SECRET"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/images`, {
headers: { Authorization: auth },
});
console.log(res.status, await res.json());import requests
API = "https://api.boxlatvia.lv"
r = requests.get(
f"{API}/v1/boxes/c4883b4a-4e38-47bf-8a69-ef025917e022/images",
auth=(CLIENT_ID, CLIENT_SECRET),
)
print(r.status_code, r.json())200 OK
{
"data": [
{
"id": "eb28418e-6710-4ba2-aee5-f15b53c02d8f",
"url": "https://gnfvochksxcodgoqdgxx.supabase.co/storage/v1/object/public/box-images/sup-1/c4883b4a-4e38-47bf-8a69-ef025917e022/e8219951-043a-45bd-a9ee-0c75a6819510.png",
"created_at": "2026-11-07T09:30:12.418Z"
},
{
"id": "ae6e8d2e-da66-4026-bb04-9418e52a7c43",
"url": "https://gnfvochksxcodgoqdgxx.supabase.co/storage/v1/object/public/box-images/sup-1/c4883b4a-4e38-47bf-8a69-ef025917e022/4f380fb3-d246-4c5b-b6d0-37d7fa8b5523.png",
"created_at": "2026-11-07T09:30:12.418Z"
}
]
}Errors
| Status | When |
|---|---|
| 404 | No such box of yours. |
#Upload pictures
POST
/v1/boxes/{id}/imagesimages:writeUploads up to 5 pictures at once as multipart/form-data, one part named file per picture.
- JPEG, PNG, WebP or GIF: the file's content is checked, not its name or declared type.
- Up to 5 files per request, 5 MB each, a whole request up to 27 MB.
- At most 20 pictures per box or location.
- All or nothing: if any file is refused, none is stored and every problem is listed.
The same three endpoints exist for locations under /v1/locations/{id}/images; they need locations:read instead of boxes:read for listing.
Path parameters
| Name | Type | Description |
|---|---|---|
id | uuid | The box's id (or the location's). |
Query parameters
| Name | Type | Description |
|---|---|---|
validate_only | boolean | true runs every check and returns what would happen, but saves nothing. See dry runs. |
Headers
| Name | Type | Description |
|---|---|---|
Idempotency-Key | string | 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 (multipart/form-data)
| Name | Type | Description |
|---|---|---|
file required | file | A picture. Repeat the part for more pictures (up to 5). |
Examples
Two pictures
curl -X POST "$API/v1/boxes/c4883b4a-4e38-47bf-8a69-ef025917e022/images" \
-u "$CLIENT_ID:$CLIENT_SECRET" \
-F "file=@front.jpg" \
-F "file=@inside.png"const API = "https://api.boxlatvia.lv";
const auth = "Basic " + btoa(`${CLIENT_ID}:${CLIENT_SECRET}`);
const form = new FormData();
form.append("file", frontFile); // a File or Blob
form.append("file", insideFile); // a File or Blob
const res = await fetch(`${API}/v1/boxes/c4883b4a-4e38-47bf-8a69-ef025917e022/images`, {
method: "POST",
headers: { Authorization: auth },
body: form,
});
console.log(res.status, await res.json());import requests
API = "https://api.boxlatvia.lv"
r = requests.post(
f"{API}/v1/boxes/c4883b4a-4e38-47bf-8a69-ef025917e022/images",
auth=(CLIENT_ID, CLIENT_SECRET),
files=[("file", open("front.jpg", "rb")), ("file", open("inside.png", "rb"))],
)
print(r.status_code, r.json())201 Created
{
"data": [
{
"id": "eb28418e-6710-4ba2-aee5-f15b53c02d8f",
"url": "https://gnfvochksxcodgoqdgxx.supabase.co/storage/v1/object/public/box-images/sup-1/c4883b4a-4e38-47bf-8a69-ef025917e022/e8219951-043a-45bd-a9ee-0c75a6819510.png",
"created_at": "2026-11-07T09:30:12.418Z"
},
{
"id": "ae6e8d2e-da66-4026-bb04-9418e52a7c43",
"url": "https://gnfvochksxcodgoqdgxx.supabase.co/storage/v1/object/public/box-images/sup-1/c4883b4a-4e38-47bf-8a69-ef025917e022/4f380fb3-d246-4c5b-b6d0-37d7fa8b5523.png",
"created_at": "2026-11-07T09:30:12.418Z"
}
]
}A file that is not a picture
curl -X POST "$API/v1/boxes/c4883b4a-4e38-47bf-8a69-ef025917e022/images" \
-u "$CLIENT_ID:$CLIENT_SECRET" \
-F "file=@notes.jpg"const API = "https://api.boxlatvia.lv";
const auth = "Basic " + btoa(`${CLIENT_ID}:${CLIENT_SECRET}`);
const form = new FormData();
form.append("file", notesFile); // a File or Blob
const res = await fetch(`${API}/v1/boxes/c4883b4a-4e38-47bf-8a69-ef025917e022/images`, {
method: "POST",
headers: { Authorization: auth },
body: form,
});
console.log(res.status, await res.json());import requests
API = "https://api.boxlatvia.lv"
r = requests.post(
f"{API}/v1/boxes/c4883b4a-4e38-47bf-8a69-ef025917e022/images",
auth=(CLIENT_ID, CLIENT_SECRET),
files=[("file", open("notes.jpg", "rb"))],
)
print(r.status_code, r.json())422 Unprocessable Content
{
"type": "https://api.boxlatvia.lv/problems/validation-failed",
"title": "Validation failed",
"status": 422,
"detail": "One or more fields are invalid.",
"request_id": "78c2fa2a-cb59-4455-bd8d-10df4eb605c6",
"errors": [
{
"field": "file[0]",
"message": "notes.jpg is not a JPEG, PNG, WebP or GIF picture"
}
]
}The box already has 20 pictures
curl -X POST "$API/v1/boxes/c4883b4a-4e38-47bf-8a69-ef025917e022/images" \
-u "$CLIENT_ID:$CLIENT_SECRET" \
-F "file=@extra.jpg"const API = "https://api.boxlatvia.lv";
const auth = "Basic " + btoa(`${CLIENT_ID}:${CLIENT_SECRET}`);
const form = new FormData();
form.append("file", extraFile); // a File or Blob
const res = await fetch(`${API}/v1/boxes/c4883b4a-4e38-47bf-8a69-ef025917e022/images`, {
method: "POST",
headers: { Authorization: auth },
body: form,
});
console.log(res.status, await res.json());import requests
API = "https://api.boxlatvia.lv"
r = requests.post(
f"{API}/v1/boxes/c4883b4a-4e38-47bf-8a69-ef025917e022/images",
auth=(CLIENT_ID, CLIENT_SECRET),
files=[("file", open("extra.jpg", "rb"))],
)
print(r.status_code, r.json())409 Conflict
{
"type": "https://api.boxlatvia.lv/problems/image-limit",
"title": "Conflict",
"status": 409,
"detail": "A box can have at most 20 pictures; it has 20 and you sent 1.",
"request_id": "afc3eb2c-5cb3-47ab-9731-583d4671a79c",
"limit": 20,
"existing": 20
}Errors
| Status | When |
|---|---|
| 404 | No such box of yours. |
| 409 | The 20 pictures limit would be exceeded. |
| 413 | The request is larger than 27 MB. |
| 415 | The body is not multipart/form-data. |
| 422 | A file is not a supported picture, is empty or larger than 5 MB, or more than 5 were sent. |
#Delete a picture
DELETE
/v1/boxes/{id}/images/{image_id}images:writeDeletes the picture and its file. Deleting a box or a location deletes its pictures too.
The same three endpoints exist for locations under /v1/locations/{id}/images; they need locations:read instead of boxes:read for listing.
Path parameters
| Name | Type | Description |
|---|---|---|
id | uuid | The box's id (or the location's). |
image_id | uuid | The picture's id from the list. |
Example
Deleted
curl -X DELETE "$API/v1/boxes/c4883b4a-4e38-47bf-8a69-ef025917e022/images/eb28418e-6710-4ba2-aee5-f15b53c02d8f" \
-u "$CLIENT_ID:$CLIENT_SECRET"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/images/eb28418e-6710-4ba2-aee5-f15b53c02d8f`, {
method: "DELETE",
headers: { Authorization: auth },
});
console.log(res.status); // 204, no bodyimport requests
API = "https://api.boxlatvia.lv"
r = requests.delete(
f"{API}/v1/boxes/c4883b4a-4e38-47bf-8a69-ef025917e022/images/eb28418e-6710-4ba2-aee5-f15b53c02d8f",
auth=(CLIENT_ID, CLIENT_SECRET),
)
print(r.status_code) # 204, no body204 No Content
No response body
Errors
| Status | When |
|---|---|
| 404 | No such picture of yours. |