Cart Dynamic Fields
Each Commerce cart is a ticket actor, and any app can store state on it in the ticket's dynamic map using the setDynamicFields command. This is how PSP, shipping, tax and tracking apps carry their own data alongside the cart.
Most of that state is private to the app that wrote it. But some of it needs to survive cart completion and end up on the created order — an external payment reference, a shipping selection, a consent flag. Commerce projects those fields onto the order using key prefixes.
Key prefixes
When a cart completes, the cart's dynamic fields are projected to the order:
| Cart dynamic key | Lands on | As | Notes |
|---|---|---|---|
order:<key> | Order | <key> | Verbatim — vendor-prefix your key names |
delivery:<key> | The order's delivery | <key> | |
field:<key> | Order | cart:<key> | Storefront-facing; also returned in the rendered cart's fields |
| anything else | — | not projected | App-private cart state |
Everything without a recognised prefix stays on the cart and is never copied to the order. That is the default, and it's the right place for working state such as sync hashes, session pointers or cached external payloads.
order: and delivery:
These are for apps. Keys land on the target verbatim, with the prefix stripped:
cart.dynamic:
order:pspOrderId = 'ord_1a2b3c'
delivery:shippingOptionId = '4f2c...'
→ order.dynamic:
pspOrderId = 'ord_1a2b3c'
→ order.deliveries[0].dynamic:
shippingOptionId = '4f2c...'Because keys are not namespaced on arrival, the order's dynamic map is a shared space across every installed app. setDynamicFields merges per top-level key, so apps writing different keys never interfere — even concurrently. Prefix your key names with your app or vendor so two apps never fight over a generic name like reference or status.
field:
field:<key> is the storefront-facing namespace, written through the set-field ingress:
POST /ingress/commerce/carts/{cartId}/set-field/giftMessage
{ "value": "Happy birthday!" }These are returned to the storefront in the rendered cart's fields property, and projected onto the order under the cart: namespace (cart:giftMessage). The extra namespace is deliberate: this data originates from the client, so it is kept clearly separated from app-authored order fields.
Writing prefixed fields
Prefixed keys contain a :, which isn't valid in a bare identifier. Wrap the name in backticks and it behaves like any other record field:
from [{
type = 'setDynamicFields'
fields = {
`delivery:nShiftCheckoutOptionId` = optionId
`delivery:nShiftCheckoutPickupPointId` = pickupPointId
}
}]Backticked and plain field names mix freely in the same record literal, so a prefixed field never forces you to restructure the rest:
from [{
type = 'setDynamicFields'
fields = {
email = email
address = shippingAddress
`order:marketingConsent` = marketingConsent
}
}]Backticks aren't specific to dynamic fields
Backtick-quoting works anywhere Filtrera expects a symbol — record fields in literals and in type declarations, and member access on a record. Reach for it whenever a name isn't a plain identifier.
Map-literal syntax ('key' -> value) is equally valid, and is the natural choice when the key is computed rather than written out:
from [{
type = 'setDynamicFields'
fields = {
('delivery:' + fieldName) -> fieldValue
}
}]Write the fields before sending the complete message — cart-to-order reads the cart's state at completion time. See PSP Integration.
Reading fields back
Off the cart, while it is still open:
let optionId = input.ticket.dynamic->'delivery:nShiftCheckoutOptionId' match
(id: text) |> id
|> nothingOff the order or delivery, after completion — the prefix is gone:
param input: OnOrderValidate
from
input.order.deliveries
select d => d.dynamic->'nShiftCheckoutOptionId'UUIDs come back as text
Dynamic values are stored as JSON, which has no UUID type. A uuid written to a dynamic field is always read back as text, so match it as text and convert:
let sessionId = d.dynamic->'nShiftCheckoutSessionActorId' match
(id: text) |> id::uuid
|> nothingTo surface a field in the portal or the query graph, register it as a graph field in your app manifest with a dynamic-> source:
registryEntries:
- path: graph/ticket/cart/kustomOrderId
value:
source: "dynamic->'field:kustomOrderId'"See Also
- Cart Lifecycle — Cart states, mutation and completion
- PSP Integration — Completing a cart from a payment app
- Ticket
setDynamicFields— Command reference - Order
setOrderDynamicFields— Setting fields directly on an order - Order
setDeliveryDynamicFields— Setting fields directly on a delivery