Value expressions

Last updated: July 20, 2026

Everywhere a value is needed in a Script (a URL, a request body, a field value, a condition, a filter value, a target id, and so on) takes one of the three forms below. This document explains those three forms, where values come from (the context roots), and the locale-map rules specific to WEEGLOO data. Every field in the Statement catalog follows these rules.

The three forms

FormRuleExample
ReferenceResolves a { /json-pointer } inside a string against the context."{ /payload/fields/title }"
LiteralA value with no { /ptr } (a string, number, boolean, object, or array). Used as-is."draft", 42, true, { "a": 1 }
Operations and conditions (JsonLogic)An object whose single key is an operator. Its operands are themselves value expressions (references, literals, or nested operations).{ "+": [ "{ /vars/n }", 1 ] }

The three forms nest: you put a reference in a JsonLogic operand, then feed that reference's result back into another operation.

Reference: { /json-pointer }

Put an RFC 6901 JSON Pointer (which must start with /) inside the braces. Whitespace around the braces is allowed ({ /a/b } is the same as {/a/b}).

Single pointer vs. mixed template: type rules

  • When the entire string is a single pointer, the value keeps its original type (a number stays a number, an object stays an object, an array stays an array).
  • When it is mixed with literal text, the result is string concatenation.
"{ /payload/fields/count }"                 // a number value stays a number (e.g. 42)
"{ /payload/fields/tags }"                  // an array stays an array
"page-{ /payload/fields/n }-of-10"          // string concatenation → "page-42-of-10"
"Bearer { /payload/fields/token }"          // string concatenation → "Bearer abc123"

Missing values and escaping

  • When the path is absent or the value is empty, a single pointer becomes null and a mixed template becomes an empty string.
  • To use { as a literal, escape it as \{ (that position is not interpreted as a pointer).

Context roots: where values come from

The top-level segment of a { /pointer } is one of the five below.

RootContents
/payloadThe JSON payload (the input) passed on the call. Example: { /payload/fields/email }
/headersThe request HTTP headers passed on the call. Keys are lowercase, with a single value per name. Example: { /headers/authorization }
/<name>The result of an earlier statement that carries that name. Example: { /order/sys/id }
/vars/<name>A script-scoped mutable variable declared with SetVar. Example: { /vars/total }
/errorUsed only inside a Try block's catch. The caught error, { message, statement }. Example: { /error/message }

The shape of a statement's result

The result shape of a statement that carries a name differs by type.

StatementResult shapeReference example
Http{ status, body }{ /resp/status }, { /resp/body/choices/0/message/content }
ResourceCreate, ResourceRead (single), ResourceFind (single)The resource itself{ /post/sys/id }, { /post/fields/title/en-US }
ResourcePageRead{ items, next }{ /page/items/0/sys/id }, { /page/next }
  • ResourceFind binds null when there is no match. Branch on existence with { "==": [ "{ /found }", null ] }.
  • ResourceRead (single) is an error when the target does not exist (you can handle it with Try). See Reading resources in the Statement catalog for details.

Operations and conditions: JsonLogic

When you need a calculation or a condition, use an operator object from the jsonlogic.com specification.

  • Data access is standardized on { /ptr } references, not the vanilla var (dot-path). The engine resolves the operands' pointers first, then applies the operator.
  • When a single-key object's key is a registered operator, it is treated as an operation; otherwise it is treated as a plain object.

Operator table

CategoryOperatorMeaning and example
Conditionif (alias ?:){ "if": [cond, then, cond2, then2, …, default] }. The value of the first true condition, or the last default if none.
Logicand, orShort-circuit evaluation. and returns the first falsy operand (or the last), or the first truthy operand (or the last), as a value.
Logic! (not), !! (to-bool){ "!": x } negates truthiness; { "!!": x } gives the truthiness. !! is often used for existence checks.
Equality==, !=Loose comparison (compares after numeric coercion; "1"==1 is true).
Equality===, !==Strict comparison (type included).
Comparison<, <=, >, >=Chainable: { "<": [1,2,3] } means 1<2 AND 2<3. If a value cannot be made numeric (NaN), it is false.
Arithmetic+The sum of all operands.
Arithmetic-With one operand, negation; with two, subtraction.
Arithmetic*, /, %Multiplication, division, remainder.
Aggregationmin, maxThe minimum and maximum of the operands.
StringcatConcatenates all operands as strings.
Membershipin{ "in": [needle, haystack] }. If the haystack is a string, substring; if a collection, element membership.
ArraymergeFlattens multiple arrays or values into a single array (used for accumulation).

The array iteration operators (map, filter, reduce, all, some, none) are not supported. Script iterates over an array with Loop (see Loop in the Statement catalog).

Numeric coercion and examples

The numeric coercion rules are as follows. A number is left as-is, true becomes 1, false becomes 0, a string is parsed (if it cannot be parsed, the calculation produces a failure value), and null becomes 0.

{ "-":  [ "{ /wallet/fields/balance/en-US }", "{ /payload/fields/cost }" ] }   // balance - cost
{ "<":  [ "{ /wallet/fields/balance/en-US }", "{ /payload/fields/cost }" ] }   // balance < cost → boolean
{ "and": [ { "<": [ "{ /a/body/risk }", 0.5 ] }, { ">=": [ "{ /b/body/score }", 700 ] } ] }
{ "cat": [ "id-", "{ /payload/sys/id }" ] }                                    // "id-<uuid>"
{ "!!": "{ /found/sys/id }" }                                                  // true if it exists
{ "merge": [ "{ /vars/ids }", [ "{ /row/sys/id }" ] ] }                        // accumulate one element into the array
{ "if": [ "{ /page/next }", "{ /page/next }", "END" ] }                        // next if present, otherwise "END"

Truthiness

if, and, or, !, !!, along with If.condition and Loop.while, decide true and false by the following rules.

  • falsy: null, false, the number 0, the empty string "", and an empty collection (an empty array).
  • truthy: everything else (nonzero numbers, non-empty strings and arrays, and every object).

Keys can be references too

The keys of a map such as fields also support { /ptr } references. The key is resolved at runtime.

"fields": { "{ /payload/fields/fieldName }": { "en-US": "{ /payload/fields/fieldValue }" } }

If two keys resolve to the same value, they collide and it is an engine error.

Locale map (LocaleValueMap): rules specific to Content and Media

In WEEGLOO, each field of a Content or Media is not a value but a per-locale map (for example, balance is { "en-US": 1, "ko-KR": 10 }). So you must handle the locale together when you read and write. For Media as well, title and description (scalars) and file (the ingest directive) are locale maps. JSON that is not a Content or Media, such as /payload or an HTTP response, is unaffected by this rule (it keeps the structure the schema defines, and a scalar stays a scalar).

Reading

  • To get a scalar, specify down to the locale: { /<name>/fields/<field>/<locale> } (for example, { /post/fields/title/en-US }).
  • Without a locale, { /<name>/fields/<field> } returns the whole locale-map object.
  • A localized:false field lives only in the default-locale bucket, so read it with that default-locale code.

Writing (the fields of ResourceCreate, ResourceUpdate, and ResourcePatch)

The value is a locale map, { "<locale>": <scalar value expression> }. It is symmetric with reading.

"fields": {
  "title":  { "en-US": "Hello", "ko-KR": "안녕" },   // list buckets for multiple locales
  "status": { "en-US": "paid" }
}
  • ResourceCreate must include the space default-locale bucket in every field it populates (the default-locale rule).
  • ResourceUpdate is a full replacement. Fields and locales absent from fields are removed (including the file).
  • ResourcePatch updates only the specified fields and buckets (other fields and locales are kept).
  • Delete with a literal null: when the value is a literal null, that (field, locale) bucket is deleted (the standard way to clear a specific locale in a Patch). "" (an empty string) is not a deletion but sets an empty value. When a value expression ({ /ptr }) evaluates to null at runtime, it is not a deletion but an error (a missing payload is not silently swallowed). Only a literal null deletes.
  • Media file: the value is not a scalar but an ingest directive, { "source": …, "encoding": "url"|"base64" }. A write that includes a file is Async-only (see ResourceCreate in the Statement catalog).
  • Put a localized:false field only in the default-locale bucket.
  • A locale code (a map key) can also be a { /ptr } reference (see Keys can be references too above). Use this to build a dynamic locale.

The locale convenience field

When you give locale to ResourceCreate, ResourceUpdate, or ResourcePatch, the engine automatically wraps each value in fields in a { <locale>: value } bucket. In other words, you only need to give scalars.

// the two below are equivalent
{ "type": "ResourceCreate", "resource": "Content", "contentType": { "sys": { "id": "ct_post" } },
  "locale": "en-US", "fields": { "title": "Hello" } }
 
{ "type": "ResourceCreate", "resource": "Content", "contentType": { "sys": { "id": "ct_post" } },
  "fields": { "title": { "en-US": "Hello" } } }

If you give locale while the value already nests a locale map ({ "en-US": … }), it becomes doubly nested as { <locale>: { "en-US": … } } (an authoring mistake). Standardize on one style: with locale, use scalars only; without it, use explicit locale maps only.

Locales in where and order

  • In where and order, for fields.X the engine automatically applies the space default locale (the same as a CMA query).
  • To target a specific locale, state it explicitly as fields.X.<locale>.
"where": { "fields.slug": { "eq": "{ /payload/fields/slug }" } }   // default-locale slug
"where": { "fields.title.ko-KR": { "prefix": "안" } }              // specific locale