# Agentforce — Full Documentation > This file contains all Agentforce documentation in a single file for LLM consumption. > For a curated navigation file, see /llms.txt instead. --- # GET STARTED ## Build # Build Agentforce agents can be built from any environment that connects to your Salesforce org. Pick the tool that fits your workflow: visual, code-first, or conversational. ## Salesforce Tools Build and test agents directly in the browser with no local environment. Visual, browser-based builder. Define topics, actions, and instructions with no local setup required. AI-assisted Agent Script authoring inside Code Builder, directly connected to your org. Build and deploy agents that work natively inside Slack conversations. ## CLI Agents Terminal-based AI agents that write, edit, and deploy code from natural language. Anthropic's CLI agent. Install Salesforce skills and build agents from natural language. OpenAI's CLI agent. Describe tasks in plain English, get code and commands back. Google's terminal-based AI agent for code generation, debugging, and reviews. ## Agentic IDEs Full editors with built-in AI assistance for writing Agent Script and Apex. AI-first editor built on VS Code with inline completions and chat. Codeium's AI editor with deep agentic capabilities. Google's AI-native IDE built around Gemini models. Currently in private preview. ## Agentforce Builder # Agentforce Builder The visual way to build agents. Describe what your agent should do, configure subagents and actions, test in preview, and deploy, all from a browser. No local setup required. *(Open Agentforce Builder — available on the live page)* ![Agentforce Builder](/images/products/agent-builder.png) ## How it works 1. **Click the button above**: signs you in and drops you into a provisioned org 2. **Start building**: define subagents, add actions, write instructions, test in preview Your org comes with Agentforce, Data Cloud, and all permissions pre-configured. ## What you can build - **Subagents**: conversation flows your agent handles (order lookup, returns, scheduling) - **Actions**: tools your agent calls (Apex, Flow, APIs) - **Instructions**: reasoning logic that guides your agent's behavior - **Guardrails**: safety rules and escalation paths ## Deploy Once your agent works in preview, deploy to: - **Experience Sites**: hosted Salesforce page with your agent embedded - **Enhanced Chat**: embed on your own website - **API**: call your agent programmatically from any client ## Want more control? For code-first development with Agent Script, try [Claude Code](/docs/claude-code). Both paths produce the same thing: an Agentforce agent. Choose the one that fits how you work. ## Agentforce Vibes # Agentforce Vibes AI-assisted agent development inside Salesforce Code Builder. Write Agent Script with AI help, directly connected to your org, no local setup, no context switching. *(Open Agentforce Vibes — available on the live page)* ![Agentforce Vibes](/images/products/agentforce-vibes.png) ## What makes Vibes different - **Runs in the browser**: Code Builder inside your Salesforce org, nothing to install - **AI-assisted**: describe what you want, get Agent Script generated in context - **Org-connected**: direct access to your objects, permissions, and metadata as you build - **Fast iteration**: edit, save, test without leaving the browser ## When to use Vibes Vibes sits between Builder and a full IDE. Use it when you want to write Agent Script directly but don't need a local development environment: - Exploring Agent Script syntax hands-on - Quick iterations on agent logic - Working against your org's data model in real time ## Other paths - [Agentforce Builder](/docs/agentforce-builder): visual, no-code agent configuration - [Claude Code](/docs/claude-code): full AI-assisted development with sf-skills and eval loops ## Setting Up Claude Code for Agentforce # Getting Started with Claude Code Claude Code is Anthropic's official CLI tool for building with Claude. Follow these steps to set up Agentforce development in Claude Code. ## 1. Install Claude Code #### curl ```bash curl -fsSL https://claude.ai/install.sh | bash ``` #### Homebrew ```bash brew install claude ``` #### npm ```bash npm install -g @anthropic-ai/claude-code ``` ## 2. Install the Salesforce CLI & Connect Your Org #### Homebrew ```bash brew install sf ``` #### npm ```bash npm install -g @salesforce/cli ``` Requires Node.js 18+. **Verify the installation:** Run this command: ```bash sf --version ``` **Expected output:** `@salesforce/cli/` ### Authenticate Your Org (No Browser Required) Copy the command below and run it in your terminal to authenticate the Salesforce CLI with your org: **Authenticate SF CLI**: Log in on this page to get your pre-filled authentication command. ```bash echo "" | sf org login sfdx-url --sfdx-url-stdin --set-default --alias afdx-lab ``` **Verify your org connection:** Run this command: ```bash sf org display user ``` **Expected output:** Your org username and alias ## 3. Install Salesforce Skills & Start Building Choose a skills library to install. Each provides Salesforce development knowledge that Claude Code uses automatically. [Compare all options](/docs/skills). #### Agentforce ADLC Agent Development Life Cycle by Salesforce AI Research: build, deploy, test, and optimize Agentforce agents. 3 skills + 4 agents + safety guardrails. Install the official Claude Code plugin: ``` /plugin install agentforce-adlc ``` Or use the full setup script (also installs Salesforce CLI if needed): ```bash curl -sSL https://raw.githubusercontent.com/SalesforceAIResearch/agentforce-adlc/main/tools/install.sh | bash ``` #### SF Skills Official Salesforce skills, prompts, and rules library: metadata creation, Flow development, Apex patterns, and more. ```bash npx skills add forcedotcom/sf-skills ``` > **ℹ️ Restart Claude Code after running**: Skills are loaded when Claude Code starts. Run the command above **before** launching Claude Code, or **restart Claude Code** if it's already running. ### Start Building Copy and paste this prompt into Claude Code. Optionally enter a project and agent name below. If you leave it blank, the prompt uses "ExampleName". ```text Before anything else, verify prerequisites are installed: 1. Check SF CLI: `sf --version` If not found, install it: `brew install sf` If found, update it: `sf update` 2. Check agentforce-adlc skills: `ls ~/.claude/skills/developing-agentforce/SKILL.md 2>/dev/null` If not found, install them: `curl -sSL https://raw.githubusercontent.com/SalesforceAIResearch/agentforce-adlc/main/tools/install.sh | bash` 3. Check SF CLI auth: `sf org display user --json` If not authenticated, run the SFDX Auth URL command from the setup page. Use the installed agentforce-adlc skills for the full agent lifecycle. Interview me about the agent I want to build — its role, company, and behaviors. Wait for my response before building. Then use /developing-agentforce to author, discover, scaffold, and deploy the agent. Use /testing-agentforce to run smoke tests and create a test suite. Build real actions — not stubs. Iterate until quality is solid. ``` ## What Happens Next After you paste the prompt, Claude will use the installed agentforce-adlc skills: 1. **Discover**: Retrieve your agent, parse subagents and actions, validate targets exist in the org 2. **Scaffold**: Generate real backing actions (Flows/Apex) for every subagent, no stubs or mocks 3. **Deploy**: Validate bundle, deploy metadata, publish and activate the agent 4. **Test & Optimize**: Run evaluation tests, analyze traces, fix issues, and iterate until quality is good ## Tips - **Be specific about scope**: "Handle order status and returns" beats "Customer support" - **Say what it should NOT do**: "Does NOT handle billing questions or account changes" - **Mention verification needs**: "Verify identity before showing account details" - **Include state tracking**: "Track the order number across conversation turns so the customer doesn't have to repeat it" ## Agent API - Headless Communication # Agent API - Headless Communication Interact with your deployed Agentforce agents programmatically using REST APIs. Perfect for building custom integrations, testing workflows, or automating agent interactions. > **Interactive Explorer (on agentforce.dev)**: > The live page has an interactive Agent API explorer that lets you create > sessions and send messages to your agent. If you are not using the live page, > follow the curl/REST API examples below. ## API Usage Guide Follow these steps to interact with your agent programmatically: ### Step 0: Get OAuth Access Token First, get an OAuth access token using your credentials. Copy the `access_token` from the response. ```bash curl -X POST "{{INSTANCE_URL}}/services/oauth2/token" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=client_credentials" \ -d "client_id={{CLIENT_ID}}" \ -d "client_secret={{CLIENT_SECRET}}" ``` **Response example:** ```json { "access_token": "00D5e0000...", "instance_url": "https://your-org.my.salesforce.com", "id": "https://login.salesforce.com/id/...", "token_type": "Bearer", "issued_at": "1234567890", "signature": "..." } ``` ### Step 1: Start Agent Session Start a new agent session. Save the `sessionId` from the response. ```bash curl -X POST "{{AGENT_API_BASE_URL}}/agents/{{AGENT_ID}}/sessions" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" \ -H "Content-Type: application/json" \ -d '{ "externalSessionKey": "session_1234567890", "instanceConfig": { "endpoint": "{{INSTANCE_URL}}" }, "tz": "America/Los_Angeles", "variables": [{ "name": "$Context.EndUserLanguage", "type": "Text", "value": "en_US" }], "bypassUser": true }' ``` **Key parameters:** - `externalSessionKey`: A unique identifier for this session (use timestamps or UUIDs) - `instanceConfig.endpoint`: Your Salesforce instance URL - `tz`: Timezone for the session - `variables`: Context variables for the agent - `bypassUser`: Set to `true` for headless API access **Response example:** ```json { "sessionId": "0Xx5e0000...", "createdDate": "2024-01-01T00:00:00.000Z", "status": "ACTIVE" } ``` ### Step 2: Send Message to Agent Send a message to the agent. Increment `sequenceId` for each new message (1, 2, 3, ...). ```bash curl -X POST "{{AGENT_API_BASE_URL}}/sessions/YOUR_SESSION_ID/messages" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" \ -H "Content-Type: application/json" \ -d '{ "message": { "sequenceId": 1, "type": "Text", "text": "Hello! Can you help me?" } }' ``` **Important:** Track the `sequenceId` yourself - it must increment with each message in the session. **Response example:** ```json { "messages": [ { "sequenceId": 1, "type": "Text", "text": "Hello! I'm here to help. What can I assist you with today?", "role": "Agent" } ] } ``` ### Step 3: End Agent Session Close the session when done to free up resources. ```bash curl -X DELETE "{{AGENT_API_BASE_URL}}/sessions/YOUR_SESSION_ID" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" \ -H "x-session-end-reason: UserRequest" ``` **Response:** Returns HTTP 200 with a `SessionEnded` message. **Note:** The `x-session-end-reason` header is required. Valid values: `UserRequest`, `Transfer`. ## Streaming Responses For real-time streaming responses, add streaming capabilities to your session: ```json { "featureSupport": "Streaming", "streamingCapabilities": { "chunkTypes": ["Text"] } } ``` When streaming is enabled, responses will be sent as Server-Sent Events (SSE). ## Error Handling Common error responses: | Status Code | Description | Solution | | ----------- | --------------------- | -------------------------------------------- | | 401 | Unauthorized | OAuth token expired - get a new one | | 404 | Not Found | Check agent ID or session ID | | 429 | Too Many Requests | Rate limit exceeded - slow down requests | | 500 | Internal Server Error | Salesforce server issue - retry with backoff | ## Rate Limits - **Sessions per hour**: 1000 per org - **Messages per session**: 100 - **Concurrent sessions**: 50 per org ## SDK Examples **Choose one of the following methods:** #### JavaScript/Node.js ```javascript const axios = require("axios"); const client = new AgentAPIClient( "{{INSTANCE_URL}}", "{{CLIENT_ID}}", "{{CLIENT_SECRET}}" ); class AgentAPIClient { constructor(instanceUrl, clientId, clientSecret) { this.instanceUrl = instanceUrl; this.clientId = clientId; this.clientSecret = clientSecret; this.baseUrl = "https://api.salesforce.com/einstein/ai-agent/v1"; } async getAccessToken() { const response = await axios.post( `${this.instanceUrl}/services/oauth2/token`, new URLSearchParams({ grant_type: "client_credentials", client_id: this.clientId, client_secret: this.clientSecret, }), { headers: { "Content-Type": "application/x-www-form-urlencoded" } } ); return response.data.access_token; } async startSession(agentId, accessToken) { const response = await axios.post( `${this.baseUrl}/agents/${agentId}/sessions`, { externalSessionKey: `session_${Date.now()}`, instanceConfig: { endpoint: this.instanceUrl }, tz: "America/Los_Angeles", bypassUser: true, }, { headers: { Authorization: `Bearer ${accessToken}` } } ); return response.data.sessionId; } async sendMessage(sessionId, text, sequenceId, accessToken) { const response = await axios.post( `${this.baseUrl}/sessions/${sessionId}/messages`, { message: { sequenceId, type: "Text", text, }, }, { headers: { Authorization: `Bearer ${accessToken}` } } ); return response.data; } async endSession(sessionId, accessToken) { await axios.delete(`${this.baseUrl}/sessions/${sessionId}`, { headers: { Authorization: `Bearer ${accessToken}`, "x-session-end-reason": "UserRequest", }, }); } } // Usage (async () => { const token = await client.getAccessToken(); const sessionId = await client.startSession("{{AGENT_ID}}", token); const response = await client.sendMessage(sessionId, "Hello!", 1, token); console.log(response); await client.endSession(sessionId, token); })(); ``` #### Python ```python import requests import time client = AgentAPIClient( "{{INSTANCE_URL}}", "{{CLIENT_ID}}", "{{CLIENT_SECRET}}" ) class AgentAPIClient: def __init__(self, instance_url, client_id, client_secret): self.instance_url = instance_url self.client_id = client_id self.client_secret = client_secret self.base_url = 'https://api.salesforce.com/einstein/ai-agent/v1' def get_access_token(self): response = requests.post( f"{self.instance_url}/services/oauth2/token", data={ 'grant_type': 'client_credentials', 'client_id': self.client_id, 'client_secret': self.client_secret } ) return response.json()['access_token'] def start_session(self, agent_id, access_token): response = requests.post( f"{self.base_url}/agents/{agent_id}/sessions", json={ 'externalSessionKey': f"session_{int(time.time())}", 'instanceConfig': {'endpoint': self.instance_url}, 'tz': 'America/Los_Angeles', 'bypassUser': True }, headers={'Authorization': f'Bearer {access_token}'} ) return response.json()['sessionId'] def send_message(self, session_id, text, sequence_id, access_token): response = requests.post( f"{self.base_url}/sessions/{session_id}/messages", json={ 'message': { 'sequenceId': sequence_id, 'type': 'Text', 'text': text } }, headers={'Authorization': f'Bearer {access_token}'} ) return response.json() def end_session(self, session_id, access_token): requests.delete( f"{self.base_url}/sessions/{session_id}", headers={ 'Authorization': f'Bearer {access_token}', 'x-session-end-reason': 'UserRequest' } ) # Usage token = client.get_access_token() session_id = client.start_session("{{AGENT_ID}}", token) response = client.send_message(session_id, "Hello!", 1, token) print(response) client.end_session(session_id, token) ``` ## Tips - **Token expiration**: OAuth tokens expire after ~2 hours - implement token refresh logic - **Session management**: Always end sessions when done to avoid hitting Salesforce limits - **Sequence tracking**: Maintain sequence IDs on your side - they must increment sequentially - **Error handling**: Implement retry logic with exponential backoff for transient errors - **Rate limiting**: Respect rate limits and implement proper throttling ## Additional Resources - [Official Salesforce Agent API Documentation](https://developer.salesforce.com/docs/einstein/genai/guide/agent-api.html) - [OAuth 2.0 Client Credentials Flow](https://help.salesforce.com/s/articleView?id=sf.remoteaccess_oauth_client_credentials_flow.htm) - [Einstein Platform Rate Limits](https://developer.salesforce.com/docs/einstein/genai/guide/rate-limits.html) ## Setting Up Codex for Agentforce # Getting Started with Codex Codex is OpenAI's AI coding agent that works alongside you in your development workflow. It connects to your codebase via CLI, letting you describe tasks in natural language, then it writes or edits code, runs commands, and helps with debugging and reviews. ## 1. Install Codex CLI Install Codex CLI globally via npm or Homebrew: #### npm ```bash npm i -g @openai/codex ``` #### Homebrew ```bash brew install codex ``` **Verify the installation:** ```bash codex --version ``` ## 2. Install the Salesforce CLI & Connect Your Org #### Homebrew ```bash brew install sf ``` #### npm ```bash npm install -g @salesforce/cli ``` Requires Node.js 18+. **Verify the installation:** Run this command: ```bash sf --version ``` **Expected output:** `@salesforce/cli/` ### Authenticate Your Org (No Browser Required) Copy the command below and run it in your terminal to authenticate the Salesforce CLI with your org: **Authenticate SF CLI**: Log in on this page to get your pre-filled authentication command. ```bash echo "" | sf org login sfdx-url --sfdx-url-stdin --set-default --alias afdx-lab ``` **Verify your org connection:** Run this command: ```bash sf org display user ``` **Expected output:** Your org username and alias ## 3. Install Salesforce Skills & Start Building Choose a skills library to install. Each provides Salesforce development knowledge via the [Agent Skills](https://agentskills.io) open standard. [Compare all options](/docs/skills). #### Agentforce ADLC Agent Development Life Cycle by Salesforce AI Research: build, deploy, test, and optimize Agentforce agents. 3 consolidated skills. ```bash npx skills add SalesforceAIResearch/agentforce-adlc ``` #### SF Skills Official Salesforce skills, prompts, and rules library: metadata creation, Flow development, Apex patterns, and more. ```bash npx skills add forcedotcom/sf-skills ``` ### Start Building Copy and paste this prompt into Codex. Optionally enter a project and agent name below; if you leave it blank, the prompt uses "ExampleName". ```text Before anything else, verify prerequisites are installed: 1. Check SF CLI: `sf --version` If not found, install it: `brew install sf` If found, update it: `sf update` 2. Check agentforce-adlc skills: `npx skills list 2>/dev/null | grep -q agentforce-adlc` If not found, install them: `npx skills add SalesforceAIResearch/agentforce-adlc` 3. Check SF CLI auth: `sf org display user --json` If not authenticated, run the SFDX Auth URL command from the setup page. Use the installed agentforce-adlc skills for the full agent lifecycle. Interview me about the agent I want to build: its role, company, and behaviors. Wait for my response before building. Then use the developing-agentforce skill to author, discover, scaffold, and deploy the agent. Use the testing-agentforce skill to run smoke tests and create a test suite. Build real actions, not stubs. Iterate until quality is solid. ``` ## What Happens Next After you paste the prompt, Codex will use the installed agentforce-adlc skills: 1. **Discover**: Retrieve your agent, parse subagents and actions, validate targets exist in the org 2. **Scaffold**: Generate real backing actions (Flows/Apex) for every subagent, no stubs or mocks 3. **Deploy**: Validate bundle, deploy metadata, publish and activate the agent 4. **Test & Optimize**: Run evaluation tests, analyze traces, fix issues, and iterate until quality is good ## Tips - **Be specific about scope**: "Handle order status and returns" beats "Customer support" - **Say what it should NOT do**: "Does NOT handle billing questions or account changes" - **Mention verification needs**: "Verify identity before showing account details" - **Include state tracking**: "Track the order number across conversation turns so the customer doesn't have to repeat it" ## Setting Up Gemini CLI for Agentforce # Getting Started with Gemini CLI Gemini CLI is Google's AI coding agent that runs in your terminal. It connects to your codebase, lets you describe tasks in natural language, and writes code, runs commands, and helps with debugging and reviews. ## 1. Install Gemini CLI #### npx (no install) ```bash npx @google/gemini-cli ``` #### npm ```bash npm install -g @google/gemini-cli ``` #### Homebrew ```bash brew install gemini-cli ``` **Verify the installation:** ```bash gemini --version ``` ### Authenticate Gemini #### Google Login (Free) Run `gemini` and follow the browser login prompt. Free tier includes 60 requests/min and 1,000 requests/day. #### API Key Get a key from [Google AI Studio](https://aistudio.google.com/apikey), then: ```bash export GEMINI_API_KEY="YOUR_KEY" ``` ## 2. Install the Salesforce CLI & Connect Your Org #### Homebrew ```bash brew install sf ``` #### npm ```bash npm install -g @salesforce/cli ``` Requires Node.js 18+. **Verify the installation:** Run this command: ```bash sf --version ``` **Expected output:** `@salesforce/cli/` ### Authenticate Your Org (No Browser Required) Copy the command below and run it in your terminal to authenticate the Salesforce CLI with your org: **Authenticate SF CLI**: Log in on this page to get your pre-filled authentication command. ```bash echo "" | sf org login sfdx-url --sfdx-url-stdin --set-default --alias afdx-lab ``` **Verify your org connection:** Run this command: ```bash sf org display user ``` **Expected output:** Your org username and alias ## 3. Install Salesforce Skills & Start Building Choose a skills library to install. Each provides Salesforce development knowledge via the [Agent Skills](https://agentskills.io) open standard. [Compare all options](/docs/skills). #### Agentforce ADLC Agent Development Life Cycle by Salesforce AI Research: build, deploy, test, and optimize Agentforce agents. 3 consolidated skills. ```bash npx skills add SalesforceAIResearch/agentforce-adlc ``` #### SF Skills Official Salesforce skills, prompts, and rules library: metadata creation, Flow development, Apex patterns, and more. ```bash npx skills add forcedotcom/sf-skills ``` ### Start Building Copy and paste this prompt into Gemini CLI. Optionally enter a project and agent name below; if you leave it blank, the prompt uses "ExampleName". ```text Before anything else, verify prerequisites are installed: 1. Check SF CLI: `sf --version` If not found, install it: `brew install sf` If found, update it: `sf update` 2. Check agentforce-adlc skills: `npx skills list 2>/dev/null | grep -q agentforce-adlc` If not found, install them: `npx skills add SalesforceAIResearch/agentforce-adlc` 3. Check SF CLI auth: `sf org display user --json` If not authenticated, run the SFDX Auth URL command from the setup page. Use the installed agentforce-adlc skills for the full agent lifecycle. Interview me about the agent I want to build: its role, company, and behaviors. Wait for my response before building. Then use the developing-agentforce skill to author, discover, scaffold, and deploy the agent. Use the testing-agentforce skill to run smoke tests and create a test suite. Build real actions, not stubs. Iterate until quality is solid. ``` ## What Happens Next After you paste the prompt, Gemini CLI will use the installed agentforce-adlc skills: 1. **Discover**: Retrieve your agent, parse subagents and actions, validate targets exist in the org 2. **Scaffold**: Generate real backing actions (Flows/Apex) for every subagent, no stubs or mocks 3. **Deploy**: Validate bundle, deploy metadata, publish and activate the agent 4. **Test & Optimize**: Run evaluation tests, analyze traces, fix issues, and iterate until quality is good ## Tips - **Be specific about scope**: "Handle order status and returns" beats "Customer support" - **Say what it should NOT do**: "Does NOT handle billing questions or account changes" - **Mention verification needs**: "Verify identity before showing account details" - **Include state tracking**: "Track the order number across conversation turns so the customer doesn't have to repeat it" ## Setting Up Cursor for Agentforce # Getting Started with Cursor Cursor is an AI-first code editor built on VS Code. Follow these steps to set up Agentforce development in Cursor. ## 1. Install Cursor #### Homebrew (macOS) ```bash brew install --cask cursor ``` #### Download Download from [cursor.com](https://www.cursor.com) and install. Open Cursor and sign in to your account. ## 2. Install the Salesforce CLI & Connect Your Org Open Cursor's integrated terminal (Ctrl+`) and install the Salesforce CLI: #### Homebrew ```bash brew install sf ``` #### npm ```bash npm install -g @salesforce/cli ``` Requires Node.js 18+. **Verify the installation:** Run this command: ```bash sf --version ``` **Expected output:** `@salesforce/cli/` ### Authenticate Your Org Copy the command below and run it in your terminal to authenticate the Salesforce CLI with your org: **Authenticate SF CLI**: Log in on this page to get your pre-filled authentication command. ```bash echo "" | sf org login sfdx-url --sfdx-url-stdin --set-default --alias afdx-lab ``` **Verify your org connection:** Run this command: ```bash sf org display user ``` **Expected output:** Your org username and alias ## 3. Install Salesforce Skills & Start Building Choose a skills library to install. Each provides Salesforce development knowledge via the [Agent Skills](https://agentskills.io) open standard. [Compare all options](/docs/skills). #### Agentforce ADLC Agent Development Life Cycle by Salesforce AI Research: build, deploy, test, and optimize Agentforce agents. 3 consolidated skills. ```bash npx skills add SalesforceAIResearch/agentforce-adlc ``` #### SF Skills Official Salesforce skills, prompts, and rules library: metadata creation, Flow development, Apex patterns, and more. ```bash npx skills add forcedotcom/sf-skills ``` ### Start Building Copy and paste this prompt into Cursor's chat (Cmd+L). Optionally enter a project and agent name below; if you leave it blank, the prompt uses "ExampleName". ```text Before anything else, verify prerequisites are installed: 1. Check SF CLI: `sf --version` If not found, install it: `brew install sf` If found, update it: `sf update` 2. Check agentforce-adlc skills: `npx skills list 2>/dev/null | grep -q agentforce-adlc` If not found, install them: `npx skills add SalesforceAIResearch/agentforce-adlc` 3. Check SF CLI auth: `sf org display user --json` If not authenticated, run the SFDX Auth URL command from the setup page. Use the installed agentforce-adlc skills for the full agent lifecycle. Interview me about the agent I want to build: its role, company, and behaviors. Wait for my response before building. Then use the developing-agentforce skill to author, discover, scaffold, and deploy the agent. Use the testing-agentforce skill to run smoke tests and create a test suite. Build real actions, not stubs. Iterate until quality is solid. ``` ## What Happens Next After you paste the prompt, Cursor will use the installed agentforce-adlc skills: 1. **Discover**: Retrieve your agent, parse subagents and actions, validate targets exist in the org 2. **Scaffold**: Generate real backing actions (Flows/Apex) for every subagent, no stubs or mocks 3. **Deploy**: Validate bundle, deploy metadata, publish and activate the agent 4. **Test & Optimize**: Run evaluation tests, analyze traces, fix issues, and iterate until quality is good ## Tips - **Be specific about scope**: "Handle order status and returns" beats "Customer support" - **Say what it should NOT do**: "Does NOT handle billing questions or account changes" - **Mention verification needs**: "Verify identity before showing account details" - **Include state tracking**: "Track the order number across conversation turns so the customer doesn't have to repeat it" ## Setting Up Windsurf for Agentforce # Getting Started with Windsurf Windsurf is Codeium's AI-powered editor with deep agentic capabilities. Follow these steps to set up Agentforce development in Windsurf. ## 1. Install Windsurf #### Homebrew (macOS) ```bash brew install --cask windsurf ``` #### Download Download from [windsurf.com](https://windsurf.com/download) and install. Open Windsurf and sign in to your account. ## 2. Install the Salesforce CLI & Connect Your Org Open Windsurf's integrated terminal (Ctrl+`) and install the Salesforce CLI: #### Homebrew ```bash brew install sf ``` #### npm ```bash npm install -g @salesforce/cli ``` Requires Node.js 18+. **Verify the installation:** Run this command: ```bash sf --version ``` **Expected output:** `@salesforce/cli/` ### Authenticate Your Org Copy the command below and run it in your terminal to authenticate the Salesforce CLI with your org: **Authenticate SF CLI**: Log in on this page to get your pre-filled authentication command. ```bash echo "" | sf org login sfdx-url --sfdx-url-stdin --set-default --alias afdx-lab ``` **Verify your org connection:** Run this command: ```bash sf org display user ``` **Expected output:** Your org username and alias ## 3. Install Salesforce Skills & Start Building Choose a skills library to install. Each provides Salesforce development knowledge via the [Agent Skills](https://agentskills.io) open standard. [Compare all options](/docs/skills). #### Agentforce ADLC Agent Development Life Cycle by Salesforce AI Research: build, deploy, test, and optimize Agentforce agents. 3 consolidated skills. ```bash npx skills add SalesforceAIResearch/agentforce-adlc ``` #### SF Skills Official Salesforce skills, prompts, and rules library: metadata creation, Flow development, Apex patterns, and more. ```bash npx skills add forcedotcom/sf-skills ``` ### Start Building Copy and paste this prompt into Windsurf's Cascade chat (Cmd+L). Optionally enter a project and agent name below; if you leave it blank, the prompt uses "ExampleName". ```text Before anything else, verify prerequisites are installed: 1. Check SF CLI: `sf --version` If not found, install it: `brew install sf` If found, update it: `sf update` 2. Check agentforce-adlc skills: `npx skills list 2>/dev/null | grep -q agentforce-adlc` If not found, install them: `npx skills add SalesforceAIResearch/agentforce-adlc` 3. Check SF CLI auth: `sf org display user --json` If not authenticated, run the SFDX Auth URL command from the setup page. Use the installed agentforce-adlc skills for the full agent lifecycle. Interview me about the agent I want to build: its role, company, and behaviors. Wait for my response before building. Then use the developing-agentforce skill to author, discover, scaffold, and deploy the agent. Use the testing-agentforce skill to run smoke tests and create a test suite. Build real actions, not stubs. Iterate until quality is solid. ``` ## What Happens Next After you paste the prompt, Windsurf will use the installed agentforce-adlc skills: 1. **Discover**: Retrieve your agent, parse subagents and actions, validate targets exist in the org 2. **Scaffold**: Generate real backing actions (Flows/Apex) for every subagent, no stubs or mocks 3. **Deploy**: Validate bundle, deploy metadata, publish and activate the agent 4. **Test & Optimize**: Run evaluation tests, analyze traces, fix issues, and iterate until quality is good ## Tips - **Be specific about scope**: "Handle order status and returns" beats "Customer support" - **Say what it should NOT do**: "Does NOT handle billing questions or account changes" - **Mention verification needs**: "Verify identity before showing account details" - **Include state tracking**: "Track the order number across conversation turns so the customer doesn't have to repeat it" ## Setting Up Antigravity for Agentforce # Getting Started with Antigravity Antigravity is Google's AI-native IDE built around Gemini models. > **ℹ️ Private Preview**: Antigravity is currently in private preview. If you have access, follow the setup instructions provided by Google to install it, then continue with the steps below. ## 1. Install Antigravity Install Antigravity following the instructions from your private preview access. Once installed, open it and sign in with your Google account. ## 2. Install the Salesforce CLI & Connect Your Org Open Antigravity's integrated terminal and install the Salesforce CLI: #### Homebrew ```bash brew install sf ``` #### npm ```bash npm install -g @salesforce/cli ``` Requires Node.js 18+. **Verify the installation:** Run this command: ```bash sf --version ``` **Expected output:** `@salesforce/cli/` ### Authenticate Your Org Copy the command below and run it in your terminal to authenticate the Salesforce CLI with your org: **Authenticate SF CLI**: Log in on this page to get your pre-filled authentication command. ```bash echo "" | sf org login sfdx-url --sfdx-url-stdin --set-default --alias afdx-lab ``` **Verify your org connection:** Run this command: ```bash sf org display user ``` **Expected output:** Your org username and alias ## 3. Install Salesforce Skills & Start Building Choose a skills library to install. Each provides Salesforce development knowledge via the [Agent Skills](https://agentskills.io) open standard. [Compare all options](/docs/skills). #### Agentforce ADLC Agent Development Life Cycle by Salesforce AI Research: build, deploy, test, and optimize Agentforce agents. 3 consolidated skills. ```bash npx skills add SalesforceAIResearch/agentforce-adlc ``` #### SF Skills Official Salesforce skills, prompts, and rules library: metadata creation, Flow development, Apex patterns, and more. ```bash npx skills add forcedotcom/sf-skills ``` ### Start Building Copy and paste this prompt into Antigravity. Optionally enter a project and agent name below; if you leave it blank, the prompt uses "ExampleName". ```text Before anything else, verify prerequisites are installed: 1. Check SF CLI: `sf --version` If not found, install it: `brew install sf` If found, update it: `sf update` 2. Check agentforce-adlc skills: `npx skills list 2>/dev/null | grep -q agentforce-adlc` If not found, install them: `npx skills add SalesforceAIResearch/agentforce-adlc` 3. Check SF CLI auth: `sf org display user --json` If not authenticated, run the SFDX Auth URL command from the setup page. Use the installed agentforce-adlc skills for the full agent lifecycle. Interview me about the agent I want to build: its role, company, and behaviors. Wait for my response before building. Then use the developing-agentforce skill to author, discover, scaffold, and deploy the agent. Use the testing-agentforce skill to run smoke tests and create a test suite. Build real actions, not stubs. Iterate until quality is solid. ``` ## What Happens Next After you paste the prompt, Antigravity will use the installed agentforce-adlc skills: 1. **Discover**: Retrieve your agent, parse subagents and actions, validate targets exist in the org 2. **Scaffold**: Generate real backing actions (Flows/Apex) for every subagent, no stubs or mocks 3. **Deploy**: Validate bundle, deploy metadata, publish and activate the agent 4. **Test & Optimize**: Run evaluation tests, analyze traces, fix issues, and iterate until quality is good ## Tips - **Be specific about scope**: "Handle order status and returns" beats "Customer support" - **Say what it should NOT do**: "Does NOT handle billing questions or account changes" - **Mention verification needs**: "Verify identity before showing account details" - **Include state tracking**: "Track the order number across conversation turns so the customer doesn't have to repeat it" ## LabBox # LabBox A LabBox is a Salesforce org provisioned through Agentforce Labs with every AI feature already enabled. Einstein, Agentforce, Data Cloud, OmniChannel routing, messaging channels, and an Experience Site with a chat widget are all configured and ready to use the moment you sign in. No manual setup required. If you want to replicate the same configuration in your own org, each section below includes step-by-step instructions. > **ℹ️ Already have a LabBox?**: You do not need to enable any of these settings yourself. This page documents what is pre-configured so you can understand the capabilities available to you. ## Core Platform Einstein, Agentforce, and Data Cloud must be turned on before any AI features work. These are the foundational toggles. Everything else depends on them. | Setting | Setup Location | What it does | | ---------- | ---------------------- | -------------------------------------------------------------------------------------------------------------- | | Einstein | Einstein Setup | Enables the Einstein AI platform across the org | | Agentforce | Einstein Copilot Setup | Enables the Agentforce agent runtime and Copilot features | | Data Cloud | Org Provisioning | Enables the Customer Data Platform for unified data access and grounding. Provisioning can take 30–60 minutes. | ### Enable it yourself **Choose one of the following methods:** #### SF CLI ```bash # Verify Data Cloud is provisioned sf data query --target-org {{ORG_ALIAS}} \ --query "SELECT Id, Name, DurableId FROM Organization LIMIT 1" # Verify Einstein limits are available sf org display --target-org {{ORG_ALIAS}} --verbose --json | jq '.result' ``` #### REST API ```bash # Verify Data Cloud curl -s "{{INSTANCE_URL}}/services/data/v65.0/query?q=SELECT+Id,Name,DurableId+FROM+Organization+LIMIT+1" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" # Verify Einstein curl -s "{{INSTANCE_URL}}/services/data/v65.0/limits" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" | jq '.EinsteinGenAiStandard' ``` #### Browser Agent ```text 1. Setup → Data Cloud Setup Home → verify "Home Org" shows Org Details 2. Setup → Quick Find → "Einstein Setup" → verify "Turn on Einstein" toggle is active 3. Setup → Quick Find → "Agentforce Agents" → click toggle in top-right to ON 4. Refresh page and confirm toggle persists ``` ## API Access | Setting | Setup Location | What it does | | -------------- | -------------------- | ------------------------------------------------------------------------------ | | SOAP API Login | User Interface Setup | Allows programmatic login via SOAP API, used by CLI tools and backend services | ### Enable it yourself **Choose one of the following methods:** #### SF CLI ```bash # No CLI equivalent. Use Browser Agent echo "SOAP API Login must be enabled via Setup UI" ``` #### REST API ```bash # No API equivalent. Use Browser Agent echo "SOAP API Login must be enabled via Setup UI" ``` #### Browser Agent ```text 1. Setup → Quick Find → "User Interface" 2. Find "Enable SOAP API Login" checkbox 3. Check the box 4. Click "Save" ``` ## Einstein Trust Layer | Setting | Setup Location | What it does | | ----------------------------- | -------------------- | --------------------------------------------------------------------------------------------------------- | | Large Language Model Data Masking | Einstein Trust Layer | Disabled to allow larger LLM context. Toggle is set to OFF so sensitive data patterns are not masked in prompts. | ### Enable it yourself **Choose one of the following methods:** #### SF CLI ```bash # No CLI equivalent. Use Browser Agent echo "Data Masking must be disabled via Setup UI" ``` #### REST API ```bash # No API equivalent. Use Browser Agent echo "Data Masking must be disabled via Setup UI" ``` #### Browser Agent ```text 1. Setup → Quick Find → "Einstein Trust" 2. Click "Einstein Trust Layer" (under Einstein → Einstein Generative AI) 3. Go to "Data Masking" tab 4. Turn OFF "Large Language Model Data Masking" toggle 5. Confirm if prompted ``` ## Identity Verification | Setting | Setup Location | What it does | | --------------------------------------------- | --------------------- | -------------------------------------------------------------------------------------------- | | Require identity verification for email changes | Identity Verification | Disabled so that org automation can update user email addresses without triggering verification flows. | ### Enable it yourself **Choose one of the following methods:** #### SF CLI ```bash # No CLI equivalent. Use Browser Agent echo "Identity Verification must be configured via Setup UI" ``` #### REST API ```bash # No API equivalent. Use Browser Agent echo "Identity Verification must be configured via Setup UI" ``` #### Browser Agent ```text 1. Setup → Quick Find → "Identity Verification" 2. Scroll to the "General" section at the bottom 3. Uncheck "Require identity verification for email address changes" 4. Click "Save" ``` ## Permissions A custom **AiAdmin** permission set is created with the following system permissions: | Permission | What it grants | | ------------------------- | -------------------------------------------- | | Manage Agentforce Grids | Create and manage Agentforce Grid worksheets | | Manage Agentforce Testing | Run Agent Eval Labs tests against agents | | Manage AI Agents | Create, edit, and publish Agentforce agents | | API Enabled | Programmatic API access for CLI tools and integrations | The following permission sets are assigned to the org user: | Permission Set | Purpose | | --------------------------------------- | ------------------------------------------- | | AiAdmin | AI agent management (see above) | | Data Cloud User | Access to Data Cloud objects and features | | Access Agentforce Optimization | Agent optimization and performance features | | Tableau Next Admin | Tableau analytics administration | | Tableau Next Platform Analyst | Tableau analytics usage and dashboards | | Contact Center Admin | Contact center administration | > **💡 Tip**: The full list of enabled permission sets also includes AI Workbench, Access Agentforce Default Agent, Agent Presence Status Access, Agentforce Default Admin, Agentforce Reference App, Agentforce Service Agent Configuration, Code Builder User, Contact Center Agent, Contact Center Supervisor, Customer Orders, Data Cloud Architect, Einstein Sales Emails, Prompt Template Manager, Prompt Template User, and Tableau Next Consumer. ### Enable it yourself **Choose one of the following methods:** #### SF CLI ```bash # Create AiAdmin permission set EXISTING=$(sf data query --target-org {{ORG_ALIAS}} --json \ --query "SELECT Id FROM PermissionSet WHERE Name='AiAdmin'" \ | jq -r '.result.totalSize') if [ "$EXISTING" = "0" ]; then sf data create record --target-org {{ORG_ALIAS}} \ --sobject PermissionSet \ --values "Name='AiAdmin' Label='AiAdmin' Description='Admin permissions for Agentforce'" fi # Deploy system permissions via Metadata API mkdir -p /tmp/aiadmin-deploy/permissionsets cat > /tmp/aiadmin-deploy/permissionsets/AiAdmin.permissionset-meta.xml << 'XMLEOF' Admin permissions for Agentforce true ManageAgentforceGrids true ManageAgentforceTesting true ManageAiAgents true ApiEnabled XMLEOF cat > /tmp/aiadmin-deploy/package.xml << 'XMLEOF' AiAdmin PermissionSet 65.0 XMLEOF sf project deploy start --target-org {{ORG_ALIAS}} --source-dir /tmp/aiadmin-deploy # Assign AiAdmin + Data Cloud User to admin ADMIN_ID=$(sf data query --target-org {{ORG_ALIAS}} --json \ --query "SELECT Id FROM User WHERE Profile.Name='System Administrator' AND IsActive=true ORDER BY CreatedDate ASC LIMIT 1" \ | jq -r '.result.records[0].Id') PS_ID=$(sf data query --target-org {{ORG_ALIAS}} --json \ --query "SELECT Id FROM PermissionSet WHERE Name='AiAdmin'" \ | jq -r '.result.records[0].Id') sf data create record --target-org {{ORG_ALIAS}} \ --sobject PermissionSetAssignment \ --values "PermissionSetId='$PS_ID' AssigneeId='$ADMIN_ID'" DC_PS_ID=$(sf data query --target-org {{ORG_ALIAS}} --json \ --query "SELECT Id FROM PermissionSet WHERE Label='Data Cloud User' LIMIT 1" \ | jq -r '.result.records[0].Id') sf data create record --target-org {{ORG_ALIAS}} \ --sobject PermissionSetAssignment \ --values "PermissionSetId='$DC_PS_ID' AssigneeId='$ADMIN_ID'" ``` #### REST API ```bash # Create AiAdmin permission set curl -X POST "{{INSTANCE_URL}}/services/data/v65.0/sobjects/PermissionSet" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" \ -H "Content-Type: application/json" \ -d '{"Name":"AiAdmin","Label":"AiAdmin","Description":"Admin permissions for Agentforce","IsOwnedByProfile":false}' # System permissions require Metadata API. Use SF CLI or Browser Agent # Assign to admin user ADMIN_ID=$(curl -s "{{INSTANCE_URL}}/services/data/v65.0/query?q=SELECT+Id+FROM+User+WHERE+Profile.Name='System+Administrator'+AND+IsActive=true+ORDER+BY+CreatedDate+ASC+LIMIT+1" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" | jq -r '.records[0].Id') PS_ID=$(curl -s "{{INSTANCE_URL}}/services/data/v65.0/query?q=SELECT+Id+FROM+PermissionSet+WHERE+Name='AiAdmin'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" | jq -r '.records[0].Id') curl -X POST "{{INSTANCE_URL}}/services/data/v65.0/sobjects/PermissionSetAssignment" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" \ -H "Content-Type: application/json" \ -d "{\"PermissionSetId\":\"$PS_ID\",\"AssigneeId\":\"$ADMIN_ID\"}" ``` #### Browser Agent ```text 1. Setup → Quick Find → "Permission Sets" → New 2. Label: AiAdmin, API Name: AiAdmin → Save 3. Click "System Permissions" → Edit 4. Check: Manage Agentforce Grids, Manage Agentforce Testing, Manage AI Agents, API Enabled 5. Save (confirm if prompted) 6. Setup → Quick Find → "Users" → click admin user 7. Hover "Permission Set Assignments" → Edit Assignments 8. Add: AiAdmin, Data Cloud User 9. Save ``` > **✅ Success**: **Verify:** `SELECT Id, Assignee.Name, PermissionSet.Name FROM PermissionSetAssignment WHERE PermissionSet.Name='AiAdmin' AND Assignee.Profile.Name='System Administrator'`. This should return one record. ## Observability & Analytics These settings enable the full observability stack for agents: monitoring, debugging, and optimization. | Setting | Setup Location | What it does | | ---------------------------------------- | ------------------------------------- | ------------------------------------------------------ | | Einstein Audit and Feedback | Einstein Audit Analytics & Monitoring | Audit logging for all Einstein AI interactions | | Knowledge / RAG Quality Data and Metrics | Einstein Audit Analytics & Monitoring | Quality metrics for RAG-based knowledge retrieval | | Agent Analytics | Einstein Audit Analytics & Monitoring | Agent performance dashboards and usage data | | Agentforce Session Tracing | Einstein Audit Analytics & Monitoring | Session-level debugging traces for agent conversations | | Agent Optimization | Einstein Audit Analytics & Monitoring | AI-driven optimization recommendations | | Service Agent Analytics App | Agent Analytics Setup | Pre-built analytics app for service agent performance | > **💡 Tip**: The Service Agent Analytics app can take up to 15 minutes to fully install after org provisioning because it depends on Agent Analytics being enabled first. ### Enable it yourself **Choose one of the following methods:** #### SF CLI ```bash # Audit toggles require Setup UI. Use Browser Agent # After enabling, assign Access Agentforce Optimization: ADMIN_ID=$(sf data query --target-org {{ORG_ALIAS}} --json \ --query "SELECT Id FROM User WHERE Profile.Name='System Administrator' AND IsActive=true LIMIT 1" \ | jq -r '.result.records[0].Id') OPT_PS_ID=$(sf data query --target-org {{ORG_ALIAS}} --json \ --query "SELECT Id FROM PermissionSet WHERE Label='Access Agentforce Optimization' LIMIT 1" \ | jq -r '.result.records[0].Id') sf data create record --target-org {{ORG_ALIAS}} \ --sobject PermissionSetAssignment \ --values "PermissionSetId='$OPT_PS_ID' AssigneeId='$ADMIN_ID'" ``` #### REST API ```bash # Audit toggles require Setup UI. Use Browser Agent # After enabling, assign Access Agentforce Optimization: ADMIN_ID=$(curl -s "{{INSTANCE_URL}}/services/data/v65.0/query?q=SELECT+Id+FROM+User+WHERE+Profile.Name='System+Administrator'+AND+IsActive=true+LIMIT+1" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" | jq -r '.records[0].Id') OPT_PS_ID=$(curl -s "{{INSTANCE_URL}}/services/data/v65.0/query?q=SELECT+Id+FROM+PermissionSet+WHERE+Label='Access+Agentforce+Optimization'+LIMIT+1" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" | jq -r '.records[0].Id') curl -X POST "{{INSTANCE_URL}}/services/data/v65.0/sobjects/PermissionSetAssignment" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" \ -H "Content-Type: application/json" \ -d "{\"PermissionSetId\":\"$OPT_PS_ID\",\"AssigneeId\":\"$ADMIN_ID\"}" ``` #### Browser Agent ```text 1. Setup → Quick Find → "Audit" → select "Einstein Audit, Analytics, and Monitoring Setup" 2. Enable: Audit and Feedback, Knowledge/RAG Quality Data and Metrics, Agent Analytics, Agentforce Session Tracing 3. Refresh Setup → navigate to Setup → Agent Analytics 4. Wait ~15 minutes if page is blank. Apps need time to appear 5. Once ready: install "Service Agent Analytics" (click down arrow → Install → Data Space: default → Ok) 6. Wait for "Installation Completed Successfully" notification 7. Setup → Users → admin user → Edit Assignments → add "Access Agentforce Optimization" → Save ``` ## Digital Experiences | Setting | Setup Location | What it does | | -------------------------- | -------------------------------------------------- | --------------------------------------------------------------- | | Enable Digital Experiences | Feature Settings → Digital Experiences → Settings | Allows creation of Experience Cloud sites for hosting chat widgets | ### Enable it yourself **Choose one of the following methods:** #### SF CLI ```bash # No CLI equivalent. Use Browser Agent echo "Digital Experiences must be enabled via Setup UI" ``` #### REST API ```bash # No API equivalent. Use Browser Agent echo "Digital Experiences must be enabled via Setup UI" ``` #### Browser Agent ```text 1. Setup → Feature Settings → Digital Experiences → Settings 2. Check "Enable Digital Experiences" 3. Click "Save" 4. A popup will appear asking to enable and register domain. Click "OK" ``` ## Routing OmniChannel routing allows agents to receive and handle conversations. | Setting | Setup Location | What it does | | ---------------------------------------- | -------------------- | ------------------------------------------------------------------- | | Skills-Based and Direct-to-Agent Routing | OmniChannel Settings | Routes conversations to agents based on skills or direct assignment | | Secondary Routing Priority | OmniChannel Settings | Fallback routing when the primary agent is unavailable | A routing configuration and fallback queue are pre-created: | Resource | Name | Details | | --------------------------------- | ---------------------------------- | ------------------------------------------------- | | Routing Configuration | Agentforce Service configuration | Priority 1, Most Available model, capacity 2/3 | | Queue | Agentforce Service Queue | Supports Messaging Session, linked to routing config | ### Enable it yourself **Choose one of the following methods:** #### SF CLI ```bash # Enable OmniChannel via Browser Agent first, then create routing config: sf data create record --target-org {{ORG_ALIAS}} \ --sobject ServiceChannel \ --values "DeveloperName='Agentforce_Service_configuration' MasterLabel='Agentforce Service configuration' RoutingModel='MostAvailable' SecondaryRoutingPriority=1" # Create queue via Metadata API mkdir -p /tmp/queue-deploy/queues cat > /tmp/queue-deploy/queues/Agentforce_Service_Queue.queue-meta.xml << 'XMLEOF' Agentforce_Service_Queue Agentforce Service Queue MessagingSession XMLEOF cat > /tmp/queue-deploy/package.xml << 'XMLEOF' Agentforce_Service_Queue Queue 65.0 XMLEOF sf project deploy start --target-org {{ORG_ALIAS}} --source-dir /tmp/queue-deploy ``` #### REST API ```bash # Create routing configuration curl -X POST "{{INSTANCE_URL}}/services/data/v65.0/sobjects/ServiceChannel" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" \ -H "Content-Type: application/json" \ -d '{"DeveloperName":"Agentforce_Service_configuration","MasterLabel":"Agentforce Service configuration","RoutingModel":"MostAvailable","SecondaryRoutingPriority":1}' # Create queue curl -X POST "{{INSTANCE_URL}}/services/data/v65.0/sobjects/Group" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" \ -H "Content-Type: application/json" \ -d '{"Name":"Agentforce Service Queue","DeveloperName":"Agentforce_Service_Queue","Type":"Queue"}' # Add MessagingSession as supported object QUEUE_ID=$(curl -s "{{INSTANCE_URL}}/services/data/v65.0/query?q=SELECT+Id+FROM+Group+WHERE+DeveloperName='Agentforce_Service_Queue'+AND+Type='Queue'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" | jq -r '.records[0].Id') curl -X POST "{{INSTANCE_URL}}/services/data/v65.0/sobjects/QueueSobject" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" \ -H "Content-Type: application/json" \ -d "{\"QueueId\":\"$QUEUE_ID\",\"SobjectType\":\"MessagingSession\"}" ``` #### Browser Agent ```text 1. Setup → Feature Settings → Service → Omni-Channel Settings 2. Check "Enable Skills-Based and Direct-to-Agent Routing" and "Enable Secondary Routing Priority" → Save 3. Setup → Feature Settings → Service → Messaging → Messaging Settings → toggle ON 4. Setup → Quick Find → "Routing Configuration" → New 5. Name: Agentforce Service configuration, Priority: 1, Model: Most Available, Capacity: 2/3 → Save 6. Setup → Quick Find → "Queues" → New 7. Label: Agentforce Service Queue, Supported Objects: Messaging Session, Routing Config: Agentforce Service configuration, Members: add admin → Save ``` > **✅ Success**: **Verify:** `SELECT Id, DeveloperName FROM ServiceChannel WHERE DeveloperName='Agentforce_Service_configuration'` and `SELECT Id, DeveloperName FROM Group WHERE Type='Queue' AND DeveloperName='Agentforce_Service_Queue'`. Both should return one record. ## Messaging | Setting | Setup Location | What it does | | ----------------- | ------------------------------------------------------ | ---------------------------------------------------------------- | | Enable Messaging | Feature Settings → Service → Messaging → Messaging Settings | Enables the Messaging platform for agent chat conversations | A messaging channel is pre-created: | Field | Value | | ----------------- | ---------------------------- | | Channel Name | Salesforce Agent Channel | | Developer Name | Salesforce_Agent | | Type | Enhanced Chat | | Deployment Type | Web | | Routing Type | Agentforce Service Agent | | Fallback Queue | Agentforce Service Queue | ### Enable it yourself **Choose one of the following methods:** #### SF CLI ```bash BOT_ID=$(sf data query --target-org {{ORG_ALIAS}} --json \ --query "SELECT Id FROM BotDefinition WHERE DeveloperName='{{AGENT_NAME}}'" \ | jq -r '.result.records[0].Id') QUEUE_ID=$(sf data query --target-org {{ORG_ALIAS}} --json \ --query "SELECT Id FROM Group WHERE Type='Queue' AND DeveloperName='Agentforce_Service_Queue'" \ | jq -r '.result.records[0].Id') sf apex run --target-org {{ORG_ALIAS}} << EOF MessagingChannel channel = new MessagingChannel(); channel.DeveloperName = 'Salesforce_Agent'; channel.MasterLabel = 'Salesforce Agent Channel'; channel.MessageType = 'EmbeddedMessaging'; channel.MessagingPlatformKey = 'Salesforce_Agent-' + String.valueOf(Crypto.getRandomLong()); channel.IsActive = true; channel.SessionHandlerId = '$BOT_ID'; channel.FallbackQueueId = '$QUEUE_ID'; insert channel; System.debug('Created MessagingChannel: ' + channel.Id); EOF ``` #### REST API ```bash BOT_ID=$(curl -s "{{INSTANCE_URL}}/services/data/v65.0/query?q=SELECT+Id+FROM+BotDefinition+WHERE+DeveloperName='{{AGENT_NAME}}'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" | jq -r '.records[0].Id') QUEUE_ID=$(curl -s "{{INSTANCE_URL}}/services/data/v65.0/query?q=SELECT+Id+FROM+Group+WHERE+Type='Queue'+AND+DeveloperName='Agentforce_Service_Queue'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" | jq -r '.records[0].Id') curl -X POST "{{INSTANCE_URL}}/services/data/v65.0/sobjects/MessagingChannel" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" \ -H "Content-Type: application/json" \ -d "{\"DeveloperName\":\"Salesforce_Agent\",\"MasterLabel\":\"Salesforce Agent Channel\",\"MessageType\":\"EmbeddedMessaging\",\"MessagingPlatformKey\":\"Salesforce_Agent-$(date +%s)\",\"IsActive\":true,\"SessionHandlerId\":\"$BOT_ID\",\"FallbackQueueId\":\"$QUEUE_ID\"}" ``` #### Browser Agent ```text 1. Setup → Feature Settings → Service → Messaging → Messaging Settings 2. Click "New Channel" → Select "Enhanced Chat" → Next 3. Channel Name: Salesforce Agent Channel, Developer Name: Salesforce_Agent 4. Deployment Type: Web, Domain: 5. Routing Type: Agentforce Service Agent, Agent: {{AGENT_NAME}} 6. Fallback Queue: Agentforce Service Queue → Save ``` ## Embedded Service Deployment An Embedded Service Deployment container is pre-created to host the chat widget. | Field | Value | | ------------------------------- | ------------------------------------ | | Embedded Service Deployment Name | Salesforce Agent Web Deployment | | API Name | Salesforce_Agent | | Type | Enhanced Chat | | Messaging Channel | Salesforce Agent Channel | > **ℹ️ Info**: After creation the deployment is switched to V2 and published so the chat widget is immediately available. ### Enable it yourself **Choose one of the following methods:** #### SF CLI ```bash # Embedded Service Deployments are best created via the Setup UI wizard echo "Use Browser Agent for Embedded Service Deployment creation" ``` #### REST API ```bash # Embedded Service Deployments are best created via the Setup UI wizard echo "Use Browser Agent for Embedded Service Deployment creation" ``` #### Browser Agent ```text 1. Setup → Embedded Service → Embedded Service Deployments → New Deployment 2. Select "Enhanced Chat" → Select "Web" 3. Name: Salesforce Agent Web Deployment, API Name: Salesforce_Agent 4. Domain: , Messaging Channel: Salesforce Agent Channel → Save 5. Click "Switch to V2" (top right) 6. Click "Publish" (top right) ``` ## Experience Site An Experience Cloud site is pre-created with the chat widget embedded. | Field | Value | | ---------------- | ---------------------------- | | Site Name | Salesforce Agent | | Template | Build Your Own (LWR) | | Public Access | Enabled | | Enhanced Chat | Placed in Footer, linked to Salesforce Agent Web Deployment | ### Enable it yourself **Choose one of the following methods:** #### SF CLI ```bash # Create site sf community create --target-org {{ORG_ALIAS}} \ --name "Salesforce Agent" \ --template-name "Build Your Own (LWR)" \ --url-path-prefix "salesforceagent" # After adding Enhanced Chat component via Builder, publish: sf community publish --target-org {{ORG_ALIAS}} \ --name "Salesforce Agent" ``` #### REST API ```bash # Publish via Connect API (after creating site and adding component via Builder) NETWORK_ID=$(curl -s "{{INSTANCE_URL}}/services/data/v65.0/query?q=SELECT+Id+FROM+Network+WHERE+Name='Salesforce+Agent'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" | jq -r '.records[0].Id') curl -X POST "{{INSTANCE_URL}}/services/data/v65.0/connect/communities/$NETWORK_ID/publish" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" \ -H "Content-Type: application/json" ``` #### Browser Agent ```text 1. Setup → Digital Experiences → All Sites → New 2. Select "Build Your Own (LWR)" → Get Started → Name: Salesforce Agent → Create 3. In Experience Builder: Settings → General → enable "Public Access" 4. Drag "Enhanced Chat" component into Footer → select "Salesforce Agent Web Deployment" 5. Click "Publish" → confirm ``` > **✅ Success**: **Verify:** `SELECT Id, Name, Status FROM Network WHERE Name='Salesforce Agent'`. This should return one record with Status = 'Live'. ## Site Domain & Trusted Domains The Salesforce Site domain is registered and trusted domains are configured so the chat widget loads without CORS errors. | Setting | Setup Location | What it does | | ---------------------- | -------------------------------------- | ------------------------------------------------------------- | | Site Domain | User Interface → Sites & Domains → Sites | Registered so Experience Sites have a public URL | | Trusted Domain | Sites & Domains → Sites | `*.my.site.com` added to trusted domains for inline frames | > **⚠️ Trusted domains are critical**: Without the trusted domain entry, the chat widget will fail silently. No button appears and no errors are shown to the user. This is the most common cause of "chat doesn't work" issues. ### Enable it yourself **Choose one of the following methods:** #### SF CLI ```bash # Register domain (check if already exists) sf data query --target-org {{ORG_ALIAS}} \ --query "SELECT Id, Domain FROM Domain WHERE DomainType='DNS' LIMIT 1" # Add trusted domain sf data create record --target-org {{ORG_ALIAS}} \ --sobject CorsWhitelistEntry \ --values "UrlPattern='https://*.my.site.com'" # Republish site after adding trusted domain sf community publish --target-org {{ORG_ALIAS}} \ --name "Salesforce Agent" ``` #### REST API ```bash curl -X POST "{{INSTANCE_URL}}/services/data/v65.0/sobjects/CorsWhitelistEntry" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" \ -H "Content-Type: application/json" \ -d '{"UrlPattern":"https://*.my.site.com"}' # Republish site NETWORK_ID=$(curl -s "{{INSTANCE_URL}}/services/data/v65.0/query?q=SELECT+Id+FROM+Network+WHERE+Name='Salesforce+Agent'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" | jq -r '.records[0].Id') curl -X POST "{{INSTANCE_URL}}/services/data/v65.0/connect/communities/$NETWORK_ID/publish" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" \ -H "Content-Type: application/json" ``` #### Browser Agent ```text 1. Setup → User Interface → Sites and Domains → Sites 2. Accept terms → Click "Register My Salesforce Site Domain" → accept popup 3. Find "ESW_Salesforce_Agent_*" → "Trusted Domains for Inline Frames" → Add Domain: *.my.site.com 4. Republish Experience Site: Setup → Digital Experiences → All Sites → Salesforce Agent → Builder → Publish ``` ## Agent Deployment A default Agentforce Service Agent is deployed, compiled, and activated in the org. | Step | What happens | | ------------------ | ----------------------------------------------------------------- | | Metadata Deploy | Agent bundle deployed via Metadata API | | Script Compilation | AgentScript compiled via SFAP authoring endpoint | | Version Publish | Agent version published and linked to the org instance | | Activation | Bot version set to Active so the agent can receive conversations | ### Enable it yourself **Choose one of the following methods:** #### SF CLI ```bash # 1. Deploy agent metadata sf project deploy start --target-org {{ORG_ALIAS}} \ --source-dir data/AgentforceDotCom # 2. Get session and instance SESSION_ID=$(sf org display --target-org {{ORG_ALIAS}} --json | jq -r '.result.accessToken') INSTANCE=$(sf org display --target-org {{ORG_ALIAS}} --json | jq -r '.result.instanceUrl') # 3. Get NamedUser JWT JWT=$(curl -s "$INSTANCE/agentforce/bootstrap/nameduser" \ -H "Cookie: sid=$SESSION_ID" \ -H "Accept: application/json" | jq -r '.access_token') # 4. Compile AgentScript SCRIPT=$(cat data/AgentforceDotCom/aiAuthoringBundles/{{AGENT_NAME}}/{{AGENT_NAME}}.agent) COMPILED=$(curl -s -X POST "https://api.salesforce.com/einstein/ai-agent/v1.1/authoring/scripts" \ -H "Authorization: Bearer $JWT" \ -H "Content-Type: application/json" \ -H "x-client-name: org-recipe" \ -d "{\"assets\":[{\"type\":\"AFScript\",\"name\":\"AFScript\",\"content\":$(echo "$SCRIPT" | jq -Rs .)}],\"afScriptVersion\":\"1.0.1\"}") ARTIFACT=$(echo "$COMPILED" | jq '.compiledArtifact') # 5. Publish agent version PUBLISH=$(curl -s -X POST "https://api.salesforce.com/einstein/ai-agent/v1.1/authoring/agents" \ -H "Authorization: Bearer $JWT" \ -H "Content-Type: application/json" \ -d "{\"agentDefinition\":$ARTIFACT,\"agentVersion\":\"1\",\"instanceConfig\":{\"endpoint\":\"$INSTANCE\"}}") BOT_VERSION_ID=$(echo "$PUBLISH" | jq -r '.botVersionId') # 6. Activate curl -X POST "$INSTANCE/services/data/v65.0/connect/bot-versions/$BOT_VERSION_ID/activation" \ -H "Authorization: Bearer $SESSION_ID" \ -H "Content-Type: application/json" \ -d '{"status":"ACTIVE"}' ``` #### REST API ```bash # Same flow as SF CLI. Deploy ZIP via SOAP Metadata API, then: # GET {instance}/agentforce/bootstrap/nameduser → JWT # POST https://api.salesforce.com/einstein/ai-agent/v1.1/authoring/scripts → compile # POST https://api.salesforce.com/einstein/ai-agent/v1.1/authoring/agents → publish # POST /services/data/v65.0/connect/bot-versions/{botVersionId}/activation → activate ``` #### Browser Agent ```text 1. Setup → Agents → Agent Builder 2. Import or create the agent from metadata 3. Click "Activate" or "Publish" 4. Verify status shows "Active" ``` ## MCP Servers MCP (Model Context Protocol) servers expose Salesforce data and actions to agents as callable tools. Each server bundles a set of related tools an agent can invoke at runtime. | Setting | Setup Location | What it does | | ------------------------- | ------------------------------------------------- | --------------------------------------- | | Enable MCP Service (Beta) | Setup → User Interface | Turns on the MCP Service for the org | | Activate servers | Setup → Integrations → API Catalog → MCP Servers | Exposes each server's tools to agents | MCP is available on **Enterprise, Developer, and Unlimited editions** (GA, April 2026), so a freshly provisioned LabBox already has it. To turn it on, check the **"Enable MCP Service (Beta)"** preference under **Setup → User Interface**, then activate the servers you want. Standard MCP servers ship **Inactive by default** — each one must be activated individually in Setup before its tools become available to agents. There is **no Metadata API type or Tooling-API field** to activate a server programmatically — activation is UI-only. | Server | Tools | What it exposes | | ----------------------- | ---------------------- | --------------------------------------------------------------------- | | sobject-reads | 6 tools | Read access to standard and custom sObject records | | sobject-mutations | 9 tools | Create and update sObject records | | sobject-deletes | 8 tools | Delete sObject records | | sobject-all | 11 tools + 2 prompts | Combined read, mutate, and delete access with guided prompts | | data-cloud-queries | 2 tools | Query Data Cloud objects and unified profiles | | metadata-experts | 1 tool | Inspect org metadata | | salesforce-api-context | 6 tools | Salesforce API reference and context for grounding agent responses | ### Enable it yourself **Choose one of the following methods:** #### SF CLI ```bash # No CLI equivalent. MCP server activation is UI-only. # There is no Metadata API type or Tooling-API field to activate a server. echo "MCP Servers must be activated via Setup UI" ``` #### REST API ```bash # No API equivalent. MCP server activation is UI-only — there is no supported # Tooling/Metadata API object to query or toggle a server's activation state. echo "MCP Servers must be activated via Setup UI" ``` #### Browser Agent ```text 1. Setup → User Interface → check "Enable MCP Service (Beta)" 2. Setup → Integrations → API Catalog → MCP Servers 3. Open the "Salesforce Servers" tab 4. Click a server (e.g. sobject-reads) → "Activate" → confirm 5. Repeat step 4 for each server your agents need ``` > **ℹ️ Client auth (External Client App)**: External MCP clients authenticate with an **External Client App (ECA)** using OAuth scopes `api, sfap_api, refresh_token, einstein_gpt_api` (keep `refresh_token` or clients disconnect frequently). Runtime endpoints are `https://api.salesforce.com/platform/mcp/v1/platform/` for standard servers and `/v1/custom/` for custom ones. ## Agentforce Coworker Agentforce Coworker is a one-click enablement that provisions an end-to-end coworker experience: Agentforce Studio, CRM connectivity, search infrastructure, the coworker agent, personalization, and Data 360 user ingestion with identity resolution. | Setting | Setup Location | What it does | | -------------------------- | -------------------------------------------------- | -------------------------------------------------------------------- | | Enable Agentforce Coworker | Setup → Agentforce Coworker → Get Started → Turn On | Provisions the end-to-end coworker experience for the org | | Component | What it sets up | | --------------------- | --------------------------------------------------------------------- | | Agentforce Studio | The authoring and management surface for the coworker agent | | CRM connectivity | Connects the coworker to your CRM data | | Search infrastructure | AI search indexing that grounds coworker responses | | Coworker agent | The pre-built Agentforce Coworker agent | | Personalization | Per-user tailoring of coworker behavior | | Data 360 ingestion | User ingestion and identity resolution via Data 360 | **Prerequisites:** | Prerequisite | Notes | | ------------------------------- | ----------------------------------------------------------------------------------------------------------- | | `AgenticEnterpriseSearchAddOn` | The binding add-on license (paired with `EinsteinAgentPlatform`). Auto-provisions the AI Search permission set licenses. Bundled on Flex Foundations / A1E / A4X / Agentforce for Service or Sales; otherwise add it (internally via Black Tab, customers via their SKU). | | Data 360 / Data Cloud | A Data Cloud data space is required. Verify a Data 360 app appears in the App Launcher. | | Beta GenAI Models | Setup → Einstein Setup → "Beta Generative AI Models" enabled | | Role | Data 360 Admin or Data 360 Architect | > **ℹ️ The license is the gate, not the PSLs**: The `AgenticEnterpriseSearchAddOn` license **auto-provisions** the permission set licenses behind Coworker — `AI Search Permission Set License` (`AISearchUserPsl`) and `AI Search Setup Permission Set License` (`AISearchAdminPsl`). Admins do **not** request these PSLs directly; provisioning the add-on license grants them. Note that the add-on is a SKU/provisioning-level grant — it does **not** appear as a row in `PermissionSetLicense`; the queryable artifacts are the two AI Search PSLs named above. **Permissions:** | To do this | Assign | | ------------------- | ----------------------------------------------------------------------------------------------- | | Configure Coworker | The **`Agentforce Coworker Admin`** permission set. (The `Agentforce Coworker Setup` PSL is consumed automatically — no manual step.) | | Use Coworker | Grant end-user access via the **"Manage Access to Agentforce Coworker"** section of Coworker Setup (it assigns the AI Search access permission set group created during setup — confirm the exact name in your org). Do **not** assign the bare "Agentforce Coworker User" / "Ask Agentforce User" permission *set*; it grants the UI but not the agent. | > **ℹ️ Legacy \**: On orgs not yet on the 262 release, this feature and its permission sets may still appear under the legacy **"Ask Agentforce"** label — it is the same product (the rename rolled out around early June). The "Get Started with Agentforce Coworker" Setup node only appears **after** the `Agentforce Coworker Admin` permission set is assigned and the page is refreshed. ### Enable it yourself **Choose one of the following methods:** #### SF CLI ```bash # The add-on license is provisioned via Black Tab (internal) or your SKU, # then Coworker is turned on via the Setup UI. But you can verify the AI Search # permission set licenses landed in the org: sf data query --target-org {{ORG_ALIAS}} \ --query "SELECT DeveloperName, MasterLabel FROM PermissionSetLicense WHERE DeveloperName IN ('AISearchUserPsl','AISearchAdminPsl')" ``` #### REST API ```bash # Enablement is UI-only. Verify the AI Search permission set licenses are present: curl -s "{{INSTANCE_URL}}/services/data/v65.0/query?q=SELECT+DeveloperName,MasterLabel+FROM+PermissionSetLicense+WHERE+DeveloperName+IN+('AISearchUserPsl','AISearchAdminPsl')" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" ``` #### Browser Agent ```text 1. Confirm prerequisites: qualifying edition (EE/Unlimited/A1), AgenticEnterpriseSearchAddOn license present, a Data Cloud data space, and Beta Generative AI Models enabled 2. Assign the "Agentforce Coworker Admin" permission set to yourself, then refresh the page 3. Setup → Quick Find → "Agentforce Coworker" (or "Ask Agentforce" on pre-262 orgs) 4. Set Up Data Space → pick your Data Cloud data space 5. Click "Get Started" → "Turn On" → confirm 6. Enable the Global Search Bar 7. Grant end users access via "Manage Access to Agentforce Coworker" (assigns the AI Search access permission set group) ``` > **ℹ️ Reference**: Salesforce's public guide: [Turn On Agentforce Coworker Infrastructure](https://developer.salesforce.com/docs/data/agentforce-coworker/guide/agentforce-coworker-turn-on-infrastructure.html). ## Agentforce Voice Agentforce Voice brings agent conversations to voice channels. | Setting | Setup Location | What it does | | ------------------- | --------------------------------------- | --------------------------------------------------------- | | Add a voice connection | Agent Builder → open agent → Voice Settings | Unlocks Voice for the agent (Telephony or Enhanced Chat v2) | You unlock **Voice Settings** in Agent Builder by adding a voice **connection** to your agent. There are two enablement paths: | Path | What it gives you | Setup effort | | ----------------------- | ------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Telephony connection | Full voice agent over PSTN/SIP (phone calls) | Separate manual setup — AWS Contact Center, call flows, and procuring phone numbers (not automated). OrgFarm offers an "AgentforceVoice" checkout template for voice-enabled orgs. | | Enhanced Chat v2 (ECv2) | "Voice in chat" — microphone input within the Enhanced Chat v2 window | Lighter path; "Voice-Enabled Agents in Enhanced Chat v2" (Summer '26 release) | > **⚠️ Full telephony is a separate manual setup**: Telephony voice (AWS Contact Center, call flows, and phone numbers) is **not pre-configured in a LabBox** and is not automated — it is a separate manual setup that takes roughly 40 minutes end to end. For a ready-made voice-enabled org, use the OrgFarm "AgentforceVoice" checkout template instead of building telephony by hand. ### Required licenses and permission Agentforce Voice requires two licenses in the org: an **Agentforce** license and a **Service Cloud Voice** license. Agentforce Voice is the AI voice-agent layer that runs on top of Service Cloud Voice telephony, so both must be present. > **ℹ️ Reference**: See Salesforce's [Agentforce Voice prerequisites](https://help.salesforce.com/s/articleView?id=ai.agentforce_voice_setup_prereqs.htm&type=5) for the current license and permission requirements. ### Enable it yourself **Choose one of the following methods:** #### SF CLI ```bash # The Agentforce + Service Cloud Voice licenses are provisioned commercially # (Black Tab internal, or your Voice SKU), not via CLI. But you can verify # whether Service Cloud Voice is switched on in the org: sf data query --use-tooling-api --target-org {{ORG_ALIAS}} \ --query "SELECT IsServiceCloudVoiceEnabled FROM ServiceCloudVoiceSettings" ``` #### REST API ```bash # Licenses are provisioned commercially, not via API. Verify whether # Service Cloud Voice is enabled in the org: curl -s "{{INSTANCE_URL}}/services/data/v65.0/tooling/query?q=SELECT+IsServiceCloudVoiceEnabled+FROM+ServiceCloudVoiceSettings" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" ``` #### Browser Agent ```text 1. Ensure the org has an Agentforce license and a Service Cloud Voice license 2. Open the agent in Agent Builder 3. Add a Voice connection (Telephony or Enhanced Chat v2) to unlock Voice Settings 4. For full telephony, complete the separate AWS Contact Center / phone-number setup, or check out an OrgFarm "AgentforceVoice" template org ``` ## Vibes Vibes is Salesforce's in-org agentic coding experience. It comes in two surfaces: **Vibes Chat**, which runs in **VS Code Desktop**, and **Vibes IDE**, which is **browser-based and launched from within the org**. | Setting | Setup Location | What it does | | ------- | ---------------------------------------------------------------- | ------------------------------------- | | Vibes | Launched in-org (Vibes IDE) or VS Code Desktop (Vibes Chat) | In-org agentic coding experience | | Aspect | Detail | | ----------------- | --------------------------------------------------------------------------------------- | | Surfaces | Vibes Chat (VS Code Desktop) and Vibes IDE (browser, launched from within the org) | | Pricing | Free in Developer Edition — no purchase required | | Model | Anthropic Claude Sonnet (current generation) | | Lifetime limit | 110 requests / 1.5M tokens per org — one-time, does not refresh, no fallback model | _Model and limits last verified June 2026; both are subject to change — confirm the current values in your org._ > **⚠️ Developer Edition limits and exclusions**: The Developer Edition allowance is a **one-time lifetime cap of 110 requests / 1.5M tokens per org**. It does **not** refresh, and once it is exhausted there is **no fallback model** — Vibes simply stops. Developer Edition orgs **cannot** receive the unmetered per-user-per-month (PUPM) license because there is no billing mechanism in DE; ISVs likewise cannot get unmetered access in DE orgs. Vibes is **not available in the EU Operating Zone or Government Cloud**. ### Enable it yourself **Choose one of the following methods:** #### SF CLI ```bash # No CLI equivalent. Vibes is available in-org by edition and region, # not provisioned via API. echo "Vibes availability is determined by org edition and region" ``` #### REST API ```bash # No API equivalent. Vibes is available in-org by edition and region, # not provisioned via API. echo "Vibes availability is determined by org edition and region" ``` #### Browser Agent ```text 1. Vibes IDE: launch it from within the org 2. Vibes Chat: use the Salesforce extension in VS Code Desktop ``` > **✅ Ready to build**: Your LabBox comes pre-configured with all of the above, including a working agent, messaging channel, and Experience Site with a chat widget. If any of these settings appear disabled, contact the Agentforce Labs team. ## Agent Script # Agent Script Agent Script is a programming language purpose-built for AI agents. It gives you deterministic control over workflows while letting the LLM reason where it matters. One file defines your entire agent: subagents, actions, transitions, and guardrails. Syntax designed so product managers, architects, and developers can all understand what an agent does at a glance. Business-critical logic runs before the LLM. Conditionals, transitions, and action chains execute exactly as written. `.agent` files live in your repo. Version, diff, review, and deploy agents the same way you ship code. ## Your First Agent in 30 Lines Here's a complete agent that routes customer requests, verifies identity, and manages orders: ```yaml config: developer_name: "Support_Agent" agent_type: "AgentforceServiceAgent" system: instructions: "You are a customer support agent. Be concise and helpful." start_agent topic_selector: description: "Welcome the user and route to the right subagent" reasoning: instructions: -> | Welcome the user and determine what they need help with. | Use the available actions to route them appropriately. actions: go_to_orders: @utils.transition to @subagent.Order_Management description: "Handles order lookup, refunds, and updates" go_to_faq: @utils.transition to @subagent.General_FAQ description: "Answers common questions about products and policies" go_to_escalation: @utils.transition to @subagent.Escalation description: "Connects user with a human representative" ``` That's it. The agent greets the user, the LLM classifies their intent, and they're routed to the right subagent. No framework, no boilerplate, no deployment pipeline to configure. ## Two Types of Instructions Agent Script's power comes from separating **what's deterministic** from **what needs AI reasoning**. Two arrow types make this explicit: #### Logic (Deterministic) ```yaml # -> means "run this code exactly" reasoning: instructions: -> run @actions.lookup_order with email=@variables.customer_email set @variables.order=@outputs.order_data if @variables.order is None: | We couldn't find an order with that email. Can you double-check? else: | Here's what we found for your order: {!@variables.order} ``` Logic instructions execute **before** the LLM sees anything. Actions run, variables are set, conditionals branch — all deterministically. #### Prompt (AI Reasoning) ```yaml # | means "send this to the LLM" reasoning: instructions: -> | The customer is asking about their order. | Review the order details in {!@variables.order} and help them with whatever they need — tracking, modifications, or returns. | If they seem frustrated, proactively offer to escalate using {!@actions.go_to_escalation}. ``` Prompt instructions are natural language sent to the LLM. The AI interprets them, reasons about context, and decides how to respond. #### Mixed (Both Together) ```yaml reasoning: instructions: -> # Deterministic: always fetch the customer's tier run @actions.get_loyalty_tier with customer_id=@variables.customer_id set @variables.tier=@outputs.tier # AI reasoning: let the LLM decide what to do with it | The customer's loyalty tier is {!@variables.tier}. | Adjust your tone and offers accordingly — Platinum members get priority treatment and exclusive offers. ``` Mix both freely. Run an action deterministically, then hand the results to the LLM for interpretation. ## Patterns That Ship #### Action Chaining Run multiple actions in guaranteed sequence. No hoping the LLM remembers step 2. ```yaml reasoning: instructions: -> # Step 1: Get the order run @actions.lookup_current_order with member_email=@variables.member_email set @variables.order_summary=@outputs.order_summary # Step 2: Check return eligibility (uses step 1's output) run @actions.check_return_eligibility with order_id=@variables.order_summary set @variables.eligible=@outputs.eligible | Show the order summary and return eligibility to the customer. ``` #### Conditional Routing Route users based on state — not LLM guesswork. ```yaml reasoning: instructions: -> if @variables.loyalty_tier == "Platinum VIP": transition to @subagent.vip_support if @variables.is_verified == False: transition to @subagent.identity_verification | Welcome back! How can I help you today? ``` Transitions happen before the LLM processes any other instructions. #### Data Hydration Load data before the LLM starts thinking. Use `before_reasoning` to hydrate context on every turn: ```yaml before_reasoning: -> if @variables.customer_profile == "": run @actions.get_customer_profile with user_id=@variables.user_id set @variables.customer_profile=@outputs.profile reasoning: instructions: -> | You're helping {!@variables.customer_profile}. | Reference their history when making recommendations. ``` The `before_reasoning` block runs before every reasoning turn — the LLM always has fresh data. #### Filtered Visibility Control which tools the LLM can see based on state. Unverified users can't access order management. ```yaml reasoning: actions: go_to_identity: @utils.transition to @subagent.Identity description: "Verifies user identity" available when @variables.verified == False go_to_order: @utils.transition to @subagent.Order_Management description: "Handles order management" available when @variables.verified == True ``` #### Action + Transition Define follow-up actions that fire automatically when the LLM calls a tool: ```yaml reasoning: instructions: -> | If the user wants information, use {!@actions.my_action}. actions: my_action: @actions.my_action with foo=@variables.Foo set @variables.status = @outputs.status run @actions.other_action set @variables.some_other_result=@outputs.data ``` Whenever the LLM calls `my_action`, `other_action` fires automatically afterward. No extra prompt needed. ## Building Blocks #### Variables Variables track state across subagents and conversation turns. Three types cover every use case: ```yaml variables: # Linked: populated from external context (messaging session, API) user_id: linked string description: "User identifier from the messaging session" # Mutable: your agent's working memory verified: mutable boolean = False description: "Whether identity verification passed" order_data: mutable string description: "Current order details" # Slot-fill: LLM extracts from conversation customer_email: mutable string = ... description: "Customer's email address" ``` The `...` token tells the LLM to extract the value from conversation. When the user says "my email is jane@example.com", the agent captures it automatically. #### Subagents Each subagent is a self-contained unit with its own instructions, actions, and transitions. Think of them as microservices for conversation: ```yaml subagent Identity_Verification: description: "Verify the customer's identity before accessing account data" reasoning: instructions: -> | Ask the customer for their email address. | Then use {!@actions.send_code} to send a verification code. | Once they provide the code, use {!@actions.verify_code}. actions: send_code: @actions.Send_Verification_Code with email=@variables.customer_email set @variables.auth_key=@outputs.auth_key verify_code: @actions.Verify_Code with code=@variables.customer_code with auth_key=@variables.auth_key set @variables.verified=@outputs.is_verified after_reasoning: -> if @variables.verified == True: transition to @subagent.topic_selector ``` The `after_reasoning` block runs after the LLM completes its turn — perfect for cleanup, state updates, and automatic transitions. #### System & Personas Override global behavior with per-subagent system instructions. VIP customers get a completely different personality without affecting other subagents: ```yaml system: instructions: "You are a helpful customer support agent for Acme Corp." subagent VIP_Support: description: "Premium support for VIP customers" system: instructions: -> | You are a premium concierge for Acme Corp's VIP program. | Address the customer by name. Offer proactive solutions. | You have authority to issue credits up to $500. reasoning: instructions: -> | Provide white-glove service to {!@variables.customer_name}. ``` #### Actions Actions are how your agent interacts with the world. Agent Script supports many target protocols: ```yaml actions: # Call an Apex class get_profile: @actions.Get_Customer_Profile target: "apex://c__CustomerActions.getProfile" # Trigger a Flow process_refund: @actions.Issue_Refund target: "flow://Issue_Refund" # Call an external API via External Services lookup_orders: @actions.Lookup_Orders target: "externalService://OrdersAPI.listOrders" # Use a Prompt Template summarize: @actions.Summarize_Case target: "prompt://Case_Summary_Template" ``` Apex, Flows, External Services, Prompt Templates, and more — all invoked with the same `@actions` syntax. Your agent doesn't care where the logic lives. ## Deploy from Your IDE Agent Script files are standard source — build, test, and deploy using the tools you already know: #### CLI ```bash # Publish your agent to a Salesforce org sf agent generate -d my-agent.agent sf agent publish -d force-app # Run automated tests sf agent test run --name "My_Agent_Tests" ``` #### Claude Code ```bash # Install Salesforce skills (19 skills, agents, hooks, LSP) curl -sSL https://raw.githubusercontent.com/SalesforceAIResearch/agentforce-adlc/main/tools/install.sh | bash # Build, publish, and test — all from natural language claude "Build a support agent that handles order tracking and returns. Deploy it to my org and run tests." # Or run the CLI directly sf agent publish authoring-bundle --api-name Support_Agent sf agent test run --api-name Support_Agent_Tests --wait 10 ``` ## What's Coming Next > **ℹ️ On the Roadmap**: Agent Script is evolving fast. Here's what's shipping soon: > > - **MCP Integration** — Connect agents directly to Model Context Protocol servers. Your agent will be able to call external tools, databases, and APIs through the standard MCP interface, making it a first-class citizen in the broader AI tooling ecosystem. > - **Enhanced Flexibility** — More expressive control flow, richer variable types, and deeper integration with Salesforce data models. Write less configuration, get more capability. > - **Cross-Agent Orchestration** — Agents that can invoke other agents as tools, enabling multi-agent architectures for complex enterprise workflows. ## Quick Reference | Symbol | Meaning | Example | |--------|---------|---------| | `->` | Logic instructions (deterministic) | `instructions: ->` | | `\|` | Prompt instructions (sent to LLM) | `\| Help the customer with their order` | | `@variables.` | Reference a variable | `@variables.customer_email` | | `@actions.` | Reference an action | `@actions.Get_Order` | | `@subagent.` | Delegate to another subagent | `@subagent.Order_Management` | | `@outputs.` | Reference action output | `@outputs.order_data` | | `@utils.` | Utility functions | `@utils.transition to @subagent.FAQ` | | `{!expr}` | Interpolate in prompts | `{!@variables.name}` | | `...` | LLM slot-fill token | `with order_id = ...` | | `#` | Comment | `# This is a comment` | ## Language Reference ### Variable Types | Type | Notes | |------|-------| | `string` | Alphanumeric text | | `number` | IEEE 754 double-precision (integers and decimals) | | `boolean` | `True` / `False` (case-sensitive, capitalize first letter) | | `object` | JSON object — regular variables only, not linked | | `date` | Any valid date format | | `id` | Salesforce record ID | | `list[]` | List of any primitive type — regular variables only, not linked | Variables come in three kinds: **regular** (mutable, with optional default), **linked** (read-only, tied to an external source like `@session.sessionID`), and **system** (predefined, read-only — currently only `@system_variables.user_input`). ### Action Targets Actions support several target protocols: | Target | Format | Example | |--------|--------|---------| | Apex | `apex://.` | `apex://c__CustomerActions.getProfile` | | Flow | `flow://` | `flow://Issue_Refund` | | Prompt | `prompt://` | `prompt://Case_Summary_Template` | Action parameter types: `string`, `number`, `integer`, `long`, `boolean`, `object`, `date`, `datetime`, `time`, `currency`, `id`, `list[]`. ### Operators | Category | Operators | |----------|----------| | Comparison | `==`, `!=`, `<`, `<=`, `>`, `>=`, `is`, `is not` | | Logical | `and`, `or`, `not` | | Arithmetic | `+`, `-` | Conditionals support `if` and `else` only (no `else if`). ### Utils | Utility | Purpose | |---------|---------| | `@utils.transition to @subagent.` | One-way transfer to another subagent. No return to caller. Discards any accumulated prompt from the source subagent. | | `@utils.setVariables` | Lets the LLM set variable values from conversation context. Use `...` token for LLM-determined values. | | `@utils.escalate` | Hands off to a human service rep via Omni-Channel. Requires an active `connection messaging` block. | ### Flow of Control 1. Every utterance starts at the `start_agent` subagent (the router). 2. The router classifies intent and transitions to the appropriate `subagent`. 3. Reasoning instructions are parsed sequentially, top-to-bottom. 4. Logic instructions (`->`) execute deterministically — actions run, variables set, conditions evaluate. 5. Prompt instructions (`|`) are concatenated into a single prompt sent to the LLM. 6. `after_reasoning` runs after the LLM responds. 7. The next utterance returns to `start_agent`. Transitions via `@utils.transition to` are **one-way** — the caller's context is discarded. Use `@subagent.` as a tool reference for **round-trip** delegation that returns to the caller. ## Keep Going Full syntax reference, operators, and block types. 10+ battle-tested patterns for common agent workflows. 15 official open-source recipes you can clone and modify. ## Salesforce Development Skills # Salesforce Development Skills Skills are reusable AI capabilities that teach your coding agent Salesforce-specific patterns, best practices, and workflows. Install them with one command to enhance any supported AI coding tool. ## Choose Your Skills Library Two skills libraries are available, each with a different focus. Both follow the open [Agent Skills](https://agentskills.io) standard and work across 40+ AI coding tools. #### Agentforce ADLC **Agent Development Life Cycle** — Complete toolchain for the Agentforce agent lifecycle. Author agents from natural language, scaffold backing Flows and Apex, deploy, test via preview and batch testing, and optimize using STDM session traces from Data Cloud. - **3 consolidated skills**: `/developing-agentforce`, `/testing-agentforce`, `/observing-agentforce` - **4 agents** + hooks (Claude Code) - **By**: Salesforce AI Research - **Best for**: Building and iterating on Agentforce agents [View on GitHub](https://github.com/SalesforceAIResearch/agentforce-adlc) #### SF Skills **Official Salesforce Library** — Curated collection of AI prompts, rules, and executable skills from Salesforce. Covers metadata creation, Flow development, Apex patterns, Experience Sites, web apps, and more. - **Skills, prompts, and rules** for platform development - **By**: Salesforce (forcedotcom) - **Best for**: Following official Salesforce patterns and standards [View on GitHub](https://github.com/forcedotcom/sf-skills) ## Installation ### Claude Code (Full Install) ```bash # Agentforce ADLC — agents, hooks, and skills curl -sSL https://raw.githubusercontent.com/SalesforceAIResearch/agentforce-adlc/main/tools/install.sh | bash ``` ### All Editors (Universal) Both libraries support the `npx skills add` command, which works with any AI coding tool: ```bash npx skills add SalesforceAIResearch/agentforce-adlc # Agentforce ADLC npx skills add forcedotcom/sf-skills # SF Skills ``` ## About These Skills These skill libraries are **early and rapidly evolving**. The developer community is actively bootstrapping and contributing to them. - **Agentforce ADLC** is maintained by Salesforce AI Research and covers the full agent lifecycle with built-in safety review - **SF Skills** (`forcedotcom/sf-skills`) is the official Salesforce skill library for general platform development - Both follow the open [Agent Skills](https://agentskills.io) standard, originally developed by Anthropic and now adopted by 30+ agent products Choose the one that best fits your workflow — or install both for complementary coverage. ## Experience Site # Experience Site (45 min) ## Quick Deploy an Agent to an Experience Site > **Quick Deploy (on agentforce.dev)**: > The live page has an interactive widget that selects your agent, updates the > MessagingChannel routing to point to it, and returns your Experience Site URL. > If you are not using the live page, follow the manual SF CLI or REST API > steps below to configure the same routing. ## Outcome A working chat widget on your Experience Site where visitors can message your Agentforce Service Agent in real-time. When complete, you'll have: - An Experience Cloud site with the Enhanced Chat component - A Messaging Channel routed directly to your Service Agent - Verified end-to-end message flow from widget to agent > **ℹ️ Info**: This guide uses SF CLI commands as the primary method. REST API and browser fallbacks are included for each step. ## Architecture When a visitor opens the chat widget on your Experience Site, the message flows through a chain of Salesforce objects: *(Architecture diagram — see the live page for the visual)* The key routing link is `SessionHandlerId` on the MessagingChannel — it points directly to your agent's `BotDefinition.Id`. When a message arrives, Salesforce routes it to the handler specified by this ID. ### Configure Your Variables **Variable Reference** — replace these placeholders in commands below: | Variable | Description | Example | |----------|-------------|---------| | `{{ORG_ALIAS}}` | Salesforce org alias or username | `` | | `{{AGENT_NAME}}` | Service Agent developer name | `Reservation_Agent` | | `{{SITE_NAME}}` | Experience Cloud site name | `AgentforceSite` | | `{{CHANNEL_NAME}}` | Messaging Channel developer name | `AgentforceChannel` | | `{{DEPLOYMENT_NAME}}` | Embedded Service Deployment name | `AgentforceDeployment` | | `{{DOMAIN_PREFIX}}` | Experience Site domain prefix | `mycompany` | | `{{INSTANCE_URL}}` | Salesforce instance URL | `https://myorg.my.salesforce.com` | | `{{ACCESS_TOKEN}}` | OAuth access token | `YOUR_ACCESS_TOKEN` | | `{{CLIENT_ID}}` | OAuth client ID (Consumer Key) | `YOUR_CLIENT_ID` | | `{{CLIENT_SECRET}}` | OAuth client secret | `YOUR_CLIENT_SECRET` | ## 1. Verify Your Starting Point Confirm your agent exists and your org is ready. ### Confirm your Service Agent exists and is active Before building the routing chain, verify your Agentforce Service Agent is deployed and active in the target org. **Choose one of the following methods:** #### SF CLI ```bash sf data query \ --target-org {{ORG_ALIAS}} \ --query "SELECT Id, DeveloperName, MasterLabel, AgentType FROM BotDefinition WHERE DeveloperName='{{AGENT_NAME}}' AND IsActive=true" ``` #### REST API ```bash curl -X GET "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id,DeveloperName,MasterLabel,AgentType+FROM+BotDefinition+WHERE+DeveloperName='{{AGENT_NAME}}'+AND+IsActive=true" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" \ -H "Content-Type: application/json" ``` #### Browser Agent ```text 1. Navigate to Setup → Agents → Agent Builder 2. Find your agent by name: {{AGENT_NAME}} 3. Verify the status shows Active 4. Copy the Agent ID for later use ``` **Verify the agent is active:** Expected result: One record returned with a valid Id. If no record is returned, activate your agent in Agent Builder before proceeding. > **⚠️ Warning**: The agent must have `AgentType = 'Bot'` or `AgentType = 'ServiceAgent'`. Other agent types cannot be routed via Enhanced Chat. ### Authenticate and verify org access Ensure you have admin access to the target org and the CLI is connected. **Choose one of the following methods:** #### SF CLI ```bash sf org login web --alias {{ORG_ALIAS}} --instance-url https://login.salesforce.com sf org display --target-org {{ORG_ALIAS}} --verbose ``` #### REST API ```bash # Use OAuth 2.0 web server flow to obtain access token # Then verify with: curl -X GET "{{INSTANCE_URL}}/services/data/v64.0/limits" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" ``` #### Browser Agent ```text 1. Log into your Salesforce org at https://login.salesforce.com 2. Verify you see Setup in the gear menu (admin access required) 3. Navigate to Setup → Company Information to confirm org details ``` **Verify org access:** Expected result: Org display shows your username and instance URL without errors. ## 2. Build the Routing Chain Create the queue, channel, and deployment that route messages to your agent. ### Create a messaging queue for fallback routing The queue handles messages when the agent is unavailable. Even with direct agent routing, a fallback queue is required. **Choose one of the following methods:** #### SF CLI ```bash # Check if queue exists sf data query --target-org {{ORG_ALIAS}} \ --query "SELECT Id FROM Group WHERE Type='Queue' AND DeveloperName='{{CHANNEL_NAME}}_Queue'" # If not found, create via Metadata API cat > queue-metadata.xml << 'EOF' {{CHANNEL_NAME}}_Queue {{CHANNEL_NAME}} Queue MessagingSession EOF sf project deploy start --target-org {{ORG_ALIAS}} --metadata Queue:{{CHANNEL_NAME}}_Queue ``` #### REST API ```bash # Create queue curl -X POST "{{INSTANCE_URL}}/services/data/v64.0/sobjects/Group" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" \ -H "Content-Type: application/json" \ -d '{ "Name": "{{CHANNEL_NAME}} Queue", "DeveloperName": "{{CHANNEL_NAME}}_Queue", "Type": "Queue" }' # Add MessagingSession to queue QUEUE_ID=$(curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id+FROM+Group+WHERE+DeveloperName='{{CHANNEL_NAME}}_Queue'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" | jq -r '.records[0].Id') curl -X POST "{{INSTANCE_URL}}/services/data/v64.0/sobjects/QueueSobject" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" \ -H "Content-Type: application/json" \ -d "{\"QueueId\": \"$QUEUE_ID\", \"SobjectType\": \"MessagingSession\"}" ``` #### Browser Agent ```text 1. Navigate to Setup → Queues 2. Click New 3. Set Label: {{CHANNEL_NAME}} Queue 4. Set Queue Name: {{CHANNEL_NAME}}_Queue 5. In Supported Objects, add Messaging Session 6. Click Save ``` **Verify queue exists:** Expected result: One queue record returned. ### Create a Messaging Channel routed to your agent The Messaging Channel is the key routing component. Setting SessionHandlerId to your agent's BotDefinition.Id enables direct routing. **Choose one of the following methods:** #### SF CLI ```bash # Get the Bot ID and Queue ID BOT_ID=$(sf data query --target-org {{ORG_ALIAS}} --json \ --query "SELECT Id FROM BotDefinition WHERE DeveloperName='{{AGENT_NAME}}'" \ | jq -r '.result.records[0].Id') QUEUE_ID=$(sf data query --target-org {{ORG_ALIAS}} --json \ --query "SELECT Id FROM Group WHERE Type='Queue' AND DeveloperName='{{CHANNEL_NAME}}_Queue'" \ | jq -r '.result.records[0].Id') # Create or update MessagingChannel via Apex sf apex run --target-org {{ORG_ALIAS}} << EOF MessagingChannel channel = new MessagingChannel(); channel.DeveloperName = '{{CHANNEL_NAME}}'; channel.MasterLabel = '{{CHANNEL_NAME}}'; channel.MessageType = 'EmbeddedMessaging'; channel.MessagingPlatformKey = '{{CHANNEL_NAME}}-' + String.valueOf(Crypto.getRandomLong()); channel.IsActive = true; channel.SessionHandlerId = '$BOT_ID'; channel.FallbackQueueId = '$QUEUE_ID'; insert channel; System.debug('Created MessagingChannel: ' + channel.Id); EOF ``` #### REST API ```bash BOT_ID=$(curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id+FROM+BotDefinition+WHERE+DeveloperName='{{AGENT_NAME}}'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" | jq -r '.records[0].Id') QUEUE_ID=$(curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id+FROM+Group+WHERE+Type='Queue'+AND+DeveloperName='{{CHANNEL_NAME}}_Queue'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" | jq -r '.records[0].Id') curl -X POST "{{INSTANCE_URL}}/services/data/v64.0/sobjects/MessagingChannel" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" \ -H "Content-Type: application/json" \ -d "{ \"DeveloperName\": \"{{CHANNEL_NAME}}\", \"MasterLabel\": \"{{CHANNEL_NAME}}\", \"MessageType\": \"EmbeddedMessaging\", \"MessagingPlatformKey\": \"{{CHANNEL_NAME}}-$(date +%s)\", \"IsActive\": true, \"SessionHandlerId\": \"$BOT_ID\", \"FallbackQueueId\": \"$QUEUE_ID\" }" ``` #### Browser Agent ```text 1. Navigate to Setup → Messaging Settings 2. Click New Channel 3. Select Enhanced Chat as the type 4. Set Channel Name: {{CHANNEL_NAME}} 5. Under Routing, select Agent and choose your agent: {{AGENT_NAME}} 6. Under Fallback Queue, select {{CHANNEL_NAME}} Queue 7. Click Activate, then Save ``` **Verify channel configuration:** Expected result: One record with IsActive=true and SessionHandlerId matching your BotDefinition.Id. > **💡 Tip**: If `SessionHandlerId` is not available in your org's API version, you'll need to configure routing through a Flow instead. See the Troubleshooting section. ### Create an Embedded Service Deployment The deployment packages the channel configuration and generates the code snippet for your site. **Choose one of the following methods:** #### SF CLI ```bash # Create EmbeddedServiceConfig via Tooling API cat > esd-metadata.json << 'EOF' { "FullName": "{{DEPLOYMENT_NAME}}", "Metadata": { "masterLabel": "{{DEPLOYMENT_NAME}}", "deploymentFeature": "EmbeddedMessaging", "deploymentType": "Web", "isEnabled": true, "embeddedServiceMessagingChannel": { "isEnabled": true, "messagingChannel": "{{CHANNEL_NAME}}" } } } EOF sf data create record --target-org {{ORG_ALIAS}} --use-tooling-api \ --sobject EmbeddedServiceConfig --values "$(cat esd-metadata.json)" ``` #### REST API ```bash curl -X POST "{{INSTANCE_URL}}/services/data/v64.0/tooling/sobjects/EmbeddedServiceConfig" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" \ -H "Content-Type: application/json" \ -d '{ "FullName": "{{DEPLOYMENT_NAME}}", "Metadata": { "masterLabel": "{{DEPLOYMENT_NAME}}", "deploymentFeature": "EmbeddedMessaging", "deploymentType": "Web", "isEnabled": true, "embeddedServiceMessagingChannel": { "isEnabled": true, "messagingChannel": "{{CHANNEL_NAME}}" } } }' ``` #### Browser Agent ```text 1. Navigate to Setup → Embedded Service Deployments 2. Click New Deployment 3. Select Enhanced Chat 4. Set Deployment Name: {{DEPLOYMENT_NAME}} 5. Select your Messaging Channel: {{CHANNEL_NAME}} 6. Click Save 7. Click Publish to activate the deployment ``` **Verify deployment exists:** Expected result: One deployment record returned. ## 3. Create the Experience Site Stand up the site and add the chat widget component. ### Create an Experience Cloud site The Experience Site hosts your public-facing pages. We'll use the Customer Service template which includes Enhanced Chat support. **Choose one of the following methods:** #### SF CLI ```bash sf community create \ --target-org {{ORG_ALIAS}} \ --name "{{SITE_NAME}}" \ --template-name "Customer Service" \ --url-path-prefix "{{DOMAIN_PREFIX}}" ``` #### REST API ```bash # Experience Sites are typically created via Metadata API # Use sf CLI or browser for initial creation ``` #### Browser Agent ```text 1. Navigate to Setup → Digital Experiences → All Sites 2. Click New 3. Select Customer Service template 4. Set Name: {{SITE_NAME}} 5. Set URL: {{DOMAIN_PREFIX}} 6. Click Create ``` **Verify site exists:** Expected result: One site record returned. ### Add the Enhanced Chat component to your site The component renders the chat widget. It connects to your deployment configuration. **Choose one of the following methods:** #### SF CLI ```bash # Retrieve the site bundle sf project retrieve start \ --target-org {{ORG_ALIAS}} \ --metadata "DigitalExperienceBundle:site/{{SITE_NAME}}" \ --target-metadata-dir ./site-bundle # The component must be added via Experience Builder (browser) # See Browser method below ``` #### REST API ```bash # Component placement requires Experience Builder # See Browser method below ``` #### Browser Agent ```text 1. Navigate to Setup → Digital Experiences → All Sites 2. Click Builder next to {{SITE_NAME}} 3. Select the Home page from the page dropdown 4. From the Components panel, drag Enhanced Chat to the footer region 5. In the component properties: - Set Deployment: {{DEPLOYMENT_NAME}} - Leave other settings as default 6. Click Publish in the top right 7. Confirm and publish ``` **Verify component placement:** Expected result: The Enhanced Chat component appears in Experience Builder on the Home page. ### Publish the Experience Site Publishing makes the site accessible at its public URL. **Choose one of the following methods:** #### SF CLI ```bash sf community publish \ --target-org {{ORG_ALIAS}} \ --name "{{SITE_NAME}}" ``` #### REST API ```bash # Publish via Connect API NETWORK_ID=$(curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id+FROM+Network+WHERE+Name='{{SITE_NAME}}'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" | jq -r '.records[0].Id') curl -X POST "{{INSTANCE_URL}}/services/data/v64.0/connect/communities/$NETWORK_ID/publish" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" \ -H "Content-Type: application/json" ``` #### Browser Agent ```text 1. In Experience Builder, click Publish in the top right 2. Click Publish again in the confirmation dialog 3. Wait for the publish process to complete ``` **Verify site is published:** Expected result: Status = 'Live'. ## 4. Deploy & Verify Test the full flow from widget to agent. ### Get your site URL Find the public URL where your chat widget is live. **Choose one of the following methods:** #### SF CLI ```bash sf org display --target-org {{ORG_ALIAS}} --verbose --json | jq -r '.result.instanceUrl' | \ sed 's/\.my\.salesforce\./.my.site./' # Append: /{{DOMAIN_PREFIX}} ``` #### REST API ```bash # Get domain from org info curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Domain+FROM+Domain+WHERE+DomainType='DNS'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" # Site URL format: https://{domain}.my.site.com/{{DOMAIN_PREFIX}} ``` #### Browser Agent ```text 1. Navigate to Setup → Digital Experiences → All Sites 2. Click on {{SITE_NAME}} 3. Copy the Site URL shown in the details ``` **Verify site URL:** Expected result: You have a URL like https://yourorg.my.site.com/{{DOMAIN_PREFIX}}. ### Test the chat widget Open your site and verify the widget appears and can send messages. **Choose one of the following methods:** #### SF CLI ```bash # Open the site in your browser open "https://$(sf org display --target-org {{ORG_ALIAS}} --json | jq -r '.result.instanceUrl' | sed 's/https:\/\///' | sed 's/\.my\.salesforce\./.my.site./').com/{{DOMAIN_PREFIX}}" ``` #### REST API ```bash # Manual browser test required - see Browser method ``` #### Browser Agent ```text 1. Open your site URL in a new browser tab (incognito recommended) 2. Look for the chat widget icon (usually bottom-right corner) 3. Click to open the chat window 4. Type a test message: "Hello, I need help" 5. Send the message 6. Wait for the agent to respond ``` **Verify chat functionality:** Expected result: Chat widget visible, message sends successfully, agent responds. ### Verify message routing to agent Confirm messages are being routed to your agent by checking MessagingSession and AgentWork records. **Choose one of the following methods:** #### SF CLI ```bash # Check recent MessagingSessions sf data query --target-org {{ORG_ALIAS}} \ --query "SELECT Id, Status, AgentType, OwnerId, CreatedDate FROM MessagingSession ORDER BY CreatedDate DESC LIMIT 5" # Check AgentWork records sf data query --target-org {{ORG_ALIAS}} \ --query "SELECT Id, WorkItemId, BotId, RoutingType, CreatedDate FROM AgentWork ORDER BY CreatedDate DESC LIMIT 5" ``` #### REST API ```bash curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id,Status,AgentType,OwnerId,CreatedDate+FROM+MessagingSession+ORDER+BY+CreatedDate+DESC+LIMIT+5" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id,WorkItemId,BotId,RoutingType,CreatedDate+FROM+AgentWork+ORDER+BY+CreatedDate+DESC+LIMIT+5" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" ``` #### Browser Agent ```text 1. Navigate to Setup → Object Manager → Messaging Session 2. Click Recent Messaging Sessions tab 3. Look for a session created in the last few minutes 4. Click to view details 5. Verify Owner matches your agent ``` **Verify messaging session:** Expected result: Recent MessagingSession exists with Status = 'Active' or 'Ended'. ## Troubleshooting ### Widget doesn't appear on site 1. Verify the Embedded Service Deployment is published 2. Check that the Enhanced Chat component is on the Home page 3. Republish the Experience Site after adding the component 4. Clear browser cache and try incognito mode ### Messages not reaching agent 1. Verify `SessionHandlerId` is set on the MessagingChannel 2. Confirm the agent is Active (not Draft) 3. Check that the Messaging Channel is Active 4. Verify the fallback queue is properly configured ### SessionHandlerId field not available Some org configurations don't expose this field via API. Options: 1. Configure routing via Omni-Channel Flow instead 2. Use Setup UI to configure direct agent routing 3. Contact Salesforce support for API access ### Site publish fails 1. Check for unpublished components or missing required fields 2. Review the publish error in Setup → Digital Experiences 3. Ensure you have admin permissions ## Definition of Done **Definition of Done:** - Chat widget launcher visible on Experience Site - User can open chat and send a message - Agent responds to the message - MessagingSession record created with correct routing - AgentWork record shows message assigned to agent ## Deploy # Deploy Once your agent is built and tested, deploy it to the channels your customers already use. Each guide walks through end-to-end setup: configuration, routing, and verification. ## Custom Client Interact with your agent programmatically from any application. Control your agent over REST. Build custom integrations, run automated tests, or embed agent conversations in any app. ## Web Embed a chat widget on your website or Experience Cloud site. Enhanced Chat widget on an Experience Cloud site. Enhanced Chat widget on any external website. No Experience Cloud required. ## Messaging Channels Connect your agent to the platforms your customers already message on. Connect WhatsApp Business via Meta Cloud API and route messages to your agent. Provision a phone number and route text messages to your agent. Connect your Facebook Page and route Messenger conversations to your agent. Route iMessage conversations from Apple Maps, Safari, and Spotlight to your agent. Connect a LINE Official Account and route messages to your agent. Bring any messaging platform via managed package and the Interaction Service API. ## Enhanced Chat # Enhanced Chat (30 min) ## Quick Setup: Generate Embed Snippet Select an agent, enter your website domains, and generate the JavaScript embed snippet. Domains are whitelisted automatically. > **Quick Setup (on agentforce.dev)**: > The live page has an interactive widget that auto-generates the embed snippet. > It selects your agent, whitelists your domains, configures the MessagingChannel, > and outputs a ready-to-paste ` ``` > **ℹ️ Info**: The exact snippet is auto-generated by Salesforce and includes your org-specific values. Always use the snippet from Setup rather than constructing it manually, since the URLs and parameters vary by org and environment. ## 6. Add the Snippet to Your Website Paste the JavaScript snippet into your website's HTML. The snippet should be placed just before the closing `` tag for optimal loading. **Choose one of the following methods:** #### SF CLI ```bash # This step is performed on your external website, not in Salesforce. # Example: Add to an HTML file cat >> /path/to/your/website/index.html << 'SNIPPET' SNIPPET ``` #### REST API ```bash # This step is performed on your external website, not via Salesforce API. # Paste the snippet from Step 5 into your site's HTML. # # For React/Next.js apps, add the script in your layout component: # - Use next/script with strategy="lazyOnload" # - Or add to public/index.html # # For static sites: # - Add before in your HTML template ``` #### Browser Agent ```text 1. Open your website's HTML source (index.html or equivalent) 2. Paste the JavaScript snippet from Step 5 before the closing tag 3. Save and deploy your website 4. For single-page applications (React, Angular, Vue): a. Add the bootstrap script to your index.html b. Call initEmbeddedMessaging() after the DOM loads 5. For CMS platforms (WordPress, etc.): a. Use a custom HTML widget or footer script injection b. Paste the snippet in the footer scripts section ``` > **⚠️ Warning**: The domain serving your website must be added to the **CORS allowlist** in Salesforce Setup. Navigate to **Setup -> CORS -> New**, and add your website's origin (e.g., `https://www.example.com`). Without this, the chat widget will fail to connect. > **💡 Tip**: For development and testing, you can use a simple local HTML file served via `python3 -m http.server 8000` and add `http://localhost:8000` to your CORS allowlist. ## 7. Test and Verify Open your website, click the chat widget, and send a test message to confirm the full routing chain works. **Choose one of the following methods:** #### SF CLI ```bash # After sending a test message through the chat widget, # verify a MessagingSession was created: sf data query \ --target-org {{ORG_ALIAS}} \ --query "SELECT Id, Status, AgentType, OwnerId, CreatedDate FROM MessagingSession ORDER BY CreatedDate DESC LIMIT 5" # Verify AgentWork was created (shows the agent handled the conversation): sf data query \ --target-org {{ORG_ALIAS}} \ --query "SELECT Id, WorkItemId, BotId, RoutingType, CreatedDate FROM AgentWork ORDER BY CreatedDate DESC LIMIT 5" ``` #### REST API ```bash # Check for recent MessagingSession records curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id,Status,AgentType,OwnerId,CreatedDate+FROM+MessagingSession+ORDER+BY+CreatedDate+DESC+LIMIT+5" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" # Check for AgentWork records curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id,WorkItemId,BotId,RoutingType,CreatedDate+FROM+AgentWork+ORDER+BY+CreatedDate+DESC+LIMIT+5" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" ``` #### Browser Agent ```text 1. Open your website in a browser 2. Look for the chat bubble in the bottom-right corner 3. Click the bubble to open the chat window 4. Type a test message (e.g., "Hello, I need help") 5. Verify the agent responds within a few seconds 6. To verify in Salesforce: a. Navigate to Setup -> Messaging Sessions b. Confirm a new session was created with your test message c. Check the Owner is your Agentforce Service Agent ``` **Verify the chat widget works:** Expected result: The chat bubble appears on your website. Clicking it opens a chat window. Sending a message produces a response from your Agentforce agent. A `MessagingSession` record exists in Salesforce with `AgentType` indicating bot handling. ## Customization ### Branding You can customize the chat widget's appearance to match your website's design: **Choose one of the following methods:** #### SF CLI ```bash # Branding is configured on the EmbeddedServiceConfig via Setup UI # or by updating the deployment metadata. Key options: # - Primary color, secondary color, contrast colors # - Chat window header text # - Avatar image # - Font family ``` #### REST API ```bash # Update branding via Tooling API (replace DEPLOYMENT_ID): curl -X PATCH "{{INSTANCE_URL}}/services/data/v64.0/tooling/sobjects/EmbeddedServiceConfig/DEPLOYMENT_ID" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" \ -H "Content-Type: application/json" \ -d '{ "Metadata": { "embeddedServiceBranding": { "primaryColor": "#1B5297", "secondaryColor": "#FFFFFF", "headerBackgroundColor": "#1B5297", "font": "Salesforce Sans" } } }' ``` #### Browser Agent ```text 1. Navigate to Setup -> Embedded Service Deployments 2. Click your deployment: {{DEPLOYMENT_NAME}} 3. Click "Branding" or "Customize" 4. Set colors, fonts, header text, and avatar 5. Click Save, then Publish to apply changes ``` ### Pre-Chat Forms Pre-chat forms collect customer information (name, email, case subject) before starting the conversation. This data is available to your agent via the session context. **Choose one of the following methods:** #### SF CLI ```bash # Pre-chat configuration is part of the EmbeddedServiceConfig metadata. # Configure via Setup UI (see Browser tab). ``` #### REST API ```bash # Pre-chat forms are configured as part of the EmbeddedServiceConfig. # See Browser method for the recommended approach. ``` #### Browser Agent ```text 1. Navigate to Setup -> Embedded Service Deployments 2. Click your deployment: {{DEPLOYMENT_NAME}} 3. Navigate to the "Pre-Chat" section 4. Add fields: Name, Email, Subject, or custom fields 5. Mark fields as required or optional 6. Map pre-chat fields to Contact or Case fields for record creation 7. Save and Publish ``` ## Troubleshooting ### Chat bubble does not appear on the website Check these common causes in order: 1. **Snippet not loading**: Open browser DevTools (F12) and check the Console for JavaScript errors. Verify the `bootstrap.min.js` URL is correct and accessible. 2. **CORS error**: Check the Console for CORS-related errors. Add your website's origin to **Setup -> CORS -> Allowed Origins List**. 3. **Deployment not published**: Navigate to **Setup -> Embedded Service Deployments** and verify the deployment status shows as Published. 4. **Incorrect SCRT2 URL**: Verify the `scrt2URL` in your snippet matches your org's domain pattern: `https://.my.salesforce-scrt.com`. ### Chat bubble appears but messages fail to send This usually indicates a routing issue: 1. Verify the MessagingChannel is active: query `SELECT IsActive FROM MessagingChannel WHERE DeveloperName='{{CHANNEL_NAME}}'`. 2. Verify `SessionHandlerId` is set on the channel and points to a valid BotDefinition. 3. Check that the fallback queue exists and has `MessagingSession` as a supported SObject type. 4. Ensure your Agentforce Service Agent is active and published. ### Agent does not respond to messages The chat connects but the agent is silent: 1. Check the agent is active: `SELECT Id, IsActive FROM BotDefinition WHERE DeveloperName='{{AGENT_NAME}}'`. 2. Verify a BotVersion is active: `SELECT Id, Status FROM BotVersion WHERE BotDefinition.DeveloperName='{{AGENT_NAME}}' AND Status='Active'`. 3. Check `AgentWork` records to see if the conversation was assigned: `SELECT Id, WorkItemId, BotId FROM AgentWork ORDER BY CreatedDate DESC LIMIT 5`. 4. Review the agent's subagent instructions to ensure it can handle the test utterance. ### Widget loads but shows 'Service is not available' The deployment is published but the runtime cannot connect: 1. Verify Messaging is enabled: **Setup -> Messaging Settings** should show Messaging as active. 2. Check that the MessagingChannel `IsActive = true`. 3. Ensure Omni-Channel is enabled: **Setup -> Omni-Channel Settings**. 4. If the org was recently provisioned, wait 5-10 minutes for SCRT2 infrastructure to propagate. ## Enhanced Chat v1 vs v2 Salesforce offers two versions of the Enhanced Chat widget: | Feature | Enhanced Chat v1 (Standard) | Enhanced Chat v2 (Custom LWC) | | ------------- | -------------------------------------- | ------------------------------------------------------------------- | | Setup | Standard configuration in Setup | Requires custom Lightning Web Components | | Customization | Branding colors, fonts, pre-chat forms | Full custom UI via LWC (custom message types, interactive elements) | | Use case | Standard chat with agent responses | Rich interactive experiences (carousels, forms, custom cards) | | Requirement | Service Cloud | Service Cloud + custom LWC development | This guide covers Enhanced Chat v1 (standard). For Enhanced Chat v2 with custom Lightning types, see the [Salesforce Web Developer Guide](https://developer.salesforce.com/docs/service/messaging-in-app-web/overview) and the [In-App Developer Guide](https://developer.salesforce.com/docs/service/messaging-in-app-web/overview). > **ℹ️ Info**: Enhanced Chat also supports **mobile apps** via iOS and Android SDKs. The same MessagingChannel and EmbeddedServiceConfig are reused; only the client-side integration differs. See the Salesforce Mobile SDK documentation for details. ## Definition of Done **Definition of Done:** - MessagingChannel is active with `MessageType = EmbeddedMessaging` and `SessionHandlerId` pointing to your BotDefinition. - EmbeddedServiceConfig exists with `DeploymentFeature = EmbeddedMessaging` and `DeploymentType = Web`, and is published. - JavaScript snippet is added to your external website's HTML. - Your website's origin is added to the Salesforce CORS allowlist. - Chat bubble appears on the website and opens a chat window on click. - Sending a test message produces a response from the Agentforce agent. - `MessagingSession` and `AgentWork` records confirm the routing path works. ## WhatsApp Setup # WhatsApp Setup (30 min) ## Outcome A working WhatsApp-to-Agentforce pipeline where customers can message your business number and receive automated responses from your Service Agent. When complete, you'll have: - A WhatsApp Messaging Channel connected to your Salesforce org via Meta Cloud API - Omni-Channel routing configured to send inbound messages to your Agentforce agent - Verified end-to-end message flow from WhatsApp to agent and back > **ℹ️ Info**: WhatsApp channels require the **Digital Engagement** add-on license and a **Meta Business Manager** account with a verified WhatsApp Business phone number. Unlike Enhanced Chat, no Experience Cloud site or EmbeddedServiceConfig is needed. ## Architecture When a customer sends a WhatsApp message to your business number, the message flows through the following chain: *(Architecture diagram — see the live page for the visual)* The key routing field is `sessionHandlerType = AgentforceServiceAgent` on the MessagingChannel, with `sessionHandlerAsa` pointing to the BotDefinition DeveloperName. The `sessionHandlerQueue` field specifies the fallback queue when the agent is unavailable. Unlike Enhanced Chat, WhatsApp does not require an EmbeddedServiceConfig or an Experience Cloud site. The channel is a direct integration between Meta's Cloud API and Salesforce's Messaging infrastructure. ### Configure Your Variables **Variable Reference** — replace these placeholders in commands below: | Variable | Description | Example | |----------|-------------|---------| | `{{ORG_ALIAS}}` | Salesforce org alias or username | `` | | `{{AGENT_NAME}}` | Service Agent developer name | `Reservation_Agent` | | `{{CHANNEL_NAME}}` | Messaging Channel developer name | `AgentforceChannel` | | `{{INSTANCE_URL}}` | Salesforce instance URL | `https://myorg.my.salesforce.com` | | `{{ACCESS_TOKEN}}` | OAuth access token | `YOUR_ACCESS_TOKEN` | ## Implementation Steps ## 1. Verify Your Starting Point Confirm your agent exists, your org has the required licenses, and you have admin access. ### Confirm your Service Agent exists and is active Before building the routing chain, verify your Agentforce Service Agent is deployed and active in the target org. **Choose one of the following methods:** #### SF CLI ```bash sf data query \ --target-org {{ORG_ALIAS}} \ --query "SELECT Id, DeveloperName, MasterLabel, AgentType FROM BotDefinition WHERE DeveloperName='{{AGENT_NAME}}' AND IsActive=true" ``` #### REST API ```bash curl -X GET "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id,DeveloperName,MasterLabel,AgentType+FROM+BotDefinition+WHERE+DeveloperName='{{AGENT_NAME}}'+AND+IsActive=true" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" \ -H "Content-Type: application/json" ``` #### Browser Agent ```text 1. Navigate to Setup → Agents → Agent Builder 2. Find your agent by name: {{AGENT_NAME}} 3. Verify the status shows Active 4. Copy the Agent ID for later use ``` **Verify the agent is active:** Expected result: One record returned with a valid Id. If no record is returned, activate your agent in Agent Builder before proceeding. ### Authenticate and verify org access Ensure you have admin access to the target org and the CLI is connected. **Choose one of the following methods:** #### SF CLI ```bash sf org login web --alias {{ORG_ALIAS}} --instance-url https://login.salesforce.com sf org display --target-org {{ORG_ALIAS}} --verbose ``` #### REST API ```bash # Use OAuth 2.0 web server flow to obtain access token # Then verify with: curl -X GET "{{INSTANCE_URL}}/services/data/v64.0/limits" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" ``` #### Browser Agent ```text 1. Log into your Salesforce org at https://login.salesforce.com 2. Verify you see Setup in the gear menu (admin access required) 3. Navigate to Setup → Company Information to confirm org details ``` **Verify org access:** Expected result: Org display shows your username and instance URL without errors. ### Verify Digital Engagement license WhatsApp channels require the Digital Engagement add-on. Confirm it is provisioned in your org. **Choose one of the following methods:** #### SF CLI ```bash sf data query \ --target-org {{ORG_ALIAS}} \ --query "SELECT Id, Name, Status, IsProvisioned FROM PackageLicense WHERE NamespacePrefix='sfdc_livemessage'" ``` #### REST API ```bash curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id,Name,Status,IsProvisioned+FROM+PackageLicense+WHERE+NamespacePrefix='sfdc_livemessage'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" ``` #### Browser Agent ```text 1. Navigate to Setup → Company Information → Permission Set Licenses 2. Look for "Digital Engagement" or "Messaging" in the list 3. Confirm it shows as Active with available licenses ``` **Verify Digital Engagement license:** Expected result: One record returned with IsProvisioned = true. If not found, contact your Salesforce account executive to provision Digital Engagement. ## 2. Enable Messaging and Omni-Channel Enable the org-level settings required for WhatsApp messaging. ### Enable Messaging in Setup Messaging must be enabled at the org level before any messaging channels can be created. **Choose one of the following methods:** #### SF CLI ```bash # Deploy Messaging settings via metadata mkdir -p force-app/main/default/settings cat > force-app/main/default/settings/OmniChannel.settings-meta.xml << 'EOF' true EOF sf project deploy start \ --target-org {{ORG_ALIAS}} \ --source-dir force-app/main/default/settings/OmniChannel.settings-meta.xml ``` #### REST API ```bash # Messaging and Omni-Channel settings are typically enabled via # Metadata API or Setup UI. See Browser method. ``` #### Browser Agent ```text 1. Navigate to Setup → Messaging Settings 2. If you see a toggle for Messaging, enable it 3. Navigate to Setup → Omni-Channel Settings 4. Check "Enable Omni-Channel" 5. Click Save ``` **Verify messaging is enabled:** Expected result: Messaging Settings page loads without errors and shows channel management options. Omni-Channel Settings shows "Omni-Channel is enabled." ## 3. Set Up Meta Business and WhatsApp Connect your Meta Business account to Salesforce and register a WhatsApp Business phone number. > **⚠️ Warning**: The phone number you use must NOT already be registered with WhatsApp or WhatsApp Business App. It will be migrated to the WhatsApp Business Platform during setup. If the number is currently in use with WhatsApp, you must deregister it first by deleting the WhatsApp app from that phone. ### Prepare your Meta Business Manager account Before creating the WhatsApp channel in Salesforce, ensure your Meta Business environment is ready. **Choose one of the following methods:** #### SF CLI ```bash # Meta Business setup is UI-driven (OAuth flow between Salesforce and Meta) # No CLI equivalent. See Browser method for complete steps. ``` #### REST API ```bash # Meta Business setup requires OAuth flow in browser. # See Browser method for complete steps. ``` #### Browser Agent ```text 1. Go to https://business.facebook.com and sign in 2. If you don't have a Business Manager account, click Create Account 3. Navigate to Business Settings → Accounts → WhatsApp Accounts 4. If no WhatsApp Business Account (WABA) exists, create one: a. Click Add → Create a WhatsApp Account b. Enter your business name and select a timezone c. Click Create 5. Note your WABA ID (visible in the URL or account details) 6. Navigate to Meta Business Settings → Business Verification 7. Verify your business if not already verified - Upload business documents as requested - Verification may take 1-5 business days for production use - Test numbers with limited messaging are available before verification ``` > **ℹ️ Info**: Meta Business verification is required for production use (sending messages to customers). For development and testing, you can use a test phone number with limited messaging capabilities before completing verification. **Verify Meta Business Manager access:** Expected result: You have a Meta Business Manager account with a WhatsApp Business Account (WABA) and either a verified business or a test phone number ready. ### Prepare a phone number for WhatsApp You need a phone number that can receive SMS or voice calls for verification. This number will become your WhatsApp Business number. **Choose one of the following methods:** #### SF CLI ```bash # Phone number preparation is done in Meta Business Manager. # See Browser method. ``` #### REST API ```bash # Phone number preparation is done in Meta Business Manager. # See Browser method. ``` #### Browser Agent ```text 1. Ensure you have a phone number that: - Can receive SMS or voice calls (for verification code) - Is NOT registered with WhatsApp or WhatsApp Business App - Is a valid phone number with country code 2. If the number is currently on WhatsApp: a. Open WhatsApp on the device with that number b. Go to Settings → Account → Delete My Account c. Confirm deletion d. Wait a few minutes before proceeding 3. Keep the phone accessible during the next step for verification ``` **Verify phone number availability:** Expected result: You have an available phone number ready for WhatsApp Business registration. ## 4. Create the WhatsApp Messaging Channel Use the Salesforce Setup wizard to create the WhatsApp channel. This wizard handles the OAuth connection to Meta and webhook registration automatically. ### Create the channel via Setup wizard The WhatsApp channel creation in Salesforce uses an embedded Meta OAuth flow. It registers webhooks, connects your WABA, and creates the MessagingChannel record. **Choose one of the following methods:** #### SF CLI ```bash # The WhatsApp channel must be created via the Setup wizard # (OAuth flow between Salesforce and Meta cannot be done via CLI) # After creation, verify with: sf data query \ --target-org {{ORG_ALIAS}} \ --query "SELECT Id, DeveloperName, MasterLabel, MessageType, IsActive, MessagingPlatformKey FROM MessagingChannel WHERE MessageType='WhatsApp'" ``` #### REST API ```bash # WhatsApp channel creation requires the Setup wizard (OAuth flow). # After creation, verify with: curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id,DeveloperName,MasterLabel,MessageType,IsActive,MessagingPlatformKey+FROM+MessagingChannel+WHERE+MessageType='WhatsApp'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" ``` #### Browser Agent ```text 1. Navigate to Setup → Messaging Settings 2. Click New Channel 3. Select WhatsApp as the channel type 4. Click Start to begin the Meta OAuth flow 5. Sign in to your Meta Business account when prompted 6. Select your WhatsApp Business Account (WABA) 7. Select or add the phone number to use 8. Verify the phone number via SMS or voice call code 9. Set the Channel Name: {{CHANNEL_NAME}} 10. Review the configuration summary 11. Click Finish to create the channel 12. The channel will show as Active in Messaging Settings ``` **Verify channel was created:** Expected result: Query returns one record with MessageType = 'WhatsApp' and IsActive = true. ### Verify channel details and note key fields After creation, confirm the channel is properly configured with the correct platform key and message type. **Choose one of the following methods:** #### SF CLI ```bash sf data query \ --target-org {{ORG_ALIAS}} \ --query "SELECT Id, DeveloperName, MasterLabel, MessageType, IsActive, MessagingPlatformKey, SessionHandlerId, FallbackQueueId FROM MessagingChannel WHERE DeveloperName='{{CHANNEL_NAME}}'" ``` #### REST API ```bash curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id,DeveloperName,MasterLabel,MessageType,IsActive,MessagingPlatformKey,SessionHandlerId,FallbackQueueId+FROM+MessagingChannel+WHERE+DeveloperName='{{CHANNEL_NAME}}'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" ``` #### Browser Agent ```text 1. Navigate to Setup → Messaging Settings 2. Click on your WhatsApp channel: {{CHANNEL_NAME}} 3. Note the following fields: - Channel Name / DeveloperName - Message Type (should be WhatsApp) - Platform Key (your WhatsApp phone number) - Status (should be Active) ``` **Verify channel configuration:** Expected result: Channel record returned with MessageType = 'WhatsApp', IsActive = true, and MessagingPlatformKey matching your WhatsApp Business phone number. ## 5. Configure Routing to Your Agent Set up the routing so inbound WhatsApp messages are handled by your Agentforce Service Agent. ### Create a messaging queue for fallback routing The queue handles messages when the agent is unavailable. Even with direct agent routing, a fallback queue is required. **Choose one of the following methods:** #### SF CLI ```bash # Check if queue exists sf data query --target-org {{ORG_ALIAS}} \ --query "SELECT Id FROM Group WHERE Type='Queue' AND DeveloperName='{{CHANNEL_NAME}}_Queue'" # If not found, create via Metadata API mkdir -p force-app/main/default/queues cat > force-app/main/default/queues/{{CHANNEL_NAME}}_Queue.queue-meta.xml << 'EOF' {{CHANNEL_NAME}}_Queue {{CHANNEL_NAME}} Queue MessagingSession EOF sf project deploy start --target-org {{ORG_ALIAS}} --metadata Queue:{{CHANNEL_NAME}}_Queue ``` #### REST API ```bash # Create queue curl -X POST "{{INSTANCE_URL}}/services/data/v64.0/sobjects/Group" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" \ -H "Content-Type: application/json" \ -d '{ "Name": "{{CHANNEL_NAME}} Queue", "DeveloperName": "{{CHANNEL_NAME}}_Queue", "Type": "Queue" }' # Add MessagingSession to queue QUEUE_ID=$(curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id+FROM+Group+WHERE+DeveloperName='{{CHANNEL_NAME}}_Queue'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" | jq -r '.records[0].Id') curl -X POST "{{INSTANCE_URL}}/services/data/v64.0/sobjects/QueueSobject" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" \ -H "Content-Type: application/json" \ -d "{\"QueueId\": \"$QUEUE_ID\", \"SobjectType\": \"MessagingSession\"}" ``` #### Browser Agent ```text 1. Navigate to Setup → Queues 2. Click New 3. Set Label: {{CHANNEL_NAME}} Queue 4. Set Queue Name: {{CHANNEL_NAME}}_Queue 5. In Supported Objects, add Messaging Session 6. Add queue members (agents who will handle escalations) 7. Click Save ``` **Verify queue creation:** Expected result: One queue record returned with DeveloperName = '{{CHANNEL_NAME}}\_Queue'. ### Route the channel to your Agentforce agent Set the SessionHandlerId on the MessagingChannel to point to your agent's BotDefinition. This enables direct agent routing for all inbound WhatsApp messages. **Choose one of the following methods:** #### SF CLI ```bash # Get the Bot ID and Queue ID BOT_ID=$(sf data query --target-org {{ORG_ALIAS}} --json \ --query "SELECT Id FROM BotDefinition WHERE DeveloperName='{{AGENT_NAME}}'" \ | jq -r '.result.records[0].Id') QUEUE_ID=$(sf data query --target-org {{ORG_ALIAS}} --json \ --query "SELECT Id FROM Group WHERE Type='Queue' AND DeveloperName='{{CHANNEL_NAME}}_Queue'" \ | jq -r '.result.records[0].Id') CHANNEL_ID=$(sf data query --target-org {{ORG_ALIAS}} --json \ --query "SELECT Id FROM MessagingChannel WHERE DeveloperName='{{CHANNEL_NAME}}'" \ | jq -r '.result.records[0].Id') # Update the channel with agent routing sf apex run --target-org {{ORG_ALIAS}} << EOF MessagingChannel channel = [SELECT Id FROM MessagingChannel WHERE DeveloperName = '{{CHANNEL_NAME}}' LIMIT 1]; channel.SessionHandlerId = '$BOT_ID'; channel.FallbackQueueId = '$QUEUE_ID'; update channel; System.debug('Updated MessagingChannel: ' + channel.Id + ' with SessionHandlerId: $BOT_ID'); EOF ``` #### REST API ```bash BOT_ID=$(curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id+FROM+BotDefinition+WHERE+DeveloperName='{{AGENT_NAME}}'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" | jq -r '.records[0].Id') QUEUE_ID=$(curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id+FROM+Group+WHERE+Type='Queue'+AND+DeveloperName='{{CHANNEL_NAME}}_Queue'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" | jq -r '.records[0].Id') CHANNEL_ID=$(curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id+FROM+MessagingChannel+WHERE+DeveloperName='{{CHANNEL_NAME}}'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" | jq -r '.records[0].Id') curl -X PATCH "{{INSTANCE_URL}}/services/data/v64.0/sobjects/MessagingChannel/$CHANNEL_ID" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" \ -H "Content-Type: application/json" \ -d "{ \"SessionHandlerId\": \"$BOT_ID\", \"FallbackQueueId\": \"$QUEUE_ID\" }" ``` #### Browser Agent ```text 1. Navigate to Setup → Messaging Settings 2. Click on your WhatsApp channel: {{CHANNEL_NAME}} 3. Under Routing Configuration: a. Set Routing Type to "Agentforce Service Agent" b. Select your agent: {{AGENT_NAME}} c. Set Fallback Queue: {{CHANNEL_NAME}} Queue 4. Click Save ``` **Verify agent routing:** Expected result: Query the channel and confirm SessionHandlerId matches your BotDefinition.Id. > **💡 Tip**: If `SessionHandlerId` is not available in your org's API version, configure routing through an Omni-Channel inbound flow instead. Create a flow with the Route Work action: Service Channel = `sfdc_livemessage`, Route To = your Agentforce Service Agent. ## 6. Test and Verify Send a test message and confirm the full pipeline works end-to-end. ### Send a test WhatsApp message Use your personal phone to send a message to the WhatsApp Business number and verify the agent responds. **Choose one of the following methods:** #### SF CLI ```bash # After sending a test message from your phone, query for the session: sf data query --target-org {{ORG_ALIAS}} \ --query "SELECT Id, Status, AgentType, OwnerId, CreatedDate, MessagingChannelId FROM MessagingSession WHERE MessagingChannel.DeveloperName='{{CHANNEL_NAME}}' ORDER BY CreatedDate DESC LIMIT 5" ``` #### REST API ```bash curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id,Status,AgentType,OwnerId,CreatedDate,MessagingChannelId+FROM+MessagingSession+WHERE+MessagingChannel.DeveloperName='{{CHANNEL_NAME}}'+ORDER+BY+CreatedDate+DESC+LIMIT+5" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" ``` #### Browser Agent ```text 1. Open WhatsApp on your personal phone 2. Add the business phone number as a contact 3. Send a test message: "Hello, I need help" 4. Wait 5-10 seconds for the agent to respond 5. Verify you receive a response from the agent 6. Try a second message to confirm multi-turn works ``` **Verify the agent responds:** Expected result: Agent responds to WhatsApp message. MessagingSession record created with Status = 'Active' or 'Ended'. ### Verify agent routing and session records Confirm messages are being routed to your agent by checking MessagingSession and AgentWork records. **Choose one of the following methods:** #### SF CLI ```bash # Check recent MessagingSessions for WhatsApp sf data query --target-org {{ORG_ALIAS}} \ --query "SELECT Id, Status, AgentType, OwnerId, CreatedDate FROM MessagingSession WHERE MessagingChannel.MessageType='WhatsApp' ORDER BY CreatedDate DESC LIMIT 5" # Check AgentWork records sf data query --target-org {{ORG_ALIAS}} \ --query "SELECT Id, WorkItemId, BotId, RoutingType, CreatedDate FROM AgentWork ORDER BY CreatedDate DESC LIMIT 5" # Check individual messages in the session sf data query --target-org {{ORG_ALIAS}} \ --query "SELECT Id, MessageText, SenderType, CreatedDate FROM MessagingEvent WHERE MessagingSession.MessagingChannel.DeveloperName='{{CHANNEL_NAME}}' ORDER BY CreatedDate DESC LIMIT 10" ``` #### REST API ```bash curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id,Status,AgentType,OwnerId,CreatedDate+FROM+MessagingSession+WHERE+MessagingChannel.MessageType='WhatsApp'+ORDER+BY+CreatedDate+DESC+LIMIT+5" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id,WorkItemId,BotId,RoutingType,CreatedDate+FROM+AgentWork+ORDER+BY+CreatedDate+DESC+LIMIT+5" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" ``` #### Browser Agent ```text 1. Navigate to the App Launcher → Messaging Sessions 2. Find the most recent session 3. Click to view details 4. Verify: - Owner matches your agent - Message Type shows WhatsApp - Status is Active or Ended 5. Check the Messaging Events related list for individual messages ``` **Verify messaging session records:** Expected result: Recent MessagingSession exists with AgentType populated and messages visible in MessagingEvent records. > **⚠️ Warning**: WhatsApp enforces a **24-hour messaging window**. After the customer's last inbound message, you have 24 hours to respond with free-form text. After that window closes, only pre-approved **Message Templates** (submitted and approved via Meta Business Manager) can be sent. This applies to both agent and human responses. ## Troubleshooting ### Messages not reaching agent 1. Verify the MessagingChannel is Active: query `IsActive` field 2. Confirm `SessionHandlerId` is set and points to the correct BotDefinition.Id 3. Check the agent is Active (not Draft) in Agent Builder 4. Verify Omni-Channel is enabled in Setup 5. Check that the `sfdc_livemessage` Service Channel exists (auto-created with Digital Engagement) ### Phone number verification fails during channel creation 1. The number is already registered with WhatsApp. Deregister it first by deleting the WhatsApp account from the phone 2. The number cannot receive SMS or voice calls. Use a number that can 3. The verification code expired. Retry the verification step in the Setup wizard ### Channel creation fails in Setup wizard 1. Verify the Digital Engagement license is provisioned and active 2. Check your Meta Business verification status at business.facebook.com 3. Ensure your Meta Business account has a WhatsApp Business Account (WABA) created 4. Clear browser cache and try in incognito mode if the OAuth popup fails 5. Check for browser popup blockers interfering with the Meta OAuth flow ### 24-hour window expired and outbound messages fail 1. Submit Message Templates for approval in Meta Business Manager 2. Use approved templates for outbound messages after the 24-hour window 3. Templates typically take 24-48 hours for Meta to review and approve 4. Customer must send a new inbound message to reopen the free-form window ### WhatsApp messages arriving but agent not responding 1. Check `SessionHandlerId` versus `FallbackQueueId` routing. If SessionHandlerId is empty, messages route to the queue instead of the agent 2. Verify the agent's default_agent_user has proper permissions (Einstein Agent User profile, AgentforceServiceAgentUser permission set) 3. Check for errors in the agent's trace logs via Agent Builder preview ### Supported message types WhatsApp supports: text (up to 4,096 characters per message), images, documents, location sharing, and interactive messages (buttons, lists). The Agentforce agent can receive all types but responds with text by default. Rich message responses require additional configuration via Messaging Components. ## Definition of Done **Definition of Done:** - WhatsApp channel active in Messaging Settings with MessageType = WhatsApp - Customer can send a WhatsApp message to the business number - Agent responds to the message automatically - MessagingSession record created with correct routing to agent - Escalation to human agent queue works when agent transfers ## SMS Setup # SMS Setup (30 min) ## Outcome A working SMS channel where customers can text a phone number and interact with your Agentforce Service Agent in real-time. When complete, you'll have: - A provisioned phone number (long code, toll-free, or short code) - A Messaging Channel with `MessageType = Text` routed to your agent - Omni-Channel routing configured with fallback queue - Verified end-to-end text message flow from phone to agent > **ℹ️ Info**: SMS channels require the **Digital Engagement** add-on license. Phone numbers are provisioned through Salesforce's built-in setup wizard. Available number types: long codes (10-digit), toll-free (US/Canada), and short codes (5-6 digit, US/Canada). ## Architecture When a customer sends a text message to your provisioned phone number, the message flows through the following chain: *(Architecture diagram — see the live page for the visual)* The key routing link is `SessionHandlerId` on the MessagingChannel, which points directly to your agent's `BotDefinition.Id`. Unlike Enhanced Chat, SMS does not require an Experience Cloud site or an EmbeddedServiceConfig deployment. The MessagingChannel itself is the entry point. The ServiceChannel for SMS messaging is `sfdc_livemessage`, and the queue SObject type is `MessagingSession`. ### Configure Your Variables **Variable Reference** — replace these placeholders in commands below: | Variable | Description | Example | |----------|-------------|---------| | `{{ORG_ALIAS}}` | Salesforce org alias or username | `` | | `{{AGENT_NAME}}` | Service Agent developer name | `Reservation_Agent` | | `{{CHANNEL_NAME}}` | Messaging Channel developer name | `AgentforceChannel` | | `{{INSTANCE_URL}}` | Salesforce instance URL | `https://myorg.my.salesforce.com` | | `{{ACCESS_TOKEN}}` | OAuth access token | `YOUR_ACCESS_TOKEN` | ## Implementation Steps ## 1. Verify Your Starting Point Confirm your agent exists, your org has the required licenses, and CLI access is ready. ### Confirm your Service Agent exists and is active Before building the routing chain, verify your Agentforce Service Agent is deployed and active in the target org. **Choose one of the following methods:** #### SF CLI ```bash sf data query \ --target-org {{ORG_ALIAS}} \ --query "SELECT Id, DeveloperName, MasterLabel, AgentType FROM BotDefinition WHERE DeveloperName='{{AGENT_NAME}}' AND IsActive=true" ``` #### REST API ```bash curl -X GET "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id,DeveloperName,MasterLabel,AgentType+FROM+BotDefinition+WHERE+DeveloperName='{{AGENT_NAME}}'+AND+IsActive=true" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" \ -H "Content-Type: application/json" ``` #### Browser Agent ```text 1. Navigate to Setup -> Agents -> Agent Builder 2. Find your agent by name: {{AGENT_NAME}} 3. Verify the status shows Active 4. Copy the Agent ID for later use ``` **Verify the agent is active:** Expected result: One record returned with a valid Id. If no record is returned, activate your agent in Agent Builder before proceeding. ### Authenticate and verify org access Ensure you have admin access to the target org and the CLI is connected. **Choose one of the following methods:** #### SF CLI ```bash sf org login web --alias {{ORG_ALIAS}} --instance-url https://login.salesforce.com sf org display --target-org {{ORG_ALIAS}} --verbose ``` #### REST API ```bash # Use OAuth 2.0 web server flow to obtain access token # Then verify with: curl -X GET "{{INSTANCE_URL}}/services/data/v64.0/limits" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" ``` #### Browser Agent ```text 1. Log into your Salesforce org at https://login.salesforce.com 2. Verify you see Setup in the gear menu (admin access required) 3. Navigate to Setup -> Company Information to confirm org details ``` **Verify org access:** Expected result: Org display shows your username and instance URL without errors. ### Verify Digital Engagement license SMS messaging requires the Digital Engagement add-on. Confirm it is active in your org. **Choose one of the following methods:** #### SF CLI ```bash sf data query --target-org {{ORG_ALIAS}} \ --query "SELECT Name, Status, ExpirationDate FROM PackageLicense WHERE NamespacePrefix='sfdc_messaging'" ``` #### REST API ```bash curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Name,Status,ExpirationDate+FROM+PackageLicense+WHERE+NamespacePrefix='sfdc_messaging'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" ``` #### Browser Agent ```text 1. Navigate to Setup -> Company Information 2. Scroll to Permission Set Licenses or Feature Licenses 3. Look for Digital Engagement or Messaging 4. Verify it shows as Active ``` **Verify Digital Engagement license:** Expected result: License record returned with `Status = 'Active'`. ## 2. Enable Messaging and Omni-Channel Turn on the org-level settings that SMS depends on. ### Enable Messaging in Setup Messaging must be enabled at the org level before any messaging channels can be created. **Choose one of the following methods:** #### SF CLI ```bash # Verify Messaging is enabled sf data query --target-org {{ORG_ALIAS}} \ --query "SELECT Id, DeveloperName, IsActive FROM MessagingChannel LIMIT 1" # If this returns an error about the object not existing, # Messaging needs to be enabled via Setup UI (see Browser method) ``` #### REST API ```bash # Check if Messaging object is accessible curl -s "{{INSTANCE_URL}}/services/data/v64.0/sobjects/MessagingChannel/describe" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" | jq '.name' ``` #### Browser Agent ```text 1. Navigate to Setup -> Messaging Settings 2. If prompted, click Enable Messaging 3. Verify the Messaging Settings page shows "Messaging is enabled" ``` **Verify messaging is enabled:** Expected result: Messaging Settings page shows messaging is enabled, or MessagingChannel object is queryable. ### Enable Omni-Channel Omni-Channel handles the routing of messaging sessions to agents and queues. **Choose one of the following methods:** #### SF CLI ```bash # Check if Omni-Channel is enabled by querying ServiceChannel sf data query --target-org {{ORG_ALIAS}} \ --query "SELECT Id, DeveloperName FROM ServiceChannel WHERE DeveloperName='sfdc_livemessage'" ``` #### REST API ```bash curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id,DeveloperName+FROM+ServiceChannel+WHERE+DeveloperName='sfdc_livemessage'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" ``` #### Browser Agent ```text 1. Navigate to Setup -> Omni-Channel Settings 2. If not enabled, check "Enable Omni-Channel" 3. Click Save 4. Verify the sfdc_livemessage ServiceChannel exists under Setup -> Service Channels ``` **Verify service channel:** Expected result: ServiceChannel record for `sfdc_livemessage` exists. ## 3. Provision Your Phone Number Salesforce handles the carrier relationship and number provisioning through its built-in setup wizard. No external telephony provider is needed. > **💡 Tip**: **Long codes** are standard 10-digit phone numbers, good for low-volume conversational messaging. **Toll-free numbers** support higher throughput for US/Canada. **Short codes** (5-6 digits) are best for high-volume, mass messaging but require a longer carrier approval process (weeks to months). ### Request a phone number through Salesforce Setup **Choose one of the following methods:** #### SF CLI ```bash # Phone number provisioning must be done through Setup UI # After provisioning, verify the number is available: sf data query --target-org {{ORG_ALIAS}} \ --query "SELECT Id, MasterLabel, MessageType, MessagingPlatformKey, IsActive FROM MessagingChannel WHERE MessageType='Text'" ``` #### REST API ```bash # Phone number provisioning must be done through Setup UI # After provisioning, verify: curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id,MasterLabel,MessageType,MessagingPlatformKey,IsActive+FROM+MessagingChannel+WHERE+MessageType='Text'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" ``` #### Browser Agent ```text 1. Navigate to Setup -> Messaging Settings 2. Click New Channel 3. Select SMS as the channel type 4. Choose your number type: - Long Code: Standard 10-digit number (instant provisioning) - Toll-Free: Higher throughput for US/Canada (minutes to hours) - Short Code: 5-6 digit number (weeks for carrier approval) 5. Follow the provisioning wizard to request your number 6. For long codes and toll-free: number is typically available immediately 7. For short codes: you will receive a notification when approved 8. Note the provisioned phone number for the next step ``` > **⚠️ Warning**: Short code provisioning requires carrier approval and can take **2-12 weeks**. Long codes and toll-free numbers are typically provisioned within minutes. Plan accordingly if you need a short code for production. **Verify phone number provisioning:** Expected result: A phone number has been provisioned and is visible in Messaging Settings. ## 4. Create the SMS Messaging Channel Configure the Messaging Channel to route incoming text messages to your agent. ### Create a messaging queue for fallback routing The queue handles messages when the agent is unavailable. Even with direct agent routing, a fallback queue is required. **Choose one of the following methods:** #### SF CLI ```bash # Check if queue exists sf data query --target-org {{ORG_ALIAS}} \ --query "SELECT Id FROM Group WHERE Type='Queue' AND DeveloperName='{{CHANNEL_NAME}}_Queue'" # If not found, create via Metadata API cat > queue-metadata.xml << 'EOF' {{CHANNEL_NAME}}_Queue {{CHANNEL_NAME}} Queue MessagingSession EOF sf project deploy start --target-org {{ORG_ALIAS}} --metadata Queue:{{CHANNEL_NAME}}_Queue ``` #### REST API ```bash # Create queue curl -X POST "{{INSTANCE_URL}}/services/data/v64.0/sobjects/Group" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" \ -H "Content-Type: application/json" \ -d '{ "Name": "{{CHANNEL_NAME}} Queue", "DeveloperName": "{{CHANNEL_NAME}}_Queue", "Type": "Queue" }' # Add MessagingSession to queue QUEUE_ID=$(curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id+FROM+Group+WHERE+DeveloperName='{{CHANNEL_NAME}}_Queue'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" | jq -r '.records[0].Id') curl -X POST "{{INSTANCE_URL}}/services/data/v64.0/sobjects/QueueSobject" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" \ -H "Content-Type: application/json" \ -d "{\"QueueId\": \"$QUEUE_ID\", \"SobjectType\": \"MessagingSession\"}" ``` #### Browser Agent ```text 1. Navigate to Setup -> Queues 2. Click New 3. Set Label: {{CHANNEL_NAME}} Queue 4. Set Queue Name: {{CHANNEL_NAME}}_Queue 5. In Supported Objects, add Messaging Session 6. Click Save ``` **Verify queue creation:** Expected result: One queue record returned with `DeveloperName = '{{CHANNEL_NAME}}_Queue'`. ### Configure the SMS channel to route to your agent The Messaging Channel is the key routing component. Setting `SessionHandlerId` to your agent's `BotDefinition.Id` enables direct routing of incoming text messages to your Agentforce agent. **Choose one of the following methods:** #### SF CLI ```bash # Get the Bot ID and Queue ID BOT_ID=$(sf data query --target-org {{ORG_ALIAS}} --json \ --query "SELECT Id FROM BotDefinition WHERE DeveloperName='{{AGENT_NAME}}'" \ | jq -r '.result.records[0].Id') QUEUE_ID=$(sf data query --target-org {{ORG_ALIAS}} --json \ --query "SELECT Id FROM Group WHERE Type='Queue' AND DeveloperName='{{CHANNEL_NAME}}_Queue'" \ | jq -r '.result.records[0].Id') # If the channel was created during provisioning, update it to route to your agent CHANNEL_ID=$(sf data query --target-org {{ORG_ALIAS}} --json \ --query "SELECT Id FROM MessagingChannel WHERE MessageType='Text' AND DeveloperName='{{CHANNEL_NAME}}'" \ | jq -r '.result.records[0].Id') sf apex run --target-org {{ORG_ALIAS}} << EOF MessagingChannel channel = [SELECT Id FROM MessagingChannel WHERE Id = '$CHANNEL_ID']; channel.SessionHandlerId = '$BOT_ID'; channel.FallbackQueueId = '$QUEUE_ID'; channel.IsActive = true; update channel; System.debug('Updated MessagingChannel: ' + channel.Id); EOF ``` #### REST API ```bash BOT_ID=$(curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id+FROM+BotDefinition+WHERE+DeveloperName='{{AGENT_NAME}}'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" | jq -r '.records[0].Id') QUEUE_ID=$(curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id+FROM+Group+WHERE+Type='Queue'+AND+DeveloperName='{{CHANNEL_NAME}}_Queue'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" | jq -r '.records[0].Id') # Get the SMS channel created during provisioning CHANNEL_ID=$(curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id+FROM+MessagingChannel+WHERE+MessageType='Text'+AND+DeveloperName='{{CHANNEL_NAME}}'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" | jq -r '.records[0].Id') # Update the channel with agent routing curl -X PATCH "{{INSTANCE_URL}}/services/data/v64.0/sobjects/MessagingChannel/$CHANNEL_ID" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" \ -H "Content-Type: application/json" \ -d "{ \"SessionHandlerId\": \"$BOT_ID\", \"FallbackQueueId\": \"$QUEUE_ID\", \"IsActive\": true }" ``` #### Browser Agent ```text 1. Navigate to Setup -> Messaging Settings 2. Find your SMS channel (created during phone number provisioning) 3. Click Edit 4. Under Routing: - Set Session Handler Type to Agent - Select your agent: {{AGENT_NAME}} 5. Under Fallback Queue, select {{CHANNEL_NAME}} Queue 6. Ensure the channel is Active 7. Click Save ``` **Verify channel creation:** Expected result: MessagingChannel record returned with `IsActive = true`, `MessageType = 'Text'`, and `SessionHandlerId` matching your BotDefinition.Id. ### Verify the complete channel configuration Confirm all routing fields are correctly set on the MessagingChannel. **Choose one of the following methods:** #### SF CLI ```bash sf data query --target-org {{ORG_ALIAS}} \ --query "SELECT Id, DeveloperName, MasterLabel, MessageType, MessagingPlatformKey, IsActive, SessionHandlerId, FallbackQueueId FROM MessagingChannel WHERE DeveloperName='{{CHANNEL_NAME}}'" ``` #### REST API ```bash curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id,DeveloperName,MasterLabel,MessageType,MessagingPlatformKey,IsActive,SessionHandlerId,FallbackQueueId+FROM+MessagingChannel+WHERE+DeveloperName='{{CHANNEL_NAME}}'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" ``` #### Browser Agent ```text 1. Navigate to Setup -> Messaging Settings 2. Click on your SMS channel: {{CHANNEL_NAME}} 3. Verify: - Message Type: Text - Status: Active - Session Handler: {{AGENT_NAME}} - Fallback Queue: {{CHANNEL_NAME}} Queue ``` **Verify channel configuration:** Expected result: `MessageType = 'Text'`, `IsActive = true`, `SessionHandlerId` is populated, `FallbackQueueId` is populated. ## 5. Configure Consent and Compliance SMS messaging requires opt-in consent tracking and compliance with telecommunications regulations. ### Configure consent keywords Salesforce automatically handles standard consent keywords, but you should verify the configuration. **Choose one of the following methods:** #### SF CLI ```bash # Verify consent keywords are configured for the channel sf data query --target-org {{ORG_ALIAS}} \ --query "SELECT Id, MessagingChannelId, ConsentType, DoubleOptInRequired FROM MessagingChannelConsent WHERE MessagingChannel.DeveloperName='{{CHANNEL_NAME}}'" ``` #### REST API ```bash curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id,MessagingChannelId,ConsentType,DoubleOptInRequired+FROM+MessagingChannelConsent+WHERE+MessagingChannel.DeveloperName='{{CHANNEL_NAME}}'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" ``` #### Browser Agent ```text 1. Navigate to Setup -> Messaging Settings 2. Click on your SMS channel: {{CHANNEL_NAME}} 3. Scroll to Consent Settings 4. Verify consent type is configured: - ImplicitOptIn: Consent assumed when customer initiates - ExplicitOptIn: Customer must text a keyword to opt in - DoubleOptIn: Customer must confirm opt-in via reply 5. Review auto-configured keywords: - Opt-out: STOP, CANCEL, END, QUIT, STOPALL, UNSUBSCRIBE - Help: HELP ``` > **⚠️ Warning**: **TCPA Compliance is your responsibility.** Salesforce auto-configures STOP/HELP keyword handling, but you must ensure your SMS usage complies with the Telephone Consumer Protection Act (TCPA) and carrier requirements. This includes maintaining proper consent records, honoring opt-out requests immediately, and only messaging customers who have given consent. **Verify consent configuration:** Expected result: Consent configuration exists for the channel with appropriate ConsentType. ## 6. Test and Verify Send a test SMS and confirm the full routing chain works. ### Send a test text message Use a real phone to send a text message to your provisioned number and verify the agent responds. **Choose one of the following methods:** #### SF CLI ```bash # After sending a text message from your phone, verify the session was created sf data query --target-org {{ORG_ALIAS}} \ --query "SELECT Id, Status, AgentType, OwnerId, CreatedDate, MessageType FROM MessagingSession WHERE MessageType='Text' ORDER BY CreatedDate DESC LIMIT 5" ``` #### REST API ```bash curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id,Status,AgentType,OwnerId,CreatedDate,MessageType+FROM+MessagingSession+WHERE+MessageType='Text'+ORDER+BY+CreatedDate+DESC+LIMIT+5" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" ``` #### Browser Agent ```text 1. From your personal phone, send a text message to the provisioned number 2. Type: "Hello, I need help with my order" 3. Wait 10-30 seconds for the agent to respond 4. Verify you receive a text reply from the agent 5. Continue the conversation to test multi-turn behavior ``` **Verify the agent responds:** Expected result: You receive a text response from the agent, and a MessagingSession record is created with `MessageType = 'Text'`. ### Verify agent routing records Confirm messages are being routed to your agent by checking MessagingSession and AgentWork records. **Choose one of the following methods:** #### SF CLI ```bash # Check recent MessagingSessions for SMS sf data query --target-org {{ORG_ALIAS}} \ --query "SELECT Id, Status, AgentType, OwnerId, CreatedDate, MessageType FROM MessagingSession WHERE MessageType='Text' ORDER BY CreatedDate DESC LIMIT 5" # Check AgentWork records sf data query --target-org {{ORG_ALIAS}} \ --query "SELECT Id, WorkItemId, BotId, RoutingType, CreatedDate FROM AgentWork ORDER BY CreatedDate DESC LIMIT 5" # Verify individual messages in the session sf data query --target-org {{ORG_ALIAS}} \ --query "SELECT Id, MessageText, SenderType, SentDate FROM MessagingMessage ORDER BY SentDate DESC LIMIT 10" ``` #### REST API ```bash curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id,Status,AgentType,OwnerId,CreatedDate,MessageType+FROM+MessagingSession+WHERE+MessageType='Text'+ORDER+BY+CreatedDate+DESC+LIMIT+5" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id,WorkItemId,BotId,RoutingType,CreatedDate+FROM+AgentWork+ORDER+BY+CreatedDate+DESC+LIMIT+5" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" ``` #### Browser Agent ```text 1. Navigate to Setup -> Object Manager -> Messaging Session 2. Click Recent Messaging Sessions tab 3. Look for a session with Message Type = Text created in the last few minutes 4. Click to view details 5. Verify Owner matches your agent 6. Check the Messages related list for both inbound and outbound messages ``` **Verify messaging session records:** Expected result: Recent MessagingSession exists with `Status = 'Active'` or `'Ended'`, `MessageType = 'Text'`, and AgentWork record shows routing to your agent. ### Test opt-out compliance Verify that consent keywords are properly handled by the platform. **Choose one of the following methods:** #### SF CLI ```bash # After sending "STOP" from your test phone, verify opt-out was recorded sf data query --target-org {{ORG_ALIAS}} \ --query "SELECT Id, MessagingChannelId, ConsentStatus, MessagingEndUserId FROM MessagingConsent ORDER BY LastModifiedDate DESC LIMIT 5" ``` #### REST API ```bash curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id,MessagingChannelId,ConsentStatus,MessagingEndUserId+FROM+MessagingConsent+ORDER+BY+LastModifiedDate+DESC+LIMIT+5" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" ``` #### Browser Agent ```text 1. From your test phone, text "STOP" to the provisioned number 2. You should receive an automatic opt-out confirmation 3. Text "START" or "UNSTOP" to re-subscribe 4. Verify you can resume messaging with the agent ``` **Verify opt-out handling:** Expected result: Sending "STOP" triggers an automatic opt-out response, and a MessagingConsent record reflects the opt-out status. ## Troubleshooting ### Messages not reaching agent 1. Verify the MessagingChannel is Active (`IsActive = true`) 2. Confirm `SessionHandlerId` is set and points to the correct BotDefinition 3. Check that the agent is Active (not Draft) in Agent Builder 4. Verify the `sfdc_livemessage` ServiceChannel exists under Omni-Channel 5. Ensure Digital Engagement license is active ### Number provisioning delays - **Long codes**: Provisioned instantly or within minutes - **Toll-free numbers**: Usually available within minutes to hours - **Short codes**: Require carrier approval, which takes **2-12 weeks**. Short codes also require a campaign use case submission to carriers. Plan for this lead time in production timelines. ### Character limit and message splitting SMS messages are limited to 160 characters per segment. Longer agent responses are automatically split into multiple segments by the carrier. To keep agent responses clean: 1. Keep agent instructions concise to encourage shorter responses 2. Test with longer responses to verify segment splitting is acceptable 3. MMS (images) is supported on carriers that allow it, but adds cost per message ### Opt-out compliance issues Salesforce auto-configures these consent keywords: - **Opt-out**: STOP, CANCEL, END, QUIT, STOPALL, UNSUBSCRIBE - **Help**: HELP If customers report they cannot opt out, verify the MessagingChannelConsent configuration and check that the channel's consent type is set appropriately for your use case. ### Agent responds but messages are delayed 1. Check Omni-Channel queue depth in the Omni-Channel Supervisor 2. Verify the agent's response time is within acceptable bounds 3. Carrier network latency can add 1-5 seconds to delivery 4. If using a shared short code, high volume may cause queuing ### Cannot send outbound messages to opted-in customers Unlike channels with a 24-hour messaging window (WhatsApp, Facebook Messenger), SMS has no platform-imposed window. However, you must have valid consent from the customer. Check the `MessagingConsent` records and ensure `ConsentStatus` is not `OptedOut`. ## Definition of Done **Definition of Done:** - SMS channel active in Messaging Settings with `MessageType = Text` - Customer can text the provisioned phone number - Agent responds automatically via SMS - MessagingSession record created with `MessageType = Text` - Opt-out keywords (STOP) trigger automated opt-out response - Escalation to human agent queue works via fallback queue - Consent records are properly tracked in MessagingConsent ## Messenger Setup # Messenger Setup (30 min) ## Outcome By the end of this guide, your Salesforce org has: - A Facebook Messenger channel connected to your Facebook Page. - Omni-Channel routing delivering Messenger conversations to your Agentforce Service Agent. - A verified end-to-end flow: customer messages your Page, agent responds automatically in Messenger. - SOQL evidence confirming MessagingSession creation and agent assignment. > **ℹ️ Info**: Facebook Messenger channels require the **Digital Engagement** add-on license and a **Facebook Page** managed through **Meta Business Manager**. No Experience Cloud site or EmbeddedServiceConfig is needed. ## Architecture *(Architecture diagram — see the live page for the visual)* Key metadata relationships: - `MessagingChannel.MessageType` = `Facebook` - `MessagingChannel.MessagingPlatformKey` = Facebook Page ID - `ServiceChannel.DeveloperName` = `sfdc_livemessage` - `Queue.SobjectType` = `MessagingSession` - `ConsentType` = ImplicitOptIn (user initiates by messaging your Page) ### Configure Your Variables **Variable Reference** — replace these placeholders in commands below: | Variable | Description | Example | |----------|-------------|---------| | `{{ORG_ALIAS}}` | Salesforce org alias or username | `` | | `{{AGENT_NAME}}` | Service Agent developer name | `Reservation_Agent` | | `{{CHANNEL_NAME}}` | Messaging Channel developer name | `AgentforceChannel` | | `{{INSTANCE_URL}}` | Salesforce instance URL | `https://myorg.my.salesforce.com` | | `{{ACCESS_TOKEN}}` | OAuth access token | `YOUR_ACCESS_TOKEN` | ## Prerequisites - Salesforce CLI (`sf`) installed. - Admin-level access in the target Salesforce org. - **Digital Engagement** license provisioned on the org. - A published, active Agentforce Service Agent (example: `{{AGENT_NAME}}`). - A Facebook Page where you are a Page admin. - A Meta Business Manager account (business.facebook.com). ## Step 1: Set Environment Variables and Authenticate Set org alias, agent name, channel details, and queue name; then authenticate with the CLI. Use these defaults or replace with your own values. **Choose one of the following methods:** #### SF CLI ```bash export ORG_ALIAS="MY_ORG" export API_VERSION="65.0" export AGENT_NAME="Support_Agent" export CHANNEL_DEV_NAME="Facebook_Messenger_Channel" export CHANNEL_LABEL="Facebook Messenger Channel" export QUEUE_DEV_NAME="Messenger_Queue" export QUEUE_LABEL="Messenger Queue" # Authenticate and verify sf org login web --alias "$ORG_ALIAS" --instance-url https://login.salesforce.com sf org display --target-org "$ORG_ALIAS" --verbose --json ``` > **💡 Tip**: If you already have an authenticated org alias, skip `sf org login web` and just run `sf org display` to confirm your session is active. ## Step 2: Enable Messaging and Omni-Channel Confirm that Messaging and Omni-Channel are enabled in your org's Setup. Both Messaging and Omni-Channel must be enabled before you can create a Facebook Messenger channel. ### A. Enable Messaging 1. Navigate to **Setup > Messaging Settings**. 2. Toggle **Messaging** to **On** if not already enabled. > **ℹ️ Info**: Messaging Settings is auto-enabled in most orgs with Digital Engagement. If you do not see this option, confirm the Digital Engagement license is provisioned. ### B. Enable Omni-Channel 1. Navigate to **Setup > Omni-Channel Settings**. 2. Toggle **Enable Omni-Channel** to **On** if not already enabled. ### C. Verify the service channel exists **Choose one of the following methods:** #### SF CLI ```bash sf data query \ --target-org "$ORG_ALIAS" \ --query "SELECT Id,DeveloperName,MasterLabel FROM ServiceChannel WHERE DeveloperName='sfdc_livemessage' LIMIT 1" ``` #### REST API ```bash # Using curl with access token from sf org display ACCESS_TOKEN=$(sf org display --target-org "$ORG_ALIAS" --json | python3 -c "import sys,json; print(json.load(sys.stdin)['result']['accessToken'])") INSTANCE_URL=$(sf org display --target-org "$ORG_ALIAS" --json | python3 -c "import sys,json; print(json.load(sys.stdin)['result']['instanceUrl'])") curl -s "$INSTANCE_URL/services/data/v$API_VERSION/query?q=SELECT+Id,DeveloperName,MasterLabel+FROM+ServiceChannel+WHERE+DeveloperName='sfdc_livemessage'+LIMIT+1" \ -H "Authorization: Bearer $ACCESS_TOKEN" | python3 -m json.tool ``` **Verify the Live Message channel is set up:** **Expected result:** `sfdc_livemessage` ServiceChannel record returned with a valid Id If no record is returned, Messaging is not enabled. Return to Setup > Messaging Settings and enable it. ## Step 3: Set Up Meta Business and Facebook Page Prepare your Meta Business Manager account, Facebook Page, and Meta App for the Messenger integration. This step is performed entirely in Meta's platforms. Salesforce handles webhook registration automatically when you connect the channel in Step 4. ### A. Verify Meta Business Manager 1. Go to [business.facebook.com](https://business.facebook.com). 2. Confirm you have an active Meta Business Manager account. 3. If not, create one and complete business verification. ### B. Confirm Your Facebook Page 1. In Meta Business Manager, go to **Business Settings > Accounts > Pages**. 2. Confirm you have a Facebook Page and that you are a **Page admin**. 3. Note the **Page ID** (visible in Page Settings > Page transparency, or in the URL). ### C. Create a Meta App (if not already done) 1. Go to [developers.facebook.com](https://developers.facebook.com). 2. Click **Create App** and select a Business type. 3. Add the **Messenger** product to your app. 4. Link your Facebook Page to the app under **Messenger > Settings**. > **⚠️ Warning**: For production use beyond Page admins and testers, your Meta App must pass **App Review** for the `pages_messaging` permission. This review process can take days to weeks. Until approved, only users listed as Page admins, developers, or testers in the Meta App dashboard can exchange messages. > **💡 Tip**: You do **not** need to manually configure webhooks in the Meta App dashboard. Salesforce registers the webhook endpoint automatically during channel creation in Step 4. The webhook URL follows the format: `https://{{INSTANCE_URL}}/services/messaging/facebook/webhook/{{CHANNEL_ID}}` ## Step 4: Create the Messenger Channel Connect your Facebook Page to Salesforce via Setup and configure routing to your agent. ### A. Create the Channel via Setup UI 1. Navigate to **Setup > Messaging Settings**. 2. Click **New Channel**. 3. Select **Facebook Messenger** as the channel type. 4. Salesforce initiates an OAuth flow to Meta. Log in with your Facebook credentials and authorize access to your Page. 5. Select the Facebook Page you want to connect. 6. Complete the wizard. Salesforce automatically registers the webhook with Meta. ### B. Configure Agent Routing on the Channel 1. In **Setup > Messaging Settings**, click into your new Messenger channel. 2. Under **Routing**, set: - **Session Handler Type**: `AgentforceServiceAgent` - **Session Handler**: Select your agent (`{{AGENT_NAME}}`) 3. Set a **Fallback Queue** (for escalation when the agent hands off to a human). 4. Save and activate the channel. ### C. Verify the Channel via CLI **Choose one of the following methods:** #### SF CLI ```bash sf data query \ --target-org "$ORG_ALIAS" \ --query "SELECT Id,DeveloperName,IsActive,MessageType,MessagingPlatformKey,SessionHandlerId,FallbackQueueId FROM MessagingChannel WHERE DeveloperName='$CHANNEL_DEV_NAME' LIMIT 1" ``` #### REST API ```bash curl -s "$INSTANCE_URL/services/data/v$API_VERSION/query?q=SELECT+Id,DeveloperName,IsActive,MessageType,MessagingPlatformKey,SessionHandlerId,FallbackQueueId+FROM+MessagingChannel+WHERE+DeveloperName='$CHANNEL_DEV_NAME'+LIMIT+1" \ -H "Authorization: Bearer $ACCESS_TOKEN" | python3 -m json.tool ``` **Verify the Messenger channel is configured:** **Expected result:** MessagingChannel record with `MessageType=Facebook`, `IsActive=true`, and `SessionHandlerId` matching your BotDefinition Id If `SessionHandlerId` is null, re-open the channel settings and assign your agent. If `MessageType` is not `Facebook`, the channel was created with the wrong type. ### D. Create or Verify the Queue (if not done via UI) If the wizard did not create a fallback queue, create one manually: **Choose one of the following methods:** #### SF CLI ```bash # Check if queue exists sf data query \ --target-org "$ORG_ALIAS" \ --query "SELECT Id,Name,DeveloperName FROM Group WHERE Type='Queue' AND DeveloperName='$QUEUE_DEV_NAME' LIMIT 1" ``` #### REST API ```bash # Create queue if it does not exist curl -s "$INSTANCE_URL/services/data/v$API_VERSION/sobjects/Group" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d "{\"Name\": \"$QUEUE_LABEL\", \"DeveloperName\": \"$QUEUE_DEV_NAME\", \"Type\": \"Queue\"}" | python3 -m json.tool # Map queue to MessagingSession QUEUE_ID=$(sf data query --target-org "$ORG_ALIAS" --query "SELECT Id FROM Group WHERE Type='Queue' AND DeveloperName='$QUEUE_DEV_NAME' LIMIT 1" --json | python3 -c "import sys,json; print(json.load(sys.stdin)['result']['records'][0]['Id'])") curl -s "$INSTANCE_URL/services/data/v$API_VERSION/sobjects/QueueSobject" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d "{\"QueueId\": \"$QUEUE_ID\", \"SobjectType\": \"MessagingSession\"}" | python3 -m json.tool ``` ## Step 5: Configure Routing to Your Agent Ensure Omni-Channel routes Messenger conversations to your Agentforce agent with a fallback queue for escalation. If you already set the Session Handler in Step 4B, this step confirms the routing path. If your org requires flow-based routing instead of direct `SessionHandlerId`, configure it here. ### Direct Routing (Preferred) Direct routing uses `SessionHandlerId` on the MessagingChannel to point to your BotDefinition. This was configured in Step 4B. Verify the agent identity: **Choose one of the following methods:** #### SF CLI ```bash sf data query \ --target-org "$ORG_ALIAS" \ --query "SELECT Id,DeveloperName,MasterLabel,AgentType FROM BotDefinition WHERE DeveloperName='$AGENT_NAME' LIMIT 1" ``` ### Flow-Based Routing (Fallback) If your org does not support `SessionHandlerId` on MessagingChannel, use an Omni-Channel inbound flow: 1. Navigate to **Setup > Flows**. 2. Create a new **Record-Triggered Flow** or **Omni-Channel Flow** that: - Triggers on `MessagingSession` creation. - Evaluates channel type = `Facebook`. - Routes to your agent queue. 3. Assign the flow to the Messaging Channel routing configuration. > **ℹ️ Info**: Direct routing via `SessionHandlerId` is the recommended approach. Flow-based routing is only needed if your org version does not expose the `SessionHandlerId` field for API updates. ## Step 6: Test and Verify Send a test message to your Facebook Page and confirm the agent responds. ### A. Send a Test Message 1. Open Facebook Messenger (web or mobile app). 2. Search for your Facebook Page name. 3. Send a message such as: "Hi, I need help with my order." > **💡 Tip**: Before App Review approval, only Page admins, app developers, and testers (configured in Meta App Settings > Roles) can message the Page and trigger the agent. Add test users in the Meta App dashboard under **App Roles > Testers**. ### B. Verify MessagingSession Creation **Choose one of the following methods:** #### SF CLI ```bash sf data query \ --target-org "$ORG_ALIAS" \ --query "SELECT Id,Status,AgentType,OwnerId,CreatedDate,MessageType FROM MessagingSession ORDER BY CreatedDate DESC LIMIT 5" ``` #### REST API ```bash curl -s "$INSTANCE_URL/services/data/v$API_VERSION/query?q=SELECT+Id,Status,AgentType,OwnerId,CreatedDate,MessageType+FROM+MessagingSession+ORDER+BY+CreatedDate+DESC+LIMIT+5" \ -H "Authorization: Bearer $ACCESS_TOKEN" | python3 -m json.tool ``` ### C. Verify AgentWork Assignment **Choose one of the following methods:** #### SF CLI ```bash sf data query \ --target-org "$ORG_ALIAS" \ --query "SELECT Id,WorkItemId,BotId,RoutingType,CreatedDate FROM AgentWork ORDER BY CreatedDate DESC LIMIT 5" ``` ### D. Verify Agent Response in Messenger Return to the Messenger conversation. Confirm that your Agentforce agent responded to the test message. **Verify the agent responds in Messenger:** **Expected result:** MessagingSession record with `MessageType=Facebook`, agent response visible in Messenger conversation If no session is created, check that the Messenger channel is active and the webhook is registered. If the agent does not respond, verify the BotDefinition is published and activated. > **⚠️ Warning**: Facebook enforces a **24-hour messaging window**. After the customer's last message, you have 24 hours to send responses. After that window closes, only specific **message tags** are permitted (confirmed event updates, post-purchase updates, account updates). Plan your escalation and follow-up flows accordingly. ## Messenger-Specific Constraints | Constraint | Value | | ----------------------- | -------------------------------------------------------------------------------- | | Message character limit | 2,000 per message | | Attachment size limit | 25 MB | | Messaging window | 24 hours from customer's last message | | Supported content | Text, images, files, buttons, quick replies, carousels (via Messenger templates) | | Consent model | ImplicitOptIn (customer initiates by messaging your Page) | | Platform type | Enhanced (not Standard/legacy) | ## Troubleshooting ### Messages not reaching Salesforce Check the following: 1. In the Meta App dashboard (developers.facebook.com), navigate to **Messenger > Settings > Webhooks**. Confirm a webhook is registered and pointing to your Salesforce instance. 2. In Salesforce, verify the Messenger channel is **Active** in Setup > Messaging Settings. 3. Confirm the Facebook Page is linked to the Meta App and that the `pages_messaging` permission is authorized. 4. Test with a user who is a Page admin or listed as a tester in the Meta App. ### Cannot connect Facebook Page during channel creation Verify: 1. You are logged into Facebook as a **Page admin** (not just a Page editor). 2. Your Meta Business Manager account has completed business verification. 3. The Facebook Page is claimed by your Meta Business Manager account (Business Settings > Accounts > Pages). 4. Your browser is not blocking the OAuth popup from Meta. ### App Review pending - limited testing Until your Meta App passes App Review for `pages_messaging`: 1. Only Page admins, developers, and testers can interact with the agent via Messenger. 2. Add test users in the Meta App dashboard: **App Roles > Testers**. 3. Each tester must accept the invitation before they can message. 4. Submit for App Review early as it can take days to weeks. Include screenshots of your Messenger integration and describe the use case clearly. ### 24-hour messaging window expired After 24 hours from the customer's last message: 1. Standard replies are blocked by Meta. 2. Use **message tags** for permitted follow-up categories only: `CONFIRMED_EVENT_UPDATE`, `POST_PURCHASE_UPDATE`, `ACCOUNT_UPDATE`. 3. Design your agent's escalation flow to resolve conversations within the 24-hour window. 4. Consider sending a proactive "Is there anything else?" message before the window closes. ### Agent not responding but session is created 1. Verify the BotDefinition is published and the active BotVersion is correct: ```bash sf data query --target-org "$ORG_ALIAS" --query "SELECT Id,DeveloperName,Status FROM BotVersion WHERE BotDefinition.DeveloperName='$AGENT_NAME' AND Status='Active' LIMIT 1" ``` 2. Check that the `SessionHandlerId` on the MessagingChannel matches the BotDefinition Id. 3. Review agent debug logs in Setup > Agentforce > Agents > [Your Agent] > Logs. ## Definition of Done **Definition of Done:** - Messenger channel active in Messaging Settings with `MessageType=Facebook`. - Customer can message your Facebook Page and reach the Agentforce agent. - Agent responds automatically in Messenger. - `MessagingSession` record created with `MessageType=Facebook`. - `AgentWork` record confirms agent assignment. - Escalation to human agent fallback queue works when agent hands off. ## Apple Messages Setup # Apple Messages Setup (30 min) ## Outcome A working Apple Messages for Business pipeline where customers on iPhone, iPad, or Mac can message your business and receive automated responses from your Agentforce Service Agent. When complete, you will have: - An Apple Messages for Business channel connected to your Salesforce org - Omni-Channel routing configured to send inbound iMessage conversations to your Agentforce agent - Verified end-to-end message flow from Apple Messages to agent and back > **ℹ️ Info**: Apple Messages for Business channels require the **Digital Engagement** add-on license and an approved **Apple Business Register** account (register.apple.com/business). Unlike Enhanced Chat, no Experience Cloud site or EmbeddedServiceConfig is needed. Apple Messages only works on Apple devices (iPhone, iPad, Mac) via the iMessage protocol. ## Architecture When a customer sends a message to your business through Apple Maps, Safari, Spotlight, or Siri, the message flows through the following chain: *(Architecture diagram — see the live page for the visual)* The key routing field is `sessionHandlerType = AgentforceServiceAgent` on the MessagingChannel, with `sessionHandlerAsa` pointing to the BotDefinition DeveloperName. The `sessionHandlerQueue` field specifies the fallback queue when the agent is unavailable. Unlike Enhanced Chat, Apple Messages does not require an EmbeddedServiceConfig or an Experience Cloud site. The channel is a direct integration between Apple's Business Chat infrastructure and Salesforce's Messaging platform. Salesforce handles the webhook registration during channel setup. Key metadata relationships: - `MessagingChannel.MessageType` = `AppleBusinessChat` - `ServiceChannel.DeveloperName` = `sfdc_livemessage` - `Queue.SobjectType` = `MessagingSession` - No 24-hour messaging window (unlike WhatsApp/Messenger) -- Apple allows ongoing conversations ### Configure Your Variables **Variable Reference** — replace these placeholders in commands below: | Variable | Description | Example | |----------|-------------|---------| | `{{ORG_ALIAS}}` | Salesforce org alias or username | `` | | `{{AGENT_NAME}}` | Service Agent developer name | `Reservation_Agent` | | `{{CHANNEL_NAME}}` | Messaging Channel developer name | `AgentforceChannel` | | `{{INSTANCE_URL}}` | Salesforce instance URL | `https://myorg.my.salesforce.com` | | `{{ACCESS_TOKEN}}` | OAuth access token | `YOUR_ACCESS_TOKEN` | ## Implementation Steps ## 1. Verify Your Starting Point Confirm your agent exists, your org has the required licenses, and you have admin access. ### Confirm your Service Agent exists and is active Before building the routing chain, verify your Agentforce Service Agent is deployed and active in the target org. **Choose one of the following methods:** #### SF CLI ```bash sf data query \ --target-org {{ORG_ALIAS}} \ --query "SELECT Id, DeveloperName, MasterLabel, AgentType FROM BotDefinition WHERE DeveloperName='{{AGENT_NAME}}' AND IsActive=true" ``` #### REST API ```bash curl -X GET "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id,DeveloperName,MasterLabel,AgentType+FROM+BotDefinition+WHERE+DeveloperName='{{AGENT_NAME}}'+AND+IsActive=true" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" \ -H "Content-Type: application/json" ``` #### Browser Agent ```text 1. Navigate to Setup > Agents > Agent Builder 2. Find your agent by name: {{AGENT_NAME}} 3. Verify the status shows Active 4. Copy the Agent ID for later use ``` **Verify the agent is active:** Expected result: One record returned with a valid Id. If no record is returned, activate your agent in Agent Builder before proceeding. ### Authenticate and verify org access Ensure you have admin access to the target org and the CLI is connected. **Choose one of the following methods:** #### SF CLI ```bash sf org login web --alias {{ORG_ALIAS}} --instance-url https://login.salesforce.com sf org display --target-org {{ORG_ALIAS}} --verbose ``` #### REST API ```bash # Use OAuth 2.0 web server flow to obtain access token # Then verify with: curl -X GET "{{INSTANCE_URL}}/services/data/v64.0/limits" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" ``` #### Browser Agent ```text 1. Log into your Salesforce org at https://login.salesforce.com 2. Verify you see Setup in the gear menu (admin access required) 3. Navigate to Setup > Company Information to confirm org details ``` **Verify org access:** Expected result: Org display shows your username and instance URL without errors. ### Verify Digital Engagement license Apple Messages for Business channels require the Digital Engagement add-on. Confirm it is provisioned in your org. **Choose one of the following methods:** #### SF CLI ```bash sf data query \ --target-org {{ORG_ALIAS}} \ --query "SELECT Id, Name, Status, IsProvisioned FROM PackageLicense WHERE NamespacePrefix='sfdc_livemessage'" ``` #### REST API ```bash curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id,Name,Status,IsProvisioned+FROM+PackageLicense+WHERE+NamespacePrefix='sfdc_livemessage'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" ``` #### Browser Agent ```text 1. Navigate to Setup > Company Information > Permission Set Licenses 2. Look for "Digital Engagement" or "Messaging" in the list 3. Confirm it shows as Active with available licenses ``` **Verify Digital Engagement license:** Expected result: One record returned with IsProvisioned = true. If not found, contact your Salesforce account executive to provision Digital Engagement. ## 2. Enable Messaging and Omni-Channel Enable the org-level settings required for Apple Messages for Business. ### Enable Messaging in Setup Messaging must be enabled at the org level before any messaging channels can be created. **Choose one of the following methods:** #### SF CLI ```bash # Deploy Messaging settings via metadata mkdir -p force-app/main/default/settings cat > force-app/main/default/settings/OmniChannel.settings-meta.xml << 'EOF' true EOF sf project deploy start \ --target-org {{ORG_ALIAS}} \ --source-dir force-app/main/default/settings/OmniChannel.settings-meta.xml ``` #### REST API ```bash # Messaging and Omni-Channel settings are typically enabled via # Metadata API or Setup UI. See Browser method. ``` #### Browser Agent ```text 1. Navigate to Setup > Messaging Settings 2. If you see a toggle for Messaging, enable it 3. Navigate to Setup > Omni-Channel Settings 4. Check "Enable Omni-Channel" 5. Click Save ``` **Verify messaging is enabled:** Expected result: Messaging Settings page loads without errors and shows channel management options. Omni-Channel Settings shows "Omni-Channel is enabled." ## 3. Register with Apple Business Register Before creating the channel in Salesforce, you must register your business with Apple and get your account approved. > **⚠️ Warning**: Apple reviews and approves every business account. The approval process can take **days to weeks** depending on your business type and verification requirements. Start this step early. You cannot create the Salesforce channel until Apple approves your account. ### Create your Apple Business Register account **Choose one of the following methods:** #### SF CLI ```bash # Apple Business Register setup is browser-based. # No CLI equivalent. See Browser method for complete steps. ``` #### REST API ```bash # Apple Business Register setup requires browser access. # See Browser method for complete steps. ``` #### Browser Agent ```text 1. Go to https://register.apple.com/business 2. Sign in with your Apple Business account (or create one) 3. Register your business: a. Enter your business name, website, and contact information b. Select your business category c. Provide your customer support contact details 4. Plan your customer journey: a. Define your entry points (Maps, Safari, Spotlight, Siri, custom) b. Specify the intents you want to handle (sales, support, scheduling) c. Configure business hours and messaging availability 5. Select Salesforce as your Messaging Service Provider (MSP) 6. Complete the Apple review process: - Apple will verify your business identity - Approval timeline varies: days for established brands, weeks for new registrations - You will receive an email when approved 7. After approval, note your Apple Business Chat Account ID ``` > **ℹ️ Info**: Apple provides test capabilities during the review process. You can use the Apple Business Chat sandbox to test your integration before going live. Ask your Apple Business representative about sandbox access. **Verify Apple Business Register access:** Expected result: You have an approved Apple Business Register account with Salesforce selected as your Messaging Service Provider. ### Configure entry points for your business Entry points determine how customers discover and start conversations with your business on Apple devices. **Choose one of the following methods:** #### SF CLI ```bash # Entry point configuration is managed in Apple Business Register. # See Browser method. ``` #### REST API ```bash # Entry point configuration is managed in Apple Business Register. # See Browser method. ``` #### Browser Agent ```text Entry points where customers can start a conversation: 1. Apple Maps: - Ensure your business location is claimed on Apple Maps Connect - The Messages button appears automatically once channel is active 2. Safari / Spotlight: - Apple indexes your business for Spotlight and Safari suggestions - Customers see a Messages button in search results 3. Siri: - Customers can say "Message [your business name]" - Requires your business to be registered in Apple Maps 4. Custom entry points (your website or app): - Use Apple's Business Chat Button API - Add a "Message on iMessage" button to your website or iOS app - Button URL format: https://bcrw.apple.com/urn:biz:YOUR_BUSINESS_ID - See Apple developer documentation for button styling guidelines ``` **Verify entry point configuration:** Expected result: You have identified and configured at least one entry point for customers to reach your business via Apple Messages. ## 4. Create the Apple Messages Channel in Salesforce Use the Salesforce Setup wizard to create the Apple Messages for Business channel. Salesforce handles the webhook connection to Apple automatically during setup. ### Create the channel via Setup wizard **Choose one of the following methods:** #### SF CLI ```bash # The Apple Messages channel must be created via the Setup wizard. # After creation, verify with: sf data query \ --target-org {{ORG_ALIAS}} \ --query "SELECT Id, DeveloperName, MasterLabel, MessageType, IsActive, MessagingPlatformKey FROM MessagingChannel WHERE MessageType='AppleBusinessChat'" ``` #### REST API ```bash # Apple Messages channel creation requires the Setup wizard. # After creation, verify with: curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id,DeveloperName,MasterLabel,MessageType,IsActive,MessagingPlatformKey+FROM+MessagingChannel+WHERE+MessageType='AppleBusinessChat'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" ``` #### Browser Agent ```text 1. Navigate to Setup > Messaging Settings 2. Click New Channel 3. Select Apple Messages for Business as the channel type 4. Enter your Apple Business Chat Account ID from Step 3 5. Set the Channel Name: {{CHANNEL_NAME}} 6. Salesforce registers the webhook with Apple automatically 7. Review the configuration summary 8. Click Finish to create the channel 9. The channel will show as Active in Messaging Settings ``` **Verify channel was created:** Expected result: Query returns one record with MessageType = 'AppleBusinessChat' and IsActive = true. ### Verify channel details and note key fields After creation, confirm the channel is properly configured with the correct platform key and message type. **Choose one of the following methods:** #### SF CLI ```bash sf data query \ --target-org {{ORG_ALIAS}} \ --query "SELECT Id, DeveloperName, MasterLabel, MessageType, IsActive, MessagingPlatformKey, SessionHandlerId, FallbackQueueId FROM MessagingChannel WHERE DeveloperName='{{CHANNEL_NAME}}'" ``` #### REST API ```bash curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id,DeveloperName,MasterLabel,MessageType,IsActive,MessagingPlatformKey,SessionHandlerId,FallbackQueueId+FROM+MessagingChannel+WHERE+DeveloperName='{{CHANNEL_NAME}}'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" ``` #### Browser Agent ```text 1. Navigate to Setup > Messaging Settings 2. Click on your Apple Messages channel: {{CHANNEL_NAME}} 3. Note the following fields: - Channel Name / DeveloperName - Message Type (should be AppleBusinessChat) - Platform Key (your Apple Business Chat Account ID) - Status (should be Active) ``` **Verify channel configuration:** Expected result: Channel record returned with MessageType = 'AppleBusinessChat', IsActive = true, and MessagingPlatformKey matching your Apple Business Chat Account ID. ## 5. Configure Routing to Your Agent Set up the routing so inbound Apple Messages conversations are handled by your Agentforce Service Agent. ### Create a messaging queue for fallback routing The queue handles messages when the agent is unavailable. Even with direct agent routing, a fallback queue is required. **Choose one of the following methods:** #### SF CLI ```bash # Check if queue exists sf data query --target-org {{ORG_ALIAS}} \ --query "SELECT Id FROM Group WHERE Type='Queue' AND DeveloperName='{{CHANNEL_NAME}}_Queue'" # If not found, create via Metadata API mkdir -p force-app/main/default/queues cat > force-app/main/default/queues/{{CHANNEL_NAME}}_Queue.queue-meta.xml << 'EOF' {{CHANNEL_NAME}}_Queue {{CHANNEL_NAME}} Queue MessagingSession EOF sf project deploy start --target-org {{ORG_ALIAS}} --metadata Queue:{{CHANNEL_NAME}}_Queue ``` #### REST API ```bash # Create queue curl -X POST "{{INSTANCE_URL}}/services/data/v64.0/sobjects/Group" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" \ -H "Content-Type: application/json" \ -d '{ "Name": "{{CHANNEL_NAME}} Queue", "DeveloperName": "{{CHANNEL_NAME}}_Queue", "Type": "Queue" }' # Add MessagingSession to queue QUEUE_ID=$(curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id+FROM+Group+WHERE+DeveloperName='{{CHANNEL_NAME}}_Queue'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" | jq -r '.records[0].Id') curl -X POST "{{INSTANCE_URL}}/services/data/v64.0/sobjects/QueueSobject" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" \ -H "Content-Type: application/json" \ -d "{\"QueueId\": \"$QUEUE_ID\", \"SobjectType\": \"MessagingSession\"}" ``` #### Browser Agent ```text 1. Navigate to Setup > Queues 2. Click New 3. Set Label: {{CHANNEL_NAME}} Queue 4. Set Queue Name: {{CHANNEL_NAME}}_Queue 5. In Supported Objects, add Messaging Session 6. Add queue members (agents who will handle escalations) 7. Click Save ``` **Verify queue creation:** Expected result: One queue record returned with DeveloperName = '{{CHANNEL_NAME}}\_Queue'. ### Route the channel to your Agentforce agent Set the SessionHandlerId on the MessagingChannel to point to your agent's BotDefinition. This enables direct agent routing for all inbound Apple Messages conversations. **Choose one of the following methods:** #### SF CLI ```bash # Get the Bot ID and Queue ID BOT_ID=$(sf data query --target-org {{ORG_ALIAS}} --json \ --query "SELECT Id FROM BotDefinition WHERE DeveloperName='{{AGENT_NAME}}'" \ | jq -r '.result.records[0].Id') QUEUE_ID=$(sf data query --target-org {{ORG_ALIAS}} --json \ --query "SELECT Id FROM Group WHERE Type='Queue' AND DeveloperName='{{CHANNEL_NAME}}_Queue'" \ | jq -r '.result.records[0].Id') CHANNEL_ID=$(sf data query --target-org {{ORG_ALIAS}} --json \ --query "SELECT Id FROM MessagingChannel WHERE DeveloperName='{{CHANNEL_NAME}}'" \ | jq -r '.result.records[0].Id') # Update the channel with agent routing sf apex run --target-org {{ORG_ALIAS}} << EOF MessagingChannel channel = [SELECT Id FROM MessagingChannel WHERE DeveloperName = '{{CHANNEL_NAME}}' LIMIT 1]; channel.SessionHandlerId = '$BOT_ID'; channel.FallbackQueueId = '$QUEUE_ID'; update channel; System.debug('Updated MessagingChannel: ' + channel.Id + ' with SessionHandlerId: $BOT_ID'); EOF ``` #### REST API ```bash BOT_ID=$(curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id+FROM+BotDefinition+WHERE+DeveloperName='{{AGENT_NAME}}'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" | jq -r '.records[0].Id') QUEUE_ID=$(curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id+FROM+Group+WHERE+Type='Queue'+AND+DeveloperName='{{CHANNEL_NAME}}_Queue'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" | jq -r '.records[0].Id') CHANNEL_ID=$(curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id+FROM+MessagingChannel+WHERE+DeveloperName='{{CHANNEL_NAME}}'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" | jq -r '.records[0].Id') curl -X PATCH "{{INSTANCE_URL}}/services/data/v64.0/sobjects/MessagingChannel/$CHANNEL_ID" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" \ -H "Content-Type: application/json" \ -d "{ \"SessionHandlerId\": \"$BOT_ID\", \"FallbackQueueId\": \"$QUEUE_ID\" }" ``` #### Browser Agent ```text 1. Navigate to Setup > Messaging Settings 2. Click on your Apple Messages channel: {{CHANNEL_NAME}} 3. Under Routing Configuration: a. Set Routing Type to "Agentforce Service Agent" b. Select your agent: {{AGENT_NAME}} c. Set Fallback Queue: {{CHANNEL_NAME}} Queue 4. Click Save ``` **Verify agent routing:** Expected result: Query the channel and confirm SessionHandlerId matches your BotDefinition.Id. > **💡 Tip**: If `SessionHandlerId` is not available in your org's API version, configure routing through an Omni-Channel inbound flow instead. Create a flow with the Route Work action: Service Channel = `sfdc_livemessage`, Route To = your Agentforce Service Agent. ## 6. Test and Verify Send a test message and confirm the full pipeline works end-to-end. ### Send a test message via Apple Messages Use an Apple device (iPhone, iPad, or Mac) to send a message to your business and verify the agent responds. **Choose one of the following methods:** #### SF CLI ```bash # After sending a test message from your Apple device, query for the session: sf data query --target-org {{ORG_ALIAS}} \ --query "SELECT Id, Status, AgentType, OwnerId, CreatedDate, MessagingChannelId FROM MessagingSession WHERE MessagingChannel.DeveloperName='{{CHANNEL_NAME}}' ORDER BY CreatedDate DESC LIMIT 5" ``` #### REST API ```bash curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id,Status,AgentType,OwnerId,CreatedDate,MessagingChannelId+FROM+MessagingSession+WHERE+MessagingChannel.DeveloperName='{{CHANNEL_NAME}}'+ORDER+BY+CreatedDate+DESC+LIMIT+5" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" ``` #### Browser Agent ```text 1. On an iPhone, iPad, or Mac, open one of the following entry points: - Apple Maps: Search for your business name, tap the Messages icon - Safari: Visit your website with a Business Chat button - Spotlight: Search for your business name, tap the Messages suggestion - Siri: Say "Message [your business name]" 2. Send a test message: "Hello, I need help" 3. Wait 5-10 seconds for the agent to respond 4. Verify you receive a response from the agent in the Messages app 5. Try a second message to confirm multi-turn conversation works ``` > **ℹ️ Info**: Apple Messages for Business only works on Apple devices. You cannot test from Android phones or non-Apple computers. Ensure your test device is running a recent version of iOS, iPadOS, or macOS. **Verify the agent responds:** Expected result: Agent responds to Apple Messages conversation. MessagingSession record created with Status = 'Active' or 'Ended'. ### Verify agent routing and session records Confirm messages are being routed to your agent by checking MessagingSession and AgentWork records. **Choose one of the following methods:** #### SF CLI ```bash # Check recent MessagingSessions for Apple Messages sf data query --target-org {{ORG_ALIAS}} \ --query "SELECT Id, Status, AgentType, OwnerId, CreatedDate FROM MessagingSession WHERE MessagingChannel.MessageType='AppleBusinessChat' ORDER BY CreatedDate DESC LIMIT 5" # Check AgentWork records sf data query --target-org {{ORG_ALIAS}} \ --query "SELECT Id, WorkItemId, BotId, RoutingType, CreatedDate FROM AgentWork ORDER BY CreatedDate DESC LIMIT 5" # Check individual messages in the session sf data query --target-org {{ORG_ALIAS}} \ --query "SELECT Id, MessageText, SenderType, CreatedDate FROM MessagingEvent WHERE MessagingSession.MessagingChannel.DeveloperName='{{CHANNEL_NAME}}' ORDER BY CreatedDate DESC LIMIT 10" ``` #### REST API ```bash curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id,Status,AgentType,OwnerId,CreatedDate+FROM+MessagingSession+WHERE+MessagingChannel.MessageType='AppleBusinessChat'+ORDER+BY+CreatedDate+DESC+LIMIT+5" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id,WorkItemId,BotId,RoutingType,CreatedDate+FROM+AgentWork+ORDER+BY+CreatedDate+DESC+LIMIT+5" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" ``` #### Browser Agent ```text 1. Navigate to the App Launcher > Messaging Sessions 2. Find the most recent session 3. Click to view details 4. Verify: - Owner matches your agent - Message Type shows AppleBusinessChat - Status is Active or Ended 5. Check the Messaging Events related list for individual messages ``` **Verify messaging session records:** Expected result: Recent MessagingSession exists with AgentType populated and messages visible in MessagingEvent records. ## Apple Messages-Specific Capabilities Apple Messages for Business supports rich interactive content beyond plain text: | Capability | Description | | --------------------------- | -------------------------------------------------------------------- | | List Pickers | Present a scrollable list of options for the customer to select from | | Time Pickers | Let customers choose an available appointment time slot | | Apple Pay | Request payments directly within the conversation | | Authentication | Verify customer identity using Apple's authentication framework | | Custom Interactive Messages | Build custom iMessage app extensions for rich UI experiences | | Secure Forms | Collect structured data securely within the conversation | | Images and Attachments | Send and receive images, documents, and other media | > **💡 Tip**: Rich interactive content (list pickers, time pickers, Apple Pay) requires additional configuration via Messaging Components in Salesforce. Start with text-based conversations and add rich content incrementally. ## Troubleshooting ### Messages not reaching agent 1. Verify the MessagingChannel is Active: query `IsActive` field 2. Confirm `SessionHandlerId` is set and points to the correct BotDefinition.Id 3. Check the agent is Active (not Draft) in Agent Builder 4. Verify Omni-Channel is enabled in Setup 5. Check that the `sfdc_livemessage` Service Channel exists (auto-created with Digital Engagement) ### Apple Business Register account not approved 1. Apple reviews every business account manually. Typical approval timelines: - Established brands with verified web presence: 2-5 business days - New or small businesses: 1-3 weeks - Businesses requiring additional verification: up to 4 weeks 2. Ensure your business information on register.apple.com matches your public web presence 3. Verify your D-U-N-S number is correct if Apple requests business verification 4. Contact Apple Business support if approval is delayed beyond expected timelines ### Channel creation fails in Setup wizard 1. Verify the Digital Engagement license is provisioned and active 2. Confirm your Apple Business Register account is approved and shows Active status 3. Ensure Salesforce is selected as the Messaging Service Provider in Apple Business Register 4. Check that your Apple Business Chat Account ID is correct 5. Clear browser cache and retry if the Setup wizard encounters errors ### Customer cannot find your business in Apple Maps or Safari 1. Verify your business location is claimed and verified on Apple Maps Connect (mapsconnect.apple.com) 2. Ensure your Apple Business Register account entry points include Maps and Safari 3. It can take 24-48 hours after channel activation for entry points to appear in Apple services 4. Check that your business name and address match between Apple Maps Connect and Apple Business Register ### Agent not responding but session is created 1. Check `SessionHandlerId` versus `FallbackQueueId` routing. If SessionHandlerId is empty, messages route to the queue instead of the agent 2. Verify the agent's default_agent_user has proper permissions (Einstein Agent User profile, AgentforceServiceAgentUser permission set) 3. Check for errors in the agent's trace logs via Agent Builder preview 4. Confirm the BotDefinition is published and the active BotVersion is correct: ```bash sf data query --target-org {{ORG_ALIAS}} \ --query "SELECT Id, DeveloperName, Status FROM BotVersion WHERE BotDefinition.DeveloperName='{{AGENT_NAME}}' AND Status='Active' LIMIT 1" ``` ## Definition of Done **Definition of Done:** - Apple Messages channel active in Messaging Settings with MessageType = AppleBusinessChat - Customer can message your business from an Apple device (Maps, Safari, Spotlight, or Siri) - Agent responds to the message automatically via iMessage - MessagingSession record created with correct routing to agent - Escalation to human agent queue works when agent transfers ## LINE Setup # LINE Setup (30 min) ## Outcome A working LINE-to-Agentforce pipeline where customers in Japan and other LINE-dominant markets can message your business and receive automated responses from your Service Agent. When complete, you'll have: - A LINE Messaging Channel connected to your Salesforce org via the LINE Messaging API - Omni-Channel routing configured to send inbound messages to your Agentforce agent - A webhook verified between LINE and Salesforce for real-time message delivery - Verified end-to-end message flow from LINE to agent and back > **ℹ️ Info**: LINE channels require the **Digital Engagement** add-on license and a **LINE Official Account** with the **Messaging API** enabled. Unlike Enhanced Chat, no Experience Cloud site or EmbeddedServiceConfig is needed. LINE is the dominant messaging platform in Japan, Taiwan, and Thailand. ## Architecture When a customer sends a LINE message to your Official Account, the message flows through the following chain: *(Architecture diagram — see the live page for the visual)* The key routing field is `sessionHandlerType = AgentforceServiceAgent` on the MessagingChannel, with `sessionHandlerAsa` pointing to the BotDefinition DeveloperName. The `sessionHandlerQueue` field specifies the fallback queue when the agent is unavailable. Key metadata relationships: - `MessagingChannel.MessageType` = `Line` - `ServiceChannel.DeveloperName` = `sfdc_livemessage` - `Queue.SobjectType` = `MessagingSession` - No 24-hour messaging window restriction (unlike WhatsApp and Messenger) Unlike WhatsApp and Messenger, LINE does not require a Meta Business Manager account. The integration uses credentials from the LINE Developers Console (Channel ID, Channel Secret, Channel Access Token) and a webhook URL that Salesforce generates after channel creation. ### Configure Your Variables **Variable Reference** — replace these placeholders in commands below: | Variable | Description | Example | |----------|-------------|---------| | `{{ORG_ALIAS}}` | Salesforce org alias or username | `` | | `{{AGENT_NAME}}` | Service Agent developer name | `Reservation_Agent` | | `{{CHANNEL_NAME}}` | Messaging Channel developer name | `AgentforceChannel` | | `{{INSTANCE_URL}}` | Salesforce instance URL | `https://myorg.my.salesforce.com` | | `{{ACCESS_TOKEN}}` | OAuth access token | `YOUR_ACCESS_TOKEN` | ## Implementation Steps ## 1. Verify Your Starting Point Confirm your agent exists, your org has the required licenses, and you have admin access. ### Confirm your Service Agent exists and is active Before building the routing chain, verify your Agentforce Service Agent is deployed and active in the target org. **Choose one of the following methods:** #### SF CLI ```bash sf data query \ --target-org {{ORG_ALIAS}} \ --query "SELECT Id, DeveloperName, MasterLabel, AgentType FROM BotDefinition WHERE DeveloperName='{{AGENT_NAME}}' AND IsActive=true" ``` #### REST API ```bash curl -X GET "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id,DeveloperName,MasterLabel,AgentType+FROM+BotDefinition+WHERE+DeveloperName='{{AGENT_NAME}}'+AND+IsActive=true" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" \ -H "Content-Type: application/json" ``` #### Browser Agent ```text 1. Navigate to Setup > Agents > Agent Builder 2. Find your agent by name: {{AGENT_NAME}} 3. Verify the status shows Active 4. Copy the Agent ID for later use ``` **Verify the agent is active:** Expected result: One record returned with a valid Id. If no record is returned, activate your agent in Agent Builder before proceeding. ### Authenticate and verify org access Ensure you have admin access to the target org and the CLI is connected. **Choose one of the following methods:** #### SF CLI ```bash sf org login web --alias {{ORG_ALIAS}} --instance-url https://login.salesforce.com sf org display --target-org {{ORG_ALIAS}} --verbose ``` #### REST API ```bash # Use OAuth 2.0 web server flow to obtain access token # Then verify with: curl -X GET "{{INSTANCE_URL}}/services/data/v64.0/limits" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" ``` #### Browser Agent ```text 1. Log into your Salesforce org at https://login.salesforce.com 2. Verify you see Setup in the gear menu (admin access required) 3. Navigate to Setup > Company Information to confirm org details ``` **Verify org access:** Expected result: Org display shows your username and instance URL without errors. ### Verify Digital Engagement license LINE channels require the Digital Engagement add-on. Confirm it is provisioned in your org. **Choose one of the following methods:** #### SF CLI ```bash sf data query \ --target-org {{ORG_ALIAS}} \ --query "SELECT Id, Name, Status, IsProvisioned FROM PackageLicense WHERE NamespacePrefix='sfdc_livemessage'" ``` #### REST API ```bash curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id,Name,Status,IsProvisioned+FROM+PackageLicense+WHERE+NamespacePrefix='sfdc_livemessage'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" ``` #### Browser Agent ```text 1. Navigate to Setup > Company Information > Permission Set Licenses 2. Look for "Digital Engagement" or "Messaging" in the list 3. Confirm it shows as Active with available licenses ``` **Verify Digital Engagement license:** Expected result: One record returned with IsProvisioned = true. If not found, contact your Salesforce account executive to provision Digital Engagement. ## 2. Enable Messaging and Omni-Channel Enable the org-level settings required for LINE messaging. ### Enable Messaging in Setup Messaging must be enabled at the org level before any messaging channels can be created. **Choose one of the following methods:** #### SF CLI ```bash # Deploy Messaging settings via metadata mkdir -p force-app/main/default/settings cat > force-app/main/default/settings/OmniChannel.settings-meta.xml << 'EOF' true EOF sf project deploy start \ --target-org {{ORG_ALIAS}} \ --source-dir force-app/main/default/settings/OmniChannel.settings-meta.xml ``` #### REST API ```bash # Messaging and Omni-Channel settings are typically enabled via # Metadata API or Setup UI. See Browser method. ``` #### Browser Agent ```text 1. Navigate to Setup > Messaging Settings 2. If you see a toggle for Messaging, enable it 3. Navigate to Setup > Omni-Channel Settings 4. Check "Enable Omni-Channel" 5. Click Save ``` **Verify messaging is enabled:** Expected result: Messaging Settings page loads without errors and shows channel management options. Omni-Channel Settings shows "Omni-Channel is enabled." ## 3. Set Up LINE Official Account and Messaging API Create a LINE Official Account and enable the Messaging API to obtain the credentials needed for the Salesforce integration. > **ℹ️ Info**: LINE Official Accounts are free to create. The free tier includes a limited number of messages per month. For higher volume, upgrade to the Light or Standard plan in the LINE Official Account Manager. ### Create a LINE Official Account If you do not already have a LINE Official Account, create one before proceeding. **Choose one of the following methods:** #### SF CLI ```bash # LINE Official Account setup is done in the LINE platform. # No CLI equivalent. See Browser method for complete steps. ``` #### REST API ```bash # LINE Official Account setup is done in the LINE platform. # See Browser method for complete steps. ``` #### Browser Agent ```text 1. Go to https://manager.line.biz and sign in with your LINE account 2. If you do not have a LINE Official Account, click "Create a LINE Official Account" 3. Enter your business name, category, and region 4. Complete the registration 5. Note the account name for reference ``` **Verify LINE account access:** Expected result: You can log into the LINE Official Account Manager and see your account listed. ### Enable the Messaging API and create a provider The Messaging API must be enabled on your LINE Official Account to integrate with Salesforce. This creates a channel in the LINE Developers Console. **Choose one of the following methods:** #### SF CLI ```bash # Messaging API enablement is done in the LINE platform. # No CLI equivalent. See Browser method for complete steps. ``` #### REST API ```bash # Messaging API enablement is done in the LINE platform. # See Browser method for complete steps. ``` #### Browser Agent ```text 1. In the LINE Official Account Manager, go to Settings > Messaging API 2. Click "Enable Messaging API" 3. When prompted, create or select a Provider: a. A Provider is a developer entity (your company name) b. If you do not have one, click "Create a new provider" c. Enter your company or project name 4. Confirm to enable the Messaging API 5. After enabling, note these values from the LINE Developers Console (https://developers.line.biz): a. Navigate to your Provider > your Messaging API channel b. Channel Name: shown at the top of the channel settings c. Channel ID: shown in the Basic Settings tab d. Channel Secret: shown in the Basic Settings tab e. Channel Access Token: go to the Messaging API tab, click "Issue" to generate a long-lived token 6. Save these four values — you will enter them in Salesforce in Step 4 ``` > **⚠️ Warning**: Keep your Channel Secret and Channel Access Token secure. These credentials grant full access to send messages as your LINE Official Account. Do not commit them to version control or share them in public channels. **Verify LINE credentials:** Expected result: You have all four LINE credentials: Channel Name, Channel ID, Channel Secret, and Channel Access Token. ### Disable auto-reply and greeting messages LINE Official Accounts have auto-reply and greeting messages enabled by default. These conflict with Agentforce agent responses and should be disabled. **Choose one of the following methods:** #### SF CLI ```bash # Auto-reply settings are configured in the LINE platform. # No CLI equivalent. See Browser method. ``` #### REST API ```bash # Auto-reply settings are configured in the LINE platform. # See Browser method. ``` #### Browser Agent ```text 1. In the LINE Official Account Manager, go to Settings > Response Settings 2. Set "Auto-response" to Disabled 3. Set "Greeting message" to Disabled 4. These settings prevent LINE from sending its own responses that would conflict with your Agentforce agent's replies 5. Click Save ``` > **💡 Tip**: If you leave auto-reply enabled, customers will receive both the LINE auto-response and the Agentforce agent response, creating a confusing experience. **Verify auto-response disabled:** Expected result: Auto-response and greeting message are both disabled in LINE Official Account Manager > Settings > Response Settings. ## 4. Create the LINE Messaging Channel Use the Salesforce Setup wizard to create the LINE channel by entering the credentials from Step 3. ### Create the channel in Salesforce Unlike WhatsApp and Messenger, the LINE channel does not require an OAuth flow. You enter the LINE credentials directly in the Setup wizard. **Choose one of the following methods:** #### SF CLI ```bash # The LINE channel must be created via the Setup wizard. # After creation, verify with: sf data query \ --target-org {{ORG_ALIAS}} \ --query "SELECT Id, DeveloperName, MasterLabel, MessageType, IsActive FROM MessagingChannel WHERE MessageType='Line'" ``` #### REST API ```bash # LINE channel creation requires the Setup wizard. # After creation, verify with: curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id,DeveloperName,MasterLabel,MessageType,IsActive+FROM+MessagingChannel+WHERE+MessageType='Line'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" ``` #### Browser Agent ```text 1. Navigate to Setup > Messaging Settings 2. Click New Channel 3. Select LINE as the channel type 4. Enter the LINE credentials: a. Channel Name: {{CHANNEL_NAME}} b. Channel ID: (from LINE Developers Console > Basic Settings) c. Channel Secret: (from LINE Developers Console > Basic Settings) d. Channel Access Token: (from LINE Developers Console > Messaging API tab) 5. Choose routing method: - "Create new queue" to have Salesforce create a queue automatically - "Set up routing later" if you want to configure Omni-Channel flow routing 6. Click Finish to create the channel 7. After creation, copy the Webhook URL from the channel record (displayed in the channel details page) ``` **Verify channel was created:** Expected result: Query returns one record with MessageType = 'Line'. Note the Webhook URL from the channel details page -- you will need it in the next step. ### Register the webhook URL in LINE Developers Console Salesforce generates a webhook URL after channel creation. This URL must be registered in LINE so that inbound messages are forwarded to your Salesforce org. **Choose one of the following methods:** #### SF CLI ```bash # Webhook registration is done in the LINE Developers Console. # No CLI equivalent. See Browser method. ``` #### REST API ```bash # Webhook registration is done in the LINE Developers Console. # See Browser method. ``` #### Browser Agent ```text 1. Go to https://developers.line.biz 2. Navigate to your Provider > your Messaging API channel 3. Click the Messaging API tab 4. In the Webhook settings section: a. Paste the Webhook URL copied from Salesforce into the Webhook URL field b. Click Update c. Click Verify to test the connection d. You should see "Success" — this confirms LINE can reach your Salesforce org 5. Enable the "Use webhook" toggle 6. Confirm the toggle is set to On ``` > **⚠️ Warning**: If the webhook verification fails, check that your Salesforce org is accessible from the public internet (not behind a VPN or firewall that blocks LINE's servers). Sandbox orgs with My Domain restrictions may need additional configuration. **Verify webhook configuration:** Expected result: LINE Developers Console shows "Use webhook" toggle enabled and the Verify button returns "Success." ### Complete channel activation in Salesforce After the webhook is verified in LINE, return to Salesforce to complete the channel activation. **Choose one of the following methods:** #### SF CLI ```bash # Verify channel is active sf data query \ --target-org {{ORG_ALIAS}} \ --query "SELECT Id, DeveloperName, MasterLabel, MessageType, IsActive, SessionHandlerId, FallbackQueueId FROM MessagingChannel WHERE DeveloperName='{{CHANNEL_NAME}}'" ``` #### REST API ```bash curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id,DeveloperName,MasterLabel,MessageType,IsActive,SessionHandlerId,FallbackQueueId+FROM+MessagingChannel+WHERE+DeveloperName='{{CHANNEL_NAME}}'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" ``` #### Browser Agent ```text 1. Navigate to Setup > Messaging Settings 2. Click on your LINE channel: {{CHANNEL_NAME}} 3. Complete any items in the Channel Activation Checklist 4. Click Activate Channel 5. Verify the channel status shows Active ``` **Verify channel is active:** Expected result: Channel record returned with MessageType = 'Line' and IsActive = true. ## 5. Configure Routing to Your Agent Set up the routing so inbound LINE messages are handled by your Agentforce Service Agent. ### Create a messaging queue for fallback routing The queue handles messages when the agent is unavailable. Even with direct agent routing, a fallback queue is required. **Choose one of the following methods:** #### SF CLI ```bash # Check if queue exists sf data query --target-org {{ORG_ALIAS}} \ --query "SELECT Id FROM Group WHERE Type='Queue' AND DeveloperName='{{CHANNEL_NAME}}_Queue'" # If not found, create via Metadata API mkdir -p force-app/main/default/queues cat > force-app/main/default/queues/{{CHANNEL_NAME}}_Queue.queue-meta.xml << 'EOF' {{CHANNEL_NAME}}_Queue {{CHANNEL_NAME}} Queue MessagingSession EOF sf project deploy start --target-org {{ORG_ALIAS}} --metadata Queue:{{CHANNEL_NAME}}_Queue ``` #### REST API ```bash # Create queue curl -X POST "{{INSTANCE_URL}}/services/data/v64.0/sobjects/Group" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" \ -H "Content-Type: application/json" \ -d '{ "Name": "{{CHANNEL_NAME}} Queue", "DeveloperName": "{{CHANNEL_NAME}}_Queue", "Type": "Queue" }' # Add MessagingSession to queue QUEUE_ID=$(curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id+FROM+Group+WHERE+DeveloperName='{{CHANNEL_NAME}}_Queue'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" | jq -r '.records[0].Id') curl -X POST "{{INSTANCE_URL}}/services/data/v64.0/sobjects/QueueSobject" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" \ -H "Content-Type: application/json" \ -d "{\"QueueId\": \"$QUEUE_ID\", \"SobjectType\": \"MessagingSession\"}" ``` #### Browser Agent ```text 1. Navigate to Setup > Queues 2. Click New 3. Set Label: {{CHANNEL_NAME}} Queue 4. Set Queue Name: {{CHANNEL_NAME}}_Queue 5. In Supported Objects, add Messaging Session 6. Add queue members (agents who will handle escalations) 7. Click Save ``` **Verify queue creation:** Expected result: One queue record returned with DeveloperName = '{{CHANNEL_NAME}}\_Queue'. ### Route the channel to your Agentforce agent Set the SessionHandlerId on the MessagingChannel to point to your agent's BotDefinition. This enables direct agent routing for all inbound LINE messages. **Choose one of the following methods:** #### SF CLI ```bash # Get the Bot ID and Queue ID BOT_ID=$(sf data query --target-org {{ORG_ALIAS}} --json \ --query "SELECT Id FROM BotDefinition WHERE DeveloperName='{{AGENT_NAME}}'" \ | jq -r '.result.records[0].Id') QUEUE_ID=$(sf data query --target-org {{ORG_ALIAS}} --json \ --query "SELECT Id FROM Group WHERE Type='Queue' AND DeveloperName='{{CHANNEL_NAME}}_Queue'" \ | jq -r '.result.records[0].Id') CHANNEL_ID=$(sf data query --target-org {{ORG_ALIAS}} --json \ --query "SELECT Id FROM MessagingChannel WHERE DeveloperName='{{CHANNEL_NAME}}'" \ | jq -r '.result.records[0].Id') # Update the channel with agent routing sf apex run --target-org {{ORG_ALIAS}} << EOF MessagingChannel channel = [SELECT Id FROM MessagingChannel WHERE DeveloperName = '{{CHANNEL_NAME}}' LIMIT 1]; channel.SessionHandlerId = '$BOT_ID'; channel.FallbackQueueId = '$QUEUE_ID'; update channel; System.debug('Updated MessagingChannel: ' + channel.Id + ' with SessionHandlerId: $BOT_ID'); EOF ``` #### REST API ```bash BOT_ID=$(curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id+FROM+BotDefinition+WHERE+DeveloperName='{{AGENT_NAME}}'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" | jq -r '.records[0].Id') QUEUE_ID=$(curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id+FROM+Group+WHERE+Type='Queue'+AND+DeveloperName='{{CHANNEL_NAME}}_Queue'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" | jq -r '.records[0].Id') CHANNEL_ID=$(curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id+FROM+MessagingChannel+WHERE+DeveloperName='{{CHANNEL_NAME}}'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" | jq -r '.records[0].Id') curl -X PATCH "{{INSTANCE_URL}}/services/data/v64.0/sobjects/MessagingChannel/$CHANNEL_ID" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" \ -H "Content-Type: application/json" \ -d "{ \"SessionHandlerId\": \"$BOT_ID\", \"FallbackQueueId\": \"$QUEUE_ID\" }" ``` #### Browser Agent ```text 1. Navigate to Setup > Messaging Settings 2. Click on your LINE channel: {{CHANNEL_NAME}} 3. Under Routing Configuration: a. Set Routing Type to "Agentforce Service Agent" b. Select your agent: {{AGENT_NAME}} c. Set Fallback Queue: {{CHANNEL_NAME}} Queue 4. Click Save ``` **Verify agent routing:** Expected result: Query the channel and confirm SessionHandlerId matches your BotDefinition.Id. > **💡 Tip**: If `SessionHandlerId` is not available in your org's API version, configure routing through an Omni-Channel inbound flow instead. Create a flow with the Route Work action: Service Channel = `sfdc_livemessage`, Route To = your Agentforce Service Agent. ## 6. Test and Verify Send a test message and confirm the full pipeline works end-to-end. ### Send a test LINE message Use your personal phone to send a message to the LINE Official Account and verify the agent responds. **Choose one of the following methods:** #### SF CLI ```bash # After sending a test message from your phone, query for the session: sf data query --target-org {{ORG_ALIAS}} \ --query "SELECT Id, Status, AgentType, OwnerId, CreatedDate, MessagingChannelId FROM MessagingSession WHERE MessagingChannel.DeveloperName='{{CHANNEL_NAME}}' ORDER BY CreatedDate DESC LIMIT 5" ``` #### REST API ```bash curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id,Status,AgentType,OwnerId,CreatedDate,MessagingChannelId+FROM+MessagingSession+WHERE+MessagingChannel.DeveloperName='{{CHANNEL_NAME}}'+ORDER+BY+CreatedDate+DESC+LIMIT+5" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" ``` #### Browser Agent ```text 1. Open the LINE app on your phone 2. Search for your LINE Official Account by name or scan its QR code 3. Add the account as a friend if not already added 4. Send a test message: "Hello, I need help" 5. Wait 5-10 seconds for the agent to respond 6. Verify you receive a response from the agent 7. Try a second message to confirm multi-turn works ``` **Verify the agent responds:** Expected result: Agent responds to LINE message. MessagingSession record created with Status = 'Active' or 'Ended'. ### Verify agent routing and session records Confirm messages are being routed to your agent by checking MessagingSession and AgentWork records. **Choose one of the following methods:** #### SF CLI ```bash # Check recent MessagingSessions for LINE sf data query --target-org {{ORG_ALIAS}} \ --query "SELECT Id, Status, AgentType, OwnerId, CreatedDate FROM MessagingSession WHERE MessagingChannel.MessageType='Line' ORDER BY CreatedDate DESC LIMIT 5" # Check AgentWork records sf data query --target-org {{ORG_ALIAS}} \ --query "SELECT Id, WorkItemId, BotId, RoutingType, CreatedDate FROM AgentWork ORDER BY CreatedDate DESC LIMIT 5" # Check individual messages in the session sf data query --target-org {{ORG_ALIAS}} \ --query "SELECT Id, MessageText, SenderType, CreatedDate FROM MessagingEvent WHERE MessagingSession.MessagingChannel.DeveloperName='{{CHANNEL_NAME}}' ORDER BY CreatedDate DESC LIMIT 10" ``` #### REST API ```bash curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id,Status,AgentType,OwnerId,CreatedDate+FROM+MessagingSession+WHERE+MessagingChannel.MessageType='Line'+ORDER+BY+CreatedDate+DESC+LIMIT+5" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id,WorkItemId,BotId,RoutingType,CreatedDate+FROM+AgentWork+ORDER+BY+CreatedDate+DESC+LIMIT+5" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" ``` #### Browser Agent ```text 1. Navigate to the App Launcher > Messaging Sessions 2. Find the most recent session 3. Click to view details 4. Verify: - Owner matches your agent - Message Type shows Line - Status is Active or Ended 5. Check the Messaging Events related list for individual messages ``` **Verify messaging session records:** Expected result: Recent MessagingSession exists with AgentType populated and messages visible in MessagingEvent records. ## LINE-Specific Constraints | Constraint | Value | | -------------------------- | ------------------------------------------------------------- | | Messaging window | No restriction (unlike WhatsApp/Messenger 24-hour window) | | Supported inbound content | Text, images, emoji, stickers, links, videos | | Supported outbound content | Text, images, questions with clickable options | | Consent model | Explicit (user must add your account as a friend) | | Primary markets | Japan, Taiwan, Thailand, Indonesia | | Channel migration | Channels can be moved between SF orgs (sandbox to production) | | Routing types | Omni-Flow (supports bots) or Omni-Queue | ## Troubleshooting ### Messages not reaching Salesforce 1. Verify the "Use webhook" toggle is enabled in LINE Developers Console > Messaging API tab 2. Confirm the Webhook URL in LINE matches the URL from your Salesforce channel record 3. Click Verify in LINE Developers Console to test webhook connectivity 4. Check that the MessagingChannel is Active in Setup > Messaging Settings 5. Verify the `sfdc_livemessage` Service Channel exists (auto-created with Digital Engagement) ### Webhook verification fails in LINE Developers Console 1. Ensure your Salesforce org is accessible from the public internet 2. Check that the Webhook URL was copied correctly (no trailing spaces or line breaks) 3. Sandbox orgs behind My Domain restrictions may block LINE's webhook verification requests 4. Wait 1-2 minutes after channel creation before attempting verification (webhook endpoint may take time to provision) ### LINE channel creation fails in Salesforce 1. Verify the Digital Engagement license is provisioned and active 2. Confirm the Channel ID, Channel Secret, and Channel Access Token are correct 3. Re-issue the Channel Access Token in LINE Developers Console if the current one has expired or been revoked 4. Ensure the Messaging API is enabled on your LINE Official Account (not just the standard chat mode) ### Agent not responding but session is created 1. Check `SessionHandlerId` on the MessagingChannel matches the BotDefinition Id 2. Verify the agent's default_agent_user has proper permissions (Einstein Agent User profile, AgentforceServiceAgentUser permission set) 3. Confirm the BotDefinition is published and has an active BotVersion 4. Review agent debug logs in Setup > Agentforce > Agents > [Your Agent] > Logs ### Customers receiving duplicate responses 1. Disable auto-reply messages in LINE Official Account Manager > Settings > Response Settings 2. Disable greeting messages in the same settings page 3. If using a third-party LINE integration alongside Salesforce, only one system should respond to webhooks ### Supported message types LINE supports: text messages, images, stickers, emoji, location sharing, and videos as inbound content. The Agentforce agent can receive all types but responds with text by default. Rich message responses (carousels, quick reply buttons, flex messages) require additional configuration via LINE's message template format. ## Definition of Done **Definition of Done:** - LINE channel active in Messaging Settings with MessageType = Line - Webhook URL registered and verified in LINE Developers Console - "Use webhook" toggle enabled in LINE Developers Console - Auto-reply and greeting messages disabled in LINE Official Account Manager - Customer can send a LINE message and reach the Agentforce agent - Agent responds to the message automatically - MessagingSession record created with correct routing to agent - Escalation to human agent queue works when agent transfers ## Custom Channel Setup (BYOC) # Custom Channel Setup (BYOC) (45 min) ## Outcome A working Bring Your Own Channel (BYOC) pipeline where customers on any external messaging platform can reach your Agentforce Service Agent through a partner-managed integration. When complete, you will have: - A BYOC Messaging Channel connected to your Salesforce org via a managed package and the Interaction Service API - Omni-Channel routing configured to deliver inbound messages to your Agentforce agent - Verified end-to-end message flow from the external messaging provider through to your agent and back - Escalation routing to a human agent queue when the agent hands off > **ℹ️ Info**: BYOC channels require the **Digital Engagement** add-on license and a **managed package** from your messaging or CCaaS provider. Unlike native channels (WhatsApp, SMS, Messenger), BYOC requires developer integration work to translate messages between the external provider and Salesforce. No Experience Cloud site or EmbeddedServiceConfig is needed. > **⚠️ Warning**: Salesforce offers two BYOC flavors. This guide covers **Bring Your Own Channel for Messaging**, which connects an external messaging provider to Salesforce using the Interaction Service API. The second flavor, **Bring Your Own Channel for CCaaS**, connects an external contact center with unified partner-owned routing and supports both messaging and voice. Choose the flavor that matches your provider. ## Architecture When a customer sends a message through the external messaging platform, the message flows through the following chain: *(Architecture diagram — see the live page for the visual)* Key metadata relationships: - `MessagingChannel.MessageType` = `Custom` - `MessagingChannel.SessionHandlerId` = BotDefinition.Id - `sessionHandlerType` = `AgentforceServiceAgent` - `sessionHandlerAsa` = BotDefinition DeveloperName - `ServiceChannel.DeveloperName` = `sfdc_livemessage` - `Queue.SobjectType` = `MessagingSession` The managed package from your messaging provider handles webhook ingestion, message format translation, and delivery status callbacks. Salesforce receives normalized messages through the Interaction Service API, creates MessagingSession records, and routes them to your Agentforce agent via Omni-Channel. ### Configure Your Variables **Variable Reference** — replace these placeholders in commands below: | Variable | Description | Example | |----------|-------------|---------| | `{{ORG_ALIAS}}` | Salesforce org alias or username | `` | | `{{AGENT_NAME}}` | Service Agent developer name | `Reservation_Agent` | | `{{CHANNEL_NAME}}` | Messaging Channel developer name | `AgentforceChannel` | | `{{INSTANCE_URL}}` | Salesforce instance URL | `https://myorg.my.salesforce.com` | | `{{ACCESS_TOKEN}}` | OAuth access token | `YOUR_ACCESS_TOKEN` | ## Implementation Steps ## 1. Verify Your Starting Point Confirm your agent exists, your org has the required licenses, and you have admin access. ### Confirm your Service Agent exists and is active Before building the routing chain, verify your Agentforce Service Agent is deployed and active in the target org. **Choose one of the following methods:** #### SF CLI ```bash sf data query \ --target-org {{ORG_ALIAS}} \ --query "SELECT Id, DeveloperName, MasterLabel, AgentType FROM BotDefinition WHERE DeveloperName='{{AGENT_NAME}}' AND IsActive=true" ``` #### REST API ```bash curl -X GET "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id,DeveloperName,MasterLabel,AgentType+FROM+BotDefinition+WHERE+DeveloperName='{{AGENT_NAME}}'+AND+IsActive=true" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" \ -H "Content-Type: application/json" ``` #### Browser Agent ```text 1. Navigate to Setup > Agents > Agent Builder 2. Find your agent by name: {{AGENT_NAME}} 3. Verify the status shows Active 4. Copy the Agent ID for later use ``` **Verify the agent is active:** Expected result: One record returned with a valid Id. If no record is returned, activate your agent in Agent Builder before proceeding. ### Authenticate and verify org access Ensure you have admin access to the target org and the CLI is connected. **Choose one of the following methods:** #### SF CLI ```bash sf org login web --alias {{ORG_ALIAS}} --instance-url https://login.salesforce.com sf org display --target-org {{ORG_ALIAS}} --verbose ``` #### REST API ```bash # Use OAuth 2.0 web server flow to obtain access token # Then verify with: curl -X GET "{{INSTANCE_URL}}/services/data/v64.0/limits" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" ``` #### Browser Agent ```text 1. Log into your Salesforce org at https://login.salesforce.com 2. Verify you see Setup in the gear menu (admin access required) 3. Navigate to Setup > Company Information to confirm org details ``` **Verify org access:** Expected result: Org display shows your username and instance URL without errors. ### Verify Digital Engagement license BYOC channels require the Digital Engagement add-on. Confirm it is provisioned in your org. **Choose one of the following methods:** #### SF CLI ```bash sf data query \ --target-org {{ORG_ALIAS}} \ --query "SELECT Id, Name, Status, IsProvisioned FROM PackageLicense WHERE NamespacePrefix='sfdc_livemessage'" ``` #### REST API ```bash curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id,Name,Status,IsProvisioned+FROM+PackageLicense+WHERE+NamespacePrefix='sfdc_livemessage'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" ``` #### Browser Agent ```text 1. Navigate to Setup > Company Information > Permission Set Licenses 2. Look for "Digital Engagement" or "Messaging" in the list 3. Confirm it shows as Active with available licenses ``` **Verify Digital Engagement license:** Expected result: One record returned with IsProvisioned = true. If not found, contact your Salesforce account executive to provision Digital Engagement. ## 2. Enable Messaging and Omni-Channel Enable the org-level settings required for BYOC messaging. ### Enable Messaging in Setup Messaging must be enabled at the org level before any messaging channels can be created. **Choose one of the following methods:** #### SF CLI ```bash # Deploy Omni-Channel settings via metadata mkdir -p force-app/main/default/settings cat > force-app/main/default/settings/OmniChannel.settings-meta.xml << 'EOF' true EOF sf project deploy start \ --target-org {{ORG_ALIAS}} \ --source-dir force-app/main/default/settings/OmniChannel.settings-meta.xml ``` #### REST API ```bash # Messaging and Omni-Channel settings are typically enabled via # Metadata API or Setup UI. See Browser method. ``` #### Browser Agent ```text 1. Navigate to Setup > Messaging Settings 2. If you see a toggle for Messaging, enable it 3. Navigate to Setup > Omni-Channel Settings 4. Check "Enable Omni-Channel" 5. Click Save ``` **Verify messaging is enabled:** Expected result: Messaging Settings page loads without errors and shows channel management options. Omni-Channel Settings shows "Omni-Channel is enabled." ### Verify the service channel exists The `sfdc_livemessage` service channel is automatically created when Digital Engagement is enabled. **Choose one of the following methods:** #### SF CLI ```bash sf data query \ --target-org {{ORG_ALIAS}} \ --query "SELECT Id, DeveloperName, MasterLabel FROM ServiceChannel WHERE DeveloperName='sfdc_livemessage' LIMIT 1" ``` #### REST API ```bash curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id,DeveloperName,MasterLabel+FROM+ServiceChannel+WHERE+DeveloperName='sfdc_livemessage'+LIMIT+1" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" ``` #### Browser Agent ```text 1. Navigate to Setup > Service Channels 2. Look for "Messaging" or "sfdc_livemessage" in the list 3. Confirm it exists and is active ``` **Verify the Live Message channel:** Expected result: One record returned with DeveloperName = `sfdc_livemessage`. If not found, Messaging is not enabled. Return to Setup > Messaging Settings and enable it. ## 3. Install the Provider Managed Package Install the managed package from your external messaging or CCaaS provider. This package provides the webhook handlers, message translation logic, and Interaction Service API integration that bridges your external platform to Salesforce. > **ℹ️ Info**: Each messaging provider (Twilio, Vonage, MessageBird, custom platforms, etc.) distributes their own managed package. Contact your provider for the installation URL or find it on Salesforce AppExchange. The managed package may include sample Omni-Channel flows for routing. ### Install the managed package **Choose one of the following methods:** #### SF CLI ```bash # Install the managed package using the installation URL from your provider # Replace the URL with your provider's package installation link sf package install \ --target-org {{ORG_ALIAS}} \ --package "{{PROVIDER_PACKAGE_ID}}" \ --wait 10 # Verify installed packages sf package installed list --target-org {{ORG_ALIAS}} ``` #### REST API ```bash # Managed packages are typically installed via the Setup UI or SF CLI. # After installation, verify with: curl -s "{{INSTANCE_URL}}/services/data/v64.0/tooling/query?q=SELECT+Id,SubscriberPackage.Name,SubscriberPackageVersion.Name+FROM+InstalledSubscriberPackage" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" ``` #### Browser Agent ```text 1. Obtain the package installation URL from your messaging provider 2. Open the URL in your browser while logged into your Salesforce org 3. Select "Install for All Users" (recommended) 4. Click Install 5. Wait for the installation to complete (may take several minutes) 6. Navigate to Setup > Installed Packages to verify ``` **Verify the managed package installation:** Expected result: The provider's managed package appears in Setup > Installed Packages with status "Installed". ### Configure the managed package Follow your provider's configuration guide to connect the managed package to your external messaging platform. This typically involves: **Choose one of the following methods:** #### SF CLI ```bash # Provider configuration is typically UI-driven. # After configuration, verify the Named Credential exists: sf data query \ --target-org {{ORG_ALIAS}} \ --query "SELECT Id, MasterLabel, DeveloperName FROM NamedCredential WHERE DeveloperName LIKE '%{{PROVIDER_NAME}}%'" ``` #### REST API ```bash # Verify Named Credential for provider integration curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id,MasterLabel,DeveloperName+FROM+NamedCredential" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" ``` #### Browser Agent ```text 1. Navigate to Setup > [Provider Name] Settings (added by the managed package) 2. Enter your provider API credentials: - API Key or Client ID from your messaging provider - API Secret or Client Secret - Webhook endpoint URL (your provider will send messages here) 3. Configure the message format mapping if required 4. Test the connection using the provider's built-in test button 5. Save the configuration ``` > **⚠️ Warning**: The managed package configuration varies by provider. Consult your provider's documentation for exact field names, API credential formats, and webhook setup instructions. Common requirements include API keys, webhook URLs, and message format configuration. **Verify provider configuration:** Expected result: Provider configuration shows a connected/healthy status. If the provider offers a test connection button, it succeeds. ## 4. Create the BYOC Messaging Channel Create a Bring Your Own Channel in Salesforce Messaging Settings. This registers the channel and enables message routing through the Interaction Service API. ### Create the channel via Setup **Choose one of the following methods:** #### SF CLI ```bash # The BYOC channel must be created via the Setup wizard. # After creation, verify with: sf data query \ --target-org {{ORG_ALIAS}} \ --query "SELECT Id, DeveloperName, MasterLabel, MessageType, IsActive FROM MessagingChannel WHERE MessageType='Custom'" ``` #### REST API ```bash # BYOC channel creation requires the Setup wizard. # After creation, verify with: curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id,DeveloperName,MasterLabel,MessageType,IsActive+FROM+MessagingChannel+WHERE+MessageType='Custom'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" ``` #### Browser Agent ```text 1. Navigate to Setup > Messaging Settings 2. Click New Channel 3. Select "Bring Your Own Channel" as the channel type 4. Configure the channel: a. Channel Name: {{CHANNEL_NAME}} b. Select your installed messaging provider package c. Configure the Interaction Service API endpoint mapping 5. Review the configuration summary 6. Click Finish to create the channel 7. The channel will appear in Messaging Settings ``` **Verify channel was created:** Expected result: Query returns one record with MessageType = 'Custom' and the DeveloperName matching your channel. ### Verify channel details After creation, confirm the channel is properly configured with the correct message type and platform key. **Choose one of the following methods:** #### SF CLI ```bash sf data query \ --target-org {{ORG_ALIAS}} \ --query "SELECT Id, DeveloperName, MasterLabel, MessageType, IsActive, MessagingPlatformKey, SessionHandlerId, FallbackQueueId FROM MessagingChannel WHERE DeveloperName='{{CHANNEL_NAME}}'" ``` #### REST API ```bash curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id,DeveloperName,MasterLabel,MessageType,IsActive,MessagingPlatformKey,SessionHandlerId,FallbackQueueId+FROM+MessagingChannel+WHERE+DeveloperName='{{CHANNEL_NAME}}'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" ``` #### Browser Agent ```text 1. Navigate to Setup > Messaging Settings 2. Click on your BYOC channel: {{CHANNEL_NAME}} 3. Note the following fields: - Channel Name / DeveloperName - Message Type (should be Custom) - Platform Key - Status (should be Active) ``` **Verify channel is active:** Expected result: Channel record returned with MessageType = 'Custom' and IsActive = true. ## 5. Configure Routing to Your Agent Set up the routing so inbound BYOC messages are handled by your Agentforce Service Agent, with a fallback queue for escalation. ### Create a messaging queue for fallback routing The queue handles messages when the agent is unavailable or escalates to a human. Even with direct agent routing, a fallback queue is required. **Choose one of the following methods:** #### SF CLI ```bash # Check if queue exists sf data query --target-org {{ORG_ALIAS}} \ --query "SELECT Id FROM Group WHERE Type='Queue' AND DeveloperName='{{CHANNEL_NAME}}_Queue'" # If not found, create via Metadata API mkdir -p force-app/main/default/queues cat > force-app/main/default/queues/{{CHANNEL_NAME}}_Queue.queue-meta.xml << 'EOF' {{CHANNEL_NAME}}_Queue {{CHANNEL_NAME}} Queue MessagingSession EOF sf project deploy start --target-org {{ORG_ALIAS}} --metadata Queue:{{CHANNEL_NAME}}_Queue ``` #### REST API ```bash # Create queue curl -X POST "{{INSTANCE_URL}}/services/data/v64.0/sobjects/Group" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" \ -H "Content-Type: application/json" \ -d '{ "Name": "{{CHANNEL_NAME}} Queue", "DeveloperName": "{{CHANNEL_NAME}}_Queue", "Type": "Queue" }' # Add MessagingSession to queue QUEUE_ID=$(curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id+FROM+Group+WHERE+DeveloperName='{{CHANNEL_NAME}}_Queue'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" | jq -r '.records[0].Id') curl -X POST "{{INSTANCE_URL}}/services/data/v64.0/sobjects/QueueSobject" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" \ -H "Content-Type: application/json" \ -d "{\"QueueId\": \"$QUEUE_ID\", \"SobjectType\": \"MessagingSession\"}" ``` #### Browser Agent ```text 1. Navigate to Setup > Queues 2. Click New 3. Set Label: {{CHANNEL_NAME}} Queue 4. Set Queue Name: {{CHANNEL_NAME}}_Queue 5. In Supported Objects, add Messaging Session 6. Add queue members (agents who will handle escalations) 7. Click Save ``` **Verify queue creation:** Expected result: One queue record returned with DeveloperName = '{{CHANNEL_NAME}}\_Queue'. ### Route the channel to your Agentforce agent Set the SessionHandlerId on the MessagingChannel to point to your agent's BotDefinition. This enables direct agent routing for all inbound BYOC messages. **Choose one of the following methods:** #### SF CLI ```bash # Get the Bot ID and Queue ID BOT_ID=$(sf data query --target-org {{ORG_ALIAS}} --json \ --query "SELECT Id FROM BotDefinition WHERE DeveloperName='{{AGENT_NAME}}'" \ | jq -r '.result.records[0].Id') QUEUE_ID=$(sf data query --target-org {{ORG_ALIAS}} --json \ --query "SELECT Id FROM Group WHERE Type='Queue' AND DeveloperName='{{CHANNEL_NAME}}_Queue'" \ | jq -r '.result.records[0].Id') CHANNEL_ID=$(sf data query --target-org {{ORG_ALIAS}} --json \ --query "SELECT Id FROM MessagingChannel WHERE DeveloperName='{{CHANNEL_NAME}}'" \ | jq -r '.result.records[0].Id') # Update the channel with agent routing sf apex run --target-org {{ORG_ALIAS}} << EOF MessagingChannel channel = [SELECT Id FROM MessagingChannel WHERE DeveloperName = '{{CHANNEL_NAME}}' LIMIT 1]; channel.SessionHandlerId = '$BOT_ID'; channel.FallbackQueueId = '$QUEUE_ID'; update channel; System.debug('Updated MessagingChannel: ' + channel.Id + ' with SessionHandlerId: $BOT_ID'); EOF ``` #### REST API ```bash BOT_ID=$(curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id+FROM+BotDefinition+WHERE+DeveloperName='{{AGENT_NAME}}'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" | jq -r '.records[0].Id') QUEUE_ID=$(curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id+FROM+Group+WHERE+Type='Queue'+AND+DeveloperName='{{CHANNEL_NAME}}_Queue'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" | jq -r '.records[0].Id') CHANNEL_ID=$(curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id+FROM+MessagingChannel+WHERE+DeveloperName='{{CHANNEL_NAME}}'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" | jq -r '.records[0].Id') curl -X PATCH "{{INSTANCE_URL}}/services/data/v64.0/sobjects/MessagingChannel/$CHANNEL_ID" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" \ -H "Content-Type: application/json" \ -d "{ \"SessionHandlerId\": \"$BOT_ID\", \"FallbackQueueId\": \"$QUEUE_ID\" }" ``` #### Browser Agent ```text 1. Navigate to Setup > Messaging Settings 2. Click on your BYOC channel: {{CHANNEL_NAME}} 3. Under Routing Configuration: a. Set Routing Type to "Agentforce Service Agent" b. Select your agent: {{AGENT_NAME}} c. Set Fallback Queue: {{CHANNEL_NAME}} Queue 4. Click Save ``` **Verify agent routing:** Expected result: Query the channel and confirm SessionHandlerId matches your BotDefinition.Id. > **💡 Tip**: If `SessionHandlerId` is not available in your org's API version, configure routing through an Omni-Channel inbound flow instead. Create a flow with the Route Work action: Service Channel = `sfdc_livemessage`, Route To = your Agentforce Service Agent. ### Set up Omni-Channel flows for agent integration (optional) For advanced routing, create inbound and outbound Omni-Channel flows. Your provider's managed package may include sample flows you can customize. **Choose one of the following methods:** #### SF CLI ```bash # Check if the provider included sample flows sf data query \ --target-org {{ORG_ALIAS}} \ --query "SELECT Id, MasterLabel, ProcessType FROM FlowDefinition WHERE ProcessType='Flow' AND MasterLabel LIKE '%{{PROVIDER_NAME}}%'" ``` #### REST API ```bash curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id,MasterLabel,ProcessType+FROM+FlowDefinition+WHERE+ProcessType='Flow'" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" ``` #### Browser Agent ```text For inbound routing (messages arriving from customers): 1. Navigate to Setup > Flows 2. Create a new Omni-Channel Flow 3. Add a Route Work element: - Service Channel: sfdc_livemessage - Route To: Your Agentforce Service Agent 4. Activate the flow For outbound routing (escalation to human agents): 1. Create a second Omni-Channel Flow 2. Add routing logic to assign to a human agent queue 3. Configure the escalation message 4. Activate the flow 5. Reference this flow in your agent's connection block ``` > **ℹ️ Info**: Inbound flows handle how messages reach the agent. Outbound flows handle escalation from the agent to human agents. Both are optional when using direct `SessionHandlerId` routing, but required for advanced routing scenarios like skill-based routing or conditional agent assignment. ## 6. Test and Verify Send a test message through your external messaging platform and confirm the full pipeline works end-to-end. > **⚠️ Warning**: Testing BYOC requires the external messaging provider to be connected and operational. Unlike native channels, you cannot test purely within Salesforce. Ensure the managed package is configured and the external provider webhook is active before proceeding. ### Send a test message Use the external messaging platform to send a message and verify the agent responds. **Choose one of the following methods:** #### SF CLI ```bash # After sending a test message from the external platform, query for the session: sf data query --target-org {{ORG_ALIAS}} \ --query "SELECT Id, Status, AgentType, OwnerId, CreatedDate, MessagingChannelId FROM MessagingSession WHERE MessagingChannel.DeveloperName='{{CHANNEL_NAME}}' ORDER BY CreatedDate DESC LIMIT 5" ``` #### REST API ```bash curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id,Status,AgentType,OwnerId,CreatedDate,MessagingChannelId+FROM+MessagingSession+WHERE+MessagingChannel.DeveloperName='{{CHANNEL_NAME}}'+ORDER+BY+CreatedDate+DESC+LIMIT+5" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" ``` #### Browser Agent ```text 1. Open the external messaging platform (your provider's test interface or app) 2. Send a test message: "Hello, I need help" 3. Wait 5-10 seconds for the agent to respond 4. Verify you receive a response from the agent 5. Try a second message to confirm multi-turn works ``` **Verify the agent responds:** Expected result: Agent responds to the message. MessagingSession record created with Status = 'Active' or 'Ended'. ### Verify agent routing and session records Confirm messages are being routed to your agent by checking MessagingSession and AgentWork records. **Choose one of the following methods:** #### SF CLI ```bash # Check recent MessagingSessions for the BYOC channel sf data query --target-org {{ORG_ALIAS}} \ --query "SELECT Id, Status, AgentType, OwnerId, CreatedDate FROM MessagingSession WHERE MessagingChannel.MessageType='Custom' ORDER BY CreatedDate DESC LIMIT 5" # Check AgentWork records sf data query --target-org {{ORG_ALIAS}} \ --query "SELECT Id, WorkItemId, BotId, RoutingType, CreatedDate FROM AgentWork ORDER BY CreatedDate DESC LIMIT 5" # Check individual messages in the session sf data query --target-org {{ORG_ALIAS}} \ --query "SELECT Id, MessageText, SenderType, CreatedDate FROM MessagingEvent WHERE MessagingSession.MessagingChannel.DeveloperName='{{CHANNEL_NAME}}' ORDER BY CreatedDate DESC LIMIT 10" ``` #### REST API ```bash curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id,Status,AgentType,OwnerId,CreatedDate+FROM+MessagingSession+WHERE+MessagingChannel.MessageType='Custom'+ORDER+BY+CreatedDate+DESC+LIMIT+5" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id,WorkItemId,BotId,RoutingType,CreatedDate+FROM+AgentWork+ORDER+BY+CreatedDate+DESC+LIMIT+5" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" ``` #### Browser Agent ```text 1. Navigate to the App Launcher > Messaging Sessions 2. Find the most recent session 3. Click to view details 4. Verify: - Owner matches your agent - Message Type shows Custom - Status is Active or Ended 5. Check the Messaging Events related list for individual messages ``` **Verify messaging session records:** Expected result: Recent MessagingSession exists with AgentType populated and messages visible in MessagingEvent records. ### Test escalation to human agent Verify that when the agent escalates, the conversation routes to your fallback queue. **Choose one of the following methods:** #### SF CLI ```bash # Send a message that triggers escalation (e.g., "I want to speak to a human") # Then verify the session was transferred: sf data query --target-org {{ORG_ALIAS}} \ --query "SELECT Id, Status, OwnerId, Owner.Name, AgentType FROM MessagingSession WHERE MessagingChannel.DeveloperName='{{CHANNEL_NAME}}' ORDER BY CreatedDate DESC LIMIT 1" ``` #### REST API ```bash curl -s "{{INSTANCE_URL}}/services/data/v64.0/query?q=SELECT+Id,Status,OwnerId,AgentType+FROM+MessagingSession+WHERE+MessagingChannel.DeveloperName='{{CHANNEL_NAME}}'+ORDER+BY+CreatedDate+DESC+LIMIT+1" \ -H "Authorization: Bearer {{ACCESS_TOKEN}}" ``` #### Browser Agent ```text 1. In the external messaging platform, type: "I want to speak to a human" 2. The agent should acknowledge the escalation request 3. Navigate to Setup > Omni-Channel Supervisor (or the Service Console) 4. Verify the messaging session appears in the queue: {{CHANNEL_NAME}} Queue 5. A human agent can accept and continue the conversation ``` **Verify escalation routing:** Expected result: After escalation, the MessagingSession owner changes from the BotDefinition to the fallback queue or a human agent. ## BYOC-Specific Considerations | Consideration | Details | | ---------------------- | ------------------------------------------------------------------------------------------------------ | | Setup complexity | Higher than native channels. Requires managed package installation and provider-specific configuration | | Custom development | Provider must implement the Interaction Service API for message ingestion and delivery | | Message format | Messages are normalized by the managed package before reaching Salesforce | | Progress indicators | Supported. Agent can show "Doing research", "Analyzing" etc. to the customer | | Interactive components | Supported if the external platform and managed package support rich message types | | Session handling | Managed by Salesforce MessagingSession. Session lifecycle depends on provider configuration | | Consent model | Depends on the external platform. Configure consent handling in the managed package | ## Troubleshooting ### Messages not reaching Salesforce 1. Verify the managed package is installed and configured correctly 2. Check the external provider's webhook status. The webhook must be active and pointing to your Salesforce instance 3. Confirm the MessagingChannel is Active: query `IsActive` field 4. Review the managed package logs (if available) for message delivery errors 5. Verify the Interaction Service API endpoint is accessible from the provider ### Channel creation fails in Setup 1. Verify the Digital Engagement license is provisioned and active 2. Confirm the managed package from your provider is installed 3. Check that the managed package version is compatible with your org's API version 4. Clear browser cache and retry if the Setup wizard stalls ### Agent not responding but session is created 1. Verify `SessionHandlerId` is set and points to the correct BotDefinition.Id 2. Check the agent is Active (not Draft) in Agent Builder 3. Verify the agent's `default_agent_user` has proper permissions (Einstein Agent User profile, AgentforceServiceAgentUser permission set) 4. Check for errors in the agent's trace logs via Agent Builder preview 5. Verify the BotVersion is published and active: ```bash sf data query --target-org {{ORG_ALIAS}} \ --query "SELECT Id, DeveloperName, Status FROM BotVersion WHERE BotDefinition.DeveloperName='{{AGENT_NAME}}' AND Status='Active' LIMIT 1" ``` ### Messages arriving but responses not reaching the external platform 1. The managed package outbound webhook may be misconfigured. Check the provider's delivery status dashboard 2. Verify the Named Credential for the external provider is valid and not expired 3. Check for API rate limits on the external provider side 4. Review Salesforce debug logs for errors in the managed package's outbound message handling ### Interaction Service API errors 1. Check the API version. The Interaction Service API requires a minimum Salesforce API version 2. Verify the Connected App used by the managed package has the correct OAuth scopes 3. Review the provider's developer documentation for known issues and required API permissions 4. Test the Interaction Service API endpoint directly using the BYOC for Messaging Developer Guide ### Provider managed package not appearing in channel creation 1. Confirm the package installation completed successfully (Setup > Installed Packages) 2. Some packages require post-installation configuration steps before they register as a channel provider 3. Check for package version updates that may be required for BYOC compatibility 4. Contact the provider's support for channel registration issues ## Developer Resources For custom BYOC implementations: - **BYOC for Messaging Developer Guide** -- Building custom integrations using the Interaction Service API - **BYOC for CCaaS Developer Guide** -- Connecting external contact centers with unified routing - **Interaction Service API Developer Guide** -- API reference for message ingestion and delivery ## Definition of Done **Definition of Done:** - BYOC channel active in Messaging Settings with MessageType = Custom - Managed package from messaging provider installed and configured - Customer can send a message through the external platform and reach the Agentforce agent - Agent responds to the message automatically - MessagingSession record created with correct routing to agent - Escalation to human agent fallback queue works when agent transfers - Responses from the agent are delivered back through the external messaging platform --- # Labs ## Feedback # Feedback This page is a placeholder for Labs feedback docs. ## Experiments # Experiments Early-access tools and integrations from the Agentforce product and engineering teams. ## Agentforce Grid Skills + MCP # Agentforce Grid Skills + MCP Equip Claude Code to manage Agentforce Grid workbooks directly. Skills teach Claude how Grid works. The MCP server gives it the tools to act — create workbooks and worksheets, configure every column type, run agent tests and evaluations, and read results back. [Skills Repo]() [MCP Repo]() *(Open Grid: open `/lightning/n/standard-WorkbenchHome` in your Salesforce org)* ![Agentforce Grid](/images/products/agentforce-grid.png) ## Quick Install One command installs everything — Salesforce CLI, the Grid MCP server, and Grid skills: ```bash curl -sSL https://raw.githubusercontent.com/chintanavs/agentforce-grid-ai-skills/main/install.sh | bash ``` ### Options ```bash # Target a specific org curl -sSL https://raw.githubusercontent.com/chintanavs/agentforce-grid-ai-skills/main/install.sh | bash -s -- --org my-org-alias # Also install into a project directory curl -sSL https://raw.githubusercontent.com/chintanavs/agentforce-grid-ai-skills/main/install.sh | bash -s -- --project-dir ~/my-project # Skip components you already have curl -sSL https://raw.githubusercontent.com/chintanavs/agentforce-grid-ai-skills/main/install.sh | bash -s -- --skip-sf # already have sf CLI curl -sSL https://raw.githubusercontent.com/chintanavs/agentforce-grid-ai-skills/main/install.sh | bash -s -- --skip-mcp # only want skills curl -sSL https://raw.githubusercontent.com/chintanavs/agentforce-grid-ai-skills/main/install.sh | bash -s -- --skip-skills # only want MCP ``` ## Authenticate Your Org Copy the command below and run it in your terminal to authenticate the Salesforce CLI with your org: **Authenticate SF CLI**: Log in on this page to get your pre-filled authentication command. ```bash echo "" | sf org login sfdx-url --sfdx-url-stdin --set-default --alias afdx-lab ``` Or authenticate manually: ```bash sf org login web --set-default --instance-url https://your-instance.salesforce.com/ ``` Run this command: ```bash sf org display user ``` **Expected output:** Your org username and alias ## Verify Open Claude Code and ask: ```text List my Grid workbooks ``` If everything is set up, Claude queries your org and returns results. ## Manual Installation ### 1. Install Skills Install as a Claude Code plugin (recommended): ``` /plugin install https://github.com/chintanavs/agentforce-grid-ai-skills.git ``` Or clone and copy into your project: ```bash git clone https://github.com/chintanavs/agentforce-grid-ai-skills.git && cp -r agentforce-grid-ai-skills/.claude-plugin/skills/agentforce-grid your-project/.claude/skills/ ``` This gives Claude context on column configuration (every column type), Grid Connect endpoints, common patterns for agent testing and data enrichment, and evaluation types. ### 2. Set Up MCP Server The tools Claude uses to create, modify, and monitor workbooks. Install the Salesforce CLI and authenticate: ```bash brew install sf && sf org login web --set-default --instance-url https://your-instance.salesforce.com/ ``` Clone and build: ```bash git clone https://github.com/chintanavs/agentforce-grid-mcp.git && cd agentforce-grid-mcp && npm install && npm run build ``` Add to your MCP config: ```json { "mcpServers": { "grid-connect": { "command": "node", "args": ["/path/to/agentforce-grid-mcp/dist/index.js"] } } } ``` To target a specific org: ```json { "mcpServers": { "grid-connect": { "command": "node", "args": ["/path/to/agentforce-grid-mcp/dist/index.js"], "env": { "ORG_ALIAS": "orgfarm-org" } } } } ``` ## Goal-Based Agent # Goal-Based Agent (10 min) Autonomous AI agent runtime engine built natively on Salesforce. Enables goal-driven, multi-step workflows that run independently — executing tasks, calling tools, managing memory, and adapting over time. ## What You Get - **Goal-driven workflows** — define a goal, the agent plans and executes multi-step tasks autonomously - **Sequential or parallel execution** — subtasks run sequentially by default, with opt-in parallel mode for independent work - **Workflow defaults** — configure runtime defaults from the Settings > Defaults tab, automatically applied to chat-initiated workflows - **28 built-in tools** — SOQL, DML, memory, scheduling, email, Flows, prompt templates, Slack, and more - **REST API** — full external integration at `/services/apexrest/genui/agent/*` - **Persistent memory** — save, query, update, and promote memories across workflows - **Sub-agent orchestration** — spawn parallel child workflows and create reusable skills - **Scheduling** — cron-style recurring workflows and future follow-ups - **Optional Slack integration** — read channels and send notifications ## Prerequisites Before installing (automated or manual), your org needs: - **Einstein AI enabled** — Go to Setup > Einstein Setup and turn on Einstein and Beta Generative AI Models - **System Administrator profile** — required for package installation - **Lightning Experience** — must be enabled ## Manual Installation (Fallback) If automated installation is unavailable or fails, follow these steps: ### Step 1: Install the Package ``` https://login.salesforce.com/packaging/installPackage.apexp?p0=04tal000006hMrRAAU ``` Choose **Install for All Users** (recommended). This grants all users access to the Goal Agent app and custom objects without needing to assign permission sets individually. ### Step 2: Assign Permission Set (if needed) If you chose "Install for Admins Only", assign the **Goal Agent User** permission set to every user who needs access: 1. Go to Setup > Permission Sets 2. Find **Goal Agent User** 3. Click Manage Assignments > Add Assignment 4. Select the users and save This grants access to all custom objects (Goals, Workflows, Tasks, Memory, Skills, etc.) and the Goal Agent Lightning app. ## REST API The package exposes a full REST API for external integrations, automation, and programmatic workflow management. > **ℹ️ Base URL**: All endpoints are relative to: `{instance_url}/services/apexrest/genui/agent` ### Authentication All requests require a valid Salesforce access token passed via the `Authorization: Bearer {access_token}` header. **Choose one of the following methods:** #### Salesforce CLI ```bash ACCESS_TOKEN=$(sf org display --target-org myOrg --json | jq -r '.result.accessToken') INSTANCE_URL=$(sf org display --target-org myOrg --json | jq -r '.result.instanceUrl') ``` #### OAuth 2.0 ```bash curl -X POST "https://login.salesforce.com/services/oauth2/token" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=password" \ -d "client_id=YOUR_CLIENT_ID" \ -d "client_secret=YOUR_CLIENT_SECRET" \ -d "username=YOUR_USERNAME" \ -d "password=YOUR_PASSWORD_AND_SECURITY_TOKEN" ``` > **⚠️ Managed Package Namespace**: In managed package orgs, field names in GET responses include the `genui__` namespace prefix (e.g., `genui__Title__c` instead of `Title__c`). POST request bodies use parameter names **without** the namespace prefix. ### Endpoints Overview | Method | Path | Description | |--------|------|-------------| | **POST** | `/goals` | Create a new goal | | **POST** | `/workflows` | Create a new workflow | | **POST** | `/workflows/{id}/start` | Start a workflow | | **POST** | `/workflows/{id}/message` | Send a message (human-in-the-loop) | | **POST** | `/workflows/{id}/pause` | Pause a running workflow | | **POST** | `/workflows/{id}/resume` | Resume a paused workflow | | **POST** | `/workflows/{id}/cancel` | Cancel a workflow | | **GET** | `/goals` | List goals | | **GET** | `/workflows` | List workflows | | **GET** | `/workflows/{id}/status` | Get workflow status with tasks and steps | | **GET** | `/workflows/{id}/messages` | Get workflow messages | | **GET** | `/memories` | Get memories | ### Endpoints Reference Create a goal that serves as the parent container for one or more workflows. **Parameters** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `title` | String | Yes | Goal title | | `description` | String | No | Goal description | | `completionCriteria` | String | No | Success criteria for the goal | | `priority` | String | No | `High`, `Medium` (default), `Low` | **Request** ```bash curl -X POST "$INSTANCE_URL/services/apexrest/genui/agent/goals" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "title": "Q1 Pipeline Review", "description": "Review and analyze open opportunities", "completionCriteria": "All opps reviewed with next steps", "priority": "High" }' ``` **Response** ```json { "id": "a2sXXXXXXXXXXXXXXX", "title": "Q1 Pipeline Review", "status": "Draft" } ``` Create a workflow under an existing goal. Optionally include `instructions` to auto-create an initial task. **Parameters** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `goalId` | Id | Yes | Parent goal ID | | `title` | String | Yes | Workflow title | | `workflowType` | String | Yes | Workflow type, e.g. `"Chat"` | | `instructions` | String | No | Initial task instructions (auto-creates a task) | | `triggerType` | String | No | Default: `"Manual"` | | `maxIterations` | Integer | No | Max agent iterations. Default: `50` | | `config` | Object | No | Runtime config JSON, e.g. `{"parallelSubtasks": true}` | **Request** ```bash curl -X POST "$INSTANCE_URL/services/apexrest/genui/agent/workflows" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "goalId": "a2sXXXXXXXXXXXXXXX", "title": "Analyze Pipeline", "workflowType": "Chat", "instructions": "Analyze my open opportunities and summarize the pipeline", "maxIterations": 25 }' ``` **Response** ```json { "id": "a32XXXXXXXXXXXXXXX", "title": "Analyze Pipeline", "status": "Queued" } ``` Begin executing a queued workflow. The agent starts processing tasks autonomously. **Parameters:** None. Send an empty JSON body `{}`. **Request** ```bash curl -X POST "$INSTANCE_URL/services/apexrest/genui/agent/workflows/a32XXXXXXXXXXXXXXX/start" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{}' ``` **Response** ```json { "workflowId": "a32XXXXXXXXXXXXXXX", "status": "Running" } ``` Send a human-in-the-loop message to a running workflow. Use this when the workflow status is `WaitingForInput`. **Parameters** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `message` | String | Yes | User message text | **Request** ```bash curl -X POST "$INSTANCE_URL/services/apexrest/genui/agent/workflows/a32XXXXXXXXXXXXXXX/message" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"message": "Focus on deals over $100k"}' ``` **Response** ```json { "workflowId": "a32XXXXXXXXXXXXXXX", "status": "Running" } ``` Control a running workflow's lifecycle. All three endpoints accept an empty JSON body `{}`. **Pause** — temporarily halt execution: ```bash curl -X POST "$INSTANCE_URL/services/apexrest/genui/agent/workflows/a32XXXXXXXXXXXXXXX/pause" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{}' ``` **Resume** — continue a paused workflow: ```bash curl -X POST "$INSTANCE_URL/services/apexrest/genui/agent/workflows/a32XXXXXXXXXXXXXXX/resume" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{}' ``` **Cancel** — permanently stop a workflow: ```bash curl -X POST "$INSTANCE_URL/services/apexrest/genui/agent/workflows/a32XXXXXXXXXXXXXXX/cancel" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{}' ``` Retrieve all goals in the org. **Request** ```bash curl "$INSTANCE_URL/services/apexrest/genui/agent/goals" \ -H "Authorization: Bearer $ACCESS_TOKEN" ``` **Response** ```json [ { "id": "a2sXXXXXXXXXXXXXXX", "title": "Q1 Pipeline Review", "status": "In Progress", "priority": "High" } ] ``` Retrieve all workflows in the org. **Request** ```bash curl "$INSTANCE_URL/services/apexrest/genui/agent/workflows" \ -H "Authorization: Bearer $ACCESS_TOKEN" ``` **Response** ```json [ { "id": "a32XXXXXXXXXXXXXXX", "title": "Analyze Pipeline", "status": "Running", "workflowType": "Chat" } ] ``` Get detailed workflow status including tasks and steps. **Request** ```bash curl "$INSTANCE_URL/services/apexrest/genui/agent/workflows/a32XXXXXXXXXXXXXXX/status" \ -H "Authorization: Bearer $ACCESS_TOKEN" ``` **Response** ```json { "id": "a32XXXXXXXXXXXXXXX", "title": "Analyze Pipeline", "status": "Running", "tasks": [ { "id": "a33XXXXXXXXXXXXXXX", "title": "Analyze open opportunities", "status": "In Progress", "steps": [ { "toolName": "soql_query", "status": "Completed" } ] } ] } ``` Retrieve the message history for a workflow conversation. **Request** ```bash curl "$INSTANCE_URL/services/apexrest/genui/agent/workflows/a32XXXXXXXXXXXXXXX/messages" \ -H "Authorization: Bearer $ACCESS_TOKEN" ``` **Response** ```json [ { "role": "user", "content": "Focus on deals over $100k", "timestamp": "2025-01-15T10:30:00.000Z" }, { "role": "assistant", "content": "I found 12 opportunities over $100k. Here's the breakdown...", "timestamp": "2025-01-15T10:30:05.000Z" } ] ``` Retrieve the agent's persistent memories. **Request** ```bash curl "$INSTANCE_URL/services/apexrest/genui/agent/memories" \ -H "Authorization: Bearer $ACCESS_TOKEN" ``` **Response** ```json [ { "id": "a34XXXXXXXXXXXXXXX", "content": "User prefers weekly pipeline summaries on Mondays", "category": "preference", "scope": "core" } ] ``` ### Full Example A complete walkthrough: authenticate, create a goal, create and start a workflow, interact with it, and manage its lifecycle. **Choose one of the following methods:** #### curl ```bash # Authenticate ACCESS_TOKEN=$(sf org display --target-org myOrg --json | jq -r '.result.accessToken') INSTANCE_URL=$(sf org display --target-org myOrg --json | jq -r '.result.instanceUrl') # 1. Create a goal curl -X POST "$INSTANCE_URL/services/apexrest/genui/agent/goals" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "title": "Q1 Pipeline Review", "description": "Review and analyze open opportunities", "completionCriteria": "All opps reviewed with next steps", "priority": "High" }' # => {"id": "a2s...", "title": "Q1 Pipeline Review", "status": "Draft"} # 2. Create a workflow curl -X POST "$INSTANCE_URL/services/apexrest/genui/agent/workflows" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "goalId": "a2sXXXXXXXXXXXXXXX", "title": "Analyze Pipeline", "workflowType": "Chat", "instructions": "Analyze my open opportunities and summarize the pipeline", "maxIterations": 25 }' # => {"id": "a32...", "title": "Analyze Pipeline", "status": "Queued"} # 3. Start the workflow curl -X POST "$INSTANCE_URL/services/apexrest/genui/agent/workflows/a32XXXXXXXXXXXXXXX/start" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{}' # => {"workflowId": "a32...", "status": "Running"} # 4. Check status curl "$INSTANCE_URL/services/apexrest/genui/agent/workflows/a32XXXXXXXXXXXXXXX/status" \ -H "Authorization: Bearer $ACCESS_TOKEN" # 5. Send a message (when workflow is WaitingForInput) curl -X POST "$INSTANCE_URL/services/apexrest/genui/agent/workflows/a32XXXXXXXXXXXXXXX/message" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"message": "Focus on deals over $100k"}' # 6. Lifecycle control curl -X POST "$INSTANCE_URL/services/apexrest/genui/agent/workflows/a32XXXXXXXXXXXXXXX/pause" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" -d '{}' curl -X POST "$INSTANCE_URL/services/apexrest/genui/agent/workflows/a32XXXXXXXXXXXXXXX/resume" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" -d '{}' curl -X POST "$INSTANCE_URL/services/apexrest/genui/agent/workflows/a32XXXXXXXXXXXXXXX/cancel" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" -d '{}' ``` #### Python ```python import requests import subprocess import json # Authenticate via SF CLI result = subprocess.run( ["sf", "org", "display", "--target-org", "myOrg", "--json"], capture_output=True, text=True ) org = json.loads(result.stdout)["result"] access_token = org["accessToken"] instance_url = org["instanceUrl"] base = f"{instance_url}/services/apexrest/genui/agent" headers = { "Authorization": f"Bearer {access_token}", "Content-Type": "application/json", } # 1. Create a goal goal = requests.post(f"{base}/goals", headers=headers, json={ "title": "Q1 Pipeline Review", "description": "Review and analyze open opportunities", "completionCriteria": "All opps reviewed with next steps", "priority": "High", }).json() goal_id = goal["id"] # 2. Create a workflow workflow = requests.post(f"{base}/workflows", headers=headers, json={ "goalId": goal_id, "title": "Analyze Pipeline", "workflowType": "Chat", "instructions": "Analyze my open opportunities and summarize the pipeline", "maxIterations": 25, }).json() wf_id = workflow["id"] # 3. Start the workflow requests.post(f"{base}/workflows/{wf_id}/start", headers=headers, json={}) # 4. Check status status = requests.get(f"{base}/workflows/{wf_id}/status", headers=headers).json() print(status) # 5. Send a message (when workflow is WaitingForInput) requests.post(f"{base}/workflows/{wf_id}/message", headers=headers, json={ "message": "Focus on deals over $100k" }) # 6. Lifecycle control requests.post(f"{base}/workflows/{wf_id}/pause", headers=headers, json={}) requests.post(f"{base}/workflows/{wf_id}/resume", headers=headers, json={}) requests.post(f"{base}/workflows/{wf_id}/cancel", headers=headers, json={}) ``` ## Built-in Tools The agent comes with 28 built-in tools. Enable or disable them from **Settings > Tools** in the Goal Agent app. ### Data & Metadata | Tool | Description | |------|-------------| | `soql_query` | Execute SOQL queries | | `dml_operation` | Create, update, delete Salesforce records | | `describe_object` | Get object/field metadata | | `report_tool` | Run Salesforce Reports | ### Agents & Automation | Tool | Description | |------|-------------| | `invoke_agent` | Invoke an Agentforce agent | | `list_agents` | List available Agentforce agents | | `apex_action` | Invoke Apex invocable actions | | `list_apex_actions` | List available Apex invocable actions | | `flow_invoke` | Run Salesforce Flows | | `list_flows` | List available Salesforce Flows | ### Prompt Templates | Tool | Description | |------|-------------| | `list_prompt_templates` | List available prompt templates | | `describe_prompt_template` | Get prompt template details and inputs | | `invoke_prompt_template` | Execute a prompt template | ### Memory | Tool | Description | |------|-------------| | `save_memory` | Save information to persistent memory | | `query_memory` | Search memories by keyword or category | | `update_memory` | Update an existing memory | | `promote_memory` | Promote workflow memory to core memory | ### Orchestration | Tool | Description | |------|-------------| | `create_task` | Create sub-tasks within a workflow | | `create_workflow` | Create child workflows | | `spawn_subtasks` | Create parallel sub-agent workflows | | `create_skill` | Create reusable skills (custom tools) | ### Scheduling | Tool | Description | |------|-------------| | `schedule_followup` | Schedule a future follow-up | | `schedule_workflow` | Schedule a recurring workflow (cron) | | `list_schedules` | List scheduled entries | | `cancel_schedule` | Cancel a scheduled entry | | `slack_notify` | Send Slack notifications (requires setup) | | `slack_read` | Read Slack channel messages (requires setup) | `rest_callout` (HTTP callouts to external APIs) is also available but disabled by default. It requires Remote Site Settings to be configured for target domains. ### Communication | Tool | Description | |------|-------------| | `send_email` | Send emails | | `slack_notify` | Send Slack notifications (requires setup) | | `slack_read` | Read Slack channel messages (requires setup) | > **ℹ️ External API Callouts**: `rest_callout` (HTTP callouts to external APIs) is also available but disabled by default. It requires Remote Site Settings to be configured for target domains. ## Optional: Slack Integration The agent includes two Slack tools — `slack_notify` and `slack_read` — both disabled by default. ### A. Create a Slack App 1. Go to https://api.slack.com/apps > Create New App > From scratch 2. Enter an app name (e.g., "Salesforce Agent") and select your workspace 3. Click Create App ### B. Configure Bot Token Scopes (for slack_read) 1. In your Slack app settings, go to **OAuth & Permissions** 2. Under Scopes > Bot Token Scopes, add: - `channels:history` — Read messages from public channels - `channels:read` — View basic channel information 3. Click **Install to Workspace** (or Reinstall if already installed) 4. Copy the Bot User OAuth Token (starts with `xoxb-`) ### C. Configure Incoming Webhooks (for slack_notify) 1. In your Slack app settings, go to **Incoming Webhooks** 2. Toggle Activate Incoming Webhooks to **On** 3. Click **Add New Webhook to Workspace** 4. Select the channel where notifications should post 5. Copy the Webhook URL ### D. Add Remote Site Settings in Salesforce Go to Setup > Remote Site Settings and create two entries: | Remote Site Name | URL | Active | |------------------|-----|--------| | SlackAPI | `https://slack.com` | Yes | | SlackWebhooks | `https://hooks.slack.com` | Yes | ### E. Store Tokens in Custom Metadata Go to Setup > Custom Metadata Types > **Agent Config** > Manage Records and create: **For slack_read:** | Field | Value | |-------|-------| | Label | Slack Bot Token | | Agent Config Name | SlackBotToken | | Value | `xoxb-your-bot-token-here` | | Is Active | Checked | **For slack_notify:** | Field | Value | |-------|-------| | Label | Slack Webhook URL | | Agent Config Name | SlackWebhookUrl | | Value | `https://hooks.slack.com/services/T.../B.../xxx` | | Is Active | Checked | ### F. Enable the Slack Tools 1. Open the Goal Agent app 2. Go to Settings > Tools tab 3. Find `slack_read` and `slack_notify` in the tools list 4. Toggle them **On** ### G. Invite the Bot to Channels In Slack, go to each channel you want the agent to read from and type: ``` /invite @YourBotName ``` The bot can only read messages from channels it has been invited to. ## Troubleshooting ### Permission errors - Ensure the **Goal Agent User** permission set is assigned (or package was installed for All Users) - Check that the user's profile has API access enabled ### Slack tools not working - Verify Remote Site Settings are active for `https://slack.com` and `https://hooks.slack.com` - Confirm `SlackBotToken` and `SlackWebhookUrl` metadata records exist and are active - Ensure the bot has been invited to the target channel - Check that the tools are enabled in Settings > Tools ### Custom metadata fields not visible in Setup UI - If fields don't appear when creating `AgentConfig__mdt` records, check that the page layout includes the custom fields - Go to Setup > Custom Metadata Types > Agent Config > Page Layouts and add all fields ### Real-time updates not appearing in chat - Platform Events require the user to have the Goal Agent User permission set - Check browser console for EMP API subscription errors - Try refreshing the page ### Agent workflow stuck - Go to Settings and check the workflow status - Use Cancel to stop a stuck workflow, then create a new one - Check Setup > Apex Jobs for any failed Queueable jobs ## Community # Community Join our Slack community to connect with fellow Agentblazers experimenting in Agentforce Labs, share what you're building, and discover new things to build. Join the conversation in our developer Slack community. ## Agentforce Arc # Agentforce Arc Agentforce Arc is an experiment in **agents building agents**. It automates plan generation for agent development, including sub-agent orchestration, logic, guardrails, and smoke tests directly in your Salesforce environment. Generate complete implementation plans that include architecture, actions, and validation flows. Coordinate specialized sub-agents for authoring, testing, and refinement workflows. Attach deterministic guardrails and smoke-test coverage before rollout. ## How It Works ## Setting up nanoRag # Getting Started with nanoRag nanoRag is a Salesforce CLI plugin that builds BM25 document libraries and attaches them to your Agentforce agent. The library and index live in your org as `ContentDocument` and `StaticResource` records — no embeddings, no vector database, no Data Cloud. > **ℹ️ Info**: **Want to try it without installing anything?** [Try in your org →](/nanorag) ## 1. Install the Salesforce CLI #### Homebrew ```bash brew install sf ``` #### npm ```bash npm install -g @salesforce/cli ``` Requires Node.js 18+. **Verify the installation:** Run this command: ```bash sf --version ``` **Expected output:** `@salesforce/cli/` ## 2. Install the nanoRag plugin Requires Python 3.10+. ```bash git clone https://github.com/ankita13makker/nanoRAG.git cd nanoRAG sf plugins link plugins/sf-nanorag ``` The plugin creates its own virtual environment and installs Python dependencies on link. **Verify the installation:** Run this command: ```bash sf nanorag --help ``` **Expected output:** A list of `nanorag` subcommands (`install`, `build`, `attach`, …) ## 3. Authenticate Your Org The org needs Agentforce enabled and a NextGen (AgentScript) agent. ```bash sf org login web --alias myOrg ``` **Verify your org connection:** Run this command: ```bash sf org display user ``` **Expected output:** Your org username and alias ## 4. Deploy the Apex Runtime One-time per org. Installs the Apex classes that score queries at runtime. ```bash sf nanorag install --target-org myOrg ``` ## 5. Build a Library ```bash sf nanorag build --target-org myOrg \ --library-name product_docs \ --files ./docs/guide.pdf ./docs/faq.docx ``` Supported formats: PDF, DOCX, PPTX, XLSX, MD, TXT, and 30+ others. ## 6. Attach to Your Agent ```bash sf nanorag attach --target-org myOrg \ --library-name product_docs \ --agent-developer-name My_Service_Agent ``` Your agent can now retrieve from the library. Test a query: ```bash sf nanorag search --target-org myOrg \ --library-name product_docs \ --query "how do I cancel" ``` ## When to Use BM25 vs. Embeddings nanoRag uses BM25 — fast, deterministic keyword search. Use it when: - Users search with the same words that appear in your documents - Your corpus is product docs, policy manuals, runbooks, or API references - You have under ~10k documents Reach for an embedding-based RAG instead when: - You need synonym matching (user says "cancel," doc says "terminate") - Your corpus is multilingual and users mix languages - You need long-form generative answers, not retrieved passages The agent action signature is the same either way — start with BM25 and swap backends later if recall feels shallow. ## Reference | Command | Purpose | |---------|---------| | `sf nanorag search` | Test search against a library | | `sf nanorag detach` | Remove a library from an agent | | `sf nanorag library list` | List libraries in the org | | `sf nanorag library delete` | Delete a library | | `sf nanorag file add` | Add files, rebuild the index | | `sf nanorag file delete` | Remove files, rebuild the index | | `sf nanorag skill install` | Install the matching Claude Code skill | All commands accept `--json` for structured output. Source: [github.com/ankita13makker/nanoRAG](https://github.com/ankita13makker/nanoRAG). ## Synthetic Org Data (CRMArena) # Synthetic Org Data (CRMArena) Generate contextually relevant synthetic CRM data for any Salesforce org. Describe your company and use cases — CRMArena returns a fully seeded org with standard and custom objects, relationships, and realistic records. ## How it works CRMArena is a two-step pipeline from Salesforce AI Research: 1. **Schema Generation** — You provide a company name, description, and agentic use cases. CRMArena returns the relevant Salesforce objects (standard + custom) with their relationships. 2. **Org Data Generation** — Using that schema, the pipeline generates synthetic records and uploads them to either a new scratch org or your existing org. The result is a production-realistic dataset that exercises your agent's actions against real object shapes — not toy data. ## When to use it - **Agent development** — seed a scratch org so your agent has meaningful data to query, filter, and act on. - **Demo preparation** — stand up a convincing org in minutes instead of hours of manual data entry. - **Evaluation and benchmarking** — generate consistent datasets for repeatable agent testing. - **Prototyping** — validate an agent concept against realistic CRM topology before committing to production data models. ## Supported use cases | Domain | Examples | |--------|----------| | **Service** | Returns, case handling, order tracking | | **Sales** | Discount approvals, opportunity management | Other use cases are partially supported and continuously improving. ## API reference > **⚠️ Warning**: **Preview — this is a staging/POC API.** Request and response schemas may change without backward compatibility guarantees. When using `use_target_org=true`, your org data passes through the pipeline — assume it may be logged or retained for debugging purposes. **Base URL**: `https://crm-org-gen-stage-api.salesforceresearch.ai` — [Browse the live OpenAPI spec](https://crm-org-gen-stage-api.salesforceresearch.ai/docs) ### Schema generation ``` POST /preview-intent-schema/ ``` Generate the Salesforce object schema for your use case. **Request body:** ```json { "company_name": "Happy Paws Pet Supply", "company_description": "A regional pet supply retailer with 5 store locations offering pet food, toys, accessories, and grooming services.", "org_requirement": "Track customer orders and support cases for returns and complaints.", "goal": "synthetic_org" } ``` | Field | Required | Description | |-------|----------|-------------| | `company_name` | Yes | Company name | | `company_description` | Yes | Company description | | `org_requirement` | Yes | Natural language description of agentic use cases | | `goal` | No | `"synthetic_org"` (default). `"agent"` coming soon | | `instance_url` | No | Target org instance URL (must pair with `session_id`) | | `session_id` | No | Target org session ID (must pair with `instance_url`) | **Response:** ```json { "success": true, "schema_request_id": "18b8df1e-820b-42cb-9748-cc1784cef49a", "use_cases": [ { "prompt_statement": "Track customer orders", "use_case_name": "Order Tracking & Management", "use_case_description": "Handle customer requests related to locating and monitoring purchases...", "sub_tasks": [] } ], "odg_schema": { "objects": { "Account": { "fields": [], "relationships": [] } } }, "graph": { "nodes": [], "edges": [] } } ``` **Notes:** - Synchronous — typical runtime is 3–5 minutes. Set a long HTTP read timeout (e.g., `--max-time 600` with curl) - Keep `company_description` and `org_requirement` succinct — no more than a few use cases per run ### Org data generation ``` POST /process-dynamic-schema/ ``` Generate synthetic data and upload to an org. Returns immediately — work continues in the background. **Request body (with target org):** ```json { "schema_request_id": "18b8df1e-820b-42cb-9748-cc1784cef49a", "use_target_org": true, "target_org_details": { "instance_url": "https://myorg.my.salesforce.com", "session_id": "00D..." } } ``` **Request body (new scratch org):** ```json { "schema_request_id": "18b8df1e-820b-42cb-9748-cc1784cef49a", "email": "you@example.com" } ``` | Field | Required | Description | |-------|----------|-------------| | `schema_request_id` | Yes* | Returned by `/preview-intent-schema/` | | `use_target_org` | No | `true` to upload to an existing org | | `target_org_details` | If `use_target_org` | Object with `instance_url` and `session_id` (both required together) | | `email` | If scratch org | Email to associate with the new scratch org | | `slack_id` | No | Your own Slack member ID — receive a DM when the job finishes | | `crawling_url` | No | Public URL to crawl for knowledge article ingestion. Only supply URLs you control — avoid internal or metadata endpoints | | `goal` | No | `"synthetic_org"` (default). `"agent"` coming soon | > **ℹ️ Info**: **Alternative:** Instead of `schema_request_id`, you can provide `schema`, `company_name`, and `company_description` directly to skip the schema generation step. **Response:** ```json { "message": "Dynamic schema processing started successfully.", "datagen_request_id": "e9312a48-0926-4245-ac8d-a1be8adc2ce9", "status_url": "/process-dynamic-schema/status/e9312a48-0926-4245-ac8d-a1be8adc2ce9" } ``` ### Poll status ``` GET /process-dynamic-schema/status/{datagen_request_id} ``` Poll every 10–15 seconds. Typical runtime is 10–30 minutes. Set a maximum retry count (e.g., 200 attempts) to avoid polling indefinitely. **Response (in progress):** ```json { "status": "running", "goal": "synthetic_org", "detail_status": "Org Setup Completed", "pipeline_steps": { "org_setup": { "status": "Completed", "completed_at": "2026-04-28T16:12:45Z" }, "data_generation": { "status": "In Progress", "completed_at": null }, "app_creation": { "status": "Pending", "completed_at": null }, "data_upload": { "status": "Pending", "completed_at": null } }, "datagen_request_id": "...", "created_at": "2026-04-28T23:08:28Z", "updated_at": "2026-04-28T23:12:45Z", "error": null, "project_identifier": "happy-paws-pet-supply-975f627d" } ``` **Statuses:** `queued` → `running` → `completed` or `failed` When completed with a scratch org, the response includes a `result` object with `username`, `password`, `instance_url`, and `org_id`. ## Typical integration flow ``` 1. POST /preview-intent-schema/ → get schema_request_id (~3–5 min) 2. POST /process-dynamic-schema/ → start data gen, get datagen_request_id 3. GET /process-dynamic-schema/status/{id} → poll until completed or failed 4. (If scratch org) Read credentials from result ``` ## Getting your org credentials To use CRMArena with an existing org, you need the `instance_url` and `session_id`. Here's how to get them: > **⚠️ Warning**: The `session_id` is a full-privilege OAuth access token — it grants complete API access to the org for its validity window. **Use a scratch org or sandbox only.** Never use a production org. Do not paste the token into Slack, tickets, or screenshots. Revoke the session after the job completes by logging out of the org. **Using Salesforce CLI** (requires [jq](https://jqlang.github.io/jq/)): ```bash # Log in to your org (if not already) sf org login web --alias my-scratch-org # Get instance URL sf org display --target-org my-scratch-org --json | jq -r '.result.instanceUrl' # → https://mycompany.my.salesforce.com # Get session ID (the access token IS the session ID) sf org display --target-org my-scratch-org --json | jq -r '.result.accessToken' # → 00D5f00000ABC!AQE... ``` > **⚠️ Warning**: Session IDs are short-lived and expire. If you get a `401` or the job fails at "Org Setup", re-authenticate with `sf org login web` and extract a fresh token. ## Integration notes - In both endpoints, `instance_url` and `session_id` must be provided **together** or **both omitted** — mixing causes a `422` validation error. - `/preview-intent-schema/` is synchronous and can take 3–5 minutes. Set a long HTTP read timeout (e.g., `--max-time 600` with curl). - `/process-dynamic-schema/` returns immediately. Poll the status endpoint every 10–15 seconds with a per-request timeout of 60–120s. - The `target_org_details` object in `/process-dynamic-schema/` follows the same pairing rule — both `instance_url` and `session_id` are required within it. ## Team Owned by **Salesforce AI Research**. ## Bot Migration # Bot Migration Convert an Einstein or Messaging Bot to an Agentforce agent. Choose parity (1:1 mapping), enhanced (use agent capabilities to fix weak spots from past conversations), or both (generate both and compare). The migration uses the [ADLC skills](/docs/skills) under the hood — your bot's metadata and conversations are analyzed to produce a requirements document and a test plan, which ADLC then uses to author and verify the agent. [Open Bot Migration](/bot-migration) ## How it works 1. **Source.** Pick a bot from a connected Salesforce org, or upload a bot metadata zip. 2. **Conversations (optional).** Auto-pull recent conversations from your org or upload transcripts. Past conversations help the migration produce more accurate requirements and tests. 3. **Migration goal.** Choose parity, enhanced, or both, plus optional freeform intent and context files. 4. **Run path.** Convert in-page (~3 min headless), open in Claude Code on labs (interactive), or run locally with your own machine. 5. **Results.** Download the generated requirements, .agent file, and tests YAML. Deploy directly to your connected org if you have one. --- # Getting Started ## Agentforce Labs ## Stay Up to Date See the latest updates and what's new in Labs. ## Changelog # Changelog Track what's shipping, what's changing, and where Agentforce Labs is headed. --- ## February 2026 ### What shipped - **Claude Code end-to-end flow** -- Authenticate, get a provisioned org, and build agents with Claude Code using the Agentforce MCP server and Agent Script skill - **Agent Builder provisioning** -- One-click sign-in to a pre-provisioned Agentforce Builder org - **Agent Script generation** -- Describe your agent in natural language and get working Agent Script code - **Agent API Guide** -- Live API reference page with personalized credentials and agent selection - **Full documentation site** -- Getting Started guides, Core Concepts, IDE integration docs, Agent Script Recipes, Deploy Recipes, and Claude-specific references - **Workshop content** -- Hands-on builder exercises with the Pronto scenario - **Markdoc migration** -- Documentation powered by Markdoc with custom interactive components - **Enhanced Chat Setup guide** -- Detailed step-by-step recipe for deploying agents via Enhanced Chat ### What's in progress - **IDE integrations** -- Codex, Cursor, and Windsurf setup documentation available; backend provisioning coming next - **Agent authoring chat** -- Multi-turn conversation for refining agent configuration (one-shot works today) - **Experience Site deployment** -- Automating the last steps of deploying agents to Salesforce Experience Sites - **Eval-driven development** -- Building evaluation capabilities into the Claude Code skill for automated test loops - **Deploy flow polish** -- API snippets and Enhanced Chat embed being refined ### What's planned - **CLI tooling** -- Local agent development workflow via Salesforce CLI - **Additional deployment targets** -- Messaging platforms (WhatsApp, SMS, Messenger) - **Agent Vibes** -- Creative agent crafting experience in Code Builder - **Org pool scaling** -- Expanding from current pool to support broader access - **TBID authentication** -- Trailhead-based identity for the final domain --- ## January 2026 ### Foundation - **Project kickoff** -- Dev Easy Self Service initiative launched - **Architecture decisions** -- ECA for API keys, TBID for interim auth, Heroku hosting, own MCP server - **Next.js migration** -- Frontend rebuilt on Next.js 16 with App Router - **Backend API** -- FastAPI backend with Redis/RQ for async provisioning - **OrgFarm integration** -- Pre-provisioned org pool with Agentforce and Data Cloud enabled --- # IDE Integrations ## Docs MCP # Docs MCP Your AI assistant can search and retrieve official Salesforce docs without leaving the IDE. Help articles, Developer Guides, release notes, all indexed. ## Available tools | Tool | What it does | | --- | --- | | `salesforce_docs_search` | Semantic search across Salesforce doc collections. Returns ranked excerpts with source URLs. | | `salesforce_docs_fetch` | Retrieves the full content of a page by `documentPath` (returned from search). | Once connected, ask questions like "How do I configure a JWT bearer flow?" or "What fields does the AgentScript config block support?" and get cited answers inline. ## Install #### Cursor **One-click:** [Install in Cursor](cursor://anysphere.cursor-deeplink/mcp/install?name=salesforce-docs&config=eyJ1cmwiOiJodHRwczovL3NhbGVzZm9yY2UtZG9jcy03NjI1ODc0NGM5ZDcuaGVyb2t1YXBwLmNvbS9hcGkvbWNwIn0%3D) Or add to `~/.cursor/mcp.json` (Windows: `%USERPROFILE%\.cursor\mcp.json`): ```json { "mcpServers": { "salesforce-docs": { "url": "https://salesforce-docs-76258744c9d7.herokuapp.com/api/mcp" } } } ``` Restart Cursor after saving. #### Claude Code ```bash claude mcp add --transport http salesforce-docs https://salesforce-docs-76258744c9d7.herokuapp.com/api/mcp ``` > **ℹ️ Restart required**: Claude Code loads MCP servers at startup. Restart after adding. #### Windsurf Add to `~/.codeium/windsurf/mcp_config.json`: ```json { "mcpServers": { "salesforce-docs": { "url": "https://salesforce-docs-76258744c9d7.herokuapp.com/api/mcp" } } } ``` #### VS Code Add to `.vscode/mcp.json` in your workspace: ```json { "servers": { "salesforce-docs": { "type": "http", "url": "https://salesforce-docs-76258744c9d7.herokuapp.com/api/mcp" } } } ``` #### Other Any client that supports the [MCP open protocol](https://modelcontextprotocol.io/) works. Point it at: ``` https://salesforce-docs-76258744c9d7.herokuapp.com/api/mcp ``` If your client requires auth headers: ```json { "url": "https://salesforce-docs-76258744c9d7.herokuapp.com/api/mcp", "headers": { "Authorization": "Bearer YOUR_TOKEN_HERE" } } ``` ## Verify Ask your AI assistant: ``` Search Salesforce docs for "How do I set up a record-triggered flow?" ``` **Expected:** The assistant calls `salesforce_docs_search` and returns excerpts with titles, URLs, and relevance scores. ## Get better results - **Ask full questions.** "How do I configure OAuth 2.0 JWT bearer flow?" beats "JWT flow" - **Scope by collection.** Pass `collection: "developer-lwc"` or `collection: "admin-ai"` to narrow the search - **Keep limits low.** Excerpts can be long. Default is 5; lower it in busy sessions - **Fetch for full pages.** Search returns excerpts. Follow up with `salesforce_docs_fetch` using the `documentPath` when you need complete procedures or tables