On this page
- Endpoints
- List library images
- Query parameters
- Request
- Response 200
- Upload image to library
- Form fields
- Request
- Response 201
- List product images
- Request
- Response 200
- Upload image to product
- Form fields
- Request
- Response 201
- Attach library images to product
- Request body
- Request
- Response 201
- Update image metadata
- Request body
- Request
- Response 200
- Delete image
- Request
- Response 204
- Detach image from product
- Request
- Response 204
- Batch delete images
- Request body
- Request
- Response 204
- Batch detach images
- Request body
- Request
- Response 204
- Reorder images
- Request body
- Request
- Response 204
- The image object
Images
Images exist at two levels in Hydra: the store-level media library and product-level attachments. You can upload images directly to a product, or upload to the library first and attach them to products later. Images are stored on Cloudflare R2 and served via CDN.
Base URL: https://api.hydrajs.dev
Endpoints
| Method | Path | Auth | Description |
|---|---|---|---|
GET |
/v1/images |
Publishable | List images in store library |
POST |
/v1/images |
Secret | Upload image to library |
GET |
/v1/products/{id}/images |
Publishable | List images for a product |
POST |
/v1/products/{id}/images |
Secret | Upload image to product |
POST |
/v1/products/{id}/images/attach |
Secret | Attach library images to product |
PATCH |
/v1/images/{id} |
Secret | Update image metadata |
DELETE |
/v1/images/{id} |
Secret | Delete image |
POST |
/v1/images/{id}/detach |
Secret | Detach image from product |
POST |
/v1/images/batch-delete |
Secret | Delete multiple images |
POST |
/v1/images/batch-detach |
Secret | Detach multiple images |
PATCH |
/v1/images/reorder |
Secret | Reorder images by position |
List library images
GET /v1/images
Returns a paginated list of images in the store’s media library. Supports filtering by category, attachment status, and search.
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit |
integer | 25 |
Results per page (1–100) |
cursor |
string | - | Pagination cursor from a previous response |
search |
string | - | Search by filename or alt text (max 200 chars) |
category |
string | - | Filter by category: product, collection, blog, logo, general, page, marketing |
unattached |
string | - | Filter by attachment status: "true" or "false" |
fields |
string | - | Comma-separated fields to return |
Request
curl "https://api.hydrajs.dev/v1/images?category=product&unattached=true&limit=20" \
-H "Authorization: Bearer pk_live_YOUR_KEY"
Response 200
{
"data": [
{
"id": "img_abc123def456ghij",
"product_id": null,
"src": "https://cdn.hydrajs.dev/stores/store_xxx/img_abc123def456ghij.webp",
"alt": null,
"position": 0,
"width": 2400,
"height": 1600,
"file_size": 184320,
"lqip": "AAAQGH...",
"filename": "hero-banner.webp",
"mime_type": "image/webp",
"category": "product",
"slug": "hero-banner",
"slug_locked": false,
"slug_url": "https://cdn.hydrajs.dev/store_xxx/i/hero-banner",
"duplicate": false,
"created_at": "2026-07-20T09:00:00Z",
"updated_at": "2026-07-20T09:00:00Z"
}
],
"pagination": {
"cursor": "eyJ0IjoiMjAyNi...",
"has_more": false,
"total": 1
}
}
ℹPagination
All list endpoints use cursor-based pagination. Pass the cursor value from the response to fetch
the next page.
Upload image to library
POST /v1/images
Uploads an image file to the store’s media library without attaching it to a product. Send as multipart/form-data.
Form fields
| Field | Type | Required | Description |
|---|---|---|---|
file |
File | Yes | Image file. Max 10 MB |
lqip |
string | No | Base64 ThumbHash for low-quality image placeholder (generated client-side) |
ℹSupported formats
JPEG, PNG, WebP, GIF, AVIF, HEIC, and HEIF. Maximum file size is 10 MB. Image dimensions are extracted automatically via Cloudflare Images.
Request
curl -X POST https://api.hydrajs.dev/v1/images \
-H "Authorization: Bearer sk_live_YOUR_KEY" \
-F "file=@product-photo.jpg"
Response 201
{
"data": {
"id": "img_klm789nop012qrst",
"product_id": null,
"src": "https://cdn.hydrajs.dev/stores/store_xxx/img_klm789nop012qrst.jpg",
"alt": null,
"position": 0,
"width": 1200,
"height": 1600,
"file_size": 245760,
"lqip": null,
"filename": "product-photo.jpg",
"mime_type": "image/jpeg",
"category": null,
"slug": "product-photo",
"slug_locked": false,
"slug_url": "https://cdn.hydrajs.dev/store_xxx/i/product-photo",
"duplicate": false,
"created_at": "2026-08-17T10:30:00Z",
"updated_at": "2026-08-17T10:30:00Z"
}
}
⚠Content-hash deduplication
Images are content-hashed. If you upload a file that is identical to one already in the library,
the existing image is returned with "duplicate": true instead of creating a new entry.
List product images
GET /v1/products/{id}/images
Returns all images attached to a product, ordered by position.
Request
curl https://api.hydrajs.dev/v1/products/prod_abc123/images \
-H "Authorization: Bearer pk_live_YOUR_KEY"
Response 200
{
"data": [
{
"id": "img_ghi789jkl012mnop",
"product_id": "prod_abc123",
"src": "https://cdn.hydrajs.dev/stores/store_xxx/img_ghi789jkl012mnop.webp",
"alt": "Classic T-Shirt front view",
"position": 0,
"width": 1200,
"height": 1600,
"file_size": 184320,
"lqip": "AAAQGH...",
"filename": "tshirt-front.webp",
"mime_type": "image/webp",
"category": "product",
"slug": "tshirt-front",
"slug_locked": false,
"slug_url": "https://cdn.hydrajs.dev/store_xxx/i/tshirt-front",
"duplicate": false,
"created_at": "2026-05-01T14:00:00Z",
"updated_at": "2026-05-01T14:00:00Z"
},
{
"id": "img_qrs345tuv678wxyz",
"product_id": "prod_abc123",
"src": "https://cdn.hydrajs.dev/stores/store_xxx/img_qrs345tuv678wxyz.webp",
"alt": "Classic T-Shirt back view",
"position": 1,
"width": 1200,
"height": 1600,
"file_size": 156800,
"lqip": "AAAQFk...",
"filename": "tshirt-back.webp",
"mime_type": "image/webp",
"category": "product",
"slug": "tshirt-back",
"slug_locked": false,
"slug_url": "https://cdn.hydrajs.dev/store_xxx/i/tshirt-back",
"duplicate": false,
"created_at": "2026-05-01T14:01:00Z",
"updated_at": "2026-05-01T14:01:00Z"
}
]
}
Upload image to product
POST /v1/products/{id}/images
Uploads an image file and attaches it to a specific product. Send as multipart/form-data.
Form fields
| Field | Type | Required | Description |
|---|---|---|---|
file |
File | Yes | Image file (JPEG, PNG, WebP, GIF, AVIF, HEIC, HEIF). Max 10 MB |
lqip |
string | No | Base64 ThumbHash for low-quality image placeholder |
Request
curl -X POST https://api.hydrajs.dev/v1/products/prod_abc123/images \
-H "Authorization: Bearer sk_live_YOUR_KEY" \
-F "file=@photo.jpg"
Response 201
Returns the created image object with product_id set to the target product.
Attach library images to product
POST /v1/products/{id}/images/attach
Attaches one or more images from the media library to a product without re-uploading. Images that are already attached to another product are duplicated.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
image_ids |
string[] | Yes | Array of image IDs to attach (1–50) |
Request
curl -X POST https://api.hydrajs.dev/v1/products/prod_abc123/images/attach \
-H "Authorization: Bearer sk_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"image_ids": ["img_abc123def456ghij", "img_klm789nop012qrst"]}'
Response 201
{
"data": [
{
"id": "img_abc123def456ghij",
"product_id": "prod_abc123",
"src": "https://cdn.hydrajs.dev/stores/store_xxx/img_abc123def456ghij.webp",
"alt": null,
"position": 2,
"width": 2400,
"height": 1600,
"file_size": 184320,
"lqip": "AAAQGH...",
"filename": "hero-banner.webp",
"mime_type": "image/webp",
"category": "product",
"slug": "hero-banner",
"slug_locked": false,
"slug_url": "https://cdn.hydrajs.dev/store_xxx/i/hero-banner",
"duplicate": false,
"created_at": "2026-07-20T09:00:00Z",
"updated_at": "2026-08-17T11:00:00Z"
}
]
}
Update image metadata
PATCH /v1/images/{id}
Updates an image’s metadata. Only the fields you send are changed.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
alt |
string | No | Alt text for accessibility (max 500 chars) |
position |
integer | No | Display order (min 0) |
slug |
string | No | URL-safe slug for friendly CDN URLs. Must match ^[a-z0-9]+(?:-[a-z0-9]+)*$ (max 200 chars) |
category |
string | No | Image category: product, collection, blog, logo, general, page, marketing |
Request
curl -X PATCH https://api.hydrajs.dev/v1/images/img_ghi789jkl012mnop \
-H "Authorization: Bearer sk_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"alt": "Classic T-Shirt front view on white background",
"slug": "classic-tshirt-front",
"category": "product"
}'
Response 200
{
"data": {
"id": "img_ghi789jkl012mnop",
"product_id": "prod_abc123",
"src": "https://cdn.hydrajs.dev/stores/store_xxx/img_ghi789jkl012mnop.webp",
"alt": "Classic T-Shirt front view on white background",
"position": 0,
"width": 1200,
"height": 1600,
"file_size": 184320,
"lqip": "AAAQGH...",
"filename": "tshirt-front.webp",
"mime_type": "image/webp",
"category": "product",
"slug": "classic-tshirt-front",
"slug_locked": false,
"slug_url": "https://cdn.hydrajs.dev/store_xxx/i/classic-tshirt-front",
"duplicate": false,
"created_at": "2026-05-01T14:00:00Z",
"updated_at": "2026-08-17T11:15:00Z"
}
}
Delete image
DELETE /v1/images/{id}
Permanently deletes an image from storage and the database. This action cannot be undone.
Request
curl -X DELETE https://api.hydrajs.dev/v1/images/img_ghi789jkl012mnop \
-H "Authorization: Bearer sk_live_YOUR_KEY"
Response 204
Empty body.
Detach image from product
POST /v1/images/{id}/detach
Removes an image’s association with a product without deleting it. The image remains in the store’s media library for reuse.
Request
curl -X POST https://api.hydrajs.dev/v1/images/img_ghi789jkl012mnop/detach \
-H "Authorization: Bearer sk_live_YOUR_KEY"
Response 204
Empty body.
Batch delete images
POST /v1/images/batch-delete
Deletes multiple images in one request. Permanently removes them from storage and the database.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
image_ids |
string[] | Yes | Array of image IDs to delete (1–50) |
Request
curl -X POST https://api.hydrajs.dev/v1/images/batch-delete \
-H "Authorization: Bearer sk_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"image_ids": ["img_abc123def456ghij", "img_klm789nop012qrst"]}'
Response 204
Empty body.
Batch detach images
POST /v1/images/batch-detach
Detaches multiple images from their products without deleting them. The images remain in the media library.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
image_ids |
string[] | Yes | Array of image IDs to detach (1–50) |
Request
curl -X POST https://api.hydrajs.dev/v1/images/batch-detach \
-H "Authorization: Bearer sk_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"image_ids": ["img_abc123def456ghij", "img_klm789nop012qrst"]}'
Response 204
Empty body.
Reorder images
PATCH /v1/images/reorder
Sets the display position for multiple images. Useful for drag-and-drop reordering in the admin panel.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
images |
object[] | Yes | Array of {id, position} pairs (1–100) |
images[].id |
string | Yes | Image ID |
images[].position |
integer | Yes | New position (min 0) |
Request
curl -X PATCH https://api.hydrajs.dev/v1/images/reorder \
-H "Authorization: Bearer sk_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"images": [
{"id": "img_ghi789jkl012mnop", "position": 0},
{"id": "img_qrs345tuv678wxyz", "position": 1},
{"id": "img_abc123def456ghij", "position": 2}
]
}'
Response 204
Empty body.
The image object
| Field | Type | Description |
|---|---|---|
id |
string | Unique ID (prefix: img_) |
product_id |
string | null | Product this image is attached to, or null for library-only images |
src |
string | Full CDN URL for the image |
alt |
string | null | Alt text for accessibility |
position |
integer | Display order (0-indexed) |
width |
integer | null | Image width in pixels |
height |
integer | null | Image height in pixels |
file_size |
integer | null | File size in bytes |
lqip |
string | null | Base64-encoded ThumbHash for low-quality image placeholder |
filename |
string | null | Original filename |
mime_type |
string | null | MIME type (e.g. image/webp) |
category |
string | null | Image category: product, collection, blog, logo, general, page, marketing |
slug |
string | null | URL-safe slug for friendly CDN paths |
slug_locked |
boolean | Whether the slug is locked from auto-generation |
slug_url |
string | null | Full friendly CDN URL using the slug (e.g. https://cdn.hydrajs.dev/store_xxx/i/product-photo) |
duplicate |
boolean | true if the upload was deduplicated against an existing image |
created_at |
string | ISO 8601 timestamp |
updated_at |
string | ISO 8601 timestamp |