Operaciones de clientes
Lista, consulta, crea, edita, borra e importa fichas de cliente por API v1 o MCP.
Un «cliente» es la ficha de cada persona que reserva contigo: nombre, contacto, documento, dirección, etiquetas y notas internas. Es distinto del usuario del panel: aquí hablamos de quien compra, no de quien administra la cuenta. Para ver esta ficha desde el panel, consulta Ver el perfil y el historial de un cliente.
Operaciones de esta página: list-clients, get-client, create-client, update-client, delete-client, import-clients (solo lectura: list-clients, get-client; escritura: las otras cuatro).
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. La página API pública — Clientes (customerClients) documenta la consulta de estas mismas fichas por la API anterior de Buky; esta página es la de la API v1 actual, con más operaciones (alta, edición, borrado e importación).
En todas las operaciones, customer_id es opcional: se toma de tu token salvo que uses un token multicuenta.
list-clients: lista los clientes de tu cuenta
Es una operación de solo lectura. Devuelve la lista completa de clientes (pagina internamente hasta agotar la colección) en formato compacto, con filtros opcionales.
Cómo llamarla
curl "https://api.bukyapp.com/v1/ops/list-clients?q=garcia&limit=20" \
-H "Authorization: Bearer bk_TU_TOKEN"Por MCP: «Lista mis clientes cuyo nombre o email contenga García».
Parámetros
| Parámetro | Tipo | Nivel | Qué hace |
|---|---|---|---|
q | texto | opcional | Filtro por nombre+apellido o email (contiene, sin tildes). Se aplica tras traer toda la lista. |
email | texto | opcional | Filtro solo por email (contiene, sin tildes). Combinable con q. |
dni | texto | opcional | Filtro por número de documento (contiene). Es el mismo filtro que el documento en el panel. |
marketing | booleano | opcional | true para ver solo los clientes que han dado su consentimiento de marketing. |
tag | texto | opcional | Filtro por etiqueta (contiene, sin tildes). |
created_from, created_to | fecha | opcional | Rango sobre la fecha de alta (ISO). |
full | booleano | opcional | true devuelve la ficha completa de cada cliente en vez de la vista compacta. Por defecto false. |
limit | número | opcional | Recorta el resultado a los N primeros (orden alfabético), después de aplicar los filtros. |
Qué devuelve
Ejemplo real, recortado y con datos anonimizados:
{
"success": true,
"customer_id": "5a55fff67e9fe0036a541d15",
"count": 5,
"total": 12,
"items": [
{
"_id": "69a6cbb1afbedf1dee60bd06",
"name": "Laura",
"surname": "Gómez",
"email": "laura@example.com",
"phone": "+34600000000",
"dni": null,
"lang": "es",
"createdAt": "2026-03-03T11:53:21.052Z"
},
"..."
]
}| Campo | Significado |
|---|---|
count | Clientes devueltos en esta respuesta (tras filtros y limit). |
total | Total de clientes que casan los filtros de la llamada, antes de aplicar limit. |
items | Vista compacta por defecto (_id, name, surname, email, phone, dni, lang, createdAt); ficha completa con full:true. |
Consejos
- Un filtro sin coincidencias devuelve
count: 0yitems: [], no un error. - Para la ficha completa de un solo cliente es mejor
get-client: aquífull:truetrae la ficha completa de todos, más pesado.
get-client: ficha completa de un cliente
Es una operación de solo lectura. Resuelve un cliente por nombre o _id y devuelve el documento completo y fresco del servidor: es la forma de comprobar cómo quedó una ficha tras crearla o editarla.
Cómo llamarla
curl "https://api.bukyapp.com/v1/ops/get-client?client=Laura%20G%C3%B3mez" \
-H "Authorization: Bearer bk_TU_TOKEN"Por MCP: «Enséñame la ficha del cliente Laura Gómez».
Parámetros
| Parámetro | Tipo | Nivel | Qué hace |
|---|---|---|---|
client | texto | obligatorio | Nombre o _id del cliente. El manual lo marca como «recomendado», pero en la práctica es obligatorio: sin él, la operación devuelve error (client_intent_required). Acepta los alias id y client_id. |
Qué devuelve
Ejemplo real, recortado y con datos anonimizados:
{
"success": true,
"_id": "60b09285dac98f1b2514856d",
"resolved": { "id": "60b09285dac98f1b2514856d", "reason": "match por _id" },
"item": {
"_id": "60b09285dac98f1b2514856d",
"name": "Laura",
"surname": "Gómez",
"email": "laura@example.com",
"phone": "+34600000000",
"country": "ES",
"state": "M",
"city": "Madrid",
"street_address": "Calle Mayor 1",
"zip": "28013",
"tags": [],
"check_marketing": false,
"lang": "es",
"createdAt": "2021-05-28T06:49:41.493Z"
}
}| Campo | Significado |
|---|---|
resolved | Qué cliente se eligió a partir de lo que diste y por qué. Compruébalo si el nombre era ambiguo. |
item | Ficha completa: además de los campos de contacto, incluye documento, dirección, nacionalidad, notas internas y etiquetas cuando los tiene. |
Errores propios
| Situación | Respuesta |
|---|---|
Falta client | success: false, error: "client_intent_required". |
| Nombre que no coincide con ningún cliente | success: false, error: "client_unresolved" y client_candidates con toda tu lista de clientes para que elijas. |
create-client: da de alta un cliente
Es una operación de escritura. Crea una ficha nueva con los datos básicos y, opcionalmente, el resto de la ficha completa (documento, dirección, notas, etiquetas). El script de esta guía fuerza siempre dry_run:true: no hay forma de crear un cliente de verdad con él.
Cómo llamarla
curl -X POST https://api.bukyapp.com/v1/ops/create-client \
-H "Authorization: Bearer bk_TU_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "Laura", "surname": "Gómez", "email": "laura@example.com", "phone": "+34 600 00 00 00", "profile": "contactable", "dry_run": true}'Por MCP: «Da de alta un cliente nuevo: Laura Gómez, laura@example.com, 600 00 00 00».
Parámetros
| Parámetro | Tipo | Nivel | Qué hace |
|---|---|---|---|
name | texto | obligatorio | Nombre del cliente. |
surname | texto | opcional | Apellidos. |
email | texto | recomendado | Email. Obligatorio si `profile` es `"contactable"`: sin él, la operación devuelve error email_required. |
phone | texto | opcional | Teléfono. Se normaliza a formato internacional (+34600000000). |
lang | texto | recomendado | Idioma de la ficha y de los emails ("es", "en"…). Por defecto "es". |
profile | texto | recomendado | "lead" (por defecto, captura mínima) o "contactable" (cliente operativo para reservas, exige email). |
origin | texto | opcional | Procedencia: manual (por defecto), checkin, import, booker, migration. |
forms | array | opcional | Formularios a vincular, por nombre o identificador. |
document_type, dni | texto | opcional | Tipo (ID Card, Passport, Driver license, Other) y número de documento. |
birthday | fecha | opcional | Fecha de nacimiento (AAAA-MM-DD). |
gender | texto | opcional | Género. |
street_address, country, state, city, zip | texto | opcional | Dirección. country y nationality aceptan nombre o código; se guarda el código. |
nationality | texto | opcional | Nacionalidad. |
check_marketing | booleano | opcional | Consentimiento de marketing. |
customer_notes | texto | opcional | Notas internas, no visibles para el cliente. |
tags | array | opcional | Etiquetas del cliente. |
Qué devuelve
Respuesta real de la prueba en seco, para un alta mínima con perfil operativo:
{
"success": true,
"dry_run": true,
"resolved_profile": { "profile": "contactable", "requiresEmail": true, "reason": "match exacto 'contactable'" },
"resolved_lang": { "lang": "es", "reason": "default (es)" },
"payload_preview": {
"name": "Laura",
"surname": "Gómez",
"email": "laura@example.com",
"phone": "+34600000000",
"origin": "manual",
"lang": "es"
},
"warnings": []
}| Campo | Significado |
|---|---|
resolved_profile | Perfil aplicado (lead o contactable) y por qué. |
resolved_lang | Idioma aplicado; si no coincide con los idiomas activos de tu cuenta, aparece un aviso informativo en warnings, no un error. |
payload_preview | Lo que se enviaría a crear, ya normalizado (teléfono, país…). |
Errores propios
| Situación | Respuesta |
|---|---|
profile: "contactable" sin email | success: false, error: "email_required". |
Sin name | 400, error: "invalid_params" con el campo que falta. |
Consejos
- Si vas a crear un cliente para que pueda reservar, usa
profile: "contactable"y no olvides el email. - Para altas con ficha completa (por ejemplo, migrando datos de otra herramienta), añade
origin: "import".
update-client: edita una ficha existente
Es una operación de escritura. Cambia solo los campos que le pases; el resto de la ficha queda intacta. El script fuerza dry_run:true siempre.
Cómo llamarla
curl -X POST https://api.bukyapp.com/v1/ops/update-client \
-H "Authorization: Bearer bk_TU_TOKEN" \
-H "Content-Type: application/json" \
-d '{"client": "laura@example.com", "tags": ["VIP", "buceador"], "customer_notes": "Cliente recurrente, prefiere mañanas.", "dry_run": true}'Por MCP: «Añade la etiqueta VIP a Laura Gómez y anota que prefiere las mañanas».
Parámetros
| Parámetro | Tipo | Nivel | Qué hace |
|---|---|---|---|
client | texto | obligatorio | Cliente a editar: nombre completo, email o _id. |
name, surname | texto | recomendado / opcional | Nuevo nombre y apellidos. |
email, phone | texto | opcional | Nuevo email y teléfono (se normaliza a formato internacional). |
lang | texto | opcional | Nuevo idioma de la ficha. |
document_type, dni | texto | opcional | Tipo y número de documento. |
birthday, gender | fecha / texto | opcional | Fecha de nacimiento y género. |
street_address, country, state, city, zip, nationality | texto | opcional | Dirección y nacionalidad. |
check_marketing | booleano | opcional | Consentimiento de marketing. |
customer_notes | texto | opcional | Notas internas. |
tags | array | opcional | Sustituye el conjunto entero de etiquetas, no lo añade: si quieres conservar una etiqueta previa, inclúyela en la lista. |
Qué devuelve
Respuesta real de la prueba en seco:
{
"success": true,
"dry_run": true,
"_id": "60b09285dac98f1b2514856d",
"changes": {
"customer_notes": "Cliente recurrente, prefiere mañanas.",
"tags": ["VIP", "buceador"]
},
"note": "PUT parcial: solo estos campos cambian; customer/_id/__v nunca se envían."
}| Campo | Significado |
|---|---|
changes | Exactamente los campos que se van a modificar, ya normalizados. |
Errores propios
| Situación | Respuesta |
|---|---|
Falta client | 400, error: "invalid_params", campo client marcado como required. |
Consejos
- Editar la ficha propaga los datos de contacto y dirección a las reservas ya hechas por ese cliente.
- Como
tagsreemplaza la lista entera, si solo quieres añadir una etiqueta consulta antes la ficha conget-clienty envía el array completo.
delete-client: elimina un cliente
Es una operación de escritura e irreversible: el cliente se borra físicamente, no hay papelera ni operación de restaurar. Al borrarlo, el servidor anonimiza su nombre en sus reservas (queda como «*Ficha borrada», las reservas siguen existiendo) y borra sus documentos y formularios subidos. El script de esta guía fuerza siempre dry_run:true, así que ni con confirm:true llega a borrar nada de verdad.
Cómo llamarla
curl -X POST https://api.bukyapp.com/v1/ops/delete-client \
-H "Authorization: Bearer bk_TU_TOKEN" \
-H "Content-Type: application/json" \
-d '{"client": "laura@example.com", "confirm": true}'Por MCP: «Borra la ficha del cliente Laura Gómez» (el asistente te avisará del carácter irreversible antes de confirmar).
Parámetros
| Parámetro | Tipo | Nivel | Qué hace |
|---|---|---|---|
client | texto | obligatorio | Cliente a borrar: nombre completo, email o _id. |
confirm | booleano | recomendado | Debe ser true para borrar de verdad; sin él, la operación se queda en modo de prueba. |
Qué devuelve
Prueba en seco real, con confirm: true:
{
"success": true,
"dry_run": true,
"would_delete": { "_id": "60b09285dac98f1b2514856d", "name": "Laura Gómez" },
"reversible": false,
"cascade": {
"orders": "el pax se ANONIMIZA en sus orders → nombre \"*Ficha borrada\" (las reservas siguen)",
"s3": "sus uploads/forms en S3 (documentos subidos, fichas de checkin) se BORRAN"
},
"warnings": ["IRREVERSIBLE: el cliente se borra FÍSICAMENTE; el server anonimiza su pax en orders y borra sus uploads/forms en S3."]
}| Campo | Significado |
|---|---|
would_delete | Qué cliente se borraría. Compruébalo antes de confirmar. |
cascade | Lo que ocurre en sus reservas y documentos al borrarlo: la cascada es tan irreversible como el borrado en sí. |
Consejos
- El borrado no se puede deshacer. Si dudas, no borres: usa
update-clientpara vaciar campos o quitar etiquetas.
import-clients: importación masiva de clientes
Es una operación de escritura, pensada para volcar de golpe clientes de otra herramienta (una hoja de cálculo, un CSV exportado). Necesita un token de administrador de la cuenta. La entrada son siempre filas en memoria (rows, un array de objetos): no se acepta una ruta de fichero del disco, ni por API ni por MCP. El proceso tiene siempre dos pasos: una prueba en seco que genera un informe y una «firma» (manifest_hash), y una ejecución posterior que repite exactamente lo firmado. El script de esta guía se queda siempre en el primer paso.
Cómo llamarla
Por API, primero la prueba en seco (sin confirm_hash):
curl -X POST https://api.bukyapp.com/v1/ops/import-clients \
-H "Authorization: Bearer bk_TU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"rows": [{"Nombre": "Laura Gómez", "Email": "laura@example.com", "Teléfono": "600000000"}],
"mapping": {"fields": {
"name": {"column": "Nombre", "transform": "split-name", "part": "name"},
"surname": {"column": "Nombre", "transform": "split-name", "part": "surname"},
"email": {"column": "Email", "transform": "lower"},
"phone": {"column": "Teléfono", "transform": "phone"}
}},
"dry_run": true
}'Y luego, tras revisar el informe, la ejecución con el hash devuelto:
{ "rows": [ "..." ], "mapping": { "...": "..." }, "confirm_hash": "<manifest_hash del paso anterior>" }Por MCP: «Importa estos clientes desde este fichero» adjuntando el CSV o Excel; el asistente lo lee, arma las filas y el mapeo, y te enseñará el informe antes de pedirte el visto bueno para ejecutar.
Parámetros
| Parámetro | Tipo | Nivel | Qué hace |
|---|---|---|---|
rows | array | obligatorio | Filas de origen: un objeto por fila, con una clave por columna tal cual viene del fichero. Es la única forma de dar la entrada; no se admite una ruta de fichero. Máximo 1000 filas por llamada; un fichero mayor se trocea y se repite el proceso por trozo. |
mapping | objeto | obligatorio | Mapeo inline: un objeto fields con un elemento por cada campo de destino, indicando de qué columna sale (column, column_contains para coincidencia parcial, o un value fijo) y una transformación opcional (partir un nombre completo, pasar a minúsculas, normalizar teléfono...). También admite filters, notes, defaults y null_values para filtrar filas, guardar texto en notas, rellenar valores por defecto o tratar ciertos textos como vacío. Los campos de destino son los mismos que en create-client (name, surname, email, phone, document_type, dni, birthday, gender, street_address, country, state, city, zip, nationality, check_marketing, customer_notes, tags, lang); cualquier otro nombre se rechaza. |
prevent_duplicates | booleano | opcional | Evita duplicados dentro del fichero y contra los clientes que ya existen. Por defecto true. |
confirm_hash | texto | recomendado | El manifest_hash de la prueba en seco. Obligatorio para ejecutar de verdad (no para la prueba en seco); si las filas o el mapeo cambiaron después, la operación lo detecta y rechaza el hash. |
sample | número | opcional | Cuántas filas de muestra incluir en el informe. Por defecto 10. |
Qué devuelve
Prueba en seco real con tres filas ficticias, dos de ellas idénticas:
{
"success": true,
"dry_run": true,
"manifest_hash": "f1032a09d934868eb80c0a4b15afa12f62dad30f70787912d4f8b9b03041ce57",
"report": {
"total_source": 3,
"mapped": 3,
"rejected_local": [],
"intra_duplicates": 1,
"db_existing": 12,
"db_collisions": 0,
"lang_distribution": { "(schema:es)": 3 },
"sample_rows": [
{ "name": "Laura", "surname": "Gómez", "email": "laura@example.com", "phone": "600000000" },
"..."
]
},
"next_step": "Firma este informe y ejecuta con confirm_hash='f1032a09d934868eb80c0a4b15afa12f62dad30f70787912d4f8b9b03041ce57'."
}| Campo | Significado |
|---|---|
manifest_hash | Identifica exactamente estas filas + este mapeo. Es lo que firmas y lo que pasas como confirm_hash para ejecutar. |
report.mapped / rejected_local | Filas que se pudieron traducir a ficha de cliente y las que no, con el motivo. |
report.intra_duplicates | Filas repetidas dentro del propio fichero (mismo criterio de deduplicación). En la prueba con dos filas idénticas, la operación lo detectó correctamente (intra_duplicates: 1). |
report.db_collisions | Filas que coinciden con un cliente que ya existe en tu cuenta, con ese mismo criterio. |
payload_preview | Solo en la prueba en seco: la llamada exacta que se haría al confirmar. |
Errores propios
| Situación | Respuesta |
|---|
Consejos
- No encadenes la prueba en seco y la ejecución en el mismo mensaje: enseña el informe y espera el visto bueno antes de pasar
confirm_hash. - Si la ejecución responde
hash_mismatch, no lo fuerces: repite la prueba en seco con las filas y el mapeo actuales y vuelve a pedir confirmación. - Con más de 1000 filas, trocea el fichero en el cliente (asistente o script) y repite prueba en seco + ejecución por cada trozo; la deduplicación del servidor evita que se dupliquen las filas ya subidas.
