Skip to content
Back to skills

009 Actions Guide 69fe0742

ASecurity

Complete guide to implementing actions in Agentforce agents, including Flow, Apex, external API integrations, and advanced patterns.

  • 9 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 11, 2026
ai-agentsgobashreactexpressgitapi

Works with

  • cli
  • api
  • mcp

Security analysis

A100/100

Scanned October 11, 2026

npx -y skills add tools-only/X-Skills --skill 009-actions-guide_69fe0742 --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of 009 Actions Guide 69fe0742?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for 009 Actions Guide 69fe0742
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/tools-only-009-actions-guide-69fe0742/badge)](https://www.skillsdirectory.com/skills/tools-only-009-actions-guide-69fe0742)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

SKILL.md
# Actions Implementation Guide

Complete guide to implementing actions in Agentforce agents, including Flow, Apex, external API integrations, and advanced patterns.

## Table of Contents

- [Action Fundamentals](#action-fundamentals)
- [Complete Action Type Reference](#complete-action-type-reference)
- [Flow Actions](#flow-actions)
- [Apex Actions (via Flow Wrapper)](#apex-actions-via-flow-wrapper)
- [Data Type Mappings](#data-type-mappings)
- [Advanced Action Fields](#advanced-action-fields)
- [Action Callbacks](#action-callbacks)
- [Slot Filling Patterns](#slot-filling-patterns)
- [Best Practices](#best-practices)

---

## Action Fundamentals

**Actions** are the executable operations your agent can perform - calling Flows, Apex, external APIs, or generating AI content.

### Action Structure

```agentscript
actions:
   [action_name]:
      description: "What this action does"
      inputs:
         [input_name]: [type]
            description: "Input description"
            is_required: [True/False]
      outputs:
         [output_name]: [type]
            description: "Output description"
            is_used_by_planner: [True/False]
      target: "[protocol]://[TargetName]"
```

---

## Complete Action Type Reference

AgentScript supports 22+ action target types. Use the appropriate protocol prefix:

| Short Name | Long Name (Alias) | Description | Use When |
|------------|-------------------|-------------|----------|
| `flow` | `flow` | Salesforce Flow | **PRIMARY** - Most reliable, recommended for all actions |
| `apex` | `apex` | Apex Class (@InvocableMethod) | Custom server-side logic (use Flow wrapper in AiAuthoringBundle) |
| `prompt` | `generatePromptResponse` | Prompt Template | AI content generation |
| `standardInvocableAction` | `standardInvocableAction` | Built-in Salesforce actions | Standard platform actions (send email, create task) |
| `externalService` | `externalService` | External API via OpenAPI schema | External system calls via External Services |
| `quickAction` | `quickAction` | Object-specific quick actions | Quick actions (log call, create related record) |
| `api` | `api` | REST API calls | Direct Salesforce API calls |
| `apexRest` | `apexRest` | Apex REST endpoints | Custom REST services |
| `serviceCatalog` | `createCatalogItemRequest` | Service Catalog requests | IT service requests, catalog items |
| `integrationProcedureAction` | `executeIntegrationProcedure` | OmniStudio Integration Procedure | OmniStudio/Vlocity integrations |
| `expressionSet` | `runExpressionSet` | Expression Set calculations | Business rule calculations |
| `cdpMlPrediction` | `cdpMlPrediction` | CDP ML predictions | Customer Data Platform ML models |
| `externalConnector` | `externalConnector` | External system connector | Pre-built external connectors |
| `slack` | `slack` | Slack integration | Slack-specific actions |
| `namedQuery` | `namedQuery` | Predefined SOQL queries | Named queries for data retrieval |
| `auraEnabled` | `auraEnabled` | Aura-enabled Apex methods | Lightning component methods |
| `mcpTool` | `mcpTool` | Model Context Protocol tools | MCP tool integrations |
| `retriever` | `retriever` | Knowledge retrieval | Knowledge base searches |

**Target Format**: `<type>://<DeveloperName>` (e.g., `flow://Get_Account_Info`, `standardInvocableAction://sendEmail`)

**0-shot Tip**: If you need a built-in action, check if `standardInvocableAction://` applies before creating a custom Flow.

### Action Targets by Deployment Method

| Target Type | GenAiPlannerBundle | AiAuthoringBundle |
|-------------|-------------------|-------------------|
| `flow://FlowName` | Works | Works (with exact name matching) |
| `apex://ClassName` | Works | Limited (class must exist) |
| `prompt://TemplateName` | Works | Requires asset in org |

---

## Flow Actions

**RECOMMENDED**: Use `flow://` for all actions - it's the most reliable and works in both deployment methods.

### Critical Requirements

**`flow://` actions work in BOTH AiAuthoringBundle and GenAiPlannerBundle**, but require:

1. **EXACT variable name matching** between Agent Script and Flow
2. Flow must be an **Autolaunched Flow** (not Screen Flow)
3. Flow variables must be marked "Available for input" / "Available for output"
4. Flow must be deployed to org **BEFORE** agent publish

**The "Internal Error" occurs when input/output names don't match Flow variables!**

```
ERROR: "property account_id was not found in the available list of
        properties: [inp_AccountId]"

This error appears as generic "Internal Error, try again later" in CLI.
```

### Correct Flow Action Pattern

**Step 1: Create Flow with specific variable names**

```xml
<!-- Get_Account_Info.flow-meta.xml -->
<variables>
    <name>inp_AccountId</name>     <!-- INPUT variable -->
    <dataType>String</dataType>
    <isInput>true</isInput>
    <isOutput>false</isOutput>
</variables>
<variables>
    <name>out_AccountName</name>   <!-- OUTPUT variable -->
    <dataType>String</dataType>
    <isInput>false</isInput>
    <isOutput>true</isOutput>
</variables>
```

**Step 2: Agent Script MUST use EXACT same names**

```agentscript
actions:
   get_account:
      description: "Retrieves account information"
      inputs:
         inp_AccountId: string        # ← MUST match Flow variable name!
            description: "Salesforce Account ID"
      outputs:
         out_AccountName: string      # ← MUST match Flow variable name!
            description: "Account name"
      target: "flow://Get_Account_Info"
```

### Common Mistake (Causes "Internal Error")

```agentscript
# ❌ WRONG - Names don't match Flow variables
actions:
   get_account:
      inputs:
         account_id: string           # Flow expects "inp_AccountId"!
      outputs:
         account_name: string         # Flow expects "out_AccountName"!
      target: "flow://Get_Account_Info"
```

This will fail with "Internal Error, try again later" because the schema validation fails silently.

### Flow Validation Timing

**Flow existence is validated at DEPLOYMENT time, NOT during `sf agent validate`!**

| Command | What It Checks | Flow Validation |
|---------|----------------|-----------------|
| `sf agent validate authoring-bundle` | Syntax only | Does NOT check if flows exist |
| `sf project deploy start` | Full deployment | Validates flow existence |

**This means:**
- An agent can **PASS validation** with `sf agent validate authoring-bundle`
- But **FAIL deployment** if the referenced flow doesn't exist in the org

```bash
# ✅ Passes - only checks Agent Script syntax
sf agent validate authoring-bundle --api-name My_Agent --target-org MyOrg
# Status: COMPLETED, Errors: 0

# ❌ Fails - flow doesn't exist in org
sf project deploy start --source-dir force-app/main/default/aiAuthoringBundles/My_Agent
# Error: "We couldn't find the flow, prompt, or apex class: flow://Missing_Flow"
```

**Best Practice: Always deploy flows BEFORE deploying agents that reference them.**

### Flow Actions in AiAuthoringBundle

**Flow actions (`flow://`) DO work in AiAuthoringBundle**, but require a specific pattern:

```agentscript
# ✅ CORRECT PATTERN FOR AiAuthoringBundle
# 1. Define actions in topic blocks (NOT start_agent)
# 2. Use simple action definition (no with/set in reasoning.actions)
# 3. Let the LLM decide when to call the action based on description

start_agent topic_selector:
   label: "Topic Selector"
   description: "Routes users to topics"

   # ✅ start_agent should ONLY have @utils.transition actions
   reasoning:
      instructions: ->
         | Route the user to the appropriate topic.
      actions:
         go_to_orders: @utils.transition to @topic.order_lookup

topic order_lookup:
   label: "Order Lookup"
   description: "Looks up order information"

   # ✅ Define flow actions in the topic's actions: block
   actions:
      get_order:
         description: "Retrieves order details by order number"
         inputs:
            inp_OrderNumber: string
               description: "The order number to look up"
         outputs:
            out_OrderStatus: string
               description: "Status of the order"
            out_OrderTotal: number
               description: "Total amount of the order"
         target: "flow://Get_Order_Details"

   # ✅ Simple reasoning - no with/set in reasoning.actions
   reasoning:
      instructions: ->
         | Help the user look up their order.
         | Ask for the order number if not provided.
         | Use the get_order action to retrieve details.
      actions:
         back_to_menu: @utils.transition to @topic.topic_selector
```

**WRONG PATTERN (causes "Internal Error" at publish):**

```agentscript
# ❌ DO NOT put flow actions in start_agent
start_agent topic_selector:
   actions:
      my_flow_action:    # ❌ WRONG - actions in start_agent fail
         target: "flow://..."

# ❌ DO NOT use with/set in reasoning.actions (AiAuthoringBundle only)
reasoning:
   actions:
      lookup: @actions.get_order
         with inp_OrderNumber=...              # ❌ WRONG - causes Internal Error
         set @variables.status = @outputs...   # ❌ WRONG - causes Internal Error
```

**Key Requirements:**
1. **Flow actions in `topic` blocks only** - NOT in `start_agent`
2. **`start_agent` uses only `@utils.transition`** - for routing to topics
3. **No `with`/`set` in `reasoning.actions`** - just define actions, LLM auto-calls
4. **Input/output names must match Flow exactly** - Case-sensitive!

---

## Data Type Mappings

**Confirmed working data types between Agent Script and Flow:**

| Agent Script Type | Flow Data Type | Status | Notes |
|-------------------|----------------|--------|-------|
| `string` | String | Works | Standard text values |
| `number` | Number (scale=0) | Works | Integer values |
| `number` | Number (scale>0) | Works | Decimal values (e.g., 3.14) |
| `boolean` | Boolean | Works | Use `True`/`False` (capitalized) |
| `list[string]` | Text Collection | Works | Collection with `isCollection=true` |
| `string` | Date | Works* | *Use String I/O pattern (see below) |
| `string` | DateTime | Works* | *Use String I/O pattern (see below) |

### Date/DateTime Workaround Pattern

Agent Script does NOT have native `date` or `datetime` types. If you try to connect an Agent Script `string` input to a Flow `Date` or `DateTime` input, it will fail with "Internal Error" because the platform cannot coerce types.

**Solution: Use String I/O pattern**

1. **Flow accepts/returns Strings** (not Date/DateTime)
2. **Flow parses strings internally** using `DATEVALUE()` or `DATETIMEVALUE()`
3. **Flow converts back to string** using `TEXT()` for output

```xml
<!-- Flow with String I/O for Date handling -->
<variables>
    <name>inp_DateString</name>
    <dataType>String</dataType>       <!-- NOT Date -->
    <isInput>true</isInput>
</variables>
<variables>
    <name>out_DateString</name>
    <dataType>String</dataType>       <!-- NOT Date -->
    <isOutput>true</isOutput>
</variables>
<formulas>
    <name>formula_ParseDate</name>
    <dataType>Date</dataType>
    <expression>DATEVALUE({!inp_DateString})</expression>
</formulas>
<formulas>
    <name>formula_DateAsString</name>
    <dataType>String</dataType>
    <expression>TEXT({!formula_ParseDate})</expression>
</formulas>
```

```agentscript
# Agent Script with string type for date
actions:
   process_date:
      inputs:
         inp_DateString: string
            description: "A date value in YYYY-MM-DD format"
      outputs:
         out_DateString: string
            description: "The processed date as string"
      target: "flow://Test_Date_Type_StringIO"
```

### Collection Types (list[string])

`list[string]` maps directly to Flow Text Collection:

```xml
<variables>
    <name>inp_TextList</name>
    <dataType>String</dataType>
    <isCollection>true</isCollection>  <!-- This makes it a list -->
    <isInput>true</isInput>
</variables>
```

```agentscript
actions:
   process_collection:
      inputs:
         inp_TextList: list[string]
            description: "A list of text values"
      target: "flow://Test_Collection_StringIO"
```

### Important: All Flow inputs must be provided!

If Flow defines 6 input variables but Agent Script only provides 4, publish fails with "Internal Error":

```
❌ FAILS - Missing inputs
   Flow inputs:    inp_String, inp_Number, inp_Boolean, inp_Date
   Agent inputs:   inp_String, inp_Number, inp_Boolean
   Result: "Internal Error, try again later"

✅ WORKS - All inputs provided
   Flow inputs:    inp_String, inp_Number, inp_Boolean
   Agent inputs:   inp_String, inp_Number, inp_Boolean
   Result: Success
```

---

## Advanced Action Fields

For fine-grained control over action behavior, use the `object` type with `complex_data_type_name` and advanced field attributes.

> **Note**: The `filter_from_agent` attribute shown below is **GenAiPlannerBundle only**. It causes "Unexpected 'filter_from_agent'" errors in AiAuthoringBundle. Omit this attribute when using `sf agent publish authoring-bundle`.

```agentscript
actions:
   lookup_order:
      description: "Retrieve order details for a given Order Number."
      inputs:
         order_number: object
            description: "The Order Number the user has provided"
            label: "order_number"
            is_required: False
            is_user_input: False
            complex_data_type_name: "lightning__textType"
      outputs:
         order_id: object
            description: "The Record ID of the Order"
            label: "order_id"
            complex_data_type_name: "lightning__textType"
            filter_from_agent: False
            is_used_by_planner: True
            is_displayable: False
         order_is_current: object
            description: "Whether the order is current"
            label: "order_is_current"
            complex_data_type_name: "lightning__booleanType"
            filter_from_agent: False
            is_used_by_planner: True
            is_displayable: False
      target: "flow://lookup_order"
      label: "Lookup Order"
      require_user_confirmation: False
      include_in_progress_indicator: False
```

### Lightning Data Types (`complex_data_type_name`)

| Type | Description |
|------|-------------|
| `lightning__textType` | Text/String values |
| `lightning__numberType` | Numeric values |
| `lightning__booleanType` | Boolean True/False |
| `lightning__dateTimeStringType` | DateTime as string |

### Input Field Attributes

| Attribute | Type | Description |
|-----------|------|-------------|
| `is_required` | Boolean | Whether the input must be provided |
| `is_user_input` | Boolean | Whether the LLM should collect from user |
| `label` | String | Display label for the field |
| `complex_data_type_name` | String | Lightning data type mapping |

### Output Field Attributes

| Attribute | Type | Description |
|-----------|------|-------------|
| `filter_from_agent` | Boolean | Hide output from agent reasoning |
| `is_used_by_planner` | Boolean | Whether planner uses this output |
| `is_displayable` | Boolean | Show output to user |
| `complex_data_type_name` | String | Lightning data type mapping |

### Action-Level Attributes

| Attribute | Type | Description |
|-----------|------|-------------|
| `label` | String | Display name for the action |
| `require_user_confirmation` | Boolean | Ask user before executing |
| `include_in_progress_indicator` | Boolean | Show progress during execution |

### Minimum Required Attributes

Only `description` and `complex_data_type_name` are required. All other attributes are optional:

```agentscript
# Minimal object type - works!
inputs:
   input_text: object
      description: "Text input"
      complex_data_type_name: "lightning__textType"
```

### Mixing Simple and Object Types

You can mix `string`/`number`/`boolean` with `object` types in the same action:

```agentscript
inputs:
   # Simple type (basic syntax)
   simple_text: string
      description: "A simple text input"
   # Object type (advanced syntax)
   advanced_text: object
      description: "An advanced text input"
      label: "Advanced Text"
      is_required: True
      is_user_input: True
      complex_data_type_name: "lightning__textType"
```

---

## Apex Actions (via Flow Wrapper)

**`apex://` targets work in GenAiPlannerBundle if the Apex class exists:**

```agentscript
# ✅ Works in GenAiPlannerBundle (if class exists in org)
target: "apex://CaseCreationService"
```

**The following do NOT work in either method:**
```agentscript
# ❌ DOES NOT WORK - Invalid format
target: "apex://CaseService.createCase"  # No method name allowed
target: "action://Create_Support_Case"   # action:// not supported
```

### RECOMMENDED: Use Flow Wrapper Pattern

The only reliable way to call Apex from Agent Script is to wrap the Apex in an Autolaunched Flow:

1. **Create Apex class** with `@InvocableMethod` annotation (use sf-apex skill)
2. **Deploy Apex** to org using `sf project deploy start`
3. **Create Autolaunched Flow wrapper** that calls the Apex via Action element:
   ```xml
   <actionCalls>
       <actionName>YourApexClassName</actionName>
       <actionType>apex</actionType>
       <!-- Map input/output variables -->
   </actionCalls>
   ```
4. **Deploy Flow** to org
5. **Reference Flow** in Agent Script:
```agentscript
# ✅ CORRECT - Use flow:// target pointing to Flow wrapper
target: "flow://Create_Support_Case"  # Flow that wraps Apex InvocableMethod
```

### Flow Wrapper Example

```xml
<!-- Create_Support_Case.flow-meta.xml -->
<Flow xmlns="http://soap.sforce.com/2006/04/metadata">
    <actionCalls>
        <name>Call_Apex_Service</name>
        <actionName>CaseCreationService</actionName>
        <actionType>apex</actionType>
        <inputParameters>
            <name>subject</name>
            <value><elementReference>inp_Subject</elementReference></value>
        </inputParameters>
        <outputParameters>
            <assignToReference>var_CaseNumber</assignToReference>
            <name>caseNumber</name>
        </outputParameters>
    </actionCalls>
    <!-- ... variables with isInput=true/isOutput=true ... -->
</Flow>
```

---

## Action Callbacks

**GenAiPlannerBundle only** - Use the `run` keyword to execute actions after another action completes:

```agentscript
# ✅ CORRECT - GenAiPlannerBundle
reasoning:
    actions:
        create_support_case: @actions.create_case
            with inp_CustomerId=@variables.ContactId
            with inp_Subject=...
            set @variables.case_number = @outputs.out_CaseNumber
            run @actions.send_confirmation_email
                with inp_CaseNumber=@variables.case_number
```

**For AiAuthoringBundle**: Define multiple actions separately and let the LLM choose when to call them:

```agentscript
# ✅ CORRECT - AiAuthoringBundle
actions:
   create_case:
      description: "Creates a support case"
      # ... inputs/outputs ...
   send_email:
      description: "Sends confirmation email after case creation"
      # ... inputs/outputs ...

reasoning:
   instructions: ->
      | Create the case first.
      | Then send a confirmation email to the customer.
```

---

## Slot Filling Patterns

**Problem**: LLM slot filling is unreliable - it may send empty JSON, wrong field names, or wrong values.

**Solution**: Use deterministic collection patterns with dedicated setter actions.

### Pattern: Critical Input Collection

```agentscript
variables:
   account_id: mutable string
      description: "The Account ID collected from user"

topic account_lookup:
   label: "Account Lookup"
   description: "Look up account information"

   actions:
      # Dedicated setter action (single-use)
      capture_account_id:
         description: "Capture the Account ID from the user. Ask them for it if not provided. This MUST be called first before any other actions."
         inputs:
            inp_AccountId: string
               description: "The 18-character Salesforce Account ID"
               is_required: True
               is_user_input: True
         target: "flow://Store_Account_ID"
         # Single-use - only available when NOT yet collected
         available when @variables.account_id == ""

      # Main action with null guard
      get_account_details:
         description: "Retrieves full account details using the stored Account ID"
         inputs:
            inp_AccountId: string
               description: "Account ID"
         outputs:
            out_AccountName: string
               description: "Account name"
         target: "flow://Get_Account_Info"
         # Null guard - only available when ID is collected
         available when @variables.account_id != ""

   reasoning:
      instructions: ->
         | FIRST, use {!@actions.capture_account_id} to collect the Account ID.
         | THEN, use {!@actions.get_account_details} to look up the account.
         |
         | if @variables.account_id == "":
         |    | I need your Account ID to proceed.
         | else:
         |    | Looking up account: {!@variables.account_id}
      actions:
         capture_id: @actions.capture_account_id
            set @variables.account_id = @outputs.stored_id
         lookup: @actions.get_account_details
            with inp_AccountId=@variables.account_id
```

**Key Elements:**
1. **Dedicated setter action** - Sole purpose is to collect the critical input
2. **Single-use availability** - `available when @variables.x == ""` prevents re-collection
3. **Null guards on downstream actions** - `available when @variables.x != ""` prevents premature execution
4. **Explicit action references** - `{!@actions.capture_id}` improves LLM reliability
5. **First-interaction instructions** - "FIRST... THEN..." guides LLM execution order

### Pattern: Multi-Step Workflow with Progress Flags

```agentscript
variables:
   step1_done: mutable boolean
      description: "Step 1 completed"
   step2_done: mutable boolean
      description: "Step 2 completed"

actions:
   step1:
      description: "Execute step 1 of the workflow"
      # ... inputs/outputs ...
      target: "flow://Step1"
      available when @variables.step1_done == False

   step2:
      description: "Execute step 2 of the workflow"
      # ... inputs/outputs ...
      target: "flow://Step2"
      available when @variables.step1_done == True
      available when @variables.step2_done == False

reasoning:
   instructions: ->
      | if @variables.step1_done == False:
      |    | Execute step 1 first.
      | if @variables.step1_done == True and @variables.step2_done == False:
      |    | Now execute step 2.
   actions:
      execute_step1: @actions.step1
         set @variables.step1_done = True
      execute_step2: @actions.step2
         set @variables.step2_done = True
```

---

## Best Practices

### Action Design Principles

1. **Clear Descriptions**: LLM uses descriptions to choose actions - be specific
2. **Explicit References**: Use `{!@actions.x}` in instructions to guide LLM
3. **Null Guards**: Use `available when` to prevent execution without required inputs
4. **Single Responsibility**: Each action should do ONE thing
5. **Deterministic Collection**: Don't rely on slot filling for critical inputs

### Naming Conventions

| Element | Convention | Example |
|---------|------------|---------|
| Action name | snake_case | `get_account_details` |
| Input/output | snake_case with prefix | `inp_AccountId`, `out_AccountName` |
| Flow variables | Prefix with `inp_` or `out_` | `inp_CustomerId`, `out_CaseNumber` |

### Common Mistakes

| Mistake | Fix |
|---------|-----|
| Mismatched variable names | Agent Script names MUST match Flow variable API names exactly |
| Missing Flow inputs | Provide ALL inputs that Flow expects |
| Reserved words as inputs | Use alternative names (e.g., `case_description` instead of `description`) |
| Flow not deployed | Deploy flows BEFORE agent publish |
| Relying on slot filling | Use dedicated setter actions for critical inputs |

### Action Requirements Summary

| Requirement | Details |
|-------------|---------|
| **Variable Name Matching** | Agent Script input/output names MUST exactly match Flow variable API names |
| **Flow Type** | Must be **Autolaunched Flow** (not Screen Flow) |
| **Flow Variables** | Mark as "Available for input" / "Available for output" |
| **Deploy Order** | Deploy Flow to org BEFORE publishing agent |
| **API Version** | API v65.0+ required for both AiAuthoringBundle and GenAiPlannerBundle |
| **All Inputs Required** | Agent Script must define ALL inputs that Flow expects (missing inputs = Internal Error) |

---

## References

For additional information, see:
- [../docs/actions-reference.md](../docs/actions-reference.md) - Complete actions reference
- [../docs/patterns-and-practices.md](../docs/patterns-and-practices.md) - Action patterns and best practices
- [agent-script-reference.md](agent-script-reference.md) - Full Agent Script syntax
- [deployment-guide.md](deployment-guide.md) - Deployment workflow and CLI commands

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…