Skip to main content

Action Entity

The Action entity executes custom JavaScript code for data transformation, external integrations, and side effects.

Purpose

Action entities provide:

  1. Data Transformation — Reshape message data
  2. External Calls — Call webhooks, APIs, services
  3. Variable Setting — Prepare data for downstream entities
  4. Side Effects — Post to social media, send notifications

Configuration

Available Fields

FieldDescription
NameDisplay name (e.g., "Format Social Post")
DescriptionOptional documentation
ScriptJavaScript code to execute
ArgumentsVariables injected before execution
note

Action entities do not have conditions, models, or prompts. They're purely for script execution.

Script Execution

Action scripts run in an isolated V8 context with full access to:

// Incoming message
const eventType = message.type;
const payload = message.data;

// Environment variables
const apiKey = MY_API_KEY;
const serverUrl = SERVICE_URL;

// Entity arguments (injected)
const formattedMessage = socialContent; // From arguments

// Built-in functions
print('Processing:', eventType);

// Previous results from upstream entities
const aiResponse = latestPromptResponse();

// Set data for downstream entities
var processedData = {
formatted: true,
content: aiResponse,
timestamp: new Date().toISOString()
};

Available in Script Context

VariableDescription
messageThe incoming message payload
__variables__Array of environment variables
__variablesByName__Map of variable name → value
{VARIABLE_NAME}Each variable as top-level var
ArgumentsEach argument as top-level var
latestPromptResponse()Get last AI response
getPromptResponse(index)Get AI response by index

Built-in Functions

// Debugging
print('Debug info:', variable);
print('Message:', JSON.stringify(message, null, 2));

// AI/LLM (if you need manual calls)
const response = await promptCallToken(token, prompt, url);
const response = await promptCallKeys(key, secret, prompt, url);

// Social media posting
await postToMastodon(serverUrl, token, status);
await postLatestPromptToMastodon();

// Condition evaluation (if needed)
const matches = evaluateCondition(conditionTree, data);

Execution Flow

The ActionEvaluator processes Action entities:

┌─────────────────────────────────────────────────────────────┐
│ ActionEvaluator.evaluate() │
├─────────────────────────────────────────────────────────────┤
│ 1. Inject message into V8 context │
│ 2. Inject arguments (no model/prompt for Actions) │
│ 3. Run script │
│ 4. Capture context variables │
│ 5. Return { action: 'process_children', children } │
└─────────────────────────────────────────────────────────────┘
tip

Action entities always continue to children, even if the script fails. Errors are logged but don't halt the workflow.

Examples

Example 1: Data Transformation

Transform message data for downstream processing:

// Extract and normalize user data
var userData = {
id: message.user.id,
name: message.user.displayName || message.user.email.split('@')[0],
email: message.user.email.toLowerCase(),
createdAt: new Date(message.timestamp).toISOString(),
source: message.source || 'unknown'
};

print('Normalized user:', JSON.stringify(userData));

Example 2: Format for Social Media

Prepare content for social posting:

// Get AI-generated content from upstream Prompt entity
const aiContent = latestPromptResponse();

// Format for Mastodon (280 char limit)
var socialContent = aiContent;

if (socialContent.length > 280) {
socialContent = socialContent.substring(0, 277) + '...';
}

// Add hashtags
const hashtags = '#welcome #newuser';
if (socialContent.length + hashtags.length + 1 <= 280) {
socialContent = socialContent + ' ' + hashtags;
}

print('Social content ready:', socialContent.length, 'chars');

Example 3: Post to Mastodon

Execute social media posting:

// Get server URL and token from environment variables
const mastodonServer = MASTODON_SERVER_URL;
const accessToken = MASTODON_ACCESS_TOKEN;

// Get content from upstream
const status = latestPromptResponse();

if (status && mastodonServer && accessToken) {
try {
await postToMastodon(mastodonServer, accessToken, status);
print('Posted to Mastodon successfully');
var postSuccess = true;
} catch (error) {
print('Mastodon post failed:', error.message);
var postSuccess = false;
}
} else {
print('Missing Mastodon configuration');
var postSuccess = false;
}

Example 4: Webhook Call (via Arguments)

Configure webhooks via arguments for flexibility:

Arguments:

[
{
"argumentName": "webhookUrl",
"argumentValue": "{{WEBHOOK_URL}}",
"argumentDescription": "Destination webhook URL"
},
{
"argumentName": "eventData",
"argumentValue": "{{message}}",
"argumentDescription": "Message payload to send"
}
]

Script:

// webhookUrl and eventData are injected from arguments
print('Sending to webhook:', webhookUrl);

// Note: Direct fetch not available in V8 isolate
// Store data for external processing or use built-in functions
var webhookPayload = {
url: webhookUrl,
data: eventData,
timestamp: new Date().toISOString()
};

// This will be available in the Result context
print('Webhook payload prepared');

Example 5: Conditional Logic

Complex branching within a script:

const eventType = message.type;
const userTier = message.user.tier || 'free';

// Different processing based on event type
switch (eventType) {
case 'order.created':
var notificationType = 'order_confirmation';
var priority = userTier === 'premium' ? 'high' : 'normal';
break;

case 'order.shipped':
var notificationType = 'shipping_update';
var priority = 'normal';
break;

case 'order.delivered':
var notificationType = 'delivery_confirmation';
var priority = 'low';
break;

default:
var notificationType = 'general';
var priority = 'low';
}

print('Notification type:', notificationType, 'Priority:', priority);

Example 6: Error Handling

Graceful error handling:

try {
// Attempt to parse nested JSON
const rawData = message.payload;
var parsedData = JSON.parse(rawData);

if (!parsedData.requiredField) {
throw new Error('Missing required field');
}

var processingSuccess = true;
var errorMessage = null;

} catch (error) {
print('Processing error:', error.message);

var processingSuccess = false;
var errorMessage = error.message;
var parsedData = null;
}

// Downstream entities can check processingSuccess

Example 7: Aggregation

Combine data from multiple sources:

// Combine message data with AI response
const originalMessage = message;
const aiAnalysis = latestPromptResponse();

var aggregatedResult = {
// Original message metadata
messageId: originalMessage.id,
timestamp: originalMessage.timestamp,
source: originalMessage.source,

// Processed content
content: originalMessage.content,

// AI-generated analysis
analysis: aiAnalysis,

// Computed fields
processedAt: new Date().toISOString(),
wordCount: originalMessage.content.split(' ').length,
hasAiAnalysis: !!aiAnalysis
};

print('Aggregated result keys:', Object.keys(aggregatedResult).join(', '));

Arguments with Handlebar References

Arguments support dynamic values via handlebar syntax:

PatternDescription
{{message.user.id}}Extract from message
{{___result___}}Previous entity result
{{latestPromptResponse()}}AI response
{{VARIABLE_NAME}}Environment variable

Example Configuration:

{
"arguments": [
{
"argumentName": "userId",
"argumentValue": "{{message.user.id}}",
"argumentDescription": "User's unique ID"
},
{
"argumentName": "aiContent",
"argumentValue": "{{latestPromptResponse()}}",
"argumentDescription": "Generated AI content"
},
{
"argumentName": "apiEndpoint",
"argumentValue": "{{API_ENDPOINT}}",
"argumentDescription": "Target API from env var"
}
]
}

Best Practices

Script Organization

  1. Declare variables at top — Make them available downstream
  2. Use meaningful namesuserData not d
  3. Add print statements — Help with debugging
  4. Handle edge cases — Check for null/undefined

Performance

  1. Keep scripts focused — One responsibility per Action
  2. Avoid loops over large arrays — Can hit timeout
  3. Don't block on external calls — V8 isolate has timeout

Variable Naming

// Good - descriptive names with var for downstream access
var processedUser = { ... };
var socialContent = '...';
var webhookPayload = { ... };

// Avoid - const doesn't persist to downstream
const result = { ... }; // Not accessible in next entity

Error Recovery

// Always have fallbacks
var result = someOperation() || defaultValue;

// Try-catch for risky operations
try {
var parsed = JSON.parse(maybeJson);
} catch {
var parsed = { raw: maybeJson };
}