API and developers

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_values is immutable: Once a variant is created, you cannot change its option_values via 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: PATCH returns 200 with the updated resource. DELETE returns 204 with 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.

Need help?