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
Pre-ScriptOptional JavaScript run before arguments, condition, and downstream steps
Post-ScriptOptional JavaScript run after prompt/model handling (if any), before branching
ArgumentsVariables injected after pre-script, before condition evaluation
note

The Action entity type is usually configured without condition, model, or prompt fields; the unified pipeline then runs preScript → arguments → condition (defaults to true) → postScript → children.

Script Execution

Action preScript and postScript run in an isolated V8 context (same unified pipeline as other entity types) with full access to:

// Incoming message (fields at top-level in V8)
const eventType = type;
const payload = 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
(top-level fields)Incoming message properties (type, organizationId, content, payload, user, …) are available directly in script scope
__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('Snapshot:', JSON.stringify({ type, organizationId, payload, content }));

// 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

Actions use UnifiedEntityEvaluator like every other entity. With no condition, model, or prompt configured, the pipeline collapses to message injection, optional preScript, arguments, condition (defaults true), skip prompt/model, optional postScript, context capture, then continue to children.

┌─────────────────────────────────────────────────────────────┐
│ UnifiedEntityEvaluator (Action node) │
├─────────────────────────────────────────────────────────────┤
│ 1. Inject message │
│ 2. Run preScript (if any) │
│ 3. Inject arguments (if any) │
│ 4. Evaluate condition (if any; else true) │
│ 5. Prompt + model (skipped for typical Actions) │
│ 6. Run postScript (if any) │
│ 7. Capture context │
│ 8. Branching → process_children │
└─────────────────────────────────────────────────────────────┘
tip

If preScript or postScript throws or times out, the consumer treats the entity as failed and stops that workflow path. Use try/catch inside scripts when you need to continue despite errors.

Examples

Example 1: Data Transformation

Transform message data for downstream processing:

// Extract and normalize user data
var userData = {
id: user.id,
name: user.displayName || user.email.split('@')[0],
email: user.email.toLowerCase(),
createdAt: new Date(timestamp).toISOString(),
source: 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"
}
]

Post-Script (or pre-script if no AI upstream):

// 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 = type;
const userTier = 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 = 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 aiAnalysis = latestPromptResponse();

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

// Processed content
content: content,

// AI-generated analysis
analysis: aiAnalysis,

// Computed fields
processedAt: new Date().toISOString(),
wordCount: 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
{{user.id}}Extract from message
{{___result___}}Previous entity result
{{latestPromptResponse()}}AI response
{{VARIABLE_NAME}}Environment variable

Example Configuration:

{
"arguments": [
{
"argumentName": "userId",
"argumentValue": "{{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. Choose pre vs post — Use preScript before the condition; postScript when you need latestPromptResponse() or final ___result___
  2. Declare variables at top — Make them available downstream
  3. Use meaningful namesuserData not d
  4. Add print statements — Help with debugging
  5. 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 };
}