Salesforce Agentforce transforms enterprise AI from passive conversational chat into autonomous agency. Instead of simply generating answers based on retrieved text, an autonomous agent assesses customer intent, selects specialized enterprise tools, executes real-time transactions, and observes the results to determine the next operational step.
Out-of-the-box standard actions provide basic CRM capabilities such as drafting emails or summarizing records. However, enterprise implementations inevitably require proprietary business rules, multi-step orchestration across legacy systems, and real-time external data access. Developers and enterprise architects must extend Agentforce by building Custom Actions backed by three foundational pillars:
- Invocable Apex Methods: Deterministic, programmatic logic handling complex computations, custom validations, and secure data access.
- Headless Autolaunched Flows: Low-code declarative automations executing business workflows and standard database transactions without manual coding.
- MuleSoft APIs via External Services and API Catalog: Enterprise integrations bridging core ERPs, payment processors, and distributed microservices directly into the agent's runtime.
This guide details the end-to-end architecture, technical implementation steps, context engineering best practices, and security governance required to build and expose these three action types in Agentforce.
1. Core Architecture: How Agentforce Understands and Executes Actions
To build reliable custom actions, developers must understand the reasoning engine powering Agentforce: the Atlas Reasoning Engine.
[ User Interaction / Omni-Channel / Digital Channel ]
│
▼
[ Atlas Reasoning Engine ]
(ReAct: Reason - Act - Observe)
│
┌────────────────┼────────────────┐
▼ ▼ ▼
[ Topic Classification ] │ [ Grounding & Context Filter ]
│ │
└───────┬────────┘
▼
[ Tool / Action Selection ]
│
┌───────────┼───────────┐
▼ ▼ ▼
[ Apex ] [ Flow ] [ MuleSoft / API ]
│ │ │
└───────────┼───────────┘
▼
[ Execution Output Observed by Atlas ]
│
▼
[ Final Response or Subsequent Action ]
The ReAct Pattern and Context Engineering
The Atlas Reasoning Engine operates on a Reason-Act-Observe (ReAct) loop. When an end user asks a question or triggers an event:
- Reason: The engine evaluates the incoming conversation against configured Topics and determines which specialized tool (Action) can fulfill the user's intent.
- Act: The engine maps conversational parameters to the action's input schema and initiates execution.
- Observe: The engine parses the structured output payload returned by the action, evaluates whether the user's goal was met, and decides whether to invoke another action or respond directly.
In traditional programmatic development, code communicates through strict types and explicit method calls. In Agentforce, metadata is the prompt. The engine does not inspect method internals; it reads the action's label, description, input parameter annotations, and action instructions. Ambiguous descriptions cause tool confusion and hallucinations, whereas precise descriptions produce deterministic execution.
2. Exposing Invocable Apex as Custom Actions
Apex actions provide the backbone for complex, high-performance logic that requires programmatic flexibility, bulk database operations, or intricate data structures.
Step 1: Design the Invocable Apex Class
Agentforce actions rely on the @InvocableMethod annotation. The method must be public or global, static, and accept a List of input wrapper objects while returning a List of output wrapper objects.
Here is a production-grade pattern for an order cancellation action:
public with sharing class OrderCancellationService {
// Input Wrapper defining parameters required by the Agent
public class OrderCancellationRequest {
@InvocableVariable(
label = 'Order Number'
description = 'The unique alphanumeric identifier of the order to cancel, e.g., ORD-10023.'
required = true
)
public String orderNumber;
@InvocableVariable(
label = 'Cancellation Reason'
description = 'The customer-provided explanation for the cancellation request.'
required = false
)
public String reason;
}
// Output Wrapper returning structured data back to the Atlas Reasoning Engine
public class OrderCancellationResult {
@InvocableVariable(
label = 'Is Success'
description = 'Boolean flag indicating whether the order was successfully marked as cancelled.'
)
public Boolean isSuccess;
@InvocableVariable(
label = 'Cancellation Reference Code'
description = 'Audit tracking number generated for the confirmed cancellation.'
)
public String referenceCode;
@InvocableVariable(
label = 'Status Message'
description = 'Human-readable explanation of the operation outcome or specific failure reason.'
)
public String message;
}
@InvocableMethod(
label = 'Cancel Customer Order'
description = 'Cancels a pending customer order and issues a cancellation audit code. Use this action only when the order status is Pending or Processing and the customer explicitly requests cancellation.'
category = 'Order Management'
)
public static List<OrderCancellationResult> cancelOrders(List<OrderCancellationRequest> requests) {
List<OrderCancellationResult> results = new List<OrderCancellationResult>();
for (OrderCancellationRequest req : requests) {
OrderCancellationResult res = new OrderCancellationResult();
try {
// Enforce User Mode security checks
List<Order> orders = [
SELECT Id, Status, TotalAmount
FROM Order
WHERE OrderNumber = :req.orderNumber
WITH USER_MODE
LIMIT 1
];
if (orders.isEmpty()) {
res.isSuccess = false;
res.message = 'No active order found with number: ' + req.orderNumber;
results.add(res);
continue;
}
Order targetOrder = orders[0];
if (targetOrder.Status == 'Activated' || targetOrder.Status == 'Completed') {
res.isSuccess = false;
res.message = 'Order ' + req.orderNumber + ' is already finalized and cannot be cancelled automatically.';
results.add(res);
continue;
}
targetOrder.Status = 'Cancelled';
targetOrder.Description = 'Cancelled via Agentforce. Reason: ' + (req.reason != null ? req.reason : 'Unspecified');
Database.SaveResult sr = Database.update(targetOrder, AccessLevel.USER_MODE);
if (sr.isSuccess()) {
res.isSuccess = true;
res.referenceCode = 'CAN-' + System.currentTimeMillis();
res.message = 'Order successfully cancelled.';
} else {
res.isSuccess = false;
res.message = 'Failed to update order database record.';
}
} catch (Exception ex) {
res.isSuccess = false;
res.message = 'Internal error during order cancellation: ' + ex.getMessage();
}
results.add(res);
}
return results;
}
}
Critical Apex Best Practices for Agentforce
- Granular Actions vs. God Classes: Avoid creating monolithic methods that perform five different sequential operations. Keep each action focused on a single verifiable step so Atlas can reason through workflows modularly.
- Graceful Error Payloads: Never let uncaught exceptions bubble up to Atlas. Catch exceptions and return
isSuccess = falsealongside a concisemessage. The reasoning engine reads this message to explain issues cleanly or ask the user for missing details. - Security Mode: Always enforce
WITH USER_MODEorwith sharingto ensure the agent executes strictly within the permissions assigned to the Agent User profile.
Step 2: Register the Apex Method as an Agent Action
- From Salesforce Setup, type
Agentin Quick Find and select Agentforce Assets (or Agent Actions). - Click New Agent Action.
- Choose Reference Action Type as Apex.
- Select
OrderCancellationServiceand theCancel Customer Ordermethod. - Review the auto-populated Label, API Name, and Descriptions derived from your
@InvocableMethodannotations. - Refine the Action Instructions: Specify conditions under which the agent should invoke this action (for example: "Trigger this action when a verified customer explicitly asks to cancel an existing order. Prompt for the Order Number if not provided.").
- Save the action.
3. Exposing Headless Flows as Custom Actions
Autolaunched Flows enable administrators and developers to deliver low-code transactional actions quickly, leveraging Flow Builder's declarative CRUD logic, record routing, and approval capabilities.
Flow Type Constraint
Agentforce can invoke only Autolaunched Flow (No Trigger). Screen Flows require interactive human UI rendering, and Record-Triggered Flows fire on asynchronous or synchronous database events. A headless flow provides a clean request-response contract that fits the ReAct execution model.
[ User Prompt: "Update my shipping address to 742 Evergreen Terr" ]
│
▼
[ Agentforce: Action Planner ]
│
(Maps extracted parameters: orderId, street, city)
│
▼
[ Autolaunched Flow: Update Shipping Address ]
┌─────────────────────────┐
│ Get Record: Target Order│
├─────────────────────────┤
│ Assignment: New Address │
├─────────────────────────┤
│ Update Record: Order │
└─────────────────────────┘
│
(Returns outputs: isUpdated = True, confirmationMsg)
│
▼
[ Atlas Engine Formulates Reply ]
Step-by-Step Flow Configuration
- Create the Flow: Open Flow Builder and select Autolaunched Flow (No Trigger).
- Define Input Variables:
- Create a text resource variable named
var_orderId. - Check Available for input.
- Fill in the Description: "The 18-character Salesforce Record ID of the target Order record."
- Create variables for payload inputs (e.g.,
var_streetAddress,var_postalCode) with descriptive text.
- Create a text resource variable named
- Define Output Variables:
- Create a boolean resource variable
var_isUpdated. Check Available for output. Description: "True if the address update was successfully committed." - Create a text resource variable
var_statusMessage. Check Available for output.
- Create a boolean resource variable
- Implement Declarative Logic:
- Use a Get Records element to find the active order.
- Use a Decision element to check if the order has already shipped.
- If allowed, use an Update Records element to persist the new address.
- Use an Assignment element to set
var_isUpdated = Trueand populatevar_statusMessage.
- Save and Activate: Save the flow as
Agent_Update_Order_Addressand activate it.
Step 3: Register the Flow Action
- Navigate to Setup > Agentforce Assets > New Agent Action.
- Select Reference Action Type = Flow.
- Select
Agent_Update_Order_Address. - The system inspects variables marked Available for input and Available for output. Verify that each variable's description provides clear guidance for the LLM.
- Set Action Instructions and execution policies. For record mutation, evaluate whether to enable Require Confirmation so the user confirms changes before execution.
4. Exposing MuleSoft APIs via External Services and API Catalog
Enterprise agents frequently need data outside Salesforce—such as inventory residing in SAP, billing history in Stripe, or shipping milestones in FedEx. MuleSoft serves as the integration backbone, while Salesforce exposes those APIs as callable agent actions using External Services and the Salesforce API Catalog.
Architectural Pathways
Developers have two primary methods to bring MuleSoft APIs into Agentforce:
| Integration Path | Mechanism | Best Suited For |
|---|---|---|
| MuleSoft for Agentforce: Topic Center & API Catalog | Native synchronization from Anypoint Exchange into Salesforce API Catalog | Organizations with managed Anypoint Platform governance, central API teams, and OpenAPI/RAML specs. |
| External Services for MuleSoft | Direct registration via Named Credentials and OpenAPI 3.0/2.0 specifications | Point-to-point integration or specific system APIs needing fast low-code invocation in Flow and Agentforce. |
[ Backend ERP / SAP / Core Banking ]
│
▼
[ MuleSoft Anypoint Platform ]
(System, Process, Experience APIs)
│
(Topic Center / OAS 3.0 Specs)
│
▼
[ Salesforce Anypoint Setup / Sync ]
│
┌────────────┴────────────┐
▼ ▼
[ Named Credentials ] [ Salesforce API Catalog ]
│ │
└────────────┬────────────┘
▼
[ External Services ]
(Generates Invocable Actions)
│
▼
[ Agentforce Custom Action Library ]
Step 1: Design Clean OpenAPI Specifications (OAS)
The Atlas Reasoning Engine inspects the OpenAPI schema to identify path parameters, request bodies, and expected responses. APIs built for agents require strict schema governance:
- Comprehensive Descriptions: Every endpoint parameter, query string, and object attribute must include a human-readable
description. - Simplified Schemas: Avoid polymorphic payloads (
oneOf,anyOf) and deeply nested arrays where possible. Flatter payloads reduce token consumption and parameter extraction errors. - Meaningful Status Codes: Return standard HTTP status codes (200, 400, 404, 500) with descriptive JSON error structures.
Example OpenAPI 3.0 specification snippet for an ERP Inventory Lookup:
openapi: 3.0.3
info:
title: Enterprise Warehouse API
version: 1.0.0
description: Real-time warehouse inventory and shipping availability service.
paths:
/inventory/{sku}:
get:
summary: Retrieve warehouse inventory levels by SKU
description: Returns available stock count, warehouse location, and replenishment lead times for a given product SKU.
operationId: getInventoryBySku
parameters:
- name: sku
in: path
required: true
description: The alphanumeric manufacturer SKU code, e.g., PROD-WIDGET-99.
schema:
type: string
responses:
'200':
description: Successful stock level inquiry
content:
application/json:
schema:
type: object
properties:
sku:
type: string
description: The requested product SKU.
quantityOnHand:
type: integer
description: Physical units available in warehouse.
isAvailableToShip:
type: boolean
description: Indicates whether immediate fulfillment is possible.
warehouseRegion:
type: string
description: Primary fulfillment hub handling the SKU.
'404':
description: SKU not found in inventory catalog
Step 2: Sync and Expose the API in Salesforce
- Configure Anypoint Platform Connection: In Salesforce Setup, search for Anypoint Platform Setup. Authorize your Salesforce org using your Anypoint Organization ID and client credentials.
- Catalog Synchronization:
- In MuleSoft Topic Center or Anypoint Exchange, tag the target API specification with
sf-api-catalog. - In Salesforce Setup, open the API Catalog. Locate the synchronized MuleSoft API.
- Click Activate Operations as Actions.
- In MuleSoft Topic Center or Anypoint Exchange, tag the target API specification with
- Configure Named Credentials: Ensure a secure Named Credential (using OAuth 2.0 or External Client Credentials) is assigned to the service to authenticate HTTP callouts securely.
- Create Agent Action from External Service:
- Navigate to Agentforce Assets > New Agent Action.
- Choose Reference Action Type = External Service.
- Select the registered warehouse service and the
getInventoryBySkuoperation. - Review and fine-tune parameter descriptions.
5. Wiring Actions into Agentforce Builder (The Topic Layer)
Custom actions do not float independently in the agent runtime. To make an action callable, it must be assigned to an Agent Topic within Agentforce Builder.
[ Autonomous Agent: Customer Service Agent ]
│
┌───────────────┴───────────────┐
▼ ▼
[ Topic: Order Inquiries ] [ Topic: Technical Support ]
(Classification & Scope) (Classification & Scope)
│
┌───┴───────────────────────────────┐
▼ ▼
[ Action: Check Order Status ] [ Action: Cancel Customer Order ]
(Autolaunched Flow) (Invocable Apex)
Topic Assignment and Scoping
- Open Agentforce Builder from Setup.
- Select your target agent (e.g., Service Agent or Sales Agent) and enter edit mode.
- Select an existing Topic or click New Topic (e.g., Order Logistics and Changes).
- Define the Topic Classification Description: Explain clearly what user questions fall under this topic.
- In the Topic's Actions panel, click Add Action and pick your newly created Custom Actions (Apex, Flow, MuleSoft).
Authoring Topic and Action Instructions
The instructions guide the agent's decision-making process:
### Topic Instructions: Order Logistics and Changes
- Help the user with order tracking, status inquiries, address updates, and cancellations.
- Never cancel an order without first asking the user to confirm their intention.
- When an order cancellation fails due to finalized status, explain that the package has already shipped and offer the return policy.
### Action Instructions: Cancel Customer Order
- Only execute this action after the user explicitly provides the Order Number and agrees to the cancellation prompt.
- If the Order Number is missing, ask the user: "Could you please provide the order number you would like to cancel?"
- Once executed, communicate the Cancellation Reference Code back to the customer.
Execution Policies and Human-in-the-Loop Safeguards
For state-modifying actions (DML inserts/updates, payments, cancellations), toggle Require Confirmation in the action settings. This forces Agentforce to generate a conversational confirmation prompt (e.g., "Are you sure you want to cancel order ORD-10023? This cannot be undone.") before invoking the underlying Apex, Flow, or API callout.
6. Architecture Comparison and Selection Matrix
Choosing the right technology for custom actions ensures long-term maintainability, security, and low operational latency.
| Architectural Dimension | Invocable Apex | Autolaunched Flow | MuleSoft External Services |
|---|---|---|---|
| Primary Skillset | Salesforce Developer (Apex) | Salesforce Admin / Low-Code Dev | Integration Architect / MuleSoft Dev |
| Execution Domain | Native Salesforce Core | Native Salesforce Core | External Enterprise Systems |
| Ideal Use Cases | Complex math, multi-object querying, custom security rules, programmatic transformations | Standard CRM CRUD, record routing, approvals, existing declarative automations | ERP sync (SAP, Oracle), payment gateways, external warehouses, cross-cloud microservices |
| Governor Limits & Latency | Standard Apex CPU (10s synchronous), SOQL/DML limits | Subject to Flow iteration and element execution limits | External API latency, callout timeout limits (up to 120s), rate limiting |
| Maintenance Model | Source code managed via Git, SFDX, and metadata packaging | Declarative versioning in Flow Builder | API contracts governed in Anypoint Exchange and API Governance |
| Error Handling | Programmatic try-catch with custom wrapper classes | Fault connectors and declarative branch assignment | Standard HTTP status codes (2xx, 4xx, 5xx) with JSON error payloads |
7. Testing, Security, and Production Deployment
1. Agent User Permissions and Security Context
Custom actions execute under the context of the configured Agent User. A common failure during rollout occurs when the agent identifies the correct action but encounters runtime execution failures due to missing object permissions.
- Apex Class Access: Grant the Agent User profile or permission set explicit access to the Apex classes containing
@InvocableMethod. - Flow Access: Ensure the Agent User has the Run Flows system permission and specific access to the autolaunched flow.
- External Credentials & Object FLS: Ensure the Agent User has access to the Principal in Named Credentials and read/edit Field-Level Security for target objects.
2. Multi-Tier Testing Strategy
- Unit Testing: Standard Apex unit tests (with 85%+ code coverage) ensuring bulk safety, null handling, and User Mode access.
- Interactive Live Testing in Agent Builder: Use the conversation preview pane to trace the agent's thought process. Inspect the reasoning tab to verify that Atlas correctly classifies the topic, extracts parameters, and receives structured outputs.
- Agentforce Testing Center: Run bulk test suites against hundreds of representative customer utterances. Define custom evaluation criteria to evaluate tool selection accuracy, hallucination rates, and parameter mapping precision across version releases.
By combining the programmatic precision of Invocable Apex, the declarative speed of Autolaunched Flows, and the enterprise connectivity of MuleSoft APIs, developers create an extensible, secure foundation for autonomous AI operations on the Salesforce Platform.
References
- Agent Customization with Apex | Salesforce Trailhead
- Learn How to Make Agent-Ready Flows | Salesforce Trailhead
- How to Write Effective Natural Language Instructions for Agentforce | Salesforce Developers Blog
- A Developer’s Guide to Context Engineering with Agentforce | Salesforce Developers Blog
- Salesforce Agentforce Specialist Certification Guide & Tips | Salesforce Ben
- External Services | Salesforce Help