In this guide, you'll learn how to use the powerful JavaScript API available within Code nodes to programmatically interact with RightMessage segmentation data, visitor context, and ESP/CRM integrations.

Overview

Code nodes in the Flow Builder expose a special RM object that gives you programmatic access to visitor data, segmentation, custom fields, tags, and more. This API allows power users to:

  • Display personalized content based on segmentation data

  • Transform and manipulate visitor data

  • Integrate with third-party analytics and tracking tools

  • Implement custom business logic within flows

  • Sync data bidirectionally with your ESP/CRM

Who this is for: The Code Node API is designed for power users and developers comfortable with JavaScript. You'll need edit access to flows and basic understanding of JavaScript syntax.

Prerequisites

Before using the Code Node API, ensure you have:

  • Edit access to flows in RightMessage

  • Basic JavaScript knowledge

  • An active ESP/CRM integration (required for custom field and tag operations)

  • Understanding of your segmentation structure (dimensions and segments)

How Code nodes work

When a Code node executes in your Flow:

  1. RightMessage creates the RM API object with access to current visitor context

  2. Your JavaScript code runs with the RM object available

  3. If your code returns a string or DOM element, it displays in the widget

  4. Any data changes sync back to your ESP/CRM automatically

Pro tip: Use your browser's developer console to debug Code nodes. The API logs helpful messages prefixed with [RM] to assist with troubleshooting.

API Methods

Variables

Variables let you store temporary data during a visitor's session or for longer periods.

RM.getVariable(name)

Retrieve a variable value by name.

// Get a stored variable
const visitCount = RM.getVariable('visit_count');

// Returns null if not found or expired
if (visitCount === null) {
  console.log('Variable not found or expired');
}

Parameters:

  • name (string) - The variable name

Returns: The variable value, or null if not found or expired

RM.setVariable(name, value, options)

Store a variable with optional expiration.

// Store a variable that expires in 7 days
RM.setVariable('last_quiz_completed', 'onboarding-quiz', {
  expiresType: 'duration',
  expiresDuration: 7,
  expiresUnit: 'days'
});

// Store a variable without expiration
RM.setVariable('user_preference', 'dark_mode');

Parameters:

  • name (string) - The variable name

  • value (any) - The value to store

  • options (object, optional) - Configuration object:

    • expiresType - Set to 'duration' to enable expiration

    • expiresDuration - Number of units until expiration

    • expiresUnit - 'minutes', 'hours', 'days', or 'weeks'

Scores

Scores allow you to track numerical values like engagement level, product interest, or lead quality.

RM.getScore(scoreName)

Retrieve the current value of a score.

// Get current engagement score
const engagementScore = RM.getScore('engagement');

console.log(`Current engagement: ${engagementScore}`);
// Returns 0 if score doesn't exist

Parameters:

  • scoreName (string) - The score name

Returns: The score value (number), or 0 if not found

RM.adjustScore(scoreName, amount)

Increase or decrease a score by a specific amount.

// Increase engagement score
RM.adjustScore('engagement', 10);

// Decrease score (use negative number)
RM.adjustScore('engagement', -5);

// Track product interest
RM.adjustScore('product_interest_premium', 15);

Parameters:

  • scoreName (string) - The score name

  • amount (number) - Amount to adjust (positive or negative)

Use case: Adjust scores based on page visits (e.g., +10 for visiting pricing page) or content engagement to build dynamic lead scoring systems.

Questions and Inputs

Access and modify answers to questions asked in your flows.

RM.getInput(questionIdOrText)

Get a visitor's answer to a specific question.

// Get by question ID
const role = RM.getInput('qst_abc123');

// Get by question text
const businessType = RM.getInput('What type of business do you run?');

console.log(`User's role: ${role}`);

Parameters:

  • questionIdOrText (string) - Question ID or the exact question text

Returns: The answer value, or undefined if not answered

RM.setInput(questionIdOrText, value)

Set or override a visitor's answer to a question.

// Set an answer programmatically
RM.setInput('qst_abc123', 'Enterprise');

// Override based on logic
if (employeeCount > 50) {
  RM.setInput('Company size', 'Large');
}

Parameters:

  • questionIdOrText (string) - Question ID or question text

  • value (string) - The answer value to set

Segmentation

Programmatically manage visitor segments.

RM.getSegments()

Get an array of all currently active segment IDs for the visitor.

// Get all active segments
const activeSegments = RM.getSegments();

console.log('Active segments:', activeSegments);
// Example output: ['seg_abc123', 'seg_def456']

// Check if visitor is in a specific segment
if (activeSegments.includes('seg_premium_customer')) {
  // Show premium content
}

Returns: Array of segment IDs

RM.addSegment(dimensionOrSegmentId, segmentIdentifier)

Assign a visitor to a segment.

// Method 1: Using segment ID directly
RM.addSegment('seg_abc123');

// Method 2: Using dimension name + segment name
RM.addSegment('Job Role', 'Marketing Manager');

// Method 3: Using dimension ID + segment ID
RM.addSegment('dim_xyz789', 'seg_abc123');

Parameters:

  • dimensionOrSegmentId (string) - Segment ID (when used alone) or dimension name/ID (when used with second parameter)

  • segmentIdentifier (string, optional) - Segment name or ID

Ambiguous names: If segment names aren't unique across dimensions, specify the dimension name to avoid ambiguity. The API will warn you if multiple segments are found.

RM.removeSegment(dimensionOrSegmentId, segmentIdentifier)

Remove a visitor from a segment.

// Remove by segment ID
RM.removeSegment('seg_trial_user');

// Remove by dimension + segment name
RM.removeSegment('Subscription Status', 'Trial');

Parameters:

  • dimensionOrSegmentId (string) - Segment ID or dimension name/ID

  • segmentIdentifier (string, optional) - Segment name or ID

Custom Fields (ESP/CRM Integration)

Read and write custom field data from your connected email platform or CRM.

Integration required: Custom field operations require an active ESP/CRM integration. Without one, these methods will return errors in the console.

RM.getCustomField(fieldIdOrToken, parseJson)

Read a custom field value from your ESP/CRM.

// Get by field ID
const companyName = RM.getCustomField('fld_company');

// Get by Liquid token
const industry = RM.getCustomField('{{ subscriber.industry }}');

// Parse JSON data automatically
const preferences = RM.getCustomField('user_preferences', true);
console.log(preferences.newsletter); // Access parsed object

Parameters:

  • fieldIdOrToken (string) - Custom field ID or Liquid templating token

  • parseJson (boolean, optional) - If true, attempts to parse the value as JSON

Returns: The field value (string or parsed object)

RM.setCustomField(fieldIdOrToken, value)

Write a custom field value and sync it to your ESP/CRM.

// Set a custom field
RM.setCustomField('fld_last_interaction', new Date().toISOString());

// Update using token
RM.setCustomField('{{ subscriber.lifecycle_stage }}', 'Customer');

// Store JSON data
const userData = { plan: 'premium', signup_date: '2025-01-15' };
RM.setCustomField('user_metadata', JSON.stringify(userData));

Parameters:

  • fieldIdOrToken (string) - Custom field ID or Liquid token

  • value (string) - The value to set

Real-time sync: Custom field updates sync automatically to your ESP/CRM in real-time, keeping visitor data consistent across platforms.

Tags (ESP/CRM Integration)

Manage tags on identified visitors in your ESP/CRM.

Identification required: Tag operations only work for identified visitors (those with an email address or subscriber ID). Anonymous visitors cannot have tags applied.

RM.addTag(tagIdOrName)

Apply a tag to the identified visitor.

// Add by tag ID (recommended)
RM.addTag('tag_completed_onboarding');

// Add by tag name (ESP-specific)
RM.addTag('VIP Customer');

Parameters:

  • tagIdOrName (string) - Tag ID or tag name

Best practice: Use tag IDs rather than names for more reliable lookups across different ESP platforms.

RM.removeTag(tagIdOrName)

Remove a tag from the identified visitor.

// Remove by tag ID
RM.removeTag('tag_trial_user');

// Remove by name
RM.removeTag('Trial User');

Parameters:

  • tagIdOrName (string) - Tag ID or tag name

RM.hasTag(tagId)

Check if a visitor has a specific tag.

// Check for a tag
if (RM.hasTag('tag_premium_member')) {
  console.log('Visitor is a premium member');
  // Display premium content
}

Parameters:

  • tagId (string) - Tag ID to check

Returns: true if visitor has the tag, false otherwise

Utility and Debugging

Helper methods to inspect and understand visitor data.

RM.getVisitorContext()

Get a clean, serializable copy of the current visitor context.

// Get full visitor context
const context = RM.getVisitorContext();

console.log('Visitor context:', context);
console.log('Current segments:', context.segments);
console.log('Custom fields:', context.customFields);

Returns: Immutable object containing visitor data (frozen to prevent modification)

The returned object filters out circular references and non-serializable values, making it safe for logging and debugging.

RM.listSegments()

Get all available dimensions and their segments with active status.

// List all segments
const allSegments = RM.listSegments();

console.log(allSegments);
// Example output:
// {
//   "Job Role": [
//     { id: "seg_abc", name: "Marketing Manager", isActive: true },
//     { id: "seg_def", name: "Developer", isActive: false }
//   ],
//   "Company Size": [...]
// }

// Check which segments are active in a dimension
const jobRoleSegments = allSegments['Job Role'];
const activeRole = jobRoleSegments.find(seg => seg.isActive);
console.log('Active job role:', activeRole.name);

Returns: Object with dimension names as keys and arrays of segment objects as values

RM.listQuestions()

Get all questions in the flow with their answer options.

// List all questions
const questions = RM.listQuestions();

console.log(questions);
// Example output:
// [
//   {
//     id: "qst_abc123",
//     text: "What's your role?",
//     options: [
//       { id: "opt_1", label: "Marketing", segmentId: "seg_marketing" },
//       { id: "opt_2", label: "Sales", segmentId: "seg_sales" }
//     ]
//   }
// ]

Returns: Array of question objects with their options

RM.listCustomFields()

Get all available custom fields from your ESP/CRM integration.

// List all custom fields
const fields = RM.listCustomFields();

console.log(fields);
// Example output:
// [
//   { id: "fld_company", token: "{{ subscriber.company }}", value: "Acme Corp" },
//   { id: "fld_plan", token: "{{ subscriber.plan }}", value: "premium" }
// ]

// Find a specific field
const companyField = fields.find(f => f.id === 'fld_company');
console.log('Company name:', companyField.value);

Returns: Array of custom field objects with ID, token, and current value

Common Use Cases

Third-party analytics integration

Send segmentation data to analytics platforms like Google Analytics, Mixpanel, or PostHog.

// Get visitor segments
const segments = RM.getSegments();
const segmentList = RM.listSegments();

// Build readable segment names
const segmentNames = segments.map(segId => {
  for (const [dimension, segs] of Object.entries(segmentList)) {
    const match = segs.find(s => s.id === segId);
    if (match) return `${dimension}: ${match.name}`;
  }
  return segId;
});

// Send to PostHog
if (typeof posthog !== 'undefined') {
  posthog.identify(visitorEmail, {
    segments: segmentNames,
    engagement_score: RM.getScore('engagement')
  });
}

// Send to Google Analytics
if (typeof gtag !== 'undefined') {
  gtag('event', 'quiz_completed', {
    'user_segments': segmentNames.join(', '),
    'event_category': 'Engagement'
  });
}

Conditional content display

Return personalized HTML based on visitor segmentation.

// Get visitor's industry segment
const segments = RM.getSegments();
const segmentData = RM.listSegments();

// Find active industry
let industry = 'General';
if (segmentData['Industry']) {
  const active = segmentData['Industry'].find(s => s.isActive);
  if (active) industry = active.name;
}

// Return personalized content
if (industry === 'E-commerce') {
  return `
    <h2>Boost Your Online Store Sales</h2>
    <p>Discover strategies specifically designed for e-commerce businesses...</p>
    <a href="/ecommerce-guide" class="btn">Get the Guide</a>
  `;
} else if (industry === 'SaaS') {
  return `
    <h2>Scale Your SaaS Business</h2>
    <p>Learn how successful SaaS companies use segmentation...</p>
    <a href="/saas-guide" class="btn">Get the Guide</a>
  `;
} else {
  return `
    <h2>Personalization for Your Business</h2>
    <p>Let us show you how to get started...</p>
    <a href="/get-started" class="btn">Learn More</a>
  `;
}

Dynamic lead scoring

Adjust scores based on visitor behavior and sync to your CRM.

// Increase score for high-value page visits
const currentPage = window.location.pathname;

if (currentPage.includes('/pricing')) {
  RM.adjustScore('purchase_intent', 15);
  RM.setCustomField('last_pricing_visit', new Date().toISOString());
}

if (currentPage.includes('/enterprise')) {
  RM.adjustScore('deal_size', 25);
  RM.addTag('enterprise_interested');
}

// Get final score and update CRM
const totalScore = RM.getScore('purchase_intent') + RM.getScore('deal_size');

if (totalScore > 50) {
  RM.setCustomField('lead_score', totalScore.toString());
  RM.addTag('hot_lead');
}

Data transformation and enrichment

Parse and transform ESP/CRM data for use in flows.

// Get JSON data from custom field
const rawPreferences = RM.getCustomField('user_preferences', true);

// Transform and use the data
if (rawPreferences && rawPreferences.newsletter_frequency) {
  const freq = rawPreferences.newsletter_frequency;
  
  // Set a segment based on preference
  if (freq === 'daily') {
    RM.addSegment('Engagement Level', 'Highly Engaged');
  } else if (freq === 'weekly') {
    RM.addSegment('Engagement Level', 'Moderately Engaged');
  }
}

// Enrich data back to ESP
const visits = parseInt(RM.getVariable('total_visits') || '0');
RM.setVariable('total_visits', (visits + 1).toString());

if (visits > 10) {
  RM.setCustomField('visitor_type', 'Frequent');
  RM.adjustScore('engagement', 5);
}

Troubleshooting

Code returns non-displayable value

Symptom: Your code runs but nothing displays in the widget.

Cause: Code returned a non-displayable value (number, boolean, plain object).

Solution: Return a string (HTML) or DOM element. Convert numeric results to strings or wrap in HTML:

// Wrong - returns number
return 42;

// Right - returns string
return '<p>Your score is 42</p>';

// Also right
const score = 42;
return `<div class="score">Score: ${score}</div>`;

Visitor not identified errors

Symptom: Console shows "Visitor not identified" when using tag operations.

Cause: Attempting tag operations on anonymous visitors who haven't provided an email address.

Solution: Check visitor identification before tag operations:

// Get visitor context to check identification
const context = RM.getVisitorContext();

if (context.email || context.subscriberId) {
  RM.addTag('completed_quiz');
} else {
  console.log('Visitor not identified - skipping tag operation');
}

Multiple segments found warnings

Symptom: Console warns "Multiple segments found" when adding/removing segments.

Cause: Segment names aren't unique across dimensions.

Solution: Specify the dimension to disambiguate:

// Ambiguous - if "Enterprise" exists in multiple dimensions
RM.addSegment('Enterprise');

// Clear - specifies the dimension
RM.addSegment('Company Size', 'Enterprise');

No integration found errors

Symptom: Console shows "No integration found" when using custom field operations.

Cause: No active ESP/CRM integration configured.

Solution: Configure an email platform integration in RightMessage settings before using custom field or tag operations.

Variable expiration not working

Symptom: Variables persist longer than expected.

Cause: Invalid expiration unit or duration format.

Solution: Use only supported units (minutes, hours, days, weeks):

// Wrong - invalid unit
RM.setVariable('temp', 'value', {
  expiresType: 'duration',
  expiresDuration: 1,
  expiresUnit: 'months' // Not supported
});

// Right - valid unit
RM.setVariable('temp', 'value', {
  expiresType: 'duration',
  expiresDuration: 30,
  expiresUnit: 'days'
});

Limitations

  • Tag operations require visitor identification: Anonymous visitors cannot have tags applied or removed

  • Custom field operations require active ESP/CRM integration: Will fail if no integration is configured

  • Segment lookup ambiguity: Segment names that aren't unique across dimensions require dimension specification

  • Data serialization limits: getVisitorContext() filters out circular references and non-serializable values

  • Return value restrictions: Only strings (HTML) and DOM elements can be displayed; other types are logged but not shown

Getting help

If you encounter issues with the Code Node API:

  1. Check your browser's developer console for [RM] prefixed log messages

  2. Use RM.listSegments(), RM.listQuestions(), and RM.listCustomFields() to inspect available data

  3. Verify your ESP/CRM integration is active and properly configured

  4. Test your code in small increments using console.log() for debugging

  5. Contact support with console logs and your code snippet for personalized assistance

Was this helpful?