# Quickstart https://dev.bentho.ai/quickstart/ QUICKSTART · ≈ 5 MIN # Tu primera respuesta en tres pasos Necesitas un espacio de Bentho con el módulo Bentho API y algún documento cargado. No hay nada que instalar: la API es HTTP y JSON. ## 1 · Crea tu clave En la consola, abre el módulo API de tu espacio y crea una clave: ponle un nombre (por ejemplo, erp-produccion), elige a qué espacios llega y si solo lee o también escribe. La clave completa se ve una sola vez. Bentho solo guarda su huella: si la pierdes, crea otra y revoca la vieja. Tiene esta forma: bth_, 16 caracteres hexadecimales, un punto y el secreto. Guárdala en una variable de entorno de tu servidor, nunca en el código: BASHCOPIAR ``` export BENTHO_KEY="bth_7f3a1c9e04b2d8a1.xQ2…" ``` ¿Cuál es el id de tu espacio? Pregúntale a la API: devuelve los espacios a los que llega la clave. BASHCOPIAR ``` curl https://bentho.ai/api/companies \ -H "Authorization: Bearer $BENTHO_KEY" # {"companies": [{"id": "mi-espacio", "name": "Mi espacio", "modules": ["api", …], …}]} ``` ¿Ya integraste con bentho.org? https://bentho.org/api sigue valiendo: es la misma API. Las integraciones nuevas, mejor con https://bentho.ai/api. ## 2 · Pregunta Una petición a POST /api/rag/{espacio}/conversations con la pregunta y un session_id tuyo. El session_id es obligatorio: agrupa los turnos, así que reúsalo para las preguntas de seguimiento. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/rag/mi-espacio/conversations \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "question": "¿Cuántos días tengo para devolver un producto?", "session_id": "primera-prueba" }' ``` Tarda entre 2 y 20 segundos. Dale a tu cliente un timeout de al menos 60. ## 3 · Lee la respuesta 200 OK · EJEMPLOCOPIAR ``` { "status_code": "1000", "response": "Según la política de devoluciones, tienes 30 días desde la entrega…\n\nReferencia: politica_devoluciones.pdf", "sources": ["politica_devoluciones.pdf"], "confidence": 0.7556, "warning": false, … } ``` - status_code "1000": contestó con apoyo en tus documentos. - status_code "1001": no había información suficiente y prefirió no inventar. response trae el mensaje de rechazo del espacio y sources llega vacío. - warning: true: contestó, pero solo parte de la respuesta está respaldada. Muéstralo como tal. - confidence: de 0 a 1. Desde 0,75 responde sin reparos. ## Y después - Mostrar la respuesta mientras se escribe: conversations/stream. - Subir documentos desde tu sistema: load_documents. - Que tu cliente aguante los errores y los límites: Errores, límites y créditos. --- # API https://dev.bentho.ai/api/ API V1 # La API de Bentho Pregúntale a los documentos de un espacio, sube y quita documentos, ajusta cómo responde y consulta tus créditos. Todo por HTTP, con JSON y una clave. Los módulos de tu espacio suman sus propias rutas. ## Lo básico | BASE | https://bentho.ai/api | CLAVE | Authorization: Bearer bth_. · Claves y permisos | FORMATO | JSON (Content-Type: application/json), salvo la subida de documentos (multipart) y el stream (SSE). | DESDE DÓNDE | Desde tu servidor. La API no admite llamadas desde el navegador de tus usuarios (CORS), y la clave nunca debe llegar ahí. | VERSIÓN | v1: se agregan campos, nunca se quitan ni se renombran. ## Las rutas | | MÉTODO | RUTA | PARA QUÉ | PERMISO | POST | /rag/{espacio}/conversations | Haz una pregunta a tu espacio | lee | SSE | /rag/{espacio}/conversations/stream | La misma pregunta, en vivo | lee | GET | /rag/{espacio}/documents | Los documentos del espacio | lee | POST | /rag/{espacio}/load_documents | Sube documentos | escribe | DEL | /rag/{espacio}/documents/{archivo} | Quita un documento | escribe | GET | /rag/{espacio}/indexing_status | ¿Ya está listo lo que subí? | lee | PATCH | /rag/{espacio}/config | Ajusta cómo responde el Cerebro | escribe | PATCH | /companies/{espacio}/api-config | Límites, cuota diaria e IPs del espacio | escribe | GET | /cuentas/{cuenta}/bolsa | Tus créditos | lee | GET | /profile | Qué puede hacer tu clave | lee | GET | /companies | Los espacios a los que llega tu clave | lee | GET | /companies/{espacio}/limite | Cuántos módulos caben en el espacio | lee | GET | /companies/{espacio}/semaforo | Si cada módulo del espacio funciona | lee | PATCH | /companies/{espacio}/modules | Activa o quita módulos del espacio | escribe | GET | /rag/{espacio}/documents/{archivo} | Descarga un documento | lee | DEL | /rag/{espacio}/clear_cache | Vacía la caché de respuestas | escribe | GET | /rag/{espacio}/config | Lee cómo responde el Cerebro | lee | GET | /companies/{espacio}/api-config | Lee los límites, la cuota y las IPs del espacio | lee | GET | /companies/{espacio}/cuenta | La cuenta de tu espacio | lee | GET | /cuentas/{cuenta}/cierres | Lo gastado en cada ciclo cerrado | lee ## Módulos Cada módulo de tu espacio suma sus rutas bajo /api/mod/{modulo}/{espacio}/, con la misma clave y los mismos límites. Solo responden en los espacios que tienen ese módulo (los ves en GET /api/companies); en los demás, 403. Cómo se llaman, qué permisos piden y cuáles gastan créditos, en Módulos. - Fuentes (fuentes): Trae la web a los documentos del espacio: fichas con cada dato citado y páginas que se leen tal cual. - Transcripción (transcripcion): Convierte notas de voz y audios en texto. - Agente ventas (agente): Atiende y vende por chat: entiende qué pide el cliente, cotiza con el catálogo y los envíos del espacio y cierra la orden. - Marca (marca): La personalidad del asistente: identidad, tono y tácticas de tu marca. - Productos (productos): El catálogo del espacio: productos, precios, inventario y promociones. - Comercial (comercial): Tarifas de envío, recargos y beneficios. - Facturación (facturacion): Órdenes, total a pagar y revisión de los comprobantes de pago. - Contabilidad (contabilidad): Conecta tu software contable: conciliación y exportaciones. - Kommo (kommo): Sincroniza leads y conversaciones con tu CRM Kommo. - Landings (landing): Crea landings con formulario desde un brief, probadas en un navegador antes de entregarlas. ## Gestionar las claves Crear, listar y revocar claves (/api/tokens) se hace con la sesión de la consola, no con una clave: así una clave filtrada no puede crear otras. Ver Claves y permisos. ## Antes de integrar - Claves y permisos - Lista de IPs - Errores, límites y créditos --- # Claves y permisos https://dev.bentho.ai/api/claves/ API · EMPEZAR # Claves y permisos Cada llamada lleva una clave de servicio en la cabecera. La clave dice qué espacios alcanzas y si puedes escribir; Bentho lo recalcula en cada petición. ## La clave CABECERACOPIAR ``` Authorization: Bearer bth_. ``` - bth_, luego un id de 16 caracteres hexadecimales, un punto y un secreto de 43 caracteres (base64url). - El secreto se ve una sola vez, al crearla. Bentho solo guarda su huella y la compara en tiempo constante. - No caduca: vale hasta que la revocas o deja de cumplirse lo que la sostiene (abajo). ## Crear una clave Lo normal es crearla en la consola, en el módulo API de tu espacio. Al menos uno de sus espacios tiene que tener contratado el módulo Bentho API. | | CAMPO | QUÉ ES | nombre | De 1 a 80 caracteres. Uno por integración: «erp-produccion», «agente-soporte». | espacios | Los ids a los que llega. Vacío ([]) = todos tus espacios en el momento de cada petición, también los que crees después. | permiso | lee o escribe (por defecto). Si tu cuenta es de solo lectura, la clave sale lee. ## A qué llega Los espacios de la clave se cruzan con los tuyos en cada petición. Si te quitan un espacio, la clave deja de llegar a él en la llamada siguiente, con 403 "Sin acceso a este espacio." ## Qué puede hacer | | PERMISO | PUEDE | lee | Preguntar (también en vivo), listar y descargar documentos, leer la configuración y la bolsa. | escribe | Todo lo anterior, más crear y refinar landings, subir y borrar documentos, vaciar la caché y cambiar la configuración. Es el permiso por defecto. El permiso efectivo es el más estricto entre el de la clave y el tuyo. Una clave nunca es administradora, aunque la cree un administrador: las rutas de administración y los campos de plataforma responden 403. ## Cuándo deja de funcionar | | PASA | RESPUESTA | La revocas | 401 desde la petición siguiente | Se borra tu usuario | 401 | Te quitan un espacio | 403 en ese espacio | La prueba del espacio vence | 403 | La cuenta lleva más de 7 días sin pagar, o está suspendida | 402 (la clave sigue siendo válida) | La API del espacio se apaga, o tu IP no está en la lista | 403 (solo a las claves: el Studio sigue entrando) ## Gestionarlas Las claves se crean, se listan y se revocan en la consola, en el módulo API de tu espacio. Allí ves también cuántas llamadas hizo cada una, a qué rutas y cuándo se usó por última vez. Esas operaciones piden la sesión de la consola, nunca una clave: con una clave, /api/tokens responde 403 "Esta operación requiere una sesión de usuario." Así, una clave filtrada no puede crear otras ni revocar las tuyas. ## Buenas prácticas - Una clave por integración: si una se filtra, revocas solo esa. - lee siempre que alcance, sobre todo para agentes. - En una variable de entorno del servidor. Nunca en el navegador ni en el repositorio. - Si la clave va a un sistema con IP fija, añade esa IP en la lista de IPs del espacio. --- # Lista de IPs https://dev.bentho.ai/api/ips/ API · EMPEZAR # Lista de IPs Cada espacio puede aceptar llamadas solo desde ciertas IPs. Una clave filtrada no sirve desde otro sitio. ## Cómo funciona - Vive en ips_permitidas, dentro de la configuración de la API del espacio. - Lista vacía ([]) = cualquier IP. Es el valor por defecto. - Hasta 100 entradas: IPs sueltas o rangos CIDR, IPv4 o IPv6. - Se aplica solo a las claves. El Studio, con tu sesión, sigue entrando: por ahí arreglas una lista mal puesta. ## Qué IP cuenta La IP pública con la que tu servidor sale a internet, tal como llega a Bentho. No la de tu red interna ni la de un proxy intermedio. Antes de activar la lista, comprueba tu IP de salida. Si tu servidor está en la nube, puede salir por un rango y no por una IP fija: pon el rango. ## Configurarla PATCH · CLAVE CON PERMISO ESCRIBECOPIAR ``` curl -X PATCH https://bentho.ai/api/companies/mi-espacio/api-config \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{"ips_permitidas": ["203.0.113.10", "198.51.100.0/24", "2001:db8::/32"]}' ``` Se guarda normalizada: - Un rango con bits de host se ajusta a su red: 198.51.100.7/24 queda 198.51.100.0/24. - Una IP con /32 (o /128) se guarda sin sufijo. - Los duplicados desaparecen. ## Si la IP no está 403COPIAR ``` {"detail": "Esta dirección IP no está autorizada en este espacio."} ``` Una petición rechazada por IP no gasta cuota diaria ni cuenta para el límite por segundo. Si Bentho no puede saber tu IP y la lista no está vacía, también rechaza. Errores al guardar (422): - "«» no es una IP ni un rango CIDR válido." - "Como máximo 100 IPs o rangos." - "«ips_permitidas» tiene que ser una lista." --- # Errores, límites y créditos https://dev.bentho.ai/api/errores/ API · EMPEZAR # Errores, límites y créditos Todo lo que puede decir que no, con el texto que devuelve. Con esto, tu cliente sabe qué reintentar y qué no. ## La forma de un error SIEMPRECOPIAR ``` {"detail": "Sin acceso a este espacio."} ``` Con dos excepciones: la validación de la entrada (422) devuelve una lista en detail, y los 402 devuelven un objeto: 402 · CUENTA NO AL DÍACOPIAR ``` {"detail": {"mensaje": "La cuenta de este espacio no está al día.", "motivo": "pago_vencido", "pagado_hasta": "2026-09-01"}} ``` 402 · CRÉDITOS AGOTADOSCOPIAR ``` {"detail": {"mensaje": "La cuenta de este espacio agotó sus créditos de este ciclo.", "motivo": "bolsa_agotada", "creditos": {"incluidos": 800, "gastados": "800.0000", "restantes": "0.0000"}, "renueva": "2026-10-15"}} ``` motivo puede ser pago_vencido, suspension_comercial o bolsa_agotada. ## Qué puede decir que no Para una ruta de un espacio llamada con una clave: | | STATUS | SI | DETAIL | 401 | Sin clave | No autenticado. | 401 | Clave inexistente, mal formada o revocada | Credencial de servicio inválida o revocada. | 429 | Más de 120 peticiones por minuto con esta clave | Demasiadas peticiones con esta credencial. | 403 | Espacio fuera del alcance de la clave | Sin acceso a este espacio. | 403 | La prueba del espacio venció | La prueba de este espacio ha finalizado. | 402 | La cuenta no está al día | objeto (abajo) | 403 | La API del espacio está apagada | La API de este espacio está apagada. | 403 | Tu IP no está en la lista | Esta dirección IP no está autorizada en este espacio. | 429 | El espacio agotó su cuota diaria | Este espacio agotó su cuota diaria de la API. | 429 | Demasiadas peticiones por segundo en el espacio | Demasiadas peticiones por segundo en este espacio. | 403 | Ruta de escritura con una clave lee | Tu cuenta es de solo lectura en este espacio. | 403 | Ruta de un módulo que el espacio no tiene | módulo no habilitado para este espacio. | 402 | Créditos agotados (preguntar, subir y los módulos) | objeto (abajo) Las validaciones de cada ruta (400, 404, 413, 422) están en su ficha. Una petición rechazada por API apagada o por IP no cuenta para tus límites. ## Otros códigos | | STATUS | CUÁNDO | 404 | La ruta no existe. Si llamas a /api/rag/{espacio} a secas, el detail te dice qué ruta usar. | 405 | Método equivocado en una ruta que existe. Trae la cabecera Allow con los métodos válidos. | 413 | Subida demasiado grande: ver load_documents. | 429 · 503 | El servicio está saturado en ese momento. Llegan sin Retry-After: espera unos segundos y reintenta con espera creciente. | 502 | El servicio no está disponible en ese momento o la pregunta pasó de 90 s. No es tu clave: reintenta con espera creciente. ## Límites | | LÍMITE | VALOR | DÓNDE SE CAMBIA | Por clave | 120 peticiones en cualquier ventana de 60 s (deslizante) | Fijo | Por espacio y clave: tasa | 2 por segundo (1 – 1000) | api-config · solicitudes_por_segundo | Por espacio y clave: ráfaga | 120 seguidas (1 – 5000) | api-config · rafaga | Por espacio: cuota diaria | Sin tope (o 100 – 100 000 000), se reinicia a las 00:00 UTC | api-config · cuota_diaria Los 429 de estos límites traen Retry-After en segundos. El de la cuota diaria apunta a la medianoche UTC. ## Qué reintentar - 429: espera lo que diga Retry-After y reintenta. Si no lo trae, espera unos segundos y dobla la espera en cada intento. - 502 y 503: reintenta con espera creciente, dos o tres veces. - 401, 402, 403, 404, 422: no reintentes. Algo hay que cambiar: la clave, el pago, la configuración o la petición. - 200 con status_code "1001": no es un error de transporte. Reintentar la misma pregunta dará lo mismo. ## Créditos - Gastan créditos las preguntas, la indexación de documentos y lo que hacen los módulos: armar una ficha de Fuentes o lanzar una fuente, y crear, refinar, validar o completar una landing. - Sin créditos se cortan con 402 conversations, conversations/stream, load_documents y las rutas de los módulos que crean o lanzan algo. Leer, listar, configurar y consultar la bolsa siguen funcionando. - El saldo, en la bolsa de créditos. ## Tiempos - Una pregunta tarda de 2 a 20 s. Pon el timeout de tu cliente en 60 s o más. - Una pregunta que pase de 90 s se corta con 502. - En el stream, si no hay datos durante 15 s llega un : keepalive para que la conexión no se corte. --- # Módulos https://dev.bentho.ai/api/modulos/ API · MÓDULOS # Módulos Un módulo suma a tu espacio una capacidad nueva: un catálogo, el cobro de pedidos, la conexión con tu CRM, fichas con datos de la web… Cada uno tiene sus propias rutas en la API, con la misma clave, los mismos límites y los mismos créditos que el resto. También se manejan desde la consola: en el Estudio de tu espacio, cada módulo activo tiene su panel. Lo que haces allí y lo que haces por la API es lo mismo. ## Cómo se llama a un módulo Todas las rutas de un módulo cuelgan de la misma raíz: el id del módulo y el id del espacio. RUTACOPIAR ``` https://bentho.ai/api/mod/{modulo}/{espacio}/{ruta} ``` BASHCOPIAR ``` curl https://bentho.ai/api/mod/productos/mi-espacio/productos \ -H "Authorization: Bearer $BENTHO_KEY" ``` - {modulo} es el id del módulo (la columna ID del catálogo), no su nombre: landing, no «Landings». - {espacio} es el id del espacio, el mismo de las rutas de preguntar y de documentos. - El cuerpo, la respuesta y los errores propios de cada ruta están en su ficha. JSON salvo que la ficha diga otra cosa (subidas de archivos o descargas). ## Qué módulos tiene tu espacio GET /api/companies devuelve los espacios a los que llega tu clave; cada uno trae en modules los ids de los módulos que tiene activos. Un módulo responde solo en los espacios donde está activo: en los demás, cada ruta da 403. RESPUESTA (RECORTADA)COPIAR ``` { "companies": [ { "id": "mi-espacio", "name": "Tienda Aurora", "modules": ["rag-chat", "documents", "productos", "facturacion", "fuentes"] } ] } ``` Cada espacio tiene un cupo de módulos. Míralo en GET /api/companies/{espacio}/limite y activa o quita módulos con PATCH /api/companies/{espacio}/modules (clave escribe), o desde la consola. Algunos módulos solo los activa o los quita Bentho: esos no salen en activables y se piden a tu asesor. ## Permisos - Leer (GET) vale con cualquier clave. - Crear, cambiar o borrar (POST, PUT, PATCH, DELETE) pide una clave escribe; con una lee, 403. - Excepción: conversar con un módulo (por ejemplo, mandarle un mensaje al Agente ventas) es consultar, y vale con una clave lee. Cada ficha dice qué permiso pide. ## Créditos Solo gastan créditos las rutas que ponen a trabajar al asistente: conversar, armar fichas, crear landings… Leer, configurar y gestionar el catálogo, los pedidos o las tarifas no gasta. Sin créditos, esas rutas responden 402 y las demás siguen funcionando; cancelar un trabajo en curso nunca se corta. Estas son las que gastan: | | MÓDULO | MÉTODO | RUTA | PERMISO | Fuentes | POST | /fichas | escribe | Fuentes | POST | /fuentes/{fuente}/lanzar | escribe | Agente ventas | POST | /conversations/ | lee | Agente ventas | SSE | /conversations/stream/ | lee | Agente ventas | POST | /conversations/evidence | lee | Landings | POST | /landings | escribe | Landings | POST | /landings/{landing}/completar | escribe | Landings | POST | /landings/{landing}/refinar | escribe | Landings | POST | /landings/{landing}/validar | escribe Cuánto llevas gastado en un módulo, en créditos, lo dice GET /api/mod/{modulo}/{espacio}/consumo en los módulos que la tienen (Agente ventas y Fuentes); el total de la cuenta, la bolsa de créditos. ## Errores comunes a todos los módulos Además de los de cada ficha y de los generales de la API (401, 429…): | | STATUS | CUÁNDO | DETAIL | 404 | El id del módulo no existe o el módulo se retiró (ventas, comprension) | módulo desconocido. | 403 | El espacio no tiene ese módulo activo | módulo no habilitado para este espacio. | 403 | Clave lee en una ruta que escribe | Tu cuenta es de solo lectura en este espacio. | 403 | Ruta reservada a la administración de Bentho | Esta ruta del módulo es solo para administración. | 402 | Sin créditos, en una ruta que gasta (objeto con motivo) | Ver Errores, límites y créditos | 502 | El módulo no está disponible en este momento | Reintenta 2 o 3 veces con espera creciente ## Catálogo Cada módulo tiene su guía («cómo funciona») y una ficha por ruta. En el mapa (tecla M) se despliegan las rutas del módulo en el que estás. ### Conocimiento Lo que sabe tu espacio. | | MÓDULO | ID | QUÉ HACE | RUTAS | Fuentes | fuentes | Trae la web a los documentos del espacio: fichas con cada dato citado y páginas que se leen tal cual. | 14 | Transcripción | transcripcion | Convierte notas de voz y audios en texto. | 1 ### Conversar Cómo atiende y qué responde. | | MÓDULO | ID | QUÉ HACE | RUTAS | Agente ventas | agente | Atiende y vende por chat: entiende qué pide el cliente, cotiza con el catálogo y los envíos del espacio y cierra la orden. | 30 | Marca | marca | La personalidad del asistente: identidad, tono y tácticas de tu marca. | 3 ### Vender Catálogo, envíos y cobro. | | MÓDULO | ID | QUÉ HACE | RUTAS | Productos | productos | El catálogo del espacio: productos, precios, inventario y promociones. | 52 | Comercial | comercial | Tarifas de envío, recargos y beneficios. | 10 | Facturación | facturacion | Órdenes, total a pagar y revisión de los comprobantes de pago. | 15 ### Integrar Tus otros sistemas. | | MÓDULO | ID | QUÉ HACE | RUTAS | Contabilidad | contabilidad | Conecta tu software contable: conciliación y exportaciones. | 18 | Kommo | kommo | Sincroniza leads y conversaciones con tu CRM Kommo. | 12 ### Crear Piezas listas para publicar. | | MÓDULO | ID | QUÉ HACE | RUTAS | Landings | landing | Crea landings con formulario desde un brief, probadas en un navegador antes de entregarlas. En preparación. | 10 --- # Fuentes https://dev.bentho.ai/api/fuentes/ MÓDULO FUENTES # Fuentes: cómo funciona Fuentes trae información de la web pública a los documentos de tu espacio, para que Bentho la use al responder. Tiene dos formas de hacerlo: - Fichas: le das un tema y una plantilla con los datos que quieres. Bentho busca, lee y llena cada campo con el dato, la página de la que sale y la frase literal que lo respalda. - Fuentes: le das páginas o dominios concretos. Bentho los lee y los publica tal cual, cada vez que la lanzas. ## Antes de empezar - El espacio tiene que tener el módulo Fuentes: fuentes sale en sus módulos en GET /api/companies. Si no, cada ruta responde 403 módulo no habilitado para este espacio. - Armar fichas y crear, lanzar, cancelar o borrar fuentes cambia los documentos del espacio: pide una clave escribe. Leer vale con cualquier clave. - Las rutas cuelgan de /api/mod/fuentes/{espacio}/, con la misma clave y los mismos límites que el resto de la API. ## Una ficha, paso a paso - Ármala con el tema y la plantilla: responde 202 con su id en recibido. - Consulta su estado cada 10 a 15 s hasta que sea publicado o fallido. Suele tardar unos minutos. - Lee lo que encontró en markdown: el valor, la página y la cita de cada campo, y la traza de la búsqueda. - Ya está en los documentos del espacio: Bentho la usa para responder desde ese momento. Busca por vueltas: cada una busca solo los campos que siguen vacíos, y para cuando los llena todos o llega a sus topes (vueltas, páginas o tiempo). Un dato sin página que lo respalde no se inventa: el campo queda «sin respaldo en las páginas leídas». Para comprobar un dato, pide el texto de la página de la que sale con página leída: es el que leyó la ficha, no la página de hoy. ## El cierre externo Con como.cierre_externo: true, al terminar las vueltas una búsqueda externa intenta llenar los campos que siguen vacíos: - Si trae una página de los dominios permitidos y esa página respalda el dato, Bentho la lee y lo cita como cualquier otro. - Si no, el campo se llena igual, pero sale marcado «completado por búsqueda externa, sin página que lo respalde». Revísalo antes de darlo por bueno. - Gasta créditos a tarifa externa, aparte de lo que gasta la ficha. Si la búsqueda externa no responde, la ficha se arma sin ella. ## La plantilla Markdown: ## abre una sección y cada línea - Nombre: qué buscar es un campo. La descripción guía la búsqueda: di la unidad o el formato que esperas. El # del título se ignora; el tema llega aparte. PLANTILLACOPIAR ``` # Suelo ## Físico - Textura: proporción de arena, limo y arcilla - Densidad aparente: cifra y unidad ## Químico - pH: valor y método de medida ``` Una plantilla sin ninguna sección con campos da 422 La plantilla no tiene secciones con campos. ## Qué se publica, y dónde - La ficha se publica en los documentos del espacio como ficha-.md, cortada en trozos que se entienden solos. Los campos vacíos no se publican. - Armar otra ficha con el mismo tema la reemplaza: queda una versión nueva del mismo documento. - Una fuente publica lo que lee cada vez que la lanzas. Borrarla quita de los documentos lo que publicó. Lo que trajo cada corrida queda en sus capturas. - Si ningún campo se llenó, la ficha queda publicado pero no publica nada: traza.publicacion.status es sin_datos. - publicado quiere decir armada. Si su paso a los documentos falla, traza.publicacion.status queda en fallida y se reintenta sola; con rechazada no se publicará, y conviene armarla otra vez. ## Para que salga más completa - Aporta 2 a 4 urls concretas del caso: suben cuántos campos se llenan. Si la ficha llena menos de la mitad sin ellas, traza.sugerencia te lo recuerda. - Si hay casos parecidos que se mezclan (dos fincas, dos productos), enciende como.coherencia. - Con como.dominios lee solo de los sitios que confías; con como.instrucciones, dile qué priorizar. ## Créditos - Armar una ficha y lanzar una fuente gastan créditos de la bolsa de tu cuenta. Lo que gasta cada una depende de cuánto tenga que buscar y leer; el cierre externo, si lo pides, se cobra aparte a tarifa externa. - Con la bolsa agotada, las rutas que crean o lanzan responden 402 (bolsa_agotada). Leer y borrar siguen funcionando. - El saldo, en la bolsa de créditos. ## Rutas | | MÉTODO | RUTA | PARA QUÉ | PERMISO | FICHAS | POST | /fichas | Arma una ficha · gasta créditos | escribe | GET | /fichas/{ficha} | El estado de una ficha | lee | GET | /fichas/{ficha}/markdown | La ficha armada y cómo se armó | lee | GET | /fichas/{ficha}/pagina | El texto de una página que leyó la ficha | lee | GET | /fichas | Las fichas del espacio | lee | FUENTES | POST | /fuentes | Crea una fuente | escribe | POST | /fuentes/{fuente}/lanzar | Lanza una fuente · gasta créditos | escribe | GET | /fuentes/{fuente} | Una fuente y su última corrida | lee | GET | /fuentes/{fuente}/capturas | Las capturas de una fuente | lee | GET | /fuentes/{fuente}/capturas/{captura} | Una captura de una fuente | lee | GET | /fuentes | Las fuentes del espacio | lee | POST | /fuentes/{fuente}/cancelar | Cancela la corrida de una fuente | escribe | DEL | /fuentes/{fuente} | Borra una fuente | escribe | CONSUMO | GET | /consumo | Lo que gastó Fuentes, en créditos | lee --- # Transcripción: cómo funciona https://dev.bentho.ai/api/transcripcion/ MÓDULO TRANSCRIPCIÓN # Transcripción: cómo funciona Transcripción convierte notas de voz y audios en texto. Le mandas el archivo y te devuelve lo que se dice, en la misma petición. Sirve para que tu bot, tu CRM o tu bandeja de mensajes traten los audios de tus clientes como si los hubieran escrito: transcribes la nota de voz y sigues con el texto. Si tu espacio tiene Kommo, no hace falta llamarlo: las notas de voz que llegan por el chat se transcriben solas cuando enciendes audio_activo en su configuración. En la consola, el módulo aparece en el Estudio del espacio, sin nada que configurar. ## Antes de empezar - El espacio tiene que tener el módulo Transcripción: transcripcion sale en sus módulos en GET /api/companies. Si no, la ruta responde 403 módulo no habilitado para este espacio. - Transcribir es un POST: pide una clave escribe, aunque no cambia nada del espacio. Con una de solo lectura, 403. - La ruta cuelga de /api/mod/transcripcion/{espacio}/, con la misma clave y los mismos límites que el resto de la API. - No gasta créditos. ## Paso a paso - Descarga el audio de tu canal (WhatsApp, tu app, tu centralita). - Súbelo en multipart/form-data, en el campo archivo, y si no es español, con su idioma. - Si responde 200 con vacio: false, usa texto. Si vacio es true, el audio no tiene palabras: díselo a tu usuario, sin inventar lo que dijo. - Si no, mira el codigo del error (abajo): unos piden otro audio y otros, solo reintentar. ## Formatos y límites - OGG/Opus (las notas de voz de WhatsApp), MP3, M4A, WAV y otros formatos de audio. De un vídeo se transcribe el sonido. - Hasta 25 MB y 120 segundos por archivo. Un audio por petición. - Con duracion_max_s fijas un tope más bajo para tu caso: lo que pase de él da 422 y no se transcribe a medias. - El idioma por defecto es el español (es). Para otro, mándalo en idioma. - Bentho no guarda ni el audio ni el texto. ## Cuando no se puede Los errores del audio traen detail como objeto, con un codigo fijo y una frase en detalle. Decide por el código: | | CÓDIGO | STATUS | QUÉ HACER | demasiado_grande | 413 | Pasa de 25 MB. Recórtalo o comprímelo. | demasiado_largo | 422 | Dura más de la cuenta. Pide un audio más corto o que lo escriba. | no_soportado | 422 | No es audio o no tiene sonido. No reintentes. | audio_invalido | 422 | Vacío o dañado. No reintentes con el mismo archivo. | ocupado | 503 | Hay muchas transcripciones en curso. Reintenta en unos segundos. | motor_no_listo | 503 | El servicio está arrancando. Reintenta en un momento. ## Rutas | | MÉTODO | RUTA | PARA QUÉ | PERMISO | POST | /transcripciones | Transcribe un audio | escribe --- # Agente ventas: cómo funciona https://dev.bentho.ai/api/agente/ MÓDULO AGENTE VENTAS # Agente ventas: cómo funciona Agente ventas atiende y vende por chat. Entiende qué pide cada cliente, resuelve sus dudas con los documentos del espacio, mira el stock, le cotiza el envío y los descuentos, arma el pedido y, si lo conectas al cobro, crea la orden y recibe el comprobante de pago. Ninguna cifra la inventa: precios, envíos y totales salen de tu catálogo, tus tarifas o tus reglas. Si no puede cotizar algo, lo dice y avisa de que conviene pasar la conversación a una persona. Por la API lo conectas a tu canal (WhatsApp, el chat de tu web, tu CRM): mandas cada mensaje del cliente y recibes la respuesta, en qué punto va la venta, la cotización y si hace falta alguien de tu equipo. También lees tus leads, ajustas lo que dice el agente y mantienes sus tarifas de envío y su inventario. También puedes hacerlo desde la consola: en el Estudio de un espacio con el módulo activo, el panel Agente ventas edita las frases del agente (su guion) y enseña el embudo de leads. Sustituye a Ventas y a Comprensión desde el 2026-10-13. /api/mod/ventas/… y /api/mod/comprension/… se retiraron. Si tu integración llamaba a Ventas, cambia el prefijo por /api/mod/agente/…: el cuerpo y la respuesta son los mismos. Ver Ventas y Comprensión. ## Antes de empezar - El espacio tiene que tener el módulo Agente ventas: agente sale en sus módulos en GET /api/companies. Si no, cada ruta responde 403 módulo no habilitado para este espacio. - Conversar y enviar comprobantes vale con cualquier clave, también una lee. Cambiar la configuración, mover leads, reiniciar conversaciones, cotizar a mano y tocar tarifas o inventario pide una clave escribe. Leer vale con cualquiera. - Las rutas cuelgan de /api/mod/agente/{espacio}/, con la misma clave y los mismos límites que el resto de la API. - Copia cada ruta tal cual, con su barra final si la lleva (conversations/, leads/, inventory/). Sin ella la respuesta es un 307 vacío. - Conversar gasta créditos de la bolsa de tu cuenta: ver créditos. ## Una conversación, paso a paso - Elige un session_id por chat (el número del cliente en tu canal, el id del hilo) y úsalo en todos sus mensajes. Con él, el agente recuerda el carrito, la ciudad, el método de pago y el pedido abierto. - Manda cada mensaje a conversar con question, session_id y el msg_id de tu canal. Si tu webhook reintenta, el mismo msg_id devuelve la misma respuesta sin repetir el turno. - Contesta a tu cliente con responses (uno o dos mensajes, en orden). En el primer turno llega además aviso_privacidad: enséñaselo. Si viene opciones, puedes pintarlas como botones. - Pásale consent: true cuando tu cliente autorice el seguimiento comercial: solo así se guarda su contacto. - Si llega handoff: true, pasa la conversación a alguien de tu equipo (ver abajo). - Si el cliente manda una imagen o un PDF (el comprobante de pago), pásalo a comprobante con el mismo session_id. - Para empezar de cero (un botón «nueva conversación», una prueba), reinicia la sesión. Sin eso, una sesión olvida su estado tras unas 48 h sin mensajes. Si prefieres eventos, conversar por SSE recibe el mismo cuerpo. El mensaje no llega palabra a palabra: el agente comprueba cada cifra antes de enviarla y manda el texto entero. En un espacio con Agente ventas, la ruta del núcleo POST conversations también llega a este agente, con question, session_id y consent. La del módulo añade msg_id. ## Qué entiende de cada mensaje En el mismo turno, antes de contestar, el agente interpreta lo que pide el mensaje: un precio, sumar o quitar del carrito, cotizar el envío a una ciudad, pagar, el estado de un pedido, hablar con una persona. Entiende «ponme el grande», «uno más de ese» o un «sí» a lo que acaba de preguntar, porque tiene a mano el carrito y la conversación. Si dos productos encajan y el mensaje no dice cuál, pregunta en vez de apostar. No hay una ruta aparte para esto ni nada que configurar. Antes lo hacía el módulo Comprensión, que se retiró: si lo usabas con tu propio bot, manda el mensaje a conversar y deja que el agente lo atienda entero. ## Estados de la conversación Cada respuesta trae su estado. Lo decide el código, no el texto: puedes apoyarte en él para tus métricas o tu CRM. ESTADOSCOPIAR ``` saludo → descubrimiento → rag_producto ↘ recoleccion_slots → cotizacion → cierre cualquiera → handoff (conviene que siga una persona) ``` | | ESTADO | QUÉ PASA | saludo | Primer contacto. | descubrimiento | Charla o preguntas generales, sin intención de compra clara. | rag_producto | Respondió sobre un producto con lo que dicen tus documentos. | recoleccion_slots | Le faltan datos para seguir (producto, cantidad, ciudad, método de pago, datos de envío) y los está pidiendo. | cotizacion | Acaba de cotizar: las cifras están en cotizacion. | cierre | Confirma el pedido, pide el pago o sigue un pedido abierto. | handoff | Conviene que siga una persona. status_code es aparte: "1000" respondió; "1001" no tenía información para eso y contestó con tu refusal_fuera_dominio. ## Cuándo pasa a una persona El agente no improvisa lo que no sabe. Marca handoff: true cuando no hay tarifa para ese destino o ese producto, cuando un producto no tiene precio, cuando el cliente pide hablar con alguien, en las gestiones de posventa (estado de un pedido, devoluciones, garantías, quejas) o cuando falla el cobro. El mensaje para el cliente ya lo dice, con tu mensaje_handoff o la frase de soporte que toque. Lo que te toca: avisar a tu equipo y, si tu canal lo permite, pausar las respuestas automáticas en esa conversación. Por SSE, el evento handoff trae además un reason corto para tus registros. ## El agente: su configuración GET leads-config te da todo lo que define al agente y PATCH leads-config cambia solo lo que mandes. Los cambios valen desde el siguiente mensaje. - Quién es: name, saludo, modo_venta (atención o venta consultiva) y sus instrucciones (system_prompt, system_prompt_venta). - Con qué se conecta: tus documentos (cerebro_activo), Comercial para envíos, recargos, beneficios y métodos de pago (comercial_activo), Marca para la voz (marca_activa), el cobro con Facturación (facturacion_activo) y el stock de Productos (inventario_activo, inventario_delegado). - Cómo vende: carrito de varios productos (carrito_activo), botones para elegir (whatsapp_botones_combo_activo), aviso de stock bajo, alternativas a lo agotado, el enlace a tu catálogo (catalogo_url). ## Las frases del agente Todo lo que el agente dice sin redactar lo escribes tú: los campos mensaje_*, msg_*, cta_* y pregunta_*. Las cifras nunca van en la frase: entran por sus variables, con el valor que calcula el agente. FRASES CON VARIABLESCOPIAR ``` saludo ¡Hola! Soy el asistente de {name}. … mensaje_precio El precio de {nombre} es {precio}. ✨ mensaje_carrito_agregado ¡Anotado! Llevas: {resumen}. ¿Sumamos algo más o vamos con el pago? 🛒 mensaje_cotizacion_envio El envío a {destino} cuesta {envio}: tu pedido quedaría en {total} … mensaje_checkout ¡Perfecto! El total de tu pedido es {total}. … {cuentas} … vence {expira}. mensaje_catalogo Aquí puedes ver todo nuestro catálogo: {url} ``` - Usa solo las variables que la frase trae por defecto (míralas en GET). Una llave sin cerrar o una variable que la frase no conoce puede dejarla sin enviar o con la llave a la vista. - Si una frase termina en pregunta, que siga terminando en pregunta: el agente decide el siguiente paso según eso. Las preguntas de cierre («¿vamos con el pago?») son las que tu cliente suele copiar al responder; cámbialas con cuidado. - GET trae los ajustes que puedes cambiar. Los del funcionamiento interno del agente los gestiona Bentho: no salen, y mandarlos en el PATCH da 403. ## Leads y su embudo El agente registra un lead cuando un cliente cotiza o compra. Su lead_id llega en la respuesta del turno que lo registró: guárdalo junto a tu conversación. ESTADOS DE UN LEADCOPIAR ``` nuevo → contactado → ganado | cerrado ganado lo pone el agente solo al confirmarse el pago del pedido ``` - Lista los leads (por estado si quieres) y muévelos cuando tu equipo los contacte o los cierre. - El contacto nunca sale en claro: contacto_masked dice qué datos dejó (nombre=; telefono=), y solo si dio su consentimiento. Para hablar con él, usa tu conversación. ## Tarifas de envío Sin el módulo Comercial, el envío se cotiza con tus tablas de tarifas. Una tabla es una versión: entra como borrador, la revisas y la activas. Solo la versión validada cotiza, y solo hay una. UNA TABLA, DE PRINCIPIO A FINCOPIAR ``` subir (draft) → revisar filas → corregir o borrar → recargos → activar (validada) activar otra → la anterior pasa a retirada ``` - Sube la tabla en CSV, XLSX o PDF. Guarda su version_id. - Revísala fila por fila. Las filas con tarifa 0 o sin días son las que no se pudieron leer: corrígelas o bórralas. - Si hace falta, añade recargos (un seguro, el recaudo contra entrega). - Actívala. Desde ese momento el agente cotiza con ella; puedes comprobarlo con cotizar. La primera fila del archivo son los encabezados. Reconoce, sin importar mayúsculas ni tildes: | | COLUMNA | ENCABEZADOS QUE ACEPTA | Zona (obligatoria) | zona, ciudad, destino, departamento, región, ubicación | Tarifa (obligatoria) | tarifa, flete, envío, valor, precio, costo, coste | Días | días, tiempo, entrega, plazo. 4, 4-6 o 4 a 6 días. | Producto | producto, artículo, item, sku. Sin él, la fila vale para todos. | Cantidad | cantidad, rango, unidades. 1-10, 11+ o 5. Los importes se leen en formato colombiano: $12.500 es doce mil quinientos y 1.200,50 lleva decimales. Un destino que coincide con varias zonas no se adivina: el agente pregunta cuál. ## Inventario Con inventario_activo, el agente mira el stock antes de ofrecer: avisa de lo agotado, propone alternativas y puede decir que quedan pocas unidades. El stock puede vivir en dos sitios: - En el módulo Productos (inventario_delegado: true): lo llevas allí. Aquí, disponibilidad y registrar venta le preguntan a Productos. - En el inventario propio del agente: lo cargas con un archivo o artículo por artículo, sin borrador: entra directo. Cada cambio de cantidad queda como movimiento. El archivo de inventario necesita una columna de cantidad (cantidad, stock, existencias, disponible, unidades, saldo) y otra de nombre (producto, artículo, descripción, nombre) o de SKU (sku, referencia, código, EAN). Opcionales: unidad, bodega (almacén, ubicación) y umbral (mínimo, punto de reorden), que marca desde dónde hay stock bajo. Las ventas que cierra el agente descuentan solas al confirmarse el pago. Las que haces fuera del chat, regístralas con registrar venta y una sale_ref: un reintento con la misma referencia no descuenta dos veces. ## Créditos - Conversar, por JSON o por SSE, y enviar comprobantes gastan créditos de la bolsa de tu cuenta. Lo que gasta cada mensaje depende de lo que tenga que hacer el agente. - Con la bolsa agotada, esas rutas responden 402 (bolsa_agotada). La configuración, los leads, las tarifas y el inventario siguen funcionando. - Un mensaje repetido con el mismo msg_id no vuelve a gastar. - Lo que gastó el agente en el espacio, en consumo; el saldo de la cuenta, en la bolsa de créditos. - Entender cada mensaje va dentro del turno: no se cobra aparte. ## Ventas y Comprensión Hasta el 2026-10-13 la conversación eran dos módulos: Ventas (ventas), que atendía y cerraba la venta, y Comprensión (comprension), que interpretaba cada mensaje. Agente ventas hace las dos cosas en un solo turno y los sustituye: - ventas y comprension ya no salen en el catálogo ni se pueden activar. Los espacios que tenían Ventas pasaron a agente con su configuración, sus leads y sus conexiones. - /api/mod/ventas/… y /api/mod/comprension/… responden 404 módulo desconocido. - Cada ruta de Ventas tiene aquí su igual, con el mismo cuerpo y la misma respuesta: cambia /api/mod/ventas/ por /api/mod/agente/ y nada más. - Las de Comprensión no tienen sustituta: el agente entiende cada mensaje por dentro (ver qué entiende). ## Rutas | | MÉTODO | RUTA | PARA QUÉ | PERMISO | CONVERSAR | POST | /conversations/ | Conversa con un cliente · gasta créditos | lee | SSE | /conversations/stream/ | La misma conversación, por eventos · gasta créditos | lee | POST | /conversations/evidence | Recibe una imagen del cliente · gasta créditos | lee | DEL | /sessions/{sesion} | Reinicia una conversación | escribe | AGENTE | GET | /leads-config | La configuración del agente | lee | PATCH | /leads-config | Cambia la configuración del agente | escribe | LEADS | GET | /leads/ | Los leads del espacio | lee | GET | /leads/{lead} | Un lead | lee | PATCH | /leads/{lead} | Mueve un lead en el embudo | escribe | TARIFAS DE ENVÍO | POST | /pricing/tables | Sube una tabla de tarifas de envío | escribe | GET | /pricing/versions | Las versiones de la tabla de tarifas | lee | GET | /pricing/drafts/{tabla} | Una versión, fila por fila | lee | PATCH | /pricing/drafts/{tabla}/rates/{tarifa} | Corrige una fila del borrador | escribe | DEL | /pricing/drafts/{tabla}/rates/{tarifa} | Borra una fila del borrador | escribe | POST | /pricing/drafts/{tabla}/surcharges | Añade un recargo | escribe | POST | /pricing/versions/{tabla}/activate | Valida y activa una versión | escribe | POST | /pricing/versions/{tabla}/retire | Retira una versión | escribe | POST | /pricing/quote | Cotiza un envío | escribe | INVENTARIO | POST | /inventory/upload | Carga el inventario desde un archivo | escribe | GET | /inventory/ | El inventario del espacio | lee | GET | /inventory/summary | El resumen del inventario | lee | GET | /inventory/low-stock | Lo que está por agotarse | lee | GET | /inventory/export | Descarga el inventario en CSV | lee | GET | /inventory/availability | ¿Hay stock para este pedido? | lee | GET | /inventory/items/{sku} | Un artículo del inventario | lee | POST | /inventory/items | Crea o fija un artículo | escribe | PATCH | /inventory/items/{sku} | Ajusta la cantidad de un artículo | escribe | DEL | /inventory/items/{sku} | Borra un artículo | escribe | POST | /inventory/sale | Registra una venta confirmada | escribe | CONSUMO | GET | /consumo | Lo que gastó el agente, en créditos | lee --- # Marca: cómo funciona https://dev.bentho.ai/api/marca/ MÓDULO MARCA # Marca: cómo funciona Marca le da a tu agente de ventas la personalidad de tu marca: quién es, cómo habla y cómo lleva la conversación hacia una compra. No guarda precios ni datos del catálogo: decide cómo se dicen las cosas, no qué se dice. Por API la lees y la cambias desde tus propias herramientas, por ejemplo para ajustar el tono de una campaña o comprobar cómo queda descrito el agente antes de encenderlo. También puedes hacerlo desde la consola: en el Estudio de un espacio con el módulo activo, el panel Marca tiene los apartados «Quién es tu agente», «Cómo habla», «Cómo lleva la conversación a una compra» y «Así queda descrito tu agente». ## Antes de empezar - El espacio tiene que tener el módulo Marca: marca sale en sus módulos en GET /api/companies. Si no, cada ruta responde 403 módulo no habilitado para este espacio. - Cambiar la marca pide una clave escribe. Leerla vale con cualquier clave. - Las rutas cuelgan de /api/mod/marca/{espacio}/, con la misma clave y los mismos límites que el resto de la API. - No gasta créditos: ninguna ruta del módulo descuenta de tu bolsa. - La marca se nota en las conversaciones del Agente ventas: el espacio necesita ese módulo con la personalidad de marca encendida (paso 4). ## Paso a paso - Lee la marca. Si nunca se configuró, vienen los valores por defecto y name es el id del espacio. - Cámbiala: el nombre de tu marca y, enteros, los apartados que quieras tocar. - Mira cómo queda descrito tu agente: es el texto que el agente lee antes de contestar. - En Agente ventas, enciende Personalidad de marca desde el Estudio, o manda {"marca_activa": true} a PATCH /api/mod/agente/{espacio}/leads-config. Viene apagada. ## Cómo cambia las respuestas Con la marca encendida en Agente ventas y activo: true aquí, en cada mensaje el agente cambia su descripción propia por la de tu marca. Desde ahí: - Se presenta y habla como tu marca: con su nombre y su rol, en el tono que elegiste, con tus rasgos, el largo de max_lineas, en texto plano si lo pides y con tus emojis. - Las cifras no cambian de origen: precios, totales, envíos y existencias siguen saliendo del catálogo y se comprueban antes de enviarse. La descripción termina siempre con unas reglas fijas que ninguna marca puede quitar: no inventar precios, totales, plazos, descuentos ni disponibilidad, no hablar de procesos internos y no ofrecer un asesor humano. - Sugiere el combo si upsell_activo está encendido: cuando responde del catálogo sobre un producto que casa con una clave de upsell_combos, añade la frase de upsell_plantilla. No lo hace mientras cotiza, si el producto no tiene existencias, si la respuesta ya termina en pregunta o ya nombra el combo. Cada combo se sugiere una vez por conversación, y entre una sugerencia y otra deja pasar unos mensajes (lo ajustas en Agente ventas). - Cierra con una pregunta si pregunta_cierre_obligatoria está encendida: si la respuesta no termina en pregunta, le añade una de preguntas_cierre, turnándolas. No lo hace al despedirse ni cuando la conversación pasa a una persona, y se salta las que piden la ciudad si ya la sabe. CÓMO SE ARMA LA SUGERENCIA DEL COMBOCOPIAR ``` upsell_combos { "cafe de origen": "Combo Barista en Casa" } upsell_argumento "con la prensa francesa la taza sale mejor" upsell_plantilla "Si buscas un mejor resultado, te recomiendo el {combo}: {argumento}. ¿Te lo cotizo?" El cliente pregunta por «Café de origen 500 g» → al final de la respuesta se añade: Si buscas un mejor resultado, te recomiendo el Combo Barista en Casa: con la prensa francesa la taza sale mejor. ¿Te lo cotizo? ``` Con activo: false, el agente atiende con su descripción propia y sin tus tácticas. Si Marca no responde, usa la última marca que tenía o, si no tiene ninguna, la suya propia: la marca nunca deja una conversación sin respuesta. Un cambio tarda poco en llegar a las conversaciones, hasta un minuto. ## Los tres apartados | | APARTADO | QUÉ DECIDE | identidad | Quién es: nombre_agente, rol y linea. Con name, arma la primera frase: «Eres Lía, asesora de café de especialidad de Café La Loma». | estilo | Cómo habla: tono, rasgos, emojis_permitidos, emojis_obligatorios, max_lineas y solo_texto_plano. | tacticas | Cómo lleva a la compra: el combo que sugiere y la pregunta con la que cierra. Sin cifras: el argumento y las preguntas son texto tuyo. Cada apartado se manda entero. Un PATCH con estilo reemplaza todo el estilo: lo que no vaya dentro vuelve a su valor por defecto. Lee la marca, cambia el dato y manda el apartado completo. Lo que no mandes (otro apartado, name, activo) se queda como estaba. CAMBIAR SOLO EL TONOCOPIAR ``` GET …/config → "estilo": { "tono": [...], "rasgos": [...], … } PATCH …/config { "estilo": { "tono": ["elegante", "directa"], "rasgos": [...], "emojis_permitidos": [...], "emojis_obligatorios": [], "max_lineas": 4, "solo_texto_plano": true } } ``` ## Rutas | | MÉTODO | RUTA | PARA QUÉ | PERMISO | GET | /config | La marca de tu agente | lee | PATCH | /config | Cambia la marca de tu agente | escribe | GET | /persona | Cómo queda descrito tu agente | lee --- # Productos: cómo funciona https://dev.bentho.ai/api/productos/ MÓDULO PRODUCTOS # Productos: cómo funciona Productos es el catálogo de tu espacio: qué vendes, a qué precio, con qué promociones y cuánto stock queda. Bentho consulta aquí esas cifras cuando atiende a tus clientes, en vez de inventarlas. Por API mantienes el catálogo al día desde tu propio sistema, cotizas un carrito con sus descuentos, apartas stock mientras tu cliente paga y confirmas la venta para que las existencias bajen. También puedes hacerlo desde la consola: en el Estudio de un espacio con el módulo activo, abre Productos. El panel permite crear productos, fijar su precio y su stock, cambiar su estado, importar un catálogo y gestionar listas de precios y promociones. ## Antes de empezar - El espacio tiene que tener el módulo Productos: productos sale en sus módulos en GET /api/companies. Si no, cada ruta responde 403 módulo no habilitado para este espacio. - Leer vale con cualquier clave. Todo lo que no es GET pide una clave escribe, también cotizar, que no cambia nada pero es un POST. - Las rutas cuelgan de /api/mod/productos/{espacio}/, con la misma clave y los mismos límites que el resto de la API. Si el módulo no está disponible en ese momento, responde 502: reintenta en unos segundos. - products/, product-types/ y catalog/imports/ llevan la barra final. Sin ella no responden con los datos. - Productos no gasta créditos: ninguna de sus rutas descuenta de la bolsa. ## Del alta a la venta, paso a paso - Crea el producto (o importa tu catálogo de un archivo). Nace en borrador, con su variante única. - Ponlo a la venta con {"estado": "activo"}: solo los activos salen en la búsqueda. - Crea una lista de precios y fija el precio de cada variante en ella. - Fija su stock, o súbelo de un archivo. - Cuando un cliente compra: cotiza el carrito, reserva lo que lleva mientras paga y, al cobrar, confirma la venta con las promociones que se aplicaron. UNA COMPRACOPIAR ``` POST …/price-quote → total, adjustments, catalog_version POST …/inventory/reservations → id de la reserva (15 min por defecto) POST …/inventory/sale { lines: [{ variant_id, cantidad, reservation_id }], sale_ref, promociones_aplicadas } ``` ## Productos, variantes y tipos - Un producto es lo que ves en la carta: «Café molido La Loma 500 g». Tiene nombre, descripción, imágenes (URL), atributos y un estado. - Una variante es lo que se vende: tiene su SKU, su precio y su stock. Un producto sin opciones tiene una sola; uno con opciones («Molienda»: en grano o molido) tiene una por combinación, que añades con Crea una variante. - Un tipo de producto («Café», «Repuesto», «Plan») dice qué atributos llevan sus productos y cuáles son obligatorios. Es opcional: sin tipo, los atributos son libres. ESTADOS DE UN PRODUCTOCOPIAR ``` borrador → activo → archivado (cualquiera se cambia con PATCH products/{producto}) borrador recién creado; no sale en la búsqueda activo a la venta; sale en la búsqueda archivado retirado; se conserva, no sale en la búsqueda ``` ## Importar desde un archivo Sube un CSV, un Excel (.xlsx) o un PDF con una tabla a importar. Queda en draft: revisa sus filas en la importación, descarta las que sobren y actívala. Hasta entonces el catálogo no cambia. | | CAMPO | COLUMNAS QUE ENTIENDE | nombre | producto, nombre, artículo, ítem | sku | sku, referencia, ref, código, cod, código de barras, ean | tipo | tipo, categoría, tipo de producto | descripcion | descripción, detalle | precio | precio, valor, precio unitario, pvp | atributos | Cualquier otra columna, con su encabezado como nombre. - Hace falta una columna de nombre o una de SKU. Los encabezados no distinguen tildes ni mayúsculas, y el CSV puede ir separado por comas o por punto y coma. - Los precios se leen en formato colombiano: $45.000 es 45000 y 12,5 es 12.5. - Cada fila activada crea un producto nuevo y activo. Activar dos veces el mismo archivo duplica el catálogo. - El precio del archivo no se cobra: queda en metadata.precio_sugerido para que lo pases a una lista con Fija un precio. - Un .xls antiguo no se lee: guárdalo como .xlsx o como CSV. Hasta 25 MB por archivo. ## Cómo se elige un precio Los precios viven en listas. Una variante puede tener precio en varias (la de la tienda, una oferta de temporada, uno puntual) y en varios escalones de cantidad. Al cotizar, entre los precios que valen para esa línea —lista activa y vigente en la fecha, misma moneda, min_qty no mayor que la cantidad— gana: - El de la lista con más prioridad. - A igual prioridad, por tipo de lista: override, luego oferta, luego base. - Luego el escalón más alto que aplica (min_qty 12 antes que 1 si compran 12). - Luego el monto más bajo. - Para cambiar un precio, fíjalo otra vez con la misma lista, variante, moneda y min_qty: reemplaza al anterior. - Una variante sin precio vigente no se cobra a 0: sale en no_cotizados. - Cada cotización trae catalog_version, una huella de los precios, promociones y catálogo con que se calculó. Guárdala con el pedido. - Importes y cantidades viajan como texto decimal: «32000», «1.5». ## Promociones Una promoción es una regla: cuándo aplica (predicado) y cuánto descuenta (metodo). Se aplica sola en cada cotización que la cumple; cada descuento sale en el adjustments de su línea. 10 % CON CÓDIGO, DESDE 50 000COPIAR ``` { "nombre": "10 % de bienvenida", "codigo": "BIENVENIDA", "predicado": { "op": "and", "clauses": [ { "field": "codigos_promo", "cmp": "contains", "value": "BIENVENIDA" }, { "field": "subtotal", "cmp": "gte", "value": 50000 } ] }, "metodo": { "metodo": "percentage", "target": "order", "valor": 10 } } ``` - El codigo es solo una etiqueta. Para que la promoción pida el código, ponlo en el predicado como arriba, y manda en la cotización los codigos_promo que escribió tu cliente. - El predicado mira subtotal, cantidad_total, variant_ids, codigos_promo y canal. Vacío, aplica siempre. - target: "item" rebaja líneas; si el predicado nombra variantes con variant_ids contains, solo esas. target: "order" rebaja el subtotal y se aplica después de las de línea. - Las de un mismo grupo_exclusividad no se suman: aplica la de mayor prioridad. Las que no tienen grupo se suman todas. - limite_usos y el presupuesto de una campaña solo avanzan cuando confirmas una venta declarando promociones_aplicadas. Cotizar no gasta usos. - Una promoción no se edita ni se pausa por la API: para cambiarla o pararla, bórrala y, si hace falta, créala de nuevo. ## Stock, reservas y ventas - El stock es por variante y por bodega (texto vacío: la única). Disponible es stocked − reserved, nunca menos de 0. - Fijar pone las existencias en absoluto; ajustar suma o resta. Subir un archivo o sincronizar con tu software contable fija, cruzando por SKU. El archivo necesita una columna de cantidad (cantidad, stock, existencias, disponible, inventario, unidades, saldo, qty) y una de SKU; admite también unidad, bodega y umbral (mínimo, reorden). - Una reserva aparta unidades durante ttl_s segundos (por defecto 15 minutos). Al vencer, vuelven solas. - Confirmar la venta descuenta todo de una vez. Pasa el reservation_id en la línea si la reservaste y un sale_ref por pedido: repetir la llamada no descuenta dos veces. - La disponibilidad responde no_rastreado si la variante no tiene stock cargado: no es un «no hay». Si tu negocio no controla stock, apágalo en la configuración. - Los cambios de precios, promociones y stock los usa Bentho en sus respuestas siguientes. ESTADOS DE UNA RESERVACOPIAR ``` activa → liberada (DELETE inventory/reservations/{reserva}) → consumida (venta con su reservation_id) → expirada (venció ttl_s; el stock vuelve solo) ``` ## Rutas | | MÉTODO | RUTA | PARA QUÉ | PERMISO | CATÁLOGO | GET | /products/ | Los productos del espacio | lee | POST | /products/ | Crea un producto | escribe | GET | /products/{producto} | Un producto | lee | PATCH | /products/{producto} | Edita un producto | escribe | DEL | /products/{producto} | Borra un producto | escribe | POST | /products/{producto}/variants | Crea una variante | escribe | PATCH | /variants/{variante} | Edita una variante | escribe | DEL | /variants/{variante} | Borra una variante | escribe | GET | /product-types/ | Los tipos de producto | lee | POST | /product-types/ | Crea un tipo de producto | escribe | GET | /product-types/{tipo_producto} | Un tipo de producto | lee | DEL | /product-types/{tipo_producto} | Borra un tipo de producto | escribe | GET | /catalog/search | Busca en el catálogo | lee | GET | /catalog/context | Las fichas de unas variantes | lee | GET | /catalog/bundle-components | Los componentes de un combo | lee | IMPORTAR | POST | /catalog/import | Importa un catálogo desde un archivo | escribe | GET | /catalog/imports/ | Las importaciones | lee | GET | /catalog/imports/{importacion} | Una importación y sus filas | lee | DEL | /catalog/imports/{importacion}/rows/{fila} | Descarta una fila | escribe | POST | /catalog/imports/{importacion}/activate | Activa una importación | escribe | PRECIOS | GET | /price-lists | Las listas de precios | lee | POST | /price-lists | Crea una lista de precios | escribe | GET | /price-lists/{lista} | Una lista de precios | lee | PATCH | /price-lists/{lista} | Edita una lista de precios | escribe | DEL | /price-lists/{lista} | Borra una lista de precios | escribe | POST | /prices | Fija un precio | escribe | GET | /prices | Los precios | lee | DEL | /prices/{precio} | Borra un precio | escribe | POST | /price-quote | Cotiza un carrito | escribe | PROMOCIONES | GET | /promotions | Las promociones | lee | POST | /promotions | Crea una promoción | escribe | GET | /promotions/{promocion} | Una promoción | lee | DEL | /promotions/{promocion} | Borra una promoción | escribe | POST | /campaigns | Crea una campaña | escribe | STOCK | GET | /inventory/availability | La disponibilidad de una variante | lee | GET | /inventory/levels | El stock del espacio | lee | GET | /inventory/items/{variante} | El stock de una variante | lee | POST | /inventory/items | Fija el stock de una variante | escribe | PATCH | /inventory/items/{variante} | Ajusta el stock de una variante | escribe | DEL | /inventory/items/{variante} | Borra el stock de una variante | escribe | GET | /inventory/low-stock | El stock bajo | lee | GET | /inventory/summary | El resumen del stock | lee | GET | /inventory/suggest | Alternativas con stock | lee | GET | /inventory/export | Exporta el stock en CSV | lee | POST | /inventory/upload | Sube el stock desde un archivo | escribe | POST | /inventory/sync | Trae el stock de tu software contable | escribe | RESERVAS Y VENTAS | POST | /inventory/reservations | Reserva stock | escribe | GET | /inventory/reservations | Las reservas activas | lee | DEL | /inventory/reservations/{reserva} | Libera una reserva | escribe | POST | /inventory/sale | Confirma una venta | escribe | CONFIGURACIÓN | GET | /config | La configuración del módulo | lee | PATCH | /config | Cambia la configuración | escribe --- # Comercial: cómo funciona https://dev.bentho.ai/api/comercial/ MÓDULO COMERCIAL # Comercial: cómo funciona Comercial pone las reglas de dinero de tu tienda que no son el precio del producto: cuánto cuesta el envío a cada ciudad según el peso y el método de pago (contraentrega, transferencia o mixto), los recargos de la contraentrega y los beneficios, como un descuento de bienvenida o el envío gratis. Es una calculadora: no guarda pedidos ni datos de clientes. Por API lo usas para cargar la tabla de tu transportadora, cotizar el envío de un pedido y saber qué beneficios aplican antes de crear la orden en Facturación. También puedes hacerlo desde la consola: en el Estudio de un espacio con el módulo activo, el panel Comercial tiene las reglas de pago, los beneficios con su interruptor, la tabla de tarifas activa y el peso de cada producto. ## Antes de empezar - El espacio tiene que tener el módulo Comercial: comercial sale en sus módulos en GET /api/companies. Si no, cada ruta responde 403 módulo no habilitado para este espacio. - Leer (las rutas GET) vale con cualquier clave. Todo lo demás pide una clave escribe, también cotizar y evaluar beneficios: no cambian nada, pero son POST. - Las rutas cuelgan de /api/mod/comercial/{espacio}/, con la misma clave y los mismos límites que el resto de la API. Si el módulo no responde en ese momento, la ruta da 502: reintenta. - No gasta créditos: ninguna ruta del módulo descuenta de tu bolsa. ## El recorrido - Revisa la configuración y cámbiala si hace falta: reglas de pago, peso de tus productos y beneficios. - Sube la tabla de tarifas de tu transportadora. Queda como borrador. - Mira sus n_ciudades, n_precios y warnings, y actívala. - Con cada pedido: mira qué beneficios aplican y cotiza el envío con la ciudad y el método de pago. - Crea la orden en Facturación con el total_envio, la tarifa_version y el descuento de la cotización. ## La tabla de tarifas Un libro de Excel (.xlsx, hasta 10 MB) con dos hojas. Se reconocen por el nombre: la que contiene «ciudad» y la que contiene «precio». HOJA «CIUDADES»COPIAR ``` Nombre_Ciudad_origen | Nombre_Ciudad_destino | Codigo_Zona | Zona MEDELLIN | BOGOTA D.C. | 2 | Nac MEDELLIN | ENVIGADO | 1 | Loc MEDELLIN | RIONEGRO | 3 | Zonal ``` - Obligatorias: Nombre_Ciudad_destino y Zona. La zona puede ir abreviada: Loc, Nac y Reg se leen como Local, Nacional y Regional. - Opcionales: Codigo_Zona y Nombre_Ciudad_origen (la primera que aparezca es la ciudad_origen). - Una fila cuyo nombre tiene más de seis dígitos (un teléfono pegado, por ejemplo) se descarta y sale en warnings. HOJA «PRECIOS ENVÍO»COPIAR ``` Peso | Local | Regional | Nacional | Zonal | Otros 1 y 2 Kg | 7960 | 9900 | 12900 | 10900 | 18500 3 a 5 Kg | 9900 | 12400 | 14350 | 13200 | 22900 kg adicional entre 30 y 50 kg | 900 | 1100 | 1300 | 1200 | 1900 ``` - El encabezado es la primera fila que tiene «peso», «local» y «nacional». Las columnas de zona que se leen son Local, Regional, Nacional, Zonal, Otros y Especial. - La primera columna es el rango de peso; su último número es el tope en kilos («3 a 5 Kg» llega hasta 5). Las filas de «kg adicional» se guardan, pero no cuentan como rango. - Los precios, sin símbolo ni separadores de miles. UNA CARGACOPIAR ``` draft → (activar) → activa → (activas otra) → retirada ``` Activar le da a la tabla la siguiente tarifa_version y retira la anterior. Una retirada no vuelve: para recuperarla, súbela otra vez. ## Cómo se calcula el envío - El destino. Se busca la ciudad en la tabla activa, sin importar tildes ni mayúsculas; vale una dirección que empiece por la ciudad. Un municipio de Colombia que la tabla no lista cae en la zona Otros, si la tabla tiene precio para ella. - El peso. El peso_kg que mandes; si no, la suma de peso_sku de las líneas; si no, peso_default_pedido_kg. Si solo algunas líneas tienen peso, las demás se estiman con el peso medio de las conocidas. - La base. El primer rango cuyo tope alcanza el peso, en la columna de la zona. Si el peso pasa de todos, el rango mayor (y lo dice en notas). - El método de pago, con tus reglas_pago: | | MÉTODO | ENVÍO | PAGA HOY · MENSAJERO | contra, subtotal bajo el umbral | base + cargo fijo | 0 · todo | contra, desde el umbral | base + IVA del envío + comisión de recaudo sobre (subtotal + base + IVA) | 0 · todo | transferencia | base | todo · 0 | mixto | base | productos menos descuento · el envío El total es subtotal + envío − descuento, nunca negativo. Con envio_gratis, el envío es 0 en cualquier método. Todas las cifras se redondean a unidades enteras. Si no hay tarifa (la ciudad no está, su zona no tiene precio o no hay tabla activa), la cotización responde igual, con degradado: true y el motivo en notas. Ese envío no está calculado: no lo cobres, confírmalo a mano. Si la ciudad tiene un solo nombre muy parecido en la tabla, sale en ciudad_sugerida para que se lo preguntes al cliente. ## Los beneficios Un beneficio es una regla con condiciones y un efecto. Aplica si cumple todas sus condiciones: - está activo; - el método de pago está en metodos_pago (vacío: todos; sin método todavía, no se filtra); - el subtotal llega a subtotal_min, si lo tiene; - y, con solo_primera_compra, es la primera compra del cliente. Su efecto es un descuento en porcentaje (descuento_pct), un descuento fijo (descuento_fijo, sin pasar del subtotal) o el envío gratis (envio_gratis). Comercial no sabe quién es el cliente: si no le dices si es su primera compra, los beneficios de primera compra salen igual y requiere_primera_compra te avisa de que los ofrezcas en condicional. ## Con Productos, Agente ventas y Facturación - Productos pone los precios: el precio_unitario de las líneas que mandas a cotizar sale de allí. Comercial no cambia precios. - Agente ventas usa Comercial en el chat: cuando el cliente da su ciudad y elige cómo pagar, evalúa los beneficios, cotiza el envío, le enseña el desglose y crea la orden. - Facturación guarda el resultado: la orden lleva envio = total_envio, la tarifa_version con la que se calculó y el descuento. Cambiar después la tabla o las reglas no toca las órdenes ya creadas. ## Rutas | | MÉTODO | RUTA | PARA QUÉ | PERMISO | CONFIGURACIÓN | GET | /config | La configuración comercial | lee | PATCH | /config | Cambia la configuración comercial | escribe | TARIFAS | POST | /tarifas/draft | Sube una tabla de tarifas | escribe | GET | /tarifas/imports | Las cargas de tarifas | lee | GET | /tarifas/imports/{carga} | Una carga de tarifas | lee | POST | /tarifas/{carga}/activate | Activa una tabla de tarifas | escribe | GET | /tarifas/active | La tabla de tarifas activa | lee | GET | /tarifas/ciudades | Busca una ciudad en la tabla | lee | COTIZAR | POST | /reglas/evaluar | Qué beneficios aplican | escribe | POST | /cotizar | Cotiza el envío de un pedido | escribe --- # Facturación: cómo funciona https://dev.bentho.ai/api/facturacion/ MÓDULO FACTURACIÓN # Facturación: cómo funciona Facturación cierra la venta. Convierte un carrito en una orden con su total a pagar, recibe el comprobante con el que el cliente dice que pagó y lo deja en una cola para que una persona lo apruebe o lo rechace. Al aprobarlo, la venta se confirma en Productos y la orden queda pagada, lista para despachar. Por API lo usas para cobrar desde tu propia tienda o tu propio chat, subir los comprobantes que te llegan por otros canales, revisar los pagos desde tu sistema y marcar los pedidos despachados. También puedes hacerlo desde la consola: en el Estudio de un espacio con el módulo activo, el panel Facturación lista las órdenes y sus comprobantes, enseña cada comprobante y permite aprobarlo o rechazarlo, cancelar la orden o marcarla despachada. ## Antes de empezar - El espacio tiene que tener el módulo Facturación: facturacion sale en sus módulos en GET /api/companies. Si no, cada ruta responde 403 módulo no habilitado para este espacio. - Leer (las rutas GET) vale con cualquier clave. Crear órdenes, subir y decidir comprobantes, cancelar, despachar y cambiar la configuración piden una clave escribe. - Las rutas cuelgan de /api/mod/facturacion/{espacio}/, con la misma clave y los mismos límites que el resto de la API. Si el módulo no responde en ese momento, la ruta da 502: reintenta. - No gasta créditos: ninguna ruta del módulo descuenta de tu bolsa. - Las órdenes usan el catálogo de Productos: el espacio necesita también ese módulo, con sus variantes y precios. ## Una venta, paso a paso - Una sola vez: configura las cuentas donde te pagan (Nequi, Bancolombia, efectivo…). - Crea la orden con las variantes y cantidades, y el envío si lo cotizaste en Comercial. Responde 201 con el total, el expira_at y las cuentas activas. - Dile al cliente cuánto pagar y dónde: el total y las cuentas de la orden. - Cuando te mande el comprobante, súbelo. La orden pasa a under_review. - Revisa la cola, mira el archivo y apruébalo o recházalo. - Si usas datos de despacho, guárdalos y, al enviar el pedido, márcalo despachado. Las órdenes sin pagar no vencen solas: llama de vez en cuando a vencer órdenes para pasarlas a expired y liberar su stock. ## Los estados de una orden ESTADOSCOPIAR ``` pending_payment → (comprobante) → under_review → (aprobar) → paid → (despachar) → fulfilled under_review → (rechazar) → rejected → (otro comprobante) → under_review pending_payment → (vencer órdenes, pasado expira_at) → expired pending_payment | under_review | rejected → (cancelar) → cancelled ``` | | ESTADO | QUÉ QUIERE DECIR | pending_payment | Creada, con su total. Espera el pago; el stock está apartado. | under_review | Llegó un comprobante: espera que alguien lo apruebe o lo rechace. | rejected | Se rechazó el comprobante. Admite otro; el stock sigue apartado. | paid | Pagada: la venta se confirmó en Productos. Ya no se puede cancelar. | fulfilled | Despachada. Final. | expired | Venció sin pago y se liberó su stock. Final. | cancelled | Cancelada y con el stock liberado. Final. Cada cambio queda en la historia de la orden, con quién lo hizo y por qué. Un cambio que el estado no permite responde 409. ## La revisión de los pagos Un pantallazo de una transferencia no prueba que el dinero llegó: uno falso y uno real pueden verse iguales. Por eso, por defecto, ningún pago se aprueba solo: cada comprobante espera en la cola a que una persona lo mire y decida. Para ayudar a decidir, a cada comprobante se le hacen cinco comprobaciones, que salen en reglas_json: | | COMPROBACIÓN | PASA SI… | referencia_unica | El número de referencia del comprobante no se había usado antes en el espacio. | imagen_no_duplicada | La imagen no es (casi) igual a la de otro comprobante anterior. | monto_coincide | El monto del comprobante es exactamente el total de la orden. | destino_registrado | La cuenta que recibe es una de tus cuentas activas (por su numero). | ventana_temporal | La fecha del comprobante cae entre la creación de la orden y ahora, con el margen de ventana_comprobante_horas, en tu zona_horaria. Las comprobaciones usan lo que se leyó del comprobante (extraccion_json). Si no se pudo leer (ok: false), las que dependen de lo leído no pasan, nunca se aprueba solo y decides a ojo. Aprobación automática. Un comprobante se aprueba solo únicamente si pasa las cinco, el total de la orden no supera auto_aprobar_hasta y la configuración tiene requiere_confirmacion_humana_siempre: false. De fábrica, auto_aprobar_hasta es 0 y la confirmación humana está encendida: nunca se aprueba solo. UN COMPROBANTECOPIAR ``` pending_review → (aprobar) → approved la orden pasa a paid → (rechazar) → rejected la orden pasa a rejected ``` Los comprobantes son datos personales del cliente: nombres, números de cuenta, montos. El archivo se sirve sin caché (Cache-Control: private, no-store): pídelo cuando alguien lo vaya a mirar y no lo guardes en otros sistemas. ## Los datos de despacho Si vendes con envío, Facturación puede guardar en la orden lo que necesitas para despachar. Viene apagado; lo enciendes en la configuración con despacho.activo: true. - Tú decides qué se pide: cada campo (nombre, cedula, telefono, direccion, barrio, ciudad…) se activa y se marca requerido por separado. - Los datos se van sumando: guárdalos a medida que el cliente los da. Cada orden dice en despacho_status.faltantes qué falta. - Con bloquea_fulfillment (encendido de fábrica), una orden no se puede marcar despachada hasta tener todos los requeridos: 409 despacho_incompleto. - Un campo que no pediste no se guarda: el módulo no acepta datos personales que nadie pidió. ## Con Productos, Comercial y Agente ventas - Productos pone los precios: al crear la orden se le pide la cotización del carrito (con sus promociones) y la orden la congela. Si reservar_stock está encendido, también aparta el stock. Al aprobar el pago, se le confirma la venta: descuenta el stock y cuenta el uso de las promociones. - Comercial pone el envío y los beneficios. Lo que cotiza entra en la orden como envio (su total_envio), tarifa_version y descuento. El total de la orden es el de Productos, menos ese descuento, más el envío. - Agente ventas hace todo esto solo en el chat: cotiza con Productos y Comercial, crea la orden, le da al cliente el total y las cuentas, y sube aquí el comprobante que el cliente le manda. A ti te queda la cola de revisión. - Contabilidad: con emitir_factura_contable encendido, cada orden pagada envía su factura a tu software contable. Si eso falla, la orden queda pagada igual. ## Rutas | | MÉTODO | RUTA | PARA QUÉ | PERMISO | ÓRDENES | POST | /orders | Crea una orden | escribe | GET | /orders/{orden} | Una orden | lee | GET | /orders | Las órdenes del espacio | lee | GET | /orders/{orden}/events | La historia de una orden | lee | POST | /orders/{orden}/cancel | Cancela una orden | escribe | POST | /orders/expire-due | Vence las órdenes sin pagar | escribe | PAGOS | POST | /orders/{orden}/evidence | Sube un comprobante de pago | escribe | GET | /review-queue | La cola de revisión de pagos | lee | GET | /review-queue/{comprobante}/imagen | El archivo de un comprobante | lee | POST | /review-queue/{comprobante}/approve | Aprueba un comprobante | escribe | POST | /review-queue/{comprobante}/reject | Rechaza un comprobante | escribe | DESPACHO | PUT | /orders/{orden}/despacho | Guarda los datos de despacho | escribe | POST | /orders/{orden}/fulfill | Marca una orden despachada | escribe | CONFIGURACIÓN | GET | /payment-config | La configuración de cobro | lee | PATCH | /payment-config | Cambia la configuración de cobro | escribe --- # Contabilidad: cómo funciona https://dev.bentho.ai/api/contabilidad/ MÓDULO CONTABILIDAD # Contabilidad: cómo funciona Contabilidad conecta tu espacio con tu software contable (Alegra o Siigo) para que tus ventas lleguen solas: facturas, pagos y clientes. También lee de él el catálogo y el stock, y te dice qué quedó sin confirmar. Si tu programa no tiene API (World Office clásico, Helisa…), te arma un archivo CSV listo para importar. Por API lo usas para facturar desde tu propia tienda o tu sistema de pedidos, conciliar al cierre del mes o leer el inventario sin entrar a tu programa contable. También puedes verlo desde la consola: en el Estudio de un espacio con el módulo activo, el panel Contabilidad enseña el conector, la conciliación y la cola, con un botón para procesarla y otro para reintentar lo que falló. ## Antes de empezar - El espacio tiene que tener el módulo Contabilidad: contabilidad sale en sus módulos en GET /api/companies. Si no, cada ruta responde 403 módulo no habilitado para este espacio. - Leer (las rutas GET) vale con cualquier clave. Todo lo demás pide una clave escribe, también probar la conexión, leer el catálogo o procesar la cola. - Las rutas cuelgan de /api/mod/contabilidad/{espacio}/, con la misma clave y los mismos límites que el resto de la API. - No gasta créditos: ninguna ruta del módulo descuenta de tu bolsa. ## Conecta tu software - Configura el conector con proveedor y sus credenciales. - Prueba la conexión: ok: true quiere decir que las credenciales abren tu software. - Mira qué puede hacer: si lee catálogo y stock, si recibe facturas, o si solo exporta archivos. | | PROVEEDOR | CREDENCIALES | QUÉ HACE | alegra | email y api_token | Lee catálogo y stock; recibe facturas, pagos y clientes; descuenta ventas. | siigo | username y access_key | Lee catálogo y stock; recibe facturas, pagos y clientes. | csv_export | Ninguna | Para programas sin API: lo encolado sale en un CSV que importas tú. worldoffice_cloud, quickbooks y xero se pueden elegir, pero todavía no conectan. Las credenciales entran y no vuelven a salir. Ni la configuración ni ninguna otra ruta devuelve sus valores: solo sus nombres, en credenciales_set. Para cambiar una, manda la misma clave con el valor nuevo; una clave con valor vacío no borra la que había. LO QUE MANDAS · LO QUE VUELVECOPIAR ``` PATCH …/config { "proveedor": "alegra", "credenciales": { "email": "contabilidad@example.com", "api_token": "TU_TOKEN_DE_ALEGRA" } } → { "proveedor": "alegra", "credenciales_set": ["api_token", "email"], … } ``` ## Facturas, pagos y clientes Nada se manda a tu software en el momento: todo pasa por una cola de envíos. Así una venta nunca se queda colgada porque tu programa contable tarde o esté caído. - Encola una factura, un pago o un cliente: responde 202 con su outbox_id en pending. - Procesa la cola: manda a tu software lo que toca y responde con cuántas entradas salieron bien. - Consulta el estado de cada envío: en done, resultado_json trae su id en tu software y, en las facturas, su número y su CUFE si tu software los da. - Si algo queda en dead, corrige lo que dice motivo_error y reinténtalo. referencia_interna es tu id de la venta, del pago o del cliente, y es lo que impide duplicar: encolar dos veces la misma no crea otra entrada, te devuelve la que ya había con idempotent_replay: true. La clave va por tipo: la factura y el pago de una misma venta pueden llevar la misma referencia. Si el espacio tiene además el módulo Facturación con la emisión contable encendida, cada orden pagada entra sola en la cola como factura, con el id de la orden como referencia_interna. ## Estados de un envío ESTADOSCOPIAR ``` pending → processing → done ↘ failed → (siguiente procesado) → processing ↘ dead (agotó sus intentos) failed | dead → (reintentar) → pending ``` - Un fallo no se pierde: queda en failed y vuelve a intentarse en un procesado posterior, cada vez con más espera (next_attempt_at). - Tras max_intentos fallos queda en dead y ya no se reintenta sola. Reintentar le da un intento más; no reinicia la cuenta. - Si encolas antes de conectar tu software, las entradas esperan en failed sin gastar intentos. ## Conciliación La conciliación cuadra un periodo: cuántas facturas y pagos hay en cada estado, cuánto suman los que tu software confirmó y cuáles quedaron sin confirmar. deriva: true quiere decir que hay algo pendiente o parado que mirar; la lista pendientes dice qué. ## Programas sin API: el archivo Con csv_export, procesar la cola no llama a nadie: deja cada entrada en done, lista para el archivo. Después exporta facturas, pagos o clientes y recibes el CSV dentro de un JSON (filename y contenido). - plantilla_export elige las columnas de las facturas: generico, worldoffice o helisa. - Exportar no marca nada como exportado: cada vez trae todo lo de ese tipo. Lleva tú la cuenta de qué importaste. ## Catálogo y stock - Lee el catálogo o el stock de tu software en un formato común (sku, nombre, precio, cantidad…). Cada lectura queda en el historial. - Para una tarea programada, sincroniza solo si toca: no llama a tu software hasta que pasen cadencia_sync_horas. - Con write_back_stock autorizado, puedes descontar cada venta del inventario de tu software (hoy, con Alegra). ## Rutas | | MÉTODO | RUTA | PARA QUÉ | PERMISO | CONEXIÓN | GET | /config | La configuración del conector | lee | PATCH | /config | Conecta tu software contable | escribe | POST | /config/test | Prueba la conexión | escribe | GET | /capabilities | Qué puede hacer tu software | lee | ENVIAR | POST | /push/invoice | Encola una factura | escribe | POST | /push/payment | Encola un pago | escribe | POST | /push/customer | Encola un cliente | escribe | GET | /push/{entrada} | El estado de un envío | lee | POST | /outbox/process | Procesa la cola | escribe | GET | /outbox | La cola de envíos | lee | POST | /outbox/{entrada}/retry | Reintenta un envío | escribe | CONCILIAR | GET | /reconciliation | La conciliación | lee | GET | /exports/{lote} | Exporta a un archivo | lee | SINCRONIZAR | POST | /sync/items | Lee el catálogo de tu software | escribe | POST | /sync/stock | Lee el stock de tu software | escribe | POST | /sync/{datos}/due | Sincroniza solo si toca | escribe | GET | /sync/runs | El historial de lecturas | lee | POST | /stock/write-back | Descuenta una venta del stock | escribe --- # Kommo: cómo funciona https://dev.bentho.ai/api/kommo/ MÓDULO KOMMO # Kommo: cómo funciona Kommo conecta tu espacio con tu cuenta del CRM Kommo. Hace dos cosas: - Chat automático: los mensajes que tus clientes escriben por Kommo (por WhatsApp, por ejemplo) le llegan a Bentho, que contesta en el mismo chat con el Agente ventas del espacio. Si lo enciendes, también entiende notas de voz y recoge comprobantes de pago. - Etapas sincronizadas: cuando un cliente cambia de estado en Bentho (nuevo, contactado, ganado…), su tarjeta se mueve a la etapa que elijas de tu embudo en Kommo. Por API conectas la cuenta, eliges las etapas, enciendes o apagas cada pieza y vigilas los envíos a Kommo. También puedes hacerlo desde la consola: entra al Estudio de un espacio con el módulo activo y abre Kommo. ## Antes de empezar - El espacio tiene que tener el módulo Kommo: kommo sale en sus módulos en GET /api/companies. Si no, cada ruta responde 403 módulo no habilitado para este espacio. - Cambiar la configuración, probar la conexión, sincronizar, procesar o reintentar envíos, reiniciar chats y borrar piden una clave escribe. Leer vale con cualquier clave. - Las rutas cuelgan de /api/mod/kommo/{espacio}/, con la misma clave y los mismos límites que el resto de la API. - Necesitas una cuenta de Kommo con permiso para crear integraciones, y el acceso a la consola de Bentho para copiar las direcciones que se pegan en Kommo (más abajo). ## Conectar tu cuenta, paso a paso - En Kommo, crea una integración privada y copia su token de larga duración. - Guárdalo con PATCH config, junto con tu subdominio y el canal (modo_canal). - Prueba la conexión: tiene que responder ok: true. - Lee tus embudos y etapas y guarda en mapeo_estados a qué etapa va cada estado. - Pega en Kommo las direcciones de tu espacio. - Prueba con un chat tuyo: ponlo en chat_allowlist y enciende bot_activo. Cuando conteste bien, vacía la lista para atender a todos. - Si quieres, enciende sync_leads_activo, audio_activo y adjuntos_activo. PATCH CONFIGCOPIAR ``` { "subdomain": "tiendaaurora", "auth_mode": "long_lived", "token_expires_at": 0, "credenciales": { "access_token": "TU_TOKEN_DE_LARGA_DURACION" }, "modo_canal": "salesbot" } ``` Las credenciales no salen nunca en las respuestas: credenciales_set dice cuáles hay guardadas. OAuth (client_id y client_secret en credenciales) no se completa con una clave: la autorización en Kommo pide una sesión de la consola. Por API, usa el token de larga duración. ## Las direcciones que pegas en Kommo Kommo avisa a Bentho llamando a una dirección propia de tu espacio. No la llamas tú: la pegas en Kommo. Está en la consola, en el panel de Kommo del Estudio, bajo Direcciones para pegar en Kommo: - Dirección del canal de chat: https://bentho.ai/api/hooks/kommo/inbound/. El token identifica tu espacio. - Dirección de retorno: https://bentho.ai/api/hooks/kommo/oauth/callback. Solo para OAuth: es la dirección de redirección de tu integración en Kommo. Las integraciones que configuraste antes con bentho.org siguen funcionando: las dos direcciones valen y llegan al mismo sitio, así que no hace falta cambiarlas en Kommo. Dónde pegar la del canal de chat depende de cómo lleguen los mensajes: - modo_canal: "salesbot", para los canales oficiales de Kommo como WhatsApp: un paso Widget de tu Salesbot (el widget de Bentho) le pasa cada mensaje a esa dirección, y un paso «Mensaje» muestra la respuesta, que vuelve en {{json.message}}. - modo_canal: "custom": los mensajes llegan por el canal de chat de Bentho conectado a tu cuenta (scope_id). Esos llegan firmados. - En los dos casos, pega la misma dirección en los webhooks de tu cuenta de Kommo, con los eventos de cambio de etapa del lead (para traer los cambios que hagas a mano) y de mensaje entrante (para no perder ningún texto y para las notas de voz y los comprobantes). Bentho contesta a Kommo enseguida con {"ok": true} y hace el trabajo después. Con un token que no es el de tu espacio, 401 No autorizado.; si no puede atenderlo en ese momento, 502, y Kommo lo reintenta. Trata esa dirección como una contraseña: quien la tenga puede mandar mensajes a tu espacio. Leerla o cambiarla por otra (la anterior deja de valer al momento) pide una sesión de la consola: con una clave bth_, la ruta que la da responde 403 Esta operación requiere una sesión de usuario. ## Estados y etapas Cada cliente que llega por el chat tiene un estado en Bentho. En mapeo_estados dices a qué etapa de Kommo (pipeline_id y status_id) corresponde cada uno: | | ESTADO | QUÉ ES | SIN MAPEAR | nuevo | Acaba de llegar. | La tarjeta no se mueve. | contactado | Ya se habló con él. | La tarjeta no se mueve. | ganado | Compró. | Etapa 142 (ganada) del embudo de los demás estados. | cerrado | Se cerró sin compra. | Etapa 143 (perdida) del mismo embudo. | handoff | Pidió hablar con una persona. | Solo se calla el bot; la tarjeta no se mueve. - De Bentho a Kommo: con sync_leads_activo, cada cambio de estado mueve la tarjeta (o la crea si aún no existe). Para forzar una pasada, sincroniza las etapas. - De Kommo a Bentho: si mueves a mano una tarjeta a la etapa de contactado o de cerrado, Bentho actualiza al cliente (llega por el webhook de cambio de etapa). ganado lo decide la venta en Bentho, no la tarjeta. - Cuando un cliente pide una persona, el bot se calla en ese chat pausa_humano_ttl_s segundos (una hora por defecto). Si mapeaste handoff, la tarjeta pasa a esa etapa, asignada a responsable_handoff_id. El bot también se calla cuando escribe alguien de tu equipo. ## Los envíos a Kommo Todo lo que Bentho hace en Kommo es un envío: contestar un chat, mover una tarjeta, entregar un comprobante. Si uno falla, se reintenta solo, hasta 5 veces; después queda descartado para que lo mires. ESTADOSCOPIAR ``` pending → processing → done ↘ failed → (se reintenta solo) → processing ↘ dead (5 intentos: descartado) dead | failed → (reintentar) → pending ``` - Mira cuántos hay en cada estado en estado de envíos. - Si hay descartados, léelos en envíos con ?estado=dead: motivo_error dice qué pasó. - Arregla la causa (un token caducado, una etapa borrada en Kommo) y reinténtalo. - Para no esperar, procesa los envíos ahora. Los mensajes seguidos de un mismo cliente se contestan juntos, cuando deja de escribir (debounce_s), y dentro de un chat todo sale en orden. ## Probar sin molestar a tus clientes - Con ids en chat_allowlist, el bot solo contesta a esos chats. Con el Salesbot, el id de un chat es el del lead en Kommo. - Para repetir una prueba desde cero, reinicia los chats de prueba: se borra su carrito y su pedido abierto. Solo funciona con la lista llena, para no tocar nunca a un cliente de verdad. - Con bot_activo: false el chat automático se apaga del todo; lo demás sigue. ## Créditos - Las rutas de este módulo no gastan créditos. - Las respuestas del chat automático las escribe el Agente ventas: gastan créditos de la bolsa de tu cuenta como sus conversaciones. - El saldo, en la bolsa de créditos. ## Rutas | | MÉTODO | RUTA | PARA QUÉ | PERMISO | CONEXIÓN | GET | /config | La conexión con Kommo | lee | PATCH | /config | Conecta y ajusta Kommo | escribe | POST | /config/test | Prueba la conexión | escribe | DEL | / | Desconecta Kommo y borra sus datos | escribe | EMBUDOS | GET | /pipelines | Los embudos y etapas de Kommo | lee | POST | /sync/reconcile | Sincroniza las etapas ahora | escribe | ENVÍOS | GET | /sync/status | El estado de los envíos | lee | GET | /inbound/ledger | Lo que entró por el chat y qué se hizo | lee | GET | /outbox/ | Los envíos a Kommo | lee | POST | /outbox/{envio}/retry | Reintenta un envío | escribe | POST | /sync/process | Procesa los envíos ahora | escribe | PRUEBAS | POST | /chats/reset | Reinicia los chats de prueba | escribe --- # Landings https://dev.bentho.ai/api/landings/ MÓDULO LANDINGS # Landings: cómo funciona API en preparación. Esta guía describe su contrato. Confirma que el módulo esté disponible y habilitado en tu espacio antes de integrar estas rutas. Le das a Bentho un brief y la URL que recibe los formularios. Bentho escribe una landing de una sola página, la prueba en un navegador (formulario, enlaces, teclado, escritorio y móvil) y te la entrega en un HTML listo para publicar donde quieras. También puedes hacerlo desde la consola: entra al Estudio de un espacio con el módulo activo y abre Landings. El panel permite crear la landing, responder a los datos que falten, refinarla, consultar su calidad y descargar el HTML. ## Antes de empezar - El espacio tiene que tener el módulo Landings: landing sale en sus módulos en GET /api/companies. Si no, cada ruta responde 403 módulo no habilitado para este espacio. - Crear, refinar, validar, completar, cancelar, restaurar y borrar piden una clave escribe. Leer vale con cualquier clave. - Las rutas cuelgan de /api/mod/landing/{espacio}/, con la misma clave y los mismos límites que el resto de la API. ## El recorrido - Crea la landing: responde 202 con su id y la versión 1 en pendiente. - Consulta su estado cada 10 a 15 s. Suele tardar unos minutos. - Si queda en esperando_datos, enséñale a tu usuario las preguntas de faltantes y manda lo que responda a completar. La versión sigue sola. - Cuando termine (aprobado o revision), descarga su HTML y publícalo. - Para cambiarla, refínala con una instrucción. Si la retocaste a mano, mándala a validar. Si prefieres una anterior, restáurala. ESTADOSCOPIAR ``` pendiente → generando → aprobado | revision | fallido ↘ esperando_datos → (completar) → pendiente generando → aplazado → generando (se reanuda sola) cualquiera en curso → (cancelar) → cancelado ``` ## Versiones - Cada generación, refinado, validación o restauración es una versión nueva de la misma landing. Las anteriores se conservan: descarga cualquiera con ?version= o vuelve a una con restaurar, que la copia como versión nueva, sin gastar créditos y con su certificado. - Una landing tiene una sola versión en curso: pedir otra mientras tanto da 409. Cancélala o espera. - Un espacio tiene un tope de landings generándose a la vez: al pasarlo, 429. Las que esperan datos no cuentan. - aprobado quiere decir que pasó las dos revisiones: la de funcionamiento (el formulario envía, los enlaces llevan a su sitio, se usa con teclado, nada se sale de la pantalla) y la de diseño, en escritorio y en móvil. revision trae HTML, pero alguna no pasó: calidad.motivos dice cuál. - El certificado (calidad) vale para un HTML exacto, el de su html_sha256. Si lo retocas, ya no vale: mándalo a validar. ## Datos que faltan Bentho no inventa testimonios, reseñas, resultados, ponentes, agendas ni fechas. Si el brief pide algo así sin darlo, antes de escribir te pregunta (hasta cinco cosas). Respondes lo que tengas y omites lo demás; lo omitido se escribe sin ese elemento y sin avisarle al visitante de que falta. Para que no pregunte, crea la landing con revisarBrief: false. - También pregunta por el destino de un siguiente paso que el brief promete y no da: el enlace para reservar, el número para escribir por chat, el archivo de una descarga. - Si lo que se ofrece es físico o un lugar (un producto, un local, un inmueble) y el brief no trae imágenes, pide al final URLs públicas de fotos reales. Se puede omitir. - Cada pregunta trae en ejemplo el formato de una buena respuesta, con huecos entre corchetes ([nombre del plan]: [precio]): nunca un dato que puedas confundir con uno real. - Responder «no tengo», «sin datos», «N/A» o «-» cuenta como omitir. ## Lo que recibe tu submitUrl Al enviar, el formulario de la landing hace un POST desde el navegador del visitante a tu submitUrl, con Content-Type: application/json: CUERPO DEL ENVÍOCOPIAR ``` { "page_id": "curso-excel-otono", "source": "https://tu-dominio.com/excel-para-pymes?utm_source=newsletter", "nombre": "Ana", "email": "ana@example.com", "telefono": "+34 600 000 000", "utm_source": "newsletter" } ``` - page_id es el pageId que mandaste al crearla, y source, la dirección de la página. Después, un campo por cada campo del formulario. - Llegan también utm_source, utm_medium, utm_campaign, utm_term, utm_content, gclid, fbclid, ttclid y msclkid si la visita los traía, aunque el visitante haya pasado por otras páginas antes de enviar. - Contesta con un 2xx y {"success": true}. Con cualquier otra cosa, el visitante ve «No pudimos enviar tu solicitud» y puede reintentar. - El envío sale del navegador: si tu submitUrl está en otro dominio que la landing, tiene que aceptar CORS (la petición previa OPTIONS y el POST con Content-Type: application/json). ## El formulario: formConfig Sin formConfig, Bentho arma el formulario según el brief. Con él, lo fijas tú: | | CAMPO | QUÉ ES | fields | Obligatorio. Hasta 20 campos, en orden: name (letras, números y _, empieza por letra; no puede ser page_id ni source), label, type (text, email, tel, number, textarea o select), required (boolean), y si quieres placeholder y options (para select, hasta 30). | success_message | Obligatorio. Lo que ve el visitante al enviar. Hasta 2000 caracteres. | cta_text | El texto del botón de envío. | redirect_url | Adónde lleva al visitante después de enviar (HTTP o HTTPS). Sin él se respeta el destino que pida el brief, como un enlace para reservar. | cta_url · cta_links | Adónde llevan los botones que no son el envío: una URL para todos o una lista [{text, url}], en orden. FORMCONFIGCOPIAR ``` { "fields": [ { "name": "nombre", "label": "Nombre", "type": "text", "required": true }, { "name": "email", "label": "Email", "type": "email", "required": true }, { "name": "telefono", "label": "Teléfono", "type": "tel", "required": false } ], "cta_text": "Quiero mi plaza", "success_message": "¡Listo! Te escribimos en menos de 24 horas." } ``` Un formConfig que no cumple esta forma no da 422: la versión termina en fallido y error dice por qué. El formConfig queda guardado en la landing. Refinar o validar sin él usa el guardado; mandar uno nuevo lo sustituye para las versiones siguientes. ## Créditos - Crear, refinar, validar y completar gastan créditos de la bolsa de tu cuenta, como las preguntas. Lo que gasta cada una depende del brief y de cuánto haya que corregir. - Con la bolsa agotada, esas cuatro rutas responden 402 (bolsa_agotada). Leer, cancelar, restaurar y borrar siguen funcionando. - Cancelar o borrar no devuelve lo que ya se gastó. - El saldo, en la bolsa de créditos. ## Tamaños - El cuerpo de una petición, hasta 24 MB. - prompt, hasta 24 000 caracteres; brandBlock, hasta 20 000; funnelReferenceHtml y el html de validar, hasta 300 000. - El PDF, en base64 dentro del JSON: hasta 16 000 000 de caracteres, unos 12 MB de PDF. ## Rutas | | MÉTODO | RUTA | PARA QUÉ | PERMISO | POST | /landings | Crea una landing · gasta créditos | escribe | GET | /landings/{landing} | El estado de una landing | lee | POST | /landings/{landing}/completar | Responde a lo que falta · gasta créditos | escribe | GET | /landings/{landing}/html | El HTML de una landing | lee | POST | /landings/{landing}/refinar | Pide cambios a una landing · gasta créditos | escribe | POST | /landings/{landing}/validar | Valida un HTML editado a mano · gasta créditos | escribe | POST | /landings/{landing}/cancelar | Cancela la versión en curso | escribe | POST | /landings/{landing}/restaurar | Vuelve a una versión anterior | escribe | GET | /landings | Las landings del espacio | lee | DEL | /landings/{landing} | Borra una landing | escribe --- # conversations https://dev.bentho.ai/api/conversations/ POST/api/rag/{espacio}/conversations # Haz una pregunta a tu espacio Bentho busca en los documentos del espacio y contesta solo con lo que dicen. Cada respuesta trae sus fuentes y cuánto confía en ella. Si reusas el session_id, entiende las preguntas de seguimiento. - PERMISOlee (vale cualquier clave) - CRÉDITOSgasta créditos - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | question | string | SÍ | La pregunta, en texto libre. | session_id | string | SÍ, EN LA PRÁCTICA | Tu identificador de conversación. Es obligatorio: sin él responde 400. Reúsalo para seguir el hilo; dos claves del mismo dueño con el mismo session_id comparten conversación. | history | [{ role: "user" | "assistant", content }] | no | Turnos previos, si prefieres mandar tú el contexto. Sin él, se usa el historial de la sesión. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | status_code | "1000" | "1001" | "1000" contesta con apoyo en los documentos. "1001" no hay información suficiente o prefiere no responder; response trae entonces el mensaje de rechazo del espacio. | response | string | El texto de la respuesta. Si hay fuentes, termina con el pie «Referencia: …». | sources | string[] | Los documentos citados. Vacío con 1001. | confidence | number | null | De 0 a 1. Desde 0,75 responde; entre 0,55 y 0,75 responde con warning; por debajo, 1001. | warning | boolean | true si solo parte de la respuesta está respaldada. Sigue siendo 1000. | claim_attributions | array | null | Por frase: { sentence, chunk_index, entailment, supported }. | session_id | string | null | El que mandaste. | tenant_id · query | string | El espacio y la pregunta original. | verification · condensed_query | object | null · string | null | Detalle de la verificación y la pregunta reescrita, si la hubo. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 400 | Falta session_id | session_id es obligatorio. | 402 | La cuenta agotó sus créditos del ciclo | La cuenta de este espacio agotó sus créditos de este ciclo. | 404 | El espacio no existe | — | 422 | Falta question | — | 502 | El servicio no respondió a tiempo: reintenta con espera creciente | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Un error interno al responder no llega como 5xx: llega como 200 con status_code: "1001" y el texto «Ocurrió un error interno procesando la consulta. Intenta de nuevo.». Trátalo como una respuesta sin información. - Una consulta típica tarda de 2 a 20 s. Pon el timeout de tu cliente en 60 s o más. - En espacios con el módulo Agente ventas la respuesta tiene otra forma (estado, cotizacion, handoff…) y no usa history. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/rag/mi-espacio/conversations \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "question": "¿Cuántos días tengo para devolver un producto?", "session_id": "erp-consulta-4821" }' ``` ■ 200 OKejemplo, con los campos del esquema ``` { "tenant_id": "mi-espacio", "session_id": "erp-consulta-4821", "query": "¿Cuántos días tengo para devolver un producto?", "status_code": "1000", "response": "Según la política de devoluciones, tienes 30 días desde la entrega para devolver un producto sin usar…\n\nReferencia: politica_devoluciones.pdf", "sources": ["politica_devoluciones.pdf"], "confidence": 0.7556, "warning": false, "claim_attributions": [ … ], "verification": { … }, "condensed_query": null } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # conversations/stream https://dev.bentho.ai/api/conversations-stream/ POSTSSE/api/rag/{espacio}/conversations/stream # La misma pregunta, en vivo El mismo cuerpo que conversations, pero la respuesta llega por eventos (SSE) mientras se escribe. Sirve para mostrarla a una persona sin hacerla esperar. - PERMISOlee (vale cualquier clave) - CRÉDITOSgasta créditos - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | question | string | SÍ | La pregunta. | session_id | string | SÍ, EN LA PRÁCTICA | Igual que en conversations. | history | array | no | Igual que en conversations. ## EVENTOS 200 con Content-Type: text/event-stream. Cada evento es una línea data: {…} seguida de una línea en blanco. Las líneas que empiezan por : son : keepalive (cada 15 s sin datos): ignóralas. | | CAMPO | TIPO | QUÉ ES | status | { type, stage: "verifying" } | Solo en espacios con verificación previa: está comprobando antes de generar. | chunk | { type, content } | Un pedazo de texto. Al final llega uno con el pie «Referencia: …» si hay fuentes. | handoff | { type, handoff_id, reason } | Conviene pasar a una persona (low_confidence o contradiction). Llega antes del done. | done | { type, status_code, full_response, confidence, warning, sources } | Cierre normal. Manda su status_code, aunque ya hayan llegado chunks. | error | { type, content } | Algo falló después del 200. El stream termina. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 401 | Antes de abrir el stream: clave inválida | Credencial de servicio inválida o revocada. | 402 | Antes de abrir el stream: créditos agotados | — | evento | Evento error: el servicio no respondió a tiempo | — | evento | Evento error: falta session_id | session_id es obligatorio. | evento | Evento error: el servicio está saturado; reintenta con espera creciente | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Todo lo que se puede rechazar antes (401, 402, 403, 429) llega como respuesta JSON normal, no como evento. Después del 200, cualquier fallo llega como evento error. - En espacios con verificación estricta la respuesta llega en un solo chunk, ya verificada, después de un status. - Los chunk ya enviados no se retiran aunque el done diga "1001": decide con el status_code del done. CURLPYTHONJAVASCRIPT COPIAR ``` curl -N https://bentho.ai/api/rag/mi-espacio/conversations/stream \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "question": "¿Cuántos días tengo para devolver un producto?", "session_id": "erp-consulta-4821" }' ``` ■ 200 · EVENTOSderivado del esquema ``` : keepalive data: {"type": "chunk", "content": "Según la política de devoluciones, "} data: {"type": "chunk", "content": "tienes 30 días desde la entrega…"} data: {"type": "chunk", "content": "\n\nReferencia: politica_devoluciones.pdf"} data: {"type": "done", "status_code": "1000", "full_response": "…", "confidence": 0.7556, "warning": false, "sources": ["politica_devoluciones.pdf"]} ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # documents https://dev.bentho.ai/api/documents/ GET/api/rag/{espacio}/documents # Los documentos del espacio La lista de documentos que Bentho puede usar para responder, del más reciente al más antiguo. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… Para descargar uno: GET /api/rag/{espacio}/documents/{archivo}. Llega como adjunto, sin nombre de archivo: ponle tú el nombre al guardarlo. ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | documents | array | Ordenados por fecha de modificación, el más reciente primero. | documents[].filename | string | El nombre del archivo. | documents[].size | integer | Tamaño en bytes. | documents[].modified | number | Fecha de modificación, en segundos desde 1970 (con decimales). ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 404 | El espacio no existe | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Solo lista las extensiones admitidas: .pdf, .docx, .txt y .md. Un espacio sin documentos devuelve {"documents": []}. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/rag/mi-espacio/documents \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKderivado del esquema ``` { "documents": [ { "filename": "politica_devoluciones.pdf", "size": 48213, "modified": 1758000000.25 } ] } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # load_documents https://dev.bentho.ai/api/load-documents/ POST/api/rag/{espacio}/load_documents # Sube documentos Sube uno o varios archivos. Se guardan al momento y se indexan en segundo plano: consulta indexing_status para saber cuándo puedes preguntarles. - PERMISOescribe - CRÉDITOSgasta créditos - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). ## CUERPO · MULTIPART/FORM-DATA | | CAMPO | TIPO | REQUERIDO | QUÉ ES | files | archivo, repetible | SÍ | Uno por archivo. Admitidos: .pdf, .docx, .txt y .md. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | status | string | Siempre "processing". | files | string[] | Los nombres guardados. | message | string | « archivo(s) guardado(s). Indexación en segundo plano.» ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 400 | Extensión no admitida | Extensión no soportada: . Permitidas: ['.docx', '.md', '.pdf', '.txt'] | 400 | Sin archivos | No se recibieron archivos. | 402 | Créditos agotados (indexar gasta créditos) | — | 403 | Clave de solo lectura | Tu cuenta es de solo lectura en este espacio. | 413 | Más de 200 MB en total | La subida excede el tamaño máximo total permitido. | 413 | Más de 20 archivos | Demasiados archivos (máximo 20 por subida). | 413 | Un archivo de más de 50 MB | El archivo '' excede el máximo de 50 MB. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Subir un archivo con el mismo nombre lo reemplaza. - Si un archivo de la tanda tiene una extensión no admitida, los anteriores quedan guardados pero no se indexan: sube primero solo archivos válidos. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/rag/mi-espacio/load_documents \ -H "Authorization: Bearer $BENTHO_KEY" \ -F "files=@manual_usuario.pdf" \ -F "files=@catalogo_productos.docx" ``` ■ 200 OKderivado del esquema ``` { "status": "processing", "files": ["manual_usuario.pdf", "catalogo_productos.docx"], "message": "2 archivo(s) guardado(s). Indexación en segundo plano." } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # documents/{archivo} · borrar https://dev.bentho.ai/api/delete-document/ DEL/api/rag/{espacio}/documents/{archivo} # Quita un documento Borra el archivo del espacio y sus fragmentos del índice. Deja de usarse en la siguiente pregunta. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). | archivo | string | SÍ | El nombre exacto, codificado para URL. Sin «/», «\» ni «..». ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | status | string | "success". | message | string | «'' eliminado ( chunks).» ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 400 | Nombre con barras, puntos o vacío | nombre de archivo no válido. | 403 | Clave de solo lectura | Tu cuenta es de solo lectura en este espacio. | 404 | No existe | Documento no encontrado: | 500 | Falló el borrado | Error al eliminar el documento. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X DELETE https://bentho.ai/api/rag/mi-espacio/documents/manual_usuario.pdf \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKderivado del esquema ``` { "status": "success", "message": "'manual_usuario.pdf' eliminado (42 chunks)." } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # indexing_status https://dev.bentho.ai/api/indexing-status/ GET/api/rag/{espacio}/indexing_status # ¿Ya está listo lo que subí? El estado de la última subida del espacio. Cuando dice «done», ya puedes preguntarle a esos documentos. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | status | "idle" | "processing" | "done" | "error" | idle: nada en curso. processing: indexando. done: listo. error: falló. | files | string[] | Los archivos de la última subida. | message | string | « documento(s) indexado(s) — chunks.» cuando termina. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 404 | El espacio no existe | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Es el estado de la última subida y vive en memoria: si el servicio se reinicia vuelve a idle. Consulta cada pocos segundos y para en done o error. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/rag/mi-espacio/indexing_status \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKderivado del esquema ``` { "status": "done", "files": ["manual_usuario.pdf"], "message": "1 documento(s) indexado(s) — 42 chunks." } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # config · cambiar https://dev.bentho.ai/api/config/ PATCH/api/rag/{espacio}/config # Ajusta cómo responde el Cerebro Cambia solo los campos que mandes. Surte efecto en la siguiente pregunta. Los rangos válidos de cada campo te los da el GET de la misma ruta en rangos. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… GET /api/rag/{espacio}/config devuelve el mismo sobre sin cambiar nada, con todos los campos editables y sus rangos (vale con una clave lee). ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | temperature | float | no | 0 – 0,5. Más alto, más variación al redactar. | relevance_threshold | float | no | 0 – 0,75. Por debajo, un fragmento no cuenta como relevante. | citation_max | int | no | 0 – 5 fuentes citadas por respuesta. | rerank_top_n | int | no | 1 – 10 fragmentos tras reordenar. | bm25_weight · semantic_weight | float | no | 0 – 1 cada uno; no pueden ser los dos 0. | system_prompt | string | no | Hasta 8000 caracteres. | refusal_message | string | no | Lo que responde con 1001. Hasta 1000 caracteres. | citation_label | string | no | El rótulo del pie de fuentes («Referencia»). Hasta 40. | strict_source_only | bool | no | Solo contesta con lo que dicen los documentos. | nli_entail_min | float | no | 0,4 – 0,85. Cuánto tiene que apoyar el texto a cada frase. | cache_threshold | float | no | 0 – 0,1. Cuánto puede diferir una pregunta de otra ya respondida para reusar su respuesta: más bajo, más estricto. | num_predict | int | no | 512 – 4096. Hasta dónde puede alargarse una respuesta: más alto, más larga. | … | | no | Y más: la lista completa de los editables, con sus rangos, está en GET config. ## RESPUESTA 200 El mismo sobre que el GET, con la configuración ya guardada. | | CAMPO | TIPO | QUÉ ES | config | object | La configuración del espacio. | editables | string[] | Los campos que puede cambiar ESTA clave. Con lee: []. | rangos | object | Por campo: { tipo, min?, max?, largo_max?, opciones? }. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | Clave de solo lectura | Tu cuenta es de solo lectura en este espacio. | 403 | Un campo que solo cambia Bentho | «» solo lo puede cambiar un administrador de la plataforma. | 422 | Cuerpo vacío | No hay nada que cambiar. | 422 | Fuera de rango | «» tiene que estar entre y . | 422 | Campo desconocido | «» no se puede cambiar desde aquí. | 422 | Los dos pesos a 0 | La búsqueda necesita algún peso: no pueden ser los dos 0. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - No admite null: cada campo exige su tipo. Un entero vale donde se pide un decimal. - Los demás campos de la configuración solo los cambia Bentho: con una clave responden 403. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X PATCH https://bentho.ai/api/rag/mi-espacio/config \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "temperature": 0.2, "citation_max": 3 }' ``` ■ 200 OKderivado del esquema ``` { "config": { "tenant_id": "mi-espacio", "temperature": 0.2, "citation_max": 3, … }, "editables": ["bm25_weight", "cache_threshold", "citation_label", "citation_max", …], "rangos": { "temperature": { "tipo": "float", "min": 0.0, "max": 0.5 }, "name": { "tipo": "str", "largo_max": 80 }, … } } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # api-config · cambiar https://dev.bentho.ai/api/api-config/ PATCH/api/companies/{espacio}/api-config # Límites, cuota diaria e IPs del espacio Decide si la API del espacio está activa, cuántas peticiones por segundo acepta, su ráfaga, su cuota diaria y qué IPs pueden llamar. Se valida el resultado: lo guardado más lo que mandas. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… Para leerla sin cambiar nada, con las peticiones del día: GET /api/companies/{espacio}/api-config. ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | activa | bool | no | Apaga o enciende la API del espacio. Ojo: una clave que la apaga se deja fuera a sí misma. | solicitudes_por_segundo | int | no | 1 – 1000. Por defecto, 2. | rafaga | int | no | 1 – 5000 y no menor que solicitudes_por_segundo. Por defecto, 120. | cuota_diaria | int | null | no | 100 – 100 000 000 peticiones al día (UTC). null = sin tope. | ips_permitidas | string[] | no | Hasta 100 IPs o rangos CIDR, v4 o v6. [] = cualquiera. Ver Lista de IPs. ## RESPUESTA 200 Los cinco campos, como quedaron guardados (las IPs, ya normalizadas). | | CAMPO | TIPO | QUÉ ES | config | object | activa, solicitudes_por_segundo, rafaga, cuota_diaria, ips_permitidas. ## GET /API/COMPANIES/{ESPACIO}/API-CONFIG DEVUELVE ADEMÁS | | CAMPO | TIPO | QUÉ ES | techo_por_minuto | int | El tope global por clave (120 por defecto). | uso | object | Del día (UTC): solicitudes, atendidas, rechazadas y por_motivo (apagada, ip, cuota, tasa). | claves | array | Tus claves que alcanzan este espacio, sin secreto. | contratado | bool | Si el espacio tiene el módulo «Bentho API». ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | Sin el módulo contratado | Este espacio no tiene contratado el módulo «Bentho API». | 403 | Clave de solo lectura | Tu cuenta es de solo lectura en este espacio. | 404 | El espacio no existe | Espacio no encontrado. | 422 | Ráfaga menor que la tasa | «rafaga» no puede ser menor que «solicitudes_por_segundo»: el cubo se vaciaría antes de rellenarse un segundo. | 422 | IP mal escrita | «» no es una IP ni un rango CIDR válido. | 422 | Más de 100 IPs | Como máximo 100 IPs o rangos. | 422 | Campo desconocido | Campos desconocidos: ['', …]. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Guardar reinicia los cubos del espacio, no los contadores del día. - Estos límites se aplican solo a las claves. El Studio, con tu sesión, sigue entrando aunque apagues la API. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X PATCH https://bentho.ai/api/companies/mi-espacio/api-config \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "solicitudes_por_segundo": 5, "rafaga": 12, "cuota_diaria": 5000, "ips_permitidas": [ "203.0.113.10/32", "198.51.100.7/24" ] }' ``` ■ 200 OKderivado del esquema ``` { "config": { "activa": true, "solicitudes_por_segundo": 5, "rafaga": 12, "cuota_diaria": 5000, "ips_permitidas": ["203.0.113.10", "198.51.100.0/24"] } } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # bolsa de créditos https://dev.bentho.ai/api/bolsa/ GET/api/cuentas/{cuenta}/bolsa # Tus créditos Los créditos incluidos, gastados y restantes de la cuenta, que agrupa tus espacios. Es lo que mira Bentho para cortar con 402 cuando se agotan. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | cuenta | string | SÍ | El id de la cuenta. Desde un espacio: GET /api/companies/{espacio}/cuenta → cuenta.id. | dias | int, query | no | Mide los últimos N días. Sin él, el ciclo en curso: la misma ventana con la que se corta. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | creditos.permitido | bool | false = bolsa agotada. | creditos.incluidos | int | null | Los del plan. null = sin techo. | creditos.gastados | string | Decimal como texto, 4 decimales: "0.3426". | creditos.restantes | string | null | Lo que queda del ciclo. | creditos.porcentaje | string | null | Fracción gastada, de 0 a 1. | creditos.motivo | string | ok, bolsa_agotada, sin_plan o sin_creditos_acordados. | dias · renueva | int · string | null | La ventana medida y el primer día del ciclo siguiente. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | La cuenta no es tuya | Sin acceso a esta cuenta. | 404 | No existe | Cuenta no encontrada. | 422 | dias no es un entero | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - gastados y restantes son texto, no números: así no pierden precisión. Conviértelos con un decimal de verdad (Decimal en Python). - Consulta a cada módulo en el momento: puede tardar más que las demás rutas. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/cuentas/mi-cuenta/bolsa \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKderivado del esquema ``` { "cuenta": "mi-cuenta", "plan_id": "crecimiento", "creditos": { "permitido": true, "motivo": "ok", "incluidos": 800, "gastados": "0.3426", "restantes": "799.6574", "porcentaje": "0.0004" }, "dias": 12, "renueva": "2026-10-15" } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # profile https://dev.bentho.ai/api/profile/ GET/profile # Qué puede hacer tu clave De quién es la clave, a qué espacios llega y si puede escribir. Sirve para comprobar una clave nueva antes de usarla. Va sin /api delante. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | success | bool | Siempre true si la clave vale. | user.email | string | El email de quien creó la clave. | user.name | string | Su nombre. | user.role | string | El rol de esa cuenta. Una clave nunca administra, sea cual sea el rol. | user.companies | string[] | Los espacios a los que llega la clave en esta petición: los que nombró al crearla y su dueño sigue teniendo. Si se creó sin espacios, todos los de su dueño. | user.permiso | "lee" | "escribe" | El más restrictivo entre el de la clave y el de su dueño. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 401 | Clave inexistente, mal formada o revocada | Credencial de servicio inválida o revocada. | 401 | Sin cabecera Authorization | No autenticado. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Si a su dueño le quitan un espacio, la clave lo pierde en la siguiente petición, sin revocarla: esta respuesta lo refleja. - No tiene en cuenta la configuración de API de cada espacio: un espacio con la API apagada o con lista de IPs sigue saliendo aquí, aunque sus rutas te respondan 403. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/profile \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "success": true, "message": null, "user": { "email": "ana@example.com", "name": "Ana Pérez", "role": "client", "companies": ["mi-espacio"], "permiso": "lee" } } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # companies https://dev.bentho.ai/api/companies/ GET/api/companies # Los espacios a los que llega tu clave Tus espacios, con su id y sus módulos activos. Suele ser la primera llamada: el id es el {espacio} de las demás rutas, y modules te dice qué módulos responden en cada uno. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | companies | array | Uno por espacio al que llega la clave. [] si no llega a ninguno. | companies[].id | string | El id del espacio: el {espacio} de las demás rutas. | companies[].name | string | El nombre del espacio. | companies[].sector | string | General, Salud, Legal, Financiero, Educación… | companies[].modules | string[] | Los módulos activos. rag-chat y documents vienen con todo espacio; api es Bentho API. Vacía si la prueba venció o el plan se canceló. | companies[].trial | object | null | La prueba gratuita: status (pending, active, expired o converted, ya de pago), started_at y ends_at. null = sin prueba. | companies[].subscription | object | null | El plan de pago, si lo hay: plan_id, periodo (mes o anual), status (active, past_due o canceled), proveedor del cobro, current_period_end y proximo_cobro_at. null = sin plan de pago. | companies[].branding | object | null | Los colores del espacio: palette, accent, accent2, accentInk… null = sin color propio. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 401 | Clave inexistente, mal formada o revocada | Credencial de servicio inválida o revocada. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Una clave creada sin espacios llega a todos los de su dueño en cada petición, también a los que reciba después: esta lista cambia con ellos. - En un espacio sin un módulo, sus rutas (/api/mod/{modulo}/{espacio}/…) responden 403. Qué más puedes activar, en limite. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/companies \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "companies": [ { "id": "mi-espacio", "name": "Tienda Aurora", "sector": "General", "modules": ["rag-chat", "documents", "api", "fuentes"], "branding": { "palette": "mandarina", "accent": "#fb923c", … }, "trial": { "status": "converted", "started_at": "2026-09-01T14:00:00+00:00", "ends_at": "2026-09-15T14:00:00+00:00", … } } ] } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # limite https://dev.bentho.ai/api/limite/ GET/api/companies/{espacio}/limite # Cuántos módulos caben en el espacio El cupo de módulos del espacio, cuántos lleva y cuáles puedes activar ahora mismo. Míralo antes de cambiar los módulos con PATCH modules. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). ## RESPUESTA 200 Todo va dentro de limite. | | CAMPO | TIPO | QUÉ ES | modo | "automatico" | "manual" | automatico: manda tu plan. manual: Bentho fijó la lista de módulos que puede tener el espacio (habilitados). | plan_id | string | null | El plan que rige el espacio. null = sin plan. | tope_modulos · tope_conectores | int | null | Cuántos módulos y cuántos conectores incluye el plan. null = sin tope. | tope_sin_plan | int | null | Sin plan, cuántos módulos puedes tener en total (5). null si hay plan. | habilitados | string[] | En modo manual, los módulos que el espacio puede activar. | usados_modulos | int | Módulos que ya ocupan cupo. rag-chat y documents no cuentan. | usados_conectores | int | Conectores que ya ocupan cupo. | activos | string[] | Los módulos guardados en el espacio, también los que no cuentan. | disponibles | { modulos, conectores, total } | Cuántos caben todavía. Con plan, por clase; sin plan o en manual, en total. null = sin tope. | activables | string[] | Los módulos que puedes activar ahora sin pasarte del cupo. No trae los que solo activa Bentho. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 404 | El espacio no existe | Empresa no encontrada. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Los conectores (los que enlazan con otro software, como un CRM o el programa contable) cuentan aparte de los módulos. - activos son los módulos guardados; los que responden son los modules de GET /api/companies, que quedan vacíos si la prueba venció. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/companies/mi-espacio/limite \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "limite": { "modo": "automatico", "plan_id": "crecimiento", "tope_modulos": 6, "tope_conectores": 3, "tope_sin_plan": null, "habilitados": [], "usados_modulos": 2, "usados_conectores": 0, "activos": ["rag-chat", "documents", "api", "fuentes"], "disponibles": { "modulos": 4, "conectores": 3, "total": null }, "activables": ["landing", "productos", "agente", …] } } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # semaforo https://dev.bentho.ai/api/semaforo/ GET/api/companies/{espacio}/semaforo # Si cada módulo del espacio funciona Un color por módulo y el motivo en palabras: verde si funciona, y si no, por qué. Míralo cuando un módulo empiece a fallar, antes de reintentar. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | modulos | { [id]: { color, motivo } } | Uno por módulo activo, más rag, que es el asistente. No salen rag-chat, documents ni api: van con el asistente. | modulos.*.color | "verde" | "amarillo" | "rojo" | "gris" | gris: el espacio no puede usarlo ahora. rojo: no responde. amarillo: le falta aplicar una conexión. verde: funciona. Si aplican varios, gana el primero. | modulos.*.motivo | string | El porqué, listo para enseñarlo. Lista abajo. | calculado | string (ISO 8601, UTC) | Cuándo se calculó. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | La clave no llega a ese espacio | Sin acceso a este espacio. | 404 | El espacio no existe | Empresa no encontrada. | 403 | La API del espacio está apagada o tu IP no está en su lista | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Los motivos posibles: «La prueba de tu espacio terminó.» y «La cuenta de tu espacio no está al día.» (todo el espacio en gris); «Se acabaron los créditos del espacio.» (gris); «No responde.» (rojo); «1 conexión sin aplicar.» o «N conexiones sin aplicar.» (amarillo); «Funcionando.» (verde). - Sin créditos solo se apaga lo que los gasta (el asistente, Agente ventas, Fuentes, Landings…). Productos, Facturación y los demás siguen en verde. Cuánto te queda, en la bolsa. - Las conexiones son las que se dibujan entre módulos en el Estudio. Un amarillo se resuelve solo cuando el módulo las aplica. - Se recuerda unos segundos: consultarlo más de una vez cada 10 s no da información nueva. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/companies/mi-espacio/semaforo \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "modulos": { "rag": { "color": "verde", "motivo": "Funcionando." }, "fuentes": { "color": "gris", "motivo": "Se acabaron los créditos del espacio." }, "productos": { "color": "amarillo", "motivo": "1 conexión sin aplicar." } }, "calculado": "2026-11-02T15:04:11+00:00" } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # modules https://dev.bentho.ai/api/modules-espacio/ PATCH/api/companies/{espacio}/modules # Activa o quita módulos del espacio Manda la lista completa de módulos que quieres en el espacio: lo nuevo se activa y lo que falte se quita. Frena lo que pase del cupo y los módulos que solo activa Bentho: míralo antes en limite. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | modules | string[] | SÍ | La lista entera, no un cambio: incluye rag-chat, documents y los que ya tienes y quieres conservar. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | modules | string[] | La lista guardada. | cableado | array | Al quitar un módulo, sus conexiones con los demás se apagan. Una entrada por conexión: { de, a, encender, estado, detalle, etiqueta }, con estado aplicado o pendiente. [] si no quitaste nada. | prompt_cerebro | object | Solo al activar agente. estado: aplicado (el asistente pasa a hablar como la tienda; campos dice qué cambió), especializado (ya tenía instrucciones propias y no se tocaron) o no_disponible. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | Clave de solo lectura | Tu cuenta es de solo lectura en este espacio. | 403 | Pones o quitas un módulo que solo activa Bentho | «» solo lo activa o lo desactiva un administrador de Bentho. Pídeselo a tu asesor. | 404 | El espacio no existe | Empresa no encontrada. | 409 | Quitas rag-chat o documents | «» viene con todo espacio y no se puede quitar. | 409 | Un id que no existe | «» no es un módulo de Bentho. | 409 | Modo manual: un módulo fuera de habilitados | «» no está habilitado para este espacio. Pídeselo a tu asesor. | 409 | Sin plan: más de 5 módulos | Tu espacio incluye hasta 5 módulos y este cambio dejaría . Quita uno o elige un plan. | 409 | Más módulos de los que incluye el plan | Tu plan incluye módulo(s) y este cambio dejaría . Quita uno o mejora tu plan. | 409 | Más conectores de los que incluye el plan | Tu plan incluye conector(es) y este cambio dejaría . Quita uno o mejora tu plan. | 422 | Falta modules o no es una lista | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Quitar siempre se puede. Un cambio que no suma módulos tampoco se rechaza, aunque el espacio ya esté por encima del cupo (por ejemplo, tras bajar de plan). - Activar agente cambia la forma de la respuesta de conversations: revisa tu integración antes. - ventas y comprension se retiraron el 2026-10-13. Si la lista todavía trae ventas, se guarda como agente; comprension se descarta, porque el Agente ventas ya entiende cada mensaje. - Una conexión pendiente es un módulo que no respondió al apagarla: se puede reintentar desde el Estudio. Si vuelves a activar el módulo, sus conexiones se recuperan desde allí. - Sin api, el espacio ya no tiene Bentho API contratado y PATCH api-config responde 403. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X PATCH https://bentho.ai/api/companies/mi-espacio/modules \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "modules": [ "rag-chat", "documents", "api", "fuentes", "landing" ] }' ``` ■ 200 OKejemplo, con los campos del código ``` { "modules": ["rag-chat", "documents", "api", "fuentes", "landing"], "cableado": [] } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # documents/{archivo} · descargar https://dev.bentho.ai/api/document-descargar/ GET/api/rag/{espacio}/documents/{archivo} # Descarga un documento El archivo tal como lo subiste. La respuesta es el archivo, no JSON. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… Los nombres, en GET documents. ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). | archivo | string | SÍ | El nombre exacto, como sale en GET documents, codificado para URL. Sin «/», «\» ni «..». ## RESPUESTA 200 200 con el archivo en el cuerpo. Las cabeceras que importan: | | CAMPO | TIPO | QUÉ ES | Content-Type | cabecera | application/pdf para los PDF; text/plain para el resto, también .docx. | Content-Disposition | cabecera | attachment, sin nombre: ponle tú el nombre al guardarlo. | Cache-Control | cabecera | no-store. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 400 | Nombre con barras, puntos o vacío | nombre de archivo no válido. | 404 | No existe o no es .pdf, .docx, .txt ni .md | Documento no encontrado: | 502 | El servicio no respondió: reintenta con espera creciente | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Codifica el nombre: «manual de usuario.pdf» va como manual%20de%20usuario.pdf. - Un .docx llega como text/plain pero con sus bytes de siempre: guárdalo con su extensión. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/rag/mi-espacio/documents/manual_usuario.pdf \ -H "Authorization: Bearer $BENTHO_KEY" \ -o manual_usuario.pdf ``` ■ 200 OKderivado del código ``` HTTP/1.1 200 OK Content-Type: application/pdf Content-Disposition: attachment Cache-Control: no-store %PDF-1.7 … (el archivo) ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # clear_cache https://dev.bentho.ai/api/clear-cache/ DEL/api/rag/{espacio}/clear_cache # Vacía la caché de respuestas Bentho guarda las respuestas buenas y las reusa cuando llega una pregunta casi igual. Vaciarla obliga a responder de nuevo con los documentos de ahora. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | status | string | "success". | message | string | «Caché de respuestas limpiada (memoria y persistente).» ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | Clave de solo lectura | Tu cuenta es de solo lectura en este espacio. | 404 | El espacio no existe | — | 502 | El servicio no respondió: reintenta con espera creciente | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Subir o borrar documentos no la vacía: si quieres que una pregunta ya respondida se responda con lo nuevo, llama a esta ruta cuando indexing_status diga done. - Cambiar la configuración no la necesita: las respuestas guardadas con la configuración anterior dejan de usarse solas. - Solo se guardan respuestas con status_code "1000": un «no tengo información» nunca se reusa. - Tampoco se guardan las respuestas que dan precios, totales, descuentos, envíos, stock o disponibilidad: se responden de nuevo cada vez, con los datos de ese momento. Cambiar un precio o el inventario no pide vaciar la caché. - Cuánto tiene que parecerse una pregunta para reusar la respuesta lo decide cache_threshold en config. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X DELETE https://bentho.ai/api/rag/mi-espacio/clear_cache \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKderivado del código ``` { "status": "success", "message": "Caché de respuestas limpiada (memoria y persistente)." } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # config · leer https://dev.bentho.ai/api/config-leer/ GET/api/rag/{espacio}/config # Lee cómo responde el Cerebro La configuración del asistente del espacio, qué campos puede cambiar tu clave y dentro de qué rangos. Es el mismo sobre que devuelve PATCH config. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… Para cambiarla: PATCH config. ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | config | object | La configuración del espacio. Los campos tuyos son los de editables (tabla de abajo). | editables | string[] | Los campos que puede cambiar ESTA clave, por orden alfabético. Con lee: []. | rangos | object | Por campo: { tipo, min?, max?, largo_max?, opciones? }. Valida con esto antes del PATCH. ## LOS CAMPOS DE CONFIG QUE PUEDES CAMBIAR | | CAMPO | TIPO | QUÉ ES | name | string | El nombre del asistente. Hasta 80 caracteres; no puede quedar vacío. | system_prompt | string | Sus instrucciones: cómo se presenta y cómo responde. Hasta 8000 caracteres. | refusal_message | string | Lo que responde con 1001. Hasta 1000 caracteres. | citation_label | string | El rótulo del pie de fuentes («Referencia»). Hasta 40; no puede quedar vacío. | strict_source_only | bool | Solo contesta con lo que dicen los documentos. | temperature | float | 0 – 0,5. Más alto, más variación al redactar. | num_predict | int | 512 – 4096. Hasta dónde puede alargarse una respuesta. | relevance_threshold | float | 0 – 0,75. Por debajo, un fragmento no cuenta como relevante. | nli_entail_min | float | 0,4 – 0,85. Cuánto tiene que apoyar el texto a cada frase. | nli_contradiction_faith_exempt | float | 0,5 – 1,01. Si la respuesta está respaldada al menos en esta proporción, una contradicción aislada no la anula: sale con warning. 1,01 = toda contradicción da 1001. | verify_then_release | bool | Verifica la respuesta entera antes de soltarla. En el stream llega en un solo chunk. | numeric_check | bool | Comprueba las cifras de la respuesta contra los documentos. | retry_contradictory_refusal | bool | Si hay material suficiente y aun así contesta que no tiene información, lo intenta una vez más. | bm25_weight · semantic_weight | float | 0 – 1 cada uno. Peso de la búsqueda por palabras y por significado; no pueden ser los dos 0. | rerank_top_n | int | 1 – 10 fragmentos tras reordenar. | rerank_score_threshold | float | 0 – 0,75. Descarta los fragmentos que puntúan menos al reordenar. 0 = ninguno. | condense_questions | bool | Reescribe las preguntas de seguimiento («¿y cuánto cuesta?») como preguntas completas antes de buscar. | query_translation | bool | Traduce la pregunta para buscar en documentos de otro idioma. Tarda algo más. | document_aliases | [{ aliases, source_substr }] | Otros nombres con los que se nombra un documento. Hasta 50, cada uno con hasta 20 nombres de 80 caracteres; source_substr es parte del nombre del archivo. | citation_max | int | 0 – 5 fuentes citadas por respuesta. | cache_threshold | float | 0 – 0,1. Cuánto puede diferir una pregunta de otra ya respondida para reusar su respuesta: más bajo, más estricto. Las que dan precios o stock nunca se reusan. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 404 | El espacio no existe | — | 502 | El servicio no respondió: reintenta con espera creciente | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - config trae los campos que puedes cambiar y dos más que no: reliability_profile (standard o critical, el nivel de exigencia del espacio) y rerank (si repasa lo encontrado). El resto del motor lo gestiona Bentho y no sale. - Vale con una clave lee, pero entonces editables llega vacío. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/rag/mi-espacio/config \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "config": { "name": "Tienda Aurora", "temperature": 0.2, "citation_max": 3, "strict_source_only": true, "refusal_message": "No dispongo de esa información en los documentos disponibles.", … }, "editables": ["bm25_weight", "cache_threshold", "citation_label", "citation_max", …], "rangos": { "temperature": { "tipo": "float", "min": 0.0, "max": 0.5 }, "citation_max": { "tipo": "int", "min": 0, "max": 5 }, "name": { "tipo": "str", "largo_max": 80 }, … } } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # api-config · leer https://dev.bentho.ai/api/api-config-leer/ GET/api/companies/{espacio}/api-config # Lee los límites, la cuota y las IPs del espacio Cómo está la API del espacio, cuánto se ha usado hoy y qué claves llegan a él, en una sola lectura. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… Para cambiarla: PATCH api-config. ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | config | object | activa, solicitudes_por_segundo, rafaga, cuota_diaria e ips_permitidas, con los valores por defecto en lo que nunca cambiaste. | techo_por_minuto | int | El tope fijo por clave en 60 s (120). Nada del espacio lo supera. | uso | object | Hoy (UTC): fecha, solicitudes, atendidas, rechazadas y por_motivo (apagada, ip, cuota, tasa). | claves | array | Las claves de tu usuario que llegan a este espacio, sin secreto: { id, nombre, email, espacios, permiso, creado, ultimo_uso, revocado, llamadas, por_ruta }. | contratado | bool | Si el espacio tiene el módulo «Bentho API» (api). ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | La API del espacio está apagada: una clave ya no puede leerla | La API de este espacio está apagada. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - uso suma todas las claves del espacio desde las 00:00 UTC. Las rechazadas por IP o por API apagada no gastan cuota. - Las cifras de uso sirven para seguir el tráfico, no para facturar: pueden volver a cero si el servicio se reinicia. Lo que gastas está en la bolsa. - claves incluye las revocadas (revocado: true). llamadas y por_ruta cuentan las de la clave en todos sus espacios. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/companies/mi-espacio/api-config \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "config": { "activa": true, "solicitudes_por_segundo": 2, "rafaga": 120, "cuota_diaria": null, "ips_permitidas": [] }, "techo_por_minuto": 120, "uso": { "fecha": "2026-10-03", "solicitudes": 1840, "atendidas": 1832, "rechazadas": 8, "por_motivo": { "tasa": 8 } }, "claves": [ { "id": "4f1a7e3c9b2d8a06", "nombre": "ERP producción", "email": "ana@example.com", "espacios": ["mi-espacio"], "permiso": "lee", "creado": "2026-09-20T15:04:11+00:00", "ultimo_uso": "2026-10-03T12:30:02+00:00", "revocado": false, "llamadas": 1832, "por_ruta": { "/api/rag/{company}/conversations": 1790, … } } ], "contratado": true } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # cuenta https://dev.bentho.ai/api/cuenta/ GET/api/companies/{espacio}/cuenta # La cuenta de tu espacio La cuenta que agrupa el espacio: su plan, hasta cuándo está pagada y si se atiende. De aquí sale el id de la bolsa y de los cierres. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | cuenta | object | null | null = el espacio no está en ninguna cuenta (se atiende igual). | cuenta.id | string | El {cuenta} de la bolsa y los cierres. | cuenta.nombre | string | El nombre de la cuenta. | cuenta.espacios | string[] | Los espacios que comparten la bolsa de créditos. | cuenta.plan_id | string | null | El plan: crecimiento, por-uso… | cuenta.pagado_hasta | string | null | Hasta qué día está pagada (AAAA-MM-DD). | cuenta.estado | string | activa o suspendida. | cuenta.creditos | int | Solo si se acordaron créditos propios: mandan sobre los del plan. | cuenta.ancla_ciclo | string | Solo si la tiene: el día que ancla el ciclo mensual de créditos. | veredicto.estado | string | activa, en_gracia, suspendida o sin_cuenta. | veredicto.motivo | string | al_dia, sin_fecha_de_pago, pago_vencido, suspension_comercial o sin_cuenta. | veredicto.dias_de_gracia | int | En en_gracia, los días que quedan antes de suspender. | veredicto.se_atiende | bool | false = las rutas del espacio responden 402. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no está al alcance de la clave | Sin acceso a este espacio. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Un espacio sin cuenta devuelve cuenta: null y el veredicto sin_cuenta, no 404. - Cuando vence pagado_hasta se sigue atendiendo 7 días (en_gracia); después, suspendida y las rutas del espacio responden 402. - Los créditos que quedan no están aquí: están en la bolsa. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/companies/mi-espacio/cuenta \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "cuenta": { "id": "mi-cuenta", "nombre": "Tienda Aurora", "espacios": ["mi-espacio"], "plan_id": "crecimiento", "pagado_hasta": "2026-10-15", "estado": "activa" }, "veredicto": { "estado": "activa", "motivo": "al_dia", "pagado_hasta": "2026-10-15", "dias_de_gracia": 0, "se_atiende": true } } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # cierres https://dev.bentho.ai/api/cierres/ GET/api/cuentas/{cuenta}/cierres # Lo gastado en cada ciclo cerrado Los créditos que gastó la cuenta en cada ciclo mensual ya cerrado, del más antiguo al más reciente. En el plan por uso, es lo que se factura cada mes. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | cuenta | string | SÍ | El id de la cuenta. Desde un espacio: GET …/cuenta → cuenta.id. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | cuenta · plan_id | string · string | null | La cuenta y su plan. | cierres | array | Un elemento por ciclo cerrado. [] si aún no se ha cerrado ninguno. | cierres[].inicio · fin | string | Primer y último día del ciclo, los dos incluidos (AAAA-MM-DD). | cierres[].creditos | string | Los créditos gastados. Decimal como texto, 4 decimales: "152.3071". | cierres[].espacios | string[] | Los espacios que se sumaron. | cierres[].completa | bool | false = la cifra es un suelo: se gastó al menos eso. | cierres[].modulos_mudos | string[] | Los módulos que no respondieron al medir: su gasto no está en la cifra. | cierres[].llamadas_sin_reporte | int | Llamadas cuyo gasto no quedó medido. Más de 0 = la cifra es un suelo. | cierres[].cerrado_en | string | Cuándo se escribió el cierre (ISO 8601, UTC). ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | La cuenta no es tuya | Sin acceso a esta cuenta. | 404 | No existe | Cuenta no encontrada. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Cada ciclo se cierra el primer día del siguiente. Un cierre con completa: false todavía puede cambiar: Bentho lo vuelve a medir en los días siguientes al fin del ciclo. - Lee lo ya escrito, así que responde rápido. Para el ciclo en curso, usa la bolsa. - creditos es texto, no número, como en la bolsa: conviértelo con un decimal de verdad. - Solo se cierran las cuentas con ciclo: las que tienen ancla_ciclo o pagado_hasta (ver cuenta). CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/cuentas/mi-cuenta/cierres \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "cuenta": "mi-cuenta", "plan_id": "crecimiento", "cierres": [ { "inicio": "2026-08-15", "fin": "2026-09-14", "creditos": "152.3071", "espacios": ["mi-espacio"], "completa": true, "modulos_mudos": [], "llamadas_sin_reporte": 0, "cerrado_en": "2026-09-15T00:03:41+00:00" } ] } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Fuentes · Armar ficha https://dev.bentho.ai/api/fuentes/ficha-armar/ POST/api/mod/fuentes/{espacio}/fichas # Arma una ficha Le das un tema y una plantilla con los campos que quieres. Bentho busca en la web pública, lee las páginas y llena cada campo con el dato, la página de la que sale y la frase que lo respalda. Al terminar, la ficha se publica en los documentos del espacio. - PERMISOescribe - CRÉDITOSgasta créditos - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Fuentes. ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | tema | string | SÍ | De qué trata la ficha, de 2 a 120 caracteres. | plantilla | string | SÍ | Los campos, en markdown: ## por sección y una línea - Nombre: qué buscar por campo. Su forma, en la guía. | paginas | integer | no | Cuántas páginas leer como mucho, de 1 a 5000. Por defecto 20. | urls | string[] | no | Páginas que ya conoces sobre el caso, en HTTPS (una http se acepta, pero no se lee). Se leen además de lo que encuentre, y marcan dónde seguir buscando. | como | object | no | Cómo buscar: ver como. ## RESPUESTA 202 La ficha recién creada, en recibido. Guarda su id. | | CAMPO | TIPO | QUÉ ES | id · tema | string | El id y el tema. | status | string | Su estado: ver estados. | pages | integer | Páginas leídas. | error | string | Por qué falló, en una frase: No se encontró ninguna página para el tema, No se pudo buscar el tema, La búsqueda no devolvió páginas o El buscador no respondió; cualquier otro fallo, No se pudo armar la ficha. Mientras el buscador no responde, la ficha vuelve a la cola y dice esperando al buscador. Vacío si no falló. | cuenta | string | La cuenta que la pidió. | created_at · updated_at | number | Segundos desde 1970, con decimales. | urls | string[] | Las que diste al armarla. | como | object | Las opciones con que se arma: las de como que mandaste. ## COMO | | CAMPO | TIPO | QUÉ ES | idioma | string | El idioma de la ficha y de la búsqueda. Por defecto "es". | dominios | string[] | Lee solo páginas de estos dominios (hasta 50). Vacío: toda la web pública. | rondas | integer | Vueltas de búsqueda, de 1 a 8 (por defecto 6). Cada una busca solo los campos que siguen vacíos. | paginas_por_ronda | integer | Páginas nuevas que lee cada vuelta, de 1 a 12 (por defecto 4). | instrucciones | string | Indicaciones para buscar, hasta 500 caracteres: «solo fuentes oficiales», «precios en pesos colombianos». | coherencia | boolean | Una ficha, un caso: descarta los datos de páginas que hablan de otro caso parecido. Por defecto false. | cierre_externo | boolean | Al final, una búsqueda externa intenta llenar los campos que siguen vacíos. Si trae una página que respalda el dato, se cita como cualquier otro; si no, el campo sale marcado «completado por búsqueda externa, sin página que lo respalde». Gasta créditos a tarifa externa. Si no responde, la ficha se arma sin ella. Por defecto false. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Fuentes | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 402 | La cuenta agotó sus créditos del ciclo | La cuenta de este espacio agotó sus créditos de este ciclo. | 422 | La plantilla no tiene ninguna sección con campos | La plantilla no tiene secciones con campos | 422 | Un campo fuera de su rango o de su tipo (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Responde enseguida: el trabajo sigue en segundo plano y suele tardar unos minutos. Consulta el estado cada 10 a 15 s. - Armar otra ficha con el mismo tema la reemplaza en los documentos del espacio: queda una versión nueva del mismo documento. - Si la web abierta da menos de la mitad de los campos, aportar 2 a 4 urls concretas del caso suele subir cuántos se llenan. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/fuentes/mi-espacio/fichas \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "tema": "Café de especialidad del Huila", "plantilla": "# Café\n\n## Origen\n- Altitud: metros sobre el nivel del mar\n- Variedades: las que más se cultivan\n\n## Taza\n- Notas: aroma y sabor\n- Acidez: cómo la describen los catadores\n", "paginas": 6 }' ``` ■ 202 ACCEPTEDejemplo, con los campos del código ``` { "id": "8d4e1b7a2c9f4e6d8b3a5c7e9f1d2b4a", "tema": "Café de especialidad del Huila", "status": "recibido", "pages": 0, "error": "", "cuenta": "ana@example.com", "created_at": 1790000000.12, "updated_at": 1790000000.12, "urls": [], "como": { "idioma": "es", "dominios": [], "rondas": 6, "paginas_por_ronda": 4, "instrucciones": "", "coherencia": false, "cierre_externo": false } } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Fuentes · Estado de ficha https://dev.bentho.ai/api/fuentes/ficha/ GET/api/mod/fuentes/{espacio}/fichas/{ficha} # El estado de una ficha La ficha con su estado. La ruta que consultas mientras se arma. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Fuentes. | ficha | string | SÍ | El id de la ficha: el que devolvió armarla (32 caracteres hexadecimales). ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | id · tema | string | El id y el tema. | status | string | Su estado: ver estados. | pages | integer | Páginas leídas. | error | string | Por qué falló, en una frase: No se encontró ninguna página para el tema, No se pudo buscar el tema, La búsqueda no devolvió páginas o El buscador no respondió; cualquier otro fallo, No se pudo armar la ficha. Mientras el buscador no responde, la ficha vuelve a la cola y dice esperando al buscador. Vacío si no falló. | cuenta | string | La cuenta que la pidió. | created_at · updated_at | number | Segundos desde 1970, con decimales. | urls | string[] | Las que diste al armarla. | como | object | Las opciones con que se arma: las de como que mandaste. | markdown | string | La ficha, cuando está publicada. Vacío antes. ## ESTADOS DE UNA FICHA | | CAMPO | TIPO | QUÉ ES | recibido | en curso | En la cola, esperando su turno. | armando | en curso | Buscando, leyendo y llenando campos. | publicado | terminada | Armada. Lo que encontró, en GET …/markdown; si ya está en los documentos del espacio, en traza.publicacion. | fallido | terminada | No se pudo armar: mira error. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Fuentes | módulo no habilitado para este espacio. | 404 | No hay una ficha con ese id en el espacio | La ficha no existe Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Consulta cada 10 a 15 s. Para cuando status sea publicado o fallido. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/fuentes/mi-espacio/fichas/8d4e1b7a2c9f4e6d8b3a5c7e9f1d2b4a \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "id": "8d4e1b7a2c9f4e6d8b3a5c7e9f1d2b4a", "tema": "Café de especialidad del Huila", "status": "armando", "pages": 0, "error": "", "cuenta": "ana@example.com", "created_at": 1790000000.12, "updated_at": 1790000003.4, "urls": [], "como": { "idioma": "es", "rondas": 6, "paginas_por_ronda": 4, "cierre_externo": false, … }, "markdown": "" } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Fuentes · Ficha en markdown https://dev.bentho.ai/api/fuentes/ficha-markdown/ GET/api/mod/fuentes/{espacio}/fichas/{ficha}/markdown # La ficha armada y cómo se armó El markdown de la ficha y su traza: qué páginas leyó, cuántos campos llenó, por qué paró de buscar y si se publicó. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Fuentes. | ficha | string | SÍ | El id de la ficha: el que devolvió armarla (32 caracteres hexadecimales). ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | tema | string | El tema. | markdown | string | La ficha: cada campo con su valor, su Fuente y su Cita literal. Los vacíos dicen «sin respaldo en las páginas leídas». | pages | integer | Páginas leídas. | traza.campos · llenos | integer | Campos de la plantilla y cuántos llenó. | traza.completitud | number | La fracción llena, de 0 a 1. | traza.paginas[] | { url, tipo, caracteres, pasajes, sobre_tema } | Cada página leída y si trataba el tema. | traza.rondas[] | { ronda, consultas, nuevas } | Cada vuelta de búsqueda: qué buscó y las URLs nuevas que eligió leer. | traza.paginas_leidas | integer | Las páginas que leyó. | traza.paginas_con_texto | integer | Las que tenían algo útil. | traza.motivo_cierre | string | Por qué dejó de buscar: completa, rondas, paginas, tiempo… | traza.sugerencia | string | Solo si llenó menos de la mitad sin urls tuyas: qué aportar. | traza.publicacion | { status, document_id, version, unidades } | Si llegó a los documentos del espacio: published, sin_datos (ningún campo lleno, no publica nada), fallida (todavía no llegó: se reintenta sola, cada vez más espaciado, hasta lograrlo) o rechazada (no se publicará: vuelve a armarla). Reintentar no gasta créditos. | traza.segundos | number | Lo que tardó en armarse. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Fuentes | módulo no habilitado para este espacio. | 404 | No hay una ficha con ese id en el espacio | La ficha no existe | 409 | La ficha no está en publicado (aún se arma, o falló) | La ficha todavía no está armada Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - En los documentos del espacio la ficha se llama ficha-.md y va cortada en trozos que se entienden solos, para que Bentho la use al responder. - La traza trae más campos de uso interno: no te apoyes en ellos. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/fuentes/mi-espacio/fichas/8d4e1b7a2c9f4e6d8b3a5c7e9f1d2b4a/markdown \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "tema": "Café de especialidad del Huila", "markdown": "# Café de especialidad del Huila\n\n> Ficha armada por Fuentes el 2026-10-01: 3 de 4 campos con respaldo, de 6 páginas leídas. …\n\n## Origen\n\n- **Altitud:** entre 1.200 y 2.000 m s. n. m.\n - Fuente: https://example.com/huila/cafe\n - Cita: «se cultiva entre 1.200 y 2.000 metros»\n…", "pages": 6, "traza": { "campos": 4, "llenos": 3, "completitud": 0.75, "paginas": [{ "url": "https://example.com/huila/cafe", "tipo": "html", "caracteres": 18240, "pasajes": 21, "sobre_tema": true }, …], "rondas": [{ "ronda": 1, "consultas": ["café Huila altitud variedades"], "nuevas": ["https://example.com/huila/cafe", …] }, …], "paginas_leidas": 6, "paginas_con_texto": 5, "motivo_cierre": "rondas", "publicacion": { "status": "published", "document_id": "…", "version": 1, "unidades": 5 }, "segundos": 84.2, … }, … } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Fuentes · Fichas https://dev.bentho.ai/api/fuentes/fichas/ GET/api/mod/fuentes/{espacio}/fichas # Las fichas del espacio Las últimas fichas, la más reciente primero, sin plantilla ni markdown. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Fuentes. | limite | int, query | no | Cuántas, de 1 a 200; fuera de ese rango se ajusta al extremo más cercano. Por defecto 50. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | fichas | array | La más reciente primero. Cada una con id, tema, status, pages, error, cuenta, created_at y updated_at, como en el estado. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Fuentes | módulo no habilitado para este espacio. | 422 | limite no es un número entero | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/fuentes/mi-espacio/fichas \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKderivado del código ``` { "fichas": [ { "id": "8d4e1b7a2c9f4e6d8b3a5c7e9f1d2b4a", "tema": "Café de especialidad del Huila", "status": "publicado", "pages": 6, "error": "", "cuenta": "ana@example.com", "created_at": 1790000000.12, "updated_at": 1790000084.3 } ] } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Fuentes · Crear fuente https://dev.bentho.ai/api/fuentes/fuente-crear/ POST/api/mod/fuentes/{espacio}/fuentes # Crea una fuente Una fuente son páginas o dominios que Bentho lee y publica en los documentos del espacio cada vez que la lanzas. Crearla no la lee: para eso, lánzala. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Fuentes. ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | nombre | string | SÍ | Para reconocerla, de 1 a 160 caracteres. | tema | string | no | De qué tratan las páginas, hasta 400 caracteres. | urls | string[] | UNA DE LAS DOS | Las páginas que se leen, hasta 50. | dominios | string[] | UNA DE LAS DOS | Dominios que se recorren, hasta 50. | campos | [{ nombre, descripcion }] | no | Hasta 20 datos que sacar de cada página. nombre: letras, números, _ y espacios, hasta 80; descripcion, hasta 400. | limites | { paginas, profundidad } | no | Páginas como mucho, de 1 a 50 (por defecto 20), y cuántos enlaces seguir desde cada una, 1 o 2 (por defecto 1). ## RESPUESTA 201 La fuente, con estado lista. | | CAMPO | TIPO | QUÉ ES | id · nombre · tema | string | Los que diste al crearla. | urls · dominios | string[] | Los tuyos. | campos · limites | array · object | También los tuyos (limites, con sus valores por defecto). | estado | string | El de la última corrida, o lista si nunca se lanzó: ver estados. | paginas | integer | Páginas publicadas en la última corrida. | error | string | Por qué falló, en una frase (Solo se aceptan URLs https, El destino no está en los dominios de la fuente, No se pudo leer la página…) o No se pudo completar la corrida. Vacío si no falló. | run_id | string | null | La última corrida; null si ninguna. | actualizado | number | Segundos desde 1970, con decimales. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Fuentes | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 402 | La cuenta agotó sus créditos del ciclo | La cuenta de este espacio agotó sus créditos de este ciclo. | 422 | Sin urls ni dominios | La fuente necesita URLs o dominios | 422 | Un campo fuera de su rango o de su tipo (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Crearla no gasta créditos, pero con la bolsa agotada responde 402 igual que lanzarla: no tendrías con qué leerla. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/fuentes/mi-espacio/fuentes \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "nombre": "Normativa de etiquetado", "tema": "etiquetado de alimentos", "urls": [ "https://example.com/normativa/etiquetado" ], "campos": [ { "nombre": "Plazo de adaptación", "descripcion": "fecha límite" } ] }' ``` ■ 201 CREATEDejemplo, con los campos del código ``` { "id": "c27a9e4f1b8d4a6c9e2f7b3d5a1c8e40", "nombre": "Normativa de etiquetado", "tema": "etiquetado de alimentos", "urls": ["https://example.com/normativa/etiquetado"], "dominios": [], "campos": [{ "nombre": "Plazo de adaptación", "descripcion": "fecha límite" }], "limites": { "paginas": 20, "profundidad": 1 }, "estado": "lista", "paginas": 0, "error": "", "run_id": null, "actualizado": 1790000420.5 } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Fuentes · Lanzar fuente https://dev.bentho.ai/api/fuentes/fuente-lanzar/ POST/api/mod/fuentes/{espacio}/fuentes/{fuente}/lanzar # Lanza una fuente Lee las páginas de la fuente y publica lo leído en los documentos del espacio. Si ya hay una corrida en la cola, te devuelve esa. - PERMISOescribe - CRÉDITOSgasta créditos - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Fuentes. | fuente | string | SÍ | El id de la fuente: el que devolvió crearla (32 caracteres hexadecimales). ## RESPUESTA 202 | | CAMPO | TIPO | QUÉ ES | run_id | string | La corrida. | status | "recibido" | En la cola. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Fuentes | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 402 | La cuenta agotó sus créditos del ciclo | La cuenta de este espacio agotó sus créditos de este ciclo. | 404 | No hay una fuente con ese id en el espacio | La fuente no existe Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Sigue la corrida con GET fuentes/{fuente}: su estado pasa a publicado, fallido o cancelado. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X POST https://bentho.ai/api/mod/fuentes/mi-espacio/fuentes/c27a9e4f1b8d4a6c9e2f7b3d5a1c8e40/lanzar \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 202 ACCEPTEDderivado del código ``` { "run_id": "4b0e7c2a9d1f4e8a6c3b5d7f9e2a1c04", "status": "recibido" } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Fuentes · Fuente https://dev.bentho.ai/api/fuentes/fuente/ GET/api/mod/fuentes/{espacio}/fuentes/{fuente} # Una fuente y su última corrida La fuente, con el estado de su última corrida. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Fuentes. | fuente | string | SÍ | El id de la fuente: el que devolvió crearla (32 caracteres hexadecimales). ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | id · nombre · tema | string | Los que diste al crearla. | urls · dominios | string[] | Los tuyos. | campos · limites | array · object | También los tuyos (limites, con sus valores por defecto). | estado | string | El de la última corrida, o lista si nunca se lanzó: ver estados. | paginas | integer | Páginas publicadas en la última corrida. | error | string | Por qué falló, en una frase (Solo se aceptan URLs https, El destino no está en los dominios de la fuente, No se pudo leer la página…) o No se pudo completar la corrida. Vacío si no falló. | run_id | string | null | La última corrida; null si ninguna. | actualizado | number | Segundos desde 1970, con decimales. ## ESTADOS DE UNA FUENTE | | CAMPO | TIPO | QUÉ ES | lista | sin lanzar | Creada; todavía no se ha lanzado. | recibido | en curso | En la cola, esperando su turno. | adquirido · normalizado | en curso | Leyendo las páginas y limpiándolas. | estructurado · revisado | en curso | Sacando los campos y revisando antes de publicar. | publicado | terminada | Lo leído está en los documentos del espacio. | fallido | terminada | No se pudo: mira error. | cancelado | terminada | La cancelaste. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Fuentes | módulo no habilitado para este espacio. | 404 | No hay una fuente con ese id en el espacio | La fuente no existe Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/fuentes/mi-espacio/fuentes/c27a9e4f1b8d4a6c9e2f7b3d5a1c8e40 \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "id": "c27a9e4f1b8d4a6c9e2f7b3d5a1c8e40", "nombre": "Normativa de etiquetado", "tema": "etiquetado de alimentos", "urls": ["https://example.com/normativa/etiquetado"], "dominios": [], "campos": [{ "nombre": "Plazo de adaptación", "descripcion": "fecha límite" }], "limites": { "paginas": 20, "profundidad": 1 }, "estado": "publicado", "paginas": 12, "error": "", "run_id": "4b0e7c2a9d1f4e8a6c3b5d7f9e2a1c04", "actualizado": 1790000420.5 } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Fuentes · Fuentes https://dev.bentho.ai/api/fuentes/fuentes/ GET/api/mod/fuentes/{espacio}/fuentes # Las fuentes del espacio Todas las fuentes, la más reciente primero, cada una con su última corrida. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Fuentes. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | fuentes | array | La más reciente primero. Cada una, con los campos de GET fuentes/{fuente}. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Fuentes | módulo no habilitado para este espacio. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/fuentes/mi-espacio/fuentes \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKderivado del código ``` { "fuentes": [ { "id": "c27a9e4f1b8d4a6c9e2f7b3d5a1c8e40", "nombre": "Normativa de etiquetado", "estado": "publicado", … } ] } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Fuentes · Cancelar fuente https://dev.bentho.ai/api/fuentes/fuente-cancelar/ POST/api/mod/fuentes/{espacio}/fuentes/{fuente}/cancelar # Cancela la corrida de una fuente Para la última corrida. Si aún estaba en la cola, se cancela ya; si estaba leyendo, para al acabar el paso en curso. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Fuentes. | fuente | string | SÍ | El id de la fuente: el que devolvió crearla (32 caracteres hexadecimales). ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | run_id | string | La corrida. | status | string | cancelado si ya paró; si no, su estado actual: consulta la fuente. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Fuentes | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 404 | No hay una fuente con ese id en el espacio | La fuente no existe | 404 | La fuente nunca se lanzó | La fuente no tiene corridas Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - No gasta créditos y funciona aunque la bolsa esté agotada. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X POST https://bentho.ai/api/mod/fuentes/mi-espacio/fuentes/c27a9e4f1b8d4a6c9e2f7b3d5a1c8e40/cancelar \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKderivado del código ``` { "run_id": "4b0e7c2a9d1f4e8a6c3b5d7f9e2a1c04", "status": "cancelado" } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Fuentes · Borrar fuente https://dev.bentho.ai/api/fuentes/fuente-borrar/ DEL/api/mod/fuentes/{espacio}/fuentes/{fuente} # Borra una fuente Borra la fuente, para la corrida que estuviera en la cola y quita de los documentos del espacio lo que publicó. No se puede deshacer. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Fuentes. | fuente | string | SÍ | El id de la fuente: el que devolvió crearla (32 caracteres hexadecimales). ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | id | string | La fuente borrada. | status | "deleted" | Hecho. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Fuentes | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 404 | No hay una fuente con ese id en el espacio | La fuente no existe Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X DELETE https://bentho.ai/api/mod/fuentes/mi-espacio/fuentes/c27a9e4f1b8d4a6c9e2f7b3d5a1c8e40 \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKderivado del código ``` { "id": "c27a9e4f1b8d4a6c9e2f7b3d5a1c8e40", "status": "deleted" } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Fuentes · Página leída https://dev.bentho.ai/api/fuentes/ficha-pagina/ GET/api/mod/fuentes/{espacio}/fichas/{ficha}/pagina # El texto de una página que leyó la ficha Para comprobar de dónde sale un dato: el texto que la ficha leyó de una de sus páginas, tal como lo guardó. Solo las páginas de la traza de esa ficha. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Fuentes. | ficha | string | SÍ | El id de la ficha: el que devolvió armarla (32 caracteres hexadecimales). | url | string, query | SÍ | La dirección de la página, exactamente como sale en traza.paginas[] del markdown. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | url · final | string | La pedida y a la que llevó. | tipo · titulo | string | El tipo de documento y su título. | texto | string | Con los correos, los teléfonos y los números de documento enmascarados, como se publica. | caracteres | integer | Cuántos tiene el texto. | sobre_tema | boolean | Si la ficha la tomó por una página sobre el tema. | guardada | number | Cuándo se guardó, en segundos desde 1970. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Fuentes | módulo no habilitado para este espacio. | 404 | No hay una ficha con ese id en el espacio | La ficha no existe | 404 | Esa dirección no está en la traza de la ficha | La ficha no leyó esa página | 404 | La página ya no está guardada | La página no quedó guardada | 422 | Un campo fuera de su rango o de su tipo (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - El texto es el que leyó la ficha, no la página de hoy: si cambió después, aquí sigue la versión que se leyó mientras siga guardada. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/fuentes/mi-espacio/fichas/8d4e1b7a2c9f4e6d8b3a5c7e9f1d2b4a/pagina?url=https%3A%2F%2Fexample.com%2Fcafe%2Fhuila \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "url": "https://example.com/cafe/huila", "final": "https://example.com/cafe/huila", "tipo": "html", "titulo": "Café del Huila: origen y taza", "texto": "El café del Huila se cultiva entre 1200 y 2000 metros…", "caracteres": 5320, "sobre_tema": true, "guardada": 1790000042.7 } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Fuentes · Capturas https://dev.bentho.ai/api/fuentes/fuente-capturas/ GET/api/mod/fuentes/{espacio}/fuentes/{fuente}/capturas # Las capturas de una fuente Las páginas que trajeron las corridas de la fuente, de la más nueva a la más vieja, sin su contenido. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Fuentes. | fuente | string | SÍ | El id de la fuente: el que devolvió crearla (32 caracteres hexadecimales). ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | capturas | array | Hasta 200. Cada una con id, run_id (la corrida), url, sha256 (la huella de lo traído), fetched_at (ISO 8601, UTC) y caracteres. El contenido, en una captura. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Fuentes | módulo no habilitado para este espacio. | 404 | No hay una fuente con ese id en el espacio | La fuente no existe Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Si dos capturas de la misma url tienen el mismo sha256, la página no cambió entre esas corridas. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/fuentes/mi-espacio/fuentes/c27a9e4f1b8d4a6c9e2f7b3d5a1c8e40/capturas \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "capturas": [ { "id": "e7b2c4d6f8a1435c9b0d2e4f6a8c1b37", "run_id": "4b0e7c2a9d1f4e8a6c3b5d7f9e2a1c04", "url": "https://example.com/normativa/etiquetado", "sha256": "9f2c…", "fetched_at": "2026-10-05T14:03:11.204512+00:00", "caracteres": 48211 } ] } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Fuentes · Una captura https://dev.bentho.ai/api/fuentes/fuente-captura/ GET/api/mod/fuentes/{espacio}/fuentes/{fuente}/capturas/{captura} # Una captura de una fuente Lo que trajo una corrida de una página, como texto por secciones: el mismo que se publica. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Fuentes. | fuente | string | SÍ | El id de la fuente: el que devolvió crearla (32 caracteres hexadecimales). | captura | string | SÍ | El id de la captura: el de la lista de capturas (32 caracteres hexadecimales). | html | boolean, query | no | true: suma el HTML de la página, también enmascarado. Por defecto false. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | id · run_id · url | string | La captura, su corrida y la página. | sha256 | string | La huella de lo traído. | fetched_at | string | Cuándo, en ISO 8601 (UTC). | texto | string | Con los correos, los teléfonos y los números de documento enmascarados, como se publica. | caracteres | integer | Los del HTML original. | html | string | Solo con html=true. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Fuentes | módulo no habilitado para este espacio. | 404 | No hay una fuente con ese id en el espacio | La fuente no existe | 404 | No hay esa captura en la fuente | La captura no existe Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/fuentes/mi-espacio/fuentes/c27a9e4f1b8d4a6c9e2f7b3d5a1c8e40/capturas/e7b2c4d6f8a1435c9b0d2e4f6a8c1b37 \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "id": "e7b2c4d6f8a1435c9b0d2e4f6a8c1b37", "run_id": "4b0e7c2a9d1f4e8a6c3b5d7f9e2a1c04", "url": "https://example.com/normativa/etiquetado", "sha256": "9f2c…", "fetched_at": "2026-10-05T14:03:11.204512+00:00", "texto": "Etiquetado de alimentos Los productos envasados deben…", "caracteres": 48211 } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Fuentes · Consumo https://dev.bentho.ai/api/fuentes/consumo/ GET/api/mod/fuentes/{espacio}/consumo # Lo que gastó Fuentes, en créditos Los créditos que gastó Fuentes en este espacio al armar fichas y leer fuentes. Para el saldo de la cuenta entera, mira la bolsa de créditos. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Fuentes. | dias | int, query | no | Los últimos N días; por encima de 400 cuenta 400. Sin él (o con 0), manda desde. | desde · hasta | float, query | no | La ventana en segundos desde 1970. Sin desde, desde el principio; sin hasta, hasta ahora. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | modulo · espacio | string | "fuentes" y tu espacio. | dias | integer | null | La ventana que pediste. null: sin dias. | desde · hasta | number | La ventana contada, en segundos desde 1970. desde 0: desde el principio. | total.creditos | string | Decimal como texto, 4 decimales: "3.2100". | total.solicitudes | integer | Cuántas operaciones sumaron ese gasto. Una ficha cuenta varias. | total.fichas | integer | Cuántas fichas distintas gastaron en la ventana. | actualizado | integer | Cuándo se calculó, en segundos desde 1970. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Fuentes | módulo no habilitado para este espacio. | 422 | dias, desde o hasta no son números | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - creditos es texto, no número, para no perder precisión. - Es la misma cuenta que descuenta de la bolsa, al precio de tu contrato. Con dias en 0 o negativo, es como si no lo mandaras. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/fuentes/mi-espacio/consumo?dias=30 \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "modulo": "fuentes", "espacio": "mi-espacio", "dias": 30, "desde": 1787408000.0, "hasta": 1790000001.0, "actualizado": 1790000000, "total": { "creditos": "3.2100", "solicitudes": 41, "fichas": 6 } } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Transcripción · Transcribir audio https://dev.bentho.ai/api/transcripcion/transcribir/ POST/api/mod/transcripcion/{espacio}/transcripciones # Transcribe un audio Subes una nota de voz o un audio y te devuelve lo que se dice, en texto. Contesta cuando termina, en la misma petición: no hay nada que consultar después. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Transcripción. ## CUERPO · MULTIPART/FORM-DATA | | CAMPO | TIPO | REQUERIDO | QUÉ ES | archivo | archivo | SÍ | El audio, uno por petición: hasta 25 MB y 120 segundos. OGG/Opus (las notas de voz de WhatsApp), MP3, M4A, WAV y otros formatos de audio; de un vídeo se transcribe su sonido. | idioma | string | no | El idioma del audio, en código de dos letras: "es", "en", "pt"… Por defecto "es". | duracion_max_s | number | no | Tu propio tope de duración, en segundos (mínimo 1). Un audio más largo da 422 sin transcribirse. Por encima de 120, cuenta 120. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | texto | string | Lo que se dice en el audio. Vacío si no se oye ninguna palabra. | vacio | boolean | true si el audio no tiene voz (silencio o ruido). Es un resultado válido, no un error: no reintentes. | idioma | string | null | El idioma con el que se transcribió. | duracion_s | number | Lo que dura el audio, en segundos, con dos decimales. | latencia_ms | integer | Lo que tardó la transcripción, en milisegundos. ## CÓDIGOS DE ERROR DEL AUDIO | | CAMPO | TIPO | QUÉ ES | demasiado_grande | 413 | El archivo pasa de 25 MB. Recórtalo o comprímelo. | demasiado_largo | 422 | Dura más de 120 s, o más que tu duracion_max_s. Pide a tu usuario un audio más corto. | no_soportado | 422 | No se reconoce como audio, o no trae pista de sonido. No reintentes. | audio_invalido | 422 | Llegó vacío, está dañado o no se pudo leer entero. No reintentes con el mismo. | ocupado | 503 | Hay muchas transcripciones en curso. Reintenta en unos segundos. | motor_no_listo | 503 | El servicio está arrancando. Reintenta en un momento. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Transcripción | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 413 | El archivo pasa de 25 MB | {"codigo": "demasiado_grande", "detalle": "el audio supera 25 MB"} | 422 | Dura más de 120 s, o más que tu duracion_max_s | {"codigo": "demasiado_largo", "detalle": …} | 422 | No es audio, o no trae pista de sonido | {"codigo": "no_soportado", "detalle": …} | 422 | Llegó vacío o no se pudo decodificar | {"codigo": "audio_invalido", "detalle": …} | 422 | Falta el campo archivo (detail es una lista) | — | 503 | Hay muchas transcripciones en curso: reintenta en unos segundos | {"codigo": "ocupado", "detalle": "todas las transcripciones en curso; reintente"} | 503 | El servicio está arrancando: reintenta en un momento | {"codigo": "motor_no_listo", "detalle": …} Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Los errores del audio traen detail como objeto: codigo, para tu programa, y detalle, una frase. Decide por codigo: ver códigos. - Pide una clave escribe, aunque no cambia nada del espacio: es un POST. No gasta créditos. - Bentho no guarda ni el audio ni el texto: te los devuelve y los olvida. Si quieres conservarlos, guárdalos tú. - Suele tardar unos segundos; un audio de dos minutos, algo más. Para varios audios, una petición por cada uno. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/transcripcion/mi-espacio/transcripciones \ -H "Authorization: Bearer $BENTHO_KEY" \ -F "archivo=@nota_de_voz.ogg" \ -F 'idioma=es' ``` ■ 200 OKejemplo, con los campos del código ``` { "texto": "Hola, buenas tardes. Quería saber si les queda café del Huila en grano y cuánto sale el envío a Medellín.", "vacio": false, "idioma": "es", "duracion_s": 7.84, "latencia_ms": 1830, … } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Agente ventas · Conversar https://dev.bentho.ai/api/agente/conversar/ POST/api/mod/agente/{espacio}/conversations/ # Conversa con un cliente Mandas lo que escribió tu cliente y recibes la respuesta del agente de ventas: resuelve dudas con tus documentos, cotiza el envío, arma el pedido y lo cierra. Reusa el session_id en cada mensaje del mismo chat. - PERMISOlee (vale cualquier clave) - CRÉDITOSgasta créditos - AUTENTICACIÓNBearer bth_… En un espacio con Agente ventas, POST conversations del núcleo también llega a este agente, con question, session_id y consent; msg_id solo existe aquí. ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Agente ventas. ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | question | string | SÍ | Lo que escribió tu cliente, tal cual. De 1 a 4000 caracteres: fuera de ese rango no da error, responde 1001 con tu refusal_fuera_dominio. | session_id | string | SÍ, EN LA PRÁCTICA | Tu identificador de la conversación: uno por chat (el número del cliente en tu canal, el id del hilo…). Sin él responde 400. Con él, el agente recuerda el carrito, la ciudad, el método de pago y el pedido abierto. | consent | boolean | no | true si tu cliente autorizó el seguimiento comercial. Solo así se guarda su contacto, y seudonimizado. Por defecto false. | msg_id | string | no | El id del mensaje en tu canal. Si llega dos veces con el mismo session_id (un reintento de tu webhook, un doble toque), la segunda devuelve la misma respuesta sin volver a ejecutar el turno: no agrega dos veces al carrito ni crea dos pedidos. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | response | string | El mensaje para tu cliente, entero. Úsalo si tu canal manda un solo mensaje. | responses | string[] | El mismo texto en uno o dos mensajes (por ejemplo, lo que cambió en el carrito y luego la respuesta). Si tu canal admite varios mensajes seguidos, mándalos en orden. | status_code | "1000" | "1001" | "1000" respondió. "1001" no tenía información para eso o el mensaje no se pudo atender; response trae tu refusal_fuera_dominio. | estado | string | En qué punto va la conversación: ver estados. | cotizacion | object | null | Las cifras de la cotización de este turno, como texto decimal: total, coste_envio, recargos[] y, según el caso, lineas[], subtotal, descuento o tiempo_entrega_dias. null si el turno no cotizó. | handoff | boolean | true: no puede seguir solo (no hay tarifa, pidieron una persona, falló un pago…). Pásale la conversación a alguien de tu equipo. | aviso_privacidad | string | null | Solo en el primer turno: tu aviso de privacidad. Enséñaselo a tu cliente. | opciones | [{ id, nombre }] | Cuando duda entre 2 o 3 productos parecidos y activaste los botones: las opciones, para mostrarlas como botones. El texto de response ya las nombra. | lead_id | string | null | El id del lead si este turno lo registró. Guárdalo con tu conversación. | sources | string[] | Los documentos del espacio en los que se apoyó. Para ti, no para tu cliente. | session_id · tenant_id | string | La sesión que mandaste y el espacio. ## ESTADOS DE LA CONVERSACIÓN | | CAMPO | TIPO | QUÉ ES | saludo | inicio | Primer contacto: aún no hay nada que atender. | descubrimiento | conversando | Charla o preguntas generales, sin intención de compra clara. | rag_producto | conversando | Respondió sobre un producto con lo que dicen tus documentos. | recoleccion_slots | conversando | Le faltan datos para seguir (producto, cantidad, ciudad, método de pago, datos de envío) y los está pidiendo. | cotizacion | venta | Acaba de cotizar: la cifra está en cotizacion. | cierre | venta | Cerrando la compra: confirma el pedido, pide el pago o sigue un pedido abierto. | handoff | persona | Conviene que siga una persona: mira handoff. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 400 | Falta session_id | session_id es obligatorio. | 403 | El espacio no tiene el módulo Agente ventas | módulo no habilitado para este espacio. | 402 | La cuenta agotó sus créditos del ciclo | La cuenta de este espacio agotó sus créditos de este ciclo. | 422 | Falta un campo obligatorio o uno no es de su tipo (detail es una lista) | — | 429 | El espacio mandó demasiadas peticiones seguidas: espera unos segundos | Demasiadas solicitudes; intenta en un momento. | 502 | El agente no respondió a tiempo o no está disponible: reintenta con espera creciente | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Ninguna cifra la inventa: precios, envíos y totales salen de tu catálogo, tus tarifas o tus reglas. Si no puede cotizar, lo dice y marca handoff. - Vale con cualquier clave, también una lee: conversar no cambia nada de tu espacio, aunque el agente sí crea pedidos y leads cuando el cliente compra. - Un turno puede tardar varios segundos, más si cotiza o cierra un pedido. Pon el timeout de tu cliente en 90 s o más; si el agente no termina a tiempo, responde 502. - La sesión recuerda el carrito, la ciudad y el pedido abierto. Tras unas 48 h sin mensajes empieza de cero; para hacerlo antes, reiníciala. - Copia la ruta tal cual, con su barra final. Sin ella la respuesta es un 307 sin destino, no la conversación. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/agente/mi-espacio/conversations/ \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "question": "¿Cuánto cuesta el envío de 2 bolsas a Medellín?", "session_id": "chat-4821", "consent": false, "msg_id": "msg-000123" }' ``` ■ 200 OKejemplo, con los campos del código ``` { "tenant_id": "mi-espacio", "session_id": "chat-4821", "response": "El envío de 2 unidades de Café de origen Huila 500 g a Medellín cuesta $12.000 y llega en 2 a 3 días hábiles. 🚚", "responses": ["El envío de 2 unidades de Café de origen Huila 500 g a Medellín cuesta $12.000 y llega en 2 a 3 días hábiles. 🚚"], "status_code": "1000", "estado": "cotizacion", "cotizacion": { "producto_id": "cafe-de-origen-huila-500-g", "cantidad": 2, "zona_id": "medellin", "coste_envio": "12000.00", "tiempo_entrega_dias": [2, 3], "recargos": [], "total": "12000.00", "tarifa_version": "4f2a9c…", "vigencia": null }, "sources": [], "handoff": false, "aviso_privacidad": null, "lead_id": null, "opciones": [] } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Agente ventas · Conversar (SSE) https://dev.bentho.ai/api/agente/conversar-stream/ POSTSSE/api/mod/agente/{espacio}/conversations/stream/ # La misma conversación, por eventos El mismo cuerpo que conversar, pero la respuesta llega como eventos (SSE): el aviso de privacidad, uno o dos mensajes, el paso a una persona y el cierre con el estado y la cotización. - PERMISOlee (vale cualquier clave) - CRÉDITOSgasta créditos - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Agente ventas. ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | question | string | SÍ | Lo que escribió tu cliente, tal cual. De 1 a 4000 caracteres: fuera de ese rango no da error, responde 1001 con tu refusal_fuera_dominio. | session_id | string | SÍ, EN LA PRÁCTICA | Tu identificador de la conversación: uno por chat (el número del cliente en tu canal, el id del hilo…). Sin él responde 400. Con él, el agente recuerda el carrito, la ciudad, el método de pago y el pedido abierto. | consent | boolean | no | true si tu cliente autorizó el seguimiento comercial. Solo así se guarda su contacto, y seudonimizado. Por defecto false. ## EVENTOS 200 con Content-Type: text/event-stream. Cada evento es una línea data: {…} seguida de una línea en blanco. | | CAMPO | TIPO | QUÉ ES | notice | { type, content } | Solo en el primer turno: tu aviso de privacidad. | chunk | { type, content } | Un mensaje para tu cliente, entero. Llegan uno o dos, en orden. | handoff | { type, reason } | Conviene pasar a una persona. reason dice por qué (soporte:pide_asesor, precio:sin_precio…): regístralo, no decidas por él. | done | { type, status_code, full_response, estado, cotizacion, sources } | Cierre normal, con los mismos campos que en conversar. | error | { type, content } | Algo falló después del 200. content trae un texto para tu cliente. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 400 | Falta session_id | session_id es obligatorio. | 403 | El espacio no tiene el módulo Agente ventas | módulo no habilitado para este espacio. | 402 | Antes de abrir el stream: créditos agotados | — | 429 | El espacio mandó demasiadas peticiones seguidas: espera unos segundos | Demasiadas solicitudes; intenta en un momento. | 502 | El agente no respondió a tiempo o no está disponible: reintenta con espera creciente | — | evento | Evento error: el turno falló después del 200 | No pude procesar tu mensaje en este momento. Intenta de nuevo, por favor. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - El mensaje no llega palabra a palabra: el agente comprueba cada cifra antes de enviarla, así que manda el texto entero. Por esta ruta los eventos llegan juntos al terminar el turno. - Todo lo que se rechaza antes (400, 402, 403, 429) llega como JSON normal, no como evento. - Aquí no hay msg_id: si tu canal reintenta mensajes, usa conversar. - Copia la ruta tal cual, con su barra final. Sin ella la respuesta es un 307 sin destino, no la conversación. CURLPYTHONJAVASCRIPT COPIAR ``` curl -N https://bentho.ai/api/mod/agente/mi-espacio/conversations/stream/ \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "question": "¿Cuánto cuesta el envío de 2 bolsas a Medellín?", "session_id": "chat-4821" }' ``` ■ 200 · EVENTOSejemplo, con los campos del código ``` data: {"type": "chunk", "content": "El envío de 2 unidades de Café de origen Huila 500 g a Medellín cuesta $12.000 y llega en 2 a 3 días hábiles. 🚚"} data: {"type": "done", "status_code": "1000", "full_response": "…", "estado": "cotizacion", "cotizacion": { "total": "12000.00", … }, "sources": []} ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Agente ventas · Comprobante https://dev.bentho.ai/api/agente/comprobante/ POST/api/mod/agente/{espacio}/conversations/evidence # Recibe una imagen del cliente Cuando tu cliente manda una imagen o un PDF en el chat, pásalo aquí. Con un pedido abierto es su comprobante de pago: se registra y el agente contesta en qué quedó el pago. - PERMISOlee (vale cualquier clave) - CRÉDITOSgasta créditos - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Agente ventas. ## CUERPO · MULTIPART/FORM-DATA | | CAMPO | TIPO | REQUERIDO | QUÉ ES | file | archivo | SÍ | La imagen (JPG, PNG…) o el PDF, hasta 10 MB. | session_id | string | SÍ | La conversación en la que llegó: el mismo session_id con el que conversas. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | response | string | El mensaje para tu cliente: pago recibido, en revisión, ilegible… | estado | string | El estado de la conversación, como en conversar. | session_id · tenant_id | string | La sesión y el espacio. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Agente ventas | módulo no habilitado para este espacio. | 402 | La cuenta agotó sus créditos del ciclo | La cuenta de este espacio agotó sus créditos de este ciclo. | 404 | El agente no tiene activado facturacion_activo | Función no disponible. | 409 | No hay un pedido abierto en esa conversación (y no reconoció la imagen) | No hay un pedido en proceso para este chat. | 415 | No es una imagen ni un PDF | tipo de archivo no soportado (imagen o PDF). | 400 | El archivo llegó vacío | archivo vacío. | 413 | El archivo pasa de 10 MB | archivo demasiado grande (máximo … bytes). | 429 | El espacio mandó demasiadas peticiones seguidas: espera unos segundos | Demasiadas solicitudes; intenta en un momento. | 502 | No se pudo registrar el comprobante. detail trae tu mensaje_handoff: enséñaselo a tu cliente y pásalo a una persona | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - La imagen no se guarda en el agente: se pasa al cobro del pedido y se descarta. - Con vision_imagen_activo, una imagen sin pedido abierto no da 409: si es un producto, el agente sigue la conversación; si es un pago, dice que lo vio pero que no hay pedido al que asociarlo. - Si el carrito está completo (productos, ciudad y método de pago) y llega el comprobante, se toma como la confirmación: se crea el pedido y se registra el pago. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/agente/mi-espacio/conversations/evidence \ -H "Authorization: Bearer $BENTHO_KEY" \ -F "file=@comprobante.jpg" \ -F 'session_id=chat-4821' ``` ■ 200 OKejemplo, con los campos del código ``` { "tenant_id": "mi-espacio", "session_id": "chat-4821", "response": "¡Recibí tu comprobante! Lo estamos revisando y te confirmo en cuanto quede aprobado. 💛", "estado": "cierre" } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Agente ventas · Reiniciar sesión https://dev.bentho.ai/api/agente/sesion-reiniciar/ DEL/api/mod/agente/{espacio}/sessions/{sesion} # Reinicia una conversación Borra lo que el agente recuerda de esa conversación (carrito, ciudad, método de pago, pedido abierto e historial). El siguiente mensaje empieza de cero. Los pedidos ya creados no se tocan. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Agente ventas. | sesion | string | SÍ | El session_id de la conversación: el que tú mandas al conversar. Sin barras ni signos de interrogación. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | session_id | string | La que reiniciaste. | reset | true | Siempre true. | historial_cerebro_purgado | integer | Cuántas entradas del historial de respuestas se borraron. 0 es normal. | cerebro_error | string | null | null si se borró todo. Con texto, el reinicio quedó a medias y la conversación puede arrastrar contexto: reintenta. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Agente ventas | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Reiniciar una sesión que no existe no es un error: responde igual. - No borra el lead ni sus datos: el lead sigue en tu embudo. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X DELETE https://bentho.ai/api/mod/agente/mi-espacio/sessions/chat-4821 \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKderivado del código ``` { "session_id": "chat-4821", "reset": true, "historial_cerebro_purgado": 6, "cerebro_error": null } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Agente ventas · Configuración https://dev.bentho.ai/api/agente/config/ GET/api/mod/agente/{espacio}/leads-config # La configuración del agente Todo lo que define cómo vende tu agente: su nombre, su saludo, sus instrucciones, con qué módulos se conecta y cada frase que dice. Si el espacio nunca la tocó, la crea con los valores por defecto. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Agente ventas. ## RESPUESTA 200 La configuración entera. Los campos que te sirven: | | CAMPO | TIPO | QUÉ ES | name | string | El nombre de tu negocio. Sustituye {name} en el saludo y en las instrucciones. Por defecto, el id del espacio. | saludo | string | El primer mensaje de cada conversación. Admite {name}. | modo_venta | boolean | false: atiende y resuelve dudas. true: asesor de venta consultiva que propone siempre el siguiente paso. Por defecto false. | system_prompt | string | Las instrucciones del asistente cuando modo_venta es false. | system_prompt_venta | string | Las instrucciones cuando modo_venta es true. | aviso_privacidad | string | El aviso que acompaña al primer turno (aviso_privacidad en la respuesta). | moneda | string | Cómo se escriben las cifras en los mensajes. Por defecto "COP". No cambia el cálculo. | campos_captura | string[] | Los datos de contacto que pide al registrar un lead. Por defecto ["nombre", "telefono"]. | cotiza_sin_contacto | boolean | Si cotiza antes de tener el contacto del cliente. Por defecto true. | cantidad_default | integer | La cantidad que asume si el cliente no la dice. Por defecto 1. | cantidad_max_por_linea | integer | Desde esta cantidad por producto, en vez de cotizar, pregunta si es un pedido al por mayor. Por defecto 12. | mensaje_handoff | string | Lo que dice cuando pasa la conversación a una persona. | refusal_fuera_dominio | string | Lo que dice cuando no tiene información (status_code 1001). | catalogo_url | string | El enlace a tu catálogo (tienda, PDF, catálogo de WhatsApp). Vacío: no lo ofrece. Lo entrega con mensaje_catalogo. | mensaje_* · msg_* · cta_* | string | Las frases del agente para cada situación (precio, carrito, pago, envío, stock, posventa) y las pregunta_*. Respeta sus {variables}: ver frases. | tenant_id | string | El espacio. ## CONEXIONES CON OTROS MÓDULOS (TIPO · POR DEFECTO) | | CAMPO | TIPO | QUÉ ES | cerebro_activo | boolean · true | Responde las preguntas con los documentos del espacio. Apagado, contesta que no tiene esa información. | comercial_activo | boolean · false | Envíos, recargos y métodos de pago con las reglas del módulo Comercial. Apagado, usa tus tablas de tarifas. | marca_activa | boolean · false | Habla con la voz, el tono y las tácticas del módulo Marca. | facturacion_activo | boolean · false | Crea la orden y el cobro cuando el cliente confirma, y recibe el comprobante. Sin él, no cierra pedidos ni acepta comprobantes. | inventario_activo | boolean · false | Mira el stock antes de ofrecer y avisa si algo está agotado. | inventario_delegado | boolean · false | El stock lo lleva el módulo Productos. Apagado, usa el inventario propio del agente (las rutas de inventario de esta guía). ## COMPORTAMIENTO (TIPO · POR DEFECTO) | | CAMPO | TIPO | QUÉ ES | carrito_activo | boolean · false | El cliente suma varios productos en varios mensajes y el pedido se crea al confirmar. Apagado, cada compra es un pedido de un producto. | foco_activo | boolean · false | Recuerda de qué producto se habla («¿y cuánto vale?») y saca su precio del catálogo de Productos. | soporte_activo | boolean · true | Estado de pedido, devoluciones, garantías y quejas: responde con sus frases msg_soporte_* y pasa a una persona. | vision_imagen_activo | boolean · false | Mira las imágenes que llegan sin un pedido abierto: una foto de producto sigue la conversación; un pago lo reconoce. | whatsapp_botones_combo_activo | boolean · false | Devuelve opciones (botones) cuando duda entre 2 o 3 productos parecidos. | mostrar_stock_bajo | boolean · true | Puede decir «quedan pocas unidades» cuando el stock está en su umbral. | sugerir_alternativas | boolean · true | Si algo está agotado, ofrece otros productos que sí hay. | despacho_confirma_metodo | boolean · false | Con los datos de envío completos, confirma el método de pago antes de cerrar. | metodo_frases | { contra, transferencia, mixto } | Cómo nombra cada método de pago en {metodo_frase}. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Agente ventas | módulo no habilitado para este espacio. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Trae los ajustes que puedes ver y cambiar. Los del funcionamiento interno del agente los gestiona Bentho y no salen en la respuesta. - En la consola, el panel Agente ventas del Estudio edita estas mismas frases. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/agente/mi-espacio/leads-config \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "tenant_id": "mi-espacio", "name": "Café La Loma", "saludo": "¡Hola! Soy el asistente de {name}. Con gusto te cuento sobre nuestros cafés y te calculo el envío a tu ciudad. 🌿", "modo_venta": true, "moneda": "COP", "campos_captura": ["nombre", "telefono"], "cotiza_sin_contacto": true, "cantidad_default": 1, "cantidad_max_por_linea": 12, "catalogo_url": "https://example.com/catalogo", "cerebro_activo": true, "comercial_activo": false, "facturacion_activo": true, "inventario_activo": true, "inventario_delegado": false, "carrito_activo": true, "mensaje_handoff": "No puedo cotizarte esto en este momento. Te pongo en contacto con un asesor para que te ayude de inmediato.", "mensaje_precio": "El precio de {nombre} es {precio}. ✨", "mensaje_checkout": "¡Perfecto! El total de tu pedido es {total}. Puedes pagar por estos medios:\n{cuentas}\n…", … } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Agente ventas · Editar configuración https://dev.bentho.ai/api/agente/config-editar/ PATCH/api/mod/agente/{espacio}/leads-config # Cambia la configuración del agente Mandas solo los campos que cambian; el resto se queda como estaba. Los cambios valen desde el siguiente mensaje. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Agente ventas. ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | name | string | no | El nombre de tu negocio. Sustituye {name} en el saludo y en las instrucciones. Por defecto, el id del espacio. | saludo | string | no | El primer mensaje de cada conversación. Admite {name}. | modo_venta | boolean | no | false: atiende y resuelve dudas. true: asesor de venta consultiva que propone siempre el siguiente paso. Por defecto false. | system_prompt | string | no | Las instrucciones del asistente cuando modo_venta es false. | system_prompt_venta | string | no | Las instrucciones cuando modo_venta es true. | aviso_privacidad | string | no | El aviso que acompaña al primer turno (aviso_privacidad en la respuesta). | moneda | string | no | Cómo se escriben las cifras en los mensajes. Por defecto "COP". No cambia el cálculo. | campos_captura | string[] | no | Los datos de contacto que pide al registrar un lead. Por defecto ["nombre", "telefono"]. | cotiza_sin_contacto | boolean | no | Si cotiza antes de tener el contacto del cliente. Por defecto true. | cantidad_default | integer | no | La cantidad que asume si el cliente no la dice. Por defecto 1. | cantidad_max_por_linea | integer | no | Desde esta cantidad por producto, en vez de cotizar, pregunta si es un pedido al por mayor. Por defecto 12. | mensaje_handoff | string | no | Lo que dice cuando pasa la conversación a una persona. | refusal_fuera_dominio | string | no | Lo que dice cuando no tiene información (status_code 1001). | catalogo_url | string | no | El enlace a tu catálogo (tienda, PDF, catálogo de WhatsApp). Vacío: no lo ofrece. Lo entrega con mensaje_catalogo. | mensaje_* · msg_* · cta_* | string | no | Las frases del agente para cada situación (precio, carrito, pago, envío, stock, posventa) y las pregunta_*. Respeta sus {variables}: ver frases. ## RESPUESTA 200 La configuración entera, ya guardada: la misma forma que GET leads-config. | | CAMPO | TIPO | QUÉ ES | name · saludo · … | | Todos los campos, con tus cambios aplicados. ## CONEXIONES CON OTROS MÓDULOS (TIPO · POR DEFECTO) | | CAMPO | TIPO | QUÉ ES | cerebro_activo | boolean · true | Responde las preguntas con los documentos del espacio. Apagado, contesta que no tiene esa información. | comercial_activo | boolean · false | Envíos, recargos y métodos de pago con las reglas del módulo Comercial. Apagado, usa tus tablas de tarifas. | marca_activa | boolean · false | Habla con la voz, el tono y las tácticas del módulo Marca. | facturacion_activo | boolean · false | Crea la orden y el cobro cuando el cliente confirma, y recibe el comprobante. Sin él, no cierra pedidos ni acepta comprobantes. | inventario_activo | boolean · false | Mira el stock antes de ofrecer y avisa si algo está agotado. | inventario_delegado | boolean · false | El stock lo lleva el módulo Productos. Apagado, usa el inventario propio del agente (las rutas de inventario de esta guía). ## COMPORTAMIENTO (TIPO · POR DEFECTO) | | CAMPO | TIPO | QUÉ ES | carrito_activo | boolean · false | El cliente suma varios productos en varios mensajes y el pedido se crea al confirmar. Apagado, cada compra es un pedido de un producto. | foco_activo | boolean · false | Recuerda de qué producto se habla («¿y cuánto vale?») y saca su precio del catálogo de Productos. | soporte_activo | boolean · true | Estado de pedido, devoluciones, garantías y quejas: responde con sus frases msg_soporte_* y pasa a una persona. | vision_imagen_activo | boolean · false | Mira las imágenes que llegan sin un pedido abierto: una foto de producto sigue la conversación; un pago lo reconoce. | whatsapp_botones_combo_activo | boolean · false | Devuelve opciones (botones) cuando duda entre 2 o 3 productos parecidos. | mostrar_stock_bajo | boolean · true | Puede decir «quedan pocas unidades» cuando el stock está en su umbral. | sugerir_alternativas | boolean · true | Si algo está agotado, ofrece otros productos que sí hay. | despacho_confirma_metodo | boolean · false | Con los datos de envío completos, confirma el método de pago antes de cerrar. | metodo_frases | { contra, transferencia, mixto } | Cómo nombra cada método de pago en {metodo_frase}. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Agente ventas | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 403 | Mandas un ajuste del funcionamiento interno del agente (el detail dice cuáles) | Estos ajustes los gestiona Bentho: … | 422 | Falta un campo obligatorio o uno no es de su tipo (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Las conexiones y los interruptores de las tablas de abajo se cambian igual: mándalos con true o false. - Un campo que no existe se ignora sin error, y uno con null no cambia nada. Las listas y los objetos (campos_captura, metodo_frases) se reemplazan enteros. - Respeta las {variables} de cada frase. Una llave sin cerrar o una variable que la frase no conoce puede dejarla sin enviar o con la llave a la vista. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X PATCH https://bentho.ai/api/mod/agente/mi-espacio/leads-config \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Café La Loma", "modo_venta": true, "catalogo_url": "https://example.com/catalogo", "mensaje_handoff": "Te paso con una persona de nuestro equipo para ayudarte. 🌿" }' ``` ■ 200 OKejemplo, con los campos del código ``` { "tenant_id": "mi-espacio", "name": "Café La Loma", "saludo": "¡Hola! Soy el asistente de {name}. Con gusto te cuento sobre nuestros cafés y te calculo el envío a tu ciudad. 🌿", "modo_venta": true, "moneda": "COP", "campos_captura": ["nombre", "telefono"], "cotiza_sin_contacto": true, "cantidad_default": 1, "cantidad_max_por_linea": 12, "catalogo_url": "https://example.com/catalogo", "cerebro_activo": true, "comercial_activo": false, "facturacion_activo": true, "inventario_activo": true, "inventario_delegado": false, "carrito_activo": true, "mensaje_handoff": "Te paso con una persona de nuestro equipo para ayudarte. 🌿", "mensaje_precio": "El precio de {nombre} es {precio}. ✨", "mensaje_checkout": "¡Perfecto! El total de tu pedido es {total}. Puedes pagar por estos medios:\n{cuentas}\n…", … } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Agente ventas · Leads https://dev.bentho.ai/api/agente/leads/ GET/api/mod/agente/{espacio}/leads/ # Los leads del espacio Los clientes que el agente registró al cotizar o al comprar, el más reciente primero. Es tu embudo: quién preguntó, por qué producto y en qué estado está. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Agente ventas. | estado | string, query | no | Solo los de ese estado: nuevo, contactado, ganado o cerrado. | limit | int, query | no | Cuántos, de 1 a 1000. Por defecto 200. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | leads | array | El más reciente primero. Cada uno, con los campos de GET leads/{lead}. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Agente ventas | módulo no habilitado para este espacio. | 400 | estado no es nuevo, contactado, ganado ni cerrado | estado inválido: ['cerrado', 'contactado', 'ganado', 'nuevo'] | 422 | Falta un campo obligatorio o uno no es de su tipo (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Copia la ruta tal cual, con su barra final. Sin ella la respuesta es un 307 sin destino, no la conversación. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/agente/mi-espacio/leads/?estado=nuevo \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "leads": [ { "id": "5e8a1c3f9b2d4e7a8c6f0b1d2e3a4c5b", "tenant_id": "mi-espacio", "estado": "nuevo", "producto_interes": "Café de origen Huila 500 g", "contacto_masked": "nombre=; telefono=", "cotizaciones_emitidas": 2, "consent": true, "consent_ts": 1790000000.12, "created_at": 1790000000.12, "updated_at": 1790000420.5 } ] } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Agente ventas · Lead https://dev.bentho.ai/api/agente/lead/ GET/api/mod/agente/{espacio}/leads/{lead} # Un lead El lead con su estado, el producto que le interesó y cuántas cotizaciones recibió. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Agente ventas. | lead | string | SÍ | El id del lead (32 caracteres hexadecimales): sale en la lista de leads y en lead_id al conversar. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | id | string | El id del lead. | estado | "nuevo" | "contactado" | "ganado" | "cerrado" | Dónde está en tu embudo: ver leads. | producto_interes | string | null | Lo que quería comprar. | contacto_masked | string | null | Qué datos dejó, seudonimizados: nombre=; telefono=. El contacto nunca sale en claro. null si no dio consentimiento. | cotizaciones_emitidas | integer | Cuántas cotizaciones recibió. | consent · consent_ts | boolean · number | null | Si autorizó el seguimiento comercial, y cuándo (segundos desde 1970). | created_at · updated_at | number | Segundos desde 1970, con decimales. | tenant_id | string | El espacio. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Agente ventas | módulo no habilitado para este espacio. | 404 | No hay un lead con ese id en el espacio | Lead no encontrado: … Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/agente/mi-espacio/leads/5e8a1c3f9b2d4e7a8c6f0b1d2e3a4c5b \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "id": "5e8a1c3f9b2d4e7a8c6f0b1d2e3a4c5b", "tenant_id": "mi-espacio", "estado": "nuevo", "producto_interes": "Café de origen Huila 500 g", "contacto_masked": "nombre=; telefono=", "cotizaciones_emitidas": 2, "consent": true, "consent_ts": 1790000000.12, "created_at": 1790000000.12, "updated_at": 1790000420.5 } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Agente ventas · Estado de lead https://dev.bentho.ai/api/agente/lead-estado/ PATCH/api/mod/agente/{espacio}/leads/{lead} # Mueve un lead en el embudo Cambia el estado comercial de un lead cuando tu equipo lo contacta, lo gana o lo da por cerrado. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Agente ventas. | lead | string | SÍ | El id del lead (32 caracteres hexadecimales): sale en la lista de leads y en lead_id al conversar. ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | estado | "nuevo" | "contactado" | "ganado" | "cerrado" | SÍ | El estado nuevo. ## RESPUESTA 200 El lead, ya con su estado nuevo. | | CAMPO | TIPO | QUÉ ES | id | string | El id del lead. | estado | "nuevo" | "contactado" | "ganado" | "cerrado" | Dónde está en tu embudo: ver leads. | producto_interes | string | null | Lo que quería comprar. | contacto_masked | string | null | Qué datos dejó, seudonimizados: nombre=; telefono=. El contacto nunca sale en claro. null si no dio consentimiento. | cotizaciones_emitidas | integer | Cuántas cotizaciones recibió. | consent · consent_ts | boolean · number | null | Si autorizó el seguimiento comercial, y cuándo (segundos desde 1970). | created_at · updated_at | number | Segundos desde 1970, con decimales. | tenant_id | string | El espacio. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Agente ventas | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 400 | estado no es nuevo, contactado, ganado ni cerrado | estado inválido: ['cerrado', 'contactado', 'ganado', 'nuevo'] | 404 | No hay un lead con ese id en el espacio | Lead no encontrado: … | 422 | Falta un campo obligatorio o uno no es de su tipo (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - El agente pone ganado por su cuenta cuando se confirma el pago del pedido de ese cliente. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X PATCH https://bentho.ai/api/mod/agente/mi-espacio/leads/5e8a1c3f9b2d4e7a8c6f0b1d2e3a4c5b \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "estado": "contactado" }' ``` ■ 200 OKejemplo, con los campos del código ``` { "id": "5e8a1c3f9b2d4e7a8c6f0b1d2e3a4c5b", "tenant_id": "mi-espacio", "estado": "contactado", "producto_interes": "Café de origen Huila 500 g", "contacto_masked": "nombre=; telefono=", "cotizaciones_emitidas": 2, "consent": true, "consent_ts": 1790000000.12, "created_at": 1790000000.12, "updated_at": 1790003600.7 } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Agente ventas · Subir tarifas https://dev.bentho.ai/api/agente/tarifas-subir/ POST/api/mod/agente/{espacio}/pricing/tables # Sube una tabla de tarifas de envío Subes tu tabla de envíos (zona, tarifa, días) y queda como una versión en borrador para revisarla. No cotiza nada hasta que la actives. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Agente ventas. ## CUERPO · MULTIPART/FORM-DATA | | CAMPO | TIPO | REQUERIDO | QUÉ ES | file | archivo | SÍ | La tabla en CSV, XLSX o PDF, hasta 10 MB. Sus columnas, en la guía. ## RESPUESTA 201 | | CAMPO | TIPO | QUÉ ES | version_id | string | La versión creada. Guárdala: con ella la revisas y la activas. | estado | "draft" | Siempre borrador al subir. | origen · sha256 | string | El tipo de archivo y su huella. | filas_insertadas | integer | Las filas que entraron al borrador, completas o no. | filas_incompletas | [{ zona, warnings, coste }] | Las que necesitan tu revisión: sin tarifa o sin días legibles. Entran con 0 y no dejan activar la versión hasta corregirlas. | warnings | string[] | Avisos del archivo entero, como «no se reconoció una tabla». ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Agente ventas | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 400 | El archivo no es CSV, XLSX o PDF | extensión no soportada para tabla de tarifas. | 400 | El archivo llegó vacío | archivo vacío. | 413 | El archivo pasa de 10 MB | archivo demasiado grande (máximo … bytes). | 429 | El espacio mandó demasiadas peticiones seguidas: espera unos segundos | Demasiadas solicitudes; intenta en un momento. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - CSV o XLSX exportados de tu hoja dan menos trabajo que un PDF: no hay que adivinar la tabla. - Si tu espacio usa el módulo Comercial (comercial_activo), el envío lo calcula Comercial y estas tablas no se usan. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/agente/mi-espacio/pricing/tables \ -H "Authorization: Bearer $BENTHO_KEY" \ -F "file=@tarifas-envio.csv" ``` ■ 201 CREATEDejemplo, con los campos del código ``` { "version_id": "a41f7c2e9b3d4e8f8a6c1d0b2e5f7a93", "estado": "draft", "origen": "csv", "sha256": "4f2a9c…", "filas_insertadas": 14, "filas_incompletas": [ { "zona": "Leticia", "warnings": ["coste ilegible: 'a convenir'"], "coste": null } ], "warnings": [], … } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Agente ventas · Versiones de tarifas https://dev.bentho.ai/api/agente/tarifas/ GET/api/mod/agente/{espacio}/pricing/versions # Las versiones de la tabla de tarifas Todas las tablas que subiste, la más reciente primero, con su estado. Solo una está validada: la que cotiza. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Agente ventas. | estado | string, query | no | Solo las de ese estado: draft, validada o retirada. ## RESPUESTA 200 versions: la más reciente primero. Cada versión trae: | | CAMPO | TIPO | QUÉ ES | id | string | El id de la versión. | estado | "draft" | "validada" | "retirada" | draft: borrador, no cotiza. validada: la activa, la única con la que se cotiza. retirada: ya no se usa. | origen · doc_source | string | De qué tipo de archivo salió (pdf, csv, xlsx) y su nombre. | sha256 | string | La huella del archivo. Cada cotización la cita en tarifa_version. | validada_por | string | null | Quién la activó, si lo dijiste al activarla. | created_at · activated_at | number · number | null | Cuándo se subió y cuándo se activó, en segundos desde 1970. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Agente ventas | módulo no habilitado para este espacio. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/agente/mi-espacio/pricing/versions?estado=draft \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "versions": [ { "id": "a41f7c2e9b3d4e8f8a6c1d0b2e5f7a93", "sha256": "4f2a9c…", "origen": "csv", "doc_source": "tarifas-envio.csv", "estado": "draft", "validada_por": null, "created_at": 1790000000.12, "activated_at": null } ] } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Agente ventas · Versión de tarifas https://dev.bentho.ai/api/agente/tarifas-version/ GET/api/mod/agente/{espacio}/pricing/drafts/{tabla} # Una versión, fila por fila La versión con todas sus filas de tarifa y sus recargos. Es lo que revisas antes de activarla. Sirve para cualquier versión, no solo borradores. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Agente ventas. | tabla | string | SÍ | El id de la versión de la tabla de tarifas: el version_id que devolvió subirla (32 caracteres hexadecimales). ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | version | object | La versión: id, estado, origen, sha256… como en la lista. | rates[].id | string | El id de la fila: para corregirla. | rates[].zone_id · product_id | string · string | null | La zona y el producto, como nombre en minúsculas con guiones. product_id null: vale para cualquier producto. | rates[].qty_min · qty_max | integer · integer | null | El rango de cantidades que cubre. qty_max null: sin tope. | rates[].coste | string | La tarifa de envío, como texto decimal. 0: falta revisarla. | rates[].dias_min · dias_max | integer | Los días de entrega. 0 y 0: falta revisarlos. | rates[].vigente_desde | string | null | Desde cuándo vale, YYYY-MM-DD. | rates[].vigente_hasta | string | null | Hasta cuándo. Pasada la fecha, esa fila ya no cotiza (rate_expired). | surcharges[] | { id, concepto, tipo, valor, condicion, base_normativa } | Los recargos de la versión: ver añadir un recargo. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Agente ventas | módulo no habilitado para este espacio. | 404 | No hay una versión con ese id | versión no encontrada: … Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/agente/mi-espacio/pricing/drafts/a41f7c2e9b3d4e8f8a6c1d0b2e5f7a93 \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "version": { "id": "a41f7c2e9b3d4e8f8a6c1d0b2e5f7a93", "sha256": "4f2a9c…", "origen": "csv", "doc_source": "tarifas-envio.csv", "estado": "draft", "validada_por": null, "created_at": 1790000000.12, "activated_at": null }, "rates": [ { "id": "d93b5e1a7c4f4b2e9a8d6c0f1e3b5a72", "table_version_id": "a41f7c2e9b3d4e8f8a6c1d0b2e5f7a93", "zone_id": "medellin", "product_id": null, "qty_min": 1, "qty_max": 10, "coste": "12000", "dias_min": 2, "dias_max": 3, "vigente_desde": null, "vigente_hasta": "2026-12-31" }, … ], "surcharges": [ { "id": "…", "concepto": "Seguro de envío", "tipo": "porcentual", "valor": "5", "condicion": null, "base_normativa": null, … } ] } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Agente ventas · Corregir tarifa https://dev.bentho.ai/api/agente/tarifa-corregir/ PATCH/api/mod/agente/{espacio}/pricing/drafts/{tabla}/rates/{tarifa} # Corrige una fila del borrador Cambia los campos de una fila de tarifa de un borrador: la tarifa, los días, el rango de cantidades o la vigencia. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Agente ventas. | tabla | string | SÍ | El id de la versión de la tabla de tarifas: el version_id que devolvió subirla (32 caracteres hexadecimales). | tarifa | string | SÍ | El id de la fila de tarifa: el id de rates[] en el detalle de la versión. ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | coste | string | no | La tarifa de envío, como texto decimal ("12000"). | dias_min · dias_max | integer | no | Los días de entrega. | qty_min · qty_max | integer | no | El rango de cantidades. | zone_id · product_id | string | no | La zona y el producto, con los ids que ves en las filas. | vigente_desde · vigente_hasta | string | no | Fechas YYYY-MM-DD. ## RESPUESTA 200 La fila, ya corregida. | | CAMPO | TIPO | QUÉ ES | id · zone_id · coste · … | | Los campos de rates[] en el detalle de la versión. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Agente ventas | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 404 | La fila no existe o su versión ya no es un borrador | fila no encontrada o su versión no está en draft. | 422 | Falta un campo obligatorio o uno no es de su tipo (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Solo se corrigen borradores. Para cambiar la tabla activa, sube una versión nueva y actívala. - Las cantidades y los importes van como texto decimal ("12", "2.5"), no como número: un número da 422. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X PATCH https://bentho.ai/api/mod/agente/mi-espacio/pricing/drafts/a41f7c2e9b3d4e8f8a6c1d0b2e5f7a93/rates/d93b5e1a7c4f4b2e9a8d6c0f1e3b5a72 \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "coste": "15000", "dias_min": 4, "dias_max": 6 }' ``` ■ 200 OKejemplo, con los campos del código ``` { "id": "d93b5e1a7c4f4b2e9a8d6c0f1e3b5a72", "table_version_id": "a41f7c2e9b3d4e8f8a6c1d0b2e5f7a93", "zone_id": "leticia", "product_id": null, "qty_min": 1, "qty_max": 10, "coste": "15000", "dias_min": 4, "dias_max": 6, "vigente_desde": null, "vigente_hasta": "2026-12-31" } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Agente ventas · Borrar tarifa https://dev.bentho.ai/api/agente/tarifa-borrar/ DEL/api/mod/agente/{espacio}/pricing/drafts/{tabla}/rates/{tarifa} # Borra una fila del borrador Quita una fila de tarifa de un borrador, por ejemplo una que el archivo leyó mal. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Agente ventas. | tabla | string | SÍ | El id de la versión de la tabla de tarifas: el version_id que devolvió subirla (32 caracteres hexadecimales). | tarifa | string | SÍ | El id de la fila de tarifa: el id de rates[] en el detalle de la versión. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | deleted | true | Hecho. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Agente ventas | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 404 | La fila no existe o su versión ya no es un borrador | fila no encontrada o versión no editable. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X DELETE https://bentho.ai/api/mod/agente/mi-espacio/pricing/drafts/a41f7c2e9b3d4e8f8a6c1d0b2e5f7a93/rates/d93b5e1a7c4f4b2e9a8d6c0f1e3b5a72 \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKderivado del código ``` { "deleted": true } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Agente ventas · Añadir recargo https://dev.bentho.ai/api/agente/recargo-crear/ POST/api/mod/agente/{espacio}/pricing/drafts/{tabla}/surcharges # Añade un recargo Un cargo que se suma al envío: un seguro, el recaudo contra entrega, un recargo por zona. Fijo o en porcentaje del envío, y solo cuando se cumple su condición. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Agente ventas. | tabla | string | SÍ | El id de la versión de la tabla de tarifas: el version_id que devolvió subirla (32 caracteres hexadecimales). ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | concepto | string | SÍ | Cómo se llama en la cotización: «Seguro de envío». | tipo | "fijo" | "porcentual" | SÍ | fijo: un importe. porcentual: un porcentaje de la tarifa de envío. | valor | string | SÍ | El importe o el porcentaje, como texto decimal ("2500", "5"). | condicion | { zona_id, producto_id, min_total } | no | Cuándo aplica. zona_id y producto_id son listas de ids; min_total, la tarifa de envío mínima. Sin condición, aplica siempre. | base_normativa | string | no | De dónde sale (una norma, tu política). Viaja con el recargo en la cotización. ## RESPUESTA 201 | | CAMPO | TIPO | QUÉ ES | id | string | El id del recargo. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Agente ventas | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 400 | tipo no es fijo ni porcentual | tipo de recargo inválido: … | 422 | Falta un campo obligatorio o uno no es de su tipo (detail es una lista) | — | 500 | La versión no existe (hoy no responde 404) | Error interno del servidor. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - No comprueba que la versión sea un borrador: un recargo añadido a la versión activa cuenta desde la siguiente cotización. - Un recargo porcentual se calcula sobre la tarifa de envío, no sobre el pedido. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/agente/mi-espacio/pricing/drafts/a41f7c2e9b3d4e8f8a6c1d0b2e5f7a93/surcharges \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "concepto": "Seguro de envío", "tipo": "porcentual", "valor": "5", "condicion": { "zona_id": [ "leticia", "san-andres" ] } }' ``` ■ 201 CREATEDderivado del código ``` { "id": "7b2e4c9a1f8d4e3b9c6a0d5f2e1b8c47" } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Agente ventas · Activar tarifas https://dev.bentho.ai/api/agente/tarifas-activar/ POST/api/mod/agente/{espacio}/pricing/versions/{tabla}/activate # Valida y activa una versión La versión pasa a validada y desde ese momento es la que cotiza. La que estaba activa pasa a retirada en el mismo paso. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Agente ventas. | tabla | string | SÍ | El id de la versión de la tabla de tarifas: el version_id que devolvió subirla (32 caracteres hexadecimales). | request_user | string, query | no | Quién la valida. Queda en validada_por. ## RESPUESTA 200 La versión, ya validada. | | CAMPO | TIPO | QUÉ ES | id | string | El id de la versión. | estado | "draft" | "validada" | "retirada" | draft: borrador, no cotiza. validada: la activa, la única con la que se cotiza. retirada: ya no se usa. | origen · doc_source | string | De qué tipo de archivo salió (pdf, csv, xlsx) y su nombre. | sha256 | string | La huella del archivo. Cada cotización la cita en tarifa_version. | validada_por | string | null | Quién la activó, si lo dijiste al activarla. | created_at · activated_at | number · number | null | Cuándo se subió y cuándo se activó, en segundos desde 1970. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Agente ventas | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 409 | Quedan filas con tarifa 0 o sin días | … tarifa(s) incompletas (coste/días en 0); corrígelas antes de validar | 409 | La versión no tiene filas | la versión no tiene tarifas; no puede activarse | 409 | La versión está retirada | no se puede activar una versión retirada | 409 | No hay una versión con ese id | versión no encontrada Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Siempre hay como mucho una versión validada. Para volver a una anterior, súbela de nuevo: una retirada no se reactiva. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X POST https://bentho.ai/api/mod/agente/mi-espacio/pricing/versions/a41f7c2e9b3d4e8f8a6c1d0b2e5f7a93/activate \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "id": "a41f7c2e9b3d4e8f8a6c1d0b2e5f7a93", "sha256": "4f2a9c…", "origen": "csv", "doc_source": "tarifas-envio.csv", "estado": "validada", "validada_por": "ana", "created_at": 1790000000.12, "activated_at": 1790000900.3 } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Agente ventas · Retirar tarifas https://dev.bentho.ai/api/agente/tarifas-retirar/ POST/api/mod/agente/{espacio}/pricing/versions/{tabla}/retire # Retira una versión La versión pasa a retirada y deja de cotizar. Si era la activa, el espacio se queda sin tabla hasta que actives otra. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Agente ventas. | tabla | string | SÍ | El id de la versión de la tabla de tarifas: el version_id que devolvió subirla (32 caracteres hexadecimales). ## RESPUESTA 200 La versión, ya retirada. | | CAMPO | TIPO | QUÉ ES | id | string | El id de la versión. | estado | "draft" | "validada" | "retirada" | draft: borrador, no cotiza. validada: la activa, la única con la que se cotiza. retirada: ya no se usa. | origen · doc_source | string | De qué tipo de archivo salió (pdf, csv, xlsx) y su nombre. | sha256 | string | La huella del archivo. Cada cotización la cita en tarifa_version. | validada_por | string | null | Quién la activó, si lo dijiste al activarla. | created_at · activated_at | number · number | null | Cuándo se subió y cuándo se activó, en segundos desde 1970. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Agente ventas | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 404 | No hay una versión con ese id | versión no encontrada: … Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Sin tabla activa, el agente no cotiza envíos: lo dice y pasa la conversación a una persona. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X POST https://bentho.ai/api/mod/agente/mi-espacio/pricing/versions/a41f7c2e9b3d4e8f8a6c1d0b2e5f7a93/retire \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "id": "a41f7c2e9b3d4e8f8a6c1d0b2e5f7a93", "sha256": "4f2a9c…", "origen": "csv", "doc_source": "tarifas-envio.csv", "estado": "retirada", "validada_por": "ana", "created_at": 1790000000.12, "activated_at": 1790000900.3 } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Agente ventas · Cotizar envío https://dev.bentho.ai/api/agente/cotizar/ POST/api/mod/agente/{espacio}/pricing/quote # Cotiza un envío La misma cotización que hace el agente, sin conversación: tarifa, días de entrega y recargos de la tabla activa para un destino, un producto y una cantidad. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Agente ventas. ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | destino | string | SÍ | La ciudad o la zona, en texto libre. Da igual mayúsculas o tildes. | producto | string | no | El producto, si la tabla tiene tarifas por producto. | cantidad | integer | no | Por defecto 1. | session_id | string | no | Una conversación, para que la cotización quede asociada en el registro. ## RESPUESTA 200 Siempre 200: una cotización o el motivo por el que no se pudo. | | CAMPO | TIPO | QUÉ ES | ok | boolean | true si cotizó. | cotizacion.total | string | Lo que cuesta el envío con sus recargos, texto decimal con 2 decimales. | cotizacion.coste_envio | string | La tarifa de envío sola. | cotizacion.recargos[] | { concepto, monto, base_normativa } | Los recargos que aplicaron. | cotizacion.tiempo_entrega_dias | [min, max] | Los días de entrega. | cotizacion.zona_id · producto_id | string | La zona y el producto que reconoció. producto_id vacío si no diste uno. | cotizacion.vigencia | string | null | Hasta cuándo vale la tarifa usada, si tiene fecha. | cotizacion.tarifa_version | string | La huella (sha256) de la tabla con la que cotizó. | error | { code, message, detail, candidates } | Si no cotizó: ver códigos. ## CÓDIGOS DE ERROR DE COTIZACIÓN | | CAMPO | TIPO | QUÉ ES | missing_slot | repreguntar | Falta el destino. | ambiguous_destination | repreguntar | El destino coincide con varias zonas: candidates trae sus nombres. | quantity_out_of_range | repreguntar | Ninguna fila cubre esa cantidad. | no_zone_for_destination | persona | Ninguna zona reconoce ese destino. | unknown_product | persona | Diste un producto que la tabla no conoce. | no_rate | persona | Hay zona, pero ninguna fila para esa zona y ese producto. | rate_expired | persona | La fila que aplica está vencida (vigente_hasta ya pasó): detail trae la fecha. | no_active_table | persona | No hay ninguna versión validada. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Agente ventas | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 422 | Falta un campo obligatorio o uno no es de su tipo (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Pide una clave escribe, aunque no cambie nada: cada cotización queda registrada. - Cotiza con tus tablas, no con el módulo Comercial: si tu espacio usa Comercial, esta ruta no refleja lo que cotiza el agente. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/agente/mi-espacio/pricing/quote \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "destino": "Medellín", "cantidad": 2 }' ``` ■ 200 OKejemplo, con los campos del código ``` { "ok": true, "cotizacion": { "producto_id": "", "cantidad": 2, "zona_id": "medellin", "coste_envio": "12000.00", "tiempo_entrega_dias": [2, 3], "recargos": [], "total": "12000.00", "tarifa_version": "4f2a9c…", "vigencia": "2026-12-31" }, "error": null } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Agente ventas · Subir inventario https://dev.bentho.ai/api/agente/inventario-subir/ POST/api/mod/agente/{espacio}/inventory/upload # Carga el inventario desde un archivo Subes tu hoja de existencias y cada fila fija la cantidad de su artículo. A diferencia de las tarifas, no hay borrador: entra directo y el agente la usa desde el siguiente mensaje. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Agente ventas. ## CUERPO · MULTIPART/FORM-DATA | | CAMPO | TIPO | REQUERIDO | QUÉ ES | file | archivo | SÍ | CSV, XLSX o PDF, hasta 10 MB, con una columna de cantidad y otra de nombre o SKU. Las columnas que reconoce, en la guía. ## RESPUESTA 201 | | CAMPO | TIPO | QUÉ ES | items_cargados | integer | Los artículos que se crearon o actualizaron. | items_incompletos | [{ nombre, sku, warnings }] | Las filas que no entraron: sin cantidad legible. | warnings | string[] | Avisos del archivo entero, como «no se reconoció una tabla de stock». | origen · sha256 | string | El tipo de archivo y su huella. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Agente ventas | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 400 | El archivo no es CSV, XLSX o PDF | extensión no soportada para inventario. | 400 | El archivo llegó vacío | archivo vacío. | 413 | El archivo pasa de 10 MB | archivo demasiado grande (máximo … bytes). | 429 | El espacio mandó demasiadas peticiones seguidas: espera unos segundos | Demasiadas solicitudes; intenta en un momento. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Los artículos que no vienen en el archivo no cambian. La cantidad del archivo reemplaza la que había (no se suma). - Si una fila no trae SKU, el SKU es su nombre en minúsculas con guiones. - Si el stock lo lleva el módulo Productos (inventario_delegado), carga allí tu inventario: el agente no mira este. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/agente/mi-espacio/inventory/upload \ -H "Authorization: Bearer $BENTHO_KEY" \ -F "file=@inventario.csv" ``` ■ 201 CREATEDejemplo, con los campos del código ``` { "origen": "csv", "sha256": "b81e6d…", "items_cargados": 42, "items_incompletos": [ { "nombre": "Filtro de tela", "sku": "FIL-01", "warnings": ["cantidad ilegible: 'pendiente'"] } ], "warnings": [], … } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Agente ventas · Inventario https://dev.bentho.ai/api/agente/inventario/ GET/api/mod/agente/{espacio}/inventory/ # El inventario del espacio Todos los artículos, en orden alfabético, con su cantidad. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Agente ventas. | only_active | bool, query | no | true: solo los activos. Por defecto false. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | items | array | Cada artículo, con los campos de GET inventory/items/{sku}. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Agente ventas | módulo no habilitado para este espacio. | 422 | Falta un campo obligatorio o uno no es de su tipo (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Copia la ruta tal cual, con su barra final. Sin ella la respuesta es un 307 sin destino, no la conversación. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/agente/mi-espacio/inventory/ \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "items": [ { "sku": "CAFE-500G", "nombre": "Café de origen Huila 500 g", "aliases_json": "[\"Café de origen Huila 500 g\"]", "cantidad": "38", "unidad": "bolsa", "bodega": "Principal", "umbral_bajo": "10", "activo": 1, "fuente": "csv", "external_id": null, "created_at": 1790000000.12, "updated_at": 1790000420.5 }, … ] } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Agente ventas · Resumen de inventario https://dev.bentho.ai/api/agente/inventario-resumen/ GET/api/mod/agente/{espacio}/inventory/summary # El resumen del inventario Cuántos artículos hay, cuántos tienen stock, cuántos están agotados o bajos, y la última carga. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Agente ventas. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | total · activos | integer | Artículos, y cuántos activos. | en_stock · agotados | integer | Con cantidad mayor que 0, y con 0. | stock_bajo | integer | En su umbral o por debajo. | last_sync | object | null | La última carga: fuente (file si fue un archivo), items_upserted y created_at. null si nunca se cargó. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Agente ventas | módulo no habilitado para este espacio. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/agente/mi-espacio/inventory/summary \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "total": 43, "activos": 43, "en_stock": 39, "agotados": 4, "stock_bajo": 6, "last_sync": { "fuente": "file", "items_upserted": 42, "created_at": 1790000000.12, … } } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Agente ventas · Stock bajo https://dev.bentho.ai/api/agente/stock-bajo/ GET/api/mod/agente/{espacio}/inventory/low-stock # Lo que está por agotarse Los artículos activos con umbral cuya cantidad está en el umbral o por debajo. Tu lista de reposición. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Agente ventas. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | items | array | En orden alfabético, con los campos de GET inventory/items/{sku}. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Agente ventas | módulo no habilitado para este espacio. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/agente/mi-espacio/inventory/low-stock \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "items": [ { "sku": "CAFE-500G", "nombre": "Café de origen Huila 500 g", "aliases_json": "[\"Café de origen Huila 500 g\"]", "cantidad": "7", "unidad": "bolsa", "bodega": "Principal", "umbral_bajo": "10", "activo": 1, "fuente": "csv", "external_id": null, "created_at": 1790000000.12, "updated_at": 1790000420.5 } ] } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Agente ventas · Exportar inventario https://dev.bentho.ai/api/agente/inventario-exportar/ GET/api/mod/agente/{espacio}/inventory/export # Descarga el inventario en CSV El inventario actual en un CSV, para respaldarlo o abrirlo en tu hoja de cálculo. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Agente ventas. ## RESPUESTA 200 Un archivo text/csv, una fila por artículo. | | CAMPO | TIPO | QUÉ ES | columnas | csv | sku, nombre, cantidad, unidad, bodega, umbral_bajo, activo, fuente, external_id, updated_at. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Agente ventas | módulo no habilitado para este espacio. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/agente/mi-espacio/inventory/export \ -H "Authorization: Bearer $BENTHO_KEY" \ -o inventario.csv ``` ■ 200 OKejemplo, con los campos del código ``` sku,nombre,cantidad,unidad,bodega,umbral_bajo,activo,fuente,external_id,updated_at CAFE-500G,Café de origen Huila 500 g,38,bolsa,Principal,10,1,csv,,1790000420.5 … ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Agente ventas · Disponibilidad https://dev.bentho.ai/api/agente/disponibilidad/ GET/api/mod/agente/{espacio}/inventory/availability # ¿Hay stock para este pedido? La misma consulta que hace el agente antes de ofrecer: si hay suficiente de un producto para una cantidad. Con el stock en Productos, pregunta allí. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Agente ventas. | producto | string, query | SÍ | El SKU o el nombre del producto. | cantidad | int, query | no | Por defecto 1. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | estado | string | Ver estados. | sku · nombre | string | null | El artículo que reconoció. | cantidad_disponible | string | null | Lo que hay, texto decimal. | cantidad_pedida | string | null | Lo que preguntaste. | unidad | string | null | La unidad. ## ESTADOS DE DISPONIBILIDAD | | CAMPO | TIPO | QUÉ ES | disponible | se puede vender | Hay de sobra para lo pedido. | stock_bajo | se puede vender | Alcanza, pero el stock está en su umbral o por debajo. | insuficiente | no alcanza | Existe, pero hay menos de lo pedido. | agotado | no alcanza | Existe, con cantidad 0. | no_rastreado | sin datos | No está en el inventario. No bloquea la venta: no se sabe. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Agente ventas | módulo no habilitado para este espacio. | 422 | Falta un campo obligatorio o uno no es de su tipo (detail es una lista) | — | 502 | Con el inventario en Productos: Productos no respondió | Productos: … Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Busca primero por SKU exacto, luego por nombre exacto y luego por parte del nombre o de sus alias. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/agente/mi-espacio/inventory/availability?producto=CAFE-500G&cantidad=2 \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "estado": "disponible", "sku": "CAFE-500G", "nombre": "Café de origen Huila 500 g", "cantidad_disponible": "38", "cantidad_pedida": "2", "unidad": "bolsa" } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Agente ventas · Artículo https://dev.bentho.ai/api/agente/articulo/ GET/api/mod/agente/{espacio}/inventory/items/{sku} # Un artículo del inventario El artículo con su cantidad y, si lo pides, sus últimos movimientos. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Agente ventas. | sku | string | SÍ | El SKU del artículo. Si no diste uno al cargarlo, es su nombre en minúsculas con guiones (Café Huila 500 g → cafe-huila-500-g). | with_movements | bool, query | no | true: añade movements, los últimos 50 cambios de cantidad. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | sku · nombre | string | El SKU y el nombre. | cantidad | string | Lo que hay, como texto decimal ("12", "2.5"). Nunca baja de 0. | umbral_bajo | string | null | Desde esta cantidad hacia abajo cuenta como stock bajo. null: sin umbral. | unidad · bodega | string | null | La unidad (unidad, kg, caja…) y dónde está. | aliases_json | string | Otros nombres con los que se reconoce, como una lista JSON en texto: "[\"Café Huila\"]". | activo | 0 | 1 | 1 si se ofrece. | fuente | string | null | De dónde vino su última carga: csv, xlsx, pdf o manual. | external_id | string | null | Su id en tu sistema contable, si viene de una sincronización. | created_at · updated_at | number | Segundos desde 1970, con decimales. | movements[] | { tipo, delta, resultante, motivo, origen, created_at } | El más reciente primero. tipo: ingesta (archivo), set (alta manual), ajuste, venta o sync. delta y resultante, texto decimal. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Agente ventas | módulo no habilitado para este espacio. | 404 | No hay un artículo con ese SKU en el inventario | item no encontrado: … Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/agente/mi-espacio/inventory/items/CAFE-500G?with_movements=true \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "sku": "CAFE-500G", "nombre": "Café de origen Huila 500 g", "aliases_json": "[\"Café de origen Huila 500 g\"]", "cantidad": "38", "unidad": "bolsa", "bodega": "Principal", "umbral_bajo": "10", "activo": 1, "fuente": "csv", "external_id": null, "created_at": 1790000000.12, "updated_at": 1790000420.5, "movements": [ { "tipo": "venta", "delta": "-2", "resultante": "38", "motivo": "venta confirmada", "origen": "chat-4821", "created_at": 1790000420.5, … }, { "tipo": "ingesta", "delta": "40", "resultante": "40", "motivo": null, "origen": "b81e6d…", "created_at": 1790000000.12, … } ] } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Agente ventas · Guardar artículo https://dev.bentho.ai/api/agente/articulo-guardar/ POST/api/mod/agente/{espacio}/inventory/items # Crea o fija un artículo Da de alta un artículo o, si su SKU ya existe, lo reescribe y fija su cantidad. Para sumar o restar, usa ajustar. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Agente ventas. ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | nombre | string | SÍ | El nombre del artículo. | sku | string | no | Tu SKU. Sin él, el nombre en minúsculas con guiones. | cantidad | string | no | La cantidad, texto decimal. Por defecto "0". | umbral_bajo | string | no | Desde esta cantidad hacia abajo, stock bajo. Texto decimal. | unidad | string | no | unidad, kg, caja… | bodega | string | no | Dónde está. | aliases | string[] | no | Otros nombres con los que el cliente lo pide. Sin ellos, el nombre. ## RESPUESTA 201 El artículo guardado. | | CAMPO | TIPO | QUÉ ES | sku · nombre | string | El SKU y el nombre. | cantidad | string | Lo que hay, como texto decimal ("12", "2.5"). Nunca baja de 0. | umbral_bajo | string | null | Desde esta cantidad hacia abajo cuenta como stock bajo. null: sin umbral. | unidad · bodega | string | null | La unidad (unidad, kg, caja…) y dónde está. | aliases_json | string | Otros nombres con los que se reconoce, como una lista JSON en texto: "[\"Café Huila\"]". | activo | 0 | 1 | 1 si se ofrece. | fuente | string | null | De dónde vino su última carga: csv, xlsx, pdf o manual. | external_id | string | null | Su id en tu sistema contable, si viene de una sincronización. | created_at · updated_at | number | Segundos desde 1970, con decimales. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Agente ventas | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 422 | Falta un campo obligatorio o uno no es de su tipo (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Las cantidades y los importes van como texto decimal ("12", "2.5"), no como número: un número da 422. - Al reescribir un artículo, unidad, bodega y umbral_bajo que no mandes conservan su valor. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/agente/mi-espacio/inventory/items \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "nombre": "Café de origen Huila 500 g", "sku": "CAFE-500G", "cantidad": "40", "umbral_bajo": "10", "unidad": "bolsa", "aliases": [ "café huila", "huila medio kilo" ] }' ``` ■ 201 CREATEDejemplo, con los campos del código ``` { "sku": "CAFE-500G", "nombre": "Café de origen Huila 500 g", "aliases_json": "[\"café huila\", \"huila medio kilo\"]", "cantidad": "40", "unidad": "bolsa", "bodega": "Principal", "umbral_bajo": "10", "activo": 1, "fuente": "manual", "external_id": null, "created_at": 1790000000.12, "updated_at": 1790000420.5 } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Agente ventas · Ajustar artículo https://dev.bentho.ai/api/agente/articulo-ajustar/ PATCH/api/mod/agente/{espacio}/inventory/items/{sku} # Ajusta la cantidad de un artículo Suma o resta a la cantidad actual: una reposición, una merma, un conteo. Queda como movimiento de ajuste. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Agente ventas. | sku | string | SÍ | El SKU del artículo. Si no diste uno al cargarlo, es su nombre en minúsculas con guiones (Café Huila 500 g → cafe-huila-500-g). ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | delta | string | SÍ | Lo que sumas, con signo, texto decimal: "24" o "-3". | motivo | string | no | Por qué: queda en el movimiento. ## RESPUESTA 200 El artículo, con su cantidad nueva. | | CAMPO | TIPO | QUÉ ES | sku · nombre | string | El SKU y el nombre. | cantidad | string | Lo que hay, como texto decimal ("12", "2.5"). Nunca baja de 0. | umbral_bajo | string | null | Desde esta cantidad hacia abajo cuenta como stock bajo. null: sin umbral. | unidad · bodega | string | null | La unidad (unidad, kg, caja…) y dónde está. | aliases_json | string | Otros nombres con los que se reconoce, como una lista JSON en texto: "[\"Café Huila\"]". | activo | 0 | 1 | 1 si se ofrece. | fuente | string | null | De dónde vino su última carga: csv, xlsx, pdf o manual. | external_id | string | null | Su id en tu sistema contable, si viene de una sincronización. | created_at · updated_at | number | Segundos desde 1970, con decimales. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Agente ventas | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 404 | No hay un artículo con ese SKU en el inventario | item no encontrado: … | 422 | Falta un campo obligatorio o uno no es de su tipo (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - La cantidad nunca baja de 0: si restas más de lo que hay, queda en 0. - Las cantidades y los importes van como texto decimal ("12", "2.5"), no como número: un número da 422. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X PATCH https://bentho.ai/api/mod/agente/mi-espacio/inventory/items/CAFE-500G \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "delta": "24", "motivo": "reposición del proveedor" }' ``` ■ 200 OKejemplo, con los campos del código ``` { "sku": "CAFE-500G", "nombre": "Café de origen Huila 500 g", "aliases_json": "[\"Café de origen Huila 500 g\"]", "cantidad": "62", "unidad": "bolsa", "bodega": "Principal", "umbral_bajo": "10", "activo": 1, "fuente": "csv", "external_id": null, "created_at": 1790000000.12, "updated_at": 1790000420.5 } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Agente ventas · Borrar artículo https://dev.bentho.ai/api/agente/articulo-borrar/ DEL/api/mod/agente/{espacio}/inventory/items/{sku} # Borra un artículo Quita el artículo del inventario junto con sus movimientos. No se puede deshacer. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Agente ventas. | sku | string | SÍ | El SKU del artículo. Si no diste uno al cargarlo, es su nombre en minúsculas con guiones (Café Huila 500 g → cafe-huila-500-g). ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | deleted | true | Hecho. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Agente ventas | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 404 | No hay un artículo con ese SKU en el inventario | item no encontrado: … Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Si solo quieres que no se ofrezca por ahora, deja su cantidad en 0: el agente lo dará por agotado. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X DELETE https://bentho.ai/api/mod/agente/mi-espacio/inventory/items/CAFE-500G \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKderivado del código ``` { "deleted": true } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Agente ventas · Registrar venta https://dev.bentho.ai/api/agente/venta/ POST/api/mod/agente/{espacio}/inventory/sale # Registra una venta confirmada Descuenta del inventario lo que se vendió fuera del chat (tu tienda, tu caja). Con una referencia de venta, un reintento no descuenta dos veces. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Agente ventas. ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | lines | [{ sku, cantidad }] | SÍ | Lo vendido. cantidad, texto decimal; por defecto "1". | sale_ref | string | no | Tu número de venta o de pedido. Con la misma referencia, la segunda llamada devuelve el resultado de la primera sin descontar. | session_id | string | no | La conversación de la que salió la venta, si la hubo. ## RESPUESTA 200 Con el inventario propio del agente: | | CAMPO | TIPO | QUÉ ES | confirmados | [{ sku, cantidad, restante, external_id }] | Lo que se descontó y lo que queda. | no_encontrados | string[] | SKU que no están en el inventario: no se descontó nada de ellos. | alegra | object | null | Si el espacio tiene conectada su contabilidad Alegra con escritura activada, el resultado de descontarlo allí; si no, pushed: false y el motivo. | status | "ok" | Hecho. | idempotent_replay | true | Solo si esa sale_ref ya se había registrado: es el resultado de entonces. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Agente ventas | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 400 | lines viene vacío | sin líneas de venta. | 422 | Falta un campo obligatorio o uno no es de su tipo (detail es una lista) | — | 502 | Con el inventario en Productos: Productos no respondió | Productos: … Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Las ventas que cierra el agente ya descuentan solas al confirmarse el pago: no las registres otra vez. - Con el stock en Productos (inventario_delegado), la venta la registra Productos y la respuesta es la suya. - Las cantidades y los importes van como texto decimal ("12", "2.5"), no como número: un número da 422. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/agente/mi-espacio/inventory/sale \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "lines": [ { "sku": "CAFE-500G", "cantidad": "2" } ], "sale_ref": "pedido-10482" }' ``` ■ 200 OKejemplo, con los campos del código ``` { "confirmados": [ { "sku": "CAFE-500G", "cantidad": "2", "restante": "36", "external_id": null } ], "no_encontrados": [], "alegra": { "pushed": false, "reason": "alegra_no_configurado" }, "status": "ok" } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Agente ventas · Consumo https://dev.bentho.ai/api/agente/consumo/ GET/api/mod/agente/{espacio}/consumo # Lo que gastó el agente, en créditos Los créditos que gastó el agente de ventas en este espacio. Para el saldo de la cuenta entera, mira la bolsa de créditos. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Agente ventas. | dias | int, query | no | Los últimos N días; por encima de 400 cuenta 400. Sin él, desde el principio. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | modulo · espacio | string | "agente" y tu espacio. | dias | integer | null | La ventana que pediste. null: todo. | desde · hasta | string | null | El primer y el último día con gasto, YYYY-MM-DD. | total.creditos | string | Decimal como texto, 4 decimales: "12.3456". | total.solicitudes | integer | Cuántas operaciones sumaron ese gasto. Un mensaje puede contar varias. | actualizado | integer | Cuándo se calculó, en segundos desde 1970. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Agente ventas | módulo no habilitado para este espacio. | 422 | dias es 0, negativo o no es un entero | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - creditos es texto, no número, para no perder precisión. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/agente/mi-espacio/consumo?dias=30 \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "modulo": "agente", "espacio": "mi-espacio", "dias": 30, "desde": "2026-09-04", "hasta": "2026-10-03", "actualizado": 1790000420, "total": { "creditos": "12.3456", "solicitudes": 182 } } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Marca · Configuración https://dev.bentho.ai/api/marca/config/ GET/api/mod/marca/{espacio}/config # La marca de tu agente Quién es tu agente, cómo habla y cómo lleva la conversación a una compra, tal como está guardado. Es lo que usa el Agente ventas para dar su voz a las respuestas. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Marca. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | name | string | El nombre de tu marca: el agente se presenta como «… de name». | activo | boolean | El interruptor general. En false, el Agente ventas responde con su descripción propia. | identidad | object | Quién es: ver identidad. | estilo | object | Cómo habla: ver estilo. | tacticas | object | Cómo lleva la conversación a una compra: ver tacticas. ## IDENTIDAD | | CAMPO | TIPO | QUÉ ES | nombre_agente | string | El nombre con el que se presenta. Vacío (por defecto): sin nombre propio. | rol | string | A qué se dedica, en una frase. Por defecto «asesor comercial». | linea | string | En qué se especializa. Vacío (por defecto) si atiende todo lo que ofreces. ## ESTILO | | CAMPO | TIPO | QUÉ ES | tono | string[] | Adjetivos, el más importante primero. Por defecto cordial, breve y claro. | rasgos | string[] | Frases cortas sobre su manera de ser; cada una entra tal cual en su descripción. Escríbelas sin comillas. | emojis_permitidos | string[] | Los que puede usar, con moderación. | emojis_obligatorios | string[] | Los que lleva siempre en cada respuesta. | max_lineas | integer | El largo máximo de una respuesta, en líneas. 0 (por defecto): sin límite. | solo_texto_plano | boolean | Sin viñetas, negritas ni formato: se lee natural en WhatsApp. Por defecto true. ## TACTICAS | | CAMPO | TIPO | QUÉ ES | upsell_activo | boolean | Sugerir el combo completo cuando encaja. Por defecto false. | upsell_combos | { producto: combo } | Qué combo sugerir para qué producto. La clave es una palabra o parte del nombre del producto (da igual mayúsculas o tildes); el valor, el nombre del combo tal como está en tu catálogo. | upsell_argumento | string | Por qué lo recomienda, sin precios ni cifras. Por defecto «llevarlo completo da mejores resultados». | upsell_plantilla | string | La frase de la sugerencia, con {combo} y {argumento}. Por defecto «Si buscas un mejor resultado, te recomiendo el {combo}: {argumento}. ¿Te lo cotizo?». | pregunta_cierre_obligatoria | boolean | Si una respuesta no termina en pregunta, se le añade una de preguntas_cierre. Por defecto false. | preguntas_cierre | string[] | Las preguntas con las que puede cerrar. Se turnan de un mensaje a otro. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Marca | módulo no habilitado para este espacio. | 502 | El módulo no está disponible en este momento: reintenta en unos minutos | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Si nunca se configuró, responde con los valores por defecto y name igual al id del espacio: cámbialo por el nombre de tu marca. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/marca/mi-espacio/config \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "name": "Café La Loma", "activo": true, "identidad": { "nombre_agente": "Lía", "rol": "asesora de café de especialidad", "linea": "" }, "estilo": { "tono": ["cercana", "entusiasta", "clara"], "emojis_permitidos": ["☕", "🌿"], "emojis_obligatorios": [], "max_lineas": 4, "solo_texto_plano": true, "rasgos": ["Hablas del café como un ritual de la mañana"] }, "tacticas": { "upsell_activo": true, "upsell_combos": { "cafe de origen": "Combo Barista en Casa" }, "upsell_argumento": "con la prensa francesa la taza sale mejor", "upsell_plantilla": "Si buscas un mejor resultado, te recomiendo el {combo}: {argumento}. ¿Te lo cotizo?", "pregunta_cierre_obligatoria": true, "preguntas_cierre": ["¿Te lo aparto?", "¿Para cuándo lo necesitas?"] }, … } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Marca · Editar marca https://dev.bentho.ai/api/marca/config-editar/ PATCH/api/mod/marca/{espacio}/config # Cambia la marca de tu agente Cambia el nombre de la marca, el interruptor general o un apartado entero: identidad, estilo o tácticas. Lo que no mandes se queda como estaba. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Marca. ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | name | string | no | El nombre de tu marca. | activo | boolean | no | En false, el Agente ventas deja de usar esta marca y responde con la suya propia. | identidad | object | no | Entero: ver identidad. | estilo | object | no | Entero: ver estilo. | tacticas | object | no | Entero: ver tacticas. ## RESPUESTA 200 La marca entera, ya guardada, como en GET config. | | CAMPO | TIPO | QUÉ ES | name | string | El nombre de tu marca: el agente se presenta como «… de name». | activo | boolean | El interruptor general. En false, el Agente ventas responde con su descripción propia. | identidad | object | Quién es: ver identidad. | estilo | object | Cómo habla: ver estilo. | tacticas | object | Cómo lleva la conversación a una compra: ver tacticas. ## IDENTIDAD | | CAMPO | TIPO | QUÉ ES | nombre_agente | string | El nombre con el que se presenta. Vacío (por defecto): sin nombre propio. | rol | string | A qué se dedica, en una frase. Por defecto «asesor comercial». | linea | string | En qué se especializa. Vacío (por defecto) si atiende todo lo que ofreces. ## ESTILO | | CAMPO | TIPO | QUÉ ES | tono | string[] | Adjetivos, el más importante primero. Por defecto cordial, breve y claro. | rasgos | string[] | Frases cortas sobre su manera de ser; cada una entra tal cual en su descripción. Escríbelas sin comillas. | emojis_permitidos | string[] | Los que puede usar, con moderación. | emojis_obligatorios | string[] | Los que lleva siempre en cada respuesta. | max_lineas | integer | El largo máximo de una respuesta, en líneas. 0 (por defecto): sin límite. | solo_texto_plano | boolean | Sin viñetas, negritas ni formato: se lee natural en WhatsApp. Por defecto true. ## TACTICAS | | CAMPO | TIPO | QUÉ ES | upsell_activo | boolean | Sugerir el combo completo cuando encaja. Por defecto false. | upsell_combos | { producto: combo } | Qué combo sugerir para qué producto. La clave es una palabra o parte del nombre del producto (da igual mayúsculas o tildes); el valor, el nombre del combo tal como está en tu catálogo. | upsell_argumento | string | Por qué lo recomienda, sin precios ni cifras. Por defecto «llevarlo completo da mejores resultados». | upsell_plantilla | string | La frase de la sugerencia, con {combo} y {argumento}. Por defecto «Si buscas un mejor resultado, te recomiendo el {combo}: {argumento}. ¿Te lo cotizo?». | pregunta_cierre_obligatoria | boolean | Si una respuesta no termina en pregunta, se le añade una de preguntas_cierre. Por defecto false. | preguntas_cierre | string[] | Las preguntas con las que puede cerrar. Se turnan de un mensaje a otro. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Marca | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 502 | El módulo no está disponible en este momento: reintenta en unos minutos | — | 422 | Un campo no tiene su tipo (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - identidad, estilo y tacticas se reemplazan enteros: lo que no mandes dentro vuelve a su valor por defecto. Para cambiar un solo dato, lee GET config, cámbialo y manda el apartado completo. - Un campo que no existe se ignora sin error, y uno en null también: así no se vacía nada. - Las conversaciones del Agente ventas toman el cambio al cabo de poco, hasta un minuto. Para ver cómo queda, lee la descripción del agente. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X PATCH https://bentho.ai/api/mod/marca/mi-espacio/config \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Café La Loma", "identidad": { "nombre_agente": "Lía", "rol": "asesora de café de especialidad", "linea": "" }, "estilo": { "tono": [ "cercana", "entusiasta", "clara" ], "rasgos": [ "Hablas del café como un ritual de la mañana" ], "emojis_permitidos": [ "☕", "🌿" ], "emojis_obligatorios": [], "max_lineas": 4, "solo_texto_plano": true } }' ``` ■ 200 OKejemplo, con los campos del código ``` { "name": "Café La Loma", "activo": true, "identidad": { "nombre_agente": "Lía", "rol": "asesora de café de especialidad", "linea": "" }, "estilo": { "tono": ["cercana", "entusiasta", "clara"], "emojis_permitidos": ["☕", "🌿"], "emojis_obligatorios": [], "max_lineas": 4, "solo_texto_plano": true, "rasgos": ["Hablas del café como un ritual de la mañana"] }, "tacticas": { "upsell_activo": true, "upsell_combos": { "cafe de origen": "Combo Barista en Casa" }, "upsell_argumento": "con la prensa francesa la taza sale mejor", "upsell_plantilla": "Si buscas un mejor resultado, te recomiendo el {combo}: {argumento}. ¿Te lo cotizo?", "pregunta_cierre_obligatoria": true, "preguntas_cierre": ["¿Te lo aparto?", "¿Para cuándo lo necesitas?"] }, … } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Marca · Descripción del agente https://dev.bentho.ai/api/marca/persona/ GET/api/mod/marca/{espacio}/persona # Cómo queda descrito tu agente La descripción que el Agente ventas lee antes de contestar, armada con tu identidad y tu estilo, y las tácticas que aplica después. Sirve para ver cómo queda la marca antes de que hable con nadie. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Marca. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | activo | boolean | El interruptor general de la marca. | persona | string | La descripción del agente, en prosa: quién es, su tono, sus rasgos, su largo y sus emojis. Termina siempre con unas reglas fijas que no se pueden quitar (ver la guía). | digest | string | Una huella de la configuración (64 caracteres): cambia en cuanto cambia cualquier dato. | upsell | { activo, combos, argumento, plantilla } | Las tácticas de combo, como en tacticas.upsell_*. | pregunta_cierre | { obligatoria, pool } | La pregunta de cierre: pregunta_cierre_obligatoria y preguntas_cierre. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Marca | módulo no habilitado para este espacio. | 502 | El módulo no está disponible en este momento: reintenta en unos minutos | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Las tácticas no van en persona: el agente las añade después de redactar, para no ofrecer dos veces el mismo combo ni hacer dos preguntas seguidas. - Para saber si algo cambió sin comparar el texto entero, guarda el digest y compáralo. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/marca/mi-espacio/persona \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "activo": true, "persona": "Eres Lía, asesora de café de especialidad de Café La Loma. Tu tono es cercana, entusiasta, clara, con enfoque total en ayudar al cliente a concretar su compra. Hablas del café como un ritual de la mañana. Ve directo al grano: responde en maximo 4 lineas. Escribe solo texto plano, sin vinetas, negritas ni formato. Puedes usar con moderacion estos emojis: ☕ 🌿. REGLAS INQUEBRANTABLES: NUNCA inventes precios, totales, plazos, descuentos ni disponibilidad; …", "digest": "87ba16b6dfb23f5f34eb4ba3482ce6b3050a892f72b6bddd338384f081cbab4a", "upsell": { "activo": true, "combos": { "cafe de origen": "Combo Barista en Casa" }, "argumento": "con la prensa francesa la taza sale mejor", "plantilla": "Si buscas un mejor resultado, te recomiendo el {combo}: {argumento}. ¿Te lo cotizo?" }, "pregunta_cierre": { "obligatoria": true, "pool": ["¿Te lo aparto?", "¿Para cuándo lo necesitas?"] }, … } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Lista de productos https://dev.bentho.ai/api/productos/productos/ GET/api/mod/productos/{espacio}/products/ # Los productos del espacio Todo el catálogo, por orden alfabético, cada producto con sus opciones y variantes. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. | estado | string, query | no | Solo los de un estado: borrador, activo o archivado. Sin él, todos. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | products | array | Cada uno con los campos de GET products/{producto}. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - La barra final es parte de la ruta: sin ella el módulo no responde con los datos. - Un estado que no existe no da error: devuelve una lista vacía. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/productos/mi-espacio/products/?estado=activo \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "products": [ { "id": "5b7e2c9a1d4f4e8b9c3a6d0f2e1b7c48", "nombre": "Café molido La Loma 500 g", "estado": "activo", "variantes": [{ "id": "a3f19c7e2b5d4c8e9f0a1b2c3d4e5f60", "sku": "CAF-MOL-500", "activo": true, … }], … } ] } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Crear producto https://dev.bentho.ai/api/productos/producto-crear/ POST/api/mod/productos/{espacio}/products/ # Crea un producto Da de alta un producto en borrador. Si no tiene opciones, se crea también su variante única, con el SKU que des o uno sacado del nombre. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | nombre | string | SÍ | Como lo ve tu cliente. | sku | string | no | El SKU de su variante única. Sin él, sale del nombre (cafe-molido-la-loma-500-g). Se ignora si mandas opciones. | product_type_id | string | no | Su tipo de producto. Con él, los atributos se validan contra los del tipo. | descripcion | string | no | La ficha, en texto libre. También se busca por ella. | atributos | object | no | Valores por nombre de atributo: {"Tueste": "medio"}. Los no declarados en el tipo se guardan tal cual. | metadata | object | no | Datos tuyos, libres. | imagenes | string[] | no | URL de sus imágenes. | opciones | [{ nombre, valores }] | no | Sus ejes de variación: [{"nombre": "Molienda", "valores": ["En grano", "Molido"]}]. Con opciones no se crea variante: añade cada una con Crea una variante. ## RESPUESTA 201 El producto, en borrador. | | CAMPO | TIPO | QUÉ ES | id · nombre | string | El id y el nombre. | product_type_id | string | null | Su tipo de producto, si tiene. | descripcion | string | null | La ficha en texto libre. | estado | "borrador" | "activo" | "archivado" | Ver estados. | atributos · metadata | object | Los valores de sus atributos y tus datos libres. | imagenes | string[] | Las URL de sus imágenes. | opciones[] | { id, nombre, valores } | Sus ejes de variación: "Talla" con ["S", "M", "L"]. Vacío si no tiene. | variantes[] | object | Las unidades vendibles, la más antigua primero, con id, product_id, sku, option_values, barcode, activo, external_id y created_at. | created_at · updated_at | number | Segundos desde 1970, con decimales. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 400 | product_type_id no es un tipo del espacio | tipo de producto no encontrado: … | 400 | Un atributo no cumple su tipo de producto: falta uno requerido, no es un número, no es un sí/no o no está entre sus opciones | atributo requerido ausente: '…' | 422 | Falta un campo obligatorio o uno no es de su tipo (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Nace en borrador y así no sale en la búsqueda. Para ponerlo a la venta, edítalo con {"estado": "activo"}. - Si el sku ya lo usa otra variante, no da error: la variante se crea con uno sacado del nombre. Mira variantes[0].sku en la respuesta. - La barra final es parte de la ruta: sin ella el módulo no responde con los datos. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/productos/mi-espacio/products/ \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "nombre": "Café molido La Loma 500 g", "sku": "CAF-MOL-500", "product_type_id": "7c1e9a3f5b2d4f6a8e0c2b4d6f8a1e3c", "descripcion": "Café de origen Huila, tueste medio. Para cafetera de filtro o prensa.", "atributos": { "Tueste": "medio", "Origen": "Huila" }, "imagenes": [ "https://example.com/img/cafe-la-loma-500.jpg" ] }' ``` ■ 201 CREATEDejemplo, con los campos del código ``` { "id": "5b7e2c9a1d4f4e8b9c3a6d0f2e1b7c48", "product_type_id": "7c1e9a3f5b2d4f6a8e0c2b4d6f8a1e3c", "nombre": "Café molido La Loma 500 g", "descripcion": "Café de origen Huila, tueste medio. Para cafetera de filtro o prensa.", "estado": "borrador", "atributos": { "Tueste": "medio", "Origen": "Huila" }, "metadata": {}, "imagenes": ["https://example.com/img/cafe-la-loma-500.jpg"], "opciones": [], "variantes": [ { "id": "a3f19c7e2b5d4c8e9f0a1b2c3d4e5f60", "product_id": "5b7e2c9a1d4f4e8b9c3a6d0f2e1b7c48", "sku": "CAF-MOL-500", "barcode": null, "option_values": {}, "activo": true, "external_id": null, "created_at": 1790000000.12 } ], "created_at": 1790000000.12, "updated_at": 1790000000.12 } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Producto https://dev.bentho.ai/api/productos/producto/ GET/api/mod/productos/{espacio}/products/{producto} # Un producto El producto con sus opciones y todas sus variantes. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. | producto | string | SÍ | El id del producto: el que devolvió crearlo (32 caracteres hexadecimales). ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | id · nombre | string | El id y el nombre. | product_type_id | string | null | Su tipo de producto, si tiene. | descripcion | string | null | La ficha en texto libre. | estado | "borrador" | "activo" | "archivado" | Ver estados. | atributos · metadata | object | Los valores de sus atributos y tus datos libres. | imagenes | string[] | Las URL de sus imágenes. | opciones[] | { id, nombre, valores } | Sus ejes de variación: "Talla" con ["S", "M", "L"]. Vacío si no tiene. | variantes[] | object | Las unidades vendibles, la más antigua primero, con id, product_id, sku, option_values, barcode, activo, external_id y created_at. | created_at · updated_at | number | Segundos desde 1970, con decimales. ## ESTADOS DE UN PRODUCTO | | CAMPO | TIPO | QUÉ ES | borrador | recién creado | No sale en la búsqueda (salvo con only_active=false). Todo producto nace así. | activo | a la venta | Sale en la búsqueda y se puede ofrecer. | archivado | retirado | Se conserva con sus variantes, pero no sale en la búsqueda. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. | 404 | No hay un producto con ese id en el espacio | producto no encontrado. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Sin precios ni stock: los da la cotización y la disponibilidad, por variante. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/productos/mi-espacio/products/5b7e2c9a1d4f4e8b9c3a6d0f2e1b7c48 \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "id": "5b7e2c9a1d4f4e8b9c3a6d0f2e1b7c48", "product_type_id": "7c1e9a3f5b2d4f6a8e0c2b4d6f8a1e3c", "nombre": "Café molido La Loma 500 g", "descripcion": "Café de origen Huila, tueste medio. Para cafetera de filtro o prensa.", "estado": "activo", "atributos": { "Tueste": "medio", "Origen": "Huila" }, "metadata": {}, "imagenes": ["https://example.com/img/cafe-la-loma-500.jpg"], "opciones": [], "variantes": [ { "id": "a3f19c7e2b5d4c8e9f0a1b2c3d4e5f60", "product_id": "5b7e2c9a1d4f4e8b9c3a6d0f2e1b7c48", "sku": "CAF-MOL-500", "barcode": null, "option_values": {}, "activo": true, "external_id": null, "created_at": 1790000000.12 } ], "created_at": 1790000000.12, "updated_at": 1790000310.4 } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Editar producto https://dev.bentho.ai/api/productos/producto-editar/ PATCH/api/mod/productos/{espacio}/products/{producto} # Edita un producto Cambia solo los campos que mandas. Es también como se activa o se archiva: {"estado": "activo"}. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. | producto | string | SÍ | El id del producto: el que devolvió crearlo (32 caracteres hexadecimales). ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | estado | "borrador" | "activo" | "archivado" | no | Ver estados. | nombre | string | no | El nombre nuevo. | descripcion | string | no | La ficha nueva. | product_type_id | string | no | Otro tipo. Los atributos se vuelven a validar contra él. | atributos · metadata | object | no | Reemplazan el objeto entero: manda también los valores que no cambian. | imagenes | string[] | no | Reemplaza la lista entera. ## RESPUESTA 200 El producto, ya cambiado. | | CAMPO | TIPO | QUÉ ES | id · nombre | string | El id y el nombre. | product_type_id | string | null | Su tipo de producto, si tiene. | descripcion | string | null | La ficha en texto libre. | estado | "borrador" | "activo" | "archivado" | Ver estados. | atributos · metadata | object | Los valores de sus atributos y tus datos libres. | imagenes | string[] | Las URL de sus imágenes. | opciones[] | { id, nombre, valores } | Sus ejes de variación: "Talla" con ["S", "M", "L"]. Vacío si no tiene. | variantes[] | object | Las unidades vendibles, la más antigua primero, con id, product_id, sku, option_values, barcode, activo, external_id y created_at. | created_at · updated_at | number | Segundos desde 1970, con decimales. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 404 | No hay un producto con ese id en el espacio | producto no encontrado. | 400 | product_type_id no es un tipo del espacio | tipo de producto no encontrado: … | 400 | Un atributo no cumple su tipo de producto: falta uno requerido, no es un número, no es un sí/no o no está entre sus opciones | atributo requerido ausente: '…' | 422 | estado no es borrador, activo ni archivado (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Un campo en null se ignora: no borra el valor que había. - Las opciones y las variantes no se cambian aquí: las variantes tienen su ruta propia. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X PATCH https://bentho.ai/api/mod/productos/mi-espacio/products/5b7e2c9a1d4f4e8b9c3a6d0f2e1b7c48 \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "estado": "activo" }' ``` ■ 200 OKejemplo, con los campos del código ``` { "id": "5b7e2c9a1d4f4e8b9c3a6d0f2e1b7c48", "product_type_id": "7c1e9a3f5b2d4f6a8e0c2b4d6f8a1e3c", "nombre": "Café molido La Loma 500 g", "descripcion": "Café de origen Huila, tueste medio. Para cafetera de filtro o prensa.", "estado": "activo", "atributos": { "Tueste": "medio", "Origen": "Huila" }, "metadata": {}, "imagenes": ["https://example.com/img/cafe-la-loma-500.jpg"], "opciones": [], "variantes": [ { "id": "a3f19c7e2b5d4c8e9f0a1b2c3d4e5f60", "product_id": "5b7e2c9a1d4f4e8b9c3a6d0f2e1b7c48", "sku": "CAF-MOL-500", "barcode": null, "option_values": {}, "activo": true, "external_id": null, "created_at": 1790000000.12 } ], "created_at": 1790000000.12, "updated_at": 1790000310.4 } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Borrar producto https://dev.bentho.ai/api/productos/producto-borrar/ DEL/api/mod/productos/{espacio}/products/{producto} # Borra un producto Borra el producto con sus opciones y sus variantes. No se puede deshacer. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. | producto | string | SÍ | El id del producto: el que devolvió crearlo (32 caracteres hexadecimales). ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | deleted | true | Hecho. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 404 | No hay un producto con ese id en el espacio | producto no encontrado. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Los precios y el stock de sus variantes no se borran solos. Quítalos con Borra un precio y Borra el stock, o archívalo en vez de borrarlo. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X DELETE https://bentho.ai/api/mod/productos/mi-espacio/products/5b7e2c9a1d4f4e8b9c3a6d0f2e1b7c48 \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKderivado del código ``` { "deleted": true } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Crear variante https://dev.bentho.ai/api/productos/variante-crear/ POST/api/mod/productos/{espacio}/products/{producto}/variants # Crea una variante Añade una unidad vendible a un producto: una talla, un sabor, una presentación. Cada variante tiene su SKU, su precio y su stock. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. | producto | string | SÍ | El id del producto: el que devolvió crearlo (32 caracteres hexadecimales). ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | sku | string | SÍ | Único en el espacio. | option_values | object | no | El valor de cada opción del producto: {"Molienda": "En grano"}. | barcode | string | no | Su código de barras. | external_id | string | no | Su id en tu software contable, para cruzar el stock. | activo | boolean | no | Si se vende. Por defecto true. ## RESPUESTA 201 La variante creada. | | CAMPO | TIPO | QUÉ ES | id · product_id | string | La variante y su producto. | sku | string | Su SKU: único en el espacio. | option_values | object | El valor de cada opción, p. ej. {"Molienda": "En grano"}. Vacío si no tiene. | barcode · external_id | string | null | Los que diste. | activo | boolean | Si se vende. Una inactiva no sale en la búsqueda. | created_at | number | Segundos desde 1970, con decimales. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 404 | No hay un producto con ese id en el espacio | producto no encontrado. | 409 | Ese SKU ya lo usa otra variante | el SKU ya existe: … | 422 | Falta un campo obligatorio o uno no es de su tipo (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - option_values no se comprueba contra las opciones del producto: manda los mismos nombres y valores que declaraste. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/productos/mi-espacio/products/5b7e2c9a1d4f4e8b9c3a6d0f2e1b7c48/variants \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "sku": "CAF-LOMA-500-GRANO", "option_values": { "Molienda": "En grano" }, "barcode": "7701234567890" }' ``` ■ 201 CREATEDejemplo, con los campos del código ``` { "id": "6d2b8f4a0c7e4b1d9a3f5c8e2b6d0a71", "product_id": "5b7e2c9a1d4f4e8b9c3a6d0f2e1b7c48", "sku": "CAF-LOMA-500-GRANO", "barcode": "7701234567890", "option_values": { "Molienda": "En grano" }, "activo": true, "external_id": null, "created_at": 1790000400.9 } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Editar variante https://dev.bentho.ai/api/productos/variante-editar/ PATCH/api/mod/productos/{espacio}/variants/{variante} # Edita una variante Activa o desactiva una variante, o cambia sus valores de opción, su código de barras o su id externo. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. | variante | string | SÍ | El id de la variante: sale en variantes[] del producto (32 caracteres hexadecimales). ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | sku | string | SÍ | Obligatorio, pero no se cambia: manda el que tiene. | activo | boolean | no | Si no lo mandas, vuelve a true. | option_values | object | no | Si no lo mandas, queda vacío. | barcode · external_id | string | no | Si no los mandas, no cambian. ## RESPUESTA 200 La variante, ya cambiada. | | CAMPO | TIPO | QUÉ ES | id · product_id | string | La variante y su producto. | sku | string | Su SKU: único en el espacio. | option_values | object | El valor de cada opción, p. ej. {"Molienda": "En grano"}. Vacío si no tiene. | barcode · external_id | string | null | Los que diste. | activo | boolean | Si se vende. Una inactiva no sale en la búsqueda. | created_at | number | Segundos desde 1970, con decimales. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 404 | No hay una variante con ese id en el espacio | variante no encontrada. | 422 | Falta un campo obligatorio o uno no es de su tipo (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Manda siempre activo y option_values con su valor actual: lo que no mandes vuelve a su valor por defecto. Para cambiar el SKU, crea otra variante y borra esta. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X PATCH https://bentho.ai/api/mod/productos/mi-espacio/variants/a3f19c7e2b5d4c8e9f0a1b2c3d4e5f60 \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "sku": "CAF-LOMA-500-GRANO", "activo": false, "option_values": { "Molienda": "En grano" } }' ``` ■ 200 OKejemplo, con los campos del código ``` { "id": "6d2b8f4a0c7e4b1d9a3f5c8e2b6d0a71", "product_id": "5b7e2c9a1d4f4e8b9c3a6d0f2e1b7c48", "sku": "CAF-LOMA-500-GRANO", "barcode": "7701234567890", "option_values": { "Molienda": "En grano" }, "activo": false, "external_id": null, "created_at": 1790000400.9 } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Borrar variante https://dev.bentho.ai/api/productos/variante-borrar/ DEL/api/mod/productos/{espacio}/variants/{variante} # Borra una variante Borra la variante. No se puede deshacer. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. | variante | string | SÍ | El id de la variante: sale en variantes[] del producto (32 caracteres hexadecimales). ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | deleted | true | Hecho. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 404 | No hay una variante con ese id en el espacio | variante no encontrada. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Sus precios y su stock no se borran solos. Si solo quieres dejar de venderla, desactívala. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X DELETE https://bentho.ai/api/mod/productos/mi-espacio/variants/a3f19c7e2b5d4c8e9f0a1b2c3d4e5f60 \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKderivado del código ``` { "deleted": true } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Tipos de producto https://dev.bentho.ai/api/productos/tipos/ GET/api/mod/productos/{espacio}/product-types/ # Los tipos de producto Los tipos de producto del espacio, por orden alfabético, con sus atributos. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | product_types | array | Cada uno con los campos de GET product-types/{tipo_producto}. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - La barra final es parte de la ruta: sin ella el módulo no responde con los datos. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/productos/mi-espacio/product-types/ \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKderivado del código ``` { "product_types": [ { "id": "7c1e9a3f5b2d4f6a8e0c2b4d6f8a1e3c", "nombre": "Café", "atributos": [ … ], … } ] } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Crear tipo https://dev.bentho.ai/api/productos/tipo-crear/ POST/api/mod/productos/{espacio}/product-types/ # Crea un tipo de producto Un tipo dice qué atributos lleva un producto («Café»: tueste y origen; «Repuesto»: marca y referencia). Los productos de ese tipo se validan contra él. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | nombre | string | SÍ | El nombre del tipo. | atributos | array | no | Los atributos: ver atributos[]. | track_inventory | boolean | no | Si sus productos llevan stock (false para servicios). Por defecto true. Hoy es informativo. | activo | boolean | no | Por defecto true. ## RESPUESTA 201 El tipo creado. | | CAMPO | TIPO | QUÉ ES | id · nombre | string | El id y el nombre. | atributos | [{ id, nombre, tipo, requerido, opciones }] | Los atributos que declaraste (id puede venir null). | track_inventory · activo | boolean | Los que diste. | created_at | number | Segundos desde 1970, con decimales. ## ATRIBUTOS[] | | CAMPO | TIPO | QUÉ ES | nombre | string | La clave en atributos del producto. | tipo | "texto" | "numero" | "booleano" | "opcion" | Qué valor admite. booleano acepta true, false, 1, 0, si y no. Por defecto texto. | requerido | boolean | Si un producto del tipo tiene que traerlo. Por defecto false. | opciones | string[] | Solo con opcion: los valores válidos. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 422 | Falta un campo obligatorio o uno no es de su tipo (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Un producto puede traer atributos que el tipo no declara: se guardan sin validar. Solo se comprueban los declarados. - La barra final es parte de la ruta: sin ella el módulo no responde con los datos. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/productos/mi-espacio/product-types/ \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "nombre": "Café", "atributos": [ { "nombre": "Tueste", "tipo": "opcion", "requerido": true, "opciones": [ "claro", "medio", "oscuro" ] }, { "nombre": "Origen", "tipo": "texto" } ] }' ``` ■ 201 CREATEDejemplo, con los campos del código ``` { "id": "7c1e9a3f5b2d4f6a8e0c2b4d6f8a1e3c", "nombre": "Café", "track_inventory": true, "activo": true, "atributos": [ { "id": null, "nombre": "Tueste", "tipo": "opcion", "requerido": true, "opciones": ["claro", "medio", "oscuro"] }, { "id": null, "nombre": "Origen", "tipo": "texto", "requerido": false, "opciones": [] } ], "created_at": 1789990000.5 } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Tipo de producto https://dev.bentho.ai/api/productos/tipo/ GET/api/mod/productos/{espacio}/product-types/{tipo_producto} # Un tipo de producto El tipo con sus atributos. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. | tipo_producto | string | SÍ | El id del tipo de producto: el que devolvió crearlo (32 caracteres hexadecimales). ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | id · nombre | string | El id y el nombre. | atributos | [{ id, nombre, tipo, requerido, opciones }] | Los atributos que declaraste (id puede venir null). | track_inventory · activo | boolean | Los que diste. | created_at | number | Segundos desde 1970, con decimales. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. | 404 | No hay un tipo de producto con ese id en el espacio | tipo de producto no encontrado. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/productos/mi-espacio/product-types/7c1e9a3f5b2d4f6a8e0c2b4d6f8a1e3c \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "id": "7c1e9a3f5b2d4f6a8e0c2b4d6f8a1e3c", "nombre": "Café", "track_inventory": true, "activo": true, "atributos": [ { "id": null, "nombre": "Tueste", "tipo": "opcion", "requerido": true, "opciones": ["claro", "medio", "oscuro"] }, { "id": null, "nombre": "Origen", "tipo": "texto", "requerido": false, "opciones": [] } ], "created_at": 1789990000.5 } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Borrar tipo https://dev.bentho.ai/api/productos/tipo-borrar/ DEL/api/mod/productos/{espacio}/product-types/{tipo_producto} # Borra un tipo de producto Borra el tipo. Solo se puede si ningún producto lo usa. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. | tipo_producto | string | SÍ | El id del tipo de producto: el que devolvió crearlo (32 caracteres hexadecimales). ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | deleted | true | Hecho. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 404 | No hay un tipo de producto con ese id en el espacio | tipo de producto no encontrado. | 500 | Algún producto todavía usa ese tipo | Error interno del servidor. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Antes, cambia de tipo o borra los productos que lo usan. Un tipo que sigue en uso no se borra y la respuesta es un 500 genérico. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X DELETE https://bentho.ai/api/mod/productos/mi-espacio/product-types/7c1e9a3f5b2d4f6a8e0c2b4d6f8a1e3c \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKderivado del código ``` { "deleted": true } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Buscar https://dev.bentho.ai/api/productos/buscar/ GET/api/mod/productos/{espacio}/catalog/search # Busca en el catálogo Encuentra variantes por lo que escribe una persona: «café la loma de medio kilo» encuentra «Café molido La Loma 500 g». Busca en el nombre, el SKU y, en consultas cortas, la descripción. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. | q | string, query | SÍ | Lo que se busca, en lenguaje natural. | only_active | bool, query | no | Solo productos activos y variantes activas. Por defecto true. | limit | int, query | no | Cuántos resultados como mucho. Por defecto 20. ## RESPUESTA 200 Una fila por variante, la que mejor encaja primero. | | CAMPO | TIPO | QUÉ ES | results[].product_id | string | El producto. | results[].variant_id | string | La variante. | results[].sku · nombre | string | El SKU y el nombre del producto. | results[].estado | string | El estado del producto. | results[].option_values | object | Los valores de opción de la variante. | results[].via_descripcion | true | Solo si entró por la descripción y no por el nombre o el SKU. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. | 422 | Falta q (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - No distingue tildes ni mayúsculas, entiende plurales («jabones» encuentra «Jabón») y unidades («1 litro» encuentra «1000ml», «medio kilo» no: escribe «500 g»). - Las palabras que no aparecen en ningún producto no tumban la búsqueda: se ignoran. Si nada encaja, la lista viene vacía. - Para el precio de lo encontrado, cotiza sus variant_id. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/productos/mi-espacio/catalog/search?q=cafe+la+loma+molido&limit=5 \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "results": [ { "product_id": "5b7e2c9a1d4f4e8b9c3a6d0f2e1b7c48", "variant_id": "a3f19c7e2b5d4c8e9f0a1b2c3d4e5f60", "sku": "CAF-MOL-500", "nombre": "Café molido La Loma 500 g", "estado": "activo", "option_values": {} } ] } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Fichas de variantes https://dev.bentho.ai/api/productos/contexto/ GET/api/mod/productos/{espacio}/catalog/context # Las fichas de unas variantes Para varias variantes a la vez, una ficha compacta con su producto, descripción y atributos. Sin precios. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. | ids | string, query, repetible | no | Un variant_id por cada ids=. Los que no existen se saltan. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | context[].variant_id · sku | string | La variante. | context[].option_values | object | Sus valores de opción. | context[].product_id · nombre | string | Su producto. | context[].descripcion · estado | string | La ficha y el estado del producto. | context[].atributos | object | Los atributos del producto. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/productos/mi-espacio/catalog/context?ids=a3f19c7e2b5d4c8e9f0a1b2c3d4e5f60&ids=6d2b8f4a0c7e4b1d9a3f5c8e2b6d0a71 \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "context": [ { "variant_id": "a3f19c7e2b5d4c8e9f0a1b2c3d4e5f60", "sku": "CAF-MOL-500", "option_values": {}, "product_id": "5b7e2c9a1d4f4e8b9c3a6d0f2e1b7c48", "nombre": "Café molido La Loma 500 g", "descripcion": "Café de origen Huila, tueste medio. Para cafetera de filtro o prensa.", "estado": "activo", "atributos": { "Tueste": "medio", "Origen": "Huila" } } ] } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Componentes de combo https://dev.bentho.ai/api/productos/combo/ GET/api/mod/productos/{espacio}/catalog/bundle-components # Los componentes de un combo Qué lleva un producto combo: cada variante componente con su cantidad. Vacío si el producto no es un combo. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. | product_id | string, query | SÍ | El producto. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | componentes[].variant_id · sku | string | La variante componente. | componentes[].cantidad | string | Cuántas lleva cada combo. | componentes[].nombre | string | null | El nombre de su producto. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. | 422 | Falta product_id (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Los combos no se arman por la API: esta ruta solo los lee. El stock de un combo es el de su componente más escaso, y venderlo descuenta cada componente. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/productos/mi-espacio/catalog/bundle-components?product_id=e7a1c3e5f7b94d2a6c8e0f2b4d6a8c30 \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "componentes": [ { "variant_id": "a3f19c7e2b5d4c8e9f0a1b2c3d4e5f60", "cantidad": "2", "sku": "CAF-MOL-500", "nombre": "Café molido La Loma 500 g" }, { "variant_id": "6d2b8f4a0c7e4b1d9a3f5c8e2b6d0a71", "cantidad": "1", "sku": "JAB-CAL-100", "nombre": "Jabón de caléndula 100 g" } ] } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Importar catálogo https://dev.bentho.ai/api/productos/importar/ POST/api/mod/productos/{espacio}/catalog/import # Importa un catálogo desde un archivo Sube tu catálogo en CSV, Excel o PDF. Queda en una importación en draft para que la revises: el catálogo no cambia hasta que la activas. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. ## CUERPO · MULTIPART/FORM-DATA | | CAMPO | TIPO | REQUERIDO | QUÉ ES | file | archivo | SÍ | Un .csv, .xlsx o .pdf con una tabla, hasta 25 MB. Las columnas que entiende, en la guía. ## RESPUESTA 201 | | CAMPO | TIPO | QUÉ ES | import_id | string | La importación. Guárdalo. | estado | "draft" | Esperando revisión. | n_rows | integer | Las filas que reconoció. | origen | "csv" | "xlsx" | "pdf" | El formato del archivo. | warnings | string[] | Avisos del archivo entero, p. ej. si no reconoció ninguna tabla. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 400 | El archivo no trae nada | archivo vacío. | 400 | La extensión no es .csv, .xlsx ni .pdf | extensión no soportada para catálogo: … | 413 | El archivo pasa de 25 MB | archivo demasiado grande (máximo 26214400 bytes). | 422 | Falta el campo file (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Si n_rows es 0, no encontró una columna de nombre ni de SKU: mira warnings. - Excel, en .xlsx. Un .xls antiguo no se lee: guárdalo como .xlsx o como CSV. De un PDF solo se leen las tablas. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/productos/mi-espacio/catalog/import \ -H "Authorization: Bearer $BENTHO_KEY" \ -F "file=@catalogo.csv" ``` ■ 201 CREATEDejemplo, con los campos del código ``` { "import_id": "d94b1f6e3a8c4d2b9e7f1a5c3b8d6e02", "estado": "draft", "n_rows": 42, "origen": "csv", "warnings": [], … } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Importaciones https://dev.bentho.ai/api/productos/importaciones/ GET/api/mod/productos/{espacio}/catalog/imports/ # Las importaciones Las importaciones de catálogo, la más reciente primero. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. | estado | string, query | no | Solo las de un estado: draft o validada. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | imports | array | Cada una con los campos de GET catalog/imports/{importacion}, sin filas. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - La barra final es parte de la ruta: sin ella el módulo no responde con los datos. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/productos/mi-espacio/catalog/imports/?estado=draft \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKderivado del código ``` { "imports": [ { "id": "d94b1f6e3a8c4d2b9e7f1a5c3b8d6e02", "sha256": "8f2c1a9e…", "origen": "csv", "doc_source": "catalogo.csv", "estado": "draft", "created_at": 1790000500.2, "activated_at": null, "n_rows": 42, … } ] } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Importación https://dev.bentho.ai/api/productos/importacion/ GET/api/mod/productos/{espacio}/catalog/imports/{importacion} # Una importación y sus filas La importación y, con with_rows=true, cada fila tal como la leyó: es lo que revisas antes de activar. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. | importacion | string | SÍ | El id de la importación: el import_id que devolvió subir el archivo (32 caracteres hexadecimales). | with_rows | bool, query | no | Trae también las filas. Por defecto false. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | id | string | La importación. | estado | "draft" | "validada" | draft: esperando revisión. validada: ya se aplicó al catálogo. | origen | "csv" | "xlsx" | "pdf" | El formato del archivo. | doc_source | string | El nombre del archivo que subiste. | sha256 | string | La huella del archivo. | n_rows | integer | Las filas que quedan en la importación. | created_at | number | Segundos desde 1970, con decimales. | activated_at | number | null | Cuándo se activó. null mientras está en draft. | rows[].id | string | La fila: para descartarla. | rows[].payload | { nombre, sku, tipo, descripcion, precio, atributos } | Lo que leyó. precio, en texto decimal o null; atributos, las columnas que no reconoció. | rows[].warnings | string[] | Avisos de esa fila, p. ej. «precio ilegible: …». ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. | 404 | No hay una importación con ese id | import no encontrado. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/productos/mi-espacio/catalog/imports/d94b1f6e3a8c4d2b9e7f1a5c3b8d6e02?with_rows=true \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "id": "d94b1f6e3a8c4d2b9e7f1a5c3b8d6e02", "estado": "draft", "origen": "csv", "doc_source": "catalogo.csv", "n_rows": 42, "activated_at": null, "rows": [ { "id": "2f8a6c4e1b9d4a7c8e3f5b1d9a7c6e44", "payload": { "nombre": "Jabón de caléndula 100 g", "sku": "JAB-CAL-100", "tipo": "Cuidado personal", "descripcion": "Jabón artesanal para piel sensible.", "precio": "18900", "atributos": { "Presentación": "Barra" } }, "warnings": [] }, … ], … } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Descartar fila https://dev.bentho.ai/api/productos/importacion-fila-borrar/ DEL/api/mod/productos/{espacio}/catalog/imports/{importacion}/rows/{fila} # Descarta una fila Quita una fila de una importación en draft para que no entre al catálogo. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. | importacion | string | SÍ | El id de la importación: el import_id que devolvió subir el archivo (32 caracteres hexadecimales). | fila | string | SÍ | El id de la fila: sale en rows[] de la importación (32 caracteres hexadecimales). ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | deleted | true | Hecho. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 404 | La fila no existe, o la importación ya no está en draft | fila no encontrada o import no está en draft. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X DELETE https://bentho.ai/api/mod/productos/mi-espacio/catalog/imports/d94b1f6e3a8c4d2b9e7f1a5c3b8d6e02/rows/2f8a6c4e1b9d4a7c8e3f5b1d9a7c6e44 \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKderivado del código ``` { "deleted": true } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Activar importación https://dev.bentho.ai/api/productos/importacion-activar/ POST/api/mod/productos/{espacio}/catalog/imports/{importacion}/activate # Activa una importación Pasa las filas al catálogo: cada una crea un producto activo con su variante. La importación queda en validada. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. | importacion | string | SÍ | El id de la importación: el import_id que devolvió subir el archivo (32 caracteres hexadecimales). ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | import_id | string | La importación. | estado | "validada" | Ya aplicada. | productos_creados | integer | Productos que entraron. | variantes_creadas | integer | Variantes que entraron. | errores[] | { row_id, error } | Las filas que no entraron y por qué. Una fila mala no para las demás. | advertencias[] | { tipo, productos } | nombre_duplicado con los dos nombres de productos activos que se parecen demasiado. Solo avisa: no cambia nada. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 400 | No hay una importación con ese id | import no encontrado | 400 | La importación ya se activó | solo se puede activar un import en draft (estado actual: validada) Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Cada fila crea un producto nuevo: activar dos veces el mismo catálogo lo duplica. Si un SKU ya existe, la variante nueva recibe uno sacado del nombre. - La columna de precio no fija ningún precio: se guarda en metadata.precio_sugerido. Los precios se fijan con Fija un precio. - La columna de tipo usa el tipo de producto con ese nombre, o lo crea. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X POST https://bentho.ai/api/mod/productos/mi-espacio/catalog/imports/d94b1f6e3a8c4d2b9e7f1a5c3b8d6e02/activate \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "import_id": "d94b1f6e3a8c4d2b9e7f1a5c3b8d6e02", "estado": "validada", "productos_creados": 41, "variantes_creadas": 41, "errores": [{ "row_id": "2f8a6c4e1b9d4a7c8e3f5b1d9a7c6e44", "error": "fila sin nombre ni sku" }], "advertencias": [ { "tipo": "nombre_duplicado", "productos": ["Jabón de caléndula", "Jabón de caléndula artesanal"] } ] } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Listas de precios https://dev.bentho.ai/api/productos/listas/ GET/api/mod/productos/{espacio}/price-lists # Las listas de precios Las listas de precios del espacio, la de mayor prioridad primero. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | price_lists | array | Cada una con los campos de GET price-lists/{lista}. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/productos/mi-espacio/price-lists \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKderivado del código ``` { "price_lists": [ { "id": "c5d3e1f9a7b54c2e8d6f4a2c0e9b7d15", "nombre": "Precios de tienda", "tipo": "base", "prioridad": 0, "starts_at": null, "ends_at": null, "activo": true, "created_at": 1790000600.8 } ] } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Crear lista https://dev.bentho.ai/api/productos/lista-crear/ POST/api/mod/productos/{espacio}/price-lists # Crea una lista de precios Una lista agrupa precios con una prioridad y, si quieres, una vigencia: la lista base de la tienda, una oferta de temporada, un precio puntual que manda sobre los demás. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | nombre | string | SÍ | Para reconocerla. | tipo | "base" | "oferta" | "override" | no | A igual prioridad, override gana a oferta y oferta a base. Por defecto base. | prioridad | integer | no | Mayor gana. Por defecto 0. | starts_at · ends_at | string | no | Su vigencia, en ISO 8601. Sin ellas, vale siempre. | activo | boolean | no | Por defecto true. ## RESPUESTA 201 La lista creada. | | CAMPO | TIPO | QUÉ ES | id · nombre | string | El id y el nombre. | tipo | "base" | "oferta" | "override" | La clase de lista. | prioridad | integer | Mayor gana. | starts_at · ends_at | string | null | Su vigencia, en ISO 8601. null: sin límite por ese lado. | activo | boolean | Una lista inactiva no cotiza. | created_at | number | Segundos desde 1970, con decimales. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 422 | Falta un campo obligatorio o uno no es de su tipo (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Cómo se elige el precio cuando una variante está en varias listas, en la guía. - Sin zona horaria, las fechas se toman en UTC: 2026-12-01T05:00:00Z es la medianoche del 1 de diciembre en Bogotá. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/productos/mi-espacio/price-lists \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "nombre": "Precios de tienda", "tipo": "base" }' ``` ■ 201 CREATEDejemplo, con los campos del código ``` { "id": "c5d3e1f9a7b54c2e8d6f4a2c0e9b7d15", "nombre": "Precios de tienda", "tipo": "base", "prioridad": 0, "starts_at": null, "ends_at": null, "activo": true, "created_at": 1790000600.8 } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Lista de precios https://dev.bentho.ai/api/productos/lista/ GET/api/mod/productos/{espacio}/price-lists/{lista} # Una lista de precios La lista, sin sus precios: esos salen en GET prices. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. | lista | string | SÍ | El id de la lista de precios: el que devolvió crearla (32 caracteres hexadecimales). ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | id · nombre | string | El id y el nombre. | tipo | "base" | "oferta" | "override" | La clase de lista. | prioridad | integer | Mayor gana. | starts_at · ends_at | string | null | Su vigencia, en ISO 8601. null: sin límite por ese lado. | activo | boolean | Una lista inactiva no cotiza. | created_at | number | Segundos desde 1970, con decimales. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. | 404 | No hay una lista de precios con ese id en el espacio | lista de precios no encontrada. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/productos/mi-espacio/price-lists/c5d3e1f9a7b54c2e8d6f4a2c0e9b7d15 \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "id": "c5d3e1f9a7b54c2e8d6f4a2c0e9b7d15", "nombre": "Precios de tienda", "tipo": "base", "prioridad": 0, "starts_at": null, "ends_at": null, "activo": true, "created_at": 1790000600.8 } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Editar lista https://dev.bentho.ai/api/productos/lista-editar/ PATCH/api/mod/productos/{espacio}/price-lists/{lista} # Edita una lista de precios Cambia solo los campos que mandas: nombre, tipo, prioridad, vigencia o si está activa. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. | lista | string | SÍ | El id de la lista de precios: el que devolvió crearla (32 caracteres hexadecimales). ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | nombre | string | no | El nombre nuevo. | tipo | "base" | "oferta" | "override" | no | El tipo nuevo. | prioridad | integer | no | La prioridad nueva. | starts_at · ends_at | string | no | La vigencia nueva, en ISO 8601. | activo | boolean | no | false la apaga: sus precios dejan de cotizar. ## RESPUESTA 200 La lista, ya cambiada. | | CAMPO | TIPO | QUÉ ES | id · nombre | string | El id y el nombre. | tipo | "base" | "oferta" | "override" | La clase de lista. | prioridad | integer | Mayor gana. | starts_at · ends_at | string | null | Su vigencia, en ISO 8601. null: sin límite por ese lado. | activo | boolean | Una lista inactiva no cotiza. | created_at | number | Segundos desde 1970, con decimales. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 404 | No hay una lista de precios con ese id en el espacio | lista de precios no encontrada. | 422 | Falta un campo obligatorio o uno no es de su tipo (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Una vigencia puesta no se quita con null: para dejarla sin fechas, crea otra lista. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X PATCH https://bentho.ai/api/mod/productos/mi-espacio/price-lists/c5d3e1f9a7b54c2e8d6f4a2c0e9b7d15 \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "activo": false }' ``` ■ 200 OKejemplo, con los campos del código ``` { "id": "c5d3e1f9a7b54c2e8d6f4a2c0e9b7d15", "nombre": "Precios de tienda", "tipo": "base", "prioridad": 0, "starts_at": null, "ends_at": null, "activo": false, "created_at": 1790000600.8 } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Borrar lista https://dev.bentho.ai/api/productos/lista-borrar/ DEL/api/mod/productos/{espacio}/price-lists/{lista} # Borra una lista de precios Borra la lista y todos sus precios. No se puede deshacer. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. | lista | string | SÍ | El id de la lista de precios: el que devolvió crearla (32 caracteres hexadecimales). ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | deleted | true | Hecho. | id | string | La lista borrada. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 404 | No hay una lista de precios con ese id en el espacio | lista de precios no encontrada. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Para dejar de usarla sin perder sus precios, apágala con {"activo": false}. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X DELETE https://bentho.ai/api/mod/productos/mi-espacio/price-lists/c5d3e1f9a7b54c2e8d6f4a2c0e9b7d15 \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKderivado del código ``` { "deleted": true, "id": "c5d3e1f9a7b54c2e8d6f4a2c0e9b7d15" } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Fijar precio https://dev.bentho.ai/api/productos/precio-crear/ POST/api/mod/productos/{espacio}/prices # Fija un precio Pone el precio de una variante en una lista, en una moneda y desde una cantidad. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | price_list_id | string | SÍ | La lista. | variant_id | string | SÍ | La variante. | monto | number | string | SÍ | El precio unitario, en decimal. | moneda | string | no | El código de moneda. Por defecto "COP". | min_qty | number | string | no | Desde cuántas unidades vale: 12 para un precio por docena. Por defecto 1. ## RESPUESTA 201 El precio creado. | | CAMPO | TIPO | QUÉ ES | id · price_list_id · variant_id | string | El precio, su lista y su variante. | moneda | string | El código de moneda, p. ej. "COP". | monto | string | El precio unitario, en texto decimal. | min_qty | string | Desde cuántas unidades vale (escalón por cantidad). | created_at | number | Segundos desde 1970, con decimales. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 404 | price_list_id no es una lista del espacio | la lista de precios indicada no existe. | 422 | Falta un campo obligatorio o uno no es de su tipo (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Fijar un precio con la misma lista, variante, moneda y min_qty que uno que ya existe lo reemplaza: el anterior deja de existir y la respuesta trae el id nuevo. Así se cambia un precio, sin borrarlo antes. - moneda y min_qty se comparan tal como los mandas: "COP" y "cop", o 12 y "12.00", son precios distintos y conviven. - No comprueba que la variante exista: usa un variant_id del catálogo. - Las cantidades y los importes viajan como texto decimal ("32000", "1.5") para no perder precisión. Al mandarlos vale un número o un texto. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/productos/mi-espacio/prices \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "price_list_id": "c5d3e1f9a7b54c2e8d6f4a2c0e9b7d15", "variant_id": "a3f19c7e2b5d4c8e9f0a1b2c3d4e5f60", "monto": 32000 }' ``` ■ 201 CREATEDejemplo, con los campos del código ``` { "id": "9e1d7b5c3a8f4e2d6b0c9a7e5f3d1b86", "price_list_id": "c5d3e1f9a7b54c2e8d6f4a2c0e9b7d15", "variant_id": "a3f19c7e2b5d4c8e9f0a1b2c3d4e5f60", "moneda": "COP", "monto": "32000", "min_qty": "1", "created_at": 1790000700.3 } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Precios https://dev.bentho.ai/api/productos/precios/ GET/api/mod/productos/{espacio}/prices # Los precios Todos los precios de todas las listas, o los de una variante. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. | variant_id | string, query | no | Solo los de esta variante. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | prices | array | Cada uno con los campos de Fija un precio. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Lista lo guardado, no el precio que gana: para saber qué se cobra hoy, cotiza. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/productos/mi-espacio/prices?variant_id=a3f19c7e2b5d4c8e9f0a1b2c3d4e5f60 \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKderivado del código ``` { "prices": [ { "id": "9e1d7b5c3a8f4e2d6b0c9a7e5f3d1b86", "price_list_id": "c5d3e1f9a7b54c2e8d6f4a2c0e9b7d15", "variant_id": "a3f19c7e2b5d4c8e9f0a1b2c3d4e5f60", "moneda": "COP", "monto": "32000", "min_qty": "1", "created_at": 1790000700.3 } ] } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Borrar precio https://dev.bentho.ai/api/productos/precio-borrar/ DEL/api/mod/productos/{espacio}/prices/{precio} # Borra un precio Quita un precio de su lista. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. | precio | string | SÍ | El id del precio: el que devolvió fijarlo (32 caracteres hexadecimales). ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | deleted | true | Hecho. | id | string | El precio borrado. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 404 | No hay un precio con ese id | precio no encontrado. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X DELETE https://bentho.ai/api/mod/productos/mi-espacio/prices/9e1d7b5c3a8f4e2d6b0c9a7e5f3d1b86 \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKderivado del código ``` { "deleted": true, "id": "9e1d7b5c3a8f4e2d6b0c9a7e5f3d1b86" } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Cotizar carrito https://dev.bentho.ai/api/productos/cotizar/ POST/api/mod/productos/{espacio}/price-quote # Cotiza un carrito Le das las variantes y cantidades; te devuelve el precio de cada línea, las promociones que aplican, el total y una huella de los datos usados. Las cifras salen siempre de aquí, nunca estimadas. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | lineas | [{ variant_id, cantidad }] | SÍ | Al menos una. cantidad, por defecto 1. | moneda | string | no | Solo cotizan los precios en esta moneda. Por defecto "COP". | fecha | string | no | Para qué momento, en ISO 8601. Por defecto, ahora. | codigos_promo | string[] | no | Los códigos que escribió tu cliente. | canal | string | no | Por dónde compra, para las promociones que lo miran. Por defecto "chat". ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | lineas[].variant_id · sku | string | La variante cotizada. | lineas[].nombre | string | El nombre de su producto. | lineas[].cantidad | string | La cantidad. | lineas[].precio_unitario | string | El precio que ganó. | lineas[].adjustments[] | { promotion_id, regla, target, monto } | Cada descuento de la línea: la promoción, su nombre, item u order, y el importe en negativo. | lineas[].total_linea | string | precio_unitario × cantidad, menos sus descuentos. | subtotal | string | La suma antes de descuentos. | descuento_total | string | Lo descontado, en positivo. | total | string | Lo que se cobra. Nunca menos de 0. | moneda | string | La de la cotización. | catalog_version | string | Huella de los precios, promociones y catálogo usados. Dos cotizaciones con la misma salieron de los mismos datos. | no_cotizados | string[] | Los variant_id que no se pudieron cotizar: no existen, no tienen precio vigente en esa moneda o la cantidad no es mayor que 0. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 400 | lineas viene vacía | el carrito no tiene líneas. | 422 | Falta un campo obligatorio o uno no es de su tipo (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - No cambia nada ni gasta usos de las promociones, pero es un POST: pide clave escribe. - Una línea sin precio no se cobra a 0: sale en no_cotizados y no suma al total. - Guarda catalog_version con el pedido: dice con qué precios se cotizó. - Las cantidades y los importes viajan como texto decimal ("32000", "1.5") para no perder precisión. Al mandarlos vale un número o un texto. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/productos/mi-espacio/price-quote \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "lineas": [ { "variant_id": "a3f19c7e2b5d4c8e9f0a1b2c3d4e5f60", "cantidad": 2 } ], "codigos_promo": [ "BIENVENIDA" ] }' ``` ■ 200 OKejemplo, con los campos del código ``` { "lineas": [ { "variant_id": "a3f19c7e2b5d4c8e9f0a1b2c3d4e5f60", "sku": "CAF-MOL-500", "nombre": "Café molido La Loma 500 g", "cantidad": "2", "precio_unitario": "32000", "adjustments": [ { "promotion_id": "4a6c8e0b2d4f4a6c9e1b3d5f7a9c2e57", "regla": "10 % de bienvenida", "target": "order", "monto": "-6400" } ], "total_linea": "57600" } ], "subtotal": "64000", "descuento_total": "6400", "total": "57600", "moneda": "COP", "catalog_version": "3e9b0c7a5d1f4e8b2c6a9d0f3e7b1c5a8d2f6e0b4c9a7d3f1e5b8c2a6d0f4e91", "no_cotizados": [] } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Promociones https://dev.bentho.ai/api/productos/promociones/ GET/api/mod/productos/{espacio}/promotions # Las promociones Las promociones del espacio, la de mayor prioridad primero. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. | solo_activas | bool, query | no | Solo las de estado activo. Por defecto false. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | promotions | array | Cada una con los campos de GET promotions/{promocion}. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/productos/mi-espacio/promotions?solo_activas=true \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKderivado del código ``` { "promotions": [ { "id": "4a6c8e0b2d4f4a6c9e1b3d5f7a9c2e57", "codigo": "BIENVENIDA", "nombre": "10 % de bienvenida", "estado": "activo", … } ] } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Crear promoción https://dev.bentho.ai/api/productos/promocion-crear/ POST/api/mod/productos/{espacio}/promotions # Crea una promoción Una regla de descuento: cuándo aplica (predicado) y cuánto rebaja (metodo). Se aplica sola en cada cotización que la cumpla. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | nombre | string | SÍ | Sale como regla en cada descuento. | metodo | object | no | Cuánto descuenta: ver metodo. Por defecto, 0 %. | predicado | object | no | Cuándo aplica: ver predicado. Vacío: siempre. | codigo | string | no | Su código, como etiqueta. Para exigirlo, ponlo en el predicado. | estado | string | no | activo o inactivo. Por defecto activo. | grupo_exclusividad | string | no | Las del mismo grupo no se suman: aplica la de mayor prioridad. | prioridad | integer | no | Mayor gana. Por defecto 0. | limite_usos | integer | no | Cuántas ventas confirmadas pueden usarla. Sin él, sin límite. | starts_at · ends_at | string | no | Su vigencia, en ISO 8601. | campaign_id | string | no | Una campaña con presupuesto: agotado, la promoción deja de aplicar. ## RESPUESTA 201 La promoción creada. | | CAMPO | TIPO | QUÉ ES | id · nombre | string | El id y el nombre. | codigo | string | null | Su código, como etiqueta. | estado | string | activo o inactivo. Solo activo descuenta. | grupo_exclusividad | string | null | Una sola promoción por grupo en cada cotización. | prioridad | integer | Mayor gana dentro de su grupo. | limite_usos | integer | null | Cuántas ventas pueden usarla. null: sin límite. | starts_at · ends_at | string | null | Su vigencia, en ISO 8601. | predicado · metodo | object | Cuándo aplica y cuánto descuenta: ver metodo y predicado. | campaign_id | string | null | Su campaña, si tiene. | created_at | number | Segundos desde 1970, con decimales. ## METODO | | CAMPO | TIPO | QUÉ ES | metodo | "percentage" | "fixed" | percentage: valor es un porcentaje (10 = 10 %), redondeado a unidades. fixed: valor es el importe a descontar. Por defecto percentage. | target | "item" | "order" | item: rebaja líneas del carrito. order: rebaja el subtotal y lo reparte entre las líneas. Por defecto order. | allocation | "each" | "once" | Solo con item: each rebaja cada línea que encaja; once, solo la primera. Por defecto each. | valor | number | string | El porcentaje o el importe. Por defecto 0. ## PREDICADO | | CAMPO | TIPO | QUÉ ES | field · cmp · value | una condición | field es subtotal, cantidad_total, variant_ids, codigos_promo o canal. cmp es eq, gt, gte, lt o lte para cifras; contains para las listas (variant_ids, codigos_promo); in para que canal esté en una lista. | op · clauses | "and" | "or", array | Une varias condiciones. Se pueden anidar. | {} | vacío | Sin condición: aplica a todos los carritos. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 422 | Falta un campo obligatorio o uno no es de su tipo (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - codigo no se exige solo: una promoción con código pero sin condición sobre codigos_promo se aplica a todos los carritos. - Para rebajar un producto concreto, usa target: "item" y la condición {"field": "variant_ids", "cmp": "contains", "value": ""}: el descuento cae solo en esa línea. - No se edita: para cambiarla, bórrala y créala otra vez. Más ejemplos en la guía. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/productos/mi-espacio/promotions \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "nombre": "10 % de bienvenida", "codigo": "BIENVENIDA", "grupo_exclusividad": "bienvenida", "limite_usos": 100, "ends_at": "2026-12-31T23:59:59", "predicado": { "op": "and", "clauses": [ { "field": "codigos_promo", "cmp": "contains", "value": "BIENVENIDA" }, { "field": "subtotal", "cmp": "gte", "value": 50000 } ] }, "metodo": { "metodo": "percentage", "target": "order", "valor": 10 } }' ``` ■ 201 CREATEDejemplo, con los campos del código ``` { "id": "4a6c8e0b2d4f4a6c9e1b3d5f7a9c2e57", "codigo": "BIENVENIDA", "nombre": "10 % de bienvenida", "estado": "activo", "grupo_exclusividad": "bienvenida", "prioridad": 0, "limite_usos": 100, "starts_at": null, "ends_at": "2026-12-31T23:59:59", "predicado": { "op": "and", "clauses": [ { "field": "codigos_promo", "cmp": "contains", "value": "BIENVENIDA" }, { "field": "subtotal", "cmp": "gte", "value": 50000 } ] }, "metodo": { "metodo": "percentage", "target": "order", "allocation": "each", "valor": "10", … }, "campaign_id": null, "created_at": 1790000800.6 } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Promoción https://dev.bentho.ai/api/productos/promocion/ GET/api/mod/productos/{espacio}/promotions/{promocion} # Una promoción La promoción con su predicado y su método. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. | promocion | string | SÍ | El id de la promoción: el que devolvió crearla (32 caracteres hexadecimales). ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | id · nombre | string | El id y el nombre. | codigo | string | null | Su código, como etiqueta. | estado | string | activo o inactivo. Solo activo descuenta. | grupo_exclusividad | string | null | Una sola promoción por grupo en cada cotización. | prioridad | integer | Mayor gana dentro de su grupo. | limite_usos | integer | null | Cuántas ventas pueden usarla. null: sin límite. | starts_at · ends_at | string | null | Su vigencia, en ISO 8601. | predicado · metodo | object | Cuándo aplica y cuánto descuenta: ver metodo y predicado. | campaign_id | string | null | Su campaña, si tiene. | created_at | number | Segundos desde 1970, con decimales. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. | 404 | No hay una promoción con ese id en el espacio | promoción no encontrada. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/productos/mi-espacio/promotions/4a6c8e0b2d4f4a6c9e1b3d5f7a9c2e57 \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "id": "4a6c8e0b2d4f4a6c9e1b3d5f7a9c2e57", "codigo": "BIENVENIDA", "nombre": "10 % de bienvenida", "estado": "activo", "grupo_exclusividad": "bienvenida", "prioridad": 0, "limite_usos": 100, "starts_at": null, "ends_at": "2026-12-31T23:59:59", "predicado": { "op": "and", "clauses": [ { "field": "codigos_promo", "cmp": "contains", "value": "BIENVENIDA" }, { "field": "subtotal", "cmp": "gte", "value": 50000 } ] }, "metodo": { "metodo": "percentage", "target": "order", "allocation": "each", "valor": "10", … }, "campaign_id": null, "created_at": 1790000800.6 } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Borrar promoción https://dev.bentho.ai/api/productos/promocion-borrar/ DEL/api/mod/productos/{espacio}/promotions/{promocion} # Borra una promoción Borra la promoción y sus usos contados. Deja de aplicar en la siguiente cotización. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. | promocion | string | SÍ | El id de la promoción: el que devolvió crearla (32 caracteres hexadecimales). ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | deleted | true | Hecho. | id | string | La promoción borrada. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 404 | No hay una promoción con ese id en el espacio | promoción no encontrada. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X DELETE https://bentho.ai/api/mod/productos/mi-espacio/promotions/4a6c8e0b2d4f4a6c9e1b3d5f7a9c2e57 \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKderivado del código ``` { "deleted": true, "id": "4a6c8e0b2d4f4a6c9e1b3d5f7a9c2e57" } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Crear campaña https://dev.bentho.ai/api/productos/campana-crear/ POST/api/mod/productos/{espacio}/campaigns # Crea una campaña Un presupuesto de descuentos compartido por varias promociones. Cuando lo descontado en ventas confirmadas llega al presupuesto, sus promociones dejan de aplicar. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | nombre | string | SÍ | Para reconocerla. | presupuesto | number | string | no | Lo máximo a descontar, en la moneda de tus precios. Por defecto 0. ## RESPUESTA 201 | | CAMPO | TIPO | QUÉ ES | id · nombre | string | Pon el id en campaign_id de cada promoción. | presupuesto | string | El que diste. | gastado | string | Lo descontado hasta ahora: 0 al crearla. | created_at | number | Segundos desde 1970, con decimales. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 422 | Falta un campo obligatorio o uno no es de su tipo (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Con presupuesto 0, sus promociones no aplican nunca. - gastado sube solo con las ventas que declaran el descuento en promociones_aplicadas. Las cotizaciones no gastan. - Las campañas no se leen ni se editan por la API: guarda el id que devuelve. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/productos/mi-espacio/campaigns \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "nombre": "Temporada de fin de año", "presupuesto": 2000000 }' ``` ■ 201 CREATEDejemplo, con los campos del código ``` { "id": "0f3a5c7e9b1d4f6a8c2e4b6d8f0a2c19", "nombre": "Temporada de fin de año", "presupuesto": "2000000", "gastado": "0", "created_at": 1790000850.2 } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Disponibilidad https://dev.bentho.ai/api/productos/disponibilidad/ GET/api/mod/productos/{espacio}/inventory/availability # La disponibilidad de una variante Si hay stock para una cantidad, por variant_id o por SKU. Descuenta lo reservado y, en un combo, mira su componente más escaso. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. | variant_id | string, query | UNO DE LOS DOS | La variante. | sku | string, query | UNO DE LOS DOS | O su SKU. | cantidad | string, query | no | Cuántas quieres. Por defecto 1. | bodega | string, query | no | La bodega. Por defecto, la única (texto vacío). ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | estado | string | Ver estados. | variant_id · sku · nombre | string | null | La variante y su producto. | cantidad_disponible | string | null | Lo que se puede vender ya. null si no hay dato. | cantidad_pedida | string | null | La que preguntaste. | unidad | string | null | La unidad del stock. ## ESTADOS DE LA DISPONIBILIDAD | | CAMPO | TIPO | QUÉ ES | disponible | se puede vender | Alcanza para la cantidad pedida. | stock_bajo | se puede vender | Alcanza, pero lo disponible queda en o bajo umbral_bajo. | insuficiente | no alcanza | Hay, pero menos de lo pedido. | agotado | no alcanza | Lo disponible es 0. | no_rastreado | sin dato | La variante no existe o no tiene stock cargado en esa bodega. No es un no: decide tú. | inventario_inactivo | sin dato | El espacio no controla stock (inventario_activo: false en la configuración). ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. | 400 | No viene ni variant_id ni sku | indique variant_id o sku. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Se puede vender con disponible y stock_bajo. Para apartarlo mientras tu cliente paga, resérvalo. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/productos/mi-espacio/inventory/availability?sku=CAF-MOL-500&cantidad=2 \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "estado": "disponible", "variant_id": "a3f19c7e2b5d4c8e9f0a1b2c3d4e5f60", "sku": "CAF-MOL-500", "nombre": "Café molido La Loma 500 g", "cantidad_disponible": "22", "cantidad_pedida": "2", "unidad": "unidad" } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Stock https://dev.bentho.ai/api/productos/stock/ GET/api/mod/productos/{espacio}/inventory/levels # El stock del espacio Las existencias de cada variante en cada bodega. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | levels | array | Ordenados por variante y bodega. Cada uno con los campos de GET inventory/items/{variante}. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Trae el variant_id, no el nombre: crúzalo con la lista de productos. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/productos/mi-espacio/inventory/levels \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKderivado del código ``` { "levels": [ { "variant_id": "a3f19c7e2b5d4c8e9f0a1b2c3d4e5f60", "bodega": "", "stocked": "24", "reserved": "2", "umbral_bajo": "5", "unidad": "unidad", "external_id": null, "created_at": 1790000900.1, "updated_at": 1790001200.7 } ] } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Stock de variante https://dev.bentho.ai/api/productos/stock-variante/ GET/api/mod/productos/{espacio}/inventory/items/{variante} # El stock de una variante Las existencias de una variante en una bodega y, si las pides, sus últimos movimientos. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. | variante | string | SÍ | El id de la variante: sale en variantes[] del producto (32 caracteres hexadecimales). | bodega | string, query | no | La bodega. Por defecto, la única (texto vacío). | with_movements | bool, query | no | Trae sus últimos 50 movimientos. Por defecto false. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | variant_id | string | La variante. | bodega | string | La bodega. Texto vacío: la única. | stocked | string | Las existencias físicas. | reserved | string | Lo retenido por reservas activas. Disponible = stocked − reserved. | umbral_bajo | string | null | Desde dónde cuenta como stock bajo. | unidad · external_id | string | null | La unidad (unidad, kg, caja…) y su id en tu software contable. | created_at · updated_at | number | Segundos desde 1970, con decimales. | movements[] | { tipo, delta, resultante, motivo, origen, created_at } | El más reciente primero: qué lo movió (ver tipos), cuánto, lo disponible después y por qué. ## TIPOS DE MOVIMIENTO | | CAMPO | TIPO | QUÉ ES | set | a mano | Existencias fijadas con Fijar stock. | ajuste | a mano | Una reposición o una merma (delta). | ingesta | archivo | Cargado al subir un archivo de stock. | sync | contable | Traído de tu software contable. | venta | venta | Descontado por una venta confirmada. | reserva · liberacion | reserva | Una reserva retiene stock o lo devuelve. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. | 404 | La variante no tiene stock cargado en esa bodega | nivel de stock no encontrado. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/productos/mi-espacio/inventory/items/a3f19c7e2b5d4c8e9f0a1b2c3d4e5f60?with_movements=true \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "variant_id": "a3f19c7e2b5d4c8e9f0a1b2c3d4e5f60", "bodega": "", "stocked": "24", "reserved": "2", "umbral_bajo": "5", "unidad": "unidad", "external_id": null, "created_at": 1790000900.1, "updated_at": 1790001200.7, "movements": [ { "tipo": "reserva", "delta": "-2", "resultante": "22", "motivo": "reserva de stock", "origen": "carrito-8841", "created_at": 1790001200.4, … }, … ] } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Fijar stock https://dev.bentho.ai/api/productos/stock-fijar/ POST/api/mod/productos/{espacio}/inventory/items # Fija el stock de una variante Pone las existencias físicas de una variante en una bodega: el número que cuentas en el estante. Si no tenía stock cargado, lo crea. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | variant_id | string | SÍ | La variante. | stocked | number | string | SÍ | Las existencias, en absoluto. Admite decimales (kg, litros). | bodega | string | no | Por defecto, la única. | umbral_bajo | number | string | no | Desde dónde avisa de stock bajo. | unidad | string | no | unidad, kg, caja… | external_id | string | no | Su id en tu software contable. ## RESPUESTA 201 El stock, ya fijado. | | CAMPO | TIPO | QUÉ ES | variant_id | string | La variante. | bodega | string | La bodega. Texto vacío: la única. | stocked | string | Las existencias físicas. | reserved | string | Lo retenido por reservas activas. Disponible = stocked − reserved. | umbral_bajo | string | null | Desde dónde cuenta como stock bajo. | unidad · external_id | string | null | La unidad (unidad, kg, caja…) y su id en tu software contable. | created_at · updated_at | number | Segundos desde 1970, con decimales. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 422 | Falta un campo obligatorio o uno no es de su tipo (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - No toca lo reservado: disponible sigue siendo stocked − reserved. - umbral_bajo, unidad y external_id en null dejan el valor que había. No comprueba que la variante exista. - Para sumar o restar en vez de fijar, ajusta. - Las cantidades y los importes viajan como texto decimal ("32000", "1.5") para no perder precisión. Al mandarlos vale un número o un texto. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/productos/mi-espacio/inventory/items \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "variant_id": "a3f19c7e2b5d4c8e9f0a1b2c3d4e5f60", "stocked": 24, "umbral_bajo": 5, "unidad": "unidad" }' ``` ■ 201 CREATEDejemplo, con los campos del código ``` { "variant_id": "a3f19c7e2b5d4c8e9f0a1b2c3d4e5f60", "bodega": "", "stocked": "24", "reserved": "2", "umbral_bajo": "5", "unidad": "unidad", "external_id": null, "created_at": 1790000900.1, "updated_at": 1790001200.7 } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Ajustar stock https://dev.bentho.ai/api/productos/stock-ajustar/ PATCH/api/mod/productos/{espacio}/inventory/items/{variante} # Ajusta el stock de una variante Suma o resta existencias: una reposición (+), una merma o una rotura (−). - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. | variante | string | SÍ | El id de la variante: sale en variantes[] del producto (32 caracteres hexadecimales). ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | delta | number | string | SÍ | Cuánto sumar; en negativo, restar. | bodega | string | no | Por defecto, la única. | motivo | string | no | Queda en el movimiento: «reposición», «rotura». ## RESPUESTA 200 El stock, ya ajustado. | | CAMPO | TIPO | QUÉ ES | variant_id | string | La variante. | bodega | string | La bodega. Texto vacío: la única. | stocked | string | Las existencias físicas. | reserved | string | Lo retenido por reservas activas. Disponible = stocked − reserved. | umbral_bajo | string | null | Desde dónde cuenta como stock bajo. | unidad · external_id | string | null | La unidad (unidad, kg, caja…) y su id en tu software contable. | created_at · updated_at | number | Segundos desde 1970, con decimales. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 404 | La variante no tiene stock cargado en esa bodega | nivel de stock no encontrado. | 422 | Falta un campo obligatorio o uno no es de su tipo (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Las existencias no bajan de 0: restar de más las deja en 0. - La variante tiene que tener stock cargado: si no, primero fíjalo. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X PATCH https://bentho.ai/api/mod/productos/mi-espacio/inventory/items/a3f19c7e2b5d4c8e9f0a1b2c3d4e5f60 \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "delta": -1, "motivo": "rotura en bodega" }' ``` ■ 200 OKejemplo, con los campos del código ``` { "variant_id": "a3f19c7e2b5d4c8e9f0a1b2c3d4e5f60", "bodega": "", "stocked": "23", "reserved": "2", "umbral_bajo": "5", "unidad": "unidad", "external_id": null, "created_at": 1790000900.1, "updated_at": 1790001200.7 } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Borrar stock https://dev.bentho.ai/api/productos/stock-borrar/ DEL/api/mod/productos/{espacio}/inventory/items/{variante} # Borra el stock de una variante Quita el stock cargado de una variante en una bodega. Después su disponibilidad es no_rastreado. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. | variante | string | SÍ | El id de la variante: sale en variantes[] del producto (32 caracteres hexadecimales). | bodega | string, query | no | La bodega. Por defecto, la única (texto vacío). ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | deleted | true | Hecho. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 404 | La variante no tiene stock cargado en esa bodega | nivel de stock no encontrado. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Para decir que no queda nada, mejor fíjalo a 0. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X DELETE https://bentho.ai/api/mod/productos/mi-espacio/inventory/items/a3f19c7e2b5d4c8e9f0a1b2c3d4e5f60 \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKderivado del código ``` { "deleted": true } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Stock bajo https://dev.bentho.ai/api/productos/stock-bajo/ GET/api/mod/productos/{espacio}/inventory/low-stock # El stock bajo Las variantes cuya disponibilidad está en o bajo su umbral_bajo: lo que hay que reponer. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | low_stock | array | Cada una con los campos de GET inventory/items/{variante}. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Solo cuentan las que tienen umbral_bajo. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/productos/mi-espacio/inventory/low-stock \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKderivado del código ``` { "low_stock": [ { "variant_id": "6d2b8f4a0c7e4b1d9a3f5c8e2b6d0a71", "bodega": "", "stocked": "4", "reserved": "0", "umbral_bajo": "5", … } ] } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Resumen de stock https://dev.bentho.ai/api/productos/stock-resumen/ GET/api/mod/productos/{espacio}/inventory/summary # El resumen del stock Las cifras de salud del inventario: cuántas variantes con stock, agotadas, en stock bajo y reservas activas. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | total | integer | Variantes con stock cargado (por bodega). | con_stock · agotados | integer | Con algo disponible, y con 0. | stock_bajo | integer | En o bajo su umbral. | reservas_activas | integer | Reservas reteniendo stock ahora. | last_sync | object | null | La última carga masiva: fuente (file, un archivo; contable, tu software contable), items_upserted y created_at. null si nunca hubo. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/productos/mi-espacio/inventory/summary \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "total": 41, "con_stock": 37, "agotados": 4, "stock_bajo": 6, "reservas_activas": 2, "last_sync": { "fuente": "file", "items_upserted": 39, "created_at": 1790000950.6, … } } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Alternativas https://dev.bentho.ai/api/productos/alternativas/ GET/api/mod/productos/{espacio}/inventory/suggest # Alternativas con stock Nombres de productos activos que sí tienen stock: lo que ofrecer cuando lo que piden está agotado. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. | exclude_variant_id | string, query | no | La variante agotada, para no proponerla. | limit | int, query | no | Cuántos. Por defecto 3. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | suggestions | string[] | Nombres de producto, por orden alfabético. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - No mira si se parecen a lo agotado: son productos con stock, sin más. Para algo parecido, busca y comprueba su disponibilidad. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/productos/mi-espacio/inventory/suggest?exclude_variant_id=a3f19c7e2b5d4c8e9f0a1b2c3d4e5f60&limit=3 \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "suggestions": ["Café en grano La Loma 500 g", "Café molido La Loma 250 g"] } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Exportar stock https://dev.bentho.ai/api/productos/stock-exportar/ GET/api/mod/productos/{espacio}/inventory/export # Exporta el stock en CSV Todo el stock en un CSV, una fila por variante y bodega. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. ## RESPUESTA 200 Un archivo text/csv, con Content-Disposition para guardarlo. | | CAMPO | TIPO | QUÉ ES | columnas | csv | variant_id, bodega, stocked, reserved, umbral_bajo, unidad, external_id y updated_at. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/productos/mi-espacio/inventory/export \ -H "Authorization: Bearer $BENTHO_KEY" \ -o stock.csv ``` ■ 200 OKejemplo, con las columnas del código ``` variant_id,bodega,stocked,reserved,umbral_bajo,unidad,external_id,updated_at a3f19c7e2b5d4c8e9f0a1b2c3d4e5f60,,24,2,5,unidad,,1790001200.7 6d2b8f4a0c7e4b1d9a3f5c8e2b6d0a71,,4,0,5,unidad,,1790000950.6 ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Subir stock https://dev.bentho.ai/api/productos/stock-subir/ POST/api/mod/productos/{espacio}/inventory/upload # Sube el stock desde un archivo Fija las existencias de muchas variantes de una vez desde un CSV, Excel o PDF. Cada fila se cruza por SKU con el catálogo; no crea productos. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. ## CUERPO · MULTIPART/FORM-DATA | | CAMPO | TIPO | REQUERIDO | QUÉ ES | file | archivo | SÍ | Un .csv, .xlsx o .pdf con una tabla, hasta 25 MB. Necesita una columna de cantidad y una de SKU: ver la guía. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | origen | "csv" | "xlsx" | "pdf" | El formato del archivo. | sha256 | string | La huella del archivo. | items_cargados | integer | Filas aplicadas. | no_resueltos[] | { nombre, sku } | Filas cuyo SKU no está en el catálogo (o sin SKU): no se cargaron. | incompletos[] | { nombre, sku, warnings } | Filas sin una cantidad legible. | warnings | string[] | Avisos del archivo entero. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 400 | El archivo no trae nada | archivo vacío. | 400 | La extensión no es .csv, .xlsx ni .pdf | extensión no soportada para inventario: … | 413 | El archivo pasa de 25 MB | archivo demasiado grande (máximo 26214400 bytes). | 422 | Falta el campo file (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Cada fila fija las existencias (no suma) en su bodega, y no toca lo reservado. - Excel, en .xlsx: un .xls antiguo no se lee. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/productos/mi-espacio/inventory/upload \ -H "Authorization: Bearer $BENTHO_KEY" \ -F "file=@stock.csv" ``` ■ 200 OKejemplo, con los campos del código ``` { "origen": "csv", "sha256": "51c0e9a7…", "items_cargados": 39, "no_resueltos": [{ "nombre": "Vela de soya", "sku": "VEL-SOY-01" }], "incompletos": [{ "nombre": "Jabón de avena", "sku": "JAB-AVE-100", "warnings": ["cantidad ilegible: 'pendiente'"] }], "warnings": [], … } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Sincronizar stock https://dev.bentho.ai/api/productos/stock-sincronizar/ POST/api/mod/productos/{espacio}/inventory/sync # Trae el stock de tu software contable Lee las existencias de tu software contable y las fija en las variantes con el mismo SKU. Necesita el módulo Contabilidad conectado en el espacio. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | run_id | string | Esta carga. | recibidos · aplicados | integer | Lo que llegó y lo que se fijó. | no_resueltos[] | { sku, external_id } | Lo que llegó con un SKU que no está en el catálogo. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 502 | No se pudo leer del software contable: no está conectado o no respondió | conector contable: … Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X POST https://bentho.ai/api/mod/productos/mi-espacio/inventory/sync \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "run_id": "f1d3b5a7c9e04b2d8f6a4c2e0b9d7f53", "recibidos": 40, "aplicados": 38, "no_resueltos": [{ "sku": "VEL-SOY-01", "external_id": "1187" }] } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Reservar https://dev.bentho.ai/api/productos/reserva-crear/ POST/api/mod/productos/{espacio}/inventory/reservations # Reserva stock Aparta unidades de una variante por un tiempo, mientras tu cliente paga. Lo reservado no se puede vender a otro; si vence el plazo, vuelve solo. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | variant_id | string | SÍ | La variante. | cantidad | number | string | no | Cuántas. Por defecto 1. | ttl_s | integer | no | Cuántos segundos dura. Por defecto 900 (15 minutos). | bodega | string | no | Por defecto, la única. | session_id · sale_ref | string | no | Tus referencias: el carrito y el pedido. ## RESPUESTA 201 La reserva, activa. | | CAMPO | TIPO | QUÉ ES | id · variant_id · bodega | string | La reserva y qué retiene. | cantidad | string | Lo retenido. | estado | string | Ver estados. | session_id · sale_ref | string | null | Los que diste. | expires_at · created_at | number | Cuándo vence y cuándo se creó, en segundos desde 1970. | released_at | number | null | Cuándo dejó de retener. null mientras está activa. ## ESTADOS DE UNA RESERVA | | CAMPO | TIPO | QUÉ ES | activa | retiene | Descuenta de lo disponible hasta expires_at. | liberada | terminada | La liberaste: el stock vuelve. | consumida | terminada | Se convirtió en venta (venta con su reservation_id). | expirada | terminada | Venció su plazo: el stock vuelve solo. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 409 | No alcanza lo disponible, la variante no tiene stock cargado o la cantidad no es mayor que 0 | sin disponibilidad suficiente para reservar. | 422 | Falta un campo obligatorio o uno no es de su tipo (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Al confirmar la venta, pasa el id como reservation_id de la línea en Confirma una venta: la reserva se consume y no se descuenta dos veces. - Si tu cliente abandona, libérala sin esperar al plazo. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/productos/mi-espacio/inventory/reservations \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "variant_id": "a3f19c7e2b5d4c8e9f0a1b2c3d4e5f60", "cantidad": 2, "ttl_s": 900, "session_id": "carrito-8841" }' ``` ■ 201 CREATEDejemplo, con los campos del código ``` { "id": "b8e2d6f0a4c84b2e9d7f5a3c1e0b8d93", "variant_id": "a3f19c7e2b5d4c8e9f0a1b2c3d4e5f60", "bodega": "", "cantidad": "2", "estado": "activa", "session_id": "carrito-8841", "sale_ref": null, "expires_at": 1790002100.4, "created_at": 1790001200.4, "released_at": null } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Reservas https://dev.bentho.ai/api/productos/reservas/ GET/api/mod/productos/{espacio}/inventory/reservations # Las reservas activas Las reservas que retienen stock ahora, la más antigua primero. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. | variant_id | string, query | no | Solo las de esta variante. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | reservations | array | Solo las activas. Cada una con los campos de Reserva stock. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/productos/mi-espacio/inventory/reservations \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKderivado del código ``` { "reservations": [ { "id": "b8e2d6f0a4c84b2e9d7f5a3c1e0b8d93", "variant_id": "a3f19c7e2b5d4c8e9f0a1b2c3d4e5f60", "bodega": "", "cantidad": "2", "estado": "activa", "session_id": "carrito-8841", "sale_ref": null, "expires_at": 1790002100.4, "created_at": 1790001200.4, "released_at": null } ] } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Liberar reserva https://dev.bentho.ai/api/productos/reserva-liberar/ DEL/api/mod/productos/{espacio}/inventory/reservations/{reserva} # Libera una reserva Devuelve al disponible lo que retenía una reserva activa: el carrito se abandonó o se canceló. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. | reserva | string | SÍ | El id de la reserva: el que devolvió crearla (32 caracteres hexadecimales). ## RESPUESTA 200 La reserva, liberada. | | CAMPO | TIPO | QUÉ ES | id · variant_id · bodega | string | La reserva y qué retiene. | cantidad | string | Lo retenido. | estado | string | Ver estados. | session_id · sale_ref | string | null | Los que diste. | expires_at · created_at | number | Cuándo vence y cuándo se creó, en segundos desde 1970. | released_at | number | null | Cuándo dejó de retener. null mientras está activa. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 404 | No existe, o ya no está activa (liberada, consumida o expirada) | reserva no encontrada o no estaba activa. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X DELETE https://bentho.ai/api/mod/productos/mi-espacio/inventory/reservations/b8e2d6f0a4c84b2e9d7f5a3c1e0b8d93 \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "id": "b8e2d6f0a4c84b2e9d7f5a3c1e0b8d93", "variant_id": "a3f19c7e2b5d4c8e9f0a1b2c3d4e5f60", "bodega": "", "cantidad": "2", "estado": "liberada", "session_id": "carrito-8841", "sale_ref": null, "expires_at": 1790002100.4, "created_at": 1790001200.4, "released_at": 1790001500.9 } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Confirmar venta https://dev.bentho.ai/api/productos/venta/ POST/api/mod/productos/{espacio}/inventory/sale # Confirma una venta Descuenta del stock lo vendido, todo de una vez. Con sale_ref, repetirla no descuenta dos veces. Si declaras las promociones usadas, cuentan para sus límites. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | lines | array | SÍ | Una por producto vendido: ver lines[]. | sale_ref | string | no | Tu id del pedido. Con él, un reintento devuelve el resultado guardado. | promociones_aplicadas | [{ promotion_id, descuento }] | no | Las promociones de la cotización y lo que descontó cada una: suman un uso y gastan de su campaña. | session_id | string | no | Tu referencia del carrito. | push_contable | boolean | no | Enviar el descuento a tu software contable, en las variantes con external_id. Por defecto true. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | status | "ok" | "pending" | pending: la misma venta se está procesando ahora. | confirmados[] | { variant_id, bodega, cantidad, restante, external_id } | Lo descontado y cuánto quedó. Un combo sale como sus componentes. | no_encontrados[] | { variant_id, sku?, motivo? } | Líneas sin descontar: la variante no existe o no tiene stock cargado, o motivo: "cantidad_invalida". | contable | object | null | Lo que respondió tu software contable al enviarle el descuento. null si no había nada que enviar. | idempotent_replay | true | Solo en un reintento con el mismo sale_ref: es el resultado guardado. ## LINES[] | | CAMPO | TIPO | QUÉ ES | variant_id · sku | string | La variante, por id o por SKU. | cantidad | number | string | Cuántas. Por defecto 1. | bodega | string | Por defecto, la única. | reservation_id | string | La reserva que esta línea consume, si la hiciste. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 422 | Falta un campo obligatorio o uno no es de su tipo (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - No comprueba que haya stock: descuenta lo vendido y deja las existencias en 0 como mínimo. Comprueba antes con disponibilidad o reserva. - Un fallo de tu software contable no hace fallar la venta: queda en contable. - Las promociones solo cuentan usos aquí, no al cotizar. Sin promociones_aplicadas, limite_usos y las campañas no avanzan. - Las cantidades y los importes viajan como texto decimal ("32000", "1.5") para no perder precisión. Al mandarlos vale un número o un texto. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/productos/mi-espacio/inventory/sale \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "lines": [ { "variant_id": "a3f19c7e2b5d4c8e9f0a1b2c3d4e5f60", "cantidad": 2, "reservation_id": "b8e2d6f0a4c84b2e9d7f5a3c1e0b8d93" } ], "sale_ref": "pedido-1042", "promociones_aplicadas": [ { "promotion_id": "4a6c8e0b2d4f4a6c9e1b3d5f7a9c2e57", "descuento": 6400 } ] }' ``` ■ 200 OKejemplo, con los campos del código ``` { "confirmados": [ { "variant_id": "a3f19c7e2b5d4c8e9f0a1b2c3d4e5f60", "bodega": "", "cantidad": "2", "restante": "22", "external_id": null } ], "no_encontrados": [], "contable": null, "status": "ok" } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Configuración https://dev.bentho.ai/api/productos/config/ GET/api/mod/productos/{espacio}/config # La configuración del módulo El nombre, la moneda y si el espacio controla stock. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | name | string | El nombre del catálogo. Por defecto, el id del espacio. | moneda | string | La moneda del catálogo, para mostrar. Por defecto "COP". | inventario_activo | boolean | Si el espacio controla stock. Con false, la disponibilidad responde inventario_inactivo. Por defecto true. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - moneda no cambia las cotizaciones: cada precio lleva la suya y cada cotización dice en cuál cotiza. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/productos/mi-espacio/config \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "name": "Tienda Aurora", "moneda": "COP", "inventario_activo": true, … } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Productos · Editar configuración https://dev.bentho.ai/api/productos/config-editar/ PATCH/api/mod/productos/{espacio}/config # Cambia la configuración Cambia solo los campos que mandas. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Productos. ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | name | string | no | El nombre del catálogo. | moneda | string | no | La moneda, para mostrar. | inventario_activo | boolean | no | false: el espacio deja de controlar stock. ## RESPUESTA 200 La configuración entera, como en GET config. | | CAMPO | TIPO | QUÉ ES | name · moneda | string | Los de ahora. | inventario_activo | boolean | El de ahora. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Productos | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 422 | Falta un campo obligatorio o uno no es de su tipo (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Con inventario_activo: false el stock se conserva y se sigue pudiendo cargar; solo la disponibilidad deja de mirarlo. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X PATCH https://bentho.ai/api/mod/productos/mi-espacio/config \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Tienda Aurora", "inventario_activo": false }' ``` ■ 200 OKejemplo, con los campos del código ``` { "name": "Tienda Aurora", "moneda": "COP", "inventario_activo": false, … } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Comercial · Configuración https://dev.bentho.ai/api/comercial/config/ GET/api/mod/comercial/{espacio}/config # La configuración comercial Las reglas de pago con las que se calcula el envío, el peso de los pedidos y los beneficios del espacio. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Comercial. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | name · moneda | string | Un nombre y la moneda (COP). | reglas_pago | object | Cómo se calcula el envío según el método: ver reglas_pago. | peso_default_pedido_kg | string | El peso de un pedido cuando no hay otro dato, en kilos. De fábrica, 2. | peso_sku | { sku: gramos } | El peso de cada producto, en gramos enteros, por su SKU. | beneficios[] | array | Bienvenida, envío gratis, pago anticipado…: ver beneficios. ## REGLAS_PAGO | | CAMPO | TIPO | QUÉ ES | umbral_asegurado | string | Desde este subtotal, la contraentrega va asegurada: IVA y comisión de recaudo en vez de cargo fijo. De fábrica, 250 mil. | cargo_fijo_contra | string | El cargo fijo de contraentrega por debajo del umbral. De fábrica, 5900. | iva_pct | string | El IVA del envío asegurado, como fracción. De fábrica, 0.19. | comision_recaudo_pct | string | La comisión de recaudo sobre subtotal + envío + IVA, como fracción. De fábrica, 0.02. | seguro_declarado_* | boolean · string · string | Una regla de valor declarado (_activo, _base_cubierta, _exceso_pct). Se guarda, pero hoy la cotización no la aplica. ## BENEFICIOS[] | | CAMPO | TIPO | QUÉ ES | codigo · nombre | string | Un código estable y un nombre para reconocerlo. | efecto_tipo | "descuento_pct" | "descuento_fijo" | "envio_gratis" | Qué hace. | efecto_valor | string | En descuento_pct, la fracción del subtotal (0.1 = 10 %); en descuento_fijo, el monto, sin pasar del subtotal. envio_gratis no lo usa. | activo | boolean | Por defecto true. Apagado no aplica nunca. | solo_primera_compra | boolean | Solo para clientes nuevos. Por defecto false. | subtotal_min | string | null | Subtotal mínimo para aplicar. | metodos_pago | string[] | Para qué métodos aplica: contra, transferencia, mixto. Vacío (por defecto): todos. | prosa | string | La frase corta que se le dice al cliente, con la voz de tu marca. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Comercial | módulo no habilitado para este espacio. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - La primera vez que se lee, se crea con los valores de fábrica y sin beneficios. - Las cifras van como texto decimal. La tabla de tarifas no está aquí: ver tarifa activa. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/comercial/mi-espacio/config \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "name": "Café La Loma", "moneda": "COP", "reglas_pago": { "umbral_asegurado": "300000", "cargo_fijo_contra": "5900", "iva_pct": "0.19", "comision_recaudo_pct": "0.02", "seguro_declarado_activo": false, "seguro_declarado_base_cubierta": "100000", "seguro_declarado_exceso_pct": "0.005" }, "peso_default_pedido_kg": "2", "peso_sku": { "CAFE-HUILA-500": 520, "CAFE-HUILA-250": 270 }, "beneficios": [ { "codigo": "bienvenida10", "nombre": "Bienvenida 10 %", "activo": true, "solo_primera_compra": true, "subtotal_min": null, "metodos_pago": [], "efecto_tipo": "descuento_pct", "efecto_valor": "0.1", "prosa": "En tu primera compra tienes un 10 % de descuento." } ], … } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Comercial · Editar configuración https://dev.bentho.ai/api/comercial/config-editar/ PATCH/api/mod/comercial/{espacio}/config # Cambia la configuración comercial Cambia solo los campos que mandes. Cada uno se reemplaza entero: beneficios es la lista completa, peso_sku la tabla completa y reglas_pago el objeto completo. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Comercial. ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | name · moneda | string | no | Un nombre y la moneda de las cotizaciones. | reglas_pago | object | no | Ver reglas_pago. Lo que no mandes dentro vuelve a su valor de fábrica. | peso_default_pedido_kg | string | no | Peso de un pedido sin más datos, en kilos. | peso_sku | { sku: gramos } | no | Gramos enteros por SKU. La tabla entera. | beneficios | array | no | Todos los beneficios: ver beneficios. ## RESPUESTA 200 La configuración completa, ya cambiada. | | CAMPO | TIPO | QUÉ ES | name · moneda | string | Un nombre y la moneda (COP). | reglas_pago | object | Cómo se calcula el envío según el método: ver reglas_pago. | peso_default_pedido_kg | string | El peso de un pedido cuando no hay otro dato, en kilos. De fábrica, 2. | peso_sku | { sku: gramos } | El peso de cada producto, en gramos enteros, por su SKU. | beneficios[] | array | Bienvenida, envío gratis, pago anticipado…: ver beneficios. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Comercial | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 422 | Un efecto_tipo que no existe, un peso que no es entero o un beneficio sin codigo | — | 422 | Falta un campo obligatorio o uno no es de su tipo (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Para cambiar un solo valor de reglas_pago, lee la configuración y manda el objeto entero con tu cambio: lo que falte vuelve al valor de fábrica. - Para pausar un beneficio sin perderlo, mándalo con activo: false. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X PATCH https://bentho.ai/api/mod/comercial/mi-espacio/config \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "beneficios": [ { "codigo": "bienvenida10", "nombre": "Bienvenida 10 %", "solo_primera_compra": true, "efecto_tipo": "descuento_pct", "efecto_valor": "0.1", "prosa": "En tu primera compra tienes un 10 % de descuento." }, { "codigo": "envio-gratis", "nombre": "Envío gratis desde 200 mil", "subtotal_min": "200000", "metodos_pago": [ "transferencia" ], "efecto_tipo": "envio_gratis", "prosa": "Si pagas por transferencia, desde 200 mil el envío es gratis." } ] }' ``` ■ 200 OKejemplo, con los campos del código ``` { "name": "Café La Loma", "moneda": "COP", "reglas_pago": { "umbral_asegurado": "300000", "cargo_fijo_contra": "5900", "iva_pct": "0.19", "comision_recaudo_pct": "0.02", "seguro_declarado_activo": false, "seguro_declarado_base_cubierta": "100000", "seguro_declarado_exceso_pct": "0.005" }, "peso_default_pedido_kg": "2", "peso_sku": { "CAFE-HUILA-500": 520, "CAFE-HUILA-250": 270 }, "beneficios": [ { "codigo": "bienvenida10", "nombre": "Bienvenida 10 %", "activo": true, "solo_primera_compra": true, "subtotal_min": null, "metodos_pago": [], "efecto_tipo": "descuento_pct", "efecto_valor": "0.1", "prosa": "En tu primera compra tienes un 10 % de descuento." } ], … } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Comercial · Subir tarifas https://dev.bentho.ai/api/comercial/tarifas-subir/ POST/api/mod/comercial/{espacio}/tarifas/draft # Sube una tabla de tarifas Sube el Excel de tu transportadora: qué zona es cada ciudad y cuánto cuesta enviar a cada zona según el peso. Queda como borrador: no se usa hasta que la actives. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Comercial. ## CUERPO · MULTIPART/FORM-DATA | | CAMPO | TIPO | REQUERIDO | QUÉ ES | file | archivo | SÍ | Un .xlsx de hasta 10 MB con una hoja de ciudades y otra de precios. Su forma, en la guía. ## RESPUESTA 201 | | CAMPO | TIPO | QUÉ ES | import_id | string | El id de la carga. Guárdalo. | estado | "draft" | Borrador. | n_ciudades · n_precios | integer | Cuántos destinos y cuántos rangos de peso se leyeron. | ciudad_origen | string | null | La ciudad de origen, si la hoja de ciudades la trae. | warnings | string[] | Lo que no se pudo leer: filas descartadas, una hoja sin encabezado, ninguna ciudad… Vacío si todo fue bien. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Comercial | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 400 | El archivo está vacío | archivo vacío. | 413 | El archivo pasa de 10 MB | archivo demasiado grande: máximo 10485760 bytes. | 413 | Una hoja pasa de 20 000 filas o de 64 columnas. No se guarda nada | hoja '': excede 20000 filas; archivo rechazado | 415 | El archivo no termina en .xlsx ni .xls | extensión no soportada para tarifas: | 422 | Falta el campo file | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Revisa n_ciudades, n_precios y warnings antes de activar: la API no devuelve las filas de un borrador. Un 0 en cualquiera de los dos quiere decir que esa hoja no se leyó. - Sube un .xlsx. Un archivo que no se pueda abrir como libro de Excel no se rechaza con un 4xx: responde 500. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/comercial/mi-espacio/tarifas/draft \ -H "Authorization: Bearer $BENTHO_KEY" \ -F "file=@tarifas.xlsx" ``` ■ 201 CREATEDejemplo, con los campos del código ``` { "import_id": "a9d3f1c7e2b84d6fa0c5e8b1d4f7a263", "estado": "draft", "n_ciudades": 291, "n_precios": 9, "ciudad_origen": "MEDELLIN", "warnings": [], … } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Comercial · Cargas de tarifas https://dev.bentho.ai/api/comercial/tarifas-cargas/ GET/api/mod/comercial/{espacio}/tarifas/imports # Las cargas de tarifas Todas las tablas que subiste, la más reciente primero: borradores, la activa y las retiradas. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Comercial. | estado | string, query | no | Solo las de este estado: draft, activa o retirada. ## RESPUESTA 200 Una lista JSON (sin objeto alrededor). | | CAMPO | TIPO | QUÉ ES | [] | array | Cada carga con los campos de GET tarifas/imports/{carga}. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Comercial | módulo no habilitado para este espacio. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/comercial/mi-espacio/tarifas/imports?estado=draft \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKderivado del código ``` [ { "id": "a9d3f1c7e2b84d6fa0c5e8b1d4f7a263", "estado": "draft", "tarifa_version": null, "doc_source": "tarifas-octubre.xlsx", "n_ciudades": 291, "n_precios": 9, … } ] ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Comercial · Una carga https://dev.bentho.ai/api/comercial/tarifas-carga/ GET/api/mod/comercial/{espacio}/tarifas/imports/{carga} # Una carga de tarifas Los datos de una carga: su estado, su versión, cuántos destinos y rangos tiene y quién la activó. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Comercial. | carga | string | SÍ | El id de la carga de tarifas: el import_id que devolvió subirla (32 caracteres hexadecimales). ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | id · estado | string | La carga y su estado: draft, activa o retirada. | tarifa_version | integer | null | Su versión, que sube de uno en uno al activar. null mientras es borrador. Es la que sale en cada cotización. | doc_source | string | El nombre del archivo que subiste. | sha256 | string | La huella del archivo. | ciudad_origen | string | null | La ciudad desde la que se envía, si el archivo la trae. | n_ciudades · n_precios | integer | Cuántos destinos y cuántos rangos de peso se leyeron. | validada_por | string | null | Quién la activó, si lo dijiste. | created_at · activated_at | number | null | Segundos desde 1970. activated_at es null mientras es borrador. ## ESTADOS DE UNA CARGA | | CAMPO | TIPO | QUÉ ES | draft | borrador | Subida y leída, pero no se usa todavía. Revísala y actívala. | activa | en uso | La que usan las cotizaciones. Hay una como mucho. | retirada | histórica | Estuvo activa hasta que activaste otra. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Comercial | módulo no habilitado para este espacio. | 404 | No hay una carga con ese id en el espacio | import no encontrado. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/comercial/mi-espacio/tarifas/imports/a9d3f1c7e2b84d6fa0c5e8b1d4f7a263 \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "id": "a9d3f1c7e2b84d6fa0c5e8b1d4f7a263", "sha256": "0b7e…", "origen": "xlsx", "doc_source": "tarifas-octubre.xlsx", "estado": "activa", "tarifa_version": 3, "ciudad_origen": "MEDELLIN", "n_ciudades": 291, "n_precios": 9, "validada_por": "ana@example.com", "created_at": 1790000000.12, "activated_at": 1790000300.5 } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Comercial · Activar tarifas https://dev.bentho.ai/api/comercial/tarifas-activar/ POST/api/mod/comercial/{espacio}/tarifas/{carga}/activate # Activa una tabla de tarifas Pone en uso un borrador. Recibe la siguiente tarifa_version y la tabla que estaba activa pasa a retirada. Desde ese momento, todas las cotizaciones usan esta. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Comercial. | carga | string | SÍ | El id de la carga de tarifas: el import_id que devolvió subirla (32 caracteres hexadecimales). | validada_por | string, query | no | Quién la revisó. Queda en la carga. ## RESPUESTA 200 La carga, ya activa. | | CAMPO | TIPO | QUÉ ES | id · estado | string | La carga y su estado: draft, activa o retirada. | tarifa_version | integer | null | Su versión, que sube de uno en uno al activar. null mientras es borrador. Es la que sale en cada cotización. | doc_source | string | El nombre del archivo que subiste. | sha256 | string | La huella del archivo. | ciudad_origen | string | null | La ciudad desde la que se envía, si el archivo la trae. | n_ciudades · n_precios | integer | Cuántos destinos y cuántos rangos de peso se leyeron. | validada_por | string | null | Quién la activó, si lo dijiste. | created_at · activated_at | number | null | Segundos desde 1970. activated_at es null mientras es borrador. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Comercial | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 404 | No hay una carga con ese id en el espacio | import no encontrado. | 409 | La carga no es un borrador (ya está activa o retirada) | solo se activa un import en draft (estado: ) Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Una tabla retirada no se puede volver a activar: si quieres volver a ella, súbela otra vez y activa el borrador nuevo. - Las órdenes ya creadas en Facturación no cambian: guardan el envío y la tarifa_version con los que se cotizaron. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X POST https://bentho.ai/api/mod/comercial/mi-espacio/tarifas/a9d3f1c7e2b84d6fa0c5e8b1d4f7a263/activate?validada_por=ana@example.com \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "id": "a9d3f1c7e2b84d6fa0c5e8b1d4f7a263", "sha256": "0b7e…", "origen": "xlsx", "doc_source": "tarifas-octubre.xlsx", "estado": "activa", "tarifa_version": 3, "ciudad_origen": "MEDELLIN", "n_ciudades": 291, "n_precios": 9, "validada_por": "ana@example.com", "created_at": 1790000000.12, "activated_at": 1790000300.5 } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Comercial · Tarifa activa https://dev.bentho.ai/api/comercial/tarifas-activa/ GET/api/mod/comercial/{espacio}/tarifas/active # La tabla de tarifas activa Los datos de la carga que usan ahora las cotizaciones: su versión, su archivo y cuántos destinos y rangos tiene. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Comercial. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | id · estado | string | La carga y su estado: draft, activa o retirada. | tarifa_version | integer | null | Su versión, que sube de uno en uno al activar. null mientras es borrador. Es la que sale en cada cotización. | doc_source | string | El nombre del archivo que subiste. | sha256 | string | La huella del archivo. | ciudad_origen | string | null | La ciudad desde la que se envía, si el archivo la trae. | n_ciudades · n_precios | integer | Cuántos destinos y cuántos rangos de peso se leyeron. | validada_por | string | null | Quién la activó, si lo dijiste. | created_at · activated_at | number | null | Segundos desde 1970. activated_at es null mientras es borrador. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Comercial | módulo no habilitado para este espacio. | 404 | No has activado ninguna tabla todavía | no hay tabla de tarifas activa. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Devuelve los datos de la carga, no sus filas. Para ver en qué zona cae una ciudad usa ciudades; para ver un precio, cotiza. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/comercial/mi-espacio/tarifas/active \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "id": "a9d3f1c7e2b84d6fa0c5e8b1d4f7a263", "sha256": "0b7e…", "origen": "xlsx", "doc_source": "tarifas-octubre.xlsx", "estado": "activa", "tarifa_version": 3, "ciudad_origen": "MEDELLIN", "n_ciudades": 291, "n_precios": 9, "validada_por": "ana@example.com", "created_at": 1790000000.12, "activated_at": 1790000300.5 } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Comercial · Buscar ciudad https://dev.bentho.ai/api/comercial/ciudades/ GET/api/mod/comercial/{espacio}/tarifas/ciudades # Busca una ciudad en la tabla Las ciudades de la tabla activa que contienen el texto que buscas, con su zona. Sirve para un autocompletado o para comprobar un destino. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Comercial. | q | string, query | SÍ | Lo que buscas, al menos 1 carácter. Sin importar tildes ni mayúsculas. | limit | int, query | no | Cuántas como mucho. Por defecto 20. ## RESPUESTA 200 Una lista JSON (sin objeto alrededor), la más corta primero. | | CAMPO | TIPO | QUÉ ES | [].nombre_destino | string | El nombre tal como está en tu tabla. | [].zona | string | Su zona: Local, Nacional, Zonal… | [].codigo_zona | string | null | El código de zona de tu tabla, si lo trae. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Comercial | módulo no habilitado para este espacio. | 422 | Falta q o viene vacío | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Sin tabla activa, responde una lista vacía. - Un municipio que tu tabla no lista no sale aquí, aunque al cotizar pueda caer en la zona Otros. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/comercial/mi-espacio/tarifas/ciudades?q=bogo \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` [ { "nombre_destino": "BOGOTA D.C.", "zona": "Nacional", "codigo_zona": "2" } ] ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Comercial · Evaluar beneficios https://dev.bentho.ai/api/comercial/beneficios/ POST/api/mod/comercial/{espacio}/reglas/evaluar # Qué beneficios aplican Le das el subtotal y lo que sepas del cliente, y te dice qué beneficios configurados aplican y cuánto descuentan. Con eso cotizas el envío. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Comercial. ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | subtotal | string | SÍ | El subtotal de los productos, con los precios de Productos. | es_primera_compra | boolean | null | no | Si es su primera compra. Sin él (null), los beneficios de primera compra salen igual, como condicionales. | metodo_pago | string | null | no | contra, transferencia o mixto. Sin él no se filtra por método: el cliente ve el beneficio antes de elegir. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | beneficios[] | { codigo, nombre, efecto_tipo, envio_gratis, descuento, prosa } | Los que aplican, con su efecto ya calculado: descuento es el monto (0 si es envío gratis). | requiere_primera_compra | boolean | true si alguno depende de que sea su primera compra y no lo dijiste. Díselo al cliente en condicional: «si es tu primera compra…». ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Comercial | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 422 | Falta un campo obligatorio o uno no es de su tipo (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Solo calcula: no guarda nada. Aun así pide una clave escribe, porque es un POST. - Comercial no sabe quién es el cliente ni guarda su historial: si es su primera compra lo decides tú. - Si aplican varios, suma sus descuento y pásalos juntos a cotizar; con uno de envío gratis, manda envio_gratis: true. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/comercial/mi-espacio/reglas/evaluar \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "subtotal": "76000", "metodo_pago": "transferencia" }' ``` ■ 200 OKejemplo, con los campos del código ``` { "beneficios": [ { "codigo": "bienvenida10", "nombre": "Bienvenida 10 %", "efecto_tipo": "descuento_pct", "envio_gratis": false, "descuento": "7600", "prosa": "En tu primera compra tienes un 10 % de descuento." } ], "requiere_primera_compra": true } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Comercial · Cotizar envío https://dev.bentho.ai/api/comercial/cotizar/ POST/api/mod/comercial/{espacio}/cotizar # Cotiza el envío de un pedido Le das el pedido, la ciudad y el método de pago, y te devuelve el envío, lo que el cliente paga hoy, lo que cobra el mensajero y el total, con un desglose listo para enseñar. Es lo que va a la orden de Facturación. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Comercial. ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | ciudad_destino | string | SÍ | Adónde va. Sin importar tildes ni mayúsculas; también vale una dirección que empiece por la ciudad («Bogotá, Calle 10 # 20-30»). | metodo_pago | "contra" | "transferencia" | "mixto" | SÍ | contra: paga todo al mensajero. transferencia: paga todo por adelantado. mixto: los productos por transferencia y el envío al mensajero. | lineas | [{ sku, nombre, cantidad, precio_unitario }] | ESTA O SUBTOTAL | El pedido, con los precios de Productos. cantidad es entera (por defecto 1). Si la mandas, el subtotal sale de aquí y el peso, de peso_sku. | subtotal | string | ESTA O LINEAS | El subtotal de los productos, si no mandas lineas. | peso_kg | string | no | El peso del pedido en kilos. Manda sobre peso_sku y sobre el de fábrica. | envio_gratis | boolean | no | Si un beneficio da envío gratis. Por defecto false. | descuento | string | no | El descuento de los beneficios, en monto. Por defecto 0. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | metodo_pago · ciudad_destino | string | El método y la ciudad, con el nombre que tiene en tu tabla si se encontró. | zona · rango_peso | string | null | La zona del destino y el rango de peso de la tabla que se usó. | peso_kg · moneda | string | El peso usado y la moneda. | subtotal | string | El de los productos. | base_envio | string | La tarifa de tu tabla para esa zona y ese peso. 0 con envío gratis. | cargo_fijo · iva_envio | string | Contraentrega bajo el umbral: el cargo fijo. Contraentrega desde el umbral: el IVA del envío. Si no, 0. | comision_recaudo | string | Contraentrega desde el umbral: la comisión de recaudo. Si no, 0. | descuento · envio_gratis | string · boolean | Los que mandaste. | total_envio | string | El envío con ese método. Es el envio de la orden en Facturación. | total_a_pagar_hoy | string | Lo que el cliente paga ya: todo en transferencia, los productos menos el descuento en mixto, 0 en contraentrega. | total_mensajero | string | Lo que cobra el mensajero: todo en contraentrega, el envío en mixto, 0 en transferencia. | total_pedido | string | subtotal + total_envio − descuento. Nunca negativo. | lineas[] | { concepto, monto, tipo } | El desglose listo para enseñar. tipo: producto, envio, cargo, iva, comision, beneficio o total. | tarifa_version | string | null | La versión de la tabla activa. Pásala a la orden con el envío. | degradado · notas | boolean · string[] | degradado: true: no hay tarifa para ese destino y el envío no está calculado; notas dice por qué. También avisan de un peso estimado o fuera de la tabla. | ciudad_sugerida | string | null | Si la ciudad no se encontró pero hay una sola muy parecida en tu tabla: pregúntale al cliente si es esa. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Comercial | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 422 | Falta ciudad_destino, o metodo_pago no es contra, transferencia ni mixto | — | 422 | Falta un campo obligatorio o uno no es de su tipo (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Solo calcula: no guarda nada. Aun así pide una clave escribe, porque es un POST. - Un destino sin tarifa no es un error: responde 200 con degradado: true. No cobres ese envío: confírmalo a mano. - Las cifras van como texto, redondeadas a unidades enteras de la moneda (la mitad, hacia arriba). - Los beneficios no se deciden aquí: evalúalos antes con beneficios y pasa su descuento y su envio_gratis. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/comercial/mi-espacio/cotizar \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "ciudad_destino": "Bogotá", "metodo_pago": "transferencia", "lineas": [ { "sku": "CAFE-HUILA-500", "nombre": "Café del Huila 500 g", "cantidad": 2, "precio_unitario": "38000" } ], "descuento": "7600" }' ``` ■ 200 OKejemplo, con los campos del código ``` { "metodo_pago": "transferencia", "ciudad_destino": "BOGOTA D.C.", "zona": "Nacional", "rango_peso": "1 y 2 Kg", "peso_kg": "1.04", "moneda": "COP", "subtotal": "76000", "base_envio": "12900", "cargo_fijo": "0", "iva_envio": "0", "comision_recaudo": "0", "descuento": "7600", "envio_gratis": false, "total_envio": "12900", "total_a_pagar_hoy": "81300", "total_mensajero": "0", "total_pedido": "81300", "lineas": [ { "concepto": "Subtotal productos", "monto": "76000", "tipo": "producto" }, { "concepto": "Envío (base)", "monto": "12900", "tipo": "envio" }, { "concepto": "Descuento", "monto": "7600", "tipo": "beneficio" }, { "concepto": "Total a transferir", "monto": "81300", "tipo": "total" } ], "tarifa_version": "3", "degradado": false, "notas": [], "ciudad_sugerida": null } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Facturación · Crear orden https://dev.bentho.ai/api/facturacion/orden-crear/ POST/api/mod/facturacion/{espacio}/orders # Crea una orden Convierte un carrito en una orden con su total a pagar. Mandas variantes y cantidades: Productos pone los precios y aparta el stock, y la orden los congela. Responde con el total, hasta cuándo puede pagarse y las cuentas donde pagar. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Facturación. ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | lineas | [{ variant_id, cantidad }] | SÍ | Lo que compra: el id de la variante en Productos y cuántas (por defecto 1). Al menos una. | session_ref | string | no | Tu referencia de la conversación o del carrito. Luego puedes filtrar las órdenes por ella. | lead_ref | string | no | Tu referencia del cliente. | moneda | string | no | Por defecto "COP". | canal | string | no | El canal de la venta, por defecto "chat". Se le pasa a Productos al cotizar. | envio | string | no | El envío, por defecto 0. Se suma al total. Si lo cotizaste en Comercial, es su total_envio. | tarifa_version | string | no | La tarifa_version de esa cotización de Comercial, para saber con qué tabla se calculó el envío. | descuento | string | no | Un descuento tuyo o de los beneficios de Comercial, por defecto 0. Se resta del total de productos, sin pasar de él. | ttl_min | integer | no | Minutos para pagar antes de que venza. Por defecto, ttl_reserva_min de la configuración (45). | despacho | { campo: valor } | no | Datos de despacho que ya tengas. Solo se guardan los campos activos en la configuración; el resto se descarta sin error. Datos personales. ## RESPUESTA 201 La orden, en pending_payment. Guarda su id. | | CAMPO | TIPO | QUÉ ES | id · estado | string | El id de la orden y su estado: ver estados. | session_ref · lead_ref | string | null | Tus referencias de la conversación y del cliente, las que mandaste al crearla. | lineas[] | { variant_id, sku, nombre, cantidad, precio_unitario, total_linea, adjustments } | Cada línea con el precio que dio Productos al crearla. No cambia aunque el precio cambie después. | lineas[].adjustments | [{ promotion_id, regla, target, monto }] | Las promociones de Productos que rebajaron la línea. monto va en negativo. Vacío si ninguna. | subtotal · descuento_total | string | La suma de las líneas antes de promociones, y todos los descuentos: los de Productos más el descuento que mandaste. | envio | string | El envío que mandaste al crearla. 0 si no mandaste. | total | string | Lo que el cliente paga: el total de productos de Productos, menos tu descuento, más el envío. | moneda | string | Por defecto COP. | catalog_version · tarifa_version | string | null | La versión de precios de Productos con la que se cotizó, y la de la tarifa de Comercial que mandaste. | reservation_refs | string[] | Las reservas de stock en Productos. Vacío si reservar_stock está apagado. | despacho | object | null | Los datos de despacho guardados (campo → valor). Datos personales. | creado_at · actualizado_at | number | Segundos desde 1970, con decimales. | expira_at | number | null | Hasta cuándo puede pagarse, en segundos desde 1970. | cuentas[] | { metodo, etiqueta, numero, titular } | Las cuentas activas del espacio, para decirle al cliente dónde pagar. Salen de la configuración. | despacho_status | { activo, completo, faltantes, campos } | Qué datos de despacho faltan. Con el despacho apagado: activo: false y completo: true. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Facturación | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 409 | Una variante no tiene stock suficiente. No se crea nada | Sin stock suficiente: sin disponibilidad para | 422 | lineas viene vacía | el carrito no tiene líneas. | 422 | Alguna variante no existe o no tiene precio en Productos | hay líneas sin precio; no se puede cotizar la orden. | 422 | Productos no devolvió ninguna línea con precio | la cotización no produjo líneas facturables. | 502 | Productos no responde. No se crea nada: reintenta | El servicio de productos no está disponible; intenta más tarde. | 422 | Falta un campo obligatorio o uno no es de su tipo (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - El precio lo pone Productos, nunca tú: la orden congela el de ese momento (catalog_version) y no cambia aunque edites el catálogo después. - Con reservar_stock encendido (por defecto), aparta las unidades en Productos hasta que la orden se pague, se cancele o venza. Si una línea no tiene stock, no se aparta nada y no se crea la orden. - La orden nace en pending_payment con su expira_at. Pasado ese momento no cambia sola: la vence vencer órdenes. - Los importes van como texto decimal ("81300"), para no perder precisión. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/facturacion/mi-espacio/orders \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "lineas": [ { "variant_id": "5d0a8c3e1f7b4a2c9e6d4b1a3c8f2e70", "cantidad": 2 } ], "session_ref": "conv-0042", "envio": "12900", "tarifa_version": "3", "descuento": "7600" }' ``` ■ 201 CREATEDejemplo, con los campos del código ``` { "id": "1f6b3d8a0c2e4f7a9b5d1c3e8a6f2b90", "estado": "pending_payment", "session_ref": "conv-0042", "lead_ref": null, "lineas": [ { "variant_id": "5d0a8c3e1f7b4a2c9e6d4b1a3c8f2e70", "sku": "CAFE-HUILA-500", "nombre": "Café del Huila 500 g", "cantidad": "2", "precio_unitario": "38000", "total_linea": "76000", "adjustments": [] } ], "subtotal": "76000", "descuento_total": "7600", "envio": "12900", "total": "81300", "moneda": "COP", "catalog_version": "b3f0…", "tarifa_version": "3", "reservation_refs": ["9f2c…"], "despacho": null, "creado_at": 1790000000.12, "expira_at": 1790002700.12, "actualizado_at": 1790000000.12, "cuentas": [ { "metodo": "nequi", "etiqueta": "Nequi — Café La Loma", "numero": "3000000000", "titular": "Café La Loma SAS" } ], "despacho_status": { "activo": false, "completo": true, "faltantes": [], "campos": [] } } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Facturación · Orden https://dev.bentho.ai/api/facturacion/orden/ GET/api/mod/facturacion/{espacio}/orders/{orden} # Una orden La orden con su estado, sus líneas, su total, las cuentas donde pagar y qué datos de despacho faltan. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Facturación. | orden | string | SÍ | El id de la orden: el que devolvió crearla (32 caracteres hexadecimales). ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | id · estado | string | El id de la orden y su estado: ver estados. | session_ref · lead_ref | string | null | Tus referencias de la conversación y del cliente, las que mandaste al crearla. | lineas[] | { variant_id, sku, nombre, cantidad, precio_unitario, total_linea, adjustments } | Cada línea con el precio que dio Productos al crearla. No cambia aunque el precio cambie después. | lineas[].adjustments | [{ promotion_id, regla, target, monto }] | Las promociones de Productos que rebajaron la línea. monto va en negativo. Vacío si ninguna. | subtotal · descuento_total | string | La suma de las líneas antes de promociones, y todos los descuentos: los de Productos más el descuento que mandaste. | envio | string | El envío que mandaste al crearla. 0 si no mandaste. | total | string | Lo que el cliente paga: el total de productos de Productos, menos tu descuento, más el envío. | moneda | string | Por defecto COP. | catalog_version · tarifa_version | string | null | La versión de precios de Productos con la que se cotizó, y la de la tarifa de Comercial que mandaste. | reservation_refs | string[] | Las reservas de stock en Productos. Vacío si reservar_stock está apagado. | despacho | object | null | Los datos de despacho guardados (campo → valor). Datos personales. | creado_at · actualizado_at | number | Segundos desde 1970, con decimales. | expira_at | number | null | Hasta cuándo puede pagarse, en segundos desde 1970. | cuentas[] | { metodo, etiqueta, numero, titular } | Las cuentas activas del espacio, para decirle al cliente dónde pagar. Salen de la configuración. | despacho_status | { activo, completo, faltantes, campos } | Qué datos de despacho faltan. Con el despacho apagado: activo: false y completo: true. ## ESTADOS DE UNA ORDEN | | CAMPO | TIPO | QUÉ ES | pending_payment | esperando pago | Creada, con su total. Espera el pago hasta expira_at. | under_review | esperando revisión | Llegó un comprobante: espera que alguien lo apruebe o lo rechace. | rejected | esperando pago | Se rechazó el comprobante. Admite otro; el stock sigue reservado. | paid | pagada | Pago aprobado y venta confirmada en Productos. Lista para despachar. | fulfilled | terminada | Despachada. | expired | terminada | Venció sin pago (al llamar a vencer órdenes). Su stock se liberó. | cancelled | terminada | Cancelada. Su stock se liberó. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Facturación | módulo no habilitado para este espacio. | 404 | No hay una orden con ese id en el espacio | orden no encontrada. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Quién movió la orden a cada estado, y por qué, en su historia. - Son datos personales del cliente: guárdalos solo donde los necesites y no los copies a registros ni a herramientas de terceros. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/facturacion/mi-espacio/orders/1f6b3d8a0c2e4f7a9b5d1c3e8a6f2b90 \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "id": "1f6b3d8a0c2e4f7a9b5d1c3e8a6f2b90", "estado": "under_review", "session_ref": "conv-0042", "lead_ref": null, "lineas": [ { "variant_id": "5d0a8c3e1f7b4a2c9e6d4b1a3c8f2e70", "sku": "CAFE-HUILA-500", "nombre": "Café del Huila 500 g", "cantidad": "2", "precio_unitario": "38000", "total_linea": "76000", "adjustments": [] } ], "subtotal": "76000", "descuento_total": "7600", "envio": "12900", "total": "81300", "moneda": "COP", "catalog_version": "b3f0…", "tarifa_version": "3", "reservation_refs": ["9f2c…"], "despacho": null, "creado_at": 1790000000.12, "expira_at": 1790002700.12, "actualizado_at": 1790000000.12, "cuentas": [ { "metodo": "nequi", "etiqueta": "Nequi — Café La Loma", "numero": "3000000000", "titular": "Café La Loma SAS" } ], "despacho_status": { "activo": false, "completo": true, "faltantes": [], "campos": [] } } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Facturación · Órdenes https://dev.bentho.ai/api/facturacion/ordenes/ GET/api/mod/facturacion/{espacio}/orders # Las órdenes del espacio Todas las órdenes, la más reciente primero. Puedes filtrarlas por estado o por tu referencia. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Facturación. | estado | string, query | no | Solo las de este estado: pending_payment, under_review, paid… | session_ref | string, query | no | Solo las de esta referencia, exacta. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | orders | array | La más reciente primero. Cada una con los campos de GET orders/{orden} salvo cuentas y despacho_status. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Facturación | módulo no habilitado para este espacio. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - No pagina: devuelve todas las que cumplan el filtro. Filtra por estado para traer solo lo que te interesa. - Un estado que no existe no da error: devuelve una lista vacía. - Son datos personales del cliente: guárdalos solo donde los necesites y no los copies a registros ni a herramientas de terceros. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/facturacion/mi-espacio/orders?estado=under_review \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKderivado del código ``` { "orders": [ { "id": "1f6b3d8a0c2e4f7a9b5d1c3e8a6f2b90", "estado": "under_review", "session_ref": "conv-0042", "total": "81300", "moneda": "COP", "expira_at": 1790002700.12, … } ] } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Facturación · Subir comprobante https://dev.bentho.ai/api/facturacion/comprobante-subir/ POST/api/mod/facturacion/{espacio}/orders/{orden}/evidence # Sube un comprobante de pago La foto o el PDF con el que el cliente dice que pagó. Queda en la cola de revisión y la orden pasa a under_review. Por defecto no aprueba nada: lo decide una persona. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Facturación. | orden | string | SÍ | El id de la orden: el que devolvió crearla (32 caracteres hexadecimales). ## CUERPO · MULTIPART/FORM-DATA | | CAMPO | TIPO | REQUERIDO | QUÉ ES | file | archivo | SÍ | El comprobante: una imagen (JPEG, PNG, WebP…) o un PDF. Hasta 10 MB. Uno por petición. | monto_declarado | string | no | Lo que el cliente dice que pagó. Se guarda para quien revisa. ## RESPUESTA 201 | | CAMPO | TIPO | QUÉ ES | evidence_id | string | El id del comprobante. | decision | "pending_review" | "approved" | pending_review: espera revisión. approved solo si se aprobó solo. | auto_aprobada | boolean | Si pasó las cinco comprobaciones y la configuración deja aprobarlo solo. Por defecto, siempre false. | estado_orden | string | El estado de la orden ahora: under_review, o paid si se aprobó solo. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Facturación | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 400 | El archivo está vacío | archivo vacío. | 404 | No hay una orden con ese id en el espacio | orden no encontrada. | 409 | La orden está pagada, despachada, vencida o cancelada | la orden en estado '' no admite comprobante. | 413 | El archivo pasa de 10 MB | comprobante demasiado grande. | 413 | Los comprobantes guardados del espacio llegarían a 200 MB | cuota de almacenamiento de comprobantes excedida. | 422 | Falta el campo file | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Acepta comprobantes en pending_payment, under_review (uno más) y rejected (otro intento tras un rechazo). - Un comprobante es un dato personal (nombres, cuentas, montos): súbelo directo desde donde lo recibes y no lo guardes en otros sistemas. - Cuándo se aprueba solo, en la guía. Con la configuración de fábrica, nunca. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/facturacion/mi-espacio/orders/1f6b3d8a0c2e4f7a9b5d1c3e8a6f2b90/evidence \ -H "Authorization: Bearer $BENTHO_KEY" \ -F "file=@comprobante.jpg" \ -F 'monto_declarado=81300' ``` ■ 201 CREATEDejemplo, con los campos del código ``` { "evidence_id": "e4b7c2d9a1f84c3e9b6d0a5f2c7e1b38", "decision": "pending_review", "auto_aprobada": false, "estado_orden": "under_review" } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Facturación · Cola de revisión https://dev.bentho.ai/api/facturacion/revision/ GET/api/mod/facturacion/{espacio}/review-queue # La cola de revisión de pagos Los comprobantes que esperan que alguien los apruebe o los rechace, el más antiguo primero, con lo que se leyó de cada uno y cómo pedir su archivo. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Facturación. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | queue | array | Los comprobantes en pending_review, el más antiguo primero. Cada uno con: | queue[].id · order_id | string | El comprobante y su orden. | queue[].decision | "pending_review" | "approved" | "rejected" | pending_review mientras espera revisión. | queue[].monto_declarado | string | null | Lo que el cliente dijo que pagó, si lo mandaste al subirlo. | queue[].content_type | string | null | El tipo con el que se subió el archivo. | queue[].sha256 · phash | string · string | null | Huellas del archivo: la exacta y la de la imagen (null si no es una imagen legible). Sirven para detectar comprobantes repetidos. | queue[].extraccion_json | string (JSON) | Lo que se leyó del comprobante, como texto JSON: ok, monto, cuenta_destino, fecha_hora, referencia, banco_app y nombre_destinatario. Con ok: false no se leyó nada: revísalo a ojo. | queue[].reglas_json | string (JSON) | Las cinco comprobaciones, como texto JSON: reglas (cada una true o false), todas_ok, auto_aprobar y motivo. Qué mira cada una, en la guía. | queue[].revisor · motivo | string | null | Quién lo decidió y por qué. null mientras espera. | queue[].creado_at · revisado_at | number | null | Segundos desde 1970. revisado_at es null mientras espera. | queue[].imagen_media_type | string | El tipo con el que se sirve el archivo: una imagen, application/pdf o application/octet-stream. | queue[].imagen_es_imagen | boolean | Si se puede enseñar como imagen. | queue[].imagen_disponible | boolean | Si el archivo sigue guardado. false: el comprobante existe, su archivo no. | queue[].imagen_bytes | integer | null | El tamaño del archivo. | queue[].imagen_path | string | Dónde pedir el archivo, relativo a /api/mod/facturacion/{espacio}/: ver el archivo. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Facturación | módulo no habilitado para este espacio. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - extraccion_json y reglas_json son texto: pásalos por un parser JSON antes de leerlos. - Los comprobantes ya decididos salen de la cola. Su decisión queda en la historia de la orden. - Son datos personales del cliente: guárdalos solo donde los necesites y no los copies a registros ni a herramientas de terceros. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/facturacion/mi-espacio/review-queue \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "queue": [ { "id": "e4b7c2d9a1f84c3e9b6d0a5f2c7e1b38", "order_id": "1f6b3d8a0c2e4f7a9b5d1c3e8a6f2b90", "sha256": "5e1f…", "phash": "c3d1e0f0b0a09880", "content_type": "image/jpeg", "monto_declarado": "81300", "extraccion_json": "{\"ok\":true,\"monto\":\"81300\",\"cuenta_destino\":\"3000000000\",\"fecha_hora\":\"2026-10-03T10:12:00\",\"referencia\":\"M1234567\",\"banco_app\":\"Nequi\",\"nombre_destinatario\":\"Café La Loma SAS\"}", "reglas_json": "{\"reglas\": {\"referencia_unica\": true, \"imagen_no_duplicada\": true, \"monto_coincide\": true, \"destino_registrado\": true, \"ventana_temporal\": true}, \"todas_ok\": true, \"auto_aprobar\": false, \"motivo\": \"humano_siempre\"}", "decision": "pending_review", "revisor": null, "motivo": null, "creado_at": 1790000600.4, "revisado_at": null, "imagen_media_type": "image/jpeg", "imagen_es_imagen": true, "imagen_disponible": true, "imagen_bytes": 184320, "imagen_path": "review-queue/e4b7c2d9a1f84c3e9b6d0a5f2c7e1b38/imagen", … } ] } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Facturación · Ver comprobante https://dev.bentho.ai/api/facturacion/comprobante-imagen/ GET/api/mod/facturacion/{espacio}/review-queue/{comprobante}/imagen # El archivo de un comprobante Devuelve el archivo que subió el cliente, para mirarlo antes de aprobar o rechazar. No es JSON: son los bytes de la imagen o del PDF. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Facturación. | comprobante | string | SÍ | El id del comprobante: el evidence_id que devolvió subirlo, o el id de la cola (32 caracteres hexadecimales). ## RESPUESTA 200 El archivo, con estas cabeceras: | | CAMPO | TIPO | QUÉ ES | Content-Type | cabecera | image/jpeg, image/png, image/webp… o application/pdf. Cualquier otro tipo sale como application/octet-stream. | Content-Disposition | cabecera | inline; filename="comprobante-." | Cache-Control | cabecera | private, no-store: es un dato personal y no debe quedar en caché. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Facturación | módulo no habilitado para este espacio. | 404 | No hay un comprobante con ese id en el espacio | evidencia no encontrada. | 404 | El comprobante existe, pero su archivo ya no está guardado | el archivo del comprobante no está disponible. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Respeta el no-store: no lo guardes en disco ni en una caché compartida. Pídelo cuando alguien lo vaya a mirar. - Sirve para cualquier comprobante, también los ya aprobados o rechazados. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/facturacion/mi-espacio/review-queue/e4b7c2d9a1f84c3e9b6d0a5f2c7e1b38/imagen \ -H "Authorization: Bearer $BENTHO_KEY" \ -o comprobante.jpg ``` ■ 200 OKejemplo, con los campos del código ``` Content-Type: image/jpeg Content-Disposition: inline; filename="comprobante-e4b7c2d9a1f84c3e.jpg" Cache-Control: private, no-store X-Content-Type-Options: nosniff ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Facturación · Aprobar comprobante https://dev.bentho.ai/api/facturacion/comprobante-aprobar/ POST/api/mod/facturacion/{espacio}/review-queue/{comprobante}/approve # Aprueba un comprobante Das el pago por bueno. La venta se confirma en Productos (descuenta el stock y consume la reserva) y la orden pasa a paid. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Facturación. | comprobante | string | SÍ | El id del comprobante: el evidence_id que devolvió subirlo, o el id de la cola (32 caracteres hexadecimales). ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | revisor | string | SÍ | Quién lo aprueba: un nombre o un email. Queda en la historia de la orden. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | evidence | object | null | El comprobante, ya en approved, con revisor y revisado_at. null si la orden ya estaba pagada. | estado_orden | string | paid (o fulfilled si ya lo estaba). | venta | object | null | La venta en Productos: confirmados (cada variante con cantidad y el stock restante), no_encontrados y status. | idempotente | boolean | Solo si la orden ya estaba pagada: no se repitió nada. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Facturación | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 404 | No hay un comprobante con ese id en el espacio | evidencia no encontrada. | 500 | La orden ya no admite pago: vencida, cancelada o rechazada por otro comprobante. No se cobra nada | Error interno del servidor. | 502 | Productos no confirmó la venta. La orden no se marca pagada: reintenta | No se pudo confirmar la venta en productos; reintenta. | 422 | Falta un campo obligatorio o uno no es de su tipo (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Aprobar dos veces no cobra dos veces: la segunda responde idempotente: true sin tocar nada. - Si en la configuración encendiste emitir_factura_contable, la factura sale hacia Contabilidad. Si eso falla, la orden queda pagada igual. - Mira antes el archivo y reglas_json de la cola: un pantallazo no es prueba de pago por sí solo. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/facturacion/mi-espacio/review-queue/e4b7c2d9a1f84c3e9b6d0a5f2c7e1b38/approve \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "revisor": "ana@example.com" }' ``` ■ 200 OKejemplo, con los campos del código ``` { "evidence": { "id": "e4b7c2d9a1f84c3e9b6d0a5f2c7e1b38", "order_id": "1f6b3d8a0c2e4f7a9b5d1c3e8a6f2b90", "decision": "approved", "revisor": "ana@example.com", "revisado_at": 1790001200.9, … }, "estado_orden": "paid", "venta": { "confirmados": [ { "variant_id": "5d0a8c3e1f7b4a2c9e6d4b1a3c8f2e70", "bodega": "", "cantidad": "2", "restante": "14", … } ], "no_encontrados": [], "status": "ok", … } } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Facturación · Rechazar comprobante https://dev.bentho.ai/api/facturacion/comprobante-rechazar/ POST/api/mod/facturacion/{espacio}/review-queue/{comprobante}/reject # Rechaza un comprobante El pago no es válido. La orden pasa a rejected y el cliente puede mandar otro comprobante. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Facturación. | comprobante | string | SÍ | El id del comprobante: el evidence_id que devolvió subirlo, o el id de la cola (32 caracteres hexadecimales). ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | revisor | string | SÍ | Quién lo rechaza. Queda en la historia de la orden. | motivo | string | no | Por qué. Queda en el comprobante y en la historia. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | evidence | object | El comprobante, en rejected, con revisor, motivo y revisado_at. | estado_orden | string | rejected si la orden estaba en revisión; si no, su estado actual. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Facturación | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 404 | No hay un comprobante con ese id en el espacio | evidencia no encontrada. | 422 | Falta un campo obligatorio o uno no es de su tipo (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - No libera el stock: la orden espera otro comprobante. Si el cliente ya no va a pagar, cancélala. - Un comprobante ya decidido no cambia: rechazarlo otra vez lo devuelve tal cual. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/facturacion/mi-espacio/review-queue/e4b7c2d9a1f84c3e9b6d0a5f2c7e1b38/reject \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "revisor": "ana@example.com", "motivo": "El monto no coincide con el total" }' ``` ■ 200 OKejemplo, con los campos del código ``` { "evidence": { "id": "e4b7c2d9a1f84c3e9b6d0a5f2c7e1b38", "order_id": "1f6b3d8a0c2e4f7a9b5d1c3e8a6f2b90", "decision": "rejected", "revisor": "ana@example.com", "motivo": "El monto no coincide con el total", "revisado_at": 1790001200.9, … }, "estado_orden": "rejected" } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Facturación · Datos de despacho https://dev.bentho.ai/api/facturacion/despacho-datos/ PUT/api/mod/facturacion/{espacio}/orders/{orden}/despacho # Guarda los datos de despacho Añade a la orden los datos para enviarla (nombre, cédula, dirección…), a medida que los tengas. Solo si el espacio activó el despacho. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Facturación. | orden | string | SÍ | El id de la orden: el que devolvió crearla (32 caracteres hexadecimales). ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | datos | { campo: valor } | SÍ | Los datos que tengas, por su campo de la configuración. Se suman a los que ya había; "" borra un campo. ## RESPUESTA 200 La orden, con despacho y despacho_status al día. | | CAMPO | TIPO | QUÉ ES | despacho | { campo: valor } | Todos los datos guardados. | despacho_status.completo | boolean | true cuando están todos los requeridos. | despacho_status.faltantes | string[] | Los requeridos que faltan. | despacho_status.campos[] | { campo, etiqueta, requerido } | Los campos activos, para saber qué pedir. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Facturación | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 409 | El espacio no tiene el despacho activo (se comprueba antes que la orden) | {"mensaje": "…", "code": "despacho_inactivo"} | 404 | No hay una orden con ese id en el espacio | orden no encontrada. | 409 | La orden ya está despachada, vencida o cancelada | {"mensaje": "…", "code": "despacho_cerrado", "estado": ""} | 422 | Un campo que no está activo en la configuración. No se guarda nada | {"mensaje": "…", "code": "campos_desconocidos", "campos": [ … ]} | 422 | Falta un campo obligatorio o uno no es de su tipo (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Aquí detail es un objeto: decide por su code, no por el texto de mensaje. - Son datos personales. Se guardan solo en la orden; el registro de cambios apunta qué campos tocaste, nunca sus valores. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X PUT https://bentho.ai/api/mod/facturacion/mi-espacio/orders/1f6b3d8a0c2e4f7a9b5d1c3e8a6f2b90/despacho \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "datos": { "nombre": "Ana Rodríguez", "direccion": "Calle 10 # 20-30", "ciudad": "Bogotá" } }' ``` ■ 200 OKejemplo, con los campos del código ``` { "id": "1f6b3d8a0c2e4f7a9b5d1c3e8a6f2b90", "estado": "paid", "despacho": { "nombre": "Ana Rodríguez", "direccion": "Calle 10 # 20-30", "ciudad": "Bogotá" }, "despacho_status": { "activo": true, "completo": false, "faltantes": ["cedula", "telefono", "barrio"], "campos": [ { "campo": "nombre", "etiqueta": "Nombre completo", "requerido": true }, { "campo": "cedula", "etiqueta": "Cédula", "requerido": true }, … ] }, … } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Facturación · Marcar despachada https://dev.bentho.ai/api/facturacion/orden-despachar/ POST/api/mod/facturacion/{espacio}/orders/{orden}/fulfill # Marca una orden despachada Cierra una orden pagada: pasa de paid a fulfilled. Si el despacho está activo y lo exige, antes tienen que estar todos sus datos requeridos. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Facturación. | orden | string | SÍ | El id de la orden: el que devolvió crearla (32 caracteres hexadecimales). ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | actor | string | no | Quién la despacha. Por defecto "operador". | motivo | string | no | Una nota: la guía de envío, la transportadora… ## RESPUESTA 200 La orden, en fulfilled, con los campos de GET orders/{orden}. | | CAMPO | TIPO | QUÉ ES | estado | "fulfilled" | Despachada. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Facturación | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 404 | No hay una orden con ese id en el espacio | orden no encontrada. | 409 | Faltan datos de despacho requeridos | {"mensaje": "faltan datos de despacho requeridos para despachar.", "code": "despacho_incompleto", "faltantes": [ … ]} | 409 | La orden no está pagada | transición ilegal: → fulfilled | 422 | Falta un campo obligatorio o uno no es de su tipo (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - El cuerpo es obligatorio aunque sus campos no: manda al menos {}. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/facturacion/mi-espacio/orders/1f6b3d8a0c2e4f7a9b5d1c3e8a6f2b90/fulfill \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "actor": "ana@example.com", "motivo": "Guía 700012345678" }' ``` ■ 200 OKderivado del código ``` { "id": "1f6b3d8a0c2e4f7a9b5d1c3e8a6f2b90", "estado": "fulfilled", "total": "81300", … } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Facturación · Cancelar orden https://dev.bentho.ai/api/facturacion/orden-cancelar/ POST/api/mod/facturacion/{espacio}/orders/{orden}/cancel # Cancela una orden Cancela una orden que aún no se ha pagado y libera su stock en Productos. No se puede deshacer. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Facturación. | orden | string | SÍ | El id de la orden: el que devolvió crearla (32 caracteres hexadecimales). ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | motivo | string | no | Por qué. Queda en la historia. | actor | string | no | Quién la cancela. Sin él, la historia dice "operador". ## RESPUESTA 200 La orden, en cancelled, con los campos de GET orders/{orden}. | | CAMPO | TIPO | QUÉ ES | estado | "cancelled" | Cancelada. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Facturación | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 404 | No hay una orden con ese id en el espacio | orden no encontrada. | 409 | La orden ya está pagada, despachada o vencida | transición ilegal: → cancelled | 422 | Falta un campo obligatorio o uno no es de su tipo (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - El cuerpo es obligatorio aunque sus campos no: manda al menos {}. - Cancelar una orden ya cancelada no da error: la devuelve igual. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/facturacion/mi-espacio/orders/1f6b3d8a0c2e4f7a9b5d1c3e8a6f2b90/cancel \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "motivo": "El cliente desistió", "actor": "ana@example.com" }' ``` ■ 200 OKderivado del código ``` { "id": "1f6b3d8a0c2e4f7a9b5d1c3e8a6f2b90", "estado": "cancelled", "reservation_refs": ["9f2c…"], … } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Facturación · Historia de la orden https://dev.bentho.ai/api/facturacion/orden-historia/ GET/api/mod/facturacion/{espacio}/orders/{orden}/events # La historia de una orden Cada cambio de estado de la orden, el más antiguo primero: de qué estado a cuál, quién y por qué. Responde «¿quién aprobó este pago?» o «¿quién canceló esta orden?». - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Facturación. | orden | string | SÍ | El id de la orden: el que devolvió crearla (32 caracteres hexadecimales). ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | events[].de_estado · a_estado | string | null | De dónde a dónde. de_estado es null en la creación. | events[].actor | string | Quién: el revisor o el actor que mandaste, lead (el cliente al mandar un comprobante), sistema (al vencer), auto (aprobado solo)… | events[].motivo | string | null | Por qué. | events[].ts | number | Cuándo, en segundos desde 1970. | events[].id · order_id | string | El evento y la orden. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Facturación | módulo no habilitado para este espacio. | 404 | No hay una orden con ese id en el espacio | orden no encontrada. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/facturacion/mi-espacio/orders/1f6b3d8a0c2e4f7a9b5d1c3e8a6f2b90/events \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "events": [ { "id": "…", "order_id": "1f6b3d8a0c2e4f7a9b5d1c3e8a6f2b90", "de_estado": null, "a_estado": "pending_payment", "actor": "checkout", "motivo": "creación de orden", "ts": 1790000000.12 }, { "id": "…", "order_id": "1f6b3d8a0c2e4f7a9b5d1c3e8a6f2b90", "de_estado": "pending_payment", "a_estado": "under_review", "actor": "lead", "motivo": "comprobante recibido", "ts": 1790000600.4 }, { "id": "…", "order_id": "1f6b3d8a0c2e4f7a9b5d1c3e8a6f2b90", "de_estado": "under_review", "a_estado": "paid", "actor": "ana@example.com", "motivo": "pago aprobado (ana@example.com)", "ts": 1790001200.9 } ] } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Facturación · Vencer órdenes https://dev.bentho.ai/api/facturacion/ordenes-vencer/ POST/api/mod/facturacion/{espacio}/orders/expire-due # Vence las órdenes sin pagar Pasa a expired las órdenes en pending_payment cuyo expira_at ya pasó, y libera su stock en Productos. Dice cuántas venció. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Facturación. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | expired | integer | Cuántas órdenes venció. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Facturación | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Una orden no vence sola: sigue en pending_payment (y admite comprobantes) hasta que llamas a esta ruta. Llámala con la frecuencia que quieras, por ejemplo antes de listar las órdenes. - Puedes repetirla sin miedo: lo ya vencido no se toca. Las órdenes en under_review o rejected no vencen. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X POST https://bentho.ai/api/mod/facturacion/mi-espacio/orders/expire-due \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKderivado del código ``` { "expired": 3 } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Facturación · Configuración https://dev.bentho.ai/api/facturacion/config/ GET/api/mod/facturacion/{espacio}/payment-config # La configuración de cobro Las cuentas donde te pagan, cuánto dura una orden, cuándo se aprueba solo un comprobante, qué datos de despacho se piden y si se aparta stock en Productos o se factura en Contabilidad. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Facturación. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | name · moneda | string | Un nombre y la moneda (COP). | cuentas[] | { metodo, etiqueta, numero, titular, activo } | Dónde te pagan: metodo (nequi, daviplata, bancolombia, efectivo…), etiqueta para el cliente, numero de cuenta o celular y titular. Las activas salen en cada orden. | pasarela | { proveedor, public_key } | null | Tu pasarela de pago, si tienes: wompi, bold o nequi_conecta. | auto_aprobar_hasta | string | Hasta qué total se aprueba solo un comprobante que pasa las cinco comprobaciones, y solo si requiere_confirmacion_humana_siempre (boolean) es false. De fábrica: "0" y true, es decir, nunca. | ventana_comprobante_horas | integer | Margen, en horas, para la fecha del comprobante alrededor de la orden. Por defecto 24. | zona_horaria | string | Con qué hora local se lee el comprobante. Por defecto America/Bogota. | ttl_reserva_min | integer | Minutos para pagar una orden y tiempo que se aparta su stock. Por defecto 45. | reservar_stock | boolean | Apartar el stock en Productos al crear la orden. Por defecto true. | emitir_factura_contable | boolean | Enviar la factura a Contabilidad cuando una orden se paga. Por defecto false. | despacho | { activo, bloquea_fulfillment, campos } | Qué datos de despacho se piden: ver despacho. ## DESPACHO | | CAMPO | TIPO | QUÉ ES | activo | boolean | Pedir datos de despacho. Por defecto false: no se pide ni se guarda nada. | bloquea_fulfillment | boolean | Sin los requeridos, no deja marcar la orden despachada. Por defecto true. | campos[] | { campo, etiqueta, requerido, activo } | campo: minúsculas, números y _, empieza por letra, hasta 40; sin repetir. De fábrica: nombre, cedula, telefono, direccion, barrio y ciudad, todos activos y requeridos. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Facturación | módulo no habilitado para este espacio. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - La primera vez que se lee, se crea con los valores de fábrica. El secreto de la pasarela nunca sale en la respuesta. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/facturacion/mi-espacio/payment-config \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "name": "Café La Loma", "moneda": "COP", "cuentas": [ { "metodo": "nequi", "etiqueta": "Nequi — Café La Loma", "numero": "3000000000", "titular": "Café La Loma SAS", "activo": true } ], "pasarela": null, "auto_aprobar_hasta": "0", "ventana_comprobante_horas": 24, "zona_horaria": "America/Bogota", "ttl_reserva_min": 45, "requiere_confirmacion_humana_siempre": true, "emitir_factura_contable": false, "reservar_stock": true, "despacho": { "activo": false, "bloquea_fulfillment": true, "campos": [{ "campo": "nombre", "etiqueta": "Nombre completo", "requerido": true, "activo": true }, …] }, … } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Facturación · Editar configuración https://dev.bentho.ai/api/facturacion/config-editar/ PATCH/api/mod/facturacion/{espacio}/payment-config # Cambia la configuración de cobro Cambia solo los campos que mandes. Cada uno se reemplaza entero: cuentas es la lista completa y despacho, el objeto completo. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Facturación. ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | name · moneda | string | no | Un nombre y la moneda. | cuentas | [{ metodo, etiqueta, numero, titular, activo }] | no | Todas tus cuentas. metodo y etiqueta son obligatorios; numero es contra el que se comprueba el comprobante; activo, por defecto true. | pasarela | { proveedor, public_key, secreto_webhook } | no | proveedor: wompi, bold o nequi_conecta. secreto_webhook se guarda y nunca se devuelve; si mandas pasarela sin él, el guardado se borra. | auto_aprobar_hasta | string | no | Total máximo para aprobar solo. Para que tenga efecto manda también requiere_confirmacion_humana_siempre: false. | ventana_comprobante_horas | integer | no | Margen de la fecha del comprobante, en horas. | zona_horaria | string | no | Un nombre IANA: America/Bogota, America/Mexico_City… | ttl_reserva_min | integer | no | Minutos para pagar. Vale para las órdenes nuevas. | reservar_stock | boolean | no | Apartar el stock en Productos al crear la orden. | emitir_factura_contable | boolean | no | Enviar la factura a Contabilidad al pagarse. | despacho | { activo, bloquea_fulfillment, campos } | no | Ver despacho. Sin campos, vuelven los de fábrica. ## RESPUESTA 200 La configuración completa, ya cambiada. | | CAMPO | TIPO | QUÉ ES | name · moneda | string | Un nombre y la moneda (COP). | cuentas[] | { metodo, etiqueta, numero, titular, activo } | Dónde te pagan: metodo (nequi, daviplata, bancolombia, efectivo…), etiqueta para el cliente, numero de cuenta o celular y titular. Las activas salen en cada orden. | pasarela | { proveedor, public_key } | null | Tu pasarela de pago, si tienes: wompi, bold o nequi_conecta. | auto_aprobar_hasta | string | Hasta qué total se aprueba solo un comprobante que pasa las cinco comprobaciones, y solo si requiere_confirmacion_humana_siempre (boolean) es false. De fábrica: "0" y true, es decir, nunca. | ventana_comprobante_horas | integer | Margen, en horas, para la fecha del comprobante alrededor de la orden. Por defecto 24. | zona_horaria | string | Con qué hora local se lee el comprobante. Por defecto America/Bogota. | ttl_reserva_min | integer | Minutos para pagar una orden y tiempo que se aparta su stock. Por defecto 45. | reservar_stock | boolean | Apartar el stock en Productos al crear la orden. Por defecto true. | emitir_factura_contable | boolean | Enviar la factura a Contabilidad cuando una orden se paga. Por defecto false. | despacho | { activo, bloquea_fulfillment, campos } | Qué datos de despacho se piden: ver despacho. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Facturación | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 422 | Un campo de despacho con nombre inválido o repetido, o un proveedor que no existe | — | 422 | Falta un campo obligatorio o uno no es de su tipo (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Para añadir una cuenta, lee la configuración, añade la tuya a cuentas y manda la lista entera. - Activa la aprobación automática solo si confías en tus cuentas: un comprobante falso y uno real pueden verse iguales. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X PATCH https://bentho.ai/api/mod/facturacion/mi-espacio/payment-config \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "cuentas": [ { "metodo": "nequi", "etiqueta": "Nequi — Café La Loma", "numero": "3000000000", "titular": "Café La Loma SAS" } ], "ttl_reserva_min": 60 }' ``` ■ 200 OKejemplo, con los campos del código ``` { "name": "Café La Loma", "moneda": "COP", "cuentas": [ { "metodo": "nequi", "etiqueta": "Nequi — Café La Loma", "numero": "3000000000", "titular": "Café La Loma SAS", "activo": true } ], "ttl_reserva_min": 60, … } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Contabilidad · Configuración https://dev.bentho.ai/api/contabilidad/config/ GET/api/mod/contabilidad/{espacio}/config # La configuración del conector Qué software contable tiene conectado el espacio, qué autorizas hacer con él y qué credenciales hay guardadas, sin sus valores. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Contabilidad. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | proveedor | string | null | El software contable conectado. null si todavía no hay ninguno. | credenciales_set | string[] | Los nombres de las credenciales guardadas (email, api_token…), en orden alfabético. Sus valores nunca se devuelven. | sync_items · sync_stock | boolean | Si autorizas leer el catálogo y el stock de tu software. | push_invoice · push_payment | boolean | Si autorizas mandarle facturas y pagos. | push_customer | boolean | Si autorizas mandarle clientes. | write_back_stock | boolean | Si autorizas descontar las ventas del stock de tu software. Este sí lo comprueba el módulo: en false, no descuenta nada. | cadencia_sync_horas | integer | Cada cuántas horas toca volver a leer (por defecto 24). La usa sincronizar si toca. | plantilla_export | string | null | Las columnas del archivo de facturas al exportar: generico (por defecto), worldoffice o helisa. | bodega_default · mapeo_campos | string | null · object | Se guardan, pero hoy no cambian nada en el módulo. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Contabilidad | módulo no habilitado para este espacio. | 502 | El módulo no está disponible en este momento: reintenta en unos minutos | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Un espacio que nunca se configuró responde con todo apagado: proveedor null, credenciales_set vacío y cadencia_sync_horas 24. - Los interruptores sync_* y push_* se guardan, pero hoy no frenan nada: leer y encolar funcionan aunque estén en false. Solo write_back_stock decide de verdad. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/contabilidad/mi-espacio/config \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "proveedor": "alegra", "credenciales_set": ["api_token", "email"], "sync_items": false, "sync_stock": true, "push_invoice": true, "push_payment": true, "push_customer": false, "write_back_stock": false, "cadencia_sync_horas": 24, "bodega_default": null, "mapeo_campos": {}, "plantilla_export": null, … } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Contabilidad · Conectar software https://dev.bentho.ai/api/contabilidad/config-editar/ PATCH/api/mod/contabilidad/{espacio}/config # Conecta tu software contable Eliges el software, le das sus credenciales y dices qué autorizas. Cambia solo lo que mandes. Las credenciales entran pero no vuelven a salir: la respuesta dice qué claves hay, nunca su valor. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Contabilidad. ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | proveedor | string | no | alegra, siigo o csv_export (sin API: genera archivos). worldoffice_cloud, quickbooks y xero se aceptan, pero todavía no conectan. | credenciales | { nombre: valor } | no | Las de tu software: ver credenciales. Se suman a las guardadas clave a clave; una clave con valor vacío no borra la que había. | sync_items · sync_stock | boolean | no | Autorizas leer catálogo y stock. | push_invoice · push_payment | boolean | no | Autorizas mandar facturas y pagos. | push_customer | boolean | no | Autorizas mandar clientes. | write_back_stock | boolean | no | Autorizas descontar las ventas del stock de tu software. | cadencia_sync_horas | integer | no | Cada cuántas horas toca volver a leer. Por defecto 24. | plantilla_export | string | no | generico, worldoffice o helisa: las columnas del archivo de facturas. | bodega_default · mapeo_campos | string · object | no | Se guardan, pero hoy no cambian nada. ## RESPUESTA 200 La configuración entera, como en GET config: sin valores de credenciales. | | CAMPO | TIPO | QUÉ ES | proveedor | string | null | El software contable conectado. null si todavía no hay ninguno. | credenciales_set | string[] | Los nombres de las credenciales guardadas (email, api_token…), en orden alfabético. Sus valores nunca se devuelven. | sync_items · sync_stock | boolean | Si autorizas leer el catálogo y el stock de tu software. | push_invoice · push_payment | boolean | Si autorizas mandarle facturas y pagos. | push_customer | boolean | Si autorizas mandarle clientes. | write_back_stock | boolean | Si autorizas descontar las ventas del stock de tu software. Este sí lo comprueba el módulo: en false, no descuenta nada. | cadencia_sync_horas | integer | Cada cuántas horas toca volver a leer (por defecto 24). La usa sincronizar si toca. | plantilla_export | string | null | Las columnas del archivo de facturas al exportar: generico (por defecto), worldoffice o helisa. | bodega_default · mapeo_campos | string | null · object | Se guardan, pero hoy no cambian nada en el módulo. ## CREDENCIALES, POR SOFTWARE | | CAMPO | TIPO | QUÉ ES | alegra | email · api_token | El correo de la cuenta y su token de API. Opcionales: id_warehouse (lee el inventario de esa bodega) y warehouse_name (toma la cantidad de la bodega con ese nombre). | siigo | username · access_key | El usuario y la clave de acceso de su API. Opcional: bodega, el nombre que llevará el stock leído. | csv_export | — | Ninguna: no se conecta a nada, genera archivos para importar a mano. | worldoffice_cloud · quickbooks | — | Como xero: se pueden elegir, pero todavía no conectan. Las rutas que usan el software responden con «aún no implementado». ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Contabilidad | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 502 | El módulo no está disponible en este momento: reintenta en unos minutos | — | 422 | proveedor no es uno de la lista, o un campo no tiene su tipo (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Las credenciales no se devuelven nunca, ni aquí ni en GET config: solo sus nombres, en credenciales_set. Para saber si valen, prueba la conexión. - Para cambiar una credencial, manda la misma clave con el valor nuevo. Si cambias de software, las del anterior se quedan guardadas: no estorban. - Un campo en null se ignora: así no se vacía nada. mapeo_campos, en cambio, se reemplaza entero. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X PATCH https://bentho.ai/api/mod/contabilidad/mi-espacio/config \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "proveedor": "alegra", "credenciales": { "email": "contabilidad@example.com", "api_token": "TU_TOKEN_DE_ALEGRA" }, "sync_stock": true, "push_invoice": true, "push_payment": true }' ``` ■ 200 OKejemplo, con los campos del código ``` { "proveedor": "alegra", "credenciales_set": ["api_token", "email"], "sync_items": false, "sync_stock": true, "push_invoice": true, "push_payment": true, "push_customer": false, "write_back_stock": false, "cadencia_sync_horas": 24, "bodega_default": null, "mapeo_campos": {}, "plantilla_export": null, … } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Contabilidad · Probar conexión https://dev.bentho.ai/api/contabilidad/conexion-probar/ POST/api/mod/contabilidad/{espacio}/config/test # Prueba la conexión Comprueba que las credenciales guardadas abren tu software: hace una lectura del catálogo o del stock, sin escribir nada. Siempre responde 200: el resultado va en ok. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Contabilidad. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | ok | boolean | true si tu software respondió bien. | detalle | string | conexión válida, sin proveedor/credenciales configurados o el motivo del fallo que dio tu software. | capacidades | object | Solo si ok: lo que puede hacer, como en capacidades. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Contabilidad | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 502 | El módulo no está disponible en este momento: reintenta en unos minutos | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Con un catálogo grande puede tardar: la lectura de prueba recorre el catálogo de tu software. - Con csv_export siempre da ok: no hay nada a lo que conectarse. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X POST https://bentho.ai/api/mod/contabilidad/mi-espacio/config/test \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "ok": true, "detalle": "conexión válida", "capacidades": { "sync_items": true, "sync_stock": true, "push_customer": true, "push_invoice": true, "push_payment": true, "webhooks": false, "solo_export_archivo": false } } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Contabilidad · Capacidades https://dev.bentho.ai/api/contabilidad/capacidades/ GET/api/mod/contabilidad/{espacio}/capabilities # Qué puede hacer tu software No todos los programas contables pueden lo mismo: los que tienen API leen y reciben; los que no, solo importan archivos. Esta ruta dice cuál es tu caso. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Contabilidad. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | configurado | boolean | Hay software elegido y, salvo csv_export, credenciales guardadas. | proveedor | string | null | El software elegido. | capacidades | object | null | null si no está configurado. Si lo está, ver capacidades. ## CAPACIDADES | | CAMPO | TIPO | QUÉ ES | sync_items · sync_stock | boolean | Se puede leer su catálogo y su stock. | push_customer | boolean | Acepta clientes. | push_invoice · push_payment | boolean | Acepta facturas y pagos. | webhooks | boolean | Avisa de sus cambios. Hoy, false en todos. | solo_export_archivo | boolean | No tiene API: lo que encolas sale en un archivo. Solo csv_export. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Contabilidad | módulo no habilitado para este espacio. | 502 | El módulo no está disponible en este momento: reintenta en unos minutos | — | 500 | proveedor es uno que todavía no conecta y tiene credenciales guardadas | Error interno del servidor. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/contabilidad/mi-espacio/capabilities \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "configurado": true, "proveedor": "csv_export", "capacidades": { "sync_items": false, "sync_stock": false, "push_customer": true, "push_invoice": true, "push_payment": true, "webhooks": false, "solo_export_archivo": true } } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Contabilidad · Encolar factura https://dev.bentho.ai/api/contabilidad/factura-enviar/ POST/api/mod/contabilidad/{espacio}/push/invoice # Encola una factura Deja una factura de venta en la cola para tu software contable. Responde al momento con el id de la entrada; la factura la emite tu software cuando se procesa la cola. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Contabilidad. ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | referencia_interna | string | SÍ | Tu id de la venta (el de tu pedido). Es la clave que impide facturar dos veces lo mismo. | cliente | object | SÍ | A quién se factura: ver cliente. | lineas | array | SÍ | Una por producto: ver lineas[]. | subtotal · total | cifra | SÍ | Ya calculados: el módulo no los recalcula ni los comprueba. | descuento_total | cifra | no | Por defecto 0. | moneda | string | no | Por defecto COP. | fecha | string | SÍ | La fecha de la venta, en ISO 8601 (2026-10-02T15:04:05). | metadata | object | no | La observación de la factura: anotation en Alegra, observations en Siigo. Sin ella, «Ref interna ». ## RESPUESTA 202 La entrada en la cola. Guarda su outbox_id. | | CAMPO | TIPO | QUÉ ES | outbox_id | string | El id de la entrada. | estado | string | pending si es nueva; si ya existía, el suyo de ahora. | idempotent_replay | boolean | true si esa referencia_interna ya estaba encolada: no se creó otra. ## CLIENTE | | CAMPO | TIPO | QUÉ ES | tipo_id | "NIT" | "CC" | "CE" | "PASAPORTE" | El tipo de documento. Por defecto CC. | identificacion | string | El número. | nombre | string | Nombre o razón social. | email · telefono | string | Opcionales. | raw | object | Campos extra, con los nombres de tu software, que se mandan tal cual al crear el cliente. Al facturar no se usan. ## LINEAS[] | | CAMPO | TIPO | QUÉ ES | sku | string | La referencia del producto. | descripcion | string | El texto de la línea. | cantidad · precio_unitario | cifra | Unidades y precio de cada una. | descuento · impuesto_pct | cifra | El descuento de la línea y el porcentaje de impuesto (19 = 19 %). Por defecto 0. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Contabilidad | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 502 | El módulo no está disponible en este momento: reintenta en unos minutos | — | 422 | Falta un campo obligatorio o uno no tiene su tipo (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Encolar no manda nada todavía: el envío ocurre al procesar la cola. Síguelo con GET push/{entrada}. - Encolar dos veces la misma referencia_interna no duplica nada: devuelve la entrada que ya existía, con idempotent_replay: true y su estado de ahora. - Las cifras se guardan exactas: mándalas como texto ("38000.50"). También aceptan números, pero un número con decimales puede perder precisión antes de llegar. - La factura legal la emite tu software con su numeración: Bentho le pasa los datos. El número y, si lo da, el CUFE vuelven en resultado_json. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/contabilidad/mi-espacio/push/invoice \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "referencia_interna": "pedido-1042", "cliente": { "tipo_id": "CC", "identificacion": "1020304050", "nombre": "Ana Rojas", "email": "ana@example.com", "telefono": "+57 300 000 0000" }, "lineas": [ { "sku": "CAFE-500", "descripcion": "Café de origen 500 g", "cantidad": "2", "precio_unitario": "38000", "impuesto_pct": "5" } ], "subtotal": "76000", "total": "79800", "fecha": "2026-10-02T15:04:05" }' ``` ■ 202 ACCEPTEDejemplo, con los campos del código ``` { "outbox_id": "5b8e2f1a9c7d4e3b8a6f0c2d4e6a8b1c", "estado": "pending", "idempotent_replay": false } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Contabilidad · Encolar pago https://dev.bentho.ai/api/contabilidad/pago-enviar/ POST/api/mod/contabilidad/{espacio}/push/payment # Encola un pago Deja un pago recibido en la cola para registrarlo en tu software contable. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Contabilidad. ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | referencia_interna | string | SÍ | Tu id del pago o de la venta. Impide registrarlo dos veces. | factura_external_id | string | no | El id de la factura en tu software (su external_id, en el resultado de la factura). Si va, el pago se asocia a ella. | monto | cifra | SÍ | Lo que se pagó. | metodo | string | SÍ | Cómo se pagó, con el nombre que use tu software (transfer, cash…). | fecha | string | SÍ | Cuándo, en ISO 8601. ## RESPUESTA 202 La entrada en la cola. Guarda su outbox_id. | | CAMPO | TIPO | QUÉ ES | outbox_id | string | El id de la entrada. | estado | string | pending si es nueva; si ya existía, el suyo de ahora. | idempotent_replay | boolean | true si esa referencia_interna ya estaba encolada: no se creó otra. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Contabilidad | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 502 | El módulo no está disponible en este momento: reintenta en unos minutos | — | 422 | Falta un campo obligatorio o uno no tiene su tipo (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Encolar no manda nada todavía: el envío ocurre al procesar la cola. Síguelo con GET push/{entrada}. - La clave va por tipo: un pago puede llevar la misma referencia_interna que su factura sin chocar con ella. - Las cifras se guardan exactas: mándalas como texto ("38000.50"). También aceptan números, pero un número con decimales puede perder precisión antes de llegar. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/contabilidad/mi-espacio/push/payment \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "referencia_interna": "pedido-1042", "factura_external_id": "1187", "monto": "79800", "metodo": "transfer", "fecha": "2026-10-02T15:10:00" }' ``` ■ 202 ACCEPTEDejemplo, con los campos del código ``` { "outbox_id": "5b8e2f1a9c7d4e3b8a6f0c2d4e6a8b1c", "estado": "pending", "idempotent_replay": false } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Contabilidad · Encolar cliente https://dev.bentho.ai/api/contabilidad/cliente-enviar/ POST/api/mod/contabilidad/{espacio}/push/customer # Encola un cliente Deja un cliente en la cola para darlo de alta como tercero en tu software contable. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Contabilidad. ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | referencia_interna | string | SÍ | Tu id del cliente. Impide darlo de alta dos veces. No la dejes vacía. | cliente | object | SÍ | Sus datos: ver cliente. ## RESPUESTA 202 La entrada en la cola. Guarda su outbox_id. | | CAMPO | TIPO | QUÉ ES | outbox_id | string | El id de la entrada. | estado | string | pending si es nueva; si ya existía, el suyo de ahora. | idempotent_replay | boolean | true si esa referencia_interna ya estaba encolada: no se creó otra. ## CLIENTE | | CAMPO | TIPO | QUÉ ES | tipo_id | "NIT" | "CC" | "CE" | "PASAPORTE" | El tipo de documento. Por defecto CC. | identificacion | string | El número. | nombre | string | Nombre o razón social. | email · telefono | string | Opcionales. | raw | object | Campos extra, con los nombres de tu software, que se mandan tal cual al crear el cliente. Al facturar no se usan. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Contabilidad | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 502 | El módulo no está disponible en este momento: reintenta en unos minutos | — | 422 | Falta un campo obligatorio o uno no tiene su tipo (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Encolar no manda nada todavía: el envío ocurre al procesar la cola. Síguelo con GET push/{entrada}. - Encolar dos veces la misma referencia_interna no duplica nada: devuelve la entrada que ya existía, con idempotent_replay: true y su estado de ahora. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/contabilidad/mi-espacio/push/customer \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "referencia_interna": "cliente-1020304050", "cliente": { "tipo_id": "CC", "identificacion": "1020304050", "nombre": "Ana Rojas", "email": "ana@example.com", "telefono": "+57 300 000 0000" } }' ``` ■ 202 ACCEPTEDejemplo, con los campos del código ``` { "outbox_id": "5b8e2f1a9c7d4e3b8a6f0c2d4e6a8b1c", "estado": "pending", "idempotent_replay": false } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Contabilidad · Estado de envío https://dev.bentho.ai/api/contabilidad/envio/ GET/api/mod/contabilidad/{espacio}/push/{entrada} # El estado de un envío Una entrada de la cola: en qué estado está y, cuando termina, qué contestó tu software. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Contabilidad. | entrada | string | SÍ | El id de la entrada en la cola: el outbox_id que devolvió encolarla (32 caracteres hexadecimales). ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | id | string | El id de la entrada (el outbox_id). | tipo | "invoice" | "payment" | "customer" | Factura, pago o cliente. | referencia_interna | string | La tuya. | idem_key | string | tipo:referencia_interna, la clave que impide encolarla dos veces. | payload_json | string | Lo que encolaste, como texto JSON (con las cifras como texto). | estado | string | Ver estados. | intentos · max_intentos | integer | Los intentos hechos y cuántos tiene antes de quedar en dead. | resultado_json | string | null | Al terminar, lo que contestó tu software, como texto JSON: external_id (su id allí), estado y raw (su respuesta entera); en facturas, además cufe, numero y pdf_url si los da. | motivo_error | string | null | Por qué falló el último intento, con el mensaje de tu software. | next_attempt_at | number | null | Desde cuándo se puede volver a intentar. Segundos desde 1970. | created_at · updated_at | number | Segundos desde 1970, con decimales. ## ESTADOS DE UNA ENTRADA | | CAMPO | TIPO | QUÉ ES | pending | en cola | Esperando a que se procese la cola. | processing | en curso | Mandándose ahora mismo. | done | terminada | Tu software la aceptó, una sola vez. Con csv_export: lista para el archivo. | failed | se reintenta | Falló y le quedan intentos: va en el siguiente procesado a partir de next_attempt_at. Sin software configurado también espera aquí, sin gastar intentos. | dead | parada | Agotó los intentos y ya no se reintenta sola. Mira motivo_error y reinténtala. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Contabilidad | módulo no habilitado para este espacio. | 404 | No hay una entrada con ese id en la cola del espacio | entrada de outbox no encontrada. | 502 | El módulo no está disponible en este momento: reintenta en unos minutos | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - payload_json y resultado_json son texto: pásalos por tu lector de JSON para usarlos. - Con csv_export, el estado de resultado_json es export_pending: la entrada espera a salir en el archivo. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/contabilidad/mi-espacio/push/5b8e2f1a9c7d4e3b8a6f0c2d4e6a8b1c \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "id": "5b8e2f1a9c7d4e3b8a6f0c2d4e6a8b1c", "tipo": "invoice", "referencia_interna": "pedido-1042", "idem_key": "invoice:pedido-1042", "payload_json": "{\"referencia_interna\": \"pedido-1042\", \"cliente\": {…}, \"total\": \"79800\", …}", "estado": "done", "intentos": 1, "max_intentos": 5, "resultado_json": "{\"external_id\": \"1187\", \"cufe\": \"…\", \"numero\": \"FE-1187\", \"pdf_url\": null, \"estado\": \"open\", \"raw\": {…}}", "motivo_error": null, "next_attempt_at": 1790000000.2, "created_at": 1790000000.2, "updated_at": 1790000012.7 } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Contabilidad · Procesar cola https://dev.bentho.ai/api/contabilidad/cola-procesar/ POST/api/mod/contabilidad/{espacio}/outbox/process # Procesa la cola Manda a tu software lo que está en la cola y ya toca: lo nuevo y los fallos cuyo next_attempt_at ya pasó. Las va mandando una a una y responde con el resumen. Nunca falla por una entrada: cada una queda con su estado. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Contabilidad. | limit | int, query | no | Cuántas entradas como mucho, las más antiguas primero. Por defecto 50. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | claimed | integer | Las que tomó. | done | integer | Las que tu software aceptó. | failed | integer | Las que fallaron y se reintentarán. | dead | integer | Las que agotaron sus intentos. | deferred | integer | Solo si no hay software configurado: las que esperan sin gastar intentos (quedan en failed). ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Contabilidad | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 502 | El módulo no está disponible en este momento: reintenta en unos minutos | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - En el Estudio es el botón «Procesar cola ahora» del panel de Contabilidad. Llamarla seguido no duplica nada: cada entrada se manda una sola vez. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X POST https://bentho.ai/api/mod/contabilidad/mi-espacio/outbox/process \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "claimed": 3, "done": 2, "failed": 1, "dead": 0 } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Contabilidad · Cola de envíos https://dev.bentho.ai/api/contabilidad/cola/ GET/api/mod/contabilidad/{espacio}/outbox # La cola de envíos Las facturas, pagos y clientes encolados, la más reciente primero. Filtra por estado para ver solo lo que falló. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Contabilidad. | estado | string, query | no | pending, processing, done, failed o dead. Sin él, todas. | limit | int, query | no | Cuántas. Por defecto 200. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | entries | array | La más reciente primero, cada una con los campos de GET push/{entrada}. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Contabilidad | módulo no habilitado para este espacio. | 502 | El módulo no está disponible en este momento: reintenta en unos minutos | — | 422 | Falta un campo obligatorio o uno no tiene su tipo (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Un estado que no existe no da error: devuelve la lista vacía. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/contabilidad/mi-espacio/outbox?estado=dead \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "entries": [ { "id": "5b8e2f1a9c7d4e3b8a6f0c2d4e6a8b1c", "tipo": "invoice", "referencia_interna": "pedido-1051", "estado": "dead", "intentos": 5, "max_intentos": 5, "motivo_error": "fallo en POST /invoices de Alegra: …", … } ] } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Contabilidad · Reintentar envío https://dev.bentho.ai/api/contabilidad/envio-reintentar/ POST/api/mod/contabilidad/{espacio}/outbox/{entrada}/retry # Reintenta un envío Vuelve a poner en pending una entrada que falló o quedó en dead, para que salga en el siguiente procesado. Una entrada en done no se toca. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Contabilidad. | entrada | string | SÍ | El id de la entrada en la cola: el outbox_id que devolvió encolarla (32 caracteres hexadecimales). ## RESPUESTA 200 La entrada, ya en pending (o tal cual, si estaba en done). | | CAMPO | TIPO | QUÉ ES | id | string | El id de la entrada (el outbox_id). | tipo | "invoice" | "payment" | "customer" | Factura, pago o cliente. | referencia_interna | string | La tuya. | idem_key | string | tipo:referencia_interna, la clave que impide encolarla dos veces. | payload_json | string | Lo que encolaste, como texto JSON (con las cifras como texto). | estado | string | Ver estados. | intentos · max_intentos | integer | Los intentos hechos y cuántos tiene antes de quedar en dead. | resultado_json | string | null | Al terminar, lo que contestó tu software, como texto JSON: external_id (su id allí), estado y raw (su respuesta entera); en facturas, además cufe, numero y pdf_url si los da. | motivo_error | string | null | Por qué falló el último intento, con el mensaje de tu software. | next_attempt_at | number | null | Desde cuándo se puede volver a intentar. Segundos desde 1970. | created_at · updated_at | number | Segundos desde 1970, con decimales. ## ESTADOS DE UNA ENTRADA | | CAMPO | TIPO | QUÉ ES | pending | en cola | Esperando a que se procese la cola. | processing | en curso | Mandándose ahora mismo. | done | terminada | Tu software la aceptó, una sola vez. Con csv_export: lista para el archivo. | failed | se reintenta | Falló y le quedan intentos: va en el siguiente procesado a partir de next_attempt_at. Sin software configurado también espera aquí, sin gastar intentos. | dead | parada | Agotó los intentos y ya no se reintenta sola. Mira motivo_error y reinténtala. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Contabilidad | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 404 | No hay una entrada con ese id en la cola del espacio | entrada de outbox no encontrada. | 502 | El módulo no está disponible en este momento: reintenta en unos minutos | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - No la manda: después, procesa la cola. - No reinicia intentos: una entrada que estaba en dead tiene un intento más y, si vuelve a fallar, vuelve a dead. Corrige antes lo que dice motivo_error. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X POST https://bentho.ai/api/mod/contabilidad/mi-espacio/outbox/5b8e2f1a9c7d4e3b8a6f0c2d4e6a8b1c/retry \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "id": "5b8e2f1a9c7d4e3b8a6f0c2d4e6a8b1c", "tipo": "invoice", "referencia_interna": "pedido-1051", "estado": "pending", "intentos": 5, "max_intentos": 5, "motivo_error": null, … } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Contabilidad · Conciliación https://dev.bentho.ai/api/contabilidad/conciliacion/ GET/api/mod/contabilidad/{espacio}/reconciliation # La conciliación Cuadra lo que mandaste con lo que tu software confirmó en un periodo: cuántas facturas y pagos hay en cada estado, cuánto suman los confirmados y cuáles quedaron sin confirmar. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Contabilidad. | desde · hasta | number, query | no | El periodo, en segundos desde 1970, por la fecha en que se encoló. Sin ellos, todo. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | desde · hasta | number | El periodo que pediste. | resumen.invoice · payment | object | Facturas y pagos: cuántos en done, pending (incluye los que se están mandando), failed y dead, y monto_total, la suma de los done (texto). | pendientes | array | Los que tu software no ha confirmado (pending, failed o dead): id, tipo, referencia_interna, estado y motivo. | deriva | boolean | true si algo quedó sin confirmar: hay que mirarlo. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Contabilidad | módulo no habilitado para este espacio. | 502 | El módulo no está disponible en este momento: reintenta en unos minutos | — | 422 | Falta un campo obligatorio o uno no tiene su tipo (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Los clientes no entran en la conciliación: solo facturas y pagos. - En el Estudio, el panel de Contabilidad pinta estas cifras y la lista de pendientes, con su botón de reintentar. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/contabilidad/mi-espacio/reconciliation?desde=1790000000&hasta=1792592000 \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "desde": 1790000000.0, "hasta": 1792592000.0, "resumen": { "invoice": { "done": 42, "pending": 3, "failed": 1, "dead": 1, "monto_total": "3351600" }, "payment": { "done": 40, "pending": 0, "failed": 0, "dead": 0, "monto_total": "3190000" } }, "pendientes": [ { "id": "5b8e2f1a9c7d4e3b8a6f0c2d4e6a8b1c", "tipo": "invoice", "referencia_interna": "pedido-1051", "estado": "dead", "motivo": "fallo en POST /invoices de Alegra: …" }, … ], "deriva": true } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Contabilidad · Exportar archivo https://dev.bentho.ai/api/contabilidad/exportar/ GET/api/mod/contabilidad/{espacio}/exports/{lote} # Exporta a un archivo Para programas contables sin API (csv_export): arma un CSV con las facturas, los pagos o los clientes de la cola, con las columnas de la plantilla de importación de tu programa. Lo importas tú. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Contabilidad. | lote | string | SÍ | Qué exportar: invoice (facturas), payment (pagos) o customer (clientes). | estado | string, query | no | Las entradas en qué estado. Por defecto done. ## RESPUESTA 200 Un JSON con el archivo dentro, no el archivo: guarda contenido con filename. | | CAMPO | TIPO | QUÉ ES | filename | string | _.csv, p. ej. invoice_worldoffice.csv. | contenido | string | El CSV, con cabecera y una fila por línea de factura (o por pago, o por cliente). | content_type | "text/csv" | Siempre text/csv. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Contabilidad | módulo no habilitado para este espacio. | 502 | El módulo no está disponible en este momento: reintenta en unos minutos | — | 400 | lote no es invoice, payment ni customer | tipo inválido (invoice|payment|customer). | 409 | Tu software tiene API: lo encolado se le manda directamente | el proveedor configurado tiene API; el export de archivo no aplica. | 409 | El espacio todavía no tiene proveedor configurado | No hay software contable configurado … | 409 | proveedor es worldoffice_cloud, quickbooks o xero: todavía no conectan | Adaptador '…' aún no implementado … Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Solo salen las entradas ya procesadas (done): encola, procesa la cola y luego exporta. - Exportar no marca nada: cada vez trae todas las entradas de ese tipo en ese estado (las 1000 más recientes). Lleva tú la cuenta de qué importaste. - plantilla_export cambia las columnas de las facturas: generico, worldoffice o helisa. Pagos y clientes van siempre en genérico. Un texto que empieza por = + - o @ sale con un apóstrofo delante, para que tu programa no lo lea como fórmula. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/contabilidad/mi-espacio/exports/invoice \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "filename": "invoice_worldoffice.csv", "contenido": "Prefijo,Documento,Fecha,Identificacion,Cliente,CodigoProducto,Descripcion,Cantidad,ValorUnitario,Descuento,PorcentajeIva\r\nFV,pedido-1042,2026-10-02,1020304050,Ana Rojas,CAFE-500,Café de origen 500 g,2,38000,0,5\r\n…", "content_type": "text/csv" } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Contabilidad · Leer catálogo https://dev.bentho.ai/api/contabilidad/catalogo-sincronizar/ POST/api/mod/contabilidad/{espacio}/sync/items # Lee el catálogo de tu software Lee los productos y servicios de tu software contable y te los devuelve en un formato común, con su precio e impuesto. Queda anotado en el historial. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Contabilidad. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | run_id | string | La corrida, en el historial. | modelo | "items" | Qué se leyó. | count | integer | Cuántos artículos. | items[] | array | Cada artículo: sku, nombre, tipo (producto o servicio), precio e impuesto_pct (texto o null), moneda, external_id (su id en tu software) y raw (el registro entero, como lo da tu software). ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Contabilidad | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 502 | El módulo no está disponible en este momento: reintenta en unos minutos | — | 400 | El espacio todavía no tiene proveedor configurado | No hay software contable configurado … | 400 | proveedor es worldoffice_cloud, quickbooks o xero: todavía no conectan | Adaptador '…' aún no implementado … | 400 | Tu software no permite leer su catálogo (csv_export) | el proveedor no soporta sync de ítems | 400 | Tu software rechazó las credenciales o no respondió: detail trae su motivo | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Hoy cada lectura trae el catálogo entero, hasta 5000 artículos: compáralo tú con lo que ya tienes por sku o external_id. - Si el artículo no tiene referencia en tu software, el sku se arma con su id (alegra-88, siigo-…). CURLPYTHONJAVASCRIPT COPIAR ``` curl -X POST https://bentho.ai/api/mod/contabilidad/mi-espacio/sync/items \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "run_id": "e1c4a7b2d9f04c6e8a3b5d7f1c2e4a90", "modelo": "items", "count": 2, "items": [ { "sku": "CAFE-500", "nombre": "Café de origen 500 g", "tipo": "producto", "precio": "38000", "moneda": "COP", "impuesto_pct": "5", "external_id": "88", "raw": { … } }, … ] } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Contabilidad · Leer stock https://dev.bentho.ai/api/contabilidad/stock-sincronizar/ POST/api/mod/contabilidad/{espacio}/sync/stock # Lee el stock de tu software Lee cuántas unidades hay de cada artículo en tu software contable. Queda anotado en el historial. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Contabilidad. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | run_id | string | La corrida, en el historial. | modelo | "stock" | Qué se leyó. | count | integer | Cuántos artículos. | items[] | array | Cada artículo: sku, external_id, bodega (o null) y cantidad disponible (texto). ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Contabilidad | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 502 | El módulo no está disponible en este momento: reintenta en unos minutos | — | 400 | El espacio todavía no tiene proveedor configurado | No hay software contable configurado … | 400 | proveedor es worldoffice_cloud, quickbooks o xero: todavía no conectan | Adaptador '…' aún no implementado … | 400 | Tu software no permite leer su stock (csv_export) | el proveedor no soporta sync de stock | 400 | Tu software rechazó las credenciales o no respondió: detail trae su motivo | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - La bodega sale de las credenciales: warehouse_name en Alegra, bodega en Siigo. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X POST https://bentho.ai/api/mod/contabilidad/mi-espacio/sync/stock \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "run_id": "e1c4a7b2d9f04c6e8a3b5d7f1c2e4a90", "modelo": "stock", "count": 2, "items": [ { "sku": "CAFE-500", "external_id": "88", "bodega": "Principal", "cantidad": "124" }, … ] } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Contabilidad · Sincronizar si toca https://dev.bentho.ai/api/contabilidad/sincronizar-si-toca/ POST/api/mod/contabilidad/{espacio}/sync/{datos}/due # Sincroniza solo si toca Como leer el catálogo o el stock, pero solo si toca: si nunca se leyó, si ya pasaron cadencia_sync_horas desde la última lectura o si hay cambios pendientes. Si no toca, no llama a tu software. Para llamarla a menudo desde una tarea programada. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Contabilidad. | datos | string | SÍ | items (el catálogo) o stock. | force | boolean, query | no | true para leer aunque no toque. Por defecto false. ## RESPUESTA 200 Si toca, lo mismo que leer el catálogo o el stock. Si no: | | CAMPO | TIPO | QUÉ ES | skipped | true | No leyó nada. | reason | "cadencia" | Aún no toca. | modelo | string | items o stock. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Contabilidad | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 502 | El módulo no está disponible en este momento: reintenta en unos minutos | — | 400 | datos no es items ni stock | modelo inválido (items|stock). | 400 | El espacio todavía no tiene proveedor configurado | No hay software contable configurado … | 400 | proveedor es worldoffice_cloud, quickbooks o xero: todavía no conectan | Adaptador '…' aún no implementado … Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X POST https://bentho.ai/api/mod/contabilidad/mi-espacio/sync/items/due \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "skipped": true, "reason": "cadencia", "modelo": "items" } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Contabilidad · Historial de lecturas https://dev.bentho.ai/api/contabilidad/sincronizaciones/ GET/api/mod/contabilidad/{espacio}/sync/runs # El historial de lecturas Cada lectura del catálogo o del stock, la más reciente primero. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Contabilidad. | limit | int, query | no | Cuántas. Por defecto 50. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | runs[].id | string | La corrida (el run_id que devolvió la lectura). | runs[].modelo | string | items o stock. | runs[].items | integer | Cuántos artículos leyó. | runs[].cursor_inicio · fin | string | null | Desde y hasta cuándo leyó, en segundos desde 1970 (como texto). | runs[].detalle_json | string | null | El resumen, como texto JSON. | runs[].created_at | number | Cuándo, en segundos desde 1970. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Contabilidad | módulo no habilitado para este espacio. | 502 | El módulo no está disponible en este momento: reintenta en unos minutos | — | 422 | Falta un campo obligatorio o uno no tiene su tipo (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/contabilidad/mi-espacio/sync/runs \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "runs": [ { "id": "e1c4a7b2d9f04c6e8a3b5d7f1c2e4a90", "modelo": "stock", "cursor_inicio": "1789913600.4", "cursor_fin": "1790000000.2", "items": 124, "detalle_json": "{\"count\": 124}", "created_at": 1790000000.2 } ] } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Contabilidad · Descontar stock https://dev.bentho.ai/api/contabilidad/stock-descontar/ POST/api/mod/contabilidad/{espacio}/stock/write-back # Descuenta una venta del stock Resta del inventario de tu software lo que se vendió. Solo si autorizaste write_back_stock y solo con Alegra. Nunca da error por tu software: si no pudo, lo dice en pushed y reason. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Contabilidad. ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | lines | [{ external_id, cantidad }] | SÍ | Lo vendido: el id del artículo en tu software y las unidades, como texto (por defecto "1"). Las líneas sin external_id se saltan. | fecha | string | no | La fecha del ajuste, AAAA-MM-DD. Por defecto, hoy. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | pushed | boolean | true si tu software lo descontó. | reason | string | Solo si no: write_back_desactivado, sin_items_con_external_id, sin_proveedor, proveedor_sin_write_back_stock (tu software no lo permite) o error_proveedor. | error | string | Con sin_proveedor o error_proveedor: el motivo. | items | integer | Si se descontó: cuántas líneas. | resultado | object | Si se descontó: ok, external_id (el ajuste en tu software), estado (adjusted) y raw. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Contabilidad | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 502 | El módulo no está disponible en este momento: reintenta en unos minutos | — | 422 | Falta un campo obligatorio o uno no tiene su tipo (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Siempre resta: la cantidad se toma en positivo y se descuenta. Va directo a tu software, sin pasar por la cola, y no se reintenta: si falla, vuelve a llamarla. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/contabilidad/mi-espacio/stock/write-back \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "lines": [ { "external_id": "88", "cantidad": "2" } ], "fecha": "2026-10-02" }' ``` ■ 200 OKejemplo, con los campos del código ``` { "pushed": true, "items": 1, "resultado": { "ok": true, "external_id": "301", "estado": "adjusted", "raw": { … } } } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Kommo · Configuración https://dev.bentho.ai/api/kommo/config/ GET/api/mod/kommo/{espacio}/config # La conexión con Kommo Cómo está conectado el espacio a tu cuenta de Kommo: subdominio, qué credenciales hay guardadas, el canal, el bot, las etapas y la sincronización. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Kommo. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | is_configured | boolean | true si hay subdominio y token de acceso: lo mínimo para hablar con Kommo. | credenciales_set | string[] | Los nombres de las credenciales guardadas (access_token, client_id…). Sus valores no salen nunca. | subdomain | string | El subdominio de tu cuenta: tiendaaurora en tiendaaurora.kommo.com. | modo_canal | "custom" | "salesbot" | Por dónde llegan los mensajes: custom, el canal de chat de Bentho en tu cuenta; salesbot, un paso de tu Salesbot (para los canales oficiales de Kommo, como WhatsApp). Por defecto custom. Ver la guía. | auth_mode | "long_lived" | "oauth" | long_lived (por defecto): un token de larga duración de Kommo. oauth: con renovación. | token_expires_at | number | Con OAuth, cuándo caduca el token, en segundos desde 1970; se renueva solo. Al pegar un token de larga duración, manda 0. | bot_activo | boolean | El chat automático: Bentho contesta los mensajes que llegan por Kommo. Por defecto false. | chat_allowlist | string[] | Chats de prueba: si tiene ids, el bot solo contesta a esas conversaciones y calla en las demás. Vacía (por defecto), contesta a todas. Con el Salesbot y los webhooks del CRM, el id es el del lead de Kommo. | pausa_humano_ttl_s | integer | Segundos que el bot se calla en un chat después de que escribe una persona de tu equipo. Por defecto 3600. | debounce_s · debounce_max_s | number | Si el cliente escribe varios mensajes seguidos, se contestan juntos: espera debounce_s de silencio (10 por defecto), y nunca más de debounce_max_s desde el primero (30). 0 en debounce_s: uno por uno. | audio_activo | boolean | Transcribir las notas de voz y contestarlas como texto. Por defecto false. Pide el módulo Transcripción. | audio_idioma | string | El idioma de las notas de voz. Por defecto "es". | audio_duracion_max_s | integer | Por encima de esta duración no se transcribe: el bot le pide al cliente que lo escriba o lo resuma. Por defecto 120. | audio_max_por_turno | integer | Notas de voz que se transcriben, como mucho, en una misma respuesta. Por defecto 3. | adjuntos_activo | boolean | Las imágenes y PDF que manda el cliente (JPG, PNG, WEBP, HEIC o PDF) se adjuntan como comprobante de pago a su pedido en curso. Por defecto false. | adjuntos_tamano_max_mb | number | El tamaño máximo de un adjunto, en MB. Por defecto 10. Lo que pase se queda en Kommo. | add_message_texto_activo | boolean | Atender también el texto que llega por el webhook de mensaje entrante, además del Salesbot. Por defecto true: así no se pierde ningún mensaje. | salesbot_liberacion_inmediata | boolean | En modo salesbot: libera tu Salesbot al instante y entrega la respuesta con un bot aparte (salesbot_bot_entrega_id). Por defecto false. | salesbot_bot_entrega_id | integer | null | El id del Salesbot de Kommo que muestra la respuesta, con la liberación inmediata. | salesbot_paso_cierre | integer | null | El paso al que salta tu Salesbot para cerrarse. null: termina donde esté. | mapeo_estados | { estado: { pipeline_id, status_id } } | A qué etapa de Kommo va cada estado del cliente en Bentho: nuevo, contactado, ganado, cerrado y handoff (requiere humano). Los ids, de embudos. Se reemplaza entero. | responsable_handoff_id | integer | null | El id del usuario de Kommo al que se asigna el lead cuando el cliente pide una persona. | sync_leads_activo | boolean | Mover las tarjetas de Kommo cuando cambia el estado del cliente en Bentho. Por defecto false. | amojo_id | string | null | El id de tu cuenta en el chat de Kommo. Lo rellena probar la conexión. | scope_id | string | null | En modo custom: el del canal de chat conectado a tu cuenta. | agent_ref_id | string | null | En modo custom: el usuario de Kommo que firma las respuestas, para que no parezcan del cliente. | name | string | Un nombre para la conexión. Por defecto, el id del espacio. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Kommo | módulo no habilitado para este espacio. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Un espacio que nunca se conectó responde la configuración por defecto: sin subdominio, con todo apagado. - Las credenciales no salen nunca: solo sus nombres, en credenciales_set. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/kommo/mi-espacio/config \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "name": "mi-espacio", "subdomain": "tiendaaurora", "is_configured": true, "credenciales_set": ["access_token"], "modo_canal": "salesbot", "auth_mode": "long_lived", "token_expires_at": 0, "amojo_id": "4b2f9c1e-8d3a-4e7b-9f10-2c5d6a7b8e91", "scope_id": null, "agent_ref_id": null, "bot_activo": true, "chat_allowlist": ["31415926"], "pausa_humano_ttl_s": 3600, "debounce_s": 10.0, "debounce_max_s": 30.0, "audio_activo": true, "audio_idioma": "es", "audio_duracion_max_s": 120, "audio_max_por_turno": 3, "adjuntos_activo": false, "adjuntos_tamano_max_mb": 10.0, "add_message_texto_activo": true, "salesbot_liberacion_inmediata": false, "salesbot_bot_entrega_id": null, "salesbot_paso_cierre": null, "mapeo_estados": { "nuevo": { "pipeline_id": 7012345, "status_id": 58210001 }, "contactado": { "pipeline_id": 7012345, "status_id": 58210002 }, "handoff": { "pipeline_id": 7012345, "status_id": 58210003 } }, "responsable_handoff_id": 9876543, "sync_leads_activo": true, … } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Kommo · Editar configuración https://dev.bentho.ai/api/kommo/config-editar/ PATCH/api/mod/kommo/{espacio}/config # Conecta y ajusta Kommo Guarda el subdominio y el token de tu cuenta, y enciende o ajusta el chat automático, las notas de voz, las etapas y la sincronización. Solo cambia lo que mandas. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Kommo. ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | credenciales | { clave: valor } | no | access_token: el token de larga duración de tu integración privada de Kommo. Para OAuth, client_id y client_secret. Se fusiona clave a clave: ver notas. | subdomain | string | no | El subdominio de tu cuenta: tiendaaurora en tiendaaurora.kommo.com. | modo_canal | "custom" | "salesbot" | no | Por dónde llegan los mensajes: custom, el canal de chat de Bentho en tu cuenta; salesbot, un paso de tu Salesbot (para los canales oficiales de Kommo, como WhatsApp). Por defecto custom. Ver la guía. | auth_mode | "long_lived" | "oauth" | no | long_lived (por defecto): un token de larga duración de Kommo. oauth: con renovación. | token_expires_at | number | no | Con OAuth, cuándo caduca el token, en segundos desde 1970; se renueva solo. Al pegar un token de larga duración, manda 0. | bot_activo | boolean | no | El chat automático: Bentho contesta los mensajes que llegan por Kommo. Por defecto false. | chat_allowlist | string[] | no | Chats de prueba: si tiene ids, el bot solo contesta a esas conversaciones y calla en las demás. Vacía (por defecto), contesta a todas. Con el Salesbot y los webhooks del CRM, el id es el del lead de Kommo. | pausa_humano_ttl_s | integer | no | Segundos que el bot se calla en un chat después de que escribe una persona de tu equipo. Por defecto 3600. | debounce_s · debounce_max_s | number | no | Si el cliente escribe varios mensajes seguidos, se contestan juntos: espera debounce_s de silencio (10 por defecto), y nunca más de debounce_max_s desde el primero (30). 0 en debounce_s: uno por uno. | audio_activo | boolean | no | Transcribir las notas de voz y contestarlas como texto. Por defecto false. Pide el módulo Transcripción. | audio_idioma | string | no | El idioma de las notas de voz. Por defecto "es". | audio_duracion_max_s | integer | no | Por encima de esta duración no se transcribe: el bot le pide al cliente que lo escriba o lo resuma. Por defecto 120. | audio_max_por_turno | integer | no | Notas de voz que se transcriben, como mucho, en una misma respuesta. Por defecto 3. | adjuntos_activo | boolean | no | Las imágenes y PDF que manda el cliente (JPG, PNG, WEBP, HEIC o PDF) se adjuntan como comprobante de pago a su pedido en curso. Por defecto false. | adjuntos_tamano_max_mb | number | no | El tamaño máximo de un adjunto, en MB. Por defecto 10. Lo que pase se queda en Kommo. | add_message_texto_activo | boolean | no | Atender también el texto que llega por el webhook de mensaje entrante, además del Salesbot. Por defecto true: así no se pierde ningún mensaje. | salesbot_liberacion_inmediata | boolean | no | En modo salesbot: libera tu Salesbot al instante y entrega la respuesta con un bot aparte (salesbot_bot_entrega_id). Por defecto false. | salesbot_bot_entrega_id | integer | null | no | El id del Salesbot de Kommo que muestra la respuesta, con la liberación inmediata. | salesbot_paso_cierre | integer | null | no | El paso al que salta tu Salesbot para cerrarse. null: termina donde esté. | mapeo_estados | { estado: { pipeline_id, status_id } } | no | A qué etapa de Kommo va cada estado del cliente en Bentho: nuevo, contactado, ganado, cerrado y handoff (requiere humano). Los ids, de embudos. Se reemplaza entero. | responsable_handoff_id | integer | null | no | El id del usuario de Kommo al que se asigna el lead cuando el cliente pide una persona. | sync_leads_activo | boolean | no | Mover las tarjetas de Kommo cuando cambia el estado del cliente en Bentho. Por defecto false. | amojo_id | string | null | no | El id de tu cuenta en el chat de Kommo. Lo rellena probar la conexión. | scope_id | string | null | no | En modo custom: el del canal de chat conectado a tu cuenta. | agent_ref_id | string | null | no | En modo custom: el usuario de Kommo que firma las respuestas, para que no parezcan del cliente. | name | string | no | Un nombre para la conexión. Por defecto, el id del espacio. ## RESPUESTA 200 La configuración ya cambiada, con todos los campos de GET config. Los que conviene mirar: | | CAMPO | TIPO | QUÉ ES | is_configured | boolean | true si hay subdominio y token de acceso: lo mínimo para hablar con Kommo. | credenciales_set | string[] | Los nombres de las credenciales guardadas (access_token, client_id…). Sus valores no salen nunca. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Kommo | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 422 | Un campo de otro tipo, o un valor fuera de su lista (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - credenciales se fusiona clave a clave: una clave nueva se añade, una que ya estaba se reemplaza y un valor vacío no borra nada. Los valores no se devuelven nunca. - Al pegar un token de larga duración, manda también auth_mode: "long_lived" y token_expires_at: 0: así no se intenta renovar con datos de OAuth viejos. - mapeo_estados se reemplaza entero: manda el objeto completo. Para dejar un estado sin etapa, mándalo sin esa clave. - Después, prueba la conexión. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X PATCH https://bentho.ai/api/mod/kommo/mi-espacio/config \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "subdomain": "tiendaaurora", "auth_mode": "long_lived", "token_expires_at": 0, "credenciales": { "access_token": "TU_TOKEN_DE_LARGA_DURACION" } }' ``` ■ 200 OKejemplo, con los campos del código ``` { "name": "mi-espacio", "subdomain": "tiendaaurora", "is_configured": true, "credenciales_set": ["access_token"], "auth_mode": "long_lived", "token_expires_at": 0, "bot_activo": false, … } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Kommo · Probar conexión https://dev.bentho.ai/api/kommo/probar/ POST/api/mod/kommo/{espacio}/config/test # Prueba la conexión Llama a tu cuenta de Kommo con el subdominio y el token guardados y te dice si responde. Si responde, guarda el id de la cuenta de chat (amojo_id). - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Kommo. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | ok | boolean | true si Kommo aceptó la conexión. | amojo_id | string | Con ok: el id de tu cuenta en el chat de Kommo. | error | string | Sin ok: por qué falló. | status | integer | Sin ok, si Kommo llegó a contestar: su status (401 = token rechazado). ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Kommo | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Que falle no es un error HTTP: responde 200 con ok: false y error dice por qué (falta el subdominio o el token, Kommo rechazó el token o se pasó su límite de peticiones). CURLPYTHONJAVASCRIPT COPIAR ``` curl -X POST https://bentho.ai/api/mod/kommo/mi-espacio/config/test \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKderivado del código ``` { "ok": true, "amojo_id": "4b2f9c1e-8d3a-4e7b-9f10-2c5d6a7b8e91" } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Kommo · Embudos y etapas https://dev.bentho.ai/api/kommo/embudos/ GET/api/mod/kommo/{espacio}/pipelines # Los embudos y etapas de Kommo Los embudos de tu cuenta con sus etapas, tal como los da Kommo. De aquí salen los pipeline_id y status_id de mapeo_estados. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Kommo. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | ok | boolean | true si Kommo respondió. | pipelines | array | Los embudos, con la forma de Kommo. Vacío si ok es false. | pipelines[].id · name | integer · string | El embudo. | pipelines[]._embedded.statuses | [{ id, name, … }] | Sus etapas: id es el status_id. | error | string | Sin ok: por qué. | status | integer | Sin ok, si Kommo llegó a contestar: su status. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Kommo | módulo no habilitado para este espacio. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Sin conectar, responde 200 con ok: false y pipelines vacío. - Las etapas 142 (ganada) y 143 (perdida) existen en todos los embudos de Kommo. Si no mapeas ganado y cerrado, van ahí, en el embudo de tus otros estados. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/kommo/mi-espacio/pipelines \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "ok": true, "pipelines": [ { "id": 7012345, "name": "Ventas", "is_main": true, "_embedded": { "statuses": [ { "id": 58210001, "name": "Contacto inicial", "pipeline_id": 7012345, … }, { "id": 58210002, "name": "En conversación", "pipeline_id": 7012345, … }, { "id": 58210003, "name": "Atender en persona", "pipeline_id": 7012345, … }, { "id": 142, "name": "Venta ganada", "pipeline_id": 7012345, … }, { "id": 143, "name": "Venta perdida", "pipeline_id": 7012345, … } ] }, … } ] } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Kommo · Sincronizar etapas https://dev.bentho.ai/api/kommo/etapas-sincronizar/ POST/api/mod/kommo/{espacio}/sync/reconcile # Sincroniza las etapas ahora Revisa qué clientes cambiaron de estado en Bentho desde la última vez y prepara el movimiento de sus tarjetas en Kommo. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Kommo. ## RESPUESTA 200 Uno de estos campos, según lo que pasó: | | CAMPO | TIPO | QUÉ ES | enqueued | integer | Movimientos preparados. Salen como envíos mover_etapa. | skipped | "sync_off" | "sin_leads_mapeados" | No hizo nada: la sincronización está apagada (sync_leads_activo), o aún no hay clientes que vinieran por Kommo. | error | string | No pudo leer los estados: reintenta. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Kommo | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Solo se sincronizan los clientes que llegaron por el chat de Kommo. Sigue los movimientos en envíos. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X POST https://bentho.ai/api/mod/kommo/mi-espacio/sync/reconcile \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKderivado del código ``` { "enqueued": 3 } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Kommo · Estado de envíos https://dev.bentho.ai/api/kommo/envios-estado/ GET/api/mod/kommo/{espacio}/sync/status # El estado de los envíos Cuántos envíos a Kommo hay en cada estado: respuestas al chat, movimientos de etapa y comprobantes. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Kommo. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | outbox.pending | integer | En cola. | outbox.processing | integer | En proceso. | outbox.done | integer | Sincronizados. | outbox.failed | integer | Con reintento. | outbox.dead | integer | Descartados: agotaron sus intentos. Míralos en envíos. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Kommo | módulo no habilitado para este espacio. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Cada cifra cuenta hasta 10 000. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/kommo/mi-espacio/sync/status \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKderivado del código ``` { "outbox": { "pending": 2, "processing": 0, "failed": 1, "dead": 0, "done": 1284 } } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Kommo · Mensajes recibidos https://dev.bentho.ai/api/kommo/entradas/ GET/api/mod/kommo/{espacio}/inbound/ledger # Lo que entró por el chat y qué se hizo Cuenta cada mensaje que llegó desde Kommo y qué se decidió con él: si el agente lo contestó, si se ignoró a propósito o si se perdió. La cifra que importa es perdidos: tiene que ser 0. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Kommo. | horas | float, query | no | Las últimas N horas. Por defecto 24. | detalle | bool, query | no | true suma la lista de los perdidos, para buscarlos en Kommo. | limit | int, query | no | Cuántos perdidos trae detalle, el más reciente primero. Por defecto 200. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | horas | number | La ventana que pediste. | desde | number | Su inicio, en segundos desde 1970. | total | integer | Mensajes que entraron. | atendidos | integer | Los que se volvieron un turno. | perdidos | integer | Mensajes con contenido que no se pudieron atender: sin_via, sin_conversation_id o sin_lead. Tiene que ser 0. | no_atendidos_por_politica | integer | Los que no se contestaron por una decisión tuya: chat fuera de la lista de pruebas, bot apagado, una persona al mando o pausa. | no_atendidos | integer | total − atendidos: perdidos, por política, duplicados y ecos. | por_decision | { [decision]: integer } | El recuento por decisión. Lista abajo. | por_via | { [via]: integer } | Por dónde entró: salesbot, amojo (el chat) o add_message (un adjunto). | ultimo_ts | number | null | El último mensaje anotado, de cualquier fecha. null: ninguno. | perdidos_detalle | array | Solo con detalle=true. Cada uno con id, ts, via, decision, atendido, conversation_id, session_id, msgid y outbox_id. Sin el texto del mensaje. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Kommo | módulo no habilitado para este espacio. | 422 | Un campo de otro tipo, o un valor fuera de su lista (detail es una lista) | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Las decisiones: turno (contestado); sin_via, sin_conversation_id y sin_lead (perdidos); fuera_de_allowlist, bot_off, humano_activo y pausa_manager (por política); duplicado_msgid, duplicado_contenido, duplicado_cruce_via, eco_bot y sin_contenido (el mismo mensaje otra vez, el eco de una respuesta del agente o un mensaje vacío). - Kommo recibe siempre un 200 al mandar un mensaje, se atienda o no: esta es la forma de ver lo que no llegó a turno. Con conversation_id lo encuentras en Kommo. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/kommo/mi-espacio/inbound/ledger?horas=24&detalle=true \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "horas": 24, "desde": 1789913600.0, "total": 58, "atendidos": 49, "perdidos": 0, "no_atendidos_por_politica": 3, "no_atendidos": 9, "por_decision": { "turno": 49, "duplicado_msgid": 6, "humano_activo": 3 }, "por_via": { "amojo": 52, "add_message": 6 }, "ultimo_ts": 1790000000.4, "perdidos_detalle": [] } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Kommo · Envíos https://dev.bentho.ai/api/kommo/envios/ GET/api/mod/kommo/{espacio}/outbox/ # Los envíos a Kommo Los envíos uno a uno, el más reciente primero. Con estado=dead, los que fallaron y se descartaron. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Kommo. | estado | string, query | no | pending, processing, failed, dead o done. Sin él, todos. | limit | int, query | no | Cuántos. Por defecto 200. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | entries | array | El más reciente primero. Cada uno, con los campos de un envío. ## UN ENVÍO | | CAMPO | TIPO | QUÉ ES | id | string | 32 caracteres hexadecimales: para reintentarlo. | tipo | string | turno (contestar un mensaje del chat), enviar_mensaje, mover_etapa, crear_lead o adjunto (un comprobante). | estado | string | Ver estados. | intentos | integer | Las veces que se intentó. | max_intentos | integer | El tope: 5. | motivo_error | string | null | Por qué falló la última vez. null si no falló. | next_attempt_at | number | null | Cuándo se vuelve a intentar, en segundos desde 1970. null si no queda nada. | payload_json · resultado_json | string | null | Lo que se envía y lo que salió, como texto JSON. Llevan mensajes de tus clientes; su forma interna puede cambiar. | created_at · updated_at | number | Segundos desde 1970, con decimales. ## ESTADOS DE UN ENVÍO | | CAMPO | TIPO | QUÉ ES | pending | en cola | Esperando su turno. | processing | en proceso | Se está enviando ahora. | done | sincronizado | Hecho. | failed | con reintento | Falló, pero se volverá a intentar solo (next_attempt_at). | dead | descartado | Agotó sus 5 intentos. Mira motivo_error y reinténtalo cuando lo arregles. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Kommo | módulo no habilitado para este espacio. | 422 | limit no es un número | — Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - La ruta acaba en barra: …/outbox/. Sin ella no llega. - Cada envío trae más campos de uso interno: no te apoyes en ellos. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/kommo/mi-espacio/outbox/?estado=dead&limit=50 \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "entries": [ { "id": "5e1c9a7b3d2f4e8a9b6c1d0e7f3a2b45", "tipo": "mover_etapa", "estado": "dead", "intentos": 5, "max_intentos": 5, "motivo_error": "Kommo rechazó PATCH /api/v4/leads/31415926 (400)", "next_attempt_at": null, "payload_json": "{\"session_id\": \"kommo:31415926\", \"estado\": \"ganado\"}", "resultado_json": null, "created_at": 1790000000.12, "updated_at": 1790000310.4, … } ] } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Kommo · Reintentar envío https://dev.bentho.ai/api/kommo/envio-reintentar/ POST/api/mod/kommo/{espacio}/outbox/{envio}/retry # Reintenta un envío Vuelve a poner en cola un envío descartado o con reintento, para que salga en la siguiente pasada. Arregla antes lo que lo hizo fallar. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Kommo. | envio | string | SÍ | El id del envío: el de GET …/outbox/ (32 caracteres hexadecimales). ## RESPUESTA 200 El envío, con los campos de un envío: | | CAMPO | TIPO | QUÉ ES | id | string | El envío. | estado | "pending" | De vuelta en la cola. Sale en la siguiente pasada. | motivo_error | null | Se limpia al reintentar. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Kommo | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 404 | No hay un envío con ese id en el espacio | entrada de outbox no encontrada. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Un envío ya hecho (done) no se repite: te lo devuelve tal cual. - Para que salga ya, procesa los envíos. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X POST https://bentho.ai/api/mod/kommo/mi-espacio/outbox/5e1c9a7b3d2f4e8a9b6c1d0e7f3a2b45/retry \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "id": "5e1c9a7b3d2f4e8a9b6c1d0e7f3a2b45", "tipo": "mover_etapa", "estado": "pending", "intentos": 5, "max_intentos": 5, "motivo_error": null, "next_attempt_at": 1790000400.0, "payload_json": "{\"session_id\": \"kommo:31415926\", \"estado\": \"ganado\"}", "resultado_json": null, "created_at": 1790000000.12, "updated_at": 1790000310.4, … } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Kommo · Procesar envíos https://dev.bentho.ai/api/kommo/envios-procesar/ POST/api/mod/kommo/{espacio}/sync/process # Procesa los envíos ahora Hace ya una pasada por los envíos que tocan: contesta los chats pendientes, mueve tarjetas y entrega comprobantes. Los envíos salen solos; esto los adelanta. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Kommo. | forzar | bool, query | no | true: no espera a que el cliente deje de escribir ni a los reintentos programados; lo procesa todo ya. Solo para pruebas. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | procesadas | integer | Envíos que tomó. | conversaciones | integer | De cuántos chats. | done | integer | Hechos. | failed | integer | Fallaron; se reintentan solos. | deferred | integer | Aplazados por un problema de conexión con Kommo (token, subdominio). | liberadas | integer | Devueltos a la cola para no adelantarse a uno anterior del mismo chat. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Kommo | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Dentro de un mismo chat, los envíos salen en orden: si uno falla, los siguientes de ese chat esperan. - Contestar un chat puede tardar decenas de segundos. Si la llamada se corta, los envíos siguen su curso: mira su estado en envíos. - Las respuestas del chat las escribe el Agente ventas y gastan créditos como sus conversaciones; esta ruta, por sí sola, no. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X POST https://bentho.ai/api/mod/kommo/mi-espacio/sync/process \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKderivado del código ``` { "procesadas": 3, "conversaciones": 2, "done": 3, "failed": 0, "deferred": 0, "liberadas": 0, … } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Kommo · Reiniciar chats https://dev.bentho.ai/api/kommo/chats-reiniciar/ POST/api/mod/kommo/{espacio}/chats/reset # Reinicia los chats de prueba Los chats de chat_allowlist vuelven a empezar de cero: se borran su carrito, sus datos de envío, su método de pago y su pedido abierto. Ningún otro chat cambia. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Kommo. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | reset · total | integer | Los chats reiniciados, de los que hay en la lista. | errores | integer | Los que fallaron. | chats | [{ conversation_id, ok, aviso?, error? }] | Cada chat: si se reinició, y si quedó algo sin borrar del todo (aviso). | cache_limpiada | boolean | Si se vació la caché de respuestas del espacio. Afecta a todos los chats, pero solo a su rapidez: se rellena sola. | cache_error | string | null | Si no se vació, por qué. | skipped | "allowlist_inactiva" | La lista de chats de prueba está vacía: no hizo nada. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Kommo | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Solo funciona con la lista de chats de prueba llena. Sin ella, el bot atiende a clientes de verdad y esto les borraría el pedido: responde skipped y no toca nada. - No borra la tarjeta en Kommo ni la pausa por una persona de tu equipo. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X POST https://bentho.ai/api/mod/kommo/mi-espacio/chats/reset \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKderivado del código ``` { "reset": 2, "total": 2, "chats": [ { "conversation_id": "31415926", "ok": true, … }, { "conversation_id": "27182818", "ok": true, … } ], "errores": 0, "cache_limpiada": true, "cache_error": null } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Kommo · Borrar conexión https://dev.bentho.ai/api/kommo/borrar/ DEL/api/mod/kommo/{espacio}/ # Desconecta Kommo y borra sus datos Borra la configuración (credenciales incluidas), la relación entre chats y tarjetas, los envíos y el registro de la conexión. El espacio queda como si nunca se hubiera conectado. No se puede deshacer. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Kommo. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | deleted | true | Hecho. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Kommo | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - La ruta es la raíz del módulo, con su barra final: /api/mod/kommo/{espacio}/. - No quita el módulo del espacio ni toca tu cuenta de Kommo: quita en Kommo los webhooks y el paso del Salesbot que apuntan a Bentho, o los mensajes seguirán llegando. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X DELETE https://bentho.ai/api/mod/kommo/mi-espacio/ \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKderivado del código ``` { "deleted": true, … } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Landings · Crear https://dev.bentho.ai/api/landings/crear/ POST/api/mod/landing/{espacio}/landings # Crea una landing Mandas el brief y la URL que recibe el formulario. Bentho escribe la landing, la prueba en un navegador y la deja lista para publicar. Contesta al momento con el id; el trabajo sigue en segundo plano. - PERMISOescribe - CRÉDITOSgasta créditos - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Landings. ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | prompt | string | SÍ | El brief: qué ofreces, a quién, con qué tono, qué secciones y qué datos (precios, fechas, testimonios). Hasta 24 000 caracteres. | submitUrl | string | SÍ | Adónde envía sus datos el formulario. HTTP o HTTPS, sin usuario ni contraseña. Qué le llega, en la guía. | titulo | string | no | Para reconocerla en la lista. Hasta 120 caracteres; sin él, el comienzo del brief. | pageId | string | no | Tu identificador de la página, hasta 200 caracteres. El formulario lo manda como page_id en cada envío. | brandBlock | string | no | Tu marca, en texto: nombre, colores, tipografías, tono, URL del logotipo. Hasta 20 000 caracteres. | formConfig | object | no | El formulario: campos, mensaje de éxito, botones y redirección. Su forma, en la guía. | chatHistory | [{ role, content }] | no | La conversación con tu usuario que llevó a esta petición, si la hay. Hasta 50 mensajes de 8000 caracteres. | funnelReferenceHtml | string | no | El HTML de otra página de tu embudo, para que esta siga su estilo. Hasta 300 000 caracteres. | pdf | { data, name? } | no | Un PDF con la información del producto, en base64 en data. Hasta 16 000 000 de caracteres (unos 12 MB de PDF). | revisarBrief | boolean | no | Por defecto true: antes de escribir, revisa si el brief pide datos que no da y te los pregunta (esperando_datos). Con false, no pregunta. ## RESPUESTA 202 | | CAMPO | TIPO | QUÉ ES | id | string | El id de la landing. | version | integer | El número de la versión que se encoló. | trabajo | string | El id de esa versión. Para seguirla te basta con id y version. | status | "pendiente" | Siempre pendiente al encolar. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Landings | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 402 | La cuenta agotó sus créditos del ciclo | La cuenta de este espacio agotó sus créditos de este ciclo. | 413 | El cuerpo pasa de 24 MB | Cuerpo demasiado grande | 422 | Falta prompt | prompt es obligatorio | 422 | submitUrl no es una URL HTTP/HTTPS, o lleva usuario o contraseña | submitUrl debe ser una URL HTTP/HTTPS sin credenciales | 422 | Un campo que no es de esta ruta | Campos no admitidos: … | 429 | El espacio tiene demasiadas landings generándose a la vez | Hay demasiadas landings en curso en este espacio; espera a que terminen. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Responde enseguida: el trabajo sigue en segundo plano y suele tardar unos minutos. Sigue la versión con GET landings/{landing}. - Mientras se prueba, el formulario no envía nada de verdad: a tu submitUrl no le llegan envíos de prueba. - Un formConfig que no cumple su forma no da 422: la versión termina en fallido y error dice por qué. - Los 422 del módulo traen el detail como texto ("prompt es obligatorio"), no como lista. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/landing/mi-espacio/landings \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "prompt": "Landing para el curso online «Excel para pymes»: 6 semanas, en directo los martes a las 19:00, 149 €. Público: dueños de pequeños negocios. Tono cercano. Formulario de preinscripción con nombre, email y teléfono.", "submitUrl": "https://example.com/api/leads", "titulo": "Excel para pymes" }' ``` ■ 202 ACCEPTEDejemplo, con los campos del código ``` { "id": "3f9c2a7e5b1d4c8fa6e0b2d9c4f1a7e3", "version": 1, "trabajo": "b81e6d0c47a94f2e9c3d5a1b7e2f8c60", "status": "pendiente" } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Landings · Lista https://dev.bentho.ai/api/landings/lista/ GET/api/mod/landing/{espacio}/landings # Las landings del espacio Las landings del espacio, la última que cambió primero, cada una con su última versión. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Landings. | limite | int, query | no | Cuántas, de 1 a 200. Por defecto 50. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | landings | array | La última que cambió, primero. Cada una con los campos de GET landings/{landing} salvo versiones, y además: | landings[].ultima | object | null | Su última versión, con la misma forma que versiones[] en GET landings/{landing}. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Landings | módulo no habilitado para este espacio. | 422 | limite no es un número | limite inválido Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/landing/mi-espacio/landings \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKderivado del código ``` { "landings": [ { "id": "3f9c2a7e5b1d4c8fa6e0b2d9c4f1a7e3", "titulo": "Excel para pymes", "submit_url": "https://example.com/api/leads", "page_id": null, "status": "aprobado", "version_actual": 2, "version_aprobada": 2, "version_con_html": 2, "creada": 1790000000.12, "actualizada": 1790000420.5, "ultima": { "version": 2, "tipo": "generar", "status": "aprobado", … } } ] } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Landings · Estado https://dev.bentho.ai/api/landings/estado/ GET/api/mod/landing/{espacio}/landings/{landing} # El estado de una landing La landing con todas sus versiones, la más reciente primero. Es la ruta que consultas mientras se escribe, hasta que su status termina o pide datos. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Landings. | landing | string | SÍ | El id de la landing: el que devolvió crearla (32 caracteres hexadecimales). ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | id · titulo | string | El id y el título. | submit_url · page_id | string · string | null | Los que mandaste al crearla. | status | string | El estado de la última versión. | version_actual | integer | La última versión, termine como termine. | version_aprobada | integer | null | La última que pasó las dos revisiones. null si ninguna. | version_con_html | integer | null | La última que tiene HTML, aprobada o no. | creada · actualizada | number | Segundos desde 1970, con decimales. | versiones | array | Todas, la más reciente primero. | versiones[].version | integer | 1, 2, 3… | versiones[].tipo | "generar" | "refinar" | "validar" | "restaurar" | Qué la creó. | versiones[].con_html | boolean | Si tiene HTML: se puede descargar y restaurar. | versiones[].restaurada_de | integer | null | Si es una restauración, la versión de la que copia. null en las demás. | versiones[].status | string | Su estado: ver estados. | versiones[].etapa | string | null | Qué está haciendo ahora, para enseñárselo a tu usuario. Texto libre: no lo compares. | versiones[].faltantes | array | null | Solo en esperando_datos: hasta 5 datos que el brief pide y no da, cada uno con clave, seccion, pregunta y ejemplo (el formato de una buena respuesta, con huecos entre corchetes: nunca un dato real). Respóndelos con completar. | versiones[].calidad | object | null | El certificado de las revisiones, cuando la versión termina: ver calidad. | versiones[].error | string | null | Por qué falló o se canceló, en una frase para tu usuario. | versiones[].creada | number | Segundos desde 1970, con decimales. | versiones[].iniciada · terminada | number | null | Igual; null mientras no ha empezado o no ha terminado. ## ESTADOS DE UNA VERSIÓN | | CAMPO | TIPO | QUÉ ES | pendiente | en curso | En la cola, esperando su turno. | generando | en curso | Escribiendo, probando o reparando. Mira etapa. | aplazado | en curso | Esperando capacidad. Se reanuda sola: no hagas nada. | esperando_datos | espera tu respuesta | El brief pide datos que no da. Responde con completar; no gasta mientras espera. | aprobado | terminada | Pasó las dos revisiones: lista para publicar. | revision | terminada | Tiene HTML, pero no pasó alguna revisión: calidad.motivos dice cuál. Refínala o revísala tú. | fallido | terminada | No se pudo terminar: mira error. | cancelado | terminada | La cancelaste (o borraste la landing). ## CALIDAD | | CAMPO | TIPO | QUÉ ES | status | "approved" | "needs_review" | "failed" | approved solo si pasó las dos revisiones. | motivos | string[] | Por qué no se aprobó. Vacío si se aprobó. | emitido | string | null | Cuándo, en ISO 8601. | html_sha256 | string | null | El SHA-256 del HTML que certifica. Vale para ese HTML exacto: si lo cambias, mándalo a validar. | funcional | object | null | La revisión de funcionamiento: status (passed o needs_review), brief_cumplido, los escenarios que probó y sus hallazgos. | visual | object | null | La revisión de diseño, en escritorio y en móvil: status, el umbral y una nota de 0 a 100 en cada una de sus dimensiones (hierarchy, composition, typography, spacing, consistency, legibility, imagery, mobile). Para aprobar, todas llegan al umbral. | ….hallazgos[] | { severidad, descripcion, correccion } | Dentro de funcional y de visual: lo que encontró cada revisión. severidad es blocker, major o minor; solo los minor dejan aprobar. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Landings | módulo no habilitado para este espacio. | 404 | No hay una landing con ese id en el espacio (o se borró) | La landing no existe Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Consulta cada 10 a 15 s. Para cuando status sea aprobado, revision, fallido o cancelado, o cuando sea esperando_datos: ahí espera tu respuesta. - Una versión en aplazado sigue sola. Puede tardar más, pero no hace falta que hagas nada. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/landing/mi-espacio/landings/3f9c2a7e5b1d4c8fa6e0b2d9c4f1a7e3 \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "id": "3f9c2a7e5b1d4c8fa6e0b2d9c4f1a7e3", "titulo": "Excel para pymes", "submit_url": "https://example.com/api/leads", "page_id": null, "status": "esperando_datos", "version_actual": 1, "version_aprobada": null, "version_con_html": null, "creada": 1790000000.12, "actualizada": 1790000038.4, "versiones": [ { "version": 1, "tipo": "generar", "status": "esperando_datos", "etapa": null, "con_html": false, "restaurada_de": null, "faltantes": [ { "clave": "fecha_inicio", "seccion": "Hero", "pregunta": "¿Qué día empieza el curso?", "ejemplo": "Empieza el [día] [fecha]." }, { "clave": "testimonios", "seccion": "Opiniones", "pregunta": "¿Tienes opiniones reales de alumnos, con su nombre?", "ejemplo": "«[opinión]», [nombre], [negocio]." } ], "calidad": null, "error": null, "creada": 1790000000.12, "iniciada": 1790000001.3, "terminada": null } ] } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Landings · HTML https://dev.bentho.ai/api/landings/html/ GET/api/mod/landing/{espacio}/landings/{landing}/html # El HTML de una landing El HTML listo para publicar, con su certificado de calidad. Sin version, la última aprobada; si ninguna lo está, la última que tenga HTML. - PERMISOlee (vale cualquier clave) - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Landings. | landing | string | SÍ | El id de la landing: el que devolvió crearla (32 caracteres hexadecimales). | version | int, query | no | Una versión concreta. ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | version | integer | La versión que te da. | html | string | Un documento completo, en un solo archivo y con su formulario conectado. Publícalo tal cual. | calidad | object | null | Su certificado (abajo). Publica lo que tenga status: "approved"; lo demás, revísalo antes. ## CALIDAD | | CAMPO | TIPO | QUÉ ES | status | "approved" | "needs_review" | "failed" | approved solo si pasó las dos revisiones. | motivos | string[] | Por qué no se aprobó. Vacío si se aprobó. | emitido | string | null | Cuándo, en ISO 8601. | html_sha256 | string | null | El SHA-256 del HTML que certifica. Vale para ese HTML exacto: si lo cambias, mándalo a validar. | funcional | object | null | La revisión de funcionamiento: status (passed o needs_review), brief_cumplido, los escenarios que probó y sus hallazgos. | visual | object | null | La revisión de diseño, en escritorio y en móvil: status, el umbral y una nota de 0 a 100 en cada una de sus dimensiones (hierarchy, composition, typography, spacing, consistency, legibility, imagery, mobile). Para aprobar, todas llegan al umbral. | ….hallazgos[] | { severidad, descripcion, correccion } | Dentro de funcional y de visual: lo que encontró cada revisión. severidad es blocker, major o minor; solo los minor dejan aprobar. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Landings | módulo no habilitado para este espacio. | 404 | No hay una landing con ese id en el espacio (o se borró) | La landing no existe | 404 | Esa versión no existe | La versión no existe | 409 | Sin version: ninguna versión tiene HTML todavía | La landing todavía no tiene HTML | 409 | Esa versión no tiene HTML (en curso, fallida o cancelada) | La versión todavía no tiene HTML | 422 | version no es un número | version inválido Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Un 409 aquí no es un error de tu lado: la versión aún no termina. Espera a que version_con_html deje de ser null en el estado. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/landing/mi-espacio/landings/3f9c2a7e5b1d4c8fa6e0b2d9c4f1a7e3/html \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKejemplo, con los campos del código ``` { "version": 2, "html": "…", "calidad": { "status": "approved", "motivos": [], "emitido": "2026-10-02T15:04:05.000Z", "html_sha256": "9b1c…", "funcional": { "status": "passed", "brief_cumplido": true, "escenarios": [ … ], "hallazgos": [] }, "visual": { "status": "passed", "umbral": 75, "dimensiones": { "hierarchy": 88, … }, "hallazgos": [] } } } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Landings · Completar datos https://dev.bentho.ai/api/landings/completar/ POST/api/mod/landing/{espacio}/landings/{landing}/completar # Responde a lo que falta Cuando una versión queda en esperando_datos, mandas aquí lo que tu usuario responda a sus faltantes y la versión sigue. - PERMISOescribe - CRÉDITOSgasta créditos - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Landings. | landing | string | SÍ | El id de la landing: el que devolvió crearla (32 caracteres hexadecimales). ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | respuestas | { clave: texto } | no | Una respuesta por clave de faltantes. Hasta 4000 caracteres cada una. Se añaden al brief. | omitir | string[] | no | Las claves que tu usuario no tiene o no quiere dar. Esa parte se escribe sin ese dato, y sin decirle al visitante que falta. ## RESPUESTA 202 | | CAMPO | TIPO | QUÉ ES | id · version | string · integer | La landing y la versión que sigue. | status | "pendiente" | Vuelve a la cola. | aportados | string[] | Las claves que respondiste con un dato. | declinados | string[] | Las que omitiste, dejaste sin responder o respondiste sin dato («no tengo», «sin datos», «N/A», «-»…). ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Landings | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 402 | La cuenta agotó sus créditos del ciclo | La cuenta de este espacio agotó sus créditos de este ciclo. | 404 | No hay una landing con ese id en el espacio (o se borró) | La landing no existe | 409 | Ninguna versión está en esperando_datos | La landing no está esperando datos. | 422 | Una clave que no está en faltantes | Claves desconocidas: … | 422 | respuestas no es un objeto de textos, o uno pasa de 4000 caracteres | respuestas debe ser un objeto {clave: texto} de hasta 4000 caracteres por respuesta Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Lo que no respondas ni omitas cuenta como omitido: un cuerpo vacío sigue sin ninguno de esos datos. Una respuesta que dice que no hay dato también: se escribe esa parte sin él. - Responde enseguida: el trabajo sigue en segundo plano y suele tardar unos minutos. Sigue la versión con GET landings/{landing}. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/landing/mi-espacio/landings/3f9c2a7e5b1d4c8fa6e0b2d9c4f1a7e3/completar \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "respuestas": { "fecha_inicio": "Empieza el martes 3 de noviembre de 2026." }, "omitir": [ "testimonios" ] }' ``` ■ 202 ACCEPTEDejemplo, con los campos del código ``` { "id": "3f9c2a7e5b1d4c8fa6e0b2d9c4f1a7e3", "version": 1, "status": "pendiente", "aportados": ["fecha_inicio"], "declinados": ["testimonios"] } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Landings · Refinar https://dev.bentho.ai/api/landings/refinar/ POST/api/mod/landing/{espacio}/landings/{landing}/refinar # Pide cambios a una landing Crea una versión nueva sobre la última que tiene HTML, con la instrucción que le des. Las anteriores se conservan: descárgalas con ?version= o vuelve a una con restaurar. - PERMISOescribe - CRÉDITOSgasta créditos - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Landings. | landing | string | SÍ | El id de la landing: el que devolvió crearla (32 caracteres hexadecimales). ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | prompt | string | SÍ | Qué cambiar, en texto libre. Hasta 24 000 caracteres. | brandBlock | string | no | Tu marca, en texto: nombre, colores, tipografías, tono, URL del logotipo. Hasta 20 000 caracteres. | formConfig | object | no | Sin él, se usa el que la landing ya tiene guardado. Uno nuevo lo sustituye para las versiones siguientes. Su forma, en la guía. | chatHistory | [{ role, content }] | no | La conversación con tu usuario que llevó a esta petición, si la hay. Hasta 50 mensajes de 8000 caracteres. ## RESPUESTA 202 | | CAMPO | TIPO | QUÉ ES | id | string | El id de la landing. | version | integer | El número de la versión que se encoló. | trabajo | string | El id de esa versión. Para seguirla te basta con id y version. | status | "pendiente" | Siempre pendiente al encolar. | base | integer | La versión sobre la que trabaja. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Landings | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 402 | La cuenta agotó sus créditos del ciclo | La cuenta de este espacio agotó sus créditos de este ciclo. | 404 | No hay una landing con ese id en el espacio (o se borró) | La landing no existe | 409 | La landing ya tiene una versión en curso: espera o cancélala | La landing ya tiene una versión en curso. | 409 | Ninguna versión tiene HTML todavía | La landing todavía no tiene una versión sobre la que trabajar. | 422 | Falta prompt | prompt es obligatorio | 429 | El espacio tiene demasiadas landings generándose a la vez | Hay demasiadas landings en curso en este espacio; espera a que terminen. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Retoca sin reescribir: cambia lo que pides y deja el resto. La versión nueva pasa otra vez por las dos revisiones. - Refinar no revisa el brief: no pasa por esperando_datos. Si el cambio trae datos nuevos, ponlos en el prompt. - Responde enseguida: el trabajo sigue en segundo plano y suele tardar unos minutos. Sigue la versión con GET landings/{landing}. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/landing/mi-espacio/landings/3f9c2a7e5b1d4c8fa6e0b2d9c4f1a7e3/refinar \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "prompt": "Cambia el botón principal a «Reserva tu plaza» y pon el precio junto al formulario." }' ``` ■ 202 ACCEPTEDejemplo, con los campos del código ``` { "id": "3f9c2a7e5b1d4c8fa6e0b2d9c4f1a7e3", "version": 3, "trabajo": "5d2c8e1f9a0b4c7d8e6f1a2b3c4d5e6f", "status": "pendiente", "base": 2 } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Landings · Validar https://dev.bentho.ai/api/landings/validar/ POST/api/mod/landing/{espacio}/landings/{landing}/validar # Valida un HTML editado a mano Si retocaste la landing por tu cuenta, mándala aquí: Bentho la vuelve a probar y repara lo que falle sin cambiar su contenido. Sin html, valida la última versión. - PERMISOescribe - CRÉDITOSgasta créditos - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Landings. | landing | string | SÍ | El id de la landing: el que devolvió crearla (32 caracteres hexadecimales). ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | html | string | no | El documento completo, de a . Hasta 300 000 caracteres. Parte del html de GET …/html. | formConfig | object | no | Sin él, se usa el que la landing ya tiene guardado. Uno nuevo lo sustituye para las versiones siguientes. Su forma, en la guía. ## RESPUESTA 202 | | CAMPO | TIPO | QUÉ ES | id | string | El id de la landing. | version | integer | El número de la versión que se encoló. | trabajo | string | El id de esa versión. Para seguirla te basta con id y version. | status | "pendiente" | Siempre pendiente al encolar. | base | integer | La última versión con HTML. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Landings | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 402 | La cuenta agotó sus créditos del ciclo | La cuenta de este espacio agotó sus créditos de este ciclo. | 404 | No hay una landing con ese id en el espacio (o se borró) | La landing no existe | 409 | La landing ya tiene una versión en curso: espera o cancélala | La landing ya tiene una versión en curso. | 409 | Ninguna versión tiene HTML todavía | La landing todavía no tiene una versión sobre la que trabajar. | 422 | html no empieza por | html debe ser un documento completo (…) | 429 | El espacio tiene demasiadas landings generándose a la vez | Hay demasiadas landings en curso en este espacio; espera a que terminen. Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Responde enseguida: el trabajo sigue en segundo plano y suele tardar unos minutos. Sigue la versión con GET landings/{landing}. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/landing/mi-espacio/landings/3f9c2a7e5b1d4c8fa6e0b2d9c4f1a7e3/validar \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "html": "…" }' ``` ■ 202 ACCEPTEDejemplo, con los campos del código ``` { "id": "3f9c2a7e5b1d4c8fa6e0b2d9c4f1a7e3", "version": 4, "trabajo": "9e8d7c6b5a4f3e2d1c0b9a8f7e6d5c4b", "status": "pendiente", "base": 3 } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Landings · Cancelar https://dev.bentho.ai/api/landings/cancelar/ POST/api/mod/landing/{espacio}/landings/{landing}/cancelar # Cancela la versión en curso Para la versión que se está escribiendo o que espera datos. Las versiones terminadas no cambian. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Landings. | landing | string | SÍ | El id de la landing: el que devolvió crearla (32 caracteres hexadecimales). ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | id | string | La landing. | status | "cancelado" | "cancelando" | cancelado: ya está parada (o no había nada en curso). cancelando: se para al acabar el paso en curso; consulta el estado. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 403 | El espacio no tiene el módulo Landings | módulo no habilitado para este espacio. | 404 | No hay una landing con ese id en el espacio (o se borró) | La landing no existe Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - No gasta créditos y funciona aunque la bolsa esté agotada. Lo que la versión ya gastó no se devuelve. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X POST https://bentho.ai/api/mod/landing/mi-espacio/landings/3f9c2a7e5b1d4c8fa6e0b2d9c4f1a7e3/cancelar \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKderivado del código ``` { "id": "3f9c2a7e5b1d4c8fa6e0b2d9c4f1a7e3", "status": "cancelando" } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Landings · Restaurar https://dev.bentho.ai/api/landings/restaurar/ POST/api/mod/landing/{espacio}/landings/{landing}/restaurar # Vuelve a una versión anterior Crea una versión nueva, ya terminada, con el HTML y el certificado de la que elijas. Pasa a ser la última: la que se entrega y la base del siguiente refinado. No se borra nada. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Landings. | landing | string | SÍ | El id de la landing: el que devolvió crearla (32 caracteres hexadecimales). ## CUERPO · JSON | | CAMPO | TIPO | REQUERIDO | QUÉ ES | version | integer | SÍ | La versión a la que vuelves: una con con_html que no sea la última. ## RESPUESTA 201 | | CAMPO | TIPO | QUÉ ES | id | string | La landing. | version | integer | El número de la versión nueva. | status | "aprobado" | "revision" | aprobado si la versión de origen lo estaba; si no, revision. | restaurada_de | integer | La versión que copió. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | El espacio no tiene el módulo Landings | módulo no habilitado para este espacio. | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 404 | No hay una landing con ese id en el espacio (o se borró) | La landing no existe | 404 | Esa versión no existe | La versión no existe | 409 | La landing ya tiene una versión en curso: espera o cancélala | La landing ya tiene una versión en curso. | 409 | Esa versión no tiene HTML (en curso, fallida o cancelada) | Esa versión no tiene una landing que restaurar. | 409 | Pides la versión que ya es la última | Esa ya es la versión actual. | 422 | version no es un entero mayor que 0 | version debe ser un número de versión | 422 | Mandas otro campo | Campos no admitidos: … Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - No gasta créditos y funciona aunque la bolsa esté agotada: copia, no genera. Responde con la versión ya terminada; no hace falta consultar el estado. - El certificado sigue valiendo porque el HTML es el mismo, byte a byte: su html_sha256 no cambia. CURLPYTHONJAVASCRIPT COPIAR ``` curl https://bentho.ai/api/mod/landing/mi-espacio/landings/3f9c2a7e5b1d4c8fa6e0b2d9c4f1a7e3/restaurar \ -H "Authorization: Bearer $BENTHO_KEY" \ -H "Content-Type: application/json" \ -d '{ "version": 2 }' ``` ■ 201 CREATEDejemplo, con los campos del código ``` { "id": "3f9c2a7e5b1d4c8fa6e0b2d9c4f1a7e3", "version": 5, "status": "aprobado", "restaurada_de": 2 } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # Landings · Borrar https://dev.bentho.ai/api/landings/borrar/ DEL/api/mod/landing/{espacio}/landings/{landing} # Borra una landing Borra el brief y el HTML de todas sus versiones, y cancela lo que estuviera en curso. No se puede deshacer. - PERMISOescribe - CRÉDITOSno gasta - AUTENTICACIÓNBearer bth_… ## PARÁMETROS | | CAMPO | TIPO | REQUERIDO | QUÉ ES | espacio | string | SÍ | El id del espacio (sale en GET /api/companies). Tiene que tener el módulo Landings. | landing | string | SÍ | El id de la landing: el que devolvió crearla (32 caracteres hexadecimales). ## RESPUESTA 200 | | CAMPO | TIPO | QUÉ ES | id | string | La landing borrada. | status | "deleted" | Hecho. ## ERRORES DE ESTA RUTA · SIEMPRE {"detail": …} | | STATUS | CUÁNDO | DETAIL LITERAL | 403 | La clave es de solo lectura | Tu cuenta es de solo lectura en este espacio. | 403 | El espacio no tiene el módulo Landings | módulo no habilitado para este espacio. | 404 | No hay una landing con ese id en el espacio (o se borró) | La landing no existe Y los de cualquier ruta —clave inválida, cuenta no al día, IP, límites—, en Errores, límites y créditos. ## CONVIENE SABER - Lo gastado sigue contando en tu bolsa: borrar no devuelve créditos. CURLPYTHONJAVASCRIPT COPIAR ``` curl -X DELETE https://bentho.ai/api/mod/landing/mi-espacio/landings/3f9c2a7e5b1d4c8fa6e0b2d9c4f1a7e3 \ -H "Authorization: Bearer $BENTHO_KEY" ``` ■ 200 OKderivado del código ``` { "id": "3f9c2a7e5b1d4c8fa6e0b2d9c4f1a7e3", "status": "deleted" } ``` PARA AGENTESTodo esto en texto: /llms-full.txt y la skill bentho-api. --- # ADK https://dev.bentho.ai/adk/ ADK · PRONTO # Agent Development Kit Estamos preparando el ADK de Bentho: el kit para construir agentes que usan tus espacios como fuente, en Python y JavaScript, sin escribir a mano las cabeceras, los reintentos ni el manejo de eventos. Todavía no está publicado. No hace falta esperar. La API es HTTP y JSON: cualquier lenguaje que haga una petición sirve hoy. Los ejemplos de cada ruta ya vienen en curl, Python y JavaScript. ## Python Mientras llega el paquete, basta requests (o httpx si trabajas con asyncio): PYTHONCOPIAR ``` import os, requests BENTHO = "https://bentho.ai/api" CABECERAS = {"Authorization": f"Bearer {os.environ['BENTHO_KEY']}"} def preguntar(espacio: str, pregunta: str, sesion: str | None = None) -> dict: r = requests.post( f"{BENTHO}/rag/{espacio}/conversations", headers=CABECERAS, json={"question": pregunta, "session_id": sesion}, timeout=60, ) r.raise_for_status() return r.json() ``` ## JavaScript Con fetch, en Node 18+ o en el navegador de tu backend. No pongas la clave en código que llegue al navegador de tus usuarios. JAVASCRIPTCOPIAR ``` const BENTHO = "https://bentho.ai/api"; export async function preguntar(espacio, pregunta, sesion) { const r = await fetch(`${BENTHO}/rag/${espacio}/conversations`, { method: "POST", headers: { Authorization: `Bearer ${process.env.BENTHO_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ question: pregunta, session_id: sesion }), signal: AbortSignal.timeout(60_000), }); if (!r.ok) throw new Error(`Bentho respondió ${r.status}`); return r.json(); } ``` ## Mientras tanto, HTTP Empieza por el quickstart y ten a mano errores, límites y créditos: son las dos páginas que un cliente propio necesita para ser robusto. Si tu agente ya lee skills, dale la skill bentho-api: le explica la API entera. --- # Skills https://dev.bentho.ai/skills/ SKILLS # Enséñale Bentho a tu agente Si un agente escribe tu integración o llama a la API por ti, dale el contexto correcto en vez de dejar que lo adivine. Aquí tienes dos formas: una skill lista para instalar y el texto de toda la documentación en formato para modelos. ## Skill bentho-api Un SKILL.md con lo que un agente necesita para usar la API sin inventar nada: cómo autenticarse, qué ruta usar para cada tarea, qué significan status_code, confidence y warning, y cómo reaccionar a cada error (cuándo reintentar y cuándo parar). Trae además una referencia por parte de la API (references/nucleo.md, references/agente.md, references/productos.md…) con cada ruta entera: cuerpo, respuesta, errores literales y notas. Salen de estas mismas páginas, así que dicen lo mismo que la documentación. El agente lee solo la del módulo que va a usar. Ver SKILL.md · índice de la skill ## llms.txt Para agentes que leen documentación por su cuenta: - /llms.txt: el índice de páginas, con una línea por página. - /llms-full.txt: todo el contenido en texto plano, en un solo archivo. ## Instálala en tu agente ### Con el gestor de skills Desde la raíz de tu repositorio (necesitas Node). Instala la skill con sus referencias en los agentes que tengas (Claude Code, Cursor y otros): BASHCOPIAR ``` npx skills add https://dev.bentho.ai ``` También está en GitHub, en BenthoRAG/skills, un espejo de esta página que se pone al día cada día: npx skills add BenthoRAG/skills. Con -g queda para todos tus proyectos. Para ponerla al día cuando cambie la API: npx skills update. ### A mano Copia la carpeta /skills/bentho-api/ en la de skills de tu agente (en Claude Code, .claude/skills/bentho-api/). Los archivos son los que lista el índice; como mínimo, SKILL.md: BASHCOPIAR ``` mkdir -p .claude/skills/bentho-api curl -fsSL https://dev.bentho.ai/skills/bentho-api/SKILL.md \ -o .claude/skills/bentho-api/SKILL.md ``` El agente la usa cuando la tarea tiene que ver con Bentho. Necesita la clave en una variable de entorno (BENTHO_KEY), nunca pegada en el código. ### Otros agentes Pásale la URL de /llms.txt o el contenido de SKILL.md como instrucciones del sistema. Dale la clave con el menor permiso que sirva. Si el agente solo pregunta y lee, usa una clave lee: no podrá subir ni borrar documentos ni cambiar la configuración. --- # Cambios https://dev.bentho.ai/cambios/ CAMBIOS # Qué cambió en la API, y cuándo Cada cambio que ve quien integra, del más reciente al más antiguo. La regla de v1 es simple: se agregan campos, nunca se quitan ni se renombran. ## Comprueba tu clave, el estado de cada módulo y más consumo NUEVO · 2026-11-02 GET /profile dice de quién es la clave, a qué espacios llega y si puede escribir. GET /api/companies/{espacio}/semaforo da un color por módulo (verde, amarillo, rojo o gris) con el motivo en palabras: si no responde, si se acabaron los créditos o si la cuenta no está al día. Ya están documentados también el consumo de Fuentes en créditos (GET /api/mod/fuentes/{espacio}/consumo) y el registro de mensajes que entraron por Kommo, con los que se perdieron (GET /api/mod/kommo/{espacio}/inbound/ledger). Todas son de lectura y no gastan créditos. ## Landings: vuelve a una versión anterior NUEVO · 2026-10-29 POST /api/mod/landing/{espacio}/landings/{landing}/restaurar copia una versión anterior como versión nueva, con su HTML y su certificado, sin gastar créditos. Las versiones traen con_html y restaurada_de. El formConfig queda guardado en la landing: refinar o validar sin él usa el mismo. La revisión del brief también pregunta por el destino de un siguiente paso que el brief promete, y sus ejemplos son formatos con huecos, nunca datos. ## Fuentes: la publicación se reintenta sola CORRECCIÓN · 2026-10-24 Si una ficha armada no llega a los documentos del espacio, traza.publicacion.status queda en fallida y se reintenta sola hasta lograrlo, sin gastar créditos. Con rechazada no se publicará: ármala otra vez. ## Módulos que solo activa Bentho NUEVO · 2026-10-18 Algunos módulos los activa o los quita solo Bentho. PATCH /api/companies/{espacio}/modules responde 403 si tu lista los pone o los quita, y GET /api/companies/{espacio}/limite no los ofrece en activables. Pídeselos a tu asesor. ## Productos: fijar un precio reemplaza el anterior CORRECCIÓN · 2026-10-18 POST /api/mod/productos/{espacio}/prices con la misma lista, variante, moneda y min_qty que un precio que ya existe lo reemplaza: el anterior deja de existir y la respuesta trae el id nuevo. Para cambiar un precio ya no hace falta borrarlo antes. ## Las respuestas con precios o stock no se reusan CORRECCIÓN · 2026-10-13 La caché de respuestas ya no guarda las que dan precios, totales, descuentos, envíos, stock o disponibilidad: se responden de nuevo cada vez, con los datos de ese momento. Cambiar el catálogo no pide vaciar la caché. ## Agente ventas sustituye a Ventas y a Comprensión NUEVO · 2026-10-13 El módulo agente (Agente ventas) atiende y vende por chat y entiende cada mensaje en el mismo turno. Ventas y Comprensión se retiraron: /api/mod/ventas/… y /api/mod/comprension/… responden 404. Cambia el prefijo de tu integración por /api/mod/agente/{espacio}/…: cada ruta de Ventas sigue igual, con el mismo cuerpo y la misma respuesta. Los espacios que tenían Ventas ya están en Agente ventas, y en ellos POST conversations del núcleo llega al agente. ## Fuentes: cierre externo y lo que se leyó NUEVO · 2026-10-05 Con como.cierre_externo, una búsqueda externa intenta llenar al final los campos vacíos de una ficha; lo que no respalda una página sale marcado y se cobra a tarifa externa. Tres rutas nuevas: el texto de una página que leyó una ficha y las capturas de una fuente. Los errores de fichas y corridas llegan en una frase clara. ## Respuestas sin datos internos CORRECCIÓN · 2026-10-05 Algunas respuestas traían campos internos que nunca estuvieron documentados, y ya no salen. La configuración del asistente devuelve lo que puedes cambiar más el nivel de exigencia y el repaso; la de Ventas, los ajustes que puedes cambiar, y mandar uno interno da 403. Ninguna ruta ni campo documentado cambió. ## Todos los módulos, documentados NUEVO · 2026-10-03 Cada módulo tiene ahora su guía y una ficha por ruta: Ventas, Marca, Comprensión, Productos, Comercial, Facturación, Contabilidad, Kommo y Transcripción, junto a Fuentes y Landings. En el mapa tienen su propia sección, por categorías. Y en la API, nuevas fichas para leer tus espacios, tu cuenta y su configuración. ## Landings por la API EN PREPARACIÓN · 2026-10-02 Ya puedes consultar la guía y preparar la integración de Landings. Su uso por API o desde el Estudio requiere que el módulo esté disponible y habilitado en tu espacio. Crear, refinar, validar y completar gastan créditos. ## Fuentes por la API NUEVO · 2026-10-01 Con el módulo Fuentes, arma fichas con datos de la web pública (cada uno con su página y su cita) y publícalas en los documentos del espacio. Armar una ficha y lanzar una fuente gastan créditos. ## Validaciones más estrictas en la entrada SEGURIDAD · 2026-09-25 Ninguna ruta ni campo cambió de forma. ## Consumo medido en créditos, por ruta NUEVO · 2026-09-24 Cada llamada descuenta créditos de la bolsa de tu cuenta según su uso. Consulta el saldo con GET /api/cuentas/{cuenta}/bolsa. ## La lista de IPs compara con la IP pública de tu servidor CORRECCIÓN · 2026-09-18 Una lista que incluye tu IP ya no te rechaza por error. ## Los mandos de confianza del Cerebro, con sus rangos NUEVO · 2026-09-18 GET /api/rag/{espacio}/config devuelve, además de la configuración, qué campos puedes editar y el rango válido de cada uno. ## api-config: límites, cuota diaria e IPs por espacio NUEVO · 2026-09-18 Cada espacio decide si su API está activa, cuántas peticiones por segundo acepta, su ráfaga, su cuota diaria y qué IPs pueden llamar. ## El consumo se atribuye a la clave que llama NUEVO · 2026-09-17 Ves cuánto consume cada clave. ## Claves de servicio bth_ NUEVO · 2026-09-16 Claves con alcance por espacios, permiso de lectura o escritura y revocación. El secreto se muestra una sola vez.