Manage Products
Full CRUD for products, variants, options, media, and categories
This guide covers all operations to query, create, update, and delete products and their sub-entities.
For all available fields, types, and validations for each endpoint, see the API Reference.
Important Rules
Before you begin, keep these rules in mind — they apply to all product management:
Mandatory creation order: Product → Options → Variants → Media / Categories. You cannot create variants without options first.
- Product status: Currently the only supported value is
"published". The status is returned as-is in responses. option_valuesis immutable: Once a variant is created, you cannot change itsoption_valuesvia PATCH. If you need to fix them, delete the variant and create it again.- Supported currencies:
MXN,PEN,COP,CLP,ARS,BRL,VES,BOB,USD,PYG,UYU,CRC,EUR. - Currency must match the catalog's currency: All variants of a product must use the same currency configured in the catalog. Using a different currency may cause internal inconsistencies.
- Responses:
PATCHreturns200with the updated resource.DELETEreturns204with no body. - Every product requires at least one variant: Even simple products (no sizes or colors) need at minimum one option and one variant. Use a generic name like "Presentation" or "Unit".
- No batch endpoints: Each entity must be created individually.
Query Products
List products
Returns a paginated list of products with all their sub-entities.
curl -X GET "https://api.ya.onl/v1/products?page=1&limit=25" \
-H "X-API-Key: yv_live_v1_your_api_key"The response includes pagination:
{
"data": [ /* array of products */ ],
"pagination": {
"page": 1,
"limit": 25,
"total_pages": 4,
"total_items": 100,
"has_more": true
}
}limit accepts a maximum of 100. See Pagination for more details.
Get a product
Returns a product with all its options, variants (with media), and categories.
curl -X GET "https://api.ya.onl/v1/products/9876" \
-H "X-API-Key: yv_live_v1_your_api_key"Response example:
{
"id": 9876,
"created_at": "2026-01-15T10:00:00Z",
"name": "Polo Negro",
"description": "Polo de algodón pima de alta calidad",
"characteristics": "Material: 100% algodón pima. Peso: 180g/m².",
"status": "published",
"categories": [
{ "id": 1, "name": "Ropa" }
],
"options": [
{
"id": 10,
"name": "Talla",
"values": [
{ "id": 100, "name": "S" },
{ "id": 101, "name": "M" },
{ "id": 102, "name": "L" }
]
}
],
"variants": [
{
"id": 500,
"name": "Polo Negro - Talla M",
"base_price": 49.90,
"currency": "PEN",
"status": "available",
"media": [
{
"id": 1000,
"title": "Front view",
"url": "https://cdn.example.com/polo-negro-m-front.jpg"
}
]
}
]
}Create a Complete Product
1. Create the product
curl -X POST "https://api.ya.onl/v1/products" \
-H "X-API-Key: yv_live_v1_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"catalog_id": 1,
"name": "Polo Negro",
"description": "Polo de algodón pima de alta calidad",
"characteristics": "Material: 100% algodón pima. Peso: 180g/m².",
"status": "published"
}'2. Add options
The response includes the IDs of the created values. Save these IDs to use them when creating variants.
curl -X POST "https://api.ya.onl/v1/products/9876/options" \
-H "X-API-Key: yv_live_v1_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "Talla",
"values": ["S", "M", "L"]
}'Need to add more values to an option? Use POST /v1/products/{product_id}/options/{option_id}/values with the new value name (e.g. {"name": "XL"}).
3. Create variants
Each variant must include option_values with at least one option value ID.
curl -X POST "https://api.ya.onl/v1/products/9876/variants" \
-H "X-API-Key: yv_live_v1_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "Polo Negro - Talla M",
"base_price": 49.90,
"currency": "PEN",
"status": "available",
"option_values": [101]
}'4. Add media to the variant
curl -X POST "https://api.ya.onl/v1/products/9876/variants/500/media" \
-H "X-API-Key: yv_live_v1_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"title": "Front view",
"url": "https://cdn.example.com/polo-negro-m-front.jpg"
}'Media URLs must be public and accessible. The system cannot access private or authentication-protected URLs.
Allowed file types:
| Type | Extension | Max size |
|---|---|---|
| Image | jpeg |
5 MB |
| Image | png |
5 MB |
| Video | 3gp |
16 MB |
| Video | mp4 |
16 MB |
| Document | pdf |
100 MB |
5. Assign categories
This endpoint is idempotent: if a tag is already assigned, it is skipped and the response is 200. Only returns 201 when new tags are assigned.
curl -X POST "https://api.ya.onl/v1/products/9876/categories" \
-H "X-API-Key: yv_live_v1_your_api_key" \
-H "Content-Type: application/json" \
-d '{"tag_ids": [1, 2]}'Update Entities
Update product
curl -X PATCH "https://api.ya.onl/v1/products/9876" \
-H "X-API-Key: yv_live_v1_your_api_key" \
-H "Content-Type: application/json" \
-d '{"name": "Polo Negro Premium"}'Update option
curl -X PATCH "https://api.ya.onl/v1/products/9876/options/10" \
-H "X-API-Key: yv_live_v1_your_api_key" \
-H "Content-Type: application/json" \
-d '{"name": "Talla (cm)"}'Update option value
curl -X PATCH "https://api.ya.onl/v1/products/9876/options/10/values/100" \
-H "X-API-Key: yv_live_v1_your_api_key" \
-H "Content-Type: application/json" \
-d '{"name": "Small"}'Update variant
curl -X PATCH "https://api.ya.onl/v1/products/9876/variants/500" \
-H "X-API-Key: yv_live_v1_your_api_key" \
-H "Content-Type: application/json" \
-d '{"base_price": 59.90, "status": "not_available"}'Update media
curl -X PATCH "https://api.ya.onl/v1/products/9876/variants/500/media/1000" \
-H "X-API-Key: yv_live_v1_your_api_key" \
-H "Content-Type: application/json" \
-d '{"title": "Updated front view"}'Delete Entities
Remove category from a product
This unlinks the category from the product, but does not delete the global category.
curl -X DELETE "https://api.ya.onl/v1/products/9876/categories/1" \
-H "X-API-Key: yv_live_v1_your_api_key"Delete media
curl -X DELETE "https://api.ya.onl/v1/products/9876/variants/500/media/1000" \
-H "X-API-Key: yv_live_v1_your_api_key"Delete variant
Deleting a variant also deletes all its associated media.
curl -X DELETE "https://api.ya.onl/v1/products/9876/variants/500" \
-H "X-API-Key: yv_live_v1_your_api_key"Delete option value
You cannot delete an option value if it is associated with an active variant.
curl -X DELETE "https://api.ya.onl/v1/products/9876/options/10/values/100" \
-H "X-API-Key: yv_live_v1_your_api_key"Delete option
Deleting an option also deletes all its values. Variants associated with those values may end up in an inconsistent state.
curl -X DELETE "https://api.ya.onl/v1/products/9876/options/10" \
-H "X-API-Key: yv_live_v1_your_api_key"Delete entire product
Deleting a product deletes all its variants, options, option values, and media. This action cannot be undone.
curl -X DELETE "https://api.ya.onl/v1/products/9876" \
-H "X-API-Key: yv_live_v1_your_api_key"Simple Product (no variations)
Not all products have sizes or colors. Even so, every product requires at least one option and one variant. Use a generic name for the option.
# 1. Create product
curl -X POST "https://api.ya.onl/v1/products" \
-H "X-API-Key: yv_live_v1_your_api_key" \
-H "Content-Type: application/json" \
-d '{"catalog_id": 1, "name": "Dona Glaseada", "description": "Dona artesanal con glaseado de azúcar", "status": "published"}'
# 2. Create generic option
curl -X POST "https://api.ya.onl/v1/products/9877/options" \
-H "X-API-Key: yv_live_v1_your_api_key" \
-H "Content-Type: application/json" \
-d '{"name": "Presentación", "values": ["Estándar"]}'
# 3. Create single variant
curl -X POST "https://api.ya.onl/v1/products/9877/variants" \
-H "X-API-Key: yv_live_v1_your_api_key" \
-H "Content-Type: application/json" \
-d '{"name": "Dona Glaseada", "base_price": 5.00, "currency": "PEN", "option_values": [200]}'
# 4. Add image
curl -X POST "https://api.ya.onl/v1/products/9877/variants/600/media" \
-H "X-API-Key: yv_live_v1_your_api_key" \
-H "Content-Type: application/json" \
-d '{"title": "Dona Glaseada", "url": "https://cdn.example.com/dona-glaseada.jpg"}'The option name is free-form: "Presentación", "Tipo", "Unidad", etc.
Common Errors
Validation errors (422) return a body with details about the failing field:
{
"detail": [
{
"loc": ["body", "option_values"],
"msg": "List should have at least 1 item",
"type": "too_short"
}
]
}You are creating a variant without option_values or with an empty list. You must create options first with POST /v1/products/{product_id}/options and use the IDs of the returned values.
Creating variants before options
If you try to create a variant before creating options, you will not have value IDs available for the option_values field. Always create options first.
Using name instead of tag_id for categories
The POST /v1/products/{product_id}/categories endpoint requires tag_ids (a list of numeric IDs), not category names. First create the category with POST /v1/categories and use the returned id.
Forgetting catalog_id when creating a product
The catalog_id field is required when creating a product. Get your catalog with GET /v1/catalogs.
Invalid option_value IDs
The option_values IDs must correspond to values from the same product. Using IDs from another product or nonexistent IDs will result in an error.
Trying to change option_values of an existing variant
PATCH /v1/products/{product_id}/variants/{variant_id} does not accept option_values. Option values for a variant are assigned only at creation time. If you need to fix them, delete the variant and create it again with the correct values.
