Ir al contenido
docs

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

NombreTipoRequeridoDescripción
idstring (uuid)Sí—
variantIdstring (uuid)Sí—

Cuerpo de la petición

JSON (Content-Type: application/json): un objeto VariantUpdate.

Envía al menos 1 campo.

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

CampoTipoRequeridoDescripción
labelstringNoMáximo 120 caracteres.
skustringNoÚnico por cuenta. Máximo 60 caracteres.
base_priceintegerNoSi cambias base_price sin enviar final_price, final_price toma el mismo valor. Mínimo: 0. Máximo: 10000000000.
final_priceintegerNoPrecio al que vende la caja. Mínimo: 0. Máximo: 10000000000.
percent_discountnumberNoMínimo: 0. Máximo: 100.
activebooleanNo—

Ejemplo de petición

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

Terminal
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"}'
JavaScript
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
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
$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.

CampoTipoRequeridoDescripción
object"product"Sí—
idstring (uuid)Sí—
namestringSí—
descriptionstringSí—
description_shortstringSí—
skustring | nullSíSKU del producto (null si no tiene).
category_idstring (uuid) | nullSí—
kindstringSícomposite = se arma desde una receta y no descuenta su propio stock. Valores: simple, composite.
sellablebooleanSífalse = insumo que no se vende por sí solo.
activebooleanSí—
tax_classstringSíClase fiscal del ítem. standard hereda del perfil fiscal del negocio. Valores: standard, reduced_5, excluded, exempt, service_19, event_package, bag, passthrough.
base_priceintegerSíPrecio de lista, antes del descuento propio del producto.
final_priceintegerSíPrecio al que el POS vende. Si el producto no tiene descuento es igual a base_price.
percent_discountnumberSíDescuento del producto sobre base_price, en porcentaje (0-100, puede tener decimales).
base_unitstringSíUnidad base (und, g, kg, lb, oz, ml, l, doc). Stock y costo se expresan en ella.
sold_by_weightbooleanSíSe vende por peso: la cantidad de la línea es un peso en base_unit.
stocknumber | nullSí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_costnumberSíCosto promedio ponderado por unidad base (informativo).
image_urlstring (uri) | nullSí—
imagesarreglo de string (uri)Sí—
has_variantsbooleanSí—
variantsarreglo de ProductVariantSíVariantes vigentes (no eliminadas).
deleted_atstring (date-time) | nullSí—
created_atstring (date-time)Sí—
updated_atstring (date-time)Sí—
Ejemplo de respuesta (valores ilustrativos)
{
  "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 y un request_id. Ramifica por error.code, nunca por el mensaje.

Cuerpo: un objeto Error. Qué significa cada código y qué hacer: Errores.

VALIDATION_ERRORINVALID_JSONINVALID_CURSOR

Cuerpo: un objeto Error. Qué significa cada código y qué hacer: Errores.

UNAUTHENTICATEDINVALID_API_KEYAPI_KEY_REVOKEDAPI_KEY_EXPIREDAPI_KEY_WRONG_ENVIRONMENTACCOUNT_INACTIVE

Cuerpo: un objeto Error. Qué significa cada código y qué hacer: Errores.

PLAN_REQUIREDINSUFFICIENT_SCOPEBROWSER_REQUESTS_NOT_ALLOWED

Cuerpo: un objeto Error. Qué significa cada código y qué hacer: Errores.

NOT_FOUND

Cuerpo: un objeto Error. Qué significa cada código y qué hacer: Errores.

CONFLICTSKU_ALREADY_EXISTSIDEMPOTENCY_IN_PROGRESSNO_WAREHOUSEDEVICES_OUTDATEDINSUFFICIENT_STOCKSTOCK_MODE_LEGACY

Cuerpo: un objeto Error. Qué significa cada código y qué hacer: Errores.

IDEMPOTENCY_KEY_REUSEDCOMPOSITE_STOCK_UNSUPPORTED

Cuerpo: un objeto Error. Qué significa cada código y qué hacer: Errores.

RATE_LIMITED

Cuerpo: un objeto Error. Qué significa cada código y qué hacer: Errores.

INTERNAL_ERROR