Product Media
The eleven endpoints that manage a product's gallery, order image and delivery file.
Overview
A product has three separate visual assets and they should not be confused. The gallery holds several images and can be ordered; the order image is a single one shown on the order screen; the delivery file is not an image at all, it is what a software product gives the client.
None of the upload endpoints expect a multipart form. You send the file as a base64 data URI or hand over a link that can be fetched, and the server pulls it itself.
Reference
Listing the Gallery
Returns the product's gallery images in their order.
photo, header-background, cover or mockup.curl 'https://panel.example.com/api/v1/admin/products/15/images' \
-H "Authorization: Bearer $API_KEY"const res = await fetch('https://panel.example.com/api/v1/admin/products/15/images', {
headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/products/15/images');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $apiKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);$response = Api::Products()->GetProductImages(['id' => 15]);{
"data": [
{
"id": 90,
"type": "photo",
"size": "120 KB",
"name": "ab12.jpg",
"url": "https://example.com/uploads/ab12.jpg",
"title": "Screenshot",
"sort_order": 1
}
]
}Uploading to the Gallery
Adds an image to the gallery. You send the file as base64 or hand over an address.
photo, header-background, cover or mockup. Defaults to photo.curl -X POST 'https://panel.example.com/api/v1/admin/products/15/images' \
-H "Authorization: Bearer $API_KEY" \
-H 'Content-Type: application/json' \
-d '{"image":"data:image/png;base64,iVBORw0KGgo...","title":"Screenshot"}'const res = await fetch('https://panel.example.com/api/v1/admin/products/15/images', {
method: 'POST',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
image: 'data:image/png;base64,iVBORw0KGgo...',
title: 'Screenshot',
type: 'photo',
}),
});
const body = await res.json();$data = base64_encode(file_get_contents('screenshot.png'));
$ch = curl_init('https://panel.example.com/api/v1/admin/products/15/images');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'image' => 'data:image/png;base64,' . $data,
'title' => 'Screenshot',
]),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// A new image lands at the END of the gallery; ordering is a separate call.
$image = Api::Products()->UploadProductImage([
'id' => 15,
'image' => 'data:image/png;base64,' . base64_encode($bytes),
'title' => 'Screenshot',
]);Updating an Image
Changes an image's title or role. Changing the role moves the file between folders.
photo, header-background, cover or mockup.curl -X PATCH 'https://panel.example.com/api/v1/admin/products/15/images/91' \
-H "Authorization: Bearer $API_KEY" \
-H 'Content-Type: application/json' \
-d '{"title":"Dashboard","type":"cover"}'const res = await fetch('https://panel.example.com/api/v1/admin/products/15/images/91', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ title: 'Dashboard', type: 'cover' }),
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/products/15/images/91');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PATCH',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'title' => 'Dashboard',
'type' => 'cover',
]),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// Changing the role moves the file: the old address stops working.
$response = Api::Products()->UpdateProductImage([
'id' => 15,
'image_id' => 91,
'type' => 'cover',
]);Deleting an Image
Deletes a single image from the gallery.
curl -X DELETE 'https://panel.example.com/api/v1/admin/products/15/images/91' \
-H "Authorization: Bearer $API_KEY"const res = await fetch('https://panel.example.com/api/v1/admin/products/15/images/91', {
method: 'DELETE',
headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/products/15/images/91');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $apiKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);$response = Api::Products()->DeleteProductImage([
'id' => 15,
'image_id' => 91,
]);Clearing the Gallery
Deletes every gallery image the product has.
curl -X DELETE 'https://panel.example.com/api/v1/admin/products/15/images' \
-H "Authorization: Bearer $API_KEY"const res = await fetch('https://panel.example.com/api/v1/admin/products/15/images', {
method: 'DELETE',
headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/products/15/images');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $apiKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// The address is the SAME as deleting one image; only the image id is missing.
$response = Api::Products()->ClearProductImages(['id' => 15]);Ordering the Gallery
Sets the gallery order from the list of ids you give.
photo, header-background, cover or mockup.image_ids was empty.curl -X PUT 'https://panel.example.com/api/v1/admin/products/15/images/order' \
-H "Authorization: Bearer $API_KEY" \
-H 'Content-Type: application/json' \
-d '{"image_ids":[92,90,91]}'const res = await fetch('https://panel.example.com/api/v1/admin/products/15/images/order', {
method: 'PUT',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ image_ids: [92, 90, 91] }),
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/products/15/images/order');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode(['image_ids' => [92, 90, 91]]),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// Even to move one image to the front, send the WHOLE list.
$ids = array_column(Api::Products()->GetProductImages(['id' => 15])['data'], 'id');
array_unshift($ids, array_pop($ids));
$response = Api::Products()->ReorderProductImages([
'id' => 15,
'image_ids' => $ids,
]);Reading the Delivery File
Returns the file a software product hands to the client.
curl 'https://panel.example.com/api/v1/admin/products/15/delivery-file' \
-H "Authorization: Bearer $API_KEY"const res = await fetch('https://panel.example.com/api/v1/admin/products/15/delivery-file', {
headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();
// With no file uploaded, data comes back null.
if (body.data === null) return;$ch = curl_init('https://panel.example.com/api/v1/admin/products/15/delivery-file');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $apiKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);$response = Api::Products()->GetProductDeliveryFile(['id' => 15]);
$file = $response['data'] ?? null;Uploading the Delivery File
Uploads the delivery file. The previous one is removed; a product carries one at a time.
curl -X PUT 'https://panel.example.com/api/v1/admin/products/15/delivery-file' \
-H "Authorization: Bearer $API_KEY" \
-H 'Content-Type: application/json' \
-d '{"file":"https://cdn.example.com/setup-v2.zip"}'const res = await fetch('https://panel.example.com/api/v1/admin/products/15/delivery-file', {
method: 'PUT',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ file: 'https://cdn.example.com/setup-v2.zip' }),
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/products/15/delivery-file');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'file' => 'https://cdn.example.com/setup-v2.zip',
]),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// Uploading a new version removes the old one - you may want a copy first.
$response = Api::Products()->UploadProductDeliveryFile([
'id' => 15,
'file' => 'https://cdn.example.com/setup-v2.zip',
]);Deleting the Delivery File
Removes the delivery file.
curl -X DELETE 'https://panel.example.com/api/v1/admin/products/15/delivery-file' \
-H "Authorization: Bearer $API_KEY"const res = await fetch('https://panel.example.com/api/v1/admin/products/15/delivery-file', {
method: 'DELETE',
headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/products/15/delivery-file');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $apiKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);$response = Api::Products()->DeleteProductDeliveryFile(['id' => 15]);Setting the Order Image
Sets the image shown on the order screen. A product carries one of these.
curl -X PUT 'https://panel.example.com/api/v1/admin/products/15/order-image' \
-H "Authorization: Bearer $API_KEY" \
-H 'Content-Type: application/json' \
-d '{"image":"https://cdn.example.com/plan.png"}'const res = await fetch('https://panel.example.com/api/v1/admin/products/15/order-image', {
method: 'PUT',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ image: 'https://cdn.example.com/plan.png' }),
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/products/15/order-image');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'image' => 'https://cdn.example.com/plan.png',
]),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);$response = Api::Products()->SetProductOrderImage([
'id' => 15,
'image' => 'https://cdn.example.com/plan.png',
]);Deleting the Order Image
Removes the order image.
curl -X DELETE 'https://panel.example.com/api/v1/admin/products/15/order-image' \
-H "Authorization: Bearer $API_KEY"const res = await fetch('https://panel.example.com/api/v1/admin/products/15/order-image', {
method: 'DELETE',
headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/products/15/order-image');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $apiKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);$response = Api::Products()->DeleteProductOrderImage(['id' => 15]);Pitfalls
Clearing the gallery and deleting one image use the same address; the only difference is the image id at the end. Code that builds that id from an empty variable wipes the entire gallery instead of the one image it meant to remove.
The ordering endpoint takes the list you give as the order, and images missing from it end up in an undefined place. Even to move one image to the front you read the gallery first and send every id. A newly uploaded image always lands at the end.
Changing an image's role moves the file between folders, so the old address stops working. If you cached that address on your side, read it back after the update.
Uploading a new delivery file removes the previous one; a product cannot hold two versions at once. Executable page extensions are also refused, so there is no way to hand the client a file that would run on the server.
Related Articles
Merci pour votre retour !
Notre équipe d'assistance est disponible 24h/24 pour tout ce que vous ne trouvez pas ci-dessus.