Saltar al contenido principal
Actualizado el Sep 17, 2026

Referencia de Funciones CEL

Referencia de las funciones y los operadores integrados disponibles en las expresiones CEL de AutoTalk, organizados por categoría.

Entran en ese alcance dos conjuntos de nombres, y ambos están en esta página. La biblioteca estándar de CEL — operadores, macros de lista, funciones de string, conversiones de tipo y los getters de timestamp — viene del registro CEL compartido en AutoTalk Commons (src/functions/cel/index.ts), el mismo registro del que se proyecta el panel Explorer del editor CEL, así que todo lo que el Explorer ofrece está documentado aquí. Además, AutoTalk añade sus propios auxiliares — get, coalesce, pluck, la familia math_*, los auxiliares de fecha, los auxiliares de workflow step_* — que existen solo aquí.


Core

get(obj, path, default?)

Accede de forma segura a una propiedad anidada a través de una ruta con puntos.

get(contact, "address.city") // "São Paulo"
get(contact, "address.zip", "00000-000") // retorna el predeterminado si falta
get(null, "name") // null
get(step(0), "data.results.0.title") // ruta profunda con índice de array
ParamTipoDescripción
objanyObjeto a acceder (seguro para null)
pathstringRuta con puntos (ej.: "a.b.c")
defaultanyValor retornado si la ruta no existe (predeterminado: null)

has(obj, path?)

Verifica si un valor o una ruta anidada existe y no es null/undefined.

has(contact, "platformId") // true si contact.platformId está definido
has(myVar) // true si myVar no es null/undefined
has(obj, "a.b.c") // true si la ruta completa resuelve a no-null

coalesce(...vals)

Retorna el primer valor no-null y no-undefined. Variádica — acepta hasta 5 argumentos. Si necesitas más, anida las llamadas: coalesce(a, b, c, d, coalesce(e, f)).

coalesce(get(contact, "customAttributes.nickname"), contact.name, "Guest") // el primer no-null gana
coalesce(0, 42) // 0 (no es null!)
coalesce(false, true) // false (no es null!)
coalesce("", "fallback") // "" (no es null!)
Semántica de verificación de null

coalesce() solo omite null y undefined. Valores como 0, false y "" son válidos y se retornan.

now(tz?)

Retorna la fecha/hora actual como una string ISO 8601.

now() // "2024-01-15T14:30:00.000Z"
format_datetime(now(), "YYYY-MM-DD") // "2024-01-15"

El resultado siempre se expresa en UTC (sufijo Z), incluso cuando se pasa el argumento opcional de zona horaria. Para mostrar la hora actual en una zona horaria específica, formatéala — ej.: format_datetime(now(), "HH:mm", "America/Sao_Paulo").

present(val)

Verifica si un valor está significativamente presente. Retorna false para null, undefined, strings vacías/solo espacios y arrays vacíos. Los números y booleanos siempre se consideran presentes.

present(contact.platformId) // true si es una string no vacía
present("") // false
present(" ") // false (solo espacios)
present(0) // true
present([]) // false
present([1, 2]) // true
Reemplaza patrones verbosos

present(x) reemplaza el patrón común size(trim(coalesce(x, ""))) > 0.

blank(val)

Inverso de present(). Retorna true para null, undefined, strings vacías/solo espacios y arrays vacíos.

blank(contact.platformId) // true si es null o vacío
blank("hello") // false
blank(0) // false

Utilidades

pluck(arr, path)

Extrae una propiedad de cada objeto de un array.

pluck(contacts, "name") // ["Alice", "Bob", "Carol"]
pluck(tools, "tool.function.name") // ruta profunda soportada

slice(arr, start, end?)

Corta un array de forma segura. Retorna [] para entradas que no son arrays. Admite índices negativos.

slice(results, 0, 5) // primeros 5 elementos
slice(results, -3) // últimos 3 elementos
slice(results, 1, -1) // todos excepto el primero y el último
slice(null, 0, 2) // [] (seguro para no-arrays)

defaults(obj, fallbacks)

Fusiona valores de respaldo en un objeto para las claves que son null/undefined. Fusión superficial.

defaults(response, {"status": "unknown", "retryable": false})
// Completa status y retryable solo si son null/undefined en response
Semántica de verificación de null

Como coalesce(), solo se reemplazan los valores null/undefined. 0, false y "" se mantienen.

url_params(base, params)

Construye una URL con parámetros de query. Omite los valores null y vacíos. Codifica automáticamente.

url_params("https://api.example.com/search", {"q": query, "page": 1, "lang": null})
// "https://api.example.com/search?q=hello&page=1" (lang omitido)

truncate(str, maxLen, suffix?)

Trunca una string a una longitud máxima con un sufijo opcional.

truncate("Hello World", 5) // "Hello"
truncate("Hello World", 8, "...") // "Hello..."
truncate(null, 10) // "" (seguro para null)
truncate("Hi", 100) // "Hi" (sin necesidad de truncar)
ParamTipoDescripción
stranyValor a truncar (convertido a string, null retorna "")
maxLennumberLongitud máxima del resultado (incluyendo el sufijo)
suffixstringSe agrega al truncar (predeterminado: "")
tip

El sufijo se incluye dentro de maxLen: truncate("Hello World", 8, "...") retorna "Hello..." (8 caracteres).

tpl(template, vars)

Interpolación simple de strings. Reemplaza los marcadores {key} con valores de un objeto.

tpl("Hello {name}!", {"name": "Alice"}) // "Hello Alice!"
tpl("*{title}*\n{domain}\n{url}", article) // texto formateado del artículo
tpl("{address.city}, {address.country}", contact) // soporte de ruta con puntos
tpl("Hi {name}", {"name": null}) // "Hi " (null → vacío)
ParamTipoDescripción
templatestringString de plantilla con marcadores {key}
varsobjectObjeto con valores para interpolar

join_present(separator, ...values)

Une valores con un separador, omitiendo los valores en blanco. Usa las mismas reglas que present(): null, strings vacías/solo espacios y arrays vacíos se omiten. 0 y false se mantienen. Acepta como máximo 5 argumentos en total — el separador más hasta 4 valores.

join_present(", ", "Alice", "Bob", "Carol") // "Alice, Bob, Carol"
join_present(" - ", title, null, author) // "Title - Author" (null omitido)
join_present(" ", "Hello", "", "World") // "Hello World" (vacío omitido)
join_present(" | ", 0, false, "text") // "0 | false | text" (0/false mantenidos)
Reemplaza la concatenación condicional

join_present(" - ", prefix, text) reemplaza el patrón (present(prefix) ? prefix + " - " : "") + text.

encode_uri(str)

Codifica una string para URL. Los caracteres reservados de URI (&, =, ?, /, #) no se escapan, así que solo es adecuada para codificar una URL completa — no para valores individuales de parámetros de query.

encode_uri("hello world") // "hello%20world"
encode_uri("a&b=c") // "a&b=c" (caracteres reservados preservados)
Construir query strings

Para agregar parámetros de query a una URL de forma segura, usa url_params() — codifica cada valor de parámetro por ti.

format_currency(currency, locale, amount)

Formatea un número como moneda. Los tres argumentos son obligatorios, en este orden: código de moneda, locale y, por último, el importe.

format_currency("USD", "en-US", 1234.5) // "$1,234.50"
format_currency("BRL", "pt-BR", 99.9) // "R$ 99,90"

Operadores

OperadorQué haceEjemplo
!NO lógico!step_ok(0)
&&Y lógico, con cortocircuitostep_ok(0) && present(step_data(0, "id"))
||O lógico, con cortocircuitoblank(contact.name) || contact.name == "-"
==, !=Igualdad y desigualdadstep_data(0, "status") == "open"
<, <=, >, >=Comparaciónsize(step_data(0, "items")) > 0
+Suma. También une dos strings, concatena dos listas y suma una duración a un timestamp[1, 2] + [3][1, 2, 3]
-, *, /, %Resta, multiplicación, división, resto(total - paid) % 2 == 0
inPertenencia: ¿está este elemento en la lista, o esta clave en el mapa?"vip" in contact.tagstrue

in es el que más se pasa por alto. Ahorra un bucle entero cuando solo necesitas saber si un valor está presente, y se combina con el map de abajo:

"open" in step_data(0, "items").map(r, r.status)

Listas y macros

Las macros ejecutan una expresión sobre cada elemento de una lista. Se escriben en estilo receptor — la lista primero — y su primer argumento da nombre a la variable del bucle, que existe solo dentro de esa macro.

MacroQué haceEjemplo
list.all(x, pred)Verdadero cuando todos los elementos coinciden. Verdadero para una lista vacíarows.all(x, x.total > 0)
list.exists(x, pred)Verdadero cuando al menos un elemento coincide. Falso para una lista vacíarows.exists(x, x.status == "open")
list.exists_one(x, pred)Verdadero cuando coincide exactamente un elementorows.exists_one(x, x.primary)
list.map(x, expr)Una nueva lista, con expr aplicada a cada elementorows.map(x, x.email)
list.map(x, pred, expr)Lo mismo, pero solo sobre los elementos que coinciden con predrows.map(x, x.status == "open", x.id)
list.filter(x, pred)Una nueva lista que contiene solo los elementos que coincidenrows.filter(x, x.status == "open")

Este es el camino más corto para "cuántas de las filas que devolvió un paso de búsqueda siguen abiertas" — sin necesidad de un paso Ejecutar Código:

size(step_data(0, "items").filter(r, r.status == "open"))
step_data(0, "items").map(r, r.status == "open", r.name) // nombres de las abiertas
step_data(0, "items").all(r, present(r.email)) // ¿toda fila es utilizable?

Conversión de tipos

FunciónQué haceEjemplo
int(v)A número entero. Trunca un decimal hacia cero; un timestamp pasa a segundos de época, y una duración a segundos enterosint("42")42, int(4.9)4
uint(v)A número entero sin signo. Vuelve como un valor envuelto, no como un número simple — mira abajouint("42")
double(v)A número decimaldouble("3.14")3.14
bool(v)A booleano, desde "true", "false", "1" o "0"bool("true")true
bytes(v)Una string a bytes crudosbytes("hi")
string(v)Cualquier cosa a su forma textual. Un timestamp pasa a una string ISO 8601, y una duración a segundos con una sstring(123)"123"
timestamp(v)Una string ISO 8601 completa, o milisegundos de época, a un timestamptimestamp("2024-01-15T00:00:00Z")
duration(v)Una string de duración — "90m", "1h30m", "3600s" — a una duraciónduration("48h")
type(v)El tipo de un valortype(1)
dyn(v)Pasa un valor tal cual, como tipo dinámicodyn(v)

Cinco cosas que conviene saber antes de poner una de estas en un campo:

  • Una conversión que no puede salir bien no solo vuelve vacía — se lleva la expresión entera consigo, y ninguna protección puede atraparla. int("abc"), bool("yes"), uint("abc"), duration("abc") y timestamp("2024-01-15") (sin la parte de hora) no producen nada, y tampoco lo hace nada construido a su alrededor: int("abc") > 5 queda vacío en vez de false, "n=" + string(int("abc")) queda vacío en vez de "n=", y present, blank, coalesce y ? : también quedan vacíos — present(int("abc")) no es false y coalesce(int("abc"), 0) no es 0. Este es el único punto donde el fallo no se parece al de una ruta de paso inexistente, donde present sí responde false: una condición escrita como !present(int(step_data(0, "qty"))) para decir "la cantidad no era un número" nunca se dispara. Comprueba el valor crudo antes de convertir, para que la conversión solo se ejecute sobre algo que puede aceptar:

    qty.matches("^-?[0-9]+$") ? int(qty) : 0 // 0 cuando qty es "abc"
  • double falla de otra manera, y todavía más callada. double("abc") es NaN: present lo da por presente, blank lo da por no vacío, string() lo renderiza como "NaN", y toda comparación contra él es false — tanto double("abc") > 5 como double("abc") <= 5. La protección con matches de arriba también sirve aquí.

  • uint() devuelve un valor envuelto, no un número. uint("42") compara bien (uint("42") == 42 es true) pero no hace aritmética — uint("42") + 1 queda vacío — y un campo guarda la envoltura en vez de 42. Usa int() salvo que necesites de verdad el tipo sin signo, o desenvuélvelo: int(uint(v)), string(uint(v)).

  • timestamp y duration no son valores que un campo pueda guardar. Son útiles dentro de una expresión — comparando, sumando, alimentando un getter. Para obtener algo que un campo pueda almacenar, envuélvelos: string(timestamp(x) + duration("48h")) es una string ISO, e int(duration("90m")) es 5400.

  • type() tampoco sobrevive hasta un campo, y sus comparaciones no funcionan aquí. Usa present / blank para preguntar si un valor está, y compara contra el propio valor en vez de contra su tipo.


String

CEL incluye funciones de string integradas. La mayoría funciona tanto en estilo receptor como en estilo función:

"hello".contains("ell") // true (estilo receptor)
contains("hello", "ell") // true (estilo función)
FunciónDescripciónEjemplo
contains(str, sub)Verifica si la string contiene la substring"hello".contains("ell")
startsWith(str, prefix)Verifica el prefijo"hello".startsWith("he")
endsWith(str, suffix)Verifica el sufijo"hello".endsWith("lo")
matches(str, pattern)Coincidencia por expresión regular"abc123".matches("^abc[0-9]+$")true
size(x)Longitud de una string, lista o mapasize("hello")5, size([1, 2, 3])3
split(str, sep)Divide en arraysplit("a,b,c", ",")["a","b","c"]
lowerAscii(str)MinúsculaslowerAscii("HELLO")"hello"
upperAscii(str)MayúsculasupperAscii("hello")"HELLO"
trim(str)Elimina espaciostrim(" hi ")"hi"
substring(str, start, end?)Extrae substringsubstring("hello", 1, 4)"ell"
replace(str, old, new)Reemplaza ocurrenciasreplace("aab", "a", "x")"xxb"
indexOf(str, sub)Primer índice de la substringindexOf("hello", "l")2
lastIndexOf(str, sub)Último índice de la substringlastIndexOf("hello", "l")3
charAt(str, index)Carácter en el índicecharAt("hello", 0)"h"
join(list, sep?)Une un array en una stringjoin(["a","b"], ",")"a,b"
format(str, args)Rellena los marcadores %s / %d desde una lista"%d open".format([2])"2 open"
strings.quote(str)Envuelve entre comillas y escapa el contenidostrings.quote("a b")"\"a b\""

Math

FunciónDescripciónEjemplo
math_add(a, b)Sumamath_add(5, 3)8
math_subtract(a, b)Restamath_subtract(10, 3)7
math_multiply(a, b)Multiplicaciónmath_multiply(4, 3)12
math_divide(a, b)Divisiónmath_divide(10, 3)3.333...
math_round(n, decimals?)Redondeomath_round(3.456, 2)3.46
math_floor(n)Pisomath_floor(3.7)3
math_ceil(n)Techomath_ceil(3.1)4
math_abs(n)Valor absolutomath_abs(-5)5
math_pow(base, exp)Potenciamath_pow(2, 3)8
math_sqrt(n)Raíz cuadradamath_sqrt(16)4
math_sin(n)Seno (radianes)math_sin(0)0
math_cos(n)Coseno (radianes)math_cos(0)1
math_tan(n)Tangente (radianes)math_tan(0)0
math_log(n, base?)Logaritmo (natural por defecto)math_log(8, 2)3
math_exp(n)e elevado a la potencia nmath_exp(0)1
math_max(arr)Máximomath_max([1,5,3])5
math_min(arr)Mínimomath_min([1,5,3])1
math_mean(arr)Promediomath_mean([1,2,3])2
math_median(arr)Medianamath_median([1,2,10])2
math_std(arr)Desviación estándar (muestral)math_std([2,4,6])2
math_variance(arr)Varianza (muestral)math_variance([2,4,6])4
math_dot(a, b)Producto punto de dos vectoresmath_dot([1,2,3], [4,5,6])32
math_norm(arr)Norma euclidiana (longitud del vector)math_norm([3,4])5
math_clamp(val, min, max)Restringe al rangomath_clamp(15, 0, 10)10
math_evaluate(expr)Evalúa una string con una expresión matemáticamath_evaluate("2 + 3 * 4")14

math_clamp(val, min, max)

Restringe un número dentro de un rango.

math_clamp(page, 1, 100) // asegura que page esté entre 1 y 100
math_clamp(-5, 0, 10) // 0 (por debajo del mínimo)
math_clamp(15, 0, 10) // 10 (por encima del máximo)

DateTime

FunciónDescripción
now(tz?)Fecha/hora actual como una string ISO 8601 (siempre expresada en UTC)
format_datetime(d, fmt, tz?, locale?)Formatea una fecha/hora. Acepta cualquier string de formato de dayjs — ej.: "DD/MM/YYYY", "YYYY-MM-DD HH:mm:ss", "HH:mm" — más una zona horaria y un locale opcionales ("en", "pt-BR", "es")
add_datetime(d, n, unit)Agrega tiempo. Unidades: "years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds"
diff_datetime(d1, d2, unit)Diferencia entre dos fechas/horas
zoned_datetime(dateStr, timeStr, tz, format?)Construye una fecha/hora con zona horaria a partir de strings separadas de fecha y hora, ej.: zoned_datetime("2024-01-15", "14:30", "America/Sao_Paulo"). Para convertir o mostrar una fecha/hora ya existente, usa format_datetime
is_before(d1, d2)¿d1 antes de d2?
is_after(d1, d2)¿d1 después de d2?
is_between(d, start, end)¿d entre start y end?
is_same_date(d1, d2, unit?, tz?)¿d1 y d2 son iguales en la unidad dada? (unidad predeterminada: "millisecond") — ej.: is_same_date(a, b, "day") verifica el mismo día calendario
is_same_or_before(d1, d2, unit?, tz?)¿d1 igual o antes que d2?
is_same_or_after(d1, d2, unit?, tz?)¿d1 igual o después que d2?

Getters de timestamp y duración

Estos leen una parte de un timestamp o de una duration. Funcionan tanto en estilo receptor — timestamp(x).getFullYear() — como en estilo función — getFullYear(timestamp(x)) — y todo getter de timestamp acepta una zona horaria IANA opcional como último argumento. Sin ella, leen el valor en UTC.

GetterEn un timestampEn una duración
getFullYear(t, tz?)Año de cuatro dígitos
getMonth(t, tz?)Mes, 0–11 — enero es 0
getDate(t, tz?)Día del mes, 1–31
getDayOfMonth(t, tz?)Día del mes, empezando en 0 — el día 15 es 14
getDayOfWeek(t, tz?)Día de la semana, 06, domingo es 0
getDayOfYear(t, tz?)Día del año, empezando en 0 — el 1 de enero es 0
getHours(v, tz?)Hora, 0–23Horas enteras de la duración
getMinutes(v, tz?)Minuto, 0–59Minutos totalesduration("1h30m") da 90, no 30
getSeconds(v, tz?)Segundo, 0–59Segundos totales
getMilliseconds(v, tz?)Milisegundo, 0–999La parte por debajo de un segundo, es decir 0 para cualquier duración en segundos enteros
timestamp(step_data(0, "createdAt")).getDayOfWeek("America/Sao_Paulo") == 0 // cayó en domingo, hora local
getMonth(timestamp(now())) + 1 // mes como 1-12

Dos de estos son fáciles de equivocar: getDate cuenta desde 1 mientras que getDayOfMonth cuenta desde 0, y getMonth cuenta desde 0. Cuando quieres una fecha para mostrar a alguien, y no un número para comparar, format_datetime de arriba es la mejor herramienta.


JSON

FunciónDescripción
json_parse(str)Convierte una string JSON en un objeto
json_stringify(obj)Serializa un objeto en una string JSON

TOON

TOON es un formato de serialización de datos compacto y legible para humanos.

FunciónDescripción
toon_encode(value)Codifica un valor a una string TOON
toon_decode(str)Parsea una string TOON de vuelta a un valor
toon_encode({"name": "Alice", "age": 30}) // "name: Alice\nage: 30"
toon_decode("name: Alice\nage: 30") // {"name": "Alice", "age": 30}

BSON / ObjectId

FunciónDescripción
object_id(value?)Crea o normaliza una instancia de ObjectId (una nueva si no hay argumento). Usa object_id_to_string() para obtener la string hexadecimal
object_id_is_valid(id)Verifica si la string es un ObjectId válido
object_id_to_string(id)Convierte un ObjectId a su string hexadecimal
bson_serialize(value, encoding?)Serializa un valor a bytes BSON, devueltos como una string base64 (predeterminado) o "hex"
bson_deserialize(str, encoding?)Decodifica una string BSON en base64 (predeterminado) o "hex" de vuelta a un valor
ejson_stringify(value, relaxed?)Serializa un valor a una string de MongoDB Extended JSON (modo relaxed por defecto)
ejson_parse(str, relaxed?)Parsea una string de MongoDB Extended JSON a un valor
bson_serialize({"a": 1}) // "DAAAABBhAAEAAAAA" (base64)
bson_serialize({"a": 1}, "hex") // "0c0000001061000100000000"
bson_deserialize("DAAAABBhAAEAAAAA") // {"a": 1}
ejson_stringify({"a": 1}) // "{\"a\":1}"

Específicas de Workflow

Estas funciones solo están disponibles en las expresiones de workflow y de agentes (no en el CEL de formularios).

step(N)

Accede a la salida del paso N del workflow (indexado desde 0).

step(0) // objeto completo de salida del paso
step(0).data // payload de datos del paso
step(0).status // código de estado HTTP (para pasos HTTP)

step_ok(N)

Verifica si el paso N se completó exitosamente. Retorna true cuando executionContext.status es "completed" Y el estado HTTP es 200 (o null para pasos no-HTTP).

// Antes: verboso
get(step(0), "executionContext.status") == "completed" && (get(step(0), "status") == null || get(step(0), "status") == 200)

// Después: una llamada a función
step_ok(0)

step_data(N, path?, default?)

Obtiene datos del paso N en una ruta con puntos opcional. Retorna el predeterminado (o null) si no existe.

La ruta es relativa al data del pasostep_data(0, "title") lee step(0).data.title. No repitas data en la ruta: step_data(0, "data.title") busca step(0).data.data.title, falla en silencio y devuelve el valor por defecto — indistinguible de que el campo esté vacío. (step_data(0, "data.x") solo es correcto cuando el propio cuerpo de la respuesta tiene una clave data en el nivel superior, como en respuestas JSON:API.)

// Antes
get(step(0), "data.title", null)

// Después
step_data(0, "title")
step_data(0, "results.0.name", "Unknown")
step_data(1, "choices.0.message.content")

Acciones que publican en la raíz del paso

La mayoría de las acciones pone su salida bajo data, y por eso la ruta es relativa a él. Veinte no lo hacen — declaran sus salidas en la raíz del paso y no tienen ningún objeto data. No es un caso marginal: actions/ai/llm/chat/generate está en la lista, y su choices es la salida de paso que los workflows recorren más a menudo.

AcciónSalidas en la raíz
actions/ai/agent/sendmessages
actions/ai/llm/chat/generatechoices
actions/agent/lifecycle/session/set_reply_delayreplyDelayMs
actions/ai/text/speakabletext, model
actions/data/company/resource/countcount
actions/mcp/connecturl, toolCount
actions/mcp/connect/autotalk-mcpurl, toolCount
actions/media/audio/synthesizefile, bucket, fullPath, size, contentType, format, cache, billedMeter, billedMs
actions/media/audio/transcribejobId, jobStatus, enginePath, providerType, diarized
actions/media/document/generatefile, bucket, fullPath, fileName, size, contentType, format
actions/media/readtext, supported, truncated, reason, mediaKind, mimeType, fileName, size
actions/media/storage/deletedeleted, existed, bucket, fullPath
actions/media/storage/probedurationSeconds, width, height, formatName, videoCodec, audioCodec, hasAudio, sizeBytes, contentType, cached
actions/media/storage/signed-urlsignedUrl, contentType, size, mediaKind, bucket, fullPath, name, expiresAt
actions/media/storage/uploadfile, signedUrl, contentType, size
actions/media/transformjobId, jobStatus, preset
actions/monitors/cancelmonitorId, monitorState
actions/monitors/createmonitorId, monitorState, expiresAtIso
actions/network/http/download-to-storagestatus, file, signedUrl, contentType, size
actions/security/auth/jwt/generatejwt

Lee esas salidas con get(step(N), "field"):

get(step(0), "count") > 0 // resource/count
get(step(1), "choices.0.message.content") // llm/chat/generate
get(step(2), "mediaKind") == "pdf" // media/read

step_data también resuelve contra la raíz en estos casos — un paso que no tiene ni data ni result recae sobre el propio objeto del paso — así que step_data(0, "count") también funciona. Ese fallback es deliberadamente estrecho y no se aplica a un paso que sí tiene data: en actions/network/http/request/send, step_data(N, "status") sigue devolviendo tu valor por defecto en lugar del código de estado HTTP, porque status es hermano de data en la raíz, no un campo dentro de él. (En actions/network/http/download-to-storage, que sí tiene la forma de raíz, status es una salida del paso, así que step_data(N, "status") sí lo devuelve — la regla es la misma, lo que cambia es la forma de la acción.) Las claves que pertenecen al envoltorio de ejecución (executionContext, actionContext, safeError, safeResult, conditionPassed, aclInfo) nunca son alcanzables por step_data en ningún paso.

Existe un tercer formato, y hoy hay exactamente una acción en él. Ejecutar Código publica su valor de retorno como un result en el nivel superior y no tiene data, así que step_data y step_has_content resuelven dentro de ese valor: step_data(0, "count") lee step(0).result.count, que normalmente es lo que quieres. Sus hermanos en la raíz del paso — files, logs, executionTimeMsno son alcanzables por esa vía y hay que leerlos con get(step(N), "files"). Esto vale incluso cuando el código no devolvió nada, porque entonces result es null, lo que ya basta para tomar esa rama. Por eso la tabla de arriba no tiene una fila para actions/code/execute: no tiene ni la forma data ni la forma de raíz.

Sea cual sea la forma que uses, una ruta que no existe devuelve el valor por defecto en silenciostep_ok(N) sigue siendo true. Nunca confíes en una lista recordada de memoria: comprueba las salidas declaradas de la acción (get_action_definition(actionType), cuya pista STEP OUTPUTS se deriva de esas salidas, o la referencia de la acción) antes de escribir la ruta.

step_error(N)

Obtiene la información de error de un paso fallido. Retorna {code, user_message, retryable} o null.

// Antes
coalesce(get(step(0), "executionContext.safeError.code"), "UNKNOWN")

// Después
step_error(0) // {code: "TIMEOUT", user_message: "...", retryable: true}
get(step_error(0), "code", "UNKNOWN") // "TIMEOUT"

step_has_content(N, path)

Verifica si el paso N se completó exitosamente Y tiene datos presentes (no-null, no-vacío) en la ruta dada. Combina step_ok(N) && present(step_data(N, path)) en una sola llamada.

// Antes: dos verificaciones
step_ok(0) && present(step_data(0, "articles"))

// Después: una llamada a función
step_has_content(0, "articles")

La ruta sigue exactamente la misma regla que step_data: relativa a data o — en las acciones que publican en la raíz — relativa a la raíz del paso.

getContext()

Obtiene el objeto de contexto de ejecución del workflow.

getTool()

Obtiene el objeto de contexto de la herramienta que se está ejecutando actualmente — el mismo contexto del que leen los auxiliares step(N). No toma argumentos; cualquier argumento que se pase se ignora.

getRoot()

Obtiene el contexto raíz del workflow.