CEL Expressions

CEL (Common Expression Language) is a small, non-Turing-complete expression language designed for fast, safe evaluation. Hatchet uses CEL wherever a feature needs to compute a value or make a match decision from run input at trigger time: concurrency keys, rate limit keys, idempotency keys, batch keys, event filters, task conditions, and webhook routing. Expressions are evaluated on the Hatchet engine, not in your worker, so they can run before a task is ever assigned. For a hands-on introduction to the language itself, see CEL by example.

Where Hatchet accepts CEL

FeatureThe expression producesGuide
Concurrency keysA string grouping keyConcurrency
Dynamic max runsA positive integer limit per concurrency groupConcurrency
Dynamic rate limitsA string key; units and limit may also be integer expressionsRate Limits
Idempotency keysA string keyIdempotency
Batch keysA string partitioning keyBatch Tasks
Event filtersA boolean (trigger the run or not)Events
Parent conditions (skip_if, cancel_if, wait_for)A booleanDAGs
User event conditions and durable event waitsA boolean (accept the event or not)Durable Event Waits
Incoming webhook event key and scopeA stringWebhooks

Each context exposes a different set of variables, listed below. Referencing a variable that doesn't exist in the context causes the expression to fail. A failing concurrency, max runs, rate limit, or batch key expression creates the run in a failed state with the evaluation error as the failure reason. A failing idempotency key expression skips the run entirely, and a failing filter expression means the filter does not match, so the run is not triggered.

Key expressions: concurrency, rate limits, and batch keys

Concurrency key expressions, dynamic max runs expressions, dynamic rate limit expressions, and batch key expressions are all evaluated when a run is triggered, with the same variables available:

VariableMeaning
inputThe input object of the run being triggered
additional_metadataThe additional metadata set on the run
workflow_run_idThe id of the run being triggered, as a string
parentsThe outputs of completed parent tasks, keyed by parent readable id (rate limit and batch keys)

Concurrency keys, batch keys, and rate limit keys must evaluate to a string. Dynamic rate limit units and limit expressions must evaluate to an integer, and a dynamic max runs expression must evaluate to a positive integer, which sets the concurrency limit for that run's group.

'customer:' + input.customer_id

Task-level rate limit and batch key expressions in a DAG can derive keys from a parent task's output, for example 'tenant:' + string(parents["lookup"]["tenant_id"]). Concurrency key and dynamic max runs expressions cannot reference parents and are rejected at registration if they do.

Idempotency key expressions

Idempotency key expressions are evaluated before the run is created, so no run id is available. An expression that references workflow_run_id is rejected when the workflow is registered:

VariableMeaning
inputThe input object of the run being triggered
additional_metadataThe additional metadata set on the run

The expression must evaluate to a string. Keys starting with the reserved prefix hatchet_internal_ are rejected.

checksum(input.order_id + ':' + input.customer_id)

Event filter expressions

Event filter expressions decide whether an incoming event triggers a workflow. They must evaluate to a boolean:

VariableMeaning
inputThe payload of the incoming event
payloadThe payload stored on the filter when it was created
additional_metadataThe additional metadata sent with the event
event_keyThe key of the event, such as user:created, as a string
event_idThe id of the event, as a string
input.status == 'active' && input.customer_id == payload.customer_id

Task condition expressions

Parent conditions (skip_if, cancel_if, and wait_for conditions on a parent task) are evaluated against the parent task's output and must evaluate to a boolean:

VariableMeaning
outputThe output object of the parent task
output.random_number > 50

User event conditions, used in wait_for conditions and durable event waits, are evaluated against the payload of the incoming event and must also evaluate to a boolean:

VariableMeaning
inputThe payload of the incoming event
input.user_id == 1234

Incoming webhook expressions

The event key expression and scope expression on incoming webhooks are evaluated against the webhook request. Both must evaluate to a string:

VariableMeaning
inputThe webhook's JSON body, merged with the webhook's static payload if one is configured
headersThe request headers, as a map of lowercased header names to values
'github:' + headers['x-github-event'] + ':' + input.action

Functions

All contexts support the CEL standard library, including macros like has() for checking whether a field exists:

has(input.custom.value) ? input.custom.value : 'default'

Every context except task condition expressions additionally supports a Hatchet-provided checksum function, which returns the SHA-256 hex digest of a string. This is useful for keeping keys short and uniform when the underlying values are long:

checksum(input.long_document_id)

Those same contexts (key expressions, event filters, and incoming webhook expressions) also support the strings extension, which adds functions like lowerAscii, replace, and split.

Debugging expressions

Each SDK exposes a CEL feature client with a debug method that evaluates an expression on the Hatchet engine against an input, additional metadata, and a filter payload that you provide, returning the result or the evaluation error. It runs the expression in the event filter context, so it's most useful for testing filter expressions before creating them:

Finding evaluation failures

Where an evaluation failure lands depends on the context:

ContextWhat happens to the runWhere the error is visible
Concurrency, rate limit, and batch keysThe run is created in a failed stateThe run's error message
Idempotency keysNo run is createdEngine internals only (not queryable today)
Event filtersThe filter does not match, no runEngine internals only (not queryable today)
Webhook event key and scope expressionsThe request is rejectedThe HTTP 400 response returned to the sender

Key expression failures are the easiest to find because they produce a visible failed run. The evaluation error, including the expression text, becomes the run's error message. You can see it in the run detail page of the dashboard, in the run details returned by the REST API, or from the CLI with hatchet runs get <run-id> -o json (the error also appears in the run's failure event, shown by hatchet runs events <run-id>).

Webhook expression failures are reported to the caller: Hatchet rejects the request with a 400 status and a description of the problem, such as Event key expression must evaluate to a string. Check the delivery logs of the system sending the webhook.

Idempotency key and event filter failures are the hardest to spot. The engine records them internally and writes them to its logs, but no dashboard page, API endpoint, or CLI command exposes them yet. From the outside, the only symptom is that an expected run never appears. If a trigger silently produces no run and you suspect the expression, reproduce the failure with the CEL debug method above using the same event payload or run input.

Last updated on September 4, 2026

On this page