Operaciones de productos de tienda
Lista, consulta, crea, edita, borra e importa productos de tu tienda por API v1 o MCP.
Un producto de tienda es un artículo físico que vendes junto al resto de tu catálogo: camisetas, material, merchandising… A diferencia de una actividad, un alojamiento o un pack, no tiene fechas ni sesiones: vive en sus variaciones (por ejemplo, cada talla), cada una con su propio precio y su propio stock. Se muestra en tu tienda pública igual que el resto de productos. Más sobre cómo lo ve tu cliente en Cómo se ve tu tienda (catálogo público).
Operaciones de esta página: list-shop-products, get-shop-product, create-shop-product, update-shop-product, delete-shop-product, import-shop-products (solo lectura: list-shop-products, get-shop-product; escritura: el resto).
Las bases comunes (autenticación, GET frente a POST, prueba en seco, errores HTTP, límites) están en API v1 — Introducción y primeros pasos.
list-shop-products: lista los productos de tu tienda
Devuelve los productos de tienda: identificador, título, categorías, impuestos, si está desactivado o borrado, sus variaciones (precio y stock) y el stock total. Admite filtro de texto, por estado y por fecha de creación, y tres niveles de detalle. Es de solo lectura: vale con cualquier token.
Cómo llamarla
curl "https://api.bukyapp.com/v1/ops/list-shop-products?q=camiseta" \
-H "Authorization: Bearer bk_TU_TOKEN"curl -X POST https://api.bukyapp.com/v1/ops/list-shop-products \
-H "Authorization: Bearer bk_TU_TOKEN" -H "Content-Type: application/json" \
-d '{"detail": "descriptive", "lang": "es"}'Por MCP: «Enséñame los productos de mi tienda» o «Busca camisetas en la tienda».
Parámetros
| Parámetro | Tipo | Nivel | Qué hace |
|---|---|---|---|
q | texto | opcional | Filtro por título (contiene el texto, sin acentos), igual que el buscador del panel de Tienda. |
disabled | booleano | opcional | true trae solo los productos desactivados; false, solo los activos. Si lo omites, salen todos. |
include_deleted | booleano | opcional | true incluye también los productos que ya no se ven en el panel ni en la tienda (el soporte de Buky puede recuperarlos). |
created_from / created_to | fecha | opcional | Rango sobre la fecha de creación, igual que el filtro de fecha del panel. |
detail | texto | opcional | Nivel de detalle: "compact" (por defecto) trae lo justo para identificar y enumerar; "descriptive" añade la ficha completa en texto plano. |
lang | texto | opcional | Solo con detail: "descriptive": idioma de los textos (por ejemplo "es"). Sin lang, cada texto vuelve en todos los idiomas que tengas rellenos. |
full | booleano | opcional | true trae el documento completo de cada producto tal cual lo guarda el servidor. Respuesta pesada; gana a detail si pasas los dos. |
limit | número | opcional | Máximo de productos devueltos. total refleja el total que cumple el filtro, no el recortado. |
Qué devuelve
Ejecución real sobre la cuenta demo, que ahora mismo no tiene ningún producto de tienda dado de alta, vista compacta (por defecto):
{
"operation": "list-shop-products",
"success": true,
"detail": "compact",
"count": 0,
"total": 0,
"items": []
}Con detail: "descriptive", misma tienda vacía:
{
"operation": "list-shop-products",
"success": true,
"detail": "descriptive",
"count": 0,
"total": 0,
"items": []
}| Campo | Significado |
|---|---|
detail | Vista aplicada a items: compact, descriptive o full. |
total | Total de productos que cumplen los filtros, antes de aplicar limit. |
count | Productos que trae items (después de limit). Si count es menor que total, limit recortó resultados. |
items | En vista compacta: _id, título, nombre interno, categorías e impuestos, si está desactivado o borrado, sus variaciones (etiqueta, SKU, precio y stock) y el stock total. En descriptive: lo mismo más la ficha completa en texto plano (descripción, categorías por título, métodos de pago…). En full: el documento tal cual lo guarda el servidor. |
Consejos
- Con la tienda vacía como en el ejemplo, es el primer sitio donde comprobar si ya has dado de alta algún producto antes de usar
get-shop-product. - Para enumerar y decidir con qué producto seguir trabajando, la vista compacta (por defecto) es la más rápida. Pide
fullsolo si necesitas el documento entero: es la respuesta más pesada.
get-shop-product: obtiene un producto por nombre o _id
Devuelve el documento completo y fresco de un producto de tienda, resuelto por nombre o _id. Es de solo lectura: es la forma de comprobar cómo quedó un producto tras crearlo o editarlo. También te da booker_url, el enlace público para comprarlo.
Cómo llamarla
curl "https://api.bukyapp.com/v1/ops/get-shop-product?shop_product=Camiseta%20Buky" \
-H "Authorization: Bearer bk_TU_TOKEN"curl -X POST https://api.bukyapp.com/v1/ops/get-shop-product \
-H "Authorization: Bearer bk_TU_TOKEN" -H "Content-Type: application/json" \
-d '{"shop_product": "Camiseta Buky"}'Por MCP: «Enséñame el producto Camiseta Buky de la tienda». O, para pasarle el enlace a un cliente: «Dame el enlace de compra de Camiseta Buky».
Parámetros
| Parámetro | Tipo | Nivel | Qué hace |
|---|---|---|---|
shop_product | texto | obligatorio | Nombre o _id (24 caracteres hexadecimales) del producto. Se resuelve contra el catálogo real. |
id / shop_product_id | texto | opcional | Alias de shop_product (algunos asistentes usan uno u otro nombre). |
El manual marca shop_product como «recomendado», pero al omitirlo la operación responde con un error, no con una lista: es obligatorio en la práctica.
Qué devuelve
Ejecución real: la cuenta demo no tiene productos de tienda dados de alta, así que buscar por un nombre cualquiera no encuentra nada:
{
"operation": "get-shop-product",
"success": false,
"error": "shop_product_unresolved",
"details": "el customer no tiene ninguna entidad de este tipo configurada",
"candidates": [],
"hint": "El customer no tiene ningún producto de tienda configurado todavía."
}Con un producto real, el manual documenta que la respuesta trae, además, _id (el identificador del producto), resolved (qué producto se eligió y por qué) e item con el documento completo: título, variaciones, impuesto y categorías, y booker_url. La cuenta demo no tiene productos, así que no he podido ver una respuesta con producto real: lo que sigue es lo que documenta el manual.
| Campo | Significado |
|---|---|
_id | Identificador del producto. |
resolved | Qué producto se eligió y por qué. |
item | Documento completo y fresco del producto. |
booker_url | Página pública del producto en tu motor de reservas (con tu dominio propio, si lo tienes). Es el enlace para comprarlo. |
Comprar no existe como operación de la API ni del asistente: para vender el producto, remite al cliente a booker_url.
Errores propios de esta operación
| Situación | Respuesta |
|---|---|
Falta shop_product (y sus alias) | 200 con success: false, error: "shop_product_intent_required". |
| Nombre que no coincide con ningún producto | 200 con success: false, error: "shop_product_unresolved" y candidates con las coincidencias (vacío si la tienda no tiene ningún producto de ese tipo). |
create-shop-product: da de alta un producto físico
Crea un producto de tienda con título, impuesto y una o varias variaciones vendibles (precio y stock). Es de escritura: pruébala siempre en seco antes de pedirle al asistente que la ejecute de verdad.
Cómo llamarla
curl -X POST https://api.bukyapp.com/v1/ops/create-shop-product \
-H "Authorization: Bearer bk_TU_TOKEN" -H "Content-Type: application/json" \
-d '{"title": "Camiseta Buky", "price_eur": 25, "stock": 10, "tax": "default", "dry_run": true}'Por MCP: «Crea un producto de tienda llamado Camiseta Buky a 25 euros con 10 unidades de stock».
Parámetros
| Parámetro | Tipo | Nivel | Qué hace |
|---|---|---|---|
title | texto | obligatorio | Título público del producto (en español; el resto de idiomas queda sin traducir hasta que los edites). |
price_eur + stock | número | obligatorio (forma simple) | Precio en euros y stock (entero, 0 o más) de la variación única «Estándar». stock es obligatorio en cuanto das price_eur. |
variations | lista | obligatorio (forma multi) | Alternativa a price_eur/stock: una fila { label, price_eur, stock, sku? } por talla o combinación. |
sku | texto | opcional | SKU de la variación única (solo forma simple). |
tax | texto | recomendado | Impuesto: "default" (el primero de tu cuenta), un porcentaje, un nombre o un _id. Si lo omites, el producto queda sin impuesto. |
description | texto | opcional | Descripción del producto (en español). |
categories | lista | opcional | Nombres o identificadores de categorías (ver Operaciones de categorías). Sin categoría, el producto puede no salir en tu catálogo público, aunque se crea igual. |
El manual marca price_eur/stock y variations como «recomendados», pero hace falta al menos uno de los dos: sin ninguno, la operación falla con variations_required.
Qué devuelve
Prueba en seco, forma simple:
{
"operation": "create-shop-product",
"dry_run": true,
"success": true,
"resolved_tax": { "id": "69a181ce5d6272575493bcfd", "reason": "default (primer tax del customer)" },
"variations": [
{ "label": "Estándar", "sku": null, "price_cents": 2500, "stock": 10 }
],
"warnings": [
"sin categoría: el producto puede no aparecer en el catálogo público de la tienda (reservable por enlace directo)."
]
}Prueba en seco, forma multi (dos tallas):
{
"success": true,
"resolved_tax": { "id": "69a181ce5d6272575493bcfd", "reason": "default (primer tax del customer)" },
"variations": [
{ "label": "Talla S", "sku": null, "price_cents": 2500, "stock": 5 },
{ "label": "Talla M", "sku": "CAM-M", "price_cents": 2500, "stock": 8 }
]
}| Campo | Significado |
|---|---|
resolved_tax | Impuesto que se aplicará y por qué se eligió. null si omitiste tax. |
variations | Las variaciones que se crearían, ya con el precio pasado a céntimos. |
warnings | Avisos que no bloquean la creación: por ejemplo, sin categoría el producto no sale en el catálogo público (pero sí es reservable por enlace directo). |
Con un producto real (dry_run en false), la respuesta trae además _id: el identificador del producto creado.
Errores propios de esta operación
| Situación | Respuesta |
|---|---|
Falta title | 400 con error: "invalid_params" y el campo en details. |
Ni price_eur/stock ni variations | 200 con success: false, error: "variations_required". |
price_eur sin stock | 200 con success: false, error: "variations_invalid" y code: "stock_invalid". |
Consejos
- Si vendes el mismo artículo en varias tallas, usa
variationsdesde el principio: es un array que se reemplaza entero enupdate-shop-product, así que empezar bien evita tener que reescribirlo luego. - Añade siempre una categoría si quieres que el producto aparezca en tu catálogo público sin depender de un enlace directo.
update-shop-product: edita un producto existente
Edita un producto de tienda ya creado: solo viajan los campos que aportas. Es de escritura.
Cómo llamarla
curl -X POST https://api.bukyapp.com/v1/ops/update-shop-product \
-H "Authorization: Bearer bk_TU_TOKEN" -H "Content-Type: application/json" \
-d '{"product": "Camiseta Buky", "stock": 50, "dry_run": true}'Por MCP: «Pon el stock de Camiseta Buky a 50 unidades».
Parámetros
| Parámetro | Tipo | Nivel | Qué hace |
|---|---|---|---|
product | texto | obligatorio | Producto a editar: título o _id. Sin coincidencia, la respuesta trae candidatos. |
title / description | texto | opcional | Nuevo título o descripción (en español). |
tax | texto | opcional | Nuevo impuesto ("default", porcentaje, nombre o _id). |
categories | lista | opcional | Reemplaza las categorías del producto. |
variations | lista | opcional | Reemplaza todas las variaciones: [{ label, price_eur, stock, sku? }]. Excluyente con price_eur/stock/sku. |
price_eur / stock / sku | número / texto | opcional | Atajo: cambia solo la primera variación (conserva su identificador y su etiqueta). Excluyente con variations. |
Qué devuelve
Prueba en seco sobre un producto ficticio (la tienda demo no tiene ninguno dado de alta ahora mismo, así que la resolución falla en vez de mostrar una vista previa de cambios):
{
"operation": "update-shop-product",
"dry_run": true,
"success": false,
"error": "product_unresolved",
"details": "sin match para 'Camiseta Buky Demo'",
"candidates": [],
"hint": "Especifica uno de candidates (por título exacto o _id)."
}Con un producto real, el manual documenta que la respuesta trae _id y changes con la lista de campos modificados.
Errores propios de esta operación
| Situación | Respuesta |
|---|---|
Falta product | 400 con error: "invalid_params". |
Nombre o _id que no coincide con ningún producto | 200 con success: false, error: "product_unresolved" y candidates con las coincidencias. |
Consejos
- Si solo quieres reponer stock o cambiar un precio y el producto tiene una única variación, usa el atajo
price_eur/stock/sku: es más simple que reenviarvariationsentero. - Con varias tallas, pasa siempre el array
variationscompleto: lo que no incluyas desaparece.
delete-shop-product: borra un producto
El producto deja de verse en el panel y en la tienda. No puedes deshacerlo desde la API ni desde el asistente, pero el soporte de Buky puede recuperarlo si lo necesitas. Es de escritura.
Cómo llamarla
curl -X POST https://api.bukyapp.com/v1/ops/delete-shop-product \
-H "Authorization: Bearer bk_TU_TOKEN" -H "Content-Type: application/json" \
-d '{"product": "Camiseta Buky", "dry_run": true}'Por MCP: «Borra el producto Camiseta Buky de la tienda».
Parámetros
| Parámetro | Tipo | Nivel | Qué hace |
|---|---|---|---|
product | texto | obligatorio | Producto a borrar: título o _id. Sin coincidencia, la respuesta trae candidatos. |
Qué devuelve
Prueba en seco sobre un producto ficticio (misma tienda vacía):
{
"operation": "delete-shop-product",
"dry_run": true,
"success": false,
"error": "product_unresolved",
"details": "sin match para 'Camiseta Buky Demo'",
"candidates": []
}Con un producto real, el manual documenta que la respuesta trae _id y confirma el borrado: las órdenes ya hechas con ese producto no se ven afectadas.
Consejos
- El borrado no toca las reservas u órdenes que ya incluyen ese producto: solo deja de estar disponible para nuevas ventas.
import-shop-products: sube tu tienda entera desde filas
Importa varios productos a la vez, agrupando las filas del mismo título en variaciones (por ejemplo, una fila por talla). Es de escritura, y va siempre en dos pasos: primero una prueba en seco que arma la lista de lo que se crearía junto con un manifest_hash, y solo repitiendo la llamada con ese valor como confirm_hash se ejecuta. Es reanudable: si se corta a mitad, una segunda pasada con el mismo confirm_hash omite los títulos que ya se crearon.
Cómo llamarla
Paso 1, prueba en seco, con filas inline:
curl -X POST https://api.bukyapp.com/v1/ops/import-shop-products \
-H "Authorization: Bearer bk_TU_TOKEN" -H "Content-Type: application/json" \
-d '{"rows": [{"title": "Sudadera Buky", "price_eur": "29,90", "stock": "5", "variation": "M"}], "tax": "default"}'Paso 2, con el manifest_hash que te dio el paso 1:
curl -X POST https://api.bukyapp.com/v1/ops/import-shop-products \
-H "Authorization: Bearer bk_TU_TOKEN" -H "Content-Type: application/json" \
-d '{"rows": [...], "tax": "default", "confirm_hash": "<manifest_hash del paso 1>"}'Por MCP: sube el CSV o Excel a la conversación y pide «Importa estos productos a mi tienda»; el asistente lee el fichero, lo convierte en filas y hace las dos llamadas.
Formato de las filas
Claves (o columnas, si vienes de un CSV): title (obligatoria), price_eur (obligatoria), stock (obligatoria, entero), sku, variation (agrupa varias filas del mismo título en variaciones de un solo producto), tax (por fila; si la omites, se usa el parámetro tax de la llamada), category, description. Máximo 1000 filas por llamada: con más, trocea y repite los dos pasos por cada trozo (los títulos ya creados se omiten solos). Los decimales admiten punto o coma.
Parámetros
| Parámetro | Tipo | Nivel | Qué hace |
|---|---|---|---|
rows | lista | obligatorio | Filas: array de objetos, una clave por columna. Es la única forma de pasar los datos, tanto por la API como por MCP; no se acepta una ruta de fichero. |
tax | texto | recomendado | Impuesto por defecto para las filas sin columna tax. Sin él, esas filas quedan sin impuesto. |
category | texto | opcional | Categoría por defecto para las filas sin columna category. |
confirm_hash | texto | opcional | El manifest_hash que te dio la prueba en seco. Solo con él se ejecuta de verdad, y solo si las filas no han cambiado desde entonces. |
Qué devuelve
Prueba en seco real con tres filas por rows (una sudadera en dos tallas y una gorra):
{
"operation": "import-shop-products",
"dry_run": true,
"success": true,
"manifest": {
"source": "rows inline (3)",
"products_total": 2,
"products_to_create": 2,
"variations_total": 3,
"skipped_existing": 0,
"manifest_hash": "bdd221b566c5783f"
},
"sample": [
{
"title": "Sudadera Buky",
"variations": [
{ "label": "M", "sku": null, "price_cents": 2990, "stock": 5 },
{ "label": "L", "sku": null, "price_cents": 2990, "stock": 3 }
],
"taxes": ["69a181ce5d6272575493bcfd"],
"categories": null
}
],
"skipped": [],
"warnings": [],
"hint": "Para ejecutar: repite con confirm_hash: \"bdd221b566c5783f\"."
}| Campo | Significado |
|---|---|
manifest.products_total / products_to_create | Productos distintos detectados y cuántos son nuevos (a los que ya existen se les resta). |
manifest.skipped_existing | Títulos que ya tenías dados de alta y que la importación se saltaría. |
manifest.manifest_hash | El valor a repetir como confirm_hash para ejecutar. Cambia si cambian las filas. |
sample | Los productos que se crearían, ya agrupados en variaciones y con el precio pasado a céntimos. Recortado a un ejemplo en importaciones grandes. |
skipped | Filas descartadas y por qué. |
hint | Repite literalmente la llamada que hace falta para el paso 2. |
Repetí la misma llamada con confirm_hash: "bdd221b566c5783f", pero el script de esta guía fuerza siempre dry_run:true en las operaciones de escritura, así que no se puede provocar la ejecución real desde aquí. El manual documenta que, con confirm_hash y dry_run en falso, la respuesta trae created, skipped (títulos que ya existían) y failed.
Errores propios de esta operación
| Situación | Respuesta |
|---|---|
Falta rows | 200 con success: false, error: "rows_missing". |
Consejos
- Por MCP, sube el fichero (CSV o Excel) a la conversación y deja que el asistente lo convierta en
rows: es la única vía de importación, esté donde esté corriendo la operación. - Si se corta a mitad, repite la llamada tal cual con el mismo
confirm_hash: los títulos ya creados se saltan solos y no se duplican.
