Skip to main content
Version: 4.7.0

Custom Action

API reference for the Action() constructor, the wfa.actionStep() invocation helper for embedding OOB steps, and the wfa.assignActionOutputs() output helper.

Custom actions are reusable units of work composed of sequential OOB steps. They are invoked from a Flow or Subflow via wfa.action() -- the same helper used for built-in action.core.* actions.


Action()

Defines a reusable custom action with typed inputs, typed outputs, and a body of sequential wfa.actionStep() calls.

Signature

Action<TInputs, TOutputs>(
config: ActionConfig<TInputs, TOutputs>,
body?: (params: {
inputs: TInputs,
outputs: TOutputs
}) => void
): Action<TInputs, TOutputs>

Config Parameters

ParameterTypeDefaultRequiredDescription
$idstring / Now.ID[...]-YesUnique identifier
namestring-YesDisplay name for the action
descriptionstring-NoDetailed description visible to flow developers
annotationstring-NoDefault annotation text when this action is used in a flow
categorystring-NoGrouping category (e.g., "incident_management", "provisioning")
access'public' | 'package_private''public'NoVisibility scope
protectionPolicy'read' | ''''NoIf 'read', the action body is read-protected in the runtime
inputsRecord<string, Column>-YesInput parameter definitions using column types
outputsRecord<string, Column>-YesOutput parameter definitions using column types

⚠️ access value: The valid scope-private value is 'package_private' (not 'private'). Other values fail at build time.

Body Function

The body is optional and receives a params object exposing the typed schemas:

params keyTypeDescription
inputsTInputsTyped input pills. Use wfa.dataPill(params.inputs.field, 'type')
outputsTOutputsOutput schema reference. Pass to assignActionOutputs to set the action's outputs

The body contains only wfa.actionStep() calls and (optionally) one assignActionOutputs call. No flow logic, no nested custom actions.


wfa.actionStep()

Embeds an OOB step inside a custom action body.

Signature (typed step)

wfa.actionStep<TStep>(
step: TStep, // actionStep.* namespace
config: {
$id: string,
label?: string,
annotation?: string
},
inputs: StepParameters<TStep> & {
errorHandlingType?: 'stop_the_action' | 'dont_stop_the_action',
inputVariables?: ExtendedInputConfig, // only if step allows extended inputs
outputVariables?: Record<string, Column> // only if step allows extended outputs
}
): StepOutputs<TStep>

Signature (sys_id fallback)

When a step's typed definition isn't available (custom step from another app), pass the step's sys_id (or name) as a string. The return type allows arbitrary property access.

wfa.actionStep(
stepSysId: string,
config: { $id: string, label?: string },
inputs: Record<string, unknown> & { errorHandlingType?: ... }
)

Config Parameters

FieldTypeDefaultRequiredDescription
$idstring-YesUnique identifier for this step instance
labelstring-NoDisplay label for this step instance
annotationstring-NoAdditional notes or description

Common Step Parameters

Supported by all wfa.actionStep() calls (passed alongside step-specific fields in the third argument):

ParameterTypeDefaultDescription
errorHandlingTypechoice-'stop_the_action' halts on error; 'dont_stop_the_action' continues execution on error
inputVariablesobject-Named input variables (only allowed when the step's allowExtendedInputs is true; e.g., actionStep.script)
outputVariablesobject-Named output variables (only allowed when the step's allowExtendedOutputs is true; e.g., actionStep.script)

Return Value

Returns the step's typed outputs. Capture as a const to chain into downstream steps via wfa.dataPill().

const created = wfa.actionStep(actionStep.createRecord, { $id: ... }, { ... });
// downstream:
wfa.actionStep(
actionStep.log,
{ $id: ... },
{ log_message: wfa.dataPill(created.record.number, "string"), log_level: "info" }
);

wfa.assignActionOutputs()

Assigns output values inside a custom Action body, mapping declared output names to static values, datapill references, or template literals. This is the recommended way to set action outputs based on step results and input values.

wfa.assignActionOutputs(params.outputs, values)
ParameterTypeDescription
params.outputsOutput schemaThe output schema from params.outputs in the action body function. Provides type-safe key completion.
values{ [K in keyof Outputs]?: Outputs[K] | string }Partial record mapping output names to their values. Keys are constrained to declared output names — arbitrary keys produce a TypeScript error.

Value Types

Output values support three formats:

FormatExampleUse Case
Static string'test value'Fixed values known at design time
Data pillwfa.dataPill(step.record.field, 'string')Dynamic reference to a step output or action input
Template literal`prefix${wfa.dataPill(step.field, 'string')}`Mix of static text and dynamic references

Important notes:

  • Placement: Call wfa.assignActionOutputs() after all wfa.actionStep() calls in the action body. If using wfa.errorEvaluation(), place output assignment after it.
  • Type safety: Output keys are constrained to the names declared in the action's outputs configuration. TypeScript provides autocomplete (Ctrl+Space) for valid output names.
  • Partial assignment: Not all outputs need to be assigned — the values parameter accepts a partial record.
  • Boolean values: Use '1' for true and '0' for false when assigning boolean output values as static strings.

wfa.errorEvaluation()

Defines runtime error evaluation conditions for a custom Action. Conditions are evaluated in order — the first matching condition sets the action's status code and message. Use this to report conditional success or failure based on step results and input values.

wfa.errorEvaluation(conditions)
ParameterTypeDescription
conditionsErrorEvaluationCondition[]Array of conditions evaluated in order. The first matching condition's status is applied.

ErrorEvaluationCondition

PropertyTypeRequiredDescription
labelstringYesDisplay label for the condition (visible in Flow Designer).
conditionstringYesQuery-style condition string. Supports datapill references via template literals.
status{ code, message }YesThe status to set when this condition matches.
status.codenumber | stringYesStatus code. Can be a static number or a template expression with datapills.
status.messagestringYesStatus message. Can be a static string or a template expression with datapills.
dontTreatAsErrorbooleanNoWhen true, the action does not treat this condition as an error. Defaults to false.

Condition Syntax

Condition strings use ServiceNow encoded query syntax. Operators are joined with ^ (AND) and ^OR (OR). Values can include datapill references via template literals.

// Simple equality check on step status code
condition: `${wfa.dataPill(step.__step_status__.code, 'integer')}=500`

// OR condition
condition: `${wfa.dataPill(step.__step_status__.code, 'integer')}=500^OR${wfa.dataPill(step.__step_status__.code, 'integer')}=501`

// ISNOTEMPTY check
condition: `${wfa.dataPill(params.inputs.incident, 'reference')}ISNOTEMPTY`

Important notes:

  • Evaluation order: Conditions are evaluated in the order they appear in the array. The first matching condition wins — subsequent conditions are not evaluated.
  • Placement: Call wfa.errorEvaluation() after all wfa.actionStep() calls but before wfa.assignActionOutputs().
  • dontTreatAsError: When true, the matching condition sets the status but the action is considered successful. When false (default), the action is treated as failed.
  • No match: If no condition matches, the action completes with its default status — wfa.errorEvaluation() does not alter it.
  • Step vs action error handling: errorHandlingType on individual wfa.actionStep() calls controls step-level behavior (stop or continue). wfa.errorEvaluation() sets the overall action status based on conditions evaluated after all steps complete.

DefaultActionOutputs

Every action invocation -- built-in (action.core.*) and custom -- exposes these system outputs in addition to the action's declared outputs:

FieldTypeDescription
__action_status__.codenumberStatus code of the action execution
__action_status__.messagestringStatus message describing the result
__dont_treat_as_error__booleanIf true, an error result will not propagate as an error to the caller

Useful for inspecting an action's runtime status without modeling it in the action's declared outputs.


Column Types

Import from @servicenow/sdk/core for inputs and outputs schemas:

TypeDescription
StringColumnText values
IntegerColumnWhole numbers
BooleanColumnTrue/false values
DecimalColumnDecimal numbers (fixed precision)
FloatColumnFloating-point numbers
DateColumnDate-only values
DateTimeColumnDate and time values
ReferenceColumnReference to a ServiceNow table record
ChoiceColumnChoice list values

Import from @servicenow/sdk/automation for complex types:

TypeDescription
FlowObjectNested object with typed fields
FlowArrayArray of typed elements (use with elementType)

actionStep namespace

OOB steps that can be embedded inside a custom action body via wfa.actionStep(). Step parameter shapes are documented per step below.

Step parameter naming inconsistencies

Different OOB steps use different parameter-naming conventions; mistyping a parameter name is a common error.

StepTable parameterRecord parameterValues parameterNotes
actionStep.createRecordcreate_record_table_name-create_record_field_valuescreate_record_* prefix
actionStep.createTaskcreate_record_table_name-create_record_field_valuescreate_record_* prefix
actionStep.createRecordForRemoteTablecreate_record_table_name-create_record_field_valuescreate_record_* prefix + query_id
actionStep.updateRecordtable_namerecordupdate_record_field_valuesupdate_record_* prefix on values
actionStep.updateMultipleRecordstable_name-field_valuesNo prefix on values
actionStep.createOrUpdateRecordtable-fieldsBoth unprefixed
actionStep.deleteRecordtable_namerecord--
actionStep.deleteMultipleRecordstable_name--Uses conditions
actionStep.lookUpRecordlookup_table_name--lookup_* prefix on table
actionStep.lookUpRecordstable_name---
actionStep.fireEventtablerecord-event_name (string)
actionStep.waitForConditiontable_namerecord-timeout_flag + timeout_duration
actionStep.askForApprovaltablerecord-approval_conditions mandatory
actionStep.emailtable_namerecord-Plain to/subject/body -- NOT ah_* prefix
actionStep.notificationtable_namerecord-notification reference

ServiceNow Data Steps

actionStep.askForApproval

Requests approval from users, groups, or manual approvers on a given record using rule sets. Blocking -- pauses the action until the approval reaches a final state.

Parameters:

ParameterTypeRequiredDescription
tableTable NameNoTable of the record being approved
recordDocument IDYesRecord to request approval on; dependent on table
approval_conditionsApproval RulesYesRule set defining who must approve and under what conditions
approval_reasonstringNoReason displayed to approvers
due_dateSchedule DateTimeNoDeadline by which approval must be completed
approval_fieldField NameNoField on the record that stores the approval state
journal_fieldField NameNoField on the record that stores approval comments/journal entries

Outputs:

FieldTypeDescription
statechoice'approved', 'rejected', or 'cancelled'

Example:

wfa.actionStep(
actionStep.askForApproval,
{ $id: Now.ID["request_approval"], label: "Request manager approval" },
{
table: "change_request",
record: wfa.dataPill(params.inputs.changeRecord, "reference"),
approval_conditions: "...", // see wfa.approvalRules() for builder
approval_reason: "Manager sign-off required",
due_date: "2025-12-31 17:00:00"
}
);

actionStep.createRecord

Creates a new record on a ServiceNow table.

Parameters:

ParameterTypeDefaultRequiredDescription
create_record_table_nameTable Name-NoTarget table name ("incident", "task", etc.)
create_record_field_valuesTemplateValue-NoField values as TemplateValue({...}); dependent on create_record_table_name
skip_insertbooleanfalseNoIf true, skips the actual record insert (dry-run testing)

Outputs:

FieldTypeDescription
table_nameTable NameThe table the record was created in
recordDocument IDReference to the created record; dependent on table_name

Example:

wfa.actionStep(
actionStep.createRecord,
{ $id: Now.ID["create_incident"], label: "Create incident" },
{
create_record_table_name: "incident",
create_record_field_values: TemplateValue({
short_description: wfa.dataPill(params.inputs.summary, "string"),
priority: "2"
})
}
);

actionStep.createTask

Creates a task record. Optionally pauses the action until the task reaches a completed state.

Parameters:

ParameterTypeDefaultRequiredDescription
create_record_table_namestring-NoTarget task table ("incident", "sc_task", "change_request", etc.)
create_record_field_valuesTemplateValue-NoField values as TemplateValue({...}); dependent on create_record_table_name
waitbooleanfalseNoIf true, the action pauses until the task reaches a completed state
skip_insertbooleanfalseNoIf true, skips the actual record insert (dry-run testing)

Outputs:

FieldTypeDescription
tablestringThe table the task was created in
taskDocument IDReference to the created task record; dependent on table

Example:

wfa.actionStep(
actionStep.createTask,
{ $id: Now.ID["create_review_task"], label: "Create review task" },
{
create_record_table_name: "sc_task",
create_record_field_values: TemplateValue({
short_description: "Review access request",
assignment_group: wfa.dataPill(params.inputs.fulfillment_group, "reference"),
priority: "2"
}),
wait: true // pause until task closes
}
);

actionStep.createOrUpdateRecord

Creates or updates a record on a given table.

Parameters:

ParameterTypeRequiredDescription
tableTable NameNoTarget table name
fieldsTemplateValueNoField values as TemplateValue({...}); dependent on table

Outputs:

FieldTypeDescription
recordDocument IDReference to the created or updated record; dependent on table
table_nameTable NameThe table the record was written to
statuschoiceResult status indicating whether the record was created or updated
error_messagestringError details if the operation failed due to business rules or data policies

Example:

wfa.actionStep(
actionStep.createOrUpdateRecord,
{ $id: Now.ID["upsert_ci"], label: "Create or update CMDB CI" },
{
table: "cmdb_ci_server",
fields: TemplateValue({
name: "web-server-01", // unique-identifier field
ip_address: "10.0.0.1",
operational_status: "1"
})
}
);

actionStep.createRecordForRemoteTable

Creates a record on a Remote Table (IntegrationHub virtual table) within custom action execution.

Parameters:

ParameterTypeRequiredDescription
query_idstringYesIdentifier for the remote table query context
create_record_table_nameTable NameNoTarget remote table name
create_record_field_valuesTemplateValueNoField values as TemplateValue({...}); dependent on create_record_table_name

Outputs:

FieldTypeDescription
table_nameTable NameThe remote table the record was created in

Example:

wfa.actionStep(
actionStep.createRecordForRemoteTable,
{ $id: Now.ID["create_remote"], label: "Create record in external system" },
{
query_id: "my_remote_query_id",
create_record_table_name: "x_my_remote_table",
create_record_field_values: TemplateValue({
name: "New Record",
status: "active"
})
}
);

actionStep.deleteRecord

Deletes a single record. No outputs.

Parameters:

ParameterTypeRequiredDescription
table_nameTable NameNoTable containing the record to delete
recordDocument IDNoRecord to delete; dependent on table_name

Outputs: None.

Example:

wfa.actionStep(
actionStep.deleteRecord,
{ $id: Now.ID["delete_temp"], label: "Delete temporary record" },
{
table_name: "u_staging_table",
record: wfa.dataPill(params.inputs.staging_record, "reference")
}
);

actionStep.deleteMultipleRecords

Deletes multiple records matching specified conditions.

Parameters:

ParameterTypeRequiredDescription
table_nameTable NameYesTable to delete records from
conditionsConditionsYesFilter conditions identifying which records to delete
set_workflowbooleanNoIf true, runs business rules and workflows for each deleted record
dont_fail_flow_on_errorbooleanNoIf true, the action continues even if the delete operation fails
sort_columnField NameNoField to order records by before deletion
sort_typechoiceNo'sort_asc' (a → z) or 'sort_desc' (z → a)

Outputs:

FieldTypeDescription
statuschoiceResult status of the delete operation
countintegerNumber of records successfully deleted
error_messagestringError details if the operation failed due to business rules or data policies

Example:

wfa.actionStep(
actionStep.deleteMultipleRecords,
{ $id: Now.ID["delete_expired"], label: "Delete expired staging records" },
{
table_name: "u_staging_table",
conditions: "active=false^sys_created_on<javascript:gs.daysAgoStart(7)",
dont_fail_flow_on_error: true,
sort_column: "sys_created_on",
sort_type: "sort_asc"
}
);

actionStep.fireEvent

Fires a registered ServiceNow event against a record. No outputs.

Parameters:

ParameterTypeRequiredDescription
tableTable NameNoTable of the record the event is fired against
recordDocument IDYesThe record the event is fired against; dependent on table
event_namereferenceYesThe registered event to fire (sysevent_register)
parm1stringNoFirst parameter passed to the event
parm2stringNoSecond parameter passed to the event

Outputs: None.

Example:

wfa.actionStep(
actionStep.fireEvent,
{ $id: Now.ID["fire_escalation"], label: "Fire escalation event" },
{
table: "incident",
record: wfa.dataPill(params.inputs.incident, "reference"),
event_name: "incident.escalated",
parm1: "manager_sys_id",
parm2: "Escalated due to SLA breach"
}
);

actionStep.lookUpRecord

Looks up a single record meeting a search condition.

Parameters:

ParameterTypeRequiredDescription
lookup_table_nameTable NameNoTable to search
conditionsConditionsNoFilter conditions to identify the target record
multiple_records_foundchoiceNo'use_first_record' or 'error' when multiple records match
sort_columnField NameNoField to order results by when selecting the first record
sort_typechoiceNo'sort_asc' or 'sort_desc'
dont_fail_flow_on_errorbooleanNoIf true, the action continues even if no record is found or an error occurs

Outputs:

FieldTypeDescription
statuschoiceResult status of the lookup operation
table_nameTable NameThe table the record was found in
recordDocument IDReference to the found record; dependent on table_name
error_messagestringError details if the lookup failed or no record matched

Example:

const lookup = wfa.actionStep(
actionStep.lookUpRecord,
{ $id: Now.ID["lookup_p1"], label: "Look up open P1 incident" },
{
lookup_table_name: "incident",
conditions: "priority=1^state=1",
multiple_records_found: "use_first_record",
sort_column: "sys_created_on",
sort_type: "sort_desc",
dont_fail_flow_on_error: true
}
);

actionStep.lookUpRecords

Returns the count and a set of records matching a search condition.

Parameters:

ParameterTypeRequiredDescription
table_nameTable NameNoTable to search
conditionsConditionsNoFilter conditions to narrow which records are returned
limitintegerNoMaximum number of records to return
sort_columnField NameNoField to order results by
sort_typechoiceNo'sort_asc' or 'sort_desc'

Outputs:

FieldTypeDescription
recordsRecordsThe set of matching records; can be iterated in a flow
table_nameTable NameThe table the records were retrieved from
countintegerNumber of records returned

Example:

const incidents = wfa.actionStep(
actionStep.lookUpRecords,
{ $id: Now.ID["find_p1s"], label: "Find open high-priority incidents" },
{
table_name: "incident",
conditions: "priority=1^state=1",
limit: 50,
sort_column: "sys_created_on",
sort_type: "sort_asc"
}
);

actionStep.updateRecord

Updates an existing record on a given table.

Parameters:

ParameterTypeRequiredDescription
table_nameTable NameYesTable containing the record to update
recordDocument IDYesReference to the record to update; dependent on table_name
update_record_field_valuesTemplateValueNoField values as TemplateValue({...}); dependent on table_name

Outputs:

FieldTypeDescription
table_nameTable NameThe table the record was updated in
recordDocument IDReference to the updated record; dependent on table_name

Example:

wfa.actionStep(
actionStep.updateRecord,
{ $id: Now.ID["raise_priority"], label: "Update incident priority" },
{
table_name: "incident",
record: wfa.dataPill(params.inputs.incident, "reference"),
update_record_field_values: TemplateValue({
priority: "1",
state: "2",
assignment_group: wfa.dataPill(params.inputs.escalation_group, "reference")
})
}
);

actionStep.updateMultipleRecords

Updates multiple records matching specified conditions.

Parameters:

ParameterTypeRequiredDescription
table_nameTable NameYesTable to update records in
conditionsConditionsYesFilter conditions identifying which records to update
field_valuesTemplateValueYesField values as TemplateValue({...}); dependent on table_name
set_workflowbooleanNoIf true, runs business rules and workflows for each updated record
set_autosysfieldsbooleanNoIf true, updates system fields (e.g., sys_updated_on) for each record
dont_fail_flow_on_errorbooleanNoIf true, the action continues even if the update operation fails
sort_columnField NameNoField to order records by before updating
sort_typechoiceNo'sort_asc' or 'sort_desc'

Outputs:

FieldTypeDescription
statuschoiceResult status of the update operation
countintegerNumber of records successfully updated
error_messagestringError details if the operation failed due to business rules or data policies

Example:

wfa.actionStep(
actionStep.updateMultipleRecords,
{ $id: Now.ID["bulk_close"], label: "Bulk close completed tasks" },
{
table_name: "task",
conditions: "state=3^assignment_group=<group_sys_id>",
field_values: TemplateValue({
state: "7",
close_notes: "Bulk closed by automated workflow"
}),
dont_fail_flow_on_error: true
}
);

actionStep.waitForCondition

Pauses the action until a record meets the specified conditions (or a timeout expires). Blocking.

Parameters:

ParameterTypeRequiredDescription
table_nameTable NameNoTable of the record to monitor
recordDocument IDYesRecord to watch; dependent on table_name
conditionsConditionsYesConditions that must be met on the record before the action resumes
timeout_flagbooleanNoIf true, enables the timeout
timeout_durationDurationNoTime to wait before timing out (used when timeout_flag is true)
timeout_schedulereferenceNoSchedule (cmn_schedule) for business-hours timeout calculation

Outputs:

FieldTypeDescription
statechoiceResult of the wait: whether the condition was met or the timeout expired

Example:

wfa.actionStep(
actionStep.waitForCondition,
{ $id: Now.ID["wait_task_done"], label: "Wait for task completion" },
{
table_name: "task",
record: wfa.dataPill(createdTask.task, "reference"),
conditions: "state=7",
timeout_flag: true,
timeout_duration: "172800" // 48 hours in seconds
}
);

actionStep.waitForEmailReply

Pauses the action until an email reply is received (or a timeout expires). Blocking.

Parameters:

ParameterTypeRequiredDescription
recordreferenceYesThe outgoing email record (sys_email) to monitor for a reply
enable_timeoutbooleanNoIf true, enables the timeout
timeout_durationDurationNoTime to wait before timing out (used when enable_timeout is true)
timeout_schedulereferenceNoSchedule (cmn_schedule) for business-hours timeout calculation

Outputs:

FieldTypeDescription
email_replyreferenceReference to the reply sys_email record when a reply is received
statechoiceResult of the wait: whether a reply was received or the timeout expired

Example:

wfa.actionStep(
actionStep.waitForEmailReply,
{ $id: Now.ID["wait_reply"], label: "Wait for stakeholder reply" },
{
record: wfa.dataPill(sentEmail.email, "reference"),
enable_timeout: true,
timeout_duration: "172800" // 48 hours
}
);

actionStep.waitForMessage

Pauses the action until a specific message is received via the ServiceNow Flow API. Blocking.

Parameters:

ParameterTypeRequiredDescription
messagestringYesThe message identifier to wait for (sent via the Flow API)
enable_timeoutbooleanNoIf true, enables the timeout
timeout_durationDurationNoTime to wait before timing out (used when enable_timeout is true)

Outputs:

FieldTypeDescription
payloadstringThe payload sent with the message received via the Flow API

Example:

wfa.actionStep(
actionStep.waitForMessage,
{ $id: Now.ID["wait_signal"], label: "Wait for external confirmation" },
{
message: "provisioning_complete",
enable_timeout: true,
timeout_duration: "3600" // 1 hour
}
);

Utility Steps

actionStep.collectActivityContext

Collects data from the flow execution source PAD activity context.

Parameters: None.

Outputs:

FieldTypeDescription
activity_contextreferenceThe PAD activity context record (sys_pd_activity_context)

Example:

wfa.actionStep(
actionStep.collectActivityContext,
{ $id: Now.ID["get_activity_context"], label: "Collect PAD activity context" },
{} // takes no inputs
);

actionStep.createAppFromPayload

Creates a ServiceNow application from a template payload.

Parameters:

ParameterTypeRequiredDescription
template_instance_idstringYesID of the application template instance to create from
scan_idstringNoScan configuration ID for template variants

Outputs:

FieldTypeDescription
sys_app_idstringThe sys_id of the newly created application

Example:

wfa.actionStep(
actionStep.createAppFromPayload,
{ $id: Now.ID["create_app"], label: "Create application from template" },
{
template_instance_id: wfa.dataPill(params.inputs.template_id, "string")
}
);

actionStep.email

Sends an email from within a custom action. Uses plain to/subject/body -- not ah_* prefixed parameters (those belong to action.core.sendEmail).

Parameters:

ParameterTypeDefaultRequiredDescription
tostring-YesRecipient email address(es); comma-separated for multiple
subjectstring-YesEmail subject line
bodyHTML-NoEmail body in HTML format
ccstring-NoCC recipient address(es); comma-separated
bccstring-NoBCC recipient address(es); comma-separated
watermark_emailbooleanfalseNoInclude watermark for reply tracking/threading
table_nameTable Name-NoTable of the target record to associate with the email
recordDocument ID-NoTarget record reference; dependent on table_name

Outputs:

FieldTypeDescription
emailreferenceReference to the sent email record (sys_email)

Example:

wfa.actionStep(
actionStep.email,
{ $id: Now.ID["send_escalation"], label: "Send escalation notification" },
{
to: "manager@example.com",
subject: "Incident Escalation Required",
body: "<p>An incident requires your attention.</p>",
watermark_email: true // needed for waitForEmailReply later
}
);

actionStep.getLatestResponseTextFromEmail

Extracts the latest response text from an email thread (strips quoted prior content).

Parameters:

ParameterTypeRequiredDescription
email_recordreferenceYesThe email record (sys_email) to extract the reply text from

Outputs:

FieldTypeDescription
latest_response_textstringThe latest reply text extracted from the email thread

Example:

const reply = wfa.actionStep(
actionStep.getLatestResponseTextFromEmail,
{ $id: Now.ID["extract_reply"], label: "Extract latest email reply" },
{
email_record: wfa.dataPill(waitResult.email_reply, "reference")
}
);

actionStep.log

Writes a message to the flow execution log.

Parameters:

ParameterTypeRequiredDescription
log_messagestringNoThe message to write to the log
log_levelchoiceNo'info', 'warn', or 'error'

Outputs: None.

Example:

wfa.actionStep(
actionStep.log,
{ $id: Now.ID["log_op"], label: "Log operation status" },
{
log_message: `Processing escalation for ${wfa.dataPill(params.inputs.incident_number, "string")}`,
log_level: "info"
}
);

actionStep.notification

Triggers a pre-configured ServiceNow notification (sysevent_email_action).

Parameters:

ParameterTypeRequiredDescription
notificationreferenceYesThe notification record to trigger (sysevent_email_action)
table_nameTable NameNoTable of the target record providing context to the notification template
recordDocument IDNoTarget record reference; dependent on table_name

Outputs: None.

Example:

wfa.actionStep(
actionStep.notification,
{ $id: Now.ID["send_notification"], label: "Trigger escalation notification" },
{
notification: wfa.dataPill(params.inputs.notification_record, "reference"),
table_name: "incident",
record: wfa.dataPill(params.inputs.incident, "reference")
}
);

actionStep.script

Executes custom JavaScript on the instance, a MID Server, or in a vanilla JavaScript sandbox.

Parameters:

ParameterTypeDefaultRequiredDescription
required_run_timechoice'instance'No'instance' (Glide APIs available), 'mid' (MID Server), or 'vanilla' (sandboxed JS)
scriptScript-NoThe JavaScript to execute (use Now.include('./path.js') for external files)
mid_selection_typechoice'any'No'any', 'use_connection_alias', or 'define_connection_inline'
connection_aliasreference-NoConnection alias (sys_alias) when mid_selection_type is 'use_connection_alias'
hoststring-NoHost value; depends on connection_alias
mid_selectionchoice'auto_select'No'auto_select', 'specific_mid_server', or 'specific_mid_cluster'
applicationreference(all apps)NoMID Application filter (ecc_agent_application)
capabilitiesreference-NoMID Server capabilities filter (ecc_agent_capability)
mid_clusterDocument ID-NoSpecific MID Cluster (when mid_selection is 'specific_mid_cluster')
mid_serverDocument ID-NoSpecific MID Server (when mid_selection is 'specific_mid_server')
inputVariablesobject-NoNamed input variables (label + value + optional mandatory). Allowed because script has allowExtendedInputs: true
outputVariablesobject-NoNamed output variables (each is a Column type). Allowed because script has allowExtendedOutputs: true

Outputs: No fixed outputs. Use outputVariables to declare custom outputs that downstream steps can consume as data pills.

Example:

import { StringColumn, IntegerColumn } from "@servicenow/sdk/core";

wfa.actionStep(
actionStep.script,
{ $id: Now.ID["calc_metrics"], label: "Calculate metrics" },
{
required_run_time: "instance",
script: Now.include("./scripts/calc-metrics.js"),
inputVariables: {
threshold: { label: "Threshold", value: "100" }
},
outputVariables: {
score: IntegerColumn({ label: "Score", default: "0" }),
verdict: StringColumn({ label: "Verdict", maxLength: 64 })
}
}
);

actionStep.sms

Sends an SMS message from within a custom action.

Parameters:

ParameterTypeRequiredDescription
messagestringYesThe SMS message text to send
recipientsstringYesRecipient phone number(s) or user references

Outputs:

FieldTypeDescription
emailreferenceReference to the SMS delivery record (stored as sys_email)

Example:

wfa.actionStep(
actionStep.sms,
{ $id: Now.ID["alert_oncall"], label: "Send SMS alert" },
{
message: "URGENT: P1 incident requires immediate attention.",
recipients: "+15555550100"
}
);

Complete Example

// File: fluent/actions/escalate-incident.now.ts
import { Action, wfa, actionStep } from "@servicenow/sdk/automation";
import { StringColumn, BooleanColumn, ReferenceColumn } from "@servicenow/sdk/core";

export const escalateIncident = Action(
{
$id: Now.ID["escalate_incident_action"],
name: "Escalate Incident",
description: "Escalates an incident by raising priority, logging the reason, and notifying the assignee",
category: "incident_management",
inputs: {
incident: ReferenceColumn({ label: "Incident", referenceTable: "incident", mandatory: true }),
reason: StringColumn({ label: "Escalation Reason", mandatory: true })
},
outputs: {
escalated: BooleanColumn({ label: "Escalated", mandatory: true })
}
},
params => {
wfa.actionStep(
actionStep.updateRecord,
{ $id: Now.ID["raise_priority"], label: "Raise priority to P1" },
{
table_name: "incident",
record: wfa.dataPill(params.inputs.incident, "reference"),
update_record_field_values: TemplateValue({
priority: "1",
work_notes: wfa.dataPill(params.inputs.reason, "string")
})
}
);

wfa.actionStep(
actionStep.log,
{ $id: Now.ID["log_escalation"], label: "Log escalation" },
{
log_level: "info",
log_message: `Incident escalated: ${wfa.dataPill(params.inputs.reason, "string")}`
}
);

wfa.assignActionOutputs(params.outputs, { escalated: true });
}
);
// File: fluent/flows/auto-escalate.now.ts
import { Flow, wfa, trigger } from "@servicenow/sdk/automation";
import { escalateIncident } from "../actions/escalate-incident.now";

Flow(
{ $id: Now.ID["auto_escalate_flow"], name: "Auto Escalate Critical Incidents" },
wfa.trigger(
trigger.record.created,
{ $id: Now.ID["trg_critical_incident"] },
{ table: "incident", condition: "priority=1", run_flow_in: "background" }
),
params => {
wfa.action(
escalateIncident,
{ $id: Now.ID["escalate_step"] },
{
incident: wfa.dataPill(params.trigger.current, "reference"),
reason: "Auto-escalated: Priority 1 incident created"
}
);
}
);

For when-to-use guidance, best practices, anti-patterns, and end-to-end patterns, see the Custom Action Guide.