Action Entity
The Action entity executes custom JavaScript code for data transformation, external integrations, and side effects.
Purpose
Action entities provide:
- Data Transformation — Reshape message data
- External Calls — Call webhooks, APIs, services
- Variable Setting — Prepare data for downstream entities
- Side Effects — Post to social media, send notifications
Configuration
Available Fields
| Field | Description |
|---|---|
| Name | Display name (e.g., "Format Social Post") |
| Description | Optional documentation |
| Script | JavaScript code to execute |
| Arguments | Variables injected before execution |
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
| Variable | Description |
|---|---|
message | The incoming message payload |
__variables__ | Array of environment variables |
__variablesByName__ | Map of variable name → value |
{VARIABLE_NAME} | Each variable as top-level var |
| Arguments | Each 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 } │
└─────────────────────────────────────────────────────────────┘
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:
| Pattern | Description |
|---|---|
{{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
- Declare variables at top — Make them available downstream
- Use meaningful names —
userDatanotd - Add print statements — Help with debugging
- Handle edge cases — Check for null/undefined
Performance
- Keep scripts focused — One responsibility per Action
- Avoid loops over large arrays — Can hit timeout
- 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 };
}
Related Topics
- Mastodon Scripts — Social media integration
- Prompt Scripts — AI/LLM functions
- Consumer Evaluator — ActionEvaluator implementation