Conecta tu web con ONEXFY
Muestra el catálogo del comercio en tu sitio con precios y stock reales, y envía los pedidos directo al ERP como ventas online. Una API REST con JSON y un token por comercio.
https://api.onexfy.com/api/public/v1Inicio rápido
- El comercio entra a ONEXFY y genera un token en Perfil → Integraciones → API pública. Elige los permisos (
catalog:readpara leer el catálogo,orders:writepara crear pedidos), la lista de precios y la bodega que verá la web. - Te comparte el token. Empieza por
onxapi_y se muestra una sola vez; si se pierde, se genera otro y se revoca el anterior. - Verifica que funciona con una llamada a
/store:
curl https://api.onexfy.com/api/public/v1/store \
-H "X-Api-Key: onxapi_TU_TOKEN"
Si responde 200 con los datos del comercio, ya puedes leer productos y crear pedidos.
Autenticación
Envía el token en cada solicitud con el header X-Api-Key. También se acepta Authorization: Bearer onxapi_…. El token solo da acceso a los datos del comercio que lo generó.
| Permiso | Qué habilita |
|---|---|
| catalog:read | /store, /categories, /products, /payment-methods |
| orders:write | Crear pedidos y consultarlos: POST /orders, GET /orders/{ref} |
Cada token tiene asignada una lista de precios (por ejemplo, precio detal o mayorista) y opcionalmente una bodega. Los precios y el stock que devuelve la API, y los que se aplican al pedido, salen de esa configuración.
Convenciones
| Formato | JSON en UTF-8. Envía Content-Type: application/json en los POST. |
| Respuestas | Los datos vienen en data; los listados paginados añaden meta. |
| Dinero | Pesos colombianos (COP), números sin separadores, IVA incluido. Ej.: 59900. |
| Fechas | ISO-8601 con zona horaria. Ej.: 2026-10-22T09:30:00-05:00. |
| Paginación | ?page=2&per_page=50 (máximo 100). meta.last_page dice cuántas páginas hay. |
| Límite | 120 solicitudes por minuto por token. Al superarlo recibes 429; espera unos segundos y reintenta. |
Errores
Todos los errores tienen la misma forma. Usa error.code en tu lógica; message es texto para personas y puede cambiar.
{
"error": {
"code": "invalid_items",
"message": "Hay items que no se pueden vender.",
"details": { "items.1": "Sin stock suficiente para 3 unidad(es)." }
}
}
| HTTP | code | Qué hacer |
|---|---|---|
| 401 | unauthenticated, invalid_token | Falta el token o fue revocado. Pide uno nuevo al comercio. |
| 403 | insufficient_scope | El token no tiene el permiso de ese endpoint. |
| 403 | module_disabled, account_inactive | La API no está activa para el comercio o su cuenta está inactiva. El comercio debe contactar a ONEXFY. |
| 404 | not_found | El producto o pedido no existe (o no está publicado en el catálogo). |
| 422 | validation_error | Revisa details: trae el error por campo, p. ej. customer.dni. |
| 422 | invalid_items | Algún producto no existe, no tiene stock o le falta la variante. El pedido no se creó. |
| 409 | out_of_stock | El stock se agotó justo al confirmar (otra venta al mismo tiempo). No se creó nada. Vuelve a consultar el producto antes de reintentar. |
| 429 | rate_limited | Espera y reintenta. |
| 500 | server_error | Reintenta con la misma reference; nunca se duplica el pedido. |
Conectar una tienda web
El flujo que recomendamos para una tienda con carrito:
Carga todo con /products una vez y guárdalo en tu base o caché. Luego, cada 5–15 minutos, pide solo lo modificado con updated_since.
Usa /products/{id} para descripción, galería y variantes. Muestra sale_price tachando price cuando exista. En productos con variantes esos dos valores son "desde" (el menor de las variantes); al elegir una variante muestra su price y sale_price, que es lo que se cobra.
Antes del checkout vuelve a leer el producto: stock y precio cambian en la tienda física también.
Cuando el comprador confirma (o tu pasarela aprueba el pago), envía POST /orders desde tu servidor con tu número de pedido como reference.
Productos con variantes
Si type es variable, el producto tiene variantes (talla, color…). El comprador debe elegir una y en el pedido envías el variant_id (o el SKU de la variante). El stock de un producto variable es la suma de sus variantes.
Stock sin control
Si stock viene en null, el comercio no lleva inventario de ese producto (por ejemplo, combos o servicios): muéstralo como disponible y acepta el pedido. in_stock siempre te dice si se puede vender.
Qué se cobra
ONEXFY calcula el precio de cada línea con la misma regla que ves en el catálogo: sale_price si existe, si no price. No envías precios en el pedido. El total final del pedido es grand_total = productos + shipping.
/store
catalog:readDatos del comercio y de la configuración del token. Úsalo para probar la conexión y para mostrar teléfono o dirección en tu sitio.
{
"data": {
"id": 1260,
"name": "Tienda Ejemplo",
"phone": "3001234567",
"email": "ventas@tiendaejemplo.com",
"address": "Cl 10 # 43-20",
"city": "Medellín",
"currency": "COP",
"token": { "name": "Tienda web", "scopes": ["catalog:read", "orders:write"], "price_list_id": null, "storage_id": 31 }
}
}
/categories
catalog:readTodas las categorías del comercio, ordenadas por nombre. parent_id indica la categoría padre para armar un menú en árbol.
{ "data": [
{ "id": 12, "name": "Camisetas", "slug": "camisetas", "description": null, "parent_id": null },
{ "id": 15, "name": "Polos", "slug": "polos", "description": null, "parent_id": 12 }
] }
/products
catalog:readProductos que el comercio publicó en su catálogo (opción Mostrar en catálogo en ONEXFY). Un producto que no aparece aquí no se puede vender por la API.
| Parámetro | Descripción |
|---|---|
| page | Página, desde 1. |
| per_page | Resultados por página, 1 a 100. Por defecto 20. |
| search | Busca en nombre y SKU. |
| category_id | Solo productos de esa categoría. |
| in_stock | true para ocultar agotados. |
| updated_since | Solo productos modificados desde esa fecha. Ideal para sincronizar. |
| sort | name (defecto), -name, newest, updated. |
curl "https://api.onexfy.com/api/public/v1/products?category_id=12&in_stock=true&per_page=50" \ -H "X-Api-Key: onxapi_TU_TOKEN"
{
"data": [
{
"id": 1532,
"sku": "CAM-001",
"name": "Camiseta básica",
"slug": "camiseta-basica",
"type": "variable",
"price": 59900,
"sale_price": 49900,
"tax_rate": 19,
"stock": 14,
"in_stock": true,
"image": "https://…/camiseta-basica.webp",
"categories": [{ "id": 12, "name": "Camisetas", "slug": "camisetas" }],
"variants_count": 3,
"updated_at": "2026-10-21T16:02:11-05:00"
}
],
"meta": { "page": 1, "per_page": 50, "total": 134, "last_page": 3 }
}
/products/{id}
catalog:readDetalle completo. {id} acepta el id numérico o el SKU. Además de los campos del listado trae description (puede tener HTML), short_description, barcode, unit, todas las images (la principal primero) y las variants.
{
"data": {
"id": 1532,
"name": "Camiseta básica",
"type": "variable",
"price": 59900,
"sale_price": 49900,
"images": ["https://…/frente.webp", "https://…/espalda.webp"],
"variants": [
{ "id": 8811, "sku": "CAM-001-NEG-S", "name": "Negro / S", "price": 59900, "sale_price": 49900, "stock": 0, "in_stock": false },
{ "id": 8812, "sku": "CAM-001-NEG-M", "name": "Negro / M", "price": 59900, "sale_price": 49900, "stock": 4, "in_stock": true }
]
}
}
/payment-methods
catalog:readMedios de pago que acepta POST /orders en payment_method_id. Mapea cada método de tu checkout (contra entrega, pasarela, transferencia…) a uno de estos ids una sola vez y guárdalo en tu configuración.
{ "data": [
{ "id": 1000011, "name": "Contra entrega" },
{ "id": 101, "name": "Pago en línea" }
] }
Los nombres e ids de este ejemplo son ilustrativos; usa los que devuelva la API.
/orders
orders:writeCrea una venta online en el ERP del comercio: descuenta inventario, registra o actualiza al cliente y queda lista para despacho y facturación.
- Todo o nada. Si un item no existe o no tiene stock, se responde
422 invalid_itemsy no se crea nada. - Sin duplicados.
referencees tu número de pedido. Si la envías otra vez recibes200con el pedido que ya existe. Ante un timeout o un 500, reintenta con la misma referencia. Solo se compara con pedidos que creaste por la API. - Pago confirmado. Envía
paid: truecuando tu pasarela ya aprobó el pago; el pedido queda aprobado y pagado. Confalsequeda pendiente (por ejemplo, contra entrega).
Cuerpo
| Campo | Descripción |
|---|---|
| referenceobligatorio | Tu número de pedido, máx. 60 caracteres. Único por comercio. |
| payment_method_idobligatorio | Id de /payment-methods. |
| paid | true si el pago ya está confirmado. Por defecto false. |
| shipping | Valor del envío cobrado al cliente. Por defecto 0. |
| notes | Observaciones del comprador, máx. 1000 caracteres. |
| customer.nameobligatorio | Nombre completo. |
| customer.dni | Cédula o NIT, solo números. Obligatorio para compradores de Colombia. |
| customer.document_type_id | 3 Cédula (defecto), 6 NIT. |
| customer.phoneobligatorio | Celular de contacto para la entrega. |
| customer.email | Correo del comprador. |
| customer.addressobligatorio | Dirección de entrega. |
| customer.address_note | Barrio, apartamento, indicaciones. |
| customer.cityobligatorio | Ciudad o municipio. |
| customer.state | Departamento. |
| customer.country | Código ISO de 2 letras. Por defecto CO. |
| itemsobligatorio | De 1 a 100 líneas. Cada una con quantity y una de: product_id, variant_id o sku. |
curl -X POST https://api.onexfy.com/api/public/v1/orders \ -H "X-Api-Key: onxapi_TU_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "reference": "WEB-10045", "payment_method_id": 1000011, "paid": false, "shipping": 12000, "notes": "Entregar en portería", "customer": { "name": "Laura Gómez", "dni": "1036951937", "email": "laura@example.com", "phone": "3001234567", "address": "Cra 43A # 1-50 apto 302", "city": "Medellín", "state": "Antioquia" }, "items": [ { "product_id": 1532, "variant_id": 8812, "quantity": 2 }, { "sku": "GORRA-AZ", "quantity": 1 } ] }'
// PHP 7.4+ con cURL. El token sale de una variable de entorno, nunca del código. $payload = [ 'reference' => 'WEB-10045', 'payment_method_id' => 1000011, 'paid' => false, 'shipping' => 12000, 'customer' => [ 'name' => 'Laura Gómez', 'dni' => '1036951937', 'phone' => '3001234567', 'email' => 'laura@example.com', 'address' => 'Cra 43A # 1-50 apto 302', 'city' => 'Medellín', ], 'items' => [ ['variant_id' => 8812, 'quantity' => 2], ['sku' => 'GORRA-AZ', 'quantity' => 1], ], ]; $ch = curl_init('https://api.onexfy.com/api/public/v1/orders'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30, CURLOPT_HTTPHEADER => [ 'X-Api-Key: ' . getenv('ONEXFY_API_KEY'), 'Content-Type: application/json', ], CURLOPT_POSTFIELDS => json_encode($payload), ]); $body = json_decode(curl_exec($ch), true); $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); // 201 creado, 200 ya existía (reintento): ambos son éxito. if ($status === 201 || $status === 200) { $orderId = $body['data']['id']; } elseif ($status === 422) { // Mostrar al comprador: $body['error']['details'] }
// Node 18+ (fetch nativo). Ejecutar en el servidor, nunca en el navegador. const res = await fetch('https://api.onexfy.com/api/public/v1/orders', { method: 'POST', headers: { 'X-Api-Key': process.env.ONEXFY_API_KEY, 'Content-Type': 'application/json', }, body: JSON.stringify({ reference: 'WEB-10045', payment_method_id: 1000011, paid: false, shipping: 12000, customer: { name: 'Laura Gómez', dni: '1036951937', phone: '3001234567', email: 'laura@example.com', address: 'Cra 43A # 1-50 apto 302', city: 'Medellín', }, items: [ { variant_id: 8812, quantity: 2 }, { sku: 'GORRA-AZ', quantity: 1 }, ], }), }); const body = await res.json(); if (res.status === 201 || res.status === 200) { console.log('Pedido ONEXFY', body.data.id); } else { console.error(body.error.code, body.error.details); }
{
"data": {
"id": 205871,
"reference": "WEB-10045",
"paid": false,
"approved": false,
"items_total": 134700,
"shipping": 12000,
"grand_total": 146700,
"subtotal": 113193,
"tax_total": 21506.67,
"payment_method": { "id": 1000011, "name": "Contra entrega" },
"invoice_number": null,
"tracking_number": null,
"customer": { "name": "Laura Gómez", "dni": "1036951937", "email": "laura@example.com", "phone": "3001234567" },
"items": [
{ "product_id": 1532, "variant_id": 8812, "sku": "CAM-001-NEG-M", "name": "Camiseta básica Negro / M", "quantity": 2, "unit_price": 59900, "total": 119800 },
{ "product_id": 1610, "variant_id": null, "sku": "GORRA-AZ", "name": "Gorra azul", "quantity": 1, "unit_price": 14900, "total": 14900 }
],
"created_at": "2026-10-22T10:14:03-05:00"
}
}
/orders/{reference}
orders:writeConsulta un pedido creado por la API con tu misma reference. Devuelve la misma estructura que la creación. Úsalo para mostrar al comprador el estado: paid, invoice_number cuando se factura y tracking_number cuando se despacha con guía.
curl https://api.onexfy.com/api/public/v1/orders/WEB-10045 \
-H "X-Api-Key: onxapi_TU_TOKEN"
Para no exceder el límite, consulta el estado cuando el comprador lo pida o como máximo cada pocos minutos por pedido abierto.