> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ya.works/llms.txt
> Use this file to discover all available pages before exploring further.

# Gestionar Productos

> CRUD completo de productos, variantes, opciones, media y categorías

# Gestionar Productos

Esta guía cubre todas las operaciones para consultar, crear, actualizar y eliminar productos y sus sub-entidades.

<Info>
  Para ver todos los campos disponibles, tipos y validaciones de cada endpoint, consulta el [API Reference](/api-reference).
</Info>

## Reglas Importantes

Antes de comenzar, ten en cuenta estas reglas que aplican a toda la gestión de productos:

<Warning>
  **Orden de creación obligatorio:** Producto → Opciones → Variantes → Media / Categorías. No puedes crear variantes sin opciones previas.
</Warning>

* **Status del producto:** Actualmente el único valor soportado es `"published"`. El status se devuelve igual en las respuestas.
* **`option_values` es inmutable:** Una vez creada la variante, no puedes cambiar sus `option_values` con PATCH. Si necesitas corregirlos, elimina la variante y créala de nuevo.
* **Monedas soportadas:** `MXN`, `PEN`, `COP`, `CLP`, `ARS`, `BRL`, `VES`, `BOB`, `USD`, `PYG`, `UYU`, `CRC`, `EUR`.
* **La moneda debe coincidir con la del catálogo:** Todas las variantes de un producto deben usar la misma moneda configurada en el catálogo. Usar una moneda diferente puede causar inconsistencias internas.
* **Respuestas:** `PATCH` devuelve `200` con el recurso actualizado. `DELETE` devuelve `204` sin body.
* **Todo producto requiere al menos una variante:** Incluso productos simples (sin tallas ni colores) necesitan mínimo una opción y una variante. Usa un nombre genérico como "Presentación" o "Unidad".
* **No existen endpoints batch:** Cada entidad se crea individualmente.

## Consultar Productos

### Listar productos

Devuelve una lista paginada de productos con todas sus sub-entidades.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X GET "https://api.ya.onl/v1/products?page=1&limit=25" \
    -H "X-API-Key: yv_live_v1_tu_api_key"
  ```

  ```python Python theme={null}
  import httpx

  headers = {"X-API-Key": "yv_live_v1_tu_api_key"}

  response = httpx.get(
      "https://api.ya.onl/v1/products",
      headers=headers,
      params={"page": 1, "limit": 25},
  )
  result = response.json()
  products = result["data"]
  pagination = result["pagination"]
  ```

  ```javascript Node.js theme={null}
  const headers = { "X-API-Key": "yv_live_v1_tu_api_key" };

  const response = await fetch(
    "https://api.ya.onl/v1/products?page=1&limit=25",
    { headers }
  );
  const { data: products, pagination } = await response.json();
  ```
</CodeGroup>

La respuesta incluye paginación:

```json theme={null}
{
  "data": [ /* array de productos */ ],
  "pagination": {
    "page": 1,
    "limit": 25,
    "total_pages": 4,
    "total_items": 100,
    "has_more": true
  }
}
```

<Info>
  `limit` acepta un máximo de `100`. Consulta [Paginación](/pagination) para más detalles.
</Info>

### Obtener un producto

Devuelve un producto con todas sus opciones, variantes (con media) y categorías.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X GET "https://api.ya.onl/v1/products/9876" \
    -H "X-API-Key: yv_live_v1_tu_api_key"
  ```

  ```python Python theme={null}
  response = httpx.get(
      f"https://api.ya.onl/v1/products/{product_id}",
      headers=headers,
  )
  product = response.json()
  ```

  ```javascript Node.js theme={null}
  const response = await fetch(
    `https://api.ya.onl/v1/products/${productId}`,
    { headers }
  );
  const product = await response.json();
  ```
</CodeGroup>

Ejemplo de respuesta:

```json theme={null}
{
  "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"
        }
      ]
    }
  ]
}
```

## Crear un Producto Completo

### 1. Crear el producto

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://api.ya.onl/v1/products" \
    -H "X-API-Key: yv_live_v1_tu_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"
    }'
  ```

  ```python Python theme={null}
  import httpx

  headers = {"X-API-Key": "yv_live_v1_tu_api_key"}

  response = httpx.post(
      "https://api.ya.onl/v1/products",
      headers=headers,
      json={
          "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",
      },
  )
  product = response.json()
  product_id = product["id"]
  ```

  ```javascript Node.js theme={null}
  const headers = {
    "X-API-Key": "yv_live_v1_tu_api_key",
    "Content-Type": "application/json",
  };

  const response = await fetch("https://api.ya.onl/v1/products", {
    method: "POST",
    headers,
    body: JSON.stringify({
      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",
    }),
  });
  const product = await response.json();
  const productId = product.id;
  ```
</CodeGroup>

### 2. Agregar opciones

La respuesta incluye los IDs de los valores creados. **Guarda estos IDs** para usarlos al crear variantes.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://api.ya.onl/v1/products/9876/options" \
    -H "X-API-Key: yv_live_v1_tu_api_key" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "Talla",
      "values": ["S", "M", "L"]
    }'
  ```

  ```python Python theme={null}
  response = httpx.post(
      f"https://api.ya.onl/v1/products/{product_id}/options",
      headers=headers,
      json={"name": "Talla", "values": ["S", "M", "L"]},
  )
  option = response.json()
  # option = {"id": 10, "name": "Talla", "values": [
  #   {"id": 100, "name": "S"}, {"id": 101, "name": "M"}, {"id": 102, "name": "L"}
  # ]}
  value_ids = {v["name"]: v["id"] for v in option["values"]}
  ```

  ```javascript Node.js theme={null}
  const optionRes = await fetch(
    `https://api.ya.onl/v1/products/${productId}/options`,
    {
      method: "POST",
      headers,
      body: JSON.stringify({ name: "Talla", values: ["S", "M", "L"] }),
    }
  );
  const option = await optionRes.json();
  // option = {id: 10, name: "Talla", values: [
  //   {id: 100, name: "S"}, {id: 101, name: "M"}, {id: 102, name: "L"}
  // ]}
  const valueIds = Object.fromEntries(
    option.values.map((v) => [v.name, v.id])
  );
  ```
</CodeGroup>

<Info>
  **¿Necesitas agregar más valores a una opción?** Usa `POST /v1/products/{product_id}/options/{option_id}/values` con el nombre del nuevo valor (ej. `{"name": "XL"}`).
</Info>

### 3. Crear variantes

Cada variante debe incluir `option_values` con al menos un ID de valor de opción.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://api.ya.onl/v1/products/9876/variants" \
    -H "X-API-Key: yv_live_v1_tu_api_key" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "Polo Negro - Talla M",
      "base_price": 49.90,
      "currency": "PEN",
      "status": "available",
      "option_values": [101]
    }'
  ```

  ```python Python theme={null}
  response = httpx.post(
      f"https://api.ya.onl/v1/products/{product_id}/variants",
      headers=headers,
      json={
          "name": "Polo Negro - Talla M",
          "base_price": 49.90,
            "currency": "PEN",
          "status": "available",
          "option_values": [value_ids["M"]],
      },
  )
  variant = response.json()
  variant_id = variant["id"]
  ```

  ```javascript Node.js theme={null}
  const variantRes = await fetch(
    `https://api.ya.onl/v1/products/${productId}/variants`,
    {
      method: "POST",
      headers,
      body: JSON.stringify({
        name: "Polo Negro - Talla M",
        base_price: 49.9,
        currency: "PEN",
        status: "available",
        option_values: [valueIds["M"]],
      }),
    }
  );
  const variant = await variantRes.json();
  const variantId = variant.id;
  ```
</CodeGroup>

### 4. Agregar media a la variante

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://api.ya.onl/v1/products/9876/variants/500/media" \
    -H "X-API-Key: yv_live_v1_tu_api_key" \
    -H "Content-Type: application/json" \
    -d '{
      "title": "Front view",
      "url": "https://cdn.example.com/polo-negro-m-front.jpg"
    }'
  ```

  ```python Python theme={null}
  response = httpx.post(
      f"https://api.ya.onl/v1/products/{product_id}/variants/{variant_id}/media",
      headers=headers,
      json={
          "title": "Front view",
          "url": "https://cdn.example.com/polo-negro-m-front.jpg",
      },
  )
  media = response.json()
  ```

  ```javascript Node.js theme={null}
  const mediaRes = await fetch(
    `https://api.ya.onl/v1/products/${productId}/variants/${variantId}/media`,
    {
      method: "POST",
      headers,
      body: JSON.stringify({
        title: "Front view",
        url: "https://cdn.example.com/polo-negro-m-front.jpg",
      }),
    }
  );
  const media = await mediaRes.json();
  ```
</CodeGroup>

<Warning>
  Las URLs de media deben ser **públicas y accesibles**. El sistema no puede acceder a URLs privadas o protegidas por autenticación.
</Warning>

<Info>
  **Tipos de archivo permitidos:**

  | Tipo | Extensión | Tamaño máximo |
  | - | - | - |
  | Imagen | `jpeg` | 5 MB |
  | Imagen | `png` | 5 MB |
  | Video | `3gp` | 16 MB |
  | Video | `mp4` | 16 MB |
  | Documento | `pdf` | 100 MB |
</Info>

### 5. Asignar categorías

Este endpoint es **idempotente**: si un tag ya está asignado, se ignora y la respuesta es `200`. Solo se retorna `201` cuando se asignan tags nuevos.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://api.ya.onl/v1/products/9876/categories" \
    -H "X-API-Key: yv_live_v1_tu_api_key" \
    -H "Content-Type: application/json" \
    -d '{"tag_ids": [1, 2]}'
  ```

  ```python Python theme={null}
  response = httpx.post(
      f"https://api.ya.onl/v1/products/{product_id}/categories",
      headers=headers,
      json={"tag_ids": [1, 2]},
  )
  categories = response.json()
  ```

  ```javascript Node.js theme={null}
  const catRes = await fetch(
    `https://api.ya.onl/v1/products/${productId}/categories`,
    {
      method: "POST",
      headers,
      body: JSON.stringify({ tag_ids: [1, 2] }),
    }
  );
  const categories = await catRes.json();
  ```
</CodeGroup>

## Actualizar Entidades

<AccordionGroup>
  <Accordion title="Actualizar producto">
    <CodeGroup>
      ```bash cURL theme={null}
      curl -X PATCH "https://api.ya.onl/v1/products/9876" \
        -H "X-API-Key: yv_live_v1_tu_api_key" \
        -H "Content-Type: application/json" \
        -d '{"name": "Polo Negro Premium"}'
      ```

      ```python Python theme={null}
      response = httpx.patch(
          f"https://api.ya.onl/v1/products/{product_id}",
          headers={"X-API-Key": "yv_live_v1_tu_api_key"},
          json={"name": "Polo Negro Premium"},
      )
      ```

      ```javascript Node.js theme={null}
      const response = await fetch(
        `https://api.ya.onl/v1/products/${productId}`,
        {
          method: "PATCH",
          headers: {
            "X-API-Key": "yv_live_v1_tu_api_key",
            "Content-Type": "application/json",
          },
          body: JSON.stringify({ name: "Polo Negro Premium", status: "draft" }),
        }
      );
      ```
    </CodeGroup>
  </Accordion>

  <Accordion title="Actualizar opción">
    <CodeGroup>
      ```bash cURL theme={null}
      curl -X PATCH "https://api.ya.onl/v1/products/9876/options/10" \
        -H "X-API-Key: yv_live_v1_tu_api_key" \
        -H "Content-Type: application/json" \
        -d '{"name": "Talla (cm)"}'
      ```

      ```python Python theme={null}
      response = httpx.patch(
          f"https://api.ya.onl/v1/products/{product_id}/options/10",
          headers={"X-API-Key": "yv_live_v1_tu_api_key"},
          json={"name": "Talla (cm)"},
      )
      ```

      ```javascript Node.js theme={null}
      const response = await fetch(
        `https://api.ya.onl/v1/products/${productId}/options/10`,
        {
          method: "PATCH",
          headers: {
            "X-API-Key": "yv_live_v1_tu_api_key",
            "Content-Type": "application/json",
          },
          body: JSON.stringify({ name: "Talla (cm)" }),
        }
      );
      ```
    </CodeGroup>
  </Accordion>

  <Accordion title="Actualizar valor de opción">
    <CodeGroup>
      ```bash cURL theme={null}
      curl -X PATCH "https://api.ya.onl/v1/products/9876/options/10/values/100" \
        -H "X-API-Key: yv_live_v1_tu_api_key" \
        -H "Content-Type: application/json" \
        -d '{"name": "Small"}'
      ```

      ```python Python theme={null}
      response = httpx.patch(
          f"https://api.ya.onl/v1/products/{product_id}/options/10/values/100",
          headers={"X-API-Key": "yv_live_v1_tu_api_key"},
          json={"name": "Small"},
      )
      ```

      ```javascript Node.js theme={null}
      const response = await fetch(
        `https://api.ya.onl/v1/products/${productId}/options/10/values/100`,
        {
          method: "PATCH",
          headers: {
            "X-API-Key": "yv_live_v1_tu_api_key",
            "Content-Type": "application/json",
          },
          body: JSON.stringify({ name: "Small" }),
        }
      );
      ```
    </CodeGroup>
  </Accordion>

  <Accordion title="Actualizar variante">
    <CodeGroup>
      ```bash cURL theme={null}
      curl -X PATCH "https://api.ya.onl/v1/products/9876/variants/500" \
        -H "X-API-Key: yv_live_v1_tu_api_key" \
        -H "Content-Type: application/json" \
        -d '{"base_price": 59.90, "status": "not_available"}'
      ```

      ```python Python theme={null}
      response = httpx.patch(
          f"https://api.ya.onl/v1/products/{product_id}/variants/500",
          headers={"X-API-Key": "yv_live_v1_tu_api_key"},
          json={"base_price": 59.90, "status": "not_available"},
      )
      ```

      ```javascript Node.js theme={null}
      const response = await fetch(
        `https://api.ya.onl/v1/products/${productId}/variants/500`,
        {
          method: "PATCH",
          headers: {
            "X-API-Key": "yv_live_v1_tu_api_key",
            "Content-Type": "application/json",
          },
          body: JSON.stringify({ base_price: 59.9, status: "not_available" }),
        }
      );
      ```
    </CodeGroup>
  </Accordion>

  <Accordion title="Actualizar media">
    <CodeGroup>
      ```bash cURL theme={null}
      curl -X PATCH "https://api.ya.onl/v1/products/9876/variants/500/media/1000" \
        -H "X-API-Key: yv_live_v1_tu_api_key" \
        -H "Content-Type: application/json" \
        -d '{"title": "Vista frontal actualizada"}'
      ```

      ```python Python theme={null}
      response = httpx.patch(
          f"https://api.ya.onl/v1/products/{product_id}/variants/500/media/1000",
          headers={"X-API-Key": "yv_live_v1_tu_api_key"},
          json={"title": "Vista frontal actualizada"},
      )
      ```

      ```javascript Node.js theme={null}
      const response = await fetch(
        `https://api.ya.onl/v1/products/${productId}/variants/500/media/1000`,
        {
          method: "PATCH",
          headers: {
            "X-API-Key": "yv_live_v1_tu_api_key",
            "Content-Type": "application/json",
          },
          body: JSON.stringify({ title: "Vista frontal actualizada" }),
        }
      );
      ```
    </CodeGroup>
  </Accordion>
</AccordionGroup>

## Eliminar Entidades

<AccordionGroup>
  <Accordion title="Remover categoría de un producto">
    Esto desvincula la categoría del producto, pero **no elimina** la categoría global.

    <CodeGroup>
      ```bash cURL theme={null}
      curl -X DELETE "https://api.ya.onl/v1/products/9876/categories/1" \
        -H "X-API-Key: yv_live_v1_tu_api_key"
      ```

      ```python Python theme={null}
      response = httpx.delete(
          f"https://api.ya.onl/v1/products/{product_id}/categories/1",
          headers={"X-API-Key": "yv_live_v1_tu_api_key"},
      )
      ```

      ```javascript Node.js theme={null}
      await fetch(
        `https://api.ya.onl/v1/products/${productId}/categories/1`,
        {
          method: "DELETE",
          headers: { "X-API-Key": "yv_live_v1_tu_api_key" },
        }
      );
      ```
    </CodeGroup>
  </Accordion>

  <Accordion title="Eliminar media">
    <CodeGroup>
      ```bash cURL theme={null}
      curl -X DELETE "https://api.ya.onl/v1/products/9876/variants/500/media/1000" \
        -H "X-API-Key: yv_live_v1_tu_api_key"
      ```

      ```python Python theme={null}
      response = httpx.delete(
          f"https://api.ya.onl/v1/products/{product_id}/variants/500/media/1000",
          headers={"X-API-Key": "yv_live_v1_tu_api_key"},
      )
      ```

      ```javascript Node.js theme={null}
      await fetch(
        `https://api.ya.onl/v1/products/${productId}/variants/500/media/1000`,
        {
          method: "DELETE",
          headers: { "X-API-Key": "yv_live_v1_tu_api_key" },
        }
      );
      ```
    </CodeGroup>
  </Accordion>

  <Accordion title="Eliminar variante">
    <Warning>
      Eliminar una variante también elimina toda su media asociada.
    </Warning>

    <CodeGroup>
      ```bash cURL theme={null}
      curl -X DELETE "https://api.ya.onl/v1/products/9876/variants/500" \
        -H "X-API-Key: yv_live_v1_tu_api_key"
      ```

      ```python Python theme={null}
      response = httpx.delete(
          f"https://api.ya.onl/v1/products/{product_id}/variants/500",
          headers={"X-API-Key": "yv_live_v1_tu_api_key"},
      )
      ```

      ```javascript Node.js theme={null}
      await fetch(
        `https://api.ya.onl/v1/products/${productId}/variants/500`,
        {
          method: "DELETE",
          headers: { "X-API-Key": "yv_live_v1_tu_api_key" },
        }
      );
      ```
    </CodeGroup>
  </Accordion>

  <Accordion title="Eliminar valor de opción">
    <Warning>
      No puedes eliminar un valor de opción si está asociado a una variante activa.
    </Warning>

    <CodeGroup>
      ```bash cURL theme={null}
      curl -X DELETE "https://api.ya.onl/v1/products/9876/options/10/values/100" \
        -H "X-API-Key: yv_live_v1_tu_api_key"
      ```

      ```python Python theme={null}
      response = httpx.delete(
          f"https://api.ya.onl/v1/products/{product_id}/options/10/values/100",
          headers={"X-API-Key": "yv_live_v1_tu_api_key"},
      )
      ```

      ```javascript Node.js theme={null}
      await fetch(
        `https://api.ya.onl/v1/products/${productId}/options/10/values/100`,
        {
          method: "DELETE",
          headers: { "X-API-Key": "yv_live_v1_tu_api_key" },
        }
      );
      ```
    </CodeGroup>
  </Accordion>

  <Accordion title="Eliminar opción">
    <Warning>
      Eliminar una opción también elimina todos sus valores. Las variantes asociadas a esos valores pueden quedar en estado inconsistente.
    </Warning>

    <CodeGroup>
      ```bash cURL theme={null}
      curl -X DELETE "https://api.ya.onl/v1/products/9876/options/10" \
        -H "X-API-Key: yv_live_v1_tu_api_key"
      ```

      ```python Python theme={null}
      response = httpx.delete(
          f"https://api.ya.onl/v1/products/{product_id}/options/10",
          headers={"X-API-Key": "yv_live_v1_tu_api_key"},
      )
      ```

      ```javascript Node.js theme={null}
      await fetch(
        `https://api.ya.onl/v1/products/${productId}/options/10`,
        {
          method: "DELETE",
          headers: { "X-API-Key": "yv_live_v1_tu_api_key" },
        }
      );
      ```
    </CodeGroup>
  </Accordion>

  <Accordion title="Eliminar producto completo">
    <Warning>
      Eliminar un producto elimina **todas** sus variantes, opciones, valores de opción y media. Esta acción no se puede deshacer.
    </Warning>

    <CodeGroup>
      ```bash cURL theme={null}
      curl -X DELETE "https://api.ya.onl/v1/products/9876" \
        -H "X-API-Key: yv_live_v1_tu_api_key"
      ```

      ```python Python theme={null}
      response = httpx.delete(
          f"https://api.ya.onl/v1/products/{product_id}",
          headers={"X-API-Key": "yv_live_v1_tu_api_key"},
      )
      ```

      ```javascript Node.js theme={null}
      await fetch(`https://api.ya.onl/v1/products/${productId}`, {
        method: "DELETE",
        headers: { "X-API-Key": "yv_live_v1_tu_api_key" },
      });
      ```
    </CodeGroup>
  </Accordion>
</AccordionGroup>

## Producto Simple (sin variaciones)

No todos los productos tienen tallas o colores. Aún así, todo producto requiere al menos **una opción y una variante**. Usa un nombre genérico para la opción.

<CodeGroup>
  ```bash cURL theme={null}
  # 1. Crear producto
  curl -X POST "https://api.ya.onl/v1/products" \
    -H "X-API-Key: yv_live_v1_tu_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. Crear opción genérica
  curl -X POST "https://api.ya.onl/v1/products/9877/options" \
    -H "X-API-Key: yv_live_v1_tu_api_key" \
    -H "Content-Type: application/json" \
    -d '{"name": "Presentación", "values": ["Estándar"]}'

  # 3. Crear variante única
  curl -X POST "https://api.ya.onl/v1/products/9877/variants" \
    -H "X-API-Key: yv_live_v1_tu_api_key" \
    -H "Content-Type: application/json" \
    -d '{"name": "Dona Glaseada", "base_price": 5.00, "currency": "PEN", "option_values": [200]}'

  # 4. Agregar imagen
  curl -X POST "https://api.ya.onl/v1/products/9877/variants/600/media" \
    -H "X-API-Key: yv_live_v1_tu_api_key" \
    -H "Content-Type: application/json" \
    -d '{"title": "Dona Glaseada", "url": "https://cdn.example.com/dona-glaseada.jpg"}'
  ```

  ```python Python theme={null}
  import httpx

  headers = {"X-API-Key": "yv_live_v1_tu_api_key"}
  base = "https://api.ya.onl/v1"

  # 1. Crear producto
  response = httpx.post(f"{base}/products", headers=headers, json={
      "catalog_id": 1, "name": "Dona Glaseada",
      "description": "Dona artesanal con glaseado de azúcar", "status": "published",
  })
  product_id = response.json()["id"]

  # 2. Crear opción genérica
  response = httpx.post(f"{base}/products/{product_id}/options", headers=headers,
      json={"name": "Presentación", "values": ["Estándar"]})
  value_id = response.json()["values"][0]["id"]

  # 3. Crear variante única
  response = httpx.post(f"{base}/products/{product_id}/variants", headers=headers,
      json={"name": "Dona Glaseada", "base_price": 5.00, "currency": "PEN", "option_values": [value_id]})
  variant_id = response.json()["id"]

  # 4. Agregar imagen
  httpx.post(f"{base}/products/{product_id}/variants/{variant_id}/media", headers=headers,
      json={"title": "Dona Glaseada", "url": "https://cdn.example.com/dona-glaseada.jpg"})
  ```

  ```javascript Node.js theme={null}
  const headers = { "X-API-Key": "yv_live_v1_tu_api_key", "Content-Type": "application/json" };
  const base = "https://api.ya.onl/v1";

  // 1. Crear producto
  const prodRes = await fetch(`${base}/products`, {
    method: "POST", headers,
    body: JSON.stringify({ catalog_id: 1, name: "Dona Glaseada",
      description: "Dona artesanal con glaseado de azúcar", status: "published" }),
  });
  const productId = (await prodRes.json()).id;

  // 2. Crear opción genérica
  const optRes = await fetch(`${base}/products/${productId}/options`, {
    method: "POST", headers,
    body: JSON.stringify({ name: "Presentación", values: ["Estándar"] }),
  });
  const valueId = (await optRes.json()).values[0].id;

  // 3. Crear variante única
  const varRes = await fetch(`${base}/products/${productId}/variants`, {
    method: "POST", headers,
    body: JSON.stringify({ name: "Dona Glaseada", base_price: 5.0, currency: "PEN", option_values: [valueId] }),
  });
  const variantId = (await varRes.json()).id;

  // 4. Agregar imagen
  await fetch(`${base}/products/${productId}/variants/${variantId}/media`, {
    method: "POST", headers,
    body: JSON.stringify({ title: "Dona Glaseada", url: "https://cdn.example.com/dona-glaseada.jpg" }),
  });
  ```
</CodeGroup>

<Info>
  El nombre de la opción es libre: `"Presentación"`, `"Tipo"`, `"Unidad"`, etc.
</Info>

## Errores Comunes

Los errores de validación (`422`) devuelven un body con detalles del campo que falló:

```json theme={null}
{
  "detail": [
    {
      "loc": ["body", "option_values"],
      "msg": "List should have at least 1 item",
      "type": "too_short"
    }
  ]
}
```

<AccordionGroup>
  <Accordion title="422 — &#x22;option_values must have at least 1 item&#x22;">
    Estás creando una variante sin `option_values` o con una lista vacía. Debes crear opciones primero con `POST /v1/products/{product_id}/options` y usar los IDs de los valores retornados.
  </Accordion>

  <Accordion title="Crear variantes antes de opciones">
    Si intentas crear una variante antes de haber creado opciones, no tendrás IDs de valores disponibles para el campo `option_values`. Siempre crea opciones primero.
  </Accordion>

  <Accordion title="Usar nombre en vez de tag_id para categorías">
    El endpoint `POST /v1/products/{product_id}/categories` requiere `tag_ids` (lista de IDs numéricos), no nombres de categoría. Primero crea la categoría con `POST /v1/categories` y usa el `id` retornado.
  </Accordion>

  <Accordion title="Olvidar catalog_id al crear producto">
    El campo `catalog_id` es obligatorio al crear un producto. Obtén tu catálogo con `GET /v1/catalogs`.
  </Accordion>

  <Accordion title="IDs de option_value inválidos">
    Los IDs de `option_values` deben corresponder a valores del mismo producto. Usar IDs de otro producto o IDs inexistentes resultará en un error.
  </Accordion>

  <Accordion title="Intentar cambiar option_values de una variante existente">
    `PATCH /v1/products/{product_id}/variants/{variant_id}` no acepta `option_values`. Los valores de opción de una variante se asignan solo al crearla. Si necesitas corregirlos, elimina la variante y créala de nuevo con los valores correctos.
  </Accordion>
</AccordionGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.