Product Media

8 vues Markdown

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

get/api/v1/admin/products/{id}/images
Products/GetProductImages admin ordered

Returns the product's gallery images in their order.

Response fields data[] — 7
idintImage id.
typestringThe image role: photo, header-background, cover or mockup.
sizestringA human-readable file size.
namestringThe stored file name.
urlstringThe public address.
titlestringThe image title.
sort_orderintIts place in the gallery.
Errors 2
not_found404No such product.
insufficient_scope403The key lacks the required scope.
Request
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]);
Response
{
  "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

post/api/v1/admin/products/{id}/images
Products/UploadProductImage admin 201

Adds an image to the gallery. You send the file as base64 or hand over an address.

Body 3
imagestringrequiredThe image. A base64 data URI or a link that can be fetched.
titlestringThe image title.
typestringThe image role: photo, header-background, cover or mockup. Defaults to photo.
Response fields data — 5
idintId of the new image.
typestringThe image role it was stored under.
titlestringThe image title.
sort_orderintIts place in the gallery.
urlstringThe public address.
Errors 5
not_found404No such product.
file_required422The file field was empty.
file_invalid422The file could not be read or its type was refused.
file_failed422The file could not be stored.
insufficient_scope403The key lacks the required scope.
Request
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

patch/api/v1/admin/products/{id}/images/{image_id}
Products/UpdateProductImage admin type moves the file

Changes an image's title or role. Changing the role moves the file between folders.

Body 2
titlestringThe new title. If you send it, it cannot be empty.
typestringThe new role: photo, header-background, cover or mockup.
Response fields data — 2
updatedboolWhether the update succeeded.
idintImage id.
Errors 4
not_found404No such product or image.
title_required422A title was sent but it was empty.
invalid_type422The role is not one of the allowed values.
insufficient_scope403The key lacks the required scope.
Request
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

delete/api/v1/admin/products/{id}/images/{image_id}
Products/DeleteProductImage admin

Deletes a single image from the gallery.

Response fields data — 2
deletedboolWhether the delete succeeded.
idintId of the deleted image.
Errors 2
not_found404No such product or image.
insufficient_scope403The key lacks the required scope.
Request
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

delete/api/v1/admin/products/{id}/images
Products/ClearProductImages admin cannot be undone

Deletes every gallery image the product has.

Response fields data — 1
clearedintHow many images were deleted.
Errors 2
not_found404No such product.
insufficient_scope403The key lacks the required scope.
Request
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

put/api/v1/admin/products/{id}/images/order
Products/ReorderProductImages admin full list

Sets the gallery order from the list of ids you give.

Body 1
image_idsint[]requiredThe image ids, in the order you want.
Response fields data[] — 7
idintImage id.
typestringThe image role: photo, header-background, cover or mockup.
sizestringA human-readable file size.
namestringThe stored file name.
urlstringThe public address.
titlestringThe image title.
sort_orderintIts place in the gallery.
Errors 3
not_found404No such product.
ids_required422image_ids was empty.
insufficient_scope403The key lacks the required scope.
Request
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

get/api/v1/admin/products/{id}/delivery-file
Products/GetProductDeliveryFile admin software products

Returns the file a software product hands to the client.

Response fields data — 2
namestringThe stored file name.
sizestringA human-readable file size.
Errors 2
not_found404No such product.
insufficient_scope403The key lacks the required scope.
Request
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

put/api/v1/admin/products/{id}/delivery-file
Products/UploadProductDeliveryFile admin extension limits

Uploads the delivery file. The previous one is removed; a product carries one at a time.

Body 1
filestringrequiredThe file. A base64 data URI or a link that can be fetched. Executable page extensions are refused.
Response fields data — 2
namestringThe stored file name.
sizestringA human-readable file size.
Errors 5
not_found404No such product.
file_required422The file field was empty.
file_invalid422The file could not be read or its type was refused.
file_failed422The file could not be stored.
insufficient_scope403The key lacks the required scope.
Request
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

delete/api/v1/admin/products/{id}/delivery-file
Products/DeleteProductDeliveryFile admin

Removes the delivery file.

Response fields data — 2
deletedboolWhether the delete succeeded.
idintProduct id.
Errors 2
not_found404No such product.
insufficient_scope403The key lacks the required scope.
Request
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

put/api/v1/admin/products/{id}/order-image
Products/SetProductOrderImage admin a single image

Sets the image shown on the order screen. A product carries one of these.

Body 1
imagestringrequiredThe image. A base64 data URI or a link that can be fetched.
Response fields data — 1
urlstringThe image's public address.
Errors 5
not_found404No such product.
file_required422The file field was empty.
file_invalid422The file could not be read or its type was refused.
file_failed422The file could not be stored.
insufficient_scope403The key lacks the required scope.
Request
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

delete/api/v1/admin/products/{id}/order-image
Products/DeleteProductOrderImage admin

Removes the order image.

Response fields data — 2
deletedboolWhether the delete succeeded.
idintProduct id.
Errors 2
not_found404No such product.
insufficient_scope403The key lacks the required scope.
Request
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

One address deletes one image or the whole gallery

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.

Ordering takes the full list

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 the role moves the file

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.

The delivery file is single and extension-limited

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.

Cet article vous a-t-il été utile ?

Merci pour votre retour !

Besoin d'aide supplémentaire ?

Notre équipe d'assistance est disponible 24h/24 pour tout ce que vous ne trouvez pas ci-dessus.