Testing & Troubleshooting Velocity

Dynamic Content Preview

Before sending, preview how Velocity variables render with real data directly in the message editor.

Go to Messages → Messages, open the message, and click Additional settings → Configuring dynamic content.

Configuring dynamic content

Enter the parameter values as a flat JSON object where each key is a parameter name, for example {"PurchasePrice": 36.99, "currency": "USD"}, and click View message. If you paste the whole event request — with eventTypeKey and a params array — the variables in the preview stay empty.

If there are errors in the Velocity code, the preview will show an error description instead of the rendered message.

Configuring dynamic content panel with sample JSON parameters and an Invalid JSON format error outlined in red below the code

Testing with Real Order Data

If the message is triggered by an event, you can copy real event parameters directly from the interface.

Go to Triggers → Event history, click an event, and copy its parameters.

Event history

Paste the copied parameters into the Configure dynamic content field and click Preview message.

This is the easiest way to verify field names, array paths, and conditions before sending.

Checking Available Variable Context

Before debugging an expression, check which data source is actually available in the current context.

SourceAvailable inTypical access pattern
Contact fieldsMessages, workflows, segments${firstName}, ${email}
Event parametersOnly inside the workflow triggered by that event$deliveryMethod, $items[0].name
External source dataMessages or workflows where that source is connectedDepends on the source
Webhook response dataAfter the Webhook block is called in a workflowDepends on the response structure

If a value is not being substituted — check whether the expected source is available in the current context.

Case Sensitivity Rules

Data typeCase sensitivity
Contact field namesCase-insensitive: ${firstName} = ${FIRSTNAME}
Event parameter namesCase-sensitive: $deliveryMethod$DeliveryMethod
External source field namesCase-sensitive: must match column headers or JSON keys exactly

Variable Not Substituted

Symptom: the message shows the literal text $firstName or $deliveryMethod instead of the actual value.

Causes and fixes:

CauseFix
Variable name doesn't match the field or parameter nameCheck spelling and case against the contact field list or event parameters
Value is missing and bare $var syntax is usedUse $!{var} or ${var|'fallback'}
Event parameter is not available in this contextVerify the message is sent through the correct workflow
External data source is not connected to the messageConnect the data source in the message settings

Directive Rendering Issues in Email

Symptom: #foreach or #if content renders as raw text, or the email layout breaks.

Cause: directives in email HTML must be wrapped in comments. Without the wrapper, the email client treats them as invalid markup.

Fix: wrap all directives in HTML comments:

<!--#foreach($item in $items)-->
  <tr>
    <td>$!item.name</td>
    <td>$!item.cost</td>
  </tr>
<!--#end-->

<!--#if($deliveryMethod == 'express')-->
  <p>Express delivery: 1–2 days.</p>
<!--#end-->

This rule applies only to email. In SMS, Mobile Push, Viber, Telegram, and App Inbox, directives are used without comments.

Directive Written Against Adjacent Text

Symptom: the message fails to render, or isn't sent at all.

Cause: when text follows #end or #else immediately, Velocity can read it as part of the directive name — for example, #endparagraph.

Fix: separate the directive with a space, or use its formal form: #{end}, #{else}. Check the plain-text version of the email as well: it has no HTML markup, so a directive easily ends up next to a word.

Common Mistakes with Arrays and Nested Objects

Index out of bounds

$items[3].name    ← error if the array has fewer than 4 elements

Use #foreach when the array size is not known in advance, or check the size first:

#if($items.size() > 3)
  $items[3].name
#end

Missing #end

Every #if and #foreach must be closed with #end. A missing #end breaks rendering for everything after it in the template.

<!--#if($deliveryMethod == 'express')-->
  <p>Express delivery.</p>
<!--#end-->    ← required

Wrong path to a nested value

$lastProductAdded.externalItemId    ← works if lastProductAdded is an object
$lastProductAdded[0].externalItemId ← use this if lastProductAdded is an array

If a nested value is not rendering, check whether the parent is an object or an array in the event payload.

Link Assembled with Velocity Isn't Wrapped in a Bulk Campaign

Symptom: in a bulk campaign, a link whose address is assembled in a #set directive is not wrapped into a tracking URL — clicks are not registered and UTM tags are not added.

Cause: a bulk campaign substitutes values from contact fields through personalization keys, and Velocity parameters appear in it only from a preprocessor or a data source. A #set built on a personalization key therefore has no Velocity context, and the address isn't recognized as a link. The same markup works in a triggered message.

Fix: in the link field, use a contact field variable instead of the Velocity expression. In the reported case this produced a wrapped link in the same campaign:

https://fitpulse.app/login?lang=%PERSONAL.LANGUAGE%

Next Steps



Did this page help you?