Ir al contenido

Manejo de errores

Toda la API devuelve errores con un formato consistente para que tu cliente pueda manejarlos de forma uniforme.

{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Los datos enviados no son válidos.",
"details": [
{
"field": "items[0].quantity",
"message": "quantity debe ser mayor a 0",
"value": -1
}
]
},
"requestId": "550e8400-e29b-41d4-a716-446655440000"
}
CampoDescripción
successSiempre false en respuestas de error.
error.codeCódigo machine-readable. Usalo para branching en tu cliente.
error.messageMensaje human-readable en español. Apto para logs; no siempre apto para mostrar al usuario final.
error.detailsArray de errores de validación por campo (solo en 400 VALIDATION_ERROR).
requestIdUUID único de la request. Incluilo siempre cuando reportes un problema a soporte.
HTTPCódigoDescripciónAcción recomendada
400VALIDATION_ERRORDatos inválidos en el requestCorregir según details
400INVALID_JSONBody mal formadoVerificar serialización
401UNAUTHORIZEDAPI Key inválida, expirada o ausenteRevisar header Authorization
403FORBIDDENKey válida sin permiso para el recursoPedir ajuste de permisos a soporte
403IP_NOT_ALLOWEDIP no autorizada para esta keyActualizar allowlist con soporte
404NOT_FOUNDRecurso no encontradoVerificar externalId
409CONFLICTConflicto de estado (ej: cancelar orden en proceso)Revisar estado actual
409DUPLICATE_SKUSKU existente con otro externalIdUnificar en un solo externalId
409IDEMPOTENCY_CONFLICTMisma idempotency key, distintos parámetrosGenerar nueva key o mandar mismos parámetros
413PAYLOAD_TOO_LARGEBody excede límite (5MB productos, 10MB órdenes)Reducir tamaño del batch
429RATE_LIMIT_EXCEEDEDSuperado el rate limitEsperar retryAfter segundos
500INTERNAL_ERRORError interno del servidorRetry con backoff; si persiste, reportar a soporte

El request no cumple el schema o tiene valores fuera de rango. El campo details te dice exactamente qué falla:

{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Los datos enviados no son válidos.",
"details": [
{ "field": "items[0].quantity", "message": "quantity debe ser mayor a 0", "value": -1 },
{ "field": "customer.email", "message": "email debe tener formato válido", "value": "no-es-email" }
]
},
"requestId": "..."
}

No reintentar. Arreglá el request antes de volver a mandarlo.

Ver guía de autenticación para el detalle. Resumen:

  • 401: problema de key (ausente, mal formateada, revocada, expirada).
  • 403: key válida pero sin permiso, o IP bloqueada.

No reintentar: es un problema de configuración, no transitorio.

El recurso (producto u orden) con ese externalId no existe en Pickwise. Puede ser un typo o que todavía no lo hayas creado.

Depende del código específico:

  • CONFLICT genérico: típicamente al cancelar una orden que ya está en IN_PICKING o posterior. No reintentar; consultar estado antes.
  • DUPLICATE_SKU: el SKU que querés usar está asociado a otro externalId. Decidí cuál es el correcto en tu sistema y unificá.
  • IDEMPOTENCY_CONFLICT: ver guía de idempotencia.
  • /products/batch: máximo 5 MB y 200 items.
  • /orders/batch: máximo 10 MB y 50 items.

Partí el batch en chunks más chicos.

Ver guía de rate limiting. Respetá retryAfter y reintentá.

Algo falló del lado de Pickwise. Siempre es retriable con backoff exponencial. Si persiste después de 3 intentos, reportá a soporte con el requestId.

Código¿Retriable?Estrategia
400 VALIDATION_ERRORCorregir request.
400 INVALID_JSONCorregir serialización.
401 / 403Problema de configuración.
404Verificar externalId.
409 CONFLICTRevisar estado.
409 DUPLICATE_SKUUnificar externalId.
409 IDEMPOTENCY_CONFLICTGenerar nueva idempotency key.
413Reducir tamaño del batch.
429Esperar retryAfter y reintentar.
500 INTERNAL_ERRORBackoff exponencial, max 3 intentos.
502 / 503 / 504Backoff exponencial, max 3 intentos.

Todo response (éxito o error) incluye un requestId único. Guardarlo en tus logs te permite:

  • Cruzar errores del lado cliente con logs del lado Pickwise.
  • Cuando reportás un problema a soporte, incluirlo para que localicen la request exacta en cuestión de segundos.

Cliente Node.js con handling completo de errores:

class PickwiseError extends Error {
constructor(code, message, status, details, requestId) {
super(message);
this.code = code;
this.status = status;
this.details = details;
this.requestId = requestId;
}
}
const NON_RETRIABLE = new Set([
'VALIDATION_ERROR', 'INVALID_JSON',
'UNAUTHORIZED', 'FORBIDDEN', 'IP_NOT_ALLOWED',
'NOT_FOUND',
'CONFLICT', 'DUPLICATE_SKU', 'IDEMPOTENCY_CONFLICT',
'PAYLOAD_TOO_LARGE'
]);
async function apiCall(url, init, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
const res = await fetch(url, init);
const body = await res.json().catch(() => ({}));
if (res.ok) return body;
const code = body?.error?.code ?? 'UNKNOWN';
const message = body?.error?.message ?? res.statusText;
const details = body?.error?.details;
const requestId = body?.requestId;
// No retriable — tirar error inmediatamente
if (NON_RETRIABLE.has(code)) {
throw new PickwiseError(code, message, res.status, details, requestId);
}
// 429: respetar retryAfter
if (res.status === 429) {
const retryAfter = body?.error?.retryAfter ?? Math.pow(2, attempt);
await new Promise(r => setTimeout(r, retryAfter * 1000));
continue;
}
// 5xx: backoff exponencial
if (res.status >= 500) {
await new Promise(r => setTimeout(r, Math.pow(2, attempt) * 1000));
continue;
}
// Otros 4xx no listados: tirar error
throw new PickwiseError(code, message, res.status, details, requestId);
}
throw new Error('Max retries exceeded');
}
// Uso
try {
const order = await apiCall(
'https://api-{CLIENTE}.pickwise.com.ar/api/v1/public/orders',
{
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.PICKWISE_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(orderPayload)
}
);
console.log('Order created:', order.data.externalId);
} catch (err) {
if (err instanceof PickwiseError) {
console.error(`[${err.requestId}] ${err.code}: ${err.message}`);
if (err.details) console.error('Validation:', err.details);
} else {
console.error('Unexpected error:', err);
}
}