Saltar al contenido
ShopentlyLearn

Actualizar — Productos

Modifica los datos indicados en «Productos». A continuación se describen los campos obligatorios y las reglas de escritura.

PATCH/api/v1/products/{product_id}

Autenticación
Bearer
Disponible desde
STANDARD
Permisos necesarios
catalog.product:update
Permisos adicionales para determinados campos
  • Si se incluye variant_value_ids: catalog.product_variant:create, catalog.product_variant:update, catalog.product_variant:delete
  • Si se incluye variants_enabled: catalog.product_variant:update
Más información

Ejemplos de código

Ejemplo de solicitud

cURL

: "${SHOPENTLY_SHOP_URL:?}"

curl --disable --globoff --fail-with-body --silent --show-error \
  --proto '=https' --connect-timeout 5 --max-time 30 \
  --request PATCH \
  --header "Authorization: Bearer ${SHOPENTLY_ACCESS_TOKEN:?}" \
  --header 'Idempotency-Key: example-update_product_api_v1_products__product_id__patch' \
  --header 'Content-Type: application/json' \
  --data-raw '{
  "expected_version": 1,
  "sku": "example"
}' \
  --url "${SHOPENTLY_SHOP_URL%/}"/api/v1/products/example

PHP

<?php
declare(strict_types=1);

$baseUrl = rtrim(getenv('SHOPENTLY_SHOP_URL') ?: '', '/');
$origin = parse_url($baseUrl);
if (!$origin || ($origin['scheme'] ?? '') !== 'https' || empty($origin['host']) ||
    isset($origin['user'], $origin['pass']) || isset($origin['user']) || isset($origin['path']) ||
    isset($origin['query']) || isset($origin['fragment'])) {
    throw new RuntimeException('SHOPENTLY_SHOP_URL must be an HTTPS origin');
}
$credential = getenv('SHOPENTLY_ACCESS_TOKEN') ?: '';
if ($credential === '' || strpbrk($credential, "\r\n") !== false) {
    throw new RuntimeException('Invalid credential');
}
$headers = ['Idempotency-Key: example-update_product_api_v1_products__product_id__patch', 'Content-Type: application/json'];
$headers[] = 'Authorization: Bearer ' . $credential;
$responseHeaders = [];
$handle = curl_init($baseUrl . '/api/v1/products/example');
curl_setopt_array($handle, [
    CURLOPT_CUSTOMREQUEST => 'PATCH',
    CURLOPT_HTTPHEADER => $headers,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_FOLLOWLOCATION => false,
    CURLOPT_PROTOCOLS => CURLPROTO_HTTPS,
    CURLOPT_SSL_VERIFYPEER => true,
    CURLOPT_SSL_VERIFYHOST => 2,
    CURLOPT_CONNECTTIMEOUT => 5,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_HEADERFUNCTION => static function ($handle, string $line) use (&$responseHeaders): int {
        if (str_contains($line, ':')) {
            [$name, $value] = explode(':', $line, 2);
            $responseHeaders[strtolower(trim($name))] = trim($value);
        }
        return strlen($line);
    },
]);
curl_setopt($handle, CURLOPT_POSTFIELDS, '{
  "expected_version": 1,
  "sku": "example"
}');
$raw = curl_exec($handle);
$status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE);
$error = curl_error($handle);
curl_close($handle);
if ($raw === false) {
    throw new RuntimeException('Transport error: ' . $error);
}
if ($status < 200 || $status >= 300) {
    throw new RuntimeException('HTTP ' . $status . '; request_id=' .
        ($responseHeaders['x-request-id'] ?? '') . '; retry_after=' . ($responseHeaders['retry-after'] ?? ''));
}
$result = $raw === '' ? null : json_decode($raw, true, 512, JSON_THROW_ON_ERROR | JSON_BIGINT_AS_STRING);
echo json_encode($result, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), PHP_EOL;

Python

from contextlib import ExitStack
import json
import os
from urllib.parse import urlsplit

import requests  # python -m pip install requests

base_url = os.environ["SHOPENTLY_SHOP_URL"].rstrip("/")
origin = urlsplit(base_url)
if origin.scheme != "https" or not origin.hostname or origin.username or origin.password or origin.path or origin.query or origin.fragment:
    raise ValueError("SHOPENTLY_SHOP_URL must be an HTTPS origin")
credential = os.environ['SHOPENTLY_ACCESS_TOKEN']
if not credential or "\r" in credential or "\n" in credential:
    raise ValueError("Invalid credential")
headers = {'Idempotency-Key': 'example-update_product_api_v1_products__product_id__patch'}
headers["Authorization"] = 'Bearer' + " " + credential
body = '{\n  "expected_version": 1,\n  "sku": "example"\n}'
if body is not None:
    headers["Content-Type"] = "application/json"

with ExitStack() as stack:
    session = stack.enter_context(requests.Session())
    files = None
    if False:
        image = stack.enter_context(open(os.environ["SHOPENTLY_IMAGE_FILE"], "rb"))
        if os.fstat(image.fileno()).st_size > 5242880:
            raise ValueError("Image exceeds 5 MiB")
        files = {"file": ("article.png", image, "image/png")}
    response = session.request(
        'PATCH', base_url + '/api/v1/products/example', headers=headers,
        data=body.encode("utf-8") if body is not None else None, files=files,
        timeout=(5, 30), allow_redirects=False,
    )
    if not 200 <= response.status_code < 300:
        raise RuntimeError(f"HTTP {response.status_code}; request_id={response.headers.get('X-Request-ID', '')}; retry_after={response.headers.get('Retry-After', '')}")
    if False:
        if response.headers.get("Content-Type") != "image/webp" or len(response.content) > 5242880:
            raise ValueError("Unexpected image response")
        with open(os.environ["SHOPENTLY_OUTPUT_FILE"], "xb") as output:
            output.write(response.content)
    else:
        result = response.json() if response.content else None
        print(json.dumps(result, ensure_ascii=False, indent=2))

Ejemplo de respuesta 200

JSON
{
  "data": {
    "deleted": false,
    "id": "example",
    "version": 1
  },
  "meta": {
    "request_id": "example"
  }
}

Cómo funciona

  • La integración puede leer productos no eliminados, incluidos los borradores, excepto los bloqueados por un límite del plan.
  • Los productos bloqueados se excluyen de las listas; el acceso directo devuelve entitlements.resource_blocked.
  • Por tanto, la visibilidad en la API difiere de la publicación en la tienda.
  • Utiliza identificadores existentes de marcas, categorías, características y archivos multimedia.
  • Los precios incluyen moneda, lista de precios, tratamiento fiscal y versiones explícitas para controlar cambios simultáneos.
  • Los precios de productos y variantes son distintos; utiliza la estructura admitida y cadenas decimales exactas.

Parámetros

Parámetros de ruta

product_idCadena de textoObligatorio

Identificador del producto.

Restricciones
pattern
"^[a-z0-9][a-z0-9_-]{0,62}$"
Ejemplo: "example"

Parámetros de consulta

localeCadena de texto nullOpcional

Idioma seleccionado para esta solicitud.

Cadena de texto:minLength: 2maxLength: 20
market_idCadena de texto nullOpcional

Identificador del mercado devuelto por el endpoint de contexto.

Restricciones · Cadena de texto 1
pattern
"^[a-z0-9][a-z0-9_-]{0,62}$"
market_codeCadena de texto nullOpcional

Código del mercado devuelto por el endpoint de contexto.

Cadena de texto:minLength: 1maxLength: 16

Encabezados de solicitud

Idempotency-KeyCadena de textoObligatorio

Identificador único de una operación lógica de escritura.

minLength: 1maxLength: 128
Restricciones
pattern
"^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$"
Ejemplo: "example-update_product_api_v1_products__product_id__patch"

Cuerpo de la solicitud

application/json · Obligatorio

ProductUpdate
brand_idCadena de texto nullOpcional

Identificador de la marca.

Cadena de texto:minLength: 1maxLength: 63
conditionCadena de textoOpcional

Estado del producto.

Restricciones
enum
["new", "refurbished", "used", "returned", "display", "damaged"]
descriptionCadena de texto nullOpcional

Descripción en el idioma seleccionado.

Cadena de texto:maxLength: 100000
Ejemplo: null
expected_variant_price_versionsObjetoCampos adicionales: Número entero
minimum: 0maximum: 9223372036854775807
Opcional

Versiones de precios leídas previamente, indexadas por identificador de variante.

maxProperties: 250
Restricciones
propertyNames
{"maxLength": 63, "minLength": 1}
expected_versionNúmero enteroObligatorio

Versión leída antes de escribir; si no coincide, se rechaza la actualización.

minimum: 1maximum: 9223372036854775807
Ejemplo: 1
feature_value_idsLista <Cadena de texto> Opcional

Identificadores de los valores de características seleccionados.

maxItems: 1000
Lista:minLength: 1maxLength: 63
identifiersProductIdentifiers nullOpcional

Códigos de identificación del producto.

market_idsLista <Cadena de texto> Opcional

Identificadores de los mercados seleccionados.

maxItems: 1000
Lista:minLength: 1maxLength: 63
media_idsLista <Cadena de texto> Opcional

Identificadores de archivos multimedia ya existentes.

maxItems: 20
Lista:minLength: 1maxLength: 63
meta_descriptionCadena de texto nullOpcional

Descripción para buscadores en el idioma seleccionado.

Cadena de texto:maxLength: 160
meta_keywordsLista <Cadena de texto> nullOpcional

Palabras clave para buscadores en el idioma seleccionado.

Lista:maxItems: 20
Lista / Lista:maxLength: 50
meta_titleCadena de texto nullOpcional

Título para buscadores en el idioma seleccionado.

Cadena de texto:maxLength: 70
nameCadena de texto nullOpcional

Nombre del recurso en el contexto correspondiente.

Cadena de texto:maxLength: 300
Ejemplo: null
pricingProductPrice Opcional

Configuración de precios y versiones para controlar cambios simultáneos.

Ejemplo: {"amount":"0.00","currency":"PLN","expected_price_list_version":1,"expected_version":0,"kind":"base","price_list_id":"example","tax_included":true}
primary_category_idCadena de texto nullOpcional

Identificador de la categoría principal.

Cadena de texto:minLength: 1maxLength: 63
publishedBooleanoOpcional

Indica si el recurso está publicado.

skuCadena de textoOpcional

Código de referencia de inventario.

minLength: 1maxLength: 100
Restricciones
pattern
"^\\S+$"
Ejemplo: "example"
slugCadena de texto nullOpcional

Segmento de la ruta URL en el idioma seleccionado.

Cadena de texto:maxLength: 160
tax_category_idCadena de texto nullOpcional

Identificador de la categoría fiscal.

Cadena de texto:minLength: 1maxLength: 63
variant_value_idsLista <Cadena de texto> Opcional

Identificadores de los valores de variantes seleccionados.

maxItems: 250
Lista:minLength: 1maxLength: 63
variants_enabledBooleanoOpcional

Indica si el producto utiliza variantes.

Respuestas

Respuesta 200

dataMutationData Obligatorio

Contenido de una respuesta correcta.

Campos · data
deletedBooleanoObligatorio

Indica si se aplicó la eliminación.

idCadena de textoObligatorio

Identificador del recurso; consérvalo tal como se devuelve.

versionNúmero entero nullObligatorio

Versión del recurso utilizada para controlar cambios simultáneos.

metaContextPageMeta ContextMeta PageMeta RequestMeta Obligatorio

Metadatos de la respuesta.

Campos · meta

ContextPageMeta

localeCadena de textoObligatorio

Idioma seleccionado para esta solicitud.

market_codeCadena de textoObligatorio

Código del mercado devuelto por el endpoint de contexto.

market_idCadena de textoObligatorio

Identificador del mercado devuelto por el endpoint de contexto.

paginationPagination Obligatorio

Metadatos de la paginación por cursor.

request_idCadena de textoObligatorio

Identificador de la solicitud; en los metadatos de respuesta identifica la solicitud HTTP.

minLength: 1maxLength: 100

ContextMeta

localeCadena de textoObligatorio

Idioma seleccionado para esta solicitud.

market_codeCadena de textoObligatorio

Código del mercado devuelto por el endpoint de contexto.

market_idCadena de textoObligatorio

Identificador del mercado devuelto por el endpoint de contexto.

request_idCadena de textoObligatorio

Identificador de la solicitud; en los metadatos de respuesta identifica la solicitud HTTP.

minLength: 1maxLength: 100

PageMeta

paginationPagination Obligatorio

Metadatos de la paginación por cursor.

request_idCadena de textoObligatorio

Identificador de la solicitud; en los metadatos de respuesta identifica la solicitud HTTP.

minLength: 1maxLength: 100

RequestMeta

request_idCadena de textoObligatorio

Identificador de la solicitud; en los metadatos de respuesta identifica la solicitud HTTP.

minLength: 1maxLength: 100
Ejemplo de respuesta

Estado HTTP

200
Respuesta correcta Esquema
400
Solicitud no válida
401
Autenticación necesaria
403
Permiso denegado
404
Recurso no encontrado
409
Conflicto de versión
422
Error de validación
429
Límite de solicitudes excedido
Otros estados HTTP (7)
405
Método no permitido
413
Solicitud demasiado grande
415
Formato no compatible
500
Error del servidor
502
Error de la puerta de enlace API
503
Servicio no disponible
504
Tiempo de espera agotado

Catálogo de errores: ErrorResponse

Cabeceras

401

WWW-AuthenticateCadena de textoOpcional

Esquema de autenticación exigido por el servidor.

405

AllowCadena de textoOpcional

Métodos HTTP admitidos por este recurso.

429, 503

Retry-AfterNúmero enteroOpcional

Espera mínima antes de reintentar, en segundos.

minimum: 0