Payload and State

Every workflow instance carries a payload—a Map<String, Object> that persists durably across all steps. The triggering event’s fields become the initial payload, and you can read, modify, and pass it to steps throughout the workflow.

Reading payload

When the OrderPlaced event triggers our workflow, its fields (orderId, customerId, email, amount) automatically become the initial payload:

var payload = ctx.workflowPayload(); (1)
var orderId = payload.get("orderId");
var email = payload.get("email");
1 Returns the current workflow payload as a Map<String, Object>. This always reflects the latest state, including any modifications made by previous steps.

Setting payload

After receiving the payment confirmation, we want to store the transaction details so later steps can access them:

var confirmation = ctx.awaitEvent("awaitPayment",
        PaymentConfirmed.class,
        associate(payloadProperty("orderId"),
                  equalsTo(ctx.workflowPayload().get("orderId"))),
        Duration.ofMinutes(15));

ctx.setPayload("storePaymentDetails", confirmation); (1)
1 Merges the PaymentConfirmed fields (orderId, transactionId) into the workflow payload. After this call, ctx.workflowPayload() contains both the original OrderPlaced fields and the transactionId from the confirmation.

Every setPayload call is a named step. The engine records it as an event, so the payload state survives crashes and replays.

Passing payload to steps

You can pass the workflow payload as input to any step:

ctx.awaitExecute("shipOrder",
                 ctx.workflowPayload(), (1)
                 ShippingService::shipOrder,
                 Duration.ofMinutes(5),
                 defaults());
1 The shipping service receives the full payload—including the transactionId we stored in the previous step.

The Payload helper

The Payload class wraps the raw map with convenient accessor methods:

import static io.axoniq.workflow.dsl.api.Payload.payload;

var email = payload(ctx.workflowPayload()).get("email"); (1)
1 payload(…​) wraps a Map<String, Object> and provides type-safe get methods. You can also use payload(ctx, object) to convert any object into a payload map.

Payload reducers

Under the hood, each step has two payload reducers that control how data flows in and out:

Reducer Behavior Default

GLOBAL_ONLY

Step receives the workflow payload, ignoring local input

Default for awaitEvent

LOCAL_ONLY

Step receives only its own local input, ignoring workflow payload

Default parameter reducer for execute

COMBINE_GLOBAL_AND_LOCAL

Merges both—local values overwrite duplicates

Used by setPayload

For most workflows, the defaults work well. You’ll only need custom reducers for advanced payload merging scenarios. See Payload Reducers for details.

Durability

Every payload modification is recorded as an event. If the application crashes and replays, the engine restores the payload to the exact state it was in before the crash. You never need to manage state persistence manually.