Manage images for your products. Each product supports up to 10 images in JPEG, PNG, or GIF format (max 10MB each).

Each image's url is a presigned link, valid for 10 minutes from when it's returned — not a permanent CDN URL. Re-fetch the product (expand=images) for a fresh one when you need it later. Use the reorder endpoint to control display priority.

POST /products/{id}/images

Upload a product image

Uploads an image to a product. Supports JPEG, PNG, and GIF (max 10MB). Maximum 10 images per product.

Send the image as multipart/form-data with the file in a field named file — there is no JSON, base64, or URL alternative.

Path parameters

NameTypeDescription
idrequired
string
Product ID.
e.g. "6ba7b810-9dad-11d1-80b4-00c04fd430c8"

Header parameters

NameTypeDescription
EPD-Version
string
API version override (format `YYYY-MM-DD`). If omitted, your account's pinned version or the latest version is used.
e.g. "2026-02-11"

Code samples

curl -X POST https://api.epd.com/v1/products/6ba7b810-9dad-11d1-80b4-00c04fd430c8/images \
  -H "Authorization: Bearer epd_test_sk_xxxx" \
  -H "EPD-Version: 2026-02-11" \
  -F "file=@/path/to/photo.jpg"
import { readFileSync } from 'fs';

const form = new FormData();
form.append('file', new Blob([readFileSync('./photo.jpg')]), 'photo.jpg');

const response = await fetch('https://api.epd.com/v1/products/6ba7b810-9dad-11d1-80b4-00c04fd430c8/images', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer epd_test_sk_xxxx',
    'EPD-Version': '2026-02-11',
    // Don't set Content-Type — fetch sets the multipart boundary automatically
  },
  body: form,
});

const image = await response.json();
console.log(image.url); // presigned URL, valid for 10 minutes
import requests

with open('photo.jpg', 'rb') as f:
    response = requests.post(
        'https://api.epd.com/v1/products/6ba7b810-9dad-11d1-80b4-00c04fd430c8/images',
        headers={
            'Authorization': 'Bearer epd_test_sk_xxxx',
            'EPD-Version': '2026-02-11',
        },
        files={'file': ('photo.jpg', f, 'image/jpeg')},
    )

image = response.json()
print(image['url'])  # presigned URL, valid for 10 minutes
$ch = curl_init('https://api.epd.com/v1/products/6ba7b810-9dad-11d1-80b4-00c04fd430c8/images');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer epd_test_sk_xxxx',
        'EPD-Version: 2026-02-11',
    ],
    CURLOPT_POSTFIELDS => [
        'file' => new CURLFile('/path/to/photo.jpg', 'image/jpeg', 'photo.jpg'),
    ],
]);

$response = json_decode(curl_exec($ch), true);
echo $response['url']; // presigned URL, valid for 10 minutes

Responses

201 Image uploaded.
FieldTypeDescription
idrequired
string
e.g. "6ba7b816-9dad-11d1-80b4-00c04fd430c8"
product_idrequired
string
e.g. "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
urlrequired
string (uri)
Presigned URL, valid for 10 minutes from when this response was generated — not a permanent link. Re-fetch the product (`expand=images`) for a fresh URL when needed.
e.g. "https://epd-uploads.s3.amazonaws.com/products/6ba7b810-9dad-11d1-80b4-00c04fd430c8/photo.jpg?X-Amz-Expires=600&X-Amz-Signature=abc123"
orderrequired
integer
Display order (0-based).
e.g. 0
created_atrequired
string (date-time)
e.g. "2024-01-15T10:30:00.000Z"
400 Bad Request. The request was invalid or cannot be served.
FieldTypeDescription
errorrequired
object
typerequired
enum
The type of error.
invalid_request_errorauthentication_errorauthorization_errorrate_limit_erroridempotency_errorprocessing_errorwebhook_error
coderequired
string
A short string identifying the specific error.
e.g. "validation_error"
messagerequired
string
A human-readable message providing details about the error.
e.g. "Request validation failed"
paramnullable
string
The parameter that caused the error, if applicable.
e.g. "email"
request_id
string
Unique request identifier for debugging.
e.g. "req_a1b2c3d4e5f67890abcdef0123456789"
field_errors
array[object]
Detailed field-level errors for validation failures.
404 Not Found. The requested resource doesn't exist.
FieldTypeDescription
errorrequired
object
typerequired
enum
The type of error.
invalid_request_errorauthentication_errorauthorization_errorrate_limit_erroridempotency_errorprocessing_errorwebhook_error
coderequired
string
A short string identifying the specific error.
e.g. "validation_error"
messagerequired
string
A human-readable message providing details about the error.
e.g. "Request validation failed"
paramnullable
string
The parameter that caused the error, if applicable.
e.g. "email"
request_id
string
Unique request identifier for debugging.
e.g. "req_a1b2c3d4e5f67890abcdef0123456789"
field_errors
array[object]
Detailed field-level errors for validation failures.
DELETE /products/{id}/images/{imageId}

Delete a product image

Path parameters

NameTypeDescription
idrequired
string
Product ID.
e.g. "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
imageIdrequired
string
Image ID.
e.g. "6ba7b816-9dad-11d1-80b4-00c04fd430d0"

Header parameters

NameTypeDescription
EPD-Version
string
API version override (format `YYYY-MM-DD`). If omitted, your account's pinned version or the latest version is used.
e.g. "2026-02-11"

Code samples

curl -X DELETE https://api.epd.com/v1/products/6ba7b810-9dad-11d1-80b4-00c04fd430c8/images/6ba7b816-9dad-11d1-80b4-00c04fd430d0 \
  -H "Authorization: Bearer epd_test_sk_xxxx" \
  -H "EPD-Version: 2026-02-11"
const response = await fetch('https://api.epd.com/v1/products/6ba7b810-9dad-11d1-80b4-00c04fd430c8/images/6ba7b816-9dad-11d1-80b4-00c04fd430d0', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer epd_test_sk_xxxx',
    'EPD-Version': '2026-02-11',
  },
});

const result = await response.json();
console.log(result.deleted); // true
import requests

response = requests.delete(
    'https://api.epd.com/v1/products/6ba7b810-9dad-11d1-80b4-00c04fd430c8/images/6ba7b816-9dad-11d1-80b4-00c04fd430d0',
    headers={
        'Authorization': 'Bearer epd_test_sk_xxxx',
        'EPD-Version': '2026-02-11',
    },
)

result = response.json()
print(result['deleted'])  # True
$ch = curl_init('https://api.epd.com/v1/products/6ba7b810-9dad-11d1-80b4-00c04fd430c8/images/6ba7b816-9dad-11d1-80b4-00c04fd430d0');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'DELETE',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer epd_test_sk_xxxx',
        'EPD-Version: 2026-02-11',
    ],
]);

$response = json_decode(curl_exec($ch), true);
echo $response['deleted'] ? 'true' : 'false';

Responses

200 Image deleted.
FieldTypeDescription
idrequired
string
e.g. "6ba7b816-9dad-11d1-80b4-00c04fd430c8"
deletedrequired
true
messagerequired
string
e.g. "Product image successfully deleted."
404 Not Found. The requested resource doesn't exist.
FieldTypeDescription
errorrequired
object
typerequired
enum
The type of error.
invalid_request_errorauthentication_errorauthorization_errorrate_limit_erroridempotency_errorprocessing_errorwebhook_error
coderequired
string
A short string identifying the specific error.
e.g. "validation_error"
messagerequired
string
A human-readable message providing details about the error.
e.g. "Request validation failed"
paramnullable
string
The parameter that caused the error, if applicable.
e.g. "email"
request_id
string
Unique request identifier for debugging.
e.g. "req_a1b2c3d4e5f67890abcdef0123456789"
field_errors
array[object]
Detailed field-level errors for validation failures.
PATCH /products/{id}/images/reorder

Reorder product images

Updates the display order of images. Position in the array determines the order (first = 0).

Path parameters

NameTypeDescription
idrequired
string
e.g. "6ba7b810-9dad-11d1-80b4-00c04fd430c8"

Header parameters

NameTypeDescription
EPD-Version
string
API version override (format `YYYY-MM-DD`). If omitted, your account's pinned version or the latest version is used.
e.g. "2026-02-11"

Request body required

FieldTypeDescription
image_idsrequired
array[string]
Image IDs in desired display order.
e.g. ["6ba7b816-9dad-11d1-80b4-00c04fd430d3","6ba7b816-9dad-11d1-80b4-00c04fd430d4","6ba7b816-9dad-11d1-80b4-00c04fd430d5"]

Code samples

curl -X PATCH https://api.epd.com/v1/products/6ba7b810-9dad-11d1-80b4-00c04fd430c8/images/reorder \
  -H "Authorization: Bearer epd_test_sk_xxxx" \
  -H "Content-Type: application/json" \
  -H "EPD-Version: 2026-02-11" \
  -d '{
    "image_ids": ["6ba7b816-9dad-11d1-80b4-00c04fd430d3", "6ba7b816-9dad-11d1-80b4-00c04fd430d4", "6ba7b816-9dad-11d1-80b4-00c04fd430d5"]
  }'
const response = await fetch('https://api.epd.com/v1/products/6ba7b810-9dad-11d1-80b4-00c04fd430c8/images/reorder', {
  method: 'PATCH',
  headers: {
    'Authorization': 'Bearer epd_test_sk_xxxx',
    'Content-Type': 'application/json',
    'EPD-Version': '2026-02-11',
  },
  body: JSON.stringify({
    image_ids: [
      '6ba7b816-9dad-11d1-80b4-00c04fd430d3',
      '6ba7b816-9dad-11d1-80b4-00c04fd430d4',
      '6ba7b816-9dad-11d1-80b4-00c04fd430d5',
    ],
  }),
});

const result = await response.json();
console.log(result.data); // [{ id: '...', order: 0 }, ...]
import requests

response = requests.patch(
    'https://api.epd.com/v1/products/6ba7b810-9dad-11d1-80b4-00c04fd430c8/images/reorder',
    headers={
        'Authorization': 'Bearer epd_test_sk_xxxx',
        'EPD-Version': '2026-02-11',
    },
    json={
        'image_ids': [
            '6ba7b816-9dad-11d1-80b4-00c04fd430d3',
            '6ba7b816-9dad-11d1-80b4-00c04fd430d4',
            '6ba7b816-9dad-11d1-80b4-00c04fd430d5',
        ]
    },
)

result = response.json()
print(result['data'])
$ch = curl_init('https://api.epd.com/v1/products/6ba7b810-9dad-11d1-80b4-00c04fd430c8/images/reorder');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'PATCH',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer epd_test_sk_xxxx',
        'Content-Type: application/json',
        'EPD-Version: 2026-02-11',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'image_ids' => [
            '6ba7b816-9dad-11d1-80b4-00c04fd430d3',
            '6ba7b816-9dad-11d1-80b4-00c04fd430d4',
            '6ba7b816-9dad-11d1-80b4-00c04fd430d5',
        ],
    ]),
]);

$response = json_decode(curl_exec($ch), true);
print_r($response['data']);

Responses

200 Images reordered.
FieldTypeDescription
data
array[object]
400 Bad Request. The request was invalid or cannot be served.
FieldTypeDescription
errorrequired
object
typerequired
enum
The type of error.
invalid_request_errorauthentication_errorauthorization_errorrate_limit_erroridempotency_errorprocessing_errorwebhook_error
coderequired
string
A short string identifying the specific error.
e.g. "validation_error"
messagerequired
string
A human-readable message providing details about the error.
e.g. "Request validation failed"
paramnullable
string
The parameter that caused the error, if applicable.
e.g. "email"
request_id
string
Unique request identifier for debugging.
e.g. "req_a1b2c3d4e5f67890abcdef0123456789"
field_errors
array[object]
Detailed field-level errors for validation failures.
404 Not Found. The requested resource doesn't exist.
FieldTypeDescription
errorrequired
object
typerequired
enum
The type of error.
invalid_request_errorauthentication_errorauthorization_errorrate_limit_erroridempotency_errorprocessing_errorwebhook_error
coderequired
string
A short string identifying the specific error.
e.g. "validation_error"
messagerequired
string
A human-readable message providing details about the error.
e.g. "Request validation failed"
paramnullable
string
The parameter that caused the error, if applicable.
e.g. "email"
request_id
string
Unique request identifier for debugging.
e.g. "req_a1b2c3d4e5f67890abcdef0123456789"
field_errors
array[object]
Detailed field-level errors for validation failures.