
Sf Ai Agentforce
- 29 installs
- 423 repo stars
- Updated April 27, 2026
- jaganpro/claude-code-sfskills
This is a copy of sf-ai-agentforce by jaganpro - installs and ranking accrue to the original listing.
Helps with ai & agent building tasks.
About
sf-ai-agentforce is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- sf-ai-agentforce
- AI & Agent Building
- AI-coding skill
Sf Ai Agentforce by the numbers
- 29 all-time installs (skills.sh)
- Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/jaganpro/claude-code-sfskills --skill sf-ai-agentforceAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 29 |
|---|---|
| repo stars | ★ 423 |
| Last updated | April 27, 2026 |
| Repository | jaganpro/claude-code-sfskills ↗ |
What it does
Helps with ai & agent building tasks.
Files
sf-ai-agentforce: Standard Agentforce Platform Development
Use this skill for the Setup UI / Agent Builder path: declarative topics, Builder-managed actions, GenAiFunction / GenAiPlugin metadata, Prompt Builder templates stored as `GenAiPromptTemplate` metadata, Models API usage from Apex, and custom Lightning types.
For new code-first agent development, prefer sf-ai-agentscript.
>
If the work produces or edits a .agent file — including Builder Script / Canvas work that results in an authoring bundle — use sf-ai-agentscript.When This Skill Owns the Task
Use sf-ai-agentforce when the user is:
- maintaining existing Builder-based agents
- working in Setup → Agentforce → Agents
- creating or fixing
GenAiFunction,GenAiPlugin, orGenAiPromptTemplatemetadata - wiring Builder topics to Flow / Apex / Prompt Builder actions
- using Models API or LightningTypeBundle in the context of Builder-based agents
Do not use it for:
.agentfiles or deterministic FSM design → sf-ai-agentscript- agent test suites and coverage loops → sf-ai-agentforce-testing
- persona / voice design → sf-ai-agentforce-persona
---
Required Context to Gather First
Ask for or infer:
- whether this is a Builder / Setup UI project or a code-first Agent Script project
- whether the user is editing Builder metadata or a
.agentauthoring bundle - agent type: Service Agent or Employee Agent
- whether the work targets topics, actions, Prompt Builder templates, Models API, or custom Lightning types
- what supporting Flow / Apex / metadata dependencies already exist
- whether the user needs authoring help, publish help, or troubleshooting
---
Two Agentforce Paths
| Path | Skill | Best fit |
|---|---|---|
| Builder metadata path | sf-ai-agentforce | Declarative maintenance, existing Builder agents, metadata-driven action registration |
| Agent Script authoring bundle path | sf-ai-agentscript | Code-first .agent authoring, deterministic routing, version-controlled agent logic |
If the user is starting from scratch and wants strong control over flow/state, route to Agent Script.
---
Builder Workflow Summary
1. Confirm this is a Builder / Setup UI project 2. Pick Service Agent vs Employee Agent 3. For Service Agents, provision the running user (prefer sf org create agent-user) 4. For Employee Agents, plan visibility with a Permission Set containing <agentAccesses> 5. Define topics with strong descriptions, scope, and instructions 6. Prepare supporting actions (Flow, Apex, Prompt Builder template) 7. Configure inputs / outputs carefully 8. Validate dependencies and template status 9. Publish, then activate
Expanded workflow: references/builder-workflow.md
---
Key Platform Rules
Topic quality matters
Topic descriptions are routing instructions for the planner. They must be:
- specific
- scenario-based
- non-overlapping with sibling topics
Actions are wrappers around real targets
| Target type | Typical use | Registered via |
|---|---|---|
| Flow | safest default for Builder actions | GenAiFunction |
| Apex | complex business logic via @InvocableMethod | GenAiFunction |
| Prompt Builder template | generated summaries / drafts / recommendations | GenAiFunction |
Prompt Template vs GenAiPromptTemplate
- Prompt Template is the plain-English / UI term used in Prompt Builder.
- `GenAiPromptTemplate` is the current Metadata API type for source-driven template work.
- Prefer current source format:
genAiPromptTemplates/*.genAiPromptTemplate-meta.xml. - For flexible Prompt Builder templates, plan around the 5-input maximum and consolidate inputs when needed.
- Prompt content should reference inputs with the current merge-field shape, e.g.
{!$Input:TargetRecord}or{!$Input:AdditionalContext}.
Supporting metadata deploys first
Before publishing the agent itself, deploy the supporting stack: 1. metadata / fields if needed 2. Apex if needed 3. Flows if needed 4. GenAiPromptTemplate / GenAiFunction / GenAiPlugin 5. then publish the agent
Service Agent running user
For Service Agents, prefer the native GA command: sf org create agent-user --target-org <alias> --json Use the returned username in the running-user configuration.
Employee Agent visibility
For Employee Agents, ensure end users receive a Permission Set containing <agentAccesses>. Without this, the agent can be active but still invisible in Lightning Experience. See ../sf-permissions/references/agent-access-guide.md.
Publish does not activate
After publish, run sf agent activate separately. For automation, prefer sf agent activate --api-name <AgentName> --version <n> --target-org <alias> --json so the rollout is deterministic and machine-readable.
---
Metadata Guidance
GenAiFunction
Use when registering a single callable action. Validate:
- target exists
- target is active / deployable
- input names match the target contract
- output names match the target contract
- capability text clearly says when the action should be used
GenAiPlugin
Use when grouping related functions into one logical package.
GenAiPromptTemplate
Use for generated content, not deterministic business rules.
Prefer the current metadata shape:
- metadata type:
GenAiPromptTemplate - folder:
genAiPromptTemplates/ - file suffix:
.genAiPromptTemplate-meta.xml - content lives under
templateVersions - use published template versions before wiring actions that depend on them
Models API
Use when the solution belongs in Apex-driven AI orchestration rather than Builder-only actions.
Custom Lightning Types
Use when the action needs richer structured input or output presentation.
Expanded references:
- references/metadata-reference.md
- references/genaiprompttemplate.md
---
Cross-Skill Integration
Recommended Orchestration Order
sf-metadata → sf-apex → sf-flow → sf-ai-agentforce → sf-deployRequired delegations
| Requirement | Delegate to | Why |
|---|---|---|
| Create / fix Flows | sf-flow | Action target creation and Flow validation |
| Create / fix Apex actions | sf-apex | @InvocableMethod and Apex correctness |
| Deploy / publish | sf-deploy | Deployment orchestration |
| Test the agent | sf-ai-agentforce-testing | Formal test execution and assertions |
| Employee Agent visibility / access | sf-permissions | Permission Set <agentAccesses> setup |
---
High-Signal Failure Patterns
| Symptom | Likely cause | Read next |
|---|---|---|
| Action not available in Builder | target metadata missing or not deployed | references/metadata-reference.md |
| Prompt action fails during publish or activation | template is Draft, missing inputs, or old metadata shape is being used | references/genaiprompttemplate.md |
| Need more than 5 template inputs | flex template input limit hit | references/genaiprompttemplate.md |
| Apex AI logic times out | Models API work placed in the wrong context | references/models-api.md |
| Rich input/output UI not rendering | Lightning type config or prerequisites are incomplete | references/custom-lightning-types.md |
| Agent publishes but is not usable | forgot explicit activation | references/cli-commands.md |
| Service Agent publish/runtime failure | missing or invalid running user | ../sf-ai-agentscript/references/agent-user-setup.md |
| Employee Agent active but not visible to users | missing <agentAccesses> permission set | ../sf-permissions/references/agent-access-guide.md |
---
Reference Map
Start here
- references/builder-workflow.md
- references/metadata-reference.md
- references/genaiprompttemplate.md
- references/cli-commands.md
Terminology and template planning
- references/prompt-templates.md
- references/models-api.md
- references/custom-lightning-types.md
Rubric
- references/scoring-rubric.md
Cross-skill reads
- sf-ai-agentscript
- sf-ai-agentforce-testing
- sf-flow
- sf-apex
- sf-permissions
- sf-deploy
---
Score Guide
| Score | Meaning |
|---|---|
| 90+ | Ready to deploy |
| 80–89 | Strong, minor cleanup only |
| 70–79 | Review before deploy |
| 60–69 | Needs work |
| < 60 | Block deployment |
Full rubric: references/scoring-rubric.md
Credits
sf-agentforce Skill
Created by Jag Valaiyapathy
Authors & Contributors
August Krys
Key contributions:
- corrections that drove the shift from legacy PromptTemplate guidance to current
GenAiPromptTemplateguidance - updates to Prompt Builder metadata direction, including modern template structure and input-limit guidance
- feedback on Builder-oriented agent authoring and supporting metadata sequencing
References & Inspiration
Official Salesforce Documentation
Community Resources
- Agent Script Language Guide - Community syntax reference
- Gearset GenAI Deployment Guide
License
MIT License - See LICENSE
MIT License
Copyright (c) 2024-2025 Jag Valaiyapathy
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
sf-ai-agentforce
Standard Agentforce platform skill for Setup UI / Agent Builder work: Builder-managed topics and actions, GenAiFunction / GenAiPlugin, Prompt Builder templates stored as `GenAiPromptTemplate` metadata, Einstein Models API, and custom Lightning types.
For code-first Agent Script DSL development, use sf-ai-agentscript.
>
If the work ends in a .agent file or authoring bundle, use sf-ai-agentscript.What This Skill Covers
| Area | Description |
|---|---|
| Agent Builder | Creating and configuring agents via Setup UI / Agentforce Builder |
| GenAiFunction | Metadata XML for registering Flow, Apex, and Prompt Builder actions |
| GenAiPlugin | Grouping multiple GenAiFunctions into reusable action sets |
| GenAiPromptTemplate | Current Metadata API type for Prompt Builder templates |
| Models API | Native LLM access in Apex via aiplatform.ModelsAPI |
| Custom Lightning Types | LightningTypeBundle for custom agent action UIs |
What This Skill Does NOT Cover
| Area | Use Instead |
|---|---|
Agent Script DSL (.agent files) | sf-ai-agentscript |
Builder Script / Canvas work that results in .agent authoring bundles | sf-ai-agentscript |
| Agent testing & coverage | sf-ai-agentforce-testing |
| Deployment & publishing | sf-deploy |
Requirements
| Requirement | Value |
|---|---|
| API Version | 66.0+ (Spring '26 or later) |
| Licenses | Agentforce, Einstein Generative AI |
| sf CLI | v2.x with agent commands |
Quick Start
Skill: sf-ai-agentforce
Request: "Set up a GenAiFunction for my Apex discount calculator"Key Current-State Guidance
- In conversation, users may say Prompt Template.
- In source metadata, use `GenAiPromptTemplate`.
- Prefer
genAiPromptTemplates/*.genAiPromptTemplate-meta.xmlfor source-driven Prompt Builder work. - Flex templates should be designed around the 5-input maximum.
- For Service Agents, prefer
sf org create agent-userfor running-user setup. - For Employee Agents, plan user visibility with a Permission Set that contains
<agentAccesses>.
Documentation
| Document | Description |
|---|---|
| SKILL.md | Entry point — full skill reference |
| references/genaiprompttemplate.md | Current GenAiPromptTemplate metadata guidance |
| references/prompt-templates.md | Terminology guide: Prompt Template vs GenAiPromptTemplate |
| references/models-api.md | Einstein Models API (aiplatform.ModelsAPI) |
| references/custom-lightning-types.md | LightningTypeBundle for custom agent UIs |
Orchestration
This skill fits into the Agentforce build chain:
sf-metadata → sf-apex → sf-flow → sf-ai-agentforce → sf-deployLicense
MIT License — See LICENSE
Agentforce Builder Workflow
This reference expands the Setup UI / Agent Builder workflow for sf-ai-agentforce.
Recommended order
1. Confirm this is Builder metadata work, not .agent authoring 2. Identify agent type: Service Agent vs Employee Agent 3. For Service Agents, provision the running user (prefer sf org create agent-user) 4. For Employee Agents, plan visibility with a Permission Set that contains <agentAccesses> 5. Define topics and topic scope 6. Prepare supporting actions (Flow, Apex, Prompt Builder template) 7. Configure action inputs and outputs 8. Configure agent-level instructions and messages 9. Validate supporting metadata and template status 10. Publish and activate
Builder checklist
Topics
- Topic descriptions must be concrete and routeable
- Scope should say what the topic can and cannot do
- Instructions should be procedural, not vague brand copy
Actions
- Flow actions are the safest default for Builder-based agents
- Apex actions must expose
@InvocableMethod - Prompt Builder templates should be used when the goal is generated content, not deterministic business logic
Prompt Builder templates
- In the UI, users will usually say Prompt Template
- In source metadata, use `GenAiPromptTemplate`
- Prefer
genAiPromptTemplates/*.genAiPromptTemplate-meta.xml - Flex templates should stay within the 5-input maximum
- Use published template versions before wiring dependent actions
Inputs / Outputs
- Input names must match the target contract exactly
- Output names should be meaningful to the planner
- Displayable outputs should be user-facing and concise
Agent-level settings
- System instructions should be stable and role-defining
- Welcome message should orient the user quickly
- Error message should explain fallback behavior
- Service Agents should use a running user provisioned with
sf org create agent-userwhen possible - Employee Agents need end-user visibility via Permission Sets containing
<agentAccesses>
Publish sequence
1. Deploy supporting metadata (GenAiPromptTemplate, GenAiFunction, GenAiPlugin, Flow, Apex, etc.) 2. Save / publish the agent in Builder, or deploy the relevant Builder metadata 3. Activate the target version 4. For Employee Agents, verify visibility via Permission Set <agentAccesses>
Publishing does not activate the new version automatically.
sf agent publish authoring-bundleis part of the Agent Script workflow and belongs tosf-ai-agentscript, not the default Builder metadata workflow.
Deep references
- CLI lifecycle: cli-commands.md
- Metadata details: metadata-reference.md
- GenAI prompt metadata: genaiprompttemplate.md
- Prompt terminology: prompt-templates.md
- Models API: models-api.md
- Custom Lightning types: custom-lightning-types.md
- Employee Agent visibility: ../../sf-permissions/references/agent-access-guide.md
- Service Agent running user: ../../sf-ai-agentscript/references/agent-user-setup.md
<!-- Parent: sf-ai-agentforce/SKILL.md --> <!-- TIER: 2 | DETAILED REFERENCE --> <!-- Read after: SKILL.md --> <!-- Purpose: CLI command reference for Builder metadata workflows, shared lifecycle commands, and Agent Script handoff -->
Agent CLI Commands Reference
Currentsf agentandsf orgcommands relevant to Builder metadata workflows, shared agent lifecycle operations, and Agent Script handoff.
Overview
This file focuses on:
- shared lifecycle commands that matter in Builder-heavy work
- legacy / non-Agent Script spec-driven commands that still exist
- Agent Script handoff points that should route to
sf-ai-agentscript
---
Shared Lifecycle Commands
sf org create agent-user
Creates the default Service Agent running user in the target org.
sf org create agent-user --target-org <alias> --json
sf org create agent-user --first-name Service --last-name Agent --target-org <alias> --json
sf org create agent-user --base-username service-agent@corp.com --target-org <alias> --jsonThe command auto-assigns the standard Service Agent profile and system permission sets. Use the returned username for running-user configuration.
| Flag | Required | Description |
|---|---|---|
--target-org | Yes | Alias or username of the target org |
--first-name | No | Override the default first name |
--last-name | No | Override the default last name |
--base-username | No | Username base; the CLI appends a unique suffix |
--api-version | No | Override API version |
--json | No | Return output as JSON |
sf agent activate
Makes a published agent available to users.
# Manual / interactive activation
sf agent activate --api-name <AgentApiName> --target-org <alias>
# CI / deterministic activation of a known BotVersion
sf agent activate --api-name <AgentApiName> --version <n> --target-org <alias> --json| Flag | Required | Description |
|---|---|---|
--api-name | No | API name of the agent to activate; if omitted, the CLI prompts you to choose |
--target-org | Yes | Alias or username of the target org |
--api-version | No | Override the API version used for the request |
--version | No | BotVersion number to activate (vX in metadata corresponds to --version X) |
--json | No | Format output as JSON |
If you use--jsonwithout--version, the CLI activates the latest agent version. Prefer--versionfor CI/CD and reproducible rollout scripts.
sf agent deactivate
Deactivates an active agent before major updates.
# Manual / interactive deactivation
sf agent deactivate --api-name <AgentApiName> --target-org <alias>
# Script-friendly deactivation
sf agent deactivate --api-name <AgentApiName> --target-org <alias> --json| Flag | Required | Description |
|---|---|---|
--api-name | No | API name of the agent to deactivate; if omitted, the CLI prompts you to choose |
--target-org | Yes | Alias or username of the target org |
--api-version | No | Override the API version used for the request |
--json | No | Format output as JSON |
---
Builder / Non-Agent Script Workflows
sf agent create
Creates a non-Agent Script agent from a spec file.
sf agent create --name "My Agent" --api-name My_Agent --spec <path-to-spec.yaml> --target-org <alias> --jsonLegacy / non-Agent Script path. This is not the default workflow for this repository.
| Flag | Required | Description |
|---|---|---|
--name | No | Name (label) of the new agent |
--api-name | No | API name of the new agent |
--spec | No | Path to the local agent spec file |
--target-org | Yes | Alias or username of the target org |
--preview | No | Preview the generated agent without saving it |
--json | No | Return output as JSON |
sf agent generate agent-spec
Generates an agent spec YAML file interactively or with flags.
# Interactive full interview
sf agent generate agent-spec --full-interview
# Non-interactive with key flags
sf agent generate agent-spec \
--type customer \
--role "Customer support specialist" \
--company-name "Acme Corp" \
--company-description "Enterprise SaaS provider" \
--tone formal \
--output-file ./agent-spec.yaml
# Iterative refinement of existing spec
sf agent generate agent-spec --spec ./agent-spec.yaml| Flag | Required | Description |
|---|---|---|
--type | No | Agent type: customer or internal |
--role | No | Agent's role description |
--company-name | No | Company name for agent context |
--company-description | No | Company description for grounding |
--company-website | No | Company website URL for enrichment |
--tone | No | Conversational tone: formal, casual, or neutral |
--full-interview | No | Interactive prompt for all properties |
--spec | No | Path to existing spec YAML for iterative refinement |
--prompt-template | No | Custom prompt template for spec generation |
--grounding-context | No | Additional context for grounding the agent |
--force-overwrite | No | Overwrite existing output file without prompting |
--enrich-logs | No | Include enrichment logs in output |
--max-topics | No | Maximum number of topics to generate |
--agent-user | No | Default agent user for the spec |
--output-file | No | Path for the output spec YAML file |
Keep this as an optional ideation/bootstrap path. It is not the default authoring model for sf-ai-agentscript.sf agent generate template
Generates a BotTemplate for ISV packaging via managed packages on AppExchange.
sf agent generate template \
--agent-file force-app/main/default/bots/My_Agent/My_Agent.bot-meta.xml \
--agent-version 1 \
--output-dir my-package \
--source-org my-scratch-org \
--json| Flag | Required | Description |
|---|---|---|
--agent-file | Yes | Path to the .bot-meta.xml file |
--agent-version | Yes | BotVersion number to template |
--output-dir | No | Directory where generated BotTemplate and GenAiPlannerBundle files are saved |
--source-org | Yes | Namespaced scratch org that contains the source agent |
--json | No | Return output as JSON |
Important: This command works with Bot / BotVersion metadata and does not package agents created from Agent Script files.
---
Agent Script Handoff
The following commands belong to the Agent Script workflow and are documented in: ../../sf-ai-agentscript/references/cli-guide.md
sf agent generate authoring-bundlesf agent validate authoring-bundlesf agent publish authoring-bundle
sf agent generate authoring-bundle
Generates an authoring bundle from an agent spec YAML file, or from default boilerplate when --no-spec is used. This is primarily an Agent Script workflow.
sf agent generate authoring-bundle --no-spec --name "My Agent" --target-org <alias> --json
sf agent generate authoring-bundle --spec ./agent-spec.yaml --name "My Agent" --target-org <alias> --json| Flag | Required | Description |
|---|---|---|
--spec | No | Path to the agent spec YAML file |
--no-spec | No | Skip spec generation and use default boilerplate |
--name | No | Name (label) of the new authoring bundle; required with --json |
--api-name | No | API name of the new authoring bundle |
--output-dir | No | Directory where the authoring bundle files are generated |
--force-overwrite | No | Overwrite an existing local authoring bundle without prompting |
--target-org | Yes | Alias or username of the target org |
--json | No | Return output as JSON |
Use sf-ai-agentscript when this command is the main task.---
Preview Commands
sf agent preview
Previews agent behavior interactively. This command is interactive and does not support --json.
# Published / activated agent
sf agent preview --api-name <AgentApiName> --target-org <alias>
# Published / activated agent with debug output
sf agent preview --api-name <AgentApiName> --use-live-actions --apex-debug --output-dir ./logs --target-org <alias>
# Local authoring bundle by API name
sf agent preview --authoring-bundle <BundleApiName> --target-org <alias>| Flag | Required | Description |
|---|---|---|
--api-name | Yes* | API name of the activated published agent |
--authoring-bundle | Yes* | API name of the authoring bundle metadata component |
--target-org | Yes | Alias or username of the target org |
--use-live-actions | No | Execute real Apex/Flows instead of LLM simulation |
--output-dir | No | Directory for preview output/logs |
--apex-debug | No | Include Apex debug logs in output |
*One of --api-name or --authoring-bundle is required.
GA preview session commands also exist:sf agent preview start,sf agent preview send, andsf agent preview end. For scripted smoke-test workflows, seesf-ai-agentscriptandsf-ai-agentforce-testing.
---
Cross-Skill References
| Command Area | Skill | Notes |
|---|---|---|
| Builder metadata, Prompt Builder, Models API | ../SKILL.md | This skill |
Agent Script .agent files and authoring bundles | ../../sf-ai-agentscript/SKILL.md | Code-first agent development |
| Deployment orchestration, CI/CD | ../../sf-deploy/SKILL.md | Agent deployment workflows |
| Test execution, coverage analysis | ../../sf-ai-agentforce-testing/SKILL.md | sf agent test run/list/results |
<!-- Parent: sf-ai-agentforce/SKILL.md --> <!-- TIER: 3 | DETAILED REFERENCE --> <!-- Read after: SKILL.md --> <!-- Purpose: LightningTypeBundle for custom agent action UIs (API 64.0+) -->
Custom Lightning Types for Agentforce
Build custom UI components for agent action inputs and outputs using LightningTypeBundle
Overview
Custom Lightning Types enable you to define custom data structures with dedicated UI components for Agentforce service agents. When an agent action requires structured input or displays complex output, you can create a custom type with:
- Schema: Define the data structure and validation
- Editor: Custom UI for input collection
- Renderer: Custom UI for displaying output
┌─────────────────────────────────────────────────────────────────────────────┐
│ CUSTOM LIGHTNING TYPE ARCHITECTURE │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ LightningTypeBundle/ │
│ └── MyCustomType/ │
│ ├── schema.json ← Data structure definition │
│ ├── editor.json ← Input UI configuration │
│ ├── renderer.json ← Output UI configuration │
│ └── MyCustomType.lightningTypeBundle-meta.xml │
│ │
│ ▼ │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ AGENT CONVERSATION │ │
│ ├─────────────────────────────────────────────────────────────────┤ │
│ │ Agent: I need some details to proceed. │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────┐ │ │
│ │ │ [Custom Input UI - Editor Component] │ │ │
│ │ │ ┌───────────────┐ ┌────────────────────┐ │ │ │
│ │ │ │ Name: [_____] │ │ Type: [Dropdown ▼] │ │ │ │
│ │ │ └───────────────┘ └────────────────────┘ │ │ │
│ │ │ [Submit] │ │ │
│ │ └──────────────────────────────────────────┘ │ │
│ │ │ │
│ │ Agent: Here's the result: │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────┐ │ │
│ │ │ [Custom Output UI - Renderer Component] │ │ │
│ │ │ ┌─────────────────────────────────────┐ │ │ │
│ │ │ │ Order #12345 │ │ │ │
│ │ │ │ Status: ✅ Confirmed │ │ │ │
│ │ │ │ [View Details] [Track Shipment] │ │ │ │
│ │ │ └─────────────────────────────────────┘ │ │ │
│ │ └──────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘---
Prerequisites
API Version Requirement
Minimum API v64.0+ (Fall '25) for LightningTypeBundle support.
# Verify org API version
sf org display --target-org [alias] --json | jq '.result.apiVersion'Enhanced Chat V2 Requirement
Custom Lightning Types require Enhanced Chat V2 in Service Cloud:
1. Go to Setup → Chat → Chat Settings 2. Enable Enhanced Chat Experience 3. Select Version 2 (Enhanced)
⚠️ Without Enhanced Chat V2, custom type UI components will not render.
---
File Structure
force-app/main/default/
└── lightningTypeBundles/
└── OrderDetails/
├── schema.json
├── editor.json
├── renderer.json
└── OrderDetails.lightningTypeBundle-meta.xmlBundle Metadata XML
<?xml version="1.0" encoding="UTF-8"?>
<LightningTypeBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<masterLabel>Order Details</masterLabel>
<description>Custom type for order information display</description>
</LightningTypeBundle>---
Schema Definition (schema.json)
The schema defines your data structure using JSON Schema format:
Basic Schema
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"title": "OrderDetails",
"description": "Order information for display in agent conversations",
"properties": {
"orderId": {
"type": "string",
"title": "Order ID",
"description": "Unique order identifier"
},
"orderStatus": {
"type": "string",
"title": "Order Status",
"enum": ["Pending", "Processing", "Shipped", "Delivered", "Cancelled"],
"description": "Current order status"
},
"orderDate": {
"type": "string",
"format": "date",
"title": "Order Date"
},
"totalAmount": {
"type": "number",
"title": "Total Amount",
"minimum": 0
},
"items": {
"type": "array",
"title": "Order Items",
"items": {
"type": "object",
"properties": {
"productName": {
"type": "string"
},
"quantity": {
"type": "integer",
"minimum": 1
},
"price": {
"type": "number",
"minimum": 0
}
},
"required": ["productName", "quantity", "price"]
}
}
},
"required": ["orderId", "orderStatus"]
}Supported JSON Schema Types
| Type | JSON Schema | Notes |
|---|---|---|
| Text | "type": "string" | Standard text input |
| Number | "type": "number" | Decimal values |
| Integer | "type": "integer" | Whole numbers only |
| Boolean | "type": "boolean" | True/false checkbox |
| Enum | "enum": [...] | Dropdown selection |
| Date | "format": "date" | Date picker |
| DateTime | "format": "date-time" | Date and time picker |
| Array | "type": "array" | List of items |
| Object | "type": "object" | Nested structure |
Validation Keywords
{
"properties": {
"email": {
"type": "string",
"format": "email",
"maxLength": 255
},
"quantity": {
"type": "integer",
"minimum": 1,
"maximum": 100
},
"productCode": {
"type": "string",
"pattern": "^PRD-[0-9]{6}$"
}
}
}---
Editor Configuration (editor.json)
The editor defines how input fields are collected from users:
Basic Editor
{
"component": "lightning-record-edit-form",
"attributes": {
"objectApiName": "Custom_Lightning_Type"
},
"fields": [
{
"name": "orderId",
"component": "lightning-input",
"attributes": {
"label": "Order ID",
"placeholder": "Enter order number",
"required": true
}
},
{
"name": "orderStatus",
"component": "lightning-combobox",
"attributes": {
"label": "Order Status",
"options": [
{ "label": "Pending", "value": "Pending" },
{ "label": "Processing", "value": "Processing" },
{ "label": "Shipped", "value": "Shipped" },
{ "label": "Delivered", "value": "Delivered" },
{ "label": "Cancelled", "value": "Cancelled" }
]
}
},
{
"name": "orderDate",
"component": "lightning-input",
"attributes": {
"type": "date",
"label": "Order Date"
}
},
{
"name": "totalAmount",
"component": "lightning-input",
"attributes": {
"type": "number",
"label": "Total Amount",
"formatter": "currency",
"step": "0.01"
}
}
],
"submitButton": {
"label": "Submit Order Details",
"variant": "brand"
}
}Supported Editor Components
| Component | Use Case | Example |
|---|---|---|
lightning-input | Text, number, date, email, etc. | "type": "text" |
lightning-combobox | Dropdown selection | With options array |
lightning-checkbox | Boolean toggle | Single checkbox |
lightning-checkbox-group | Multiple selections | Array of checkboxes |
lightning-radio-group | Single selection from options | Radio buttons |
lightning-textarea | Multi-line text | Long descriptions |
lightning-file-upload | File attachment | Document upload |
Conditional Fields
{
"fields": [
{
"name": "hasDiscount",
"component": "lightning-checkbox",
"attributes": {
"label": "Apply Discount?"
}
},
{
"name": "discountCode",
"component": "lightning-input",
"attributes": {
"label": "Discount Code"
},
"conditions": {
"hasDiscount": true
}
}
]
}---
Renderer Configuration (renderer.json)
The renderer defines how output is displayed to users:
Basic Renderer
{
"component": "lightning-card",
"attributes": {
"title": "Order Details",
"iconName": "standard:orders"
},
"body": [
{
"component": "lightning-layout",
"attributes": {
"multipleRows": true
},
"body": [
{
"component": "lightning-layout-item",
"attributes": {
"size": "6"
},
"body": [
{
"component": "lightning-formatted-text",
"attributes": {
"value": "Order #${orderId}"
}
}
]
},
{
"component": "lightning-layout-item",
"attributes": {
"size": "6"
},
"body": [
{
"component": "lightning-badge",
"attributes": {
"label": "${orderStatus}"
}
}
]
}
]
},
{
"component": "lightning-formatted-number",
"attributes": {
"value": "${totalAmount}",
"style": "currency",
"currencyCode": "USD"
}
}
]
}Supported Renderer Components
| Component | Use Case |
|---|---|
lightning-card | Container with header |
lightning-layout | Grid layout |
lightning-formatted-text | Display text |
lightning-formatted-number | Currency, percent |
lightning-formatted-date-time | Date display |
lightning-badge | Status indicators |
lightning-icon | Icons |
lightning-button | Actions |
lightning-datatable | Tabular data |
lightning-progress-bar | Progress display |
List Rendering
For array data:
{
"component": "lightning-datatable",
"attributes": {
"keyField": "productName",
"data": "${items}",
"columns": [
{ "label": "Product", "fieldName": "productName" },
{ "label": "Quantity", "fieldName": "quantity", "type": "number" },
{ "label": "Price", "fieldName": "price", "type": "currency" }
]
}
}---
Complete Example: Customer Address
1. schema.json
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"title": "CustomerAddress",
"properties": {
"street": {
"type": "string",
"title": "Street Address",
"maxLength": 255
},
"city": {
"type": "string",
"title": "City"
},
"state": {
"type": "string",
"title": "State/Province"
},
"postalCode": {
"type": "string",
"title": "Postal Code",
"pattern": "^[0-9]{5}(-[0-9]{4})?$"
},
"country": {
"type": "string",
"title": "Country",
"enum": ["United States", "Canada", "Mexico", "United Kingdom"]
},
"isDefault": {
"type": "boolean",
"title": "Default Address",
"default": false
}
},
"required": ["street", "city", "postalCode", "country"]
}2. editor.json
{
"layout": "vertical",
"fields": [
{
"name": "street",
"component": "lightning-textarea",
"attributes": {
"label": "Street Address",
"placeholder": "Enter your street address",
"required": true,
"maxLength": 255
}
},
{
"name": "city",
"component": "lightning-input",
"attributes": {
"type": "text",
"label": "City",
"required": true
}
},
{
"name": "state",
"component": "lightning-input",
"attributes": {
"type": "text",
"label": "State/Province"
}
},
{
"name": "postalCode",
"component": "lightning-input",
"attributes": {
"type": "text",
"label": "Postal Code",
"required": true,
"pattern": "[0-9]{5}(-[0-9]{4})?"
}
},
{
"name": "country",
"component": "lightning-combobox",
"attributes": {
"label": "Country",
"required": true,
"options": [
{ "label": "United States", "value": "United States" },
{ "label": "Canada", "value": "Canada" },
{ "label": "Mexico", "value": "Mexico" },
{ "label": "United Kingdom", "value": "United Kingdom" }
]
}
},
{
"name": "isDefault",
"component": "lightning-checkbox",
"attributes": {
"label": "Set as default address"
}
}
],
"submitButton": {
"label": "Save Address",
"variant": "brand"
}
}3. renderer.json
{
"component": "lightning-card",
"attributes": {
"title": "Shipping Address",
"iconName": "standard:address"
},
"body": [
{
"component": "lightning-formatted-address",
"attributes": {
"street": "${street}",
"city": "${city}",
"province": "${state}",
"postalCode": "${postalCode}",
"country": "${country}"
}
},
{
"component": "lightning-badge",
"conditions": {
"isDefault": true
},
"attributes": {
"label": "Default",
"class": "slds-m-top_small"
}
}
]
}4. CustomerAddress.lightningTypeBundle-meta.xml
<?xml version="1.0" encoding="UTF-8"?>
<LightningTypeBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<masterLabel>Customer Address</masterLabel>
<description>Structured address for customer shipping information</description>
</LightningTypeBundle>---
Using Custom Types in Agent Actions
In GenAiFunction Metadata
<GenAiFunction xmlns="http://soap.sforce.com/2006/04/metadata">
<masterLabel>Collect Shipping Address</masterLabel>
<developerName>Collect_Shipping_Address</developerName>
<description>Collects customer shipping address</description>
<invocationTarget>Collect_Address_Flow</invocationTarget>
<invocationTargetType>flow</invocationTargetType>
<capability>
Collect the customer's shipping address when they want to update
their delivery information or place an order.
</capability>
<!-- Output uses custom Lightning Type -->
<genAiFunctionOutputs>
<developerName>shippingAddress</developerName>
<description>The customer's shipping address</description>
<dataType>CustomerAddress</dataType>
<isRequired>true</isRequired>
</genAiFunctionOutputs>
</GenAiFunction>In Agent Script
topic address_management:
label: "Address Management"
description: "Manages customer addresses"
actions:
Collect_Shipping_Address:
description: "Collect the customer's shipping address"
outputs:
# Reference custom Lightning Type as output
shippingAddress: CustomerAddress
description: "Customer shipping address"
is_used_by_planner: True
is_displayable: True
target: "flow://Collect_Address_Flow"
reasoning:
instructions: ->
| When the user wants to update their shipping address,
| use the Collect_Shipping_Address action.
| The custom UI will collect the address details.
actions:
collect: @actions.Collect_Shipping_AddressWhy `is_displayable: True` is correct in this example
This custom Lightning-type output is meant for direct UI rendering, so is_displayable: True is intentional here.>
Useis_displayable: Falseinstead when the value should stay hidden from direct customer display and only drive planner behavior. For prompt-template outputs specifically, the safer default isis_displayable: False+is_used_by_planner: True.
---
Best Practices
┌─────────────────────────────────────────────────────────────────────────────┐
│ CUSTOM LIGHTNING TYPES BEST PRACTICES │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ SCHEMA DESIGN │
│ ───────────────────────────────────────────────────────────────────────── │
│ ✅ Keep schemas focused on a single concept │
│ ✅ Use meaningful property names │
│ ✅ Add validation constraints (min, max, pattern) │
│ ✅ Mark required fields explicitly │
│ ❌ Don't nest objects more than 2 levels deep │
│ ❌ Don't create overly complex schemas │
│ │
│ EDITOR UX │
│ ───────────────────────────────────────────────────────────────────────── │
│ ✅ Group related fields together │
│ ✅ Use appropriate input types (date picker for dates) │
│ ✅ Provide clear labels and placeholders │
│ ✅ Use conditional fields to reduce complexity │
│ ❌ Don't require too many fields at once │
│ ❌ Don't hide critical fields behind conditions │
│ │
│ RENDERER UX │
│ ───────────────────────────────────────────────────────────────────────── │
│ ✅ Highlight the most important information │
│ ✅ Use visual hierarchy (cards, badges, icons) │
│ ✅ Format data appropriately (currency, dates) │
│ ✅ Keep displays scannable and concise │
│ ❌ Don't display raw data without formatting │
│ ❌ Don't crowd too much information │
│ │
│ DEPLOYMENT │
│ ───────────────────────────────────────────────────────────────────────── │
│ ✅ Deploy LightningTypeBundle before GenAiFunction │
│ ✅ Test with Enhanced Chat V2 enabled │
│ ✅ Validate JSON files before deployment │
│ ❌ Don't reference undefined custom types │
│ │
└─────────────────────────────────────────────────────────────────────────────┘---
Deployment
package.xml Entry
<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
<types>
<members>*</members>
<name>LightningTypeBundle</name>
</types>
<version>64.0</version>
</Package>Deploy Command
# Deploy specific type
sf project deploy start -m "LightningTypeBundle:CustomerAddress"
# Deploy all types
sf project deploy start -d force-app/main/default/lightningTypeBundles/
# Deploy with dependencies (type + action)
sf project deploy start -m "LightningTypeBundle:CustomerAddress,GenAiFunction:Collect_Shipping_Address"Deployment Order
1. LightningTypeBundle - Deploy custom types first 2. GenAiFunction - Deploy actions that reference the types 3. AiAuthoringBundle - Deploy agent that uses the actions
---
Troubleshooting
| Issue | Cause | Solution |
|---|---|---|
| UI not rendering | Enhanced Chat V2 not enabled | Enable Enhanced Chat V2 in Setup |
| "Type not found" | Custom type not deployed | Deploy LightningTypeBundle first |
| Schema validation error | Invalid JSON Schema | Validate against JSON Schema draft-07 |
| Editor fields missing | Incorrect field names | Match name in editor.json to schema properties |
| Renderer empty | Variable syntax error | Use ${propertyName} for value interpolation |
---
Related Documentation
---
Source
Reference: How to Use Custom Lightning Types in Agentforce Service Agents - Salesforce Diaries
<!-- Parent: sf-ai-agentforce/SKILL.md -->
GenAiPromptTemplate Reference
Use this guide when creating or reviewing Prompt Builder templates as source metadata.
Prompt Template vs GenAiPromptTemplate
| Concept | Meaning |
|---|---|
| Prompt Template | Plain-English / UI term in Prompt Builder |
GenAiPromptTemplate | Current Metadata API type used in source control and deploy/retrieve workflows |
Use the current source shape
| Element | Current guidance |
|---|---|
| Metadata type | GenAiPromptTemplate |
| Directory | genAiPromptTemplates/ |
| File suffix | .genAiPromptTemplate-meta.xml |
| Content container | templateVersions |
| Content field | content |
| Status | publish the version used by downstream actions |
| Flexible type | einstein_gpt__flex |
Core template structure
<?xml version="1.0" encoding="UTF-8"?>
<GenAiPromptTemplate xmlns="http://soap.sforce.com/2006/04/metadata">
<developerName>Account_Briefing_Template</developerName>
<masterLabel>Account Briefing Template</masterLabel>
<type>einstein_gpt__flex</type>
<templateVersions>
<content>
Summarize {!$Input:TargetAccount.Name} using the notes below.
Notes:
{!$Input:AdditionalContext}
</content>
<inputs>
<apiName>TargetAccount</apiName>
<definition>SOBJECT://Account</definition>
<masterLabel>Target Account</masterLabel>
<referenceName>Input:TargetAccount</referenceName>
<required>true</required>
</inputs>
<inputs>
<apiName>AdditionalContext</apiName>
<definition>primitive://String</definition>
<masterLabel>Additional Context</masterLabel>
<referenceName>Input:AdditionalContext</referenceName>
<required>false</required>
</inputs>
<primaryModel>sfdc_ai__DefaultAnthropic</primaryModel>
<status>Published</status>
</templateVersions>
</GenAiPromptTemplate>Input rules
Flex template limit
Flex templates should be designed around a maximum of 5 inputs.
When a request appears to need more than 5, prefer one of these strategies:
- consolidate related text into a single structured context input
- pass an SObject input and read multiple fields from it
- move pre-aggregation into Flow or Apex before invoking the template
- reduce optional inputs to the minimum needed for quality output
Common input definitions
| Need | Definition example |
|---|---|
| Record input | SOBJECT://Account |
| Free-text input | primitive://String |
Merge-field guidance
Current prompt references
Use current-style input references in prompt content:
{!$Input:TargetAccount}{!$Input:TargetAccount.Name}{!$Input:AdditionalContext}
Common mistake
Do not default to older examples that only use {!variableName} when you are authoring current GenAiPromptTemplate metadata.
Deployment order
Deploy dependencies before the template or agent that depends on them: 1. objects / fields 2. Apex 3. Flows 4. GenAiPromptTemplate 5. GenAiFunction / GenAiPlugin 6. publish / activate the agent
Validation checklist
- [ ] Metadata uses
GenAiPromptTemplate - [ ] Template file lives under
genAiPromptTemplates/ - [ ] Flex template has 5 or fewer inputs
- [ ] Each input has the correct
definition - [ ] Prompt content references current input names with
{!$Input:...} - [ ] Template version used by downstream actions is published
- [ ] Supporting Flow / Apex / object dependencies already exist in the org
Common mistakes
| Mistake | Safer approach |
|---|---|
Using PromptTemplate as the metadata type | Use GenAiPromptTemplate |
Storing templates under promptTemplates/ | Use genAiPromptTemplates/ |
| Designing flex templates with too many inputs | Consolidate to 5 or fewer |
| Wiring an action to a Draft template | Publish the template version first |
| Treating prompt templates like deterministic logic | Move strict business logic to Flow or Apex |
Related references
- prompt-templates.md
- metadata-reference.md
- builder-workflow.md
Agentforce Metadata Reference
Use this document for the metadata-heavy parts of sf-ai-agentforce that do not need to live in the activation path.
GenAiFunction
A GenAiFunction registers one callable agent action.
Common target types
flowapexprompt
Validate before deploy
- target exists
- target is active / deployable
- input names match the target contract
- output names match the target contract
- capability text explains when the planner should use the action
GenAiPlugin
A GenAiPlugin groups related GenAiFunction records.
Use it when:
- multiple functions belong to one business domain
- you want cleaner packaging for Builder-based actions
Prompt Builder template integration
Use Prompt Builder templates when:
- the output is generated content
- the user needs a draft, summary, rewrite, or recommendation
Do not use prompt templates as a substitute for deterministic business logic.
Current source format
For modern metadata work, use:
- metadata type:
GenAiPromptTemplate - folder:
genAiPromptTemplates/ - suffix:
.genAiPromptTemplate-meta.xml - versioned content under
templateVersions
High-signal rules
- Treat Prompt Template as the UI term and `GenAiPromptTemplate` as the metadata type.
- Flex templates should stay within the 5-input maximum.
- Prompt content should reference inputs with the current merge-field shape such as
{!$Input:TargetRecord}. - Publish / activate the template version before wiring downstream actions that depend on it.
Models API
Use aiplatform.ModelsAPI when:
- the requirement is Apex-driven AI logic
- the work belongs in custom server-side orchestration
- Builder-only action patterns are insufficient
Custom Lightning Types
Use LightningTypeBundle when actions need:
- structured input collection
- richer output rendering
- UI-driven agent interaction patterns
Deployment rule of thumb
Supporting metadata first:
- objects / fields
- Apex
- Flows
GenAiPromptTemplate/GenAiFunction/GenAiPlugin- then publish the agent
Deep references
- Builder workflow: builder-workflow.md
- GenAI prompt metadata: genaiprompttemplate.md
- Prompt terminology: prompt-templates.md
- Models API: models-api.md
- Custom Lightning types: custom-lightning-types.md
- CLI lifecycle: cli-commands.md
<!-- Parent: sf-ai-agentforce/SKILL.md --> <!-- TIER: 3 | DETAILED REFERENCE --> <!-- Read after: SKILL.md --> <!-- Purpose: Native AI API (aiplatform.ModelsAPI) patterns for Apex -->
Agentforce Models API
Native AI generation in Apex using aiplatform.ModelsAPI namespaceOverview
The Agentforce Models API enables native LLM access directly from Apex code without external HTTP callouts. This API is part of the aiplatform namespace and provides access to Salesforce-managed AI models.
┌─────────────────────────────────────────────────────────────────────────────┐
│ MODELS API ARCHITECTURE │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Your Apex Code │
│ │ │
│ ▼ │
│ aiplatform.ModelsAPI.createGenerations() │
│ │ │
│ ▼ │
│ Salesforce AI Gateway │
│ │ │
│ ▼ │
│ Foundation Model (GPT-4o Mini, etc.) │
│ │
└─────────────────────────────────────────────────────────────────────────────┘---
Prerequisites
API Version Requirement
Minimum API v61.0+ (Spring '24) for Models API support.
# Verify org API version
sf org display --target-org [alias] --json | jq '.result.apiVersion'Einstein Generative AI Setup
1. Einstein Generative AI must be enabled in Setup 2. User must have Einstein Generative AI User permission set 3. Organization must have Einstein AI entitlement
Setup → Einstein Setup → Turn on Einstein
Setup → Permission Sets → Einstein Generative AI User → Assign to users---
Available Models
| Model Name | Description | Use Case |
|---|---|---|
sfdc_ai__DefaultOpenAIGPT4OmniMini | GPT-4o Mini | Cost-effective general tasks |
sfdc_ai__DefaultOpenAIGPT4Omni | GPT-4o | Complex reasoning tasks |
sfdc_ai__DefaultAnthropic | Claude (Anthropic) | Nuanced understanding |
sfdc_ai__DefaultGoogleGemini | Google Gemini | Multimodal tasks |
Note: Available models depend on your Salesforce edition and Einstein entitlements.
---
Basic Usage
Simple Text Generation
public class ModelsApiExample {
public static String generateText(String prompt) {
// Create the request
aiplatform.ModelsAPI.createGenerations_Request request =
new aiplatform.ModelsAPI.createGenerations_Request();
// Set the model
request.modelName = 'sfdc_ai__DefaultOpenAIGPT4OmniMini';
// Create the generation input
aiplatform.ModelsAPI_GenerationRequest genRequest =
new aiplatform.ModelsAPI_GenerationRequest();
genRequest.prompt = prompt;
request.body = genRequest;
// Call the API
aiplatform.ModelsAPI.createGenerations_Response response =
aiplatform.ModelsAPI.createGenerations(request);
// Extract the generated text
if (response.Code200 != null &&
response.Code200.generations != null &&
!response.Code200.generations.isEmpty()) {
return response.Code200.generations[0].text;
}
return null;
}
}---
Queueable Integration
Use Queueable for async AI processing with record context:
❌ BAD: Synchronous AI Calls in Triggers
// DON'T DO THIS - blocks transaction, hits limits
trigger CaseTrigger on Case (after insert) {
for (Case c : Trigger.new) {
String summary = ModelsApiExample.generateText(c.Description);
// This will fail or timeout
}
}✅ GOOD: Queueable for Async AI Processing
/**
* @description Queueable job for generating AI summaries
* @implements Database.AllowsCallouts - Required for API calls
*/
public with sharing class CaseSummaryQueueable implements Queueable, Database.AllowsCallouts {
private List<Id> caseIds;
public CaseSummaryQueueable(List<Id> caseIds) {
this.caseIds = caseIds;
}
public void execute(QueueableContext context) {
// Query cases
List<Case> cases = [
SELECT Id, Subject, Description
FROM Case
WHERE Id IN :caseIds
WITH USER_MODE
];
List<Case> toUpdate = new List<Case>();
for (Case c : cases) {
try {
// Generate summary using Models API
String summary = generateCaseSummary(c);
if (String.isNotBlank(summary)) {
c.AI_Summary__c = summary;
toUpdate.add(c);
}
} catch (Exception e) {
System.debug(LoggingLevel.ERROR,
'AI Summary Error for Case ' + c.Id + ': ' + e.getMessage());
}
}
// Update records
if (!toUpdate.isEmpty()) {
update toUpdate;
}
}
private String generateCaseSummary(Case c) {
String prompt = 'Summarize this customer support case in 2-3 sentences:\n\n' +
'Subject: ' + c.Subject + '\n' +
'Description: ' + c.Description;
aiplatform.ModelsAPI.createGenerations_Request request =
new aiplatform.ModelsAPI.createGenerations_Request();
request.modelName = 'sfdc_ai__DefaultOpenAIGPT4OmniMini';
aiplatform.ModelsAPI_GenerationRequest genRequest =
new aiplatform.ModelsAPI_GenerationRequest();
genRequest.prompt = prompt;
request.body = genRequest;
aiplatform.ModelsAPI.createGenerations_Response response =
aiplatform.ModelsAPI.createGenerations(request);
if (response.Code200 != null &&
response.Code200.generations != null &&
!response.Code200.generations.isEmpty()) {
return response.Code200.generations[0].text;
}
return null;
}
}Invoking from Trigger
trigger CaseTrigger on Case (after insert) {
List<Id> newCaseIds = new List<Id>();
for (Case c : Trigger.new) {
if (String.isNotBlank(c.Description)) {
newCaseIds.add(c.Id);
}
}
if (!newCaseIds.isEmpty()) {
// Enqueue async processing - non-blocking
System.enqueueJob(new CaseSummaryQueueable(newCaseIds));
}
}---
Batch Class Integration
For bulk AI processing, use Batch Apex:
/**
* @description Batch job for generating AI content on records
* @implements Database.AllowsCallouts, Database.Stateful
*/
public with sharing class OpportunitySummaryBatch
implements Database.Batchable<sObject>, Database.AllowsCallouts, Database.Stateful {
// Track statistics across batches
private Integer successCount = 0;
private Integer errorCount = 0;
public Database.QueryLocator start(Database.BatchableContext bc) {
// Query records needing AI summary
return Database.getQueryLocator([
SELECT Id, Name, Description, StageName, Amount
FROM Opportunity
WHERE AI_Summary__c = null
AND Description != null
ORDER BY CreatedDate DESC
]);
}
public void execute(Database.BatchableContext bc, List<Opportunity> scope) {
List<Opportunity> toUpdate = new List<Opportunity>();
for (Opportunity opp : scope) {
try {
String summary = generateOpportunitySummary(opp);
if (String.isNotBlank(summary)) {
opp.AI_Summary__c = summary;
toUpdate.add(opp);
successCount++;
}
} catch (Exception e) {
errorCount++;
System.debug(LoggingLevel.ERROR,
'AI Summary Error for Opp ' + opp.Id + ': ' + e.getMessage());
}
}
if (!toUpdate.isEmpty()) {
update toUpdate;
}
}
public void finish(Database.BatchableContext bc) {
System.debug('Batch Complete. Success: ' + successCount + ', Errors: ' + errorCount);
// Optional: Send completion notification
// Messaging.SingleEmailMessage email = ...
}
private String generateOpportunitySummary(Opportunity opp) {
String prompt = 'Create a brief sales summary for this opportunity:\n\n' +
'Name: ' + opp.Name + '\n' +
'Stage: ' + opp.StageName + '\n' +
'Amount: $' + opp.Amount + '\n' +
'Description: ' + opp.Description + '\n\n' +
'Summarize in 2-3 sentences focusing on key points.';
// Use same API pattern as Queueable
aiplatform.ModelsAPI.createGenerations_Request request =
new aiplatform.ModelsAPI.createGenerations_Request();
request.modelName = 'sfdc_ai__DefaultOpenAIGPT4OmniMini';
aiplatform.ModelsAPI_GenerationRequest genRequest =
new aiplatform.ModelsAPI_GenerationRequest();
genRequest.prompt = prompt;
request.body = genRequest;
aiplatform.ModelsAPI.createGenerations_Response response =
aiplatform.ModelsAPI.createGenerations(request);
if (response.Code200 != null &&
response.Code200.generations != null &&
!response.Code200.generations.isEmpty()) {
return response.Code200.generations[0].text;
}
return null;
}
}Batch Size Considerations
| Batch Size | AI Calls/Batch | Recommended For |
|---|---|---|
| 1-5 | 1-5 | Complex prompts, detailed output |
| 10-20 | 10-20 | Standard summaries |
| 50+ | Avoid | Risk of timeout, use smaller batches |
// Execute with smaller batch size for AI processing
Database.executeBatch(new OpportunitySummaryBatch(), 10);---
Chatter Integration
Post AI-generated content to Chatter:
public with sharing class ChatterAIService {
/**
* @description Generate and post AI insight to Chatter
* @param recordId The record to analyze
* @param feedMessage Additional context for the post
*/
public static void postAIInsight(Id recordId, String feedMessage) {
// Query record context
Account acc = [
SELECT Name, Industry, AnnualRevenue, Description
FROM Account
WHERE Id = :recordId
LIMIT 1
];
// Generate insight using Models API
String prompt = 'Analyze this account and provide 3 key business insights:\n\n' +
'Company: ' + acc.Name + '\n' +
'Industry: ' + acc.Industry + '\n' +
'Revenue: $' + acc.AnnualRevenue + '\n' +
'Description: ' + acc.Description + '\n\n' +
'Format as numbered bullet points.';
String insight = generateText(prompt);
if (String.isNotBlank(insight)) {
// Create Chatter post
ConnectApi.FeedItemInput feedInput = new ConnectApi.FeedItemInput();
ConnectApi.MessageBodyInput messageInput = new ConnectApi.MessageBodyInput();
ConnectApi.TextSegmentInput textSegment = new ConnectApi.TextSegmentInput();
textSegment.text = '🤖 AI Account Insight:\n\n' + insight;
messageInput.messageSegments = new List<ConnectApi.MessageSegmentInput>{ textSegment };
feedInput.body = messageInput;
feedInput.feedElementType = ConnectApi.FeedElementType.FeedItem;
feedInput.subjectId = recordId;
ConnectApi.ChatterFeeds.postFeedElement(
Network.getNetworkId(),
feedInput
);
}
}
private static String generateText(String prompt) {
aiplatform.ModelsAPI.createGenerations_Request request =
new aiplatform.ModelsAPI.createGenerations_Request();
request.modelName = 'sfdc_ai__DefaultOpenAIGPT4OmniMini';
aiplatform.ModelsAPI_GenerationRequest genRequest =
new aiplatform.ModelsAPI_GenerationRequest();
genRequest.prompt = prompt;
request.body = genRequest;
aiplatform.ModelsAPI.createGenerations_Response response =
aiplatform.ModelsAPI.createGenerations(request);
if (response.Code200 != null &&
response.Code200.generations != null &&
!response.Code200.generations.isEmpty()) {
return response.Code200.generations[0].text;
}
return null;
}
}---
Governor Limits & Best Practices
Limits to Consider
| Limit | Value | Mitigation |
|---|---|---|
| Callout time | 120s total | Use smaller batches, Queueable chaining |
| Callouts per transaction | 100 | Batch records, use async |
| CPU time | 10s sync, 60s async | Use Queueable/Batch |
| Heap size | 6MB sync, 12MB async | Limit prompt/response size |
Best Practices
┌─────────────────────────────────────────────────────────────────────────────┐
│ MODELS API BEST PRACTICES │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ARCHITECTURE │
│ ───────────────────────────────────────────────────────────────────────── │
│ ✅ Use Queueable for single-record async processing │
│ ✅ Use Batch for bulk processing (scope size 10-20) │
│ ✅ Use Platform Events for notification when AI completes │
│ ✅ Cache common AI responses if possible │
│ ❌ Don't call Models API synchronously in triggers │
│ ❌ Don't process unbounded record sets │
│ │
│ PROMPTS │
│ ───────────────────────────────────────────────────────────────────────── │
│ ✅ Be specific about expected output format │
│ ✅ Set length constraints ("summarize in 2 sentences") │
│ ✅ Include context needed for accurate responses │
│ ❌ Don't include PII in prompts unless necessary │
│ ❌ Don't rely on AI for compliance-critical decisions │
│ │
│ ERROR HANDLING │
│ ───────────────────────────────────────────────────────────────────────── │
│ ✅ Wrap API calls in try-catch │
│ ✅ Log errors with context for debugging │
│ ✅ Implement retry logic for transient failures │
│ ✅ Check response.Code200 before accessing results │
│ ❌ Don't assume AI responses are always successful │
│ │
└─────────────────────────────────────────────────────────────────────────────┘---
Common Patterns
Pattern 1: Service Layer Abstraction
public with sharing class AIGenerationService {
private static final String DEFAULT_MODEL = 'sfdc_ai__DefaultOpenAIGPT4OmniMini';
/**
* @description Generate text with standard configuration
*/
public static String generate(String prompt) {
return generate(prompt, DEFAULT_MODEL);
}
/**
* @description Generate text with specific model
*/
public static String generate(String prompt, String modelName) {
try {
aiplatform.ModelsAPI.createGenerations_Request request =
new aiplatform.ModelsAPI.createGenerations_Request();
request.modelName = modelName;
aiplatform.ModelsAPI_GenerationRequest genRequest =
new aiplatform.ModelsAPI_GenerationRequest();
genRequest.prompt = prompt;
request.body = genRequest;
aiplatform.ModelsAPI.createGenerations_Response response =
aiplatform.ModelsAPI.createGenerations(request);
if (response.Code200 != null &&
response.Code200.generations != null &&
!response.Code200.generations.isEmpty()) {
return response.Code200.generations[0].text;
}
} catch (Exception e) {
System.debug(LoggingLevel.ERROR, 'AI Generation Error: ' + e.getMessage());
}
return null;
}
}Pattern 2: Notify Completion via Platform Events
// Platform Event: AI_Generation_Complete__e
// Fields: Record_Id__c (Text), Status__c (Text), Summary__c (Long Text)
public with sharing class AIQueueableWithNotification
implements Queueable, Database.AllowsCallouts {
private Id recordId;
public AIQueueableWithNotification(Id recordId) {
this.recordId = recordId;
}
public void execute(QueueableContext context) {
String summary;
String status = 'Success';
try {
// Generate AI content
summary = AIGenerationService.generate('...');
} catch (Exception e) {
status = 'Error: ' + e.getMessage();
}
// Publish completion event
AI_Generation_Complete__e event = new AI_Generation_Complete__e();
event.Record_Id__c = recordId;
event.Status__c = status;
event.Summary__c = summary;
EventBus.publish(event);
}
}Pattern 3: LWC Subscribes to Completion
// In your LWC controller
import { subscribe, unsubscribe, onError } from 'lightning/empApi';
connectedCallback() {
this.subscribeToAICompletion();
}
subscribeToAICompletion() {
const channelName = '/event/AI_Generation_Complete__e';
subscribe(channelName, -1, (message) => {
const payload = message.data.payload;
if (payload.Record_Id__c === this.recordId) {
this.aiSummary = payload.Summary__c;
this.isLoading = false;
}
}).then((response) => {
this.subscription = response;
});
}---
Troubleshooting
| Issue | Cause | Solution |
|---|---|---|
| "Model not found" | Invalid model name | Use exact name: sfdc_ai__DefaultOpenAIGPT4OmniMini |
| "Access denied" | Missing permission | Assign Einstein Generative AI User permission set |
| "Callout not allowed" | Sync context restriction | Use Queueable with Database.AllowsCallouts |
| Timeout errors | Large prompt/response | Reduce prompt size, use batch with smaller scope |
| Empty response | Null check failed | Always validate response.Code200 and generations |
---
Related Documentation
- Prompt Templates - Using AI via metadata
- Salesforce AI Documentation
---
Source
Reference: Agentforce API Generating Case Summaries with Apex Queueable - Salesforce Diaries
<!-- Parent: sf-ai-agentforce/SKILL.md -->
Prompt Templates in Agentforce
Terminology guide: Salesforce users commonly say Prompt Template in the UI, while modern source-driven metadata work uses `GenAiPromptTemplate`.
Short answer
| Term | Meaning |
|---|---|
| Prompt Template | Plain-English / UI term used in Prompt Builder |
| `GenAiPromptTemplate` | Current Metadata API type for Prompt Builder templates |
When to use prompt templates
Use a Prompt Builder template when the action should generate content such as:
- summaries
- drafts
- rewrites
- recommendations
Do not use a prompt template when the requirement is deterministic business logic or strict transactional processing.
Current metadata direction
For source-controlled work, prefer:
genAiPromptTemplates/.genAiPromptTemplate-meta.xml- versioned content inside
templateVersions
Flex templates should be designed around the 5-input maximum.
Current merge-field style
Prompt content should reference current inputs with shapes like:
{!$Input:TargetRecord}{!$Input:AdditionalContext}{!$Input:TargetRecord.Name}
Read next
- genaiprompttemplate.md — detailed metadata guide
- metadata-reference.md — Agentforce metadata overview
- builder-workflow.md — when templates fit in the Builder lifecycle
Agentforce Builder Scoring Rubric
100-point rubric
| Category | Points | What good looks like |
|---|---|---|
| Agent configuration | 20 | Clear system guidance, correct agent user, usable welcome/error handling |
| Topic and action design | 25 | Strong routing descriptions, scoped topics, actions mapped to the right topic |
| Metadata quality | 20 | Valid GenAiFunction / GenAiPlugin structure, correct target types, clean I/O definitions |
| Integration patterns | 15 | Dependencies sequenced correctly, cross-skill orchestration used appropriately |
| Prompt / AI usage | 10 | GenAiPromptTemplate or Models API used only where it adds value |
| Deployment readiness | 10 | Validation complete, dependencies deployed, publish/activate flow understood |
Thresholds
| Score | Meaning | Recommendation |
|---|---|---|
| 90–100 | Excellent | Ready to deploy |
| 80–89 | Very good | Minor cleanup only |
| 70–79 | Acceptable | Review before deploy |
| 60–69 | Needs work | Address issues before deploy |
| < 60 | Blocking | Do not deploy |
Common downgrades
- vague topic descriptions
- unsupported / undeployed action targets
- missing or mismatched inputs/outputs
- trying to use Builder when Agent Script is the better fit
- forgetting that publish and activate are separate steps