Expressions are the mechanism Devset uses to make workflow payloads dynamic. Instead of hardcoding field values, you write expression objects that the engine resolves at runtime — generating random data, reading from the current event payload, pulling values out of workflow state, or computing results with built-in functions. Expressions are valid anywhere a value appears inside set, state, emit, repeatWhile, repeatUntil, and key.
Value Types
Devset recognises four distinct value types. You can mix them freely within any stage.
Plain Literal
A bare JSON value — string, number, boolean, or null. The engine uses it exactly as written.
{
"set": {
"status": "pending",
"retryCount": 0,
"isActive": true,
"cancelledAt": null
}
}
Function Call ($fn)
An object with a "$fn" key whose value is a function expression string. The engine calls the named built-in function and substitutes the return value.
{
"set": {
"id": { "$fn": "uuid()" },
"createdAt": { "$fn": "now()" },
"amount": { "$fn": "int(100, 9999)" },
"category": { "$fn": "choice(electronics, clothing, books)" }
}
}
Relative Field Reference ($ref)
An object with a "$ref" key that names a field in the current stage’s payload. Use this to read a value that was set earlier in the same stage’s set block, or carried over via source: "previous-stage".
{
"set": {
"orderId": { "$fn": "uuid()" },
"confirmationRef": { "$ref": "orderId" }
}
}
$ref reads from the payload object being assembled by the current stage. To read from workflow-scoped state, use $path instead.
Absolute Path Reference ($path)
An object with a "$path" key containing a dot-notation path into the workflow state. Use this to read any value that was written by a previous stage’s state block.
{
"set": {
"sequenceNumber": { "$path": "state.sequence.index" },
"sessionId": { "$path": "state.session.id" }
}
}
Conditional Values (when)
The when form lets you express if/else logic inline. The engine evaluates the when expression; if it is truthy, the value is used, otherwise default is used.
{
"set": {
"priority": {
"when": { "$fn": "gte(.amount, 1000)" },
"value": "high",
"default": "normal"
}
}
}
You can nest any expression type inside value and default:
{
"set": {
"discountCode": {
"when": { "$fn": "eq(.membershipTier, premium)" },
"value": { "$fn": "choice(SAVE10, SAVE20, SAVE30)" },
"default": null
}
}
}
Path References in Functions
Functions that take field arguments accept two path syntaxes:
| Syntax | Reads from |
|---|
.fieldName | A field in the current stage payload |
state.dot.notation | A path in the workflow state object |
Examples:
{ "$fn": "gt(.unitPrice, 0)" }
Reads the unitPrice field from the current payload and checks whether it is greater than zero.
{ "$fn": "lt(state.batch.count, 50)" }
Reads count from state.batch and checks whether it is less than 50.
{ "$fn": "add(state.sequence.index, 1)" }
Adds 1 to the index value stored in state.sequence.
Built-in Function Reference
| Function | Returns | Description |
|---|
uuid() | string | A random UUID v4 |
now() | string | Current timestamp as an ISO-8601 string |
nows() | integer | Current timestamp in whole seconds since epoch |
nowms() | integer | Current timestamp in milliseconds since epoch |
int(min, max) | integer | A random integer between min and max inclusive |
long(min, max) | long | A random long integer between min and max inclusive |
string() | string | A random UUID string |
bit() | boolean | A random true or false |
bool() | boolean | A random true or false |
boolean() | boolean | Alias for bool() |
choice(v1, v2, ...) | any | A uniformly random pick from the supplied values |
choiceWeighted(v1:w1, v2:w2, ...) | any | A weighted random pick; weights are relative integers |
add(a, b) | number | a + b |
sub(a, b) | number | a - b |
percent(value, total) | number | (value / total) * 100 |
lt(a, b) | boolean | true if a < b |
lte(a, b) | boolean | true if a <= b |
gt(a, b) | boolean | true if a > b |
gte(a, b) | boolean | true if a >= b |
eq(a, b) | boolean | true if a == b |
neq(a, b) | boolean | true if a != b |
Weighted choice example
{ "$fn": "choiceWeighted(completed:70, failed:20, pending:10)" }
This produces "completed" 70% of the time, "failed" 20% of the time, and "pending" 10% of the time.
Expressions in Context
The following workflow fragment shows all four value types and a conditional used together in a realistic stage:
{
"stage": "build-payment",
"event": "payment.initiated",
"source": "none",
"set": {
"paymentId": { "$fn": "uuid()" },
"initiatedAt": { "$fn": "now()" },
"amount": { "$fn": "int(500, 50000)" },
"currency": { "$fn": "choice(USD, EUR, GBP)" },
"method": { "$fn": "choiceWeighted(card:60, wallet:30, bank:10)" },
"sequenceNumber": { "$path": "state.sequence.index" },
"reference": { "$ref": "paymentId" },
"requiresReview": {
"when": { "$fn": "gte(.amount, 10000)" },
"value": true,
"default": false
},
"source": "api"
},
"state": {
"sequence.index": { "$fn": "add(state.sequence.index, 1)" }
},
"emit": { "$fn": "gt(.amount, 0)" }
}
paymentId and initiatedAt use $fn to generate random values at runtime.
amount, currency, and method use $fn with weighted or range functions.
sequenceNumber uses $path to read from workflow state.
reference uses $ref to copy paymentId into a second field within the same payload.
requiresReview uses a when conditional that checks the freshly assigned amount.
emit uses $fn to skip dispatch if amount is zero.
You can chain state mutations and field references across multiple stages. Write a value into state in one stage with state, then read it in a later stage with { "$path": "state.my.path" }.