Skip to main content
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:
SyntaxReads from
.fieldNameA field in the current stage payload
state.dot.notationA 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

FunctionReturnsDescription
uuid()stringA random UUID v4
now()stringCurrent timestamp as an ISO-8601 string
nows()integerCurrent timestamp in whole seconds since epoch
nowms()integerCurrent timestamp in milliseconds since epoch
int(min, max)integerA random integer between min and max inclusive
long(min, max)longA random long integer between min and max inclusive
string()stringA random UUID string
bit()booleanA random true or false
bool()booleanA random true or false
boolean()booleanAlias for bool()
choice(v1, v2, ...)anyA uniformly random pick from the supplied values
choiceWeighted(v1:w1, v2:w2, ...)anyA weighted random pick; weights are relative integers
add(a, b)numbera + b
sub(a, b)numbera - b
percent(value, total)number(value / total) * 100
lt(a, b)booleantrue if a < b
lte(a, b)booleantrue if a <= b
gt(a, b)booleantrue if a > b
gte(a, b)booleantrue if a >= b
eq(a, b)booleantrue if a == b
neq(a, b)booleantrue 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" }.