En este artículo aprenderás a crear y disparar Acciones de código, responder al usuario con mensajes y botones, integrar servicios externos, persistir el estado del contacto y depurar tus desarrollos antes de pasarlos a producción.
Las Acciones de código son scripts de JavaScript que el bot ejecuta dentro de una conversación. Son la alternativa recomendada para agregar lógica arbitraria o para conectar servicios externos y obtener información relevante en tiempo real. Cada Acción de código recibe el contexto del usuario (identidad, mensaje recibido, variables) y determina la respuesta que se va a generar.
Puedes:
Requisitos previos
Acceso a tu cuenta de Botmaker con permisos para desarrollar en la sección Código.
Conocimientos básicos de JavaScript: variables, funciones, async/await y llamadas HTTP. No se requiere experiencia previa con Botmaker.
La plataforma soporta Node.js v22. Si necesitas una biblioteca que no esté disponible, escríbenos a architecture@botmaker.com y nuestro equipo evaluará la solicitud.
Conceptos clave
Acción de código: script de JavaScript que el bot ejecuta al alcanzar una regla del flujo.
context: objeto de solo lectura con la información de la conversación (datos del contacto, mensaje recibido y parámetros de un botón).
result: objeto con el que defines la respuesta al usuario (texto, imagen, botones, etc.). Toda ejecución debe finalizar con result.done().
user: permite leer y escribir variables del contacto, que persisten entre ejecuciones.
db: base de datos clave-valor del business, compartida entre todos los chats.
rp (request-promise): cliente HTTP recomendado para llamar a servicios externos.
bmconsole: herramienta de logging que etiqueta los logs en el panel de Eventos del editor.
Nota: cada ejecución tiene un límite de 90 segundos. Además, cada ejecución es independiente y no comparte memoria con las siguientes: la información que quieras conservar debe guardarse con user o db.
Paso 1: Ingresa a la sección Código
En el menú lateral izquierdo busca la palabra Código y haz clic. Serás redirigido a la pantalla de Acciones de código.


Paso 2: Crea una nueva acción
Haz clic en Crear nueva acción de código. Asigna un nombre a la acción (no lo olvides), elige la versión de Node.js y el tipo de acción. Puedes partir de una plantilla de código para apalancar tu desarrollo.

Paso 3: Publica la acción
Cuando termines de escribir el código, haz clic en Publicar para que la acción quede disponible para usarse.

Paso 4: Usa la acción dentro de un bot
Dirígete al Botdesigner. En la intersección de un flujo, haz clic en el botón Más y selecciona Acción. Luego haz clic en Acciones de Código: podrás elegir entre Acciones de código y Acciones de código con parámetros, y seleccionar cualquiera de las que tengas publicadas.

Paso 1: Prueba un "Hola, mundo"
Crea una Acción de código en el editor, inserta el siguiente código y pruébalo desde el simulador:
result.text('¡Hola, mundo!');
result.done();
result.text(...) define un mensaje de texto y result.done() finaliza la ejecución y dispara el envío al usuario.
Para leer el nombre del contacto:
const nombre = context.userData.FIRST_NAME || 'amigo';
result.text(`¡Hola, ${nombre}!`);
result.done();
Paso 2: Adopta la plantilla recomendada
A partir de aquí, conviene usar esta plantilla en todas tus Acciones de código. Aísla la lógica en una función main, captura los errores y garantiza el cierre de la ejecución:
// ====== CONSTANTS ======
const CA_NAME = 'mi-accion-de-codigo';
// ====== HELPERS ======
// (funciones auxiliares, si aplica)
// ====== MAIN ======
const main = async () => {
// lógica principal
};
main()
.catch(err => {
const errorMessage = `[${CA_NAME}] Error - ${err.message}`;
user.set('ca_error', errorMessage);
bmconsole.log(errorMessage);
})
.finally(() => result.done());
Estos son los métodos de result disponibles para responder:
Puedes encadenar varios mensajes en una misma ejecución:
// ====== CONSTANTS ======
const CA_NAME = 'enviar-factura';
const URL_FACTURA = 'https://midominio.com/facturas/123.pdf';
// ====== MAIN ======
const main = async () => {
result.text('A continuación, tu factura:');
result.file(URL_FACTURA, 'factura.pdf');
result.text('Ante cualquier duda, escríbenos.');
};
main()
.catch(err => {
user.set('ca_error', `[${CA_NAME}] ${err.message}`);
bmconsole.log(err.message);
})
.finally(() => result.done());
El objeto context contiene la información de la conversación y se compone de tres ramas.
context.userData — datos del contacto:
context.message — el mensaje recibido:
context.params — parámetros recibidos cuando la acción se invoca desde un botón (más detalle en la sección de botones).
Para leer y escribir variables del contacto usa user.get y user.set. Estas variables persisten entre ejecuciones y sus valores deben ser de tipo string:
const cep = user.get('cep'); // lectura
user.set('ultimaConsulta', new Date().toISOString()); // escritura
Nota: puedes asignar hasta 200 variables por ejecución y guardar hasta 100 KB por valor.
Si cargaste un archivo CSV en el menú Registros, puedes leerlo y filtrarlo con entityLoader:
/* Entidad cargada:
id | name
1 | Gabriel
2 | Dario
*/
entityLoader('nombre de la entidad', json => {
if (!json) {
user.set('error', 'No hay datos');
result.done();
return;
}
const data = json.find(row => row.id === 2);
result.text('Muestro el nombre -> ' + data.name);
});
El cliente HTTP recomendado es rp (request-promise). Incluye un timeout por defecto y publica sus logs automáticamente en el panel de Eventos.
GET con query string y respuesta JSON:
const buscarUsuario = (id) => rp({
method: 'GET',
uri: 'https://api.ejemplo.com/usuarios',
qs: { id, key: API_KEY },
json: true,
headers: { Accept: 'application/json' },
});
POST con JSON body y bearer token:
const crearTicket = (titulo, descripcion) => rp({
method: 'POST',
uri: 'https://api.ejemplo.com/tickets',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${TOKEN}`,
},
body: { titulo, descripcion, contactoId: user.get('userId') },
json: true,
});
POST form-urlencoded (típico para obtener un token OAuth):
const obtenerToken = () => rp({
method: 'POST',
uri: 'https://oauth.ejemplo.com/token',
form: {
grant_type: 'client_credentials',
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
},
json: true,
});
Siempre valida la respuesta antes de acceder a campos anidados:
const main = async () => {
let data;
try {
data = await buscarUsuario(user.get('userId'));
} catch (err) {
result.text('No fue posible consultar los datos en este momento. Inténtalo más tarde.');
return;
}
if (!data || !data.results || data.results.length === 0) {
result.text('No se encontró información asociada a la cuenta.');
return;
}
result.text(`El saldo es ${data.results[0].balance}`);
};
También puedes llamar a servicios SOAP/XML (parseando la respuesta con xml2js), leer una Google Sheet pública en CSV (con csv), descargar un archivo binario para reenviarlo con result.fileFromBuffer, o enviar un template de WhatsApp mediante la API de notificaciones de Botmaker (https://api.botmaker.com/v2.0/notifications).
Botones simples:
const main = async () => {
const b = result.buttonsBuilder();
b.text('¿Qué deseas hacer?');
b.addButton('Ver pedido', 'rule-ver-pedido'); // dispara una regla del flujo
b.addURLButton('Ir al sitio', 'https://miempresa.com'); // abre una URL externa
b.addPhoneButton('Llamar', '+5511912345678'); // inicia una llamada
b.send();
};
Quick replies (botones tipo "pill" en WhatsApp y otros canales):
result.buttonsBuilder()
.text('¿Confirmas la compra?')
.quickReplies()
.addButton('Sí', 'confirmar')
.addButton('No', 'cancelar')
.send();
Botones que invocan otra Acción de código con parámetros:
result.buttonsBuilder()
.text('Selecciona una opción:')
.addClientActionButton('Opción A', 'mi-otra-ca', { opcion: 'A' })
.addClientActionButton('Opción B', 'mi-otra-ca', { opcion: 'B' })
.send();
// En la Acción de código de destino:
// const opt = context.params.opcion;
Navegación condicional con gotoRule:
if (datosOK) result.gotoRule('proximo-paso');
else result.gotoRule('volver-a-pedir-datos');
Carrusel a partir de una lista:
const main = async () => {
const productos = await listarProductos();
const car = result.carouselBuilder();
productos.forEach(p => {
const btns = result.buttonsBuilder()
.addClientActionButton('Comprar', 'agregar-al-carrito', { sku: p.sku })
.buildButtons();
car.addItem(p.titulo, p.descripcion, p.imagen, btns, p.url);
});
car.send();
};
Elige el mecanismo según el alcance del dato:
El componente db no necesita importarse y ofrece estas operaciones:
Nota: en Acciones de código de tipo Endpoint o Cron, estos métodos se usan anteponiendo request. (por ejemplo, request.db.get(...)).
Un patrón muy común es cachear un token con expiración para no pedirlo en cada ejecución:
const obtenerTokenCacheado = async () => {
const cached = await db.get('api-token');
if (cached) return cached;
const fresh = await obtenerTokenFresco();
await db.set('api-token', fresh.access_token, 3600); // expira en 1 hora
return fresh.access_token;
};
Para guardar varios campos, serializa un objeto a JSON y guárdalo en una sola variable:
const datos = { nombre: 'Juan', plan: 'Premium', score: 850 };
user.set('miObjeto', JSON.stringify(datos));
// lectura posterior:
const obj = JSON.parse(user.get('miObjeto') || '{}');
Estas funciones y bibliotecas están disponibles directamente en el editor; se invocan por su nombre, sin necesidad de importación:
Usa bmconsole para registrar logs etiquetados en el panel de Eventos, lo que facilita el filtrado en producción:
bmconsole.log('Iniciando consulta API');
bmconsole.warn('Respuesta inesperada, se usó fallback');
bmconsole.error(`Falla en la API: ${err.message}`);
Para ver el último error desde el editor sin abrir el panel de Eventos, guárdalo en una variable del contacto:
} catch (err) {
user.set('ca_error', `[mi-CA] ${err.message}`);
bmconsole.error(err.stack);
}
El modo tester te permite mostrar información de depuración solo cuando el contacto está marcado como desarrollo (la marca se configura desde el editor sobre el contacto):
const isTester = user.isTester;
const debug = (msg) => isTester ? result.text(`[DEBUG] ${msg}`) : bmconsole.log(msg);
debug(`La API respondió: ${JSON.stringify(data)}`);
Para trabajar con Acciones de código generativas, te compartimos el artículo "¿Qué son las Acciones de Código Generativas y cómo usarlas?".
Para crear una acción de código tipo MCP, te compartimos el artículo "Cómo crear una acción de código tipo MCP".
Recuerda visitar nuestro Centro de Ayuda para mayor información