API de órdenes (beta)
Beta. La API está en beta: el contrato puede cambiar, y avisamos antes.
Si tu ecommerce es de desarrollo propio, no hay un conector que descargue tus órdenes. Con esta API las envías tú, desde tu sistema, y aparecen en Wivo junto a las de tus marketplaces: en Ventas, en Rentabilidad y en el Maestro.
Envías lotes de órdenes a POST /orders. Cada orden viaja completa, con todas sus líneas, y Wivo calcula el resto: el costo desde tu Maestro, y el despacho y la comisión de la pasarela repartidos entre las líneas.
Está incluida en el plan Pro: no es un add-on. Por ahora es solo para cuentas de Chile, con montos en pesos chilenos (CLP).
¿Para qué sirve?
- Ver las ventas de tu tienda propia en los mismos dashboards que las de tus marketplaces
- Calcular su rentabilidad con el costo de tu Maestro, el costo de despacho y la comisión de tu pasarela de pago
- Mantener al día cancelaciones y devoluciones: reenvías la orden con su nuevo estado y Wivo la reemplaza
Requisitos
- Cuenta Wivo con plan Pro, de Chile
- Tu API Key, de la vista Integración con API
- El
source_idde tu fuente Tienda propia (API)
No necesitas el add-on de la API de datos: este recurso viene incluido en el plan Pro.
¿Cómo obtener tu API Key?
- Ingresa a Wivo con tu usuario.
- Abre Integración con API en el menú, o entra directo a app.wivoanalytics.com/u/0/#/api.
- Copia tu API Key. Con el plan Pro la obtienes ahí mismo, sin el add-on de la API de datos. Es personal, secreta y está asociada a tu cuenta.
Es la misma clave para todos los recursos de la API de Wivo.
¿Cómo obtener tu source_id?
El source_id identifica tu fuente Tienda propia (API): es la “integración” a la que llegan tus órdenes. La creas tú, sin credenciales:
- Abre Integraciones en el menú, o entra a app.wivoanalytics.com/u/0/#/marketplaces/accounts.
- Haz clic en Agregar y, en Tiendas Ecommerce, elige Tienda propia (API) · Beta. No te pide credenciales: la fuente se crea al elegirla.
- Copia el
source_idque aparece en pantalla.
Después lo sigues viendo en Integraciones, junto a tu Tienda propia (API). Cada cuenta tiene una sola: si la eliges de nuevo, Wivo te muestra la que ya tienes.
Base URL
https://api.wivoanalytics.com
Autenticación
Todas las solicitudes deben incluir la API Key en el siguiente header HTTP:
Ocp-Apim-Subscription-Key: TU_API_KEY_AQUI
Solicitudes sin API Key o con clave inválida reciben 401 Unauthorized.
Importante: Tu API Key es un secreto. No la compartas, no la incluyas en código fuente público ni la expongas en logs. Almacénala en variables de entorno o en un gestor de secretos.
POST /orders
Recibe un lote de hasta 100 órdenes, en JSON (Content-Type: application/json).
Lo que tienes que saber antes de integrar
- La unidad es la orden completa. Cada vez que envías una orden, trae todas sus líneas, no solo la que cambió. Reenviar el mismo
order_idreemplaza la orden entera: una línea que no viene, desaparece. Por eso reenviar nunca duplica. purchase_dateno cambia. Es la fecha de compra y se mantiene igual en cada reenvío. Si la corres más de 5 días, la orden aparece dos veces.- Todo o nada. Si una sola orden del lote tiene un error, no entra ninguna. La respuesta
422te dice qué campo de qué orden falló, para que corrijas y reenvíes el lote completo. 202quiere decir recibido, no insertado. Tus órdenes aparecen en la app unos minutos después.- Montos en CLP, con IVA incluido, tal como los ve el comprador.
- El costo del producto no viaja en la orden. Se carga en el Maestro, como en cualquier otro canal (ver la receta).
Cuerpo
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
source_id | UUID | Sí | El source_id de tu Tienda propia (API). Tiene que ser de la misma cuenta que la API Key |
orders | Lista | Sí | Entre 1 y 100 órdenes |
Campos de cada orden
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
order_id | Texto (id) | Sí | ID de la orden en tu tienda. Único por fuente y dentro del lote. Reenviarlo reemplaza la orden completa |
purchase_date | Fecha | Sí | Fecha de compra. No cambia entre envíos |
last_update_date | Fecha | Sí | Última modificación de la orden. No puede ser anterior a purchase_date |
currency | Texto | Sí | Siempre "CLP" |
payment_status | Texto | Sí | paid o unpaid |
shipment_status | Texto | No | Estado del despacho (ver estados). Default noinformation |
shopper | Objeto | No | El comprador: id (obligatorio dentro del objeto) y name |
shipping_cost | Monto | No | Lo que te cuesta a ti despachar la orden, no lo que paga el comprador |
shipping_income | Monto | No | Lo que pagó el comprador por el despacho, con IVA. Si no viene, es 0 |
payment_fee | Monto | No | Comisión de la pasarela de pago (Webpay, Mercado Pago, etc.) de la orden |
items | Lista | Sí | Todas las líneas de la orden, entre 1 y 200 |
Campos de cada línea
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
product_id | Texto (id) | Sí | ID del producto en tu tienda. Es el ID Producto con que aparece en el Maestro |
sku | Texto (id) | No | SKU del producto |
name | Texto | Sí | Nombre del producto |
variant | Objeto | No | La variante: id (obligatorio dentro del objeto) y name |
brand | Texto | No | Marca |
category | Texto | No | Categoría |
quantity | Entero | Sí | Unidades, de 1 a 100.000 |
unit_price | Monto | Sí | Precio unitario de lista, con IVA, antes del descuento |
discount | Monto | No | Descuento total de la línea (no unitario), con IVA. Default 0. No puede superar unit_price × quantity |
status | Texto | Sí | Estado de la línea: paid, canceled, returned o pending |
Si no envías variant, brand, category o shopper, Wivo los muestra como “Sin información”.
Estados
Solo se aceptan los estados de Wivo. Si tu tienda usa otros nombres, conviértelos antes de enviar: un estado que no está en la lista rechaza el lote.
items[].status, estado de la línea:
| Valor | Significado |
|---|---|
paid | Pagada |
canceled | Cancelada |
returned | Devuelta |
pending | Pendiente de pago. Wivo la cuenta como no concretada, igual que una cancelada, hasta que reenvíes la orden con la línea en paid |
payment_status, estado de pago de la orden: paid (pagada) o unpaid (no pagada).
shipment_status, estado del despacho de la orden:
| Valor | Significado |
|---|---|
noinformation | Sin información (default) |
inpreparation | En preparación |
intransit | En tránsito |
delivered | Entregada |
returned | Devuelta |
returninprocess | Devolución en curso |
inmediation | En mediación |
faileddelivery | Entrega fallida |
notrequired | No requiere despacho |
canceled | Cancelada |
Fechas
Las fechas van en formato RFC 3339 con offset explícito: 2026-09-24T15:30:00-03:00 o 2026-09-24T18:30:00Z. Sin offset, se rechazan.
- Separador
Tentre fecha y hora (no un espacio) - Segundos obligatorios; fracción de segundo opcional (
15:30:00.123-03:00) - Offset
Zo±HH:MM. No se aceptan-03ni-0300
¿Por qué el offset? Chile cambia de horario dos veces al año, y una hora local sin zona es ambigua en el cambio: puede correr una orden de día sin que nadie lo note.
Además, purchase_date no puede estar más de 24 horas en el futuro ni tener más de 2 años de antigüedad.
Montos
Todos los montos van en CLP, con IVA incluido, y no pueden ser negativos.
- El total pagado de una línea es
unit_price × quantity − discount. shipping_costes tu costo de despacho;shipping_income, lo que te pagó el comprador por el despacho. Tu costo neto de despacho es la diferencia. No mezcles el ingreso enshipping_cost: invertiría el sentido del número en tu rentabilidad.payment_feees la comisión de la pasarela. Si no la envías, la comisión queda como desconocida.- Wivo reparte
shipping_cost,shipping_incomeypayment_feeentre las líneas de la orden en proporción al total de cada línea: la línea más cara carga más.
Por ejemplo, en la orden WEB-1001 del ejemplo válido las líneas suman $23.980 y $8.990 (total $32.970). El despacho de $3.990 queda en $2.902,04 para la primera y $1.087,96 para la segunda.
Identificadores y textos
- Los ids (
order_id,product_id,sku,variant.id,shopper.id) tienen hasta 64 caracteres y no pueden llevar comilla simple (') ni caracteres de control. - Los textos (
name,brand,category,variant.name,shopper.name) tienen hasta 255 caracteres y sí aceptan la comilla simple: “María O’Higgins” es válido. - El
source_ides un UUID de 36 caracteres, con guiones. Da lo mismo si viene en mayúsculas o minúsculas. - Un
order_idno puede repetirse dentro del mismo lote.
Ejemplos
cURL
curl -X POST "https://api.wivoanalytics.com/orders" \
-H "Ocp-Apim-Subscription-Key: TU_API_KEY_AQUI" \
-H "Content-Type: application/json" \
-d '{
"source_id": "TU_SOURCE_ID_AQUI",
"orders": [
{
"order_id": "WEB-1001",
"purchase_date": "2026-09-20T15:30:00-03:00",
"last_update_date": "2026-09-20T16:05:00-03:00",
"currency": "CLP",
"payment_status": "paid",
"shipment_status": "delivered",
"shipping_cost": 3990,
"shipping_income": 2990,
"payment_fee": 1250,
"items": [
{
"product_id": "P-10",
"sku": "POL-AZ-M",
"name": "Polera azul",
"quantity": 2,
"unit_price": 12990,
"discount": 2000,
"status": "paid"
}
]
}
]
}'
Si tus datos traen comilla simple (por ejemplo, un comprador “O’Higgins”), en la terminal envía el cuerpo desde un archivo, así la comilla no corta el comando:
curl -X POST "https://api.wivoanalytics.com/orders" \
-H "Ocp-Apim-Subscription-Key: TU_API_KEY_AQUI" \
-H "Content-Type: application/json" \
--data @ordenes.json
Python (requests)
import requests
lote = {
"source_id": "TU_SOURCE_ID_AQUI",
"orders": [
{
"order_id": "WEB-1001",
"purchase_date": "2026-09-20T15:30:00-03:00",
"last_update_date": "2026-09-20T16:05:00-03:00",
"currency": "CLP",
"payment_status": "paid",
"items": [
{"product_id": "P-10", "name": "Polera azul", "quantity": 2,
"unit_price": 12990, "discount": 2000, "status": "paid"},
],
}
],
}
response = requests.post(
"https://api.wivoanalytics.com/orders",
headers={"Ocp-Apim-Subscription-Key": "TU_API_KEY_AQUI"},
json=lote,
timeout=30,
)
cuerpo = response.json()
if response.status_code == 202:
print(f"{cuerpo['accepted']} órdenes recibidas")
elif response.status_code == 422:
for error in cuerpo["errors"]:
print(f"Orden {error['order_id']} · {error['field']}: {error['error']}")
else:
print(response.status_code, cuerpo)
Node.js (fetch)
const lote = {
source_id: "TU_SOURCE_ID_AQUI",
orders: [
{
order_id: "WEB-1001",
purchase_date: "2026-09-20T15:30:00-03:00",
last_update_date: "2026-09-20T16:05:00-03:00",
currency: "CLP",
payment_status: "paid",
items: [
{ product_id: "P-10", name: "Polera azul", quantity: 2,
unit_price: 12990, discount: 2000, status: "paid" },
],
},
],
};
const response = await fetch("https://api.wivoanalytics.com/orders", {
method: "POST",
headers: {
"Ocp-Apim-Subscription-Key": "TU_API_KEY_AQUI",
"Content-Type": "application/json",
},
body: JSON.stringify(lote),
});
const cuerpo = await response.json();
if (response.status === 202) {
console.log(`${cuerpo.accepted} órdenes recibidas`);
} else if (response.status === 422) {
for (const e of cuerpo.errors) console.error(`Orden ${e.order_id} · ${e.field}: ${e.error}`);
} else {
console.error(response.status, cuerpo);
}
Ejemplo válido completo
Dos órdenes: una pagada y entregada, con todos los campos opcionales, y una no pagada con su única línea cancelada, solo con los obligatorios.
{
"source_id": "3f2b6c1e-8a4d-4c2e-9b7a-1d2e3f4a5b6c",
"orders": [
{
"order_id": "WEB-1001",
"purchase_date": "2026-09-20T15:30:00-03:00",
"last_update_date": "2026-09-20T16:05:00-03:00",
"currency": "CLP",
"payment_status": "paid",
"shipment_status": "delivered",
"shopper": {"id": "C-77", "name": "María O'Higgins"},
"shipping_cost": 3990,
"payment_fee": 1250,
"items": [
{"product_id": "P-10", "sku": "POL-AZ-M", "name": "Polera azul", "variant": {"id": "P-10-M", "name": "M"}, "brand": "Marca Propia", "category": "Poleras", "quantity": 2, "unit_price": 12990, "discount": 2000, "status": "paid"},
{"product_id": "P-22", "name": "Gorro de lana", "quantity": 1, "unit_price": 8990, "status": "paid"}
]
},
{
"order_id": "WEB-1002",
"purchase_date": "2026-09-21T10:00:00-03:00",
"last_update_date": "2026-09-22T09:00:00-03:00",
"currency": "CLP",
"payment_status": "unpaid",
"items": [
{"product_id": "P-10", "name": "Polera azul", "quantity": 1, "unit_price": 12990, "status": "canceled"}
]
}
]
}
Ejemplo inválido
Cinco errores en una sola orden: fecha sin offset, un estado de pago que no es de Wivo (pagado), cantidad 0, precio negativo y un campo que no existe en el contrato (costo, que además es justo lo que no se envía).
{
"source_id": "3f2b6c1e-8a4d-4c2e-9b7a-1d2e3f4a5b6c",
"orders": [
{
"order_id": "WEB-2001",
"purchase_date": "2026-09-20T15:30:00",
"last_update_date": "2026-09-20T16:05:00-03:00",
"currency": "CLP",
"payment_status": "pagado",
"items": [
{"product_id": "P-10", "name": "Polera azul", "quantity": 0, "unit_price": -5, "status": "paid", "costo": 4000}
]
}
]
}
La respuesta es un 422 con un error por problema, y no entra ninguna orden:
{
"errors": [
{"index": 0, "order_id": "WEB-2001", "field": "/orders/0/purchase_date", "error": "Debe ser una fecha RFC 3339 con offset explícito, p. ej. 2026-09-24T15:30:00-03:00."},
{"index": 0, "order_id": "WEB-2001", "field": "/orders/0/payment_status", "error": "Debe ser uno de: paid, unpaid."},
{"index": 0, "order_id": "WEB-2001", "field": "/orders/0/items/0/costo", "error": "El campo 'costo' no existe en el contrato v1."},
{"index": 0, "order_id": "WEB-2001", "field": "/orders/0/items/0/quantity", "error": "Debe ser mayor o igual a 1."},
{"index": 0, "order_id": "WEB-2001", "field": "/orders/0/items/0/unit_price", "error": "Debe ser mayor o igual a 0."}
],
"correlation_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
JSON Schema v1
Es el schema con el que la API valida cada lote (JSON Schema draft 2020-12). Puedes usarlo para validar tus lotes antes de enviarlos. Las reglas que el schema no alcanza a expresar están más abajo.
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://api.wivoanalytics.com/schemas/orders-v1.schema.json",
"title": "Wivo — lote de órdenes v1 (Tienda propia API)",
"description": "Cuerpo de POST /orders (contrato v1).",
"type": "object",
"additionalProperties": false,
"required": ["source_id", "orders"],
"properties": {
"source_id": {
"description": "UUID (identifier) de la fuente 'Tienda propia (API)' del tenant. Se verifica contra el tenant de la API key.",
"type": "string",
"format": "uuid"
},
"orders": {
"type": "array",
"minItems": 1,
"maxItems": 100,
"items": { "$ref": "#/$defs/order" }
}
},
"$defs": {
"text": {
"type": "string",
"minLength": 1,
"maxLength": 255,
"pattern": "^[^\\u0000-\\u001f]*$"
},
"id": {
"type": "string",
"minLength": 1,
"maxLength": 64,
"description": "Sin caracteres de control ni comilla simple.",
"pattern": "^[^\\u0000-\\u001f']*$"
},
"amount": {
"type": "number",
"minimum": 0,
"maximum": 100000000000
},
"datetime": {
"description": "ISO 8601 / RFC 3339 CON offset explícito (p. ej. 2026-09-24T15:30:00-03:00). Sin offset se rechaza.",
"type": "string",
"format": "date-time"
},
"order": {
"type": "object",
"additionalProperties": false,
"required": ["order_id", "purchase_date", "last_update_date", "currency", "payment_status", "items"],
"properties": {
"order_id": {
"$ref": "#/$defs/id",
"description": "ID de la orden en la tienda. Único por fuente. Reenviar el mismo order_id REEMPLAZA la orden completa."
},
"purchase_date": {
"$ref": "#/$defs/datetime",
"description": "Fecha de compra. INMUTABLE: si cambia más de 5 días respecto de lo ya enviado, la orden se duplica."
},
"last_update_date": { "$ref": "#/$defs/datetime" },
"currency": { "const": "CLP" },
"payment_status": { "enum": ["paid", "unpaid"] },
"shipment_status": {
"enum": ["noinformation", "inpreparation", "intransit", "delivered", "returned", "returninprocess", "inmediation", "faileddelivery", "notrequired", "canceled"],
"default": "noinformation"
},
"shopper": {
"type": "object",
"additionalProperties": false,
"required": ["id"],
"properties": {
"id": { "$ref": "#/$defs/id" },
"name": { "$ref": "#/$defs/text" }
}
},
"shipping_cost": {
"$ref": "#/$defs/amount",
"description": "Lo que le cuesta AL SELLER despachar la orden (no lo que paga el comprador). Wivo lo prorratea entre las líneas por su total."
},
"shipping_income": {
"$ref": "#/$defs/amount",
"description": "Lo que PAGÓ EL COMPRADOR por el despacho, IVA incluido. Va a shipping_import, prorrateado entre las líneas por su total. El costo neto de despacho del seller = shipping_cost − shipping_income."
},
"payment_fee": {
"$ref": "#/$defs/amount",
"description": "Comisión de la pasarela de pago de la orden (Webpay, Mercado Pago, etc.). Va a market_fee, prorrateada entre las líneas por su total."
},
"items": {
"type": "array",
"minItems": 1,
"maxItems": 200,
"description": "TODAS las líneas de la orden, siempre. Una actualización trae la orden completa.",
"items": { "$ref": "#/$defs/item" }
}
}
},
"item": {
"type": "object",
"additionalProperties": false,
"required": ["product_id", "name", "quantity", "unit_price", "status"],
"properties": {
"product_id": {
"$ref": "#/$defs/id",
"description": "ID del producto en la tienda. Es el 'ID Producto' con el que aparece en el Maestro de Wivo."
},
"sku": { "$ref": "#/$defs/id" },
"name": { "$ref": "#/$defs/text" },
"variant": {
"type": "object",
"additionalProperties": false,
"required": ["id"],
"properties": {
"id": { "$ref": "#/$defs/id" },
"name": { "$ref": "#/$defs/text" }
}
},
"brand": { "$ref": "#/$defs/text" },
"category": { "$ref": "#/$defs/text" },
"quantity": { "type": "integer", "minimum": 1, "maximum": 100000 },
"unit_price": {
"$ref": "#/$defs/amount",
"description": "Precio unitario de lista, IVA incluido, ANTES del descuento."
},
"discount": {
"$ref": "#/$defs/amount",
"default": 0,
"description": "Descuento TOTAL de la línea (no unitario), IVA incluido. No puede superar unit_price × quantity."
},
"status": { "enum": ["paid", "canceled", "returned", "pending"] }
}
}
}
}
Reglas que el schema no expresa
La API también rechaza con 422:
- Un
order_idrepetido dentro del mismo lote. - Un
discountmayor queunit_price × quantity. - Un
purchase_datemás de 24 horas en el futuro o con más de 2 años de antigüedad. - Un
last_update_dateanterior apurchase_date. - Fechas y UUID fuera del formato estricto: los
formatdate-timeyuuidse validan de verdad. Fecha con separadorT(no espacio), segundos obligatorios, fracción opcional y offsetZo±HH:MM; laTy laZse aceptan también en minúscula. UUID canónico de 36 caracteres, sin prefijourn:uuid:ni llaves, en mayúsculas o minúsculas.
Si validas con una biblioteca de JSON Schema, activa la validación de formatos (en Python, FormatChecker; en Node, ajv-formats): sin ella, una fecha sin offset pasaría tu validación y la API la rechazaría.
Respuestas del servidor
Toda respuesta de la API lleva un correlation_id. Guárdalo: es lo que nos sirve para rastrear un envío si nos escribes.
| Código | Significado | Reintentar |
|---|---|---|
202 | Lote recibido. Las órdenes aparecen en la app unos minutos después | No |
400 | El cuerpo está vacío o no es un JSON válido | No, corrige el cuerpo |
401 | API Key ausente o inválida | No |
403 | Cuenta sin plan Pro, suscripción revocada, o un source_id que no es una Tienda propia (API) activa de tu cuenta. También cuando se agota el tope diario | No |
413 | Más de 100 órdenes o más de 1 MB en el lote | No: divide el lote |
422 | Lote inválido. No entró ninguna orden | No: corrige y reenvía el lote completo |
429 | Superaste las 30 solicitudes por minuto | Sí, después de lo que indique el header Retry-After |
503 | No pudimos recibir el lote en este momento (retryable: true) | Sí, el mismo lote |
202 Accepted
{
"accepted": 1,
"order_ids": ["WEB-1001"],
"dag_run_id": "tpapi__3f2b6c1e-8a4d-4c2e-9b7a-1d2e3f4a5b6c__f5610004e788a9df4b644366ee7112b4ffd54eb640d2d2eed526b5e0fd874f31",
"duplicate": false,
"correlation_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
| Campo | Descripción |
|---|---|
accepted | Cantidad de órdenes recibidas |
order_ids | Los order_id recibidos, en el orden del lote |
dag_run_id | Identificador del procesamiento del lote. Cítalo si nos escribes por un envío |
duplicate | true si ya habíamos recibido exactamente este mismo lote: no se procesa dos veces |
202 quiere decir que recibimos el lote, no que las órdenes ya estén en tus reportes: aparecen en la app unos minutos después.
422 Unprocessable Entity
Cada error trae:
| Campo | Descripción |
|---|---|
index | Posición de la orden en orders (desde 0). null si el error es del lote, por ejemplo en /source_id |
order_id | El order_id de esa orden, o null si no vino o no es válido |
field | JSON Pointer al campo con error, por ejemplo /orders/0/items/1/unit_price. Un campo que sobra apunta a ese campo: /orders/0/items/0/costo |
error | Qué está mal |
La lista trae como máximo 100 errores. Si había más, la respuesta agrega "truncated": true. El ejemplo completo está en Ejemplo inválido.
413 Payload Too Large
{
"error": "El lote trae 101 órdenes y el máximo por request es 100. Divide el envío en lotes de hasta 100 órdenes.",
"max_orders": 100,
"correlation_id": "a1b2c3d4-..."
}
Si el lote pesa más de 1 MB, la respuesta trae "max_bytes": 1048576 en lugar de max_orders. En los dos casos, reenviar el mismo lote no va a funcionar: divídelo.
403 Forbidden
{
"error": "La fuente '3f2b6c1e-8a4d-4c2e-9b7a-1d2e3f4a5b6c' no es una fuente 'Tienda propia (API)' activa de tu cuenta.",
"correlation_id": "a1b2c3d4-..."
}
503 Service Unavailable
{
"error": "No pudimos recibir el lote en este momento. Reintenta el mismo lote: si el contenido es idéntico, Wivo reconoce el reintento y no lo procesa dos veces.",
"retryable": true,
"correlation_id": "a1b2c3d4-..."
}
Límites de uso
- 100 órdenes por solicitud, y hasta 200 líneas por orden
- 1 MB por solicitud (1.048.576 bytes)
- 30 solicitudes por minuto por API Key
- 5.000 solicitudes por día
Pasarte de 100 órdenes o de 1 MB retorna 413; de 30 solicitudes por minuto, 429; y agotar las 5.000 del día, 403 hasta que se renueve la cuota.
Con lotes de 100 órdenes, 30 solicitudes por minuto son 3.000 órdenes por minuto. Enviar un lote cada 2 segundos te mantiene bajo el límite.
Reintentos e idempotencia
Reenviar es seguro. Hay dos garantías, y juntas cubren cualquier reintento:
- El mismo lote no se procesa dos veces. Si reenvías exactamente el mismo contenido (aunque cambien el orden de las claves o los espacios), la API responde
202con"duplicate": truey el mismodag_run_id. - Reenviar una orden la reemplaza. Si el contenido cambió, es un lote nuevo, y cada
order_idreemplaza a su versión anterior. Nunca se duplica.
Qué hacer según la respuesta:
| Respuesta | Qué hacer |
|---|---|
503 con retryable: true | Reintenta el mismo lote, con espera creciente (2, 4, 8… segundos) |
429 | Espera los segundos del header Retry-After y reintenta |
| Sin respuesta (timeout, corte de red) | Reintenta el mismo lote: si ya había llegado, responde duplicate: true |
400, 401, 403, 413, 422 | No reintentes igual: corrige primero. Reintentar el mismo lote da el mismo error |
Recetas
Enviar muchas órdenes, con reintentos
Divide tus órdenes en lotes de 100, envíalos a un ritmo que respete el límite por minuto y reintenta solo lo que se arregla reintentando.
import time
import requests
URL = "https://api.wivoanalytics.com/orders"
HEADERS = {"Ocp-Apim-Subscription-Key": "TU_API_KEY_AQUI"}
SOURCE_ID = "TU_SOURCE_ID_AQUI"
def enviar_lote(ordenes, intentos=5):
lote = {"source_id": SOURCE_ID, "orders": ordenes}
espera = 2
for intento in range(intentos):
try:
response = requests.post(URL, headers=HEADERS, json=lote, timeout=30)
except requests.RequestException:
response = None # sin respuesta: reenviar el mismo lote es seguro
if response is not None:
if response.status_code == 202:
return response.json()
if response.status_code == 429:
espera = int(response.headers.get("Retry-After", espera))
elif response.status_code != 503:
# 400, 401, 403, 413 y 422 no se arreglan reintentando
raise RuntimeError(f"Lote rechazado ({response.status_code}): {response.text}")
time.sleep(espera)
espera = min(espera * 2, 60)
raise RuntimeError("No se pudo enviar el lote: reintenta más tarde con el mismo contenido")
ordenes = [...] # las órdenes de tu tienda, cada una completa
for i in range(0, len(ordenes), 100):
resultado = enviar_lote(ordenes[i:i + 100])
print(resultado["accepted"], "órdenes recibidas", "(ya estaban)" if resultado["duplicate"] else "")
time.sleep(2) # 30 solicitudes por minuto
Si un lote de 100 órdenes con muchas líneas pesa más de 1 MB, la API responde 413 con max_bytes: usa lotes más chicos.
Actualizar una orden: cancelación o devolución
Reenvía la orden completa, con el mismo order_id y el mismo purchase_date, las líneas en su nuevo estado y un last_update_date nuevo. Por ejemplo, si el comprador devolvió el gorro de la orden WEB-1001:
{
"source_id": "TU_SOURCE_ID_AQUI",
"orders": [
{
"order_id": "WEB-1001",
"purchase_date": "2026-09-20T15:30:00-03:00",
"last_update_date": "2026-09-25T11:00:00-03:00",
"currency": "CLP",
"payment_status": "paid",
"shipment_status": "delivered",
"shipping_cost": 3990,
"payment_fee": 1250,
"items": [
{"product_id": "P-10", "name": "Polera azul", "quantity": 2, "unit_price": 12990, "discount": 2000, "status": "paid"},
{"product_id": "P-22", "name": "Gorro de lana", "quantity": 1, "unit_price": 8990, "status": "returned"}
]
}
]
}
La polera viaja igual, aunque no haya cambiado: si la omites, esa línea desaparece de Wivo.
Cargar el costo de tus productos
El costo no va en la orden. Los productos de tus órdenes aparecen en el Maestro con Marketplace “Tienda propia”, y ahí les cargas el costo como en cualquier otro canal: con la planilla del Maestro en la app, o por API.
Para hacerlo por API, lee los productos de tu tienda que aún no tienen costo con el Maestro de productos por API, filtrando por tu source_id (es el ID Cuenta de tus productos):
curl "https://api.wivoanalytics.com/master/products?source_id=TU_SOURCE_ID_AQUI&withoutCost=true" \
-H "Ocp-Apim-Subscription-Key: TU_API_KEY_AQUI"
Y devuelve los costos con la carga de costos por API. El product_id que envías en cada línea es el ID Producto del Maestro.
Preguntas frecuentes
¿Cuánto tardan mis órdenes en aparecer en Wivo?
Unos minutos después del 202. El 202 confirma que recibimos el lote; el procesamiento viene después.
¿Cómo sé que mis órdenes están llegando? En Integraciones, tu Tienda propia (API) muestra cuándo llegó la última orden. Si todavía no llega ninguna, lo dice.
¿Qué pasa si recibí un 202 pero las órdenes no aparecen?
En esta versión no hay un endpoint para consultar el estado de un lote. Si pasado un rato no las ves, escríbenos por el chat de la app o a soporte@wivoanalytics.com con el correlation_id y el dag_run_id de la respuesta, y lo revisamos.
¿Qué pasa si envío dos veces el mismo lote?
Nada malo. La API responde 202 con "duplicate": true y el lote no se procesa dos veces.
¿Puedo enviar solo la línea que cambió?
No. Cada envío trae la orden completa, porque reenviar un order_id reemplaza la orden entera: una línea que no viene, desaparece.
¿Puedo corregir la fecha de compra de una orden?
Evítalo: purchase_date tiene que ser siempre la misma. Si la corres más de 5 días, la orden aparece dos veces.
¿Puedo borrar una orden?
No en esta versión. Reenviarla con sus líneas en canceled cambia su estado, pero no la borra.
¿Puedo enviar el costo de los productos en la orden?
No, y un campo costo rechaza el lote. El costo vive en el Maestro, como en todos tus canales: así hay una sola fuente de verdad. Ver Cargar el costo de tus productos.
¿Por qué mi order_id con comilla simple da error?
Los ids no aceptan comilla simple, para que el order_id quede en Wivo exactamente igual que en tu tienda y puedas cruzarlos. Los textos, como el nombre del comprador, sí la aceptan.
¿Puedo enviar órdenes antiguas?
Sí, con purchase_date de hasta 2 años atrás, en lotes de 100 y respetando los límites de uso. No hay una carga histórica masiva aparte.
¿Puedo enviar órdenes en otra moneda o de otro país? Por ahora no: durante la beta la API es solo para cuentas de Chile y montos en CLP.
¿Este recurso consume mi cuota de la API de datos? No. Viene incluido en el plan Pro, no necesita el add-on y tiene su propio límite de 30 solicitudes por minuto.