Referencia

Errores y reintentos seguros

Los errores usan application/problem+json. Registra code, request_id y X-Request-Id, sin credenciales ni información personal.

Formato

Cada error trae type (la URL de su explicación en esta página), title, status, code, detail y request_id. Decide con code, no con title ni detail: el texto puede cambiar.

Errores por campo

Un INVALID_REQUEST puede incluir errors: una lista con pointer (JSON Pointer al campo), code y detail. Hoy contiene la primera infracción encontrada. El puntero es relativo al cuerpo enviado, a los parámetros de consulta o, al publicar, al producto tal como lo devuelve GET.

Códigos de campo: required, type, range, length, format, enum, unknown_field, duplicate, relation y media.

Publicar un borrador sin categoría

Solicitud · ejemplo ilustrativo

cURL
: "${MOKU_PAT:?}" &&
test "${MOKU_ALLOW_WRITES:-}" = "1" &&
curl --silent --show-error --fail-with-body \
  --request POST \
  'https://moku.cl/api/v1/vendors/vendor_demo_ceramica/products/product_demo_borrador/publish' \
  --header "Authorization: Bearer ${MOKU_PAT}" \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data-raw '{
  "expected_revision": 1
}'
Node.js

Módulo ES para Node.js 18 o posterior (guárdalo, por ejemplo, como ejemplo.mjs). Usa el fetch incluido, sin dependencias. No debe ejecutarse en el navegador.

const token = process.env.MOKU_PAT;
if (!token) throw new Error('MOKU_PAT');
if (process.env.MOKU_ALLOW_WRITES !== '1') throw new Error('MOKU_ALLOW_WRITES=1');

const url = new URL('https://moku.cl/api/v1/vendors/vendor_demo_ceramica/products/product_demo_borrador/publish');
const body = {
  "expected_revision": 1
};
const response = await fetch(url, {
  method: 'POST',
  headers: {
    Accept: 'application/json',
    Authorization: `Bearer ${token}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify(body),
  signal: AbortSignal.timeout(30_000),
});
const result = await response.json();
if (!response.ok) console.error(`HTTP ${response.status}`);
(response.ok ? console.log : console.error)(JSON.stringify(result, null, 2));
process.exitCode = response.ok ? 0 : 1;
PHP

Ejemplo para PHP CLI con la extensión cURL. No es un plugin de WordPress ni debe ejecutarse en el navegador.

<?php
$token = getenv('MOKU_PAT');
if ($token === false || $token === '') {
    throw new RuntimeException('MOKU_PAT');
}
if (getenv('MOKU_ALLOW_WRITES') !== '1') {
    throw new RuntimeException('MOKU_ALLOW_WRITES=1');
}

$url = 'https://moku.cl/api/v1/vendors/vendor_demo_ceramica/products/product_demo_borrador/publish';
$body = <<<'MOKU_REQUEST_JSON'
{
  "expected_revision": 1
}
MOKU_REQUEST_JSON;
$curl = curl_init($url);
if ($curl === false) {
    throw new RuntimeException('curl_init');
}
curl_setopt_array($curl, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_CONNECTTIMEOUT => 10,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_HTTPHEADER => [
        'Accept: application/json',
        'Authorization: Bearer ' . $token,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => $body,
]);
$response = curl_exec($curl);
if ($response === false) {
    $message = curl_error($curl);
    curl_close($curl);
    throw new RuntimeException($message);
}
$status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
curl_close($curl);
$result = json_decode($response, false, 512, JSON_THROW_ON_ERROR);
$failed = $status < 200 || $status >= 300;
if ($failed) {
    fwrite(STDERR, "HTTP {$status}\n");
}
fwrite(
    $failed ? STDERR : STDOUT,
    json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR) . PHP_EOL
);
exit($failed ? 1 : 0);

Respuesta 400 application/problem+json

{
  "type": "https://developers.moku.cl/reference/errors/#invalid-request",
  "title": "Invalid request",
  "status": 400,
  "code": "INVALID_REQUEST",
  "detail": "The product is not ready to publish.",
  "request_id": "req_00000000000000000000000000000003",
  "errors": [
    {
      "pointer": "/category",
      "code": "required",
      "detail": "Choose a category before publishing."
    }
  ]
}

Qué reintentar

RespuestaAcción
400Corrige la solicitud; no reintentes igual.
401/403Corrige token o scopes.
409Lee el estado actual y reconcilia.
429Espera Retry-After con jitter.
500/503Verifica el estado y reintenta con backoff; conserva la misma clave para una escritura idéntica.

400 · Solicitud inválida

INVALID_CURSOR · 400
Usa sin modificar el next_cursor de la página anterior y con el mismo recurso y filtros.
INVALID_MEDIA · 400
Verifica el SHA-256 y envía una imagen JPEG, PNG o WebP válida, de un solo cuadro y hasta 16 megapíxeles.
INVALID_REQUEST · 400
Corrige el cuerpo, los parámetros o las cabeceras según el esquema OpenAPI; no repitas la misma solicitud. Cuando Moku puede ubicar el problema, errors[].pointer indica el campo: una ruta dentro del cuerpo (/variations/0/price), dentro de los parámetros de consulta (/limit) o, al publicar, dentro del producto (/image_urls).

401 · Token no válido

UNAUTHENTICATED · 401
Envía un PAT válido, no vencido ni revocado en Authorization: Bearer.

403 · Permiso insuficiente

INSUFFICIENT_SCOPE · 403
Crea o usa un PAT con el bundle requerido para el endpoint.

404 · Recurso no encontrado

RESOURCE_NOT_FOUND · 404
Verifica el ID y el vendor concedido al PAT; Moku no revela recursos de otro vendedor.

409 · Conflicto de estado, versión o autoridad

Efectos de stock entre canales

STOCK_CLAIM_EXPIRED · 409
La concesión venció o cambió. Vuelve a consultar la operación y conserva cualquier comando incierto.
STOCK_EFFECT_BUSY · 409
La operación tiene una concesión vigente. Respeta Retry-After y vuelve a consultar su estado.
STOCK_EFFECT_CONFLICT · 409
Consulta la operación y verifica cuenta, autoridad, revisión y mapeo antes de continuar.
STOCK_EFFECT_VERSION_CONFLICT · 409
Vuelve a leer la versión de la operación antes de solicitar una nueva concesión.
STOCK_RECONCILIATION_REQUIRED · 409
Consulta el recibo del comando original. Un recibo ausente nunca autoriza a repetir el débito.
STOCK_SOURCE_BUSY · 409
Otra operación ocupa el mismo artículo fuente. Respeta Retry-After sin cambiar la identidad del pedido.

Inventario y reservas

INVENTORY_UNAVAILABLE · 409
Actualiza disponibilidad y reduce o elimina las unidades que ya no pueden reservarse.
INVENTORY_VERSION_CONFLICT · 409
Vuelve a leer el item, recalcula target_on_hand y reintenta con una clave nueva.
ON_HAND_BELOW_RESERVED · 409
No bajes on_hand por debajo de reserved; libera compromisos o usa una cantidad mayor.
RESERVATION_CONFLICT · 409
Vuelve a leer la reserva y no mezcles comandos de pedido externo con comandos públicos.
RESERVATION_EXPIRED · 409
Crea una reserva nueva después de refrescar disponibilidad.
RESERVATION_MANAGED_BY_EXTERNAL_ORDER · 409
Cambia el estado mediante external-orders; no renueves, consumas ni liberes la reserva directamente.

Productos e imágenes

MARKETPLACE_NOT_ACTIVE · 409
La tienda no está activa en Moku Marketplace, por lo que este producto no puede publicarse. Puedes seguir preparándolo como borrador privado.
MEDIA_QUOTA_EXCEEDED · 409
La tienda alcanzó su presupuesto de 256 MiB o 1000 imágenes, incluidas cargas pendientes. Contacta a Moku antes de agregar más; reintentar el mismo contenido no consume otra admisión.
PRODUCT_REVISION_CONFLICT · 409
Vuelve a leer el producto, fusiona cambios y reintenta con expected_revision actualizado.
PRODUCT_SLUG_CONFLICT · 409
Moku asigna las URL de los productos. Reintenta la misma solicitud; si el conflicto persiste, contacta a soporte.
PRODUCT_STATE_CONFLICT · 409
Vuelve a leer el producto y usa una transición válida para su estado actual.
SOURCE_MEDIA_NOT_READY · 409
Carga primero los bytes de la imagen privada y confirma su admisión antes de vincular su digest al producto.
SOURCE_MEDIA_SNAPSHOT_CHANGED · 409
Consulta otra vez las imágenes pendientes y recalcula los hashes de los archivos actuales. Conserva la identidad de cualquier intento incierto hasta reconciliar su recibo.

Pedidos y fulfillment

EXTERNAL_ORDER_LINES_LOCKED · 409
Conserva las líneas comerciales originales; corrige el pedido en el sistema fuente mediante una operación compatible.
EXTERNAL_ORDER_SOURCE_CONFLICT · 409
No reutilices una versión fuente para contenido diferente; envía una nueva versión y timestamp.
EXTERNAL_ORDER_STALE · 409
Descarta la observación antigua y continúa desde el último source_updated_at aceptado.
EXTERNAL_ORDER_TERMINAL · 409
No intentes reabrir un pedido terminal; consulta su estado y crea un pedido distinto si corresponde.
EXTERNAL_ORDER_TERMINAL_REVIEW_REQUIRED · 409
Resuelve primero terminal-resolution con la versión actual del pedido externo.
FULFILLMENT_REVIEW_REQUIRED · 409
Detén fulfillment y completa la revisión terminal antes de avanzar.
FULFILLMENT_STATE_CONFLICT · 409
Vuelve a leer el pedido y aplica solo la siguiente transición monotónica permitida.
FULFILLMENT_VERSION_CONFLICT · 409
Vuelve a leer fulfillment y reintenta con expected_version actualizado.

Conexiones, autoridad y listings

AUTHORITY_CONFLICT · 409
Detén escrituras, liquida reservas y pedidos pendientes, vuelve a leer la autoridad y reintenta el reemplazo de roles.
CONNECTION_STATE_CONFLICT · 409
Vuelve a leer la conexión o conflicto y reintenta con su versión actual.
LISTING_CONFLICT · 409
Vuelve a leer el listing y corrige identidad, estado o versión fuente antes de reintentar.
OBSERVATION_STALE · 409
Descarta la inspección antigua y envía una captura nueva con otra Idempotency-Key. La clave rechazada repite el mismo rechazo.

Claves de idempotencia

IDEMPOTENCY_KEY_REUSED · 409
Usa una clave nueva para trabajo distinto; conserva la clave original solo para un replay idéntico.

413 · Archivo demasiado grande

MEDIA_TOO_LARGE · 413
Reduce la imagen: tanto el archivo original como la salida normalizada deben medir hasta 2 MiB.

429 · Límite de solicitudes

Cada credencial dispone de 60 lecturas y 12 escrituras por minuto. Las respuestas incluyen RateLimit-Limit, RateLimit-Remaining y RateLimit-Reset; un 429 agrega Retry-After.

RATE_LIMITED · 429
Espera Retry-After, agrega jitter y reduce concurrencia por PAT.

500 · Error interno

INTERNAL_ERROR · 500
Registra request_id y contacta soporte; verifica el estado antes de repetir una escritura.

503 · Servicio no disponible

SERVICE_UNAVAILABLE · 503
Reintenta con backoff; para una escritura idéntica conserva la misma Idempotency-Key y verifica el estado.