Saltar al contenido principal
Actualizado el Nov 4, 2026

Ejecutar código

Usa esta acción cuando un paso necesite lógica personalizada que CEL no puede expresar — reformatear datos, calcular valores, procesar archivos o llamar a una API con código. Una sola acción cubre ambos lenguajes: elige JavaScript o Python en el campo Lenguaje.

Mejor para​

  • Transformar JSON de pasos anteriores exactamente a la forma que un paso posterior necesita
  • Cálculos, parsing y validaciones más allá de lo que pueden hacer las expresiones CEL
  • Pequeñas integraciones usando fetch (cualquier lenguaje) cuando la acción Solicitud HTTP no es lo bastante flexible
  • Trabajo con documentos y archivos usando las bibliotecas incluidas de Python — hojas de cálculo, PDFs, imágenes, DOCX/PPTX

Elegir el lenguaje​

JavaScriptPython
RuntimeSandbox V8 aislado (sin APIs de Node.js)Python 3.12 en un contenedor desechable
Redfetch con protección SSRFfetch con protección SSRF (mediado por el host)
Tiempo límite (ms)100–30.000, por defecto 5.0001.000–120.000, por defecto 30.000
Memoria (MB)8–256, por defecto 128128–2.048, por defecto 512
BibliotecasRecursos estándar de JavaScript y módulos publicados de la empresaopenpyxl, python-docx, python-pptx, pdfplumber, pypdf, pandas, Pillow, reportlab, markitdown — sin pip en tiempo de ejecución

Ambos lenguajes comparten las mismas entradas y salidas: Datos de entrada estructurados, Archivos de entrada, un resultado estructurado, archivos de salida y logs en un formato unificado.

Campos principales​

CampoQué hace
Lenguajejavascript (por defecto) o python
CódigoEl código a ejecutar. JavaScript se envuelve en una IIFE asíncrona, así que puedes usar await en el nivel superior y terminar con return <valor>. Python se ejecuta como un script normal; escribe el resultado en /work/out/output.json
Datos de entradaExpresión CEL opcional evaluada antes de la ejecución. JavaScript ve el valor en la variable input; Python lo ve en la variable INPUT
Archivos de entradaExpresión CEL opcional que evalúa a un array de referencias de storage [{bucket, fullPath, name?}] — por ejemplo workflow.step(0).files o un input de workflow de tipo archivo. El contenido de los archivos se entrega al sandbox; el código nunca accede al storage directamente
BibliotecasBibliotecas publicadas de la empresa, fijadas a versiones exactas para este paso de código y lenguaje
PropósitoEtiqueta opcional de lo que hace el código, mostrada en los logs
Tiempo límite (ms)Límite de tiempo de ejecución en milisegundos, acotado por lenguaje (ver la tabla de arriba). El código que lo supera — incluidos los bucles infinitos — se termina
Límite de memoria (MB)Límite de memoria del sandbox, acotado por lenguaje

Bibliotecas reutilizables de la empresa​

La lógica que necesitas en más de un paso puede vivir en una biblioteca de la empresa: un módulo JavaScript o Python publicado en versiones numeradas que nunca cambian. Un paso fija una versión exacta de cada biblioteca que usa, en el cuadro Bibliotecas sobre el código, y mantiene esa versión hasta que la actualices.

  • Añadir biblioteca fija una biblioteca publicada en el lenguaje del paso. Cuando existe una versión más nueva, Actualizar a vN muestra qué cambia antes de pasar a ella.
  • Abre la fila de una biblioteca para ver sus exportaciones y parámetros, e inserta una llamada en la posición del cursor.
  • Cambiar el Lenguaje del paso quita sus bibliotecas fijadas.

Un paso JavaScript llama a una biblioteca fijada a través de autotalk_libraries:

return autotalk_libraries.greetings.greet(input.name);

Un paso Python la importa y escribe su resultado como siempre:

import json
from autotalk_libraries import greetings

with open("/work/out/output.json", "w") as output:
json.dump(greetings.greet(INPUT["name"]), output)

Un paso JavaScript solo alcanza las bibliotecas fijadas en él, no sus propios imports. La herramienta MCP run_code acepta las mismas fijaciones exactas como codeLibraries: [{libraryId, releaseId}], y get_code_library_reference las encuentra. Para escribir, probar y publicar bibliotecas, y para sus límites y permisos, consulta Bibliotecas.

Archivos de entrada y de salida​

Ambas direcciones comparten los mismos límites: como máximo 10 archivos, 20 MiB por archivo, 24 MiB en total. Los nombres de archivo deben empezar con letra o dígito y pueden contener letras, dígitos, ., _, - y espacios (hasta 128 caracteres). Los nombres input.json y output.json están reservados — son los canales de datos estructurados.

Leer archivos de entrada:

  • JavaScript — files.readText(name) devuelve el contenido como string de texto (decodificado como UTF-8); files.read(name) lo devuelve como string base64, para datos binarios
  • Python — los archivos aparecen en /work/in/<nombre> (solo lectura)

Escribir archivos de salida:

  • JavaScript — files.writeText(name, text, {contentType?}) para texto; files.write(name, contentBase64, {contentType?}) para binario. contentType se infiere de la extensión cuando se omite
  • Python — escribe los archivos en /work/out/

Para trabajar con texto — CSV, JSON, Markdown — usa readText / writeText y olvídate del base64:

const rows = files.readText("data.csv").split("\n").map((line) => line.split(","));
files.writeText("summary.txt", `${rows.length} filas`);

El par base64 existe para archivos binarios (imágenes, PDFs, hojas de cálculo), normalmente para pasar los bytes sin cambios o entregarlos a fetch.

Los archivos de salida se suben al storage de tu empresa después de la ejecución y aparecen como referencias de storage en workflow.step(N).files. Los pasos posteriores pueden encadenarlos — por ejemplo, un paso Python cuyos Archivos de entrada sean workflow.step(0).files lee todo lo que produjo el paso de código anterior.

El sandbox JavaScript​

El código se ejecuta en un sandbox V8 aislado en un servicio de ejecución separado. No hay acceso a APIs de Node.js (require, process, sistema de archivos) ni a APIs de navegador — solo los recursos estándar de JavaScript más estos puentes:

  • input — el valor evaluado de Datos de entrada (null cuando está ausente)
  • console.log/info/warn/error — capturados y devueltos en la salida logs
  • fetch(url, options) — HTTP con protección SSRF (las direcciones privadas e internas están bloqueadas) que devuelve {ok, status, statusText, body}, con body interpretado como JSON cuando es posible y devuelto como texto en caso contrario. Los cuerpos de respuesta están limitados a 10 MiB: una respuesta mayor hace fallar el paso en lugar de almacenarse en memoria
  • files.read / files.readText / files.write / files.writeText — ver arriba
  • atob(base64) / btoa(binaryString) — el codec base64 estándar, para convertir los valores con los que trabajan files.read y files.write. btoa sigue la regla de la web de rechazar caracteres por encima de U+00FF, así que usa files.writeText para texto UTF-8 en lugar de btoa

No existe Buffer, ni TextDecoder/TextEncoder, ni require — atob/btoa y los helpers de texto de files son la forma soportada de convertir entre bytes y strings.

El sandbox Python​

El código se ejecuta como python3 job.py en un contenedor nuevo que se destruye después de la ejecución:

  • INPUT — el valor evaluado de Datos de entrada, cargado desde /work/in/input.json (None cuando está ausente)
  • fetch(url, method="GET", headers=None, body=None) — el mismo HTTP con protección SSRF del lenguaje JavaScript, provisto por la plataforma (el contenedor en sí no tiene red cruda; cada solicitud es mediada por el host). Devuelve un dict {"ok", "status", "statusText", "body"}, con body interpretado como JSON cuando es posible y devuelto como texto en caso contrario; los cuerpos de respuesta están limitados a 10 MiB. Lanza FetchError cuando la solicitud no puede realizarse (un destino bloqueado o inaccesible). Una respuesta no-2xx no es un error — devuelve con ok=False, igual que en el lenguaje JavaScript. No dependas de requests ni urllib para llamadas salientes — usa fetch()
  • Archivos de entrada en /work/in/ (solo lectura), archivos de salida en /work/out/
  • Escribe el resultado estructurado del paso como JSON en /work/out/output.json — eso es lo que recibe workflow.step(N).result. print() va a la salida logs, no al resultado. Un job que no escribe output.json recibe workflow.step(N).result = null
  • Los módulos de la biblioteca estándar, los paquetes incluidos listados arriba y las bibliotecas fijadas de la empresa son importables — no hay pip en tiempo de ejecución
  • Solo un job Python se ejecuta a la vez; un runner ocupado hace fallar el paso con un error reintentable (ver abajo)

Obtener datos y transformarlos con las bibliotecas incluidas:

import json
resp = fetch("https://api.example.com/report", headers={"accept": "application/json"})
if not resp["ok"]:
raise FetchError(f"upstream devolvió {resp['status']}")
rows = resp["body"]["items"]
with open("/work/out/output.json", "w") as f:
json.dump({"count": len(rows)}, f)

Qué pueden usar los pasos posteriores​

  • workflow.step(N).result — el resultado estructurado (valor de return en JavaScript / output.json en Python), limitado a 2 MiB — escribe los datos voluminosos como archivos. null cuando el código no devolvió nada / no escribió output.json
  • workflow.step(N).files — referencias de storage de los archivos que el código escribió: [{name, bucket, fullPath, contentType, size}]
  • workflow.step(N).logs — salida capturada como entradas [{level, message}] (niveles de consola en JavaScript; stdout / stderr en Python), hasta 200 entradas de 2.000 caracteres cada una
  • workflow.step(N).executionTimeMs — cuánto tiempo se ejecutó el código

Las versiones anteriores exponían el valor de retorno de JavaScript como workflow.step(N).data. Ahora es workflow.step(N).result, para ambos lenguajes.

Como este paso publica result y no data, workflow.stepData y workflow.stepHasContent resuelven dentro de result: workflow.stepData(N, "count") lee workflow.step(N).result.count, que normalmente es lo que quieres. Los tres hermanos de la raíz del paso son invisibles para ellos — lee esos con maps.get(workflow.step(N), "files"), y no con workflow.stepHasContent(N, "files"), que fallaría en silencio mientras workflow.stepOk(N) sigue siendo true. Esto vale incluso cuando el código no devolvió nada, porque entonces result es null. Mira Acciones que publican en la raíz del paso.

Errores​

CódigoSignificado
code_execution_failedEl código lanzó una excepción — revisa workflow.step(N).logs
code_execution_timeoutEl código superó el tiempo límite (ambos lenguajes)
code_executor_busyEl único slot del runner Python está ocupado — reintentable; volver a ejecutar el paso es seguro
code_result_too_largeEl resultado serializado supera los 2 MiB — devuelve un resumen pequeño y escribe los datos voluminosos como archivos
code_result_invalid_jsonPython escribió /work/out/output.json, pero no es JSON válido
code_input_files_invalidArchivos de entrada no evaluó a referencias válidas, superó los límites o usó un nombre reservado
code_input_file_not_accessibleUn archivo de entrada no se encontró o no es accesible desde este workspace
code_input_file_unavailableFallo transitorio de storage al leer un archivo de entrada — reintentable
python_unavailableEl lenguaje Python no está habilitado en este entorno

Consejos​

  • Siempre produce un resultado — return en JavaScript, output.json en Python — y mantenlo compatible con JSON.
  • Usa console.log / print() mientras construyes el paso; la salida logs es la forma más fácil de depurar.
  • Cada llamada a fetch también está limitada por el tiempo del paso, así que mantén las llamadas a terceros bien dentro del presupuesto de tiempo.
  • Los archivos son el canal correcto para cualquier cosa grande: el resultado está limitado a 2 MiB, mientras que los archivos llevan hasta 24 MiB por ejecución.