On this page
- Endpoints
- List products
- Query parameters
- Request
- Response 200
- Get a product
- Query parameters
- Request
- Response 200
- Create a product
- Request body
- Request
- Response 201
- Batch create products
- Request body
- Request
- Response 201
- Update a product
- Request body
- Request
- Response 200
- Delete a product
- Request
- Response 204
- Duplicate a product
- Request body
- Request
- Response 201
- List variants
- Request
- Response 200
- Create a variant
- Request body
- Response 201
- Generate variants
- Request body
- Response 201
- List images
- Response 200
- Upload an image
- Form fields
- Request
- Response 201
- Attach existing images
- Request body
- Request
- Response 201
- Webhooks
- The product object
Products
Products are the core resource in Hydra. Each product has a title, handle (URL slug), description, and status. Products can have up to 5 options (e.g. Size, Color) and 250 variants representing each combination.
Base URL: https://api.hydrajs.dev
Endpoints
| Method | Path | Auth | Description |
|---|---|---|---|
GET |
/v1/products |
Publishable | List products |
POST |
/v1/products |
Secret | Create a product |
POST |
/v1/products/batch |
Secret | Batch create products |
GET |
/v1/products/{id} |
Publishable | Get a product |
PATCH |
/v1/products/{id} |
Secret | Update a product |
DELETE |
/v1/products/{id} |
Secret | Delete a product (soft) |
POST |
/v1/products/{id}/duplicate |
Secret | Duplicate a product |
GET |
/v1/products/{id}/variants |
Publishable | List variants |
POST |
/v1/products/{id}/variants |
Secret | Create a variant |
POST |
/v1/products/{id}/variants/generate |
Secret | Generate variants from combinations |
GET |
/v1/products/{id}/images |
Publishable | List images |
POST |
/v1/products/{id}/images |
Secret | Upload an image |
POST |
/v1/products/{id}/images/attach |
Secret | Attach existing images |
List products
GET /v1/products
Returns a paginated list of products. Supports search, filtering, sorting, and sparse fieldsets.
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit |
integer | 25 |
Results per page (1–100) |
cursor |
string | - | Pagination cursor from a previous response |
sort |
string | created_at |
Sort field: created_at, updated_at, title |
order |
string | desc |
Sort direction: asc, desc |
status |
string | - | Filter by status: active, draft, archived, preorder, coming_soon, discontinued |
collection_id |
string | - | Filter by collection membership |
search |
string | - | Full-text search with typo correction |
created_after |
ISO 8601 | - | Filter: created after this date |
created_before |
ISO 8601 | - | Filter: created before this date |
currency |
string | - | 3-letter ISO currency code for price conversion |
fields |
string | - | Comma-separated fields to return |
Request
curl https://api.hydrajs.dev/v1/products?status=active&limit=10 \
-H "Authorization: Bearer pk_live_YOUR_KEY"
Response 200
{
"data": [
{
"id": "prod_abc123",
"title": "Classic T-Shirt",
"handle": "classic-t-shirt",
"status": "active",
"product_type": "Apparel",
"brand": "Hydra Basics",
"created_at": "2026-01-15T10:30:00Z",
"updated_at": "2026-08-01T14:22:00Z"
}
],
"pagination": {
"cursor": "eyJ0IjoiMjAyNi...",
"has_more": true,
"total": 42
}
}
ℹPagination
All list endpoints use cursor-based pagination. Pass the cursor value from the response to fetch
the next page.
Get a product
GET /v1/products/{id}
Returns a single product by ID. Use expand to include related resources inline.
Query parameters
| Parameter | Type | Description |
|---|---|---|
expand |
string | Comma-separated: variants, images, prices |
currency |
string | 3-letter ISO currency code for price conversion |
fields |
string | Comma-separated fields to return |
Request
curl https://api.hydrajs.dev/v1/products/prod_abc123?expand=variants,images \
-H "Authorization: Bearer pk_live_YOUR_KEY"
Response 200
{
"data": {
"id": "prod_abc123",
"title": "Classic T-Shirt",
"handle": "classic-t-shirt",
"subtitle": null,
"description": "A comfortable everyday t-shirt.",
"status": "active",
"product_type": "Apparel",
"brand": "Hydra Basics",
"google_product_category": "Apparel & Accessories > Clothing > Shirts & Tops",
"hs_code": "6109.10",
"country_of_origin": "US",
"fulfillment_type": "physical",
"tags": ["basics", "cotton"],
"options": [{ "name": "Size", "values": ["S", "M", "L", "XL"], "position": 0 }],
"specifications": [
{ "label": "Material", "value": "100% Cotton" },
{ "label": "Weight", "value": "180 GSM" }
],
"seo": {
"title": "Classic T-Shirt | Hydra Basics",
"description": "A comfortable everyday t-shirt in 100% cotton."
},
"metadata": {},
"variants": [
{
"id": "var_def456",
"title": "S",
"sku": "TSH-S",
"barcode": null,
"price": 2999,
"sale_price": null,
"cost": 800,
"inventory_quantity": 50,
"weight": 180,
"weight_unit": "g",
"hs_code": null,
"country_of_origin": null,
"options": { "Size": "S" },
"position": 0
}
],
"images": [
{
"id": "img_ghi789",
"src": "https://cdn.hydrajs.dev/stores/store_xxx/img_ghi789.webp",
"alt": "Classic T-Shirt front view",
"width": 1200,
"height": 1600,
"position": 0
}
],
"created_at": "2026-01-15T10:30:00Z",
"updated_at": "2026-08-01T14:22:00Z"
}
}
ℹInternal notes
The internal_notes field is only returned for secret key requests. Publishable keys never see
this field.
Create a product
POST /v1/products
Creates a new product. Returns the created product with 201.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
title |
string | Yes | Product title (max 255 chars) |
handle |
string | No | URL slug. Auto-generated from title if omitted |
subtitle |
string | No | Short subtitle (max 65 chars). Requires subtitle extension |
description |
string | No | Markdown description (max 10,000 chars) |
status |
string | No | draft (default), active, archived, preorder, coming_soon, discontinued |
preorder_expected_date |
string | No | ISO 8601 date (e.g. 2026-12-01). Required when status is preorder |
product_type |
string | No | Free-text category (max 100 chars) |
brand |
string | No | Brand name (max 100 chars) |
google_product_category |
string | No | Google taxonomy path (max 500 chars) |
hs_code |
string | No | Harmonized System code for customs (max 20 chars) |
country_of_origin |
string | No | ISO 3166-1 alpha-2 country code (2 letters, e.g. US) |
fulfillment_type |
string | No | physical (default), digital, service |
internal_notes |
string | No | Private notes (max 5,000 chars) |
qty_step |
integer | No | Minimum quantity increment. Requires qty_step extension |
tags |
string[] | No | Array of tags (max 250 tags, each max 100 chars) |
options |
object[] | No | Product options (max 5). Each: name, values[], position |
variants |
object[] | No | Initial variants with pricing and inventory |
specifications |
object[] | No | Key-value specs (max 50). Each: label, value |
seo |
object | No | SEO metadata: title, description |
metadata |
object | No | Arbitrary key-value pairs (max 50 keys) |
ℹProduct statuses
Products with preorder status can be added to cart (requires preorder_expected_date). Products
with coming_soon or discontinued status are visible on the storefront but cart/checkout is
blocked.
Request
curl -X POST https://api.hydrajs.dev/v1/products \
-H "Authorization: Bearer sk_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Classic T-Shirt",
"status": "draft",
"product_type": "Apparel",
"tags": ["basics", "cotton"],
"variants": [
{
"title": "Default",
"price": 2999,
"inventory_quantity": 100
}
]
}'
Response 201
Returns the full product object (same shape as Get a product).
Batch create products
POST /v1/products/batch
Creates multiple products in a single atomic request. All products are created within a transaction - if any product fails validation, none are created.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
products |
object[] | Yes | Array of product objects (1–50). Each object accepts the same fields as Create a product |
⚠All-or-nothing
If any product in the batch has invalid data or a conflicting handle, the entire batch is rejected. No products are created.
Request
curl -X POST https://api.hydrajs.dev/v1/products/batch \
-H "Authorization: Bearer sk_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"products": [
{
"title": "Classic T-Shirt",
"status": "draft",
"variants": [{ "title": "Default", "price": 2999 }]
},
{
"title": "Slim Jeans",
"product_type": "Apparel",
"tags": ["denim"],
"variants": [{ "title": "Default", "price": 5999 }]
}
]
}'
Response 201
{
"data": [
{
"id": "prod_abc123",
"title": "Classic T-Shirt",
"handle": "classic-t-shirt",
"status": "draft",
"variants": [{ "id": "var_def456", "title": "Default", "price": 2999 }]
},
{
"id": "prod_xyz789",
"title": "Slim Jeans",
"handle": "slim-jeans",
"status": "draft",
"variants": [{ "id": "var_ghi012", "title": "Default", "price": 5999 }]
}
]
}
ℹRate limiting
A batch request counts as a single API call toward your rate limit and monthly quota, regardless of how many products are in the batch.
Update a product
PATCH /v1/products/{id}
Partially updates a product. Send only the fields you want to change. Returns the updated product.
Request body
All fields from Create a product are accepted, and all are optional. Additional fields:
| Field | Type | Description |
|---|---|---|
create_redirect |
boolean | If true and handle changed, creates a URL redirect from the old handle |
⚠Tags replace, not merge
Sending tags replaces the entire array. To add a tag, fetch the current tags, append, and send
the full list.
Request
curl -X PATCH https://api.hydrajs.dev/v1/products/prod_abc123 \
-H "Authorization: Bearer sk_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Premium T-Shirt",
"status": "active",
"tags": ["premium", "cotton", "new-arrival"]
}'
Response 200
Returns the full updated product object.
Delete a product
DELETE /v1/products/{id}
Soft-deletes a product. The product is hidden from all queries and its handle is freed for reuse. Permanently purged after 30 days.
Request
curl -X DELETE https://api.hydrajs.dev/v1/products/prod_abc123 \
-H "Authorization: Bearer sk_live_YOUR_KEY"
Response 204
Empty body.
Duplicate a product
POST /v1/products/{id}/duplicate
Creates a copy of an existing product with a new title and handle.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
title |
string | Yes | Title for the new product |
include_variants |
boolean | No | Copy variants (default: true) |
include_images |
boolean | No | Copy images (default: true) |
Request
curl -X POST https://api.hydrajs.dev/v1/products/prod_abc123/duplicate \
-H "Authorization: Bearer sk_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "title": "Classic T-Shirt (Copy)" }'
Response 201
Returns the new product object.
List variants
GET /v1/products/{id}/variants
Returns all variants for a product.
Request
curl https://api.hydrajs.dev/v1/products/prod_abc123/variants \
-H "Authorization: Bearer pk_live_YOUR_KEY"
Response 200
{
"data": [
{
"id": "var_def456",
"title": "S",
"sku": "TSH-S",
"price": 2999,
"sale_price": null,
"cost": 800,
"inventory_quantity": 50,
"options": { "Size": "S" },
"position": 0
}
]
}
Create a variant
POST /v1/products/{id}/variants
Adds a variant to a product.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
title |
string | Yes | Variant title (max 255 chars) |
sku |
string | No | Stock keeping unit (max 100 chars) |
barcode |
string | No | Barcode / UPC (max 100 chars) |
price |
integer | Yes | Price in cents (e.g. 2999 = $29.99) |
sale_price |
integer | No | Original price in cents for sale display |
inventory_quantity |
integer | No | Stock count (default: 0) |
weight |
integer | No | Weight in weight_unit units |
weight_unit |
string | No | g (default), kg, oz, lb |
hs_code |
string | No | Harmonized System code for customs (max 20 chars) |
country_of_origin |
string | No | ISO 3166-1 alpha-2 country code (2 letters, e.g. US) |
options |
object | No | Option values: { "Size": "M", "Color": "Blue" } |
Response 201
Returns the created variant object.
Generate variants
POST /v1/products/{id}/variants/generate
Bulk-creates variants from option combinations. Maximum 250 variants per product.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
combinations |
object[] | Yes | Array of option maps: [{ "Size": "S", "Color": "Red" }, ...] |
Response 201
{
"data": {
"variants": [
{
"id": "var_xxx",
"title": "S / Red",
"price": 0,
"options": { "Size": "S", "Color": "Red" }
}
]
}
}
List images
GET /v1/products/{id}/images
Returns all images for a product, ordered by position.
Response 200
{
"data": [
{
"id": "img_ghi789",
"src": "https://cdn.hydrajs.dev/stores/store_xxx/img_ghi789.webp",
"alt": "Front view",
"width": 1200,
"height": 1600,
"position": 0,
"lqip": "base64..."
}
]
}
Upload an image
POST /v1/products/{id}/images
Uploads an image file and attaches it to the product. Send as multipart/form-data.
Form fields
| Field | Type | Required | Description |
|---|---|---|---|
file |
File | Yes | Image file (JPEG, PNG, WebP, GIF, AVIF, HEIC). 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.
Attach existing images
POST /v1/products/{id}/images/attach
Attaches images from the media library to a product without re-uploading.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
image_ids |
string[] | Yes | Array of image IDs to attach |
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_aaa", "img_bbb"] }'
Response 201
Returns the attached image objects.
Webhooks
Product changes fire the following webhook events:
| Event | Trigger |
|---|---|
product.created |
Product created or duplicated |
product.updated |
Product fields updated |
product.deleted |
Product soft-deleted |
See Webhooks for subscription setup.
The product object
| Field | Type | Description |
|---|---|---|
id |
string | Unique ID (prefix: prod_) |
title |
string | Product title |
handle |
string | URL-safe slug |
subtitle |
string | null | Short subtitle |
description |
string | null | Markdown-formatted text |
status |
string | draft, active, archived, preorder, coming_soon, discontinued |
preorder_expected_date |
string | null | ISO 8601 date. Set when status is preorder |
product_type |
string | null | Free-text category |
brand |
string | null | Brand name |
google_product_category |
string | null | Google taxonomy path |
hs_code |
string | null | Harmonized System code for customs |
country_of_origin |
string | null | ISO 3166-1 alpha-2 country code |
fulfillment_type |
string | physical, digital, service |
internal_notes |
string | null | Private notes (secret key only) |
qty_step |
integer | null | Minimum order quantity increment |
tags |
string[] | Product tags |
options |
object[] | Option definitions with name, values[], position |
specifications |
object[] | Key-value specs with label, value |
seo |
object | title, description |
metadata |
object | Arbitrary key-value pairs |
variants |
object[] | Expanded with ?expand=variants |
images |
object[] | Expanded with ?expand=images |
created_at |
string | ISO 8601 timestamp |
updated_at |
string | ISO 8601 timestamp |