# Editar variante

Cambia etiqueta, SKU, precios o estado de una variante (nunca su stock).

PATCH `/public/v1/products/{id}/variants/{variantId}`

Permiso `products:write` · Productos

Responde el producto completo. Misma condición de versión de las cajas que editar un producto (409 DEVICES_OUTDATED). El cambio llega a las cajas del negocio por su sincronización normal (en segundos si están en línea; si están sin conexión, al reconectarse).

## Parámetros

### Parámetros de ruta

| Nombre      | Tipo          | Requerido | Descripción |
| ----------- | ------------- | --------- | ----------- |
| `id`        | string (uuid) | Sí        | —           |
| `variantId` | string (uuid) | Sí        | —           |

## Cuerpo de la petición

JSON (`Content-Type: application/json`): un objeto [VariantUpdate](https://developers.posdata.so/referencia/objetos/#obj-variantupdate).

Envía al menos 1 campo.

Un campo que no esté en la lista se rechaza con `VALIDATION_ERROR`.

| Campo              | Tipo    | Requerido | Descripción                                                                                                    |
| ------------------ | ------- | --------- | -------------------------------------------------------------------------------------------------------------- |
| `label`            | string  | No        | Máximo 120 caracteres.                                                                                         |
| `sku`              | string  | No        | Único por cuenta. Máximo 60 caracteres.                                                                        |
| `base_price`       | integer | No        | Si cambias base_price sin enviar final_price, final_price toma el mismo valor. Mínimo: 0. Máximo: 10000000000. |
| `final_price`      | integer | No        | Precio al que vende la caja. Mínimo: 0. Máximo: 10000000000.                                                   |
| `percent_discount` | number  | No        | Mínimo: 0. Máximo: 100.                                                                                        |
| `active`           | boolean | No        | —                                                                                                              |

## Ejemplo de petición

El cuerpo del ejemplo lleva solo los campos obligatorios, con valores ilustrativos: ajústalos a tus datos.

**curl**

```sh
curl -X PATCH "https://api.posdata.so/public/v1/products/3f1c2e8d-7a10-4d0b-9a57-0b6f2f0e5c1d/variants/3f1c2e8d-7a10-4d0b-9a57-0b6f2f0e5c1d" \
  -H "Authorization: Bearer $POSDATA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"label":"texto"}'
```

**Node.js**

```js
const res = await fetch('https://api.posdata.so/public/v1/products/3f1c2e8d-7a10-4d0b-9a57-0b6f2f0e5c1d/variants/3f1c2e8d-7a10-4d0b-9a57-0b6f2f0e5c1d', {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${process.env.POSDATA_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "label": "texto"
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error.code}: ${data.error.message} (${data.error.request_id})`);
```

**Python**

```python
import os

import requests

res = requests.patch(
    "https://api.posdata.so/public/v1/products/3f1c2e8d-7a10-4d0b-9a57-0b6f2f0e5c1d/variants/3f1c2e8d-7a10-4d0b-9a57-0b6f2f0e5c1d",
    headers={
        "Authorization": f"Bearer {os.environ['POSDATA_API_KEY']}",
    },
    json={
        "label": "texto",
    },
    timeout=30,
)
data = res.json()
if not res.ok:
    raise RuntimeError(f"{data['error']['code']}: {data['error']['message']} ({data['error']['request_id']})")
```

**PHP**

```php
<?php
$ch = curl_init('https://api.posdata.so/public/v1/products/3f1c2e8d-7a10-4d0b-9a57-0b6f2f0e5c1d/variants/3f1c2e8d-7a10-4d0b-9a57-0b6f2f0e5c1d');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'PATCH',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('POSDATA_API_KEY'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'label' => 'texto',
    ]),
]);
$data = json_decode(curl_exec($ch), true);
if (curl_getinfo($ch, CURLINFO_RESPONSE_CODE) >= 400) {
    throw new RuntimeException("{$data['error']['code']}: {$data['error']['message']} ({$data['error']['request_id']})");
}
```

## Respuesta

`200` Actualizado: un objeto [Product](https://developers.posdata.so/referencia/objetos/#obj-product).

| Campo               | Tipo                                                                                              | Requerido | Descripción                                                                                                                                                                                                                                                                                                                                                                                                                                |
| ------------------- | ------------------------------------------------------------------------------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `object`            | `"product"`                                                                                       | Sí        | —                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `id`                | string (uuid)                                                                                     | Sí        | —                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `name`              | string                                                                                            | Sí        | —                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `description`       | string                                                                                            | Sí        | —                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `description_short` | string                                                                                            | Sí        | —                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `sku`               | string \| null                                                                                    | Sí        | SKU del producto (null si no tiene).                                                                                                                                                                                                                                                                                                                                                                                                       |
| `category_id`       | string (uuid) \| null                                                                             | Sí        | —                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `kind`              | string                                                                                            | Sí        | `composite` = se arma desde una receta y no descuenta su propio stock. Valores: `simple`, `composite`.                                                                                                                                                                                                                                                                                                                                     |
| `sellable`          | boolean                                                                                           | Sí        | `false` = insumo que no se vende por sí solo.                                                                                                                                                                                                                                                                                                                                                                                              |
| `active`            | boolean                                                                                           | Sí        | —                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `tax_class`         | string                                                                                            | Sí        | Clase fiscal del ítem. `standard` hereda del perfil fiscal del negocio. Valores: `standard`, `reduced_5`, `excluded`, `exempt`, `service_19`, `event_package`, `bag`, `passthrough`.                                                                                                                                                                                                                                                       |
| `base_price`        | integer                                                                                           | Sí        | Precio de lista, antes del descuento propio del producto.                                                                                                                                                                                                                                                                                                                                                                                  |
| `final_price`       | integer                                                                                           | Sí        | Precio al que el POS vende. Si el producto no tiene descuento es igual a `base_price`.                                                                                                                                                                                                                                                                                                                                                     |
| `percent_discount`  | number                                                                                            | Sí        | Descuento del producto sobre `base_price`, en porcentaje (0-100, puede tener decimales).                                                                                                                                                                                                                                                                                                                                                   |
| `base_unit`         | string                                                                                            | Sí        | Unidad base (und, g, kg, lb, oz, ml, l, doc). Stock y costo se expresan en ella.                                                                                                                                                                                                                                                                                                                                                           |
| `sold_by_weight`    | boolean                                                                                           | Sí        | Se vende por peso: la cantidad de la línea es un peso en `base_unit`.                                                                                                                                                                                                                                                                                                                                                                      |
| `stock`             | number \| null                                                                                    | Sí        | Stock oficial en `base_unit` (suma de todas sus bodegas y variantes; puede tener decimales en productos por peso y ser negativo si una venta ganó la carrera). Es `null` mientras la cuenta no lleve el stock en la nube (sus cajas aún en una versión anterior), en productos sin control de stock y en los que se arman con receta. Por bodega: GET /inventory/levels. Para moverlo: POST /inventory/movements o POST /inventory/counts. |
| `unit_cost`         | number                                                                                            | Sí        | Costo promedio ponderado por unidad base (informativo).                                                                                                                                                                                                                                                                                                                                                                                    |
| `image_url`         | string (uri) \| null                                                                              | Sí        | —                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `images`            | arreglo de string (uri)                                                                           | Sí        | —                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `has_variants`      | boolean                                                                                           | Sí        | —                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `variants`          | arreglo de [ProductVariant](https://developers.posdata.so/referencia/objetos/#obj-productvariant) | Sí        | Variantes vigentes (no eliminadas).                                                                                                                                                                                                                                                                                                                                                                                                        |
| `deleted_at`        | string (date-time) \| null                                                                        | Sí        | —                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `created_at`        | string (date-time)                                                                                | Sí        | —                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `updated_at`        | string (date-time)                                                                                | Sí        | —                                                                                                                                                                                                                                                                                                                                                                                                                                          |

```json
{
  "object": "product",
  "id": "3f1c2e8d-7a10-4d0b-9a57-0b6f2f0e5c1d",
  "name": "Café molido 500 g",
  "description": "texto",
  "description_short": "texto",
  "sku": "CAF-500",
  "category_id": "3f1c2e8d-7a10-4d0b-9a57-0b6f2f0e5c1d",
  "kind": "simple",
  "sellable": true,
  "active": true,
  "tax_class": "standard",
  "base_price": 25900,
  "final_price": 25900,
  "percent_discount": 19,
  "base_unit": "texto",
  "sold_by_weight": true,
  "stock": 5,
  "unit_cost": 1,
  "image_url": "https://ejemplo.com",
  "images": [
    "https://ejemplo.com"
  ],
  "has_variants": true,
  "variants": [
    {
      "object": "product_variant",
      "id": "3f1c2e8d-7a10-4d0b-9a57-0b6f2f0e5c1d",
      "product_id": "3f1c2e8d-7a10-4d0b-9a57-0b6f2f0e5c1d",
      "label": "texto",
      "sku": "CAF-500",
      "base_price": 25900,
      "final_price": 25900,
      "percent_discount": 19,
      "images": [
        "https://ejemplo.com"
      ],
      "is_default": true,
      "active": true,
      "stock": 5,
      "created_at": "2026-09-28T15:04:05.000Z",
      "updated_at": "2026-09-28T15:04:05.000Z"
    }
  ],
  "deleted_at": "2026-09-28T15:04:05.000Z",
  "created_at": "2026-09-28T15:04:05.000Z",
  "updated_at": "2026-09-28T15:04:05.000Z"
}
```

## Errores

Todo error responde con un objeto [Error](https://developers.posdata.so/referencia/objetos/#obj-error) y un `request_id`. Ramifica por `error.code`, nunca por el mensaje.

### 400Petición inválida (VALIDATION_ERROR, INVALID_JSON, INVALID_CURSOR).

**Respuesta 400**

Cuerpo: un objeto [Error](https://developers.posdata.so/referencia/objetos/#obj-error). Qué significa cada código y qué hacer: [Errores](https://developers.posdata.so/errores/).

VALIDATION_ERRORINVALID_JSONINVALID_CURSOR

### 401Sin API key válida (UNAUTHENTICATED, INVALID_API_KEY, API_KEY_REVOKED, API_KEY_EXPIRED, API_KEY_WRONG_ENVIRONMENT, ACCOUNT_INACTIVE).

**Respuesta 401**

Cuerpo: un objeto [Error](https://developers.posdata.so/referencia/objetos/#obj-error). Qué significa cada código y qué hacer: [Errores](https://developers.posdata.so/errores/).

UNAUTHENTICATEDINVALID_API_KEYAPI_KEY_REVOKEDAPI_KEY_EXPIREDAPI_KEY_WRONG_ENVIRONMENTACCOUNT_INACTIVE

### 403Sin permiso (PLAN_REQUIRED, INSUFFICIENT_SCOPE, BROWSER_REQUESTS_NOT_ALLOWED).

**Respuesta 403**

Cuerpo: un objeto [Error](https://developers.posdata.so/referencia/objetos/#obj-error). Qué significa cada código y qué hacer: [Errores](https://developers.posdata.so/errores/).

PLAN_REQUIREDINSUFFICIENT_SCOPEBROWSER_REQUESTS_NOT_ALLOWED

### 404No existe en esta cuenta (NOT_FOUND).

**Respuesta 404**

Cuerpo: un objeto [Error](https://developers.posdata.so/referencia/objetos/#obj-error). Qué significa cada código y qué hacer: [Errores](https://developers.posdata.so/errores/).

NOT_FOUND

### 409Conflicto (CONFLICT, SKU_ALREADY_EXISTS, IDEMPOTENCY_IN_PROGRESS, NO_WAREHOUSE, DEVICES_OUTDATED, INSUFFICIENT_STOCK con `available`, STOCK_MODE_LEGACY).

**Respuesta 409**

Cuerpo: un objeto [Error](https://developers.posdata.so/referencia/objetos/#obj-error). Qué significa cada código y qué hacer: [Errores](https://developers.posdata.so/errores/).

CONFLICTSKU_ALREADY_EXISTSIDEMPOTENCY_IN_PROGRESSNO_WAREHOUSEDEVICES_OUTDATEDINSUFFICIENT_STOCKSTOCK_MODE_LEGACY

### 422Regla de negocio o idempotencia (IDEMPOTENCY_KEY_REUSED, COMPOSITE_STOCK_UNSUPPORTED).

**Respuesta 422**

Cuerpo: un objeto [Error](https://developers.posdata.so/referencia/objetos/#obj-error). Qué significa cada código y qué hacer: [Errores](https://developers.posdata.so/errores/).

IDEMPOTENCY_KEY_REUSEDCOMPOSITE_STOCK_UNSUPPORTED

### 429Límite de peticiones (RATE_LIMITED). Respeta Retry-After.

**Respuesta 429**

Cuerpo: un objeto [Error](https://developers.posdata.so/referencia/objetos/#obj-error). Qué significa cada código y qué hacer: [Errores](https://developers.posdata.so/errores/).

RATE_LIMITED

### 500Error interno (INTERNAL_ERROR). Reintenta y comparte el request_id si persiste.

**Respuesta 500**

Cuerpo: un objeto [Error](https://developers.posdata.so/referencia/objetos/#obj-error). Qué significa cada código y qué hacer: [Errores](https://developers.posdata.so/errores/).

INTERNAL_ERROR

---

Fuente: https://developers.posdata.so/referencia/operations/updatevariant/ · Índice para agentes: https://developers.posdata.so/llms.txt
