Production-ready examples showing how to combine multiple Agentforce SDK features in real-world scenarios.
These examples demonstrate integrating multiple features together. For single-feature examples, see:
- Conversations for context variables and hidden prechat fields
- Delegates for logger, navigation, and view provider delegates
- Employee Agent Authentication for auth flows
Using All Delegates Together
This example shows how to register logger, navigation, and view provider delegates in a single component with proper cleanup.
import React, { useEffect, useRef } from "react";
import { View, Button, Linking, Alert, StyleSheet } from "react-native";
import {
AgentforceService,
LogLevel,
NavigationRequest,
} from "@salesforce/react-native-agentforce";
export default function AllDelegatesExample() {
const logBufferRef = useRef<Array<{ level: LogLevel; message: string; time: Date }>>([]);
useEffect(() => {
// 1. Logger Delegate
AgentforceService.setLoggerDelegate({
onLog(level, message, error) {
logBufferRef.current.push({ level, message, time: new Date() });
// Keep buffer manageable
if (logBufferRef.current.length > 500) {
logBufferRef.current = logBufferRef.current.slice(-250);
}
// Forward errors to your analytics
if (level === "error") {
console.error(`[Agentforce ERROR] ${message}`, error);
}
},
});
// 2. Navigation Delegate
AgentforceService.setNavigationDelegate({
onNavigate(request: NavigationRequest) {
console.log("Navigation request:", JSON.stringify(request));
switch (request.type) {
case "link":
if (request.uri) {
Linking.openURL(request.uri as string).catch((err) =>
console.error("Failed to open URL:", err),
);
}
break;
case "record":
Alert.alert(
"Record Navigation",
`Open ${request.objectType} record: ${request.recordId}`,
);
break;
case "quickAction":
Alert.alert("Quick Action", `Execute action: ${request.actionName}`);
break;
case "objectHome":
Alert.alert("Object Home", `Navigate to ${request.objectType} list`);
break;
default:
console.log("Unhandled navigation type:", request.type);
}
},
});
// 3. View Provider Delegate (async)
AgentforceService.setViewProviderDelegate({
componentMap: {
"copilot/richText": "CustomAgentforceView",
},
}).catch((err) => console.error("View provider registration failed:", err));
// Cleanup on unmount
return () => {
AgentforceService.clearLoggerDelegate();
AgentforceService.clearNavigationDelegate();
AgentforceService.clearViewProviderDelegate();
};
}, []);
const handleConfigure = async () => {
try {
await AgentforceService.configure({
type: "service",
serviceApiURL: "https://mycompany-support.my.salesforce-scrt.com",
organizationId: "00Dxx0000001234EAA",
esDeveloperName: "My_Service_Agent",
featureFlags: {
enableMultiAgent: true,
enableMultiModalInput: false,
enablePDFUpload: false,
enableVoice: false,
enableCustomViewProvider: true,
},
});
Alert.alert("Success", "SDK configured");
} catch (error) {
Alert.alert("Config Error", String(error));
}
};
const handleLaunch = async () => {
try {
await AgentforceService.launchConversation();
} catch (error) {
Alert.alert("Launch Error", String(error));
}
};
const handleShowLogs = () => {
const recent = logBufferRef.current.slice(-10);
const text = recent.map((l) => `[${l.level}] ${l.message}`).join("\n");
Alert.alert(`Last ${recent.length} Logs`, text || "No logs yet");
};
return (
<View style={styles.container}>
<Button title="1. Configure" onPress={handleConfigure} />
<View style={styles.spacer} />
<Button title="2. Launch Conversation" onPress={handleLaunch} />
<View style={styles.spacer} />
<Button title="Show Recent Logs" onPress={handleShowLogs} />
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, justifyContent: "center", padding: 24 },
spacer: { height: 16 },
});
Feature Flag Management
This example shows a complete settings screen for managing feature flags with UI toggles.
import React, { useState, useEffect } from "react";
import { View, Text, Switch, Button, Alert, StyleSheet } from "react-native";
import { AgentforceService, FeatureFlags } from "@salesforce/react-native-agentforce";
const DEFAULT_FLAGS: FeatureFlags = {
enableMultiAgent: true,
enableMultiModalInput: false,
enablePDFUpload: false,
enableVoice: false,
enableCustomViewProvider: false,
};
export default function FeatureFlagManager() {
const [flags, setFlags] = useState<FeatureFlags>(DEFAULT_FLAGS);
const [dirty, setDirty] = useState(false);
useEffect(() => {
loadFlags();
}, []);
const loadFlags = async () => {
const stored = await AgentforceService.getFeatureFlags();
setFlags(stored);
setDirty(false);
};
const toggleFlag = (key: keyof FeatureFlags) => {
setFlags((prev) => ({ ...prev, [key]: !prev[key] }));
setDirty(true);
};
const handleSave = async () => {
await AgentforceService.setFeatureFlags(flags);
setDirty(false);
Alert.alert(
"Flags Saved",
"Feature flags have been saved. They will take effect the next time configure() is called.",
);
};
const handleSaveAndReconfigure = async () => {
try {
await AgentforceService.setFeatureFlags(flags);
const info = await AgentforceService.getConfigurationInfo();
if (info.configured && info.mode === "service") {
const config = await AgentforceService.getConfiguration();
if (config) {
await AgentforceService.configure({
...config,
featureFlags: flags,
});
Alert.alert("Applied", "Feature flags saved and applied immediately.");
}
} else if (info.configured && info.mode === "employee") {
Alert.alert("Saved", "Feature flags saved. Restart the conversation to apply.");
} else {
Alert.alert("Saved", "Flags saved. Configure the SDK to apply them.");
}
setDirty(false);
} catch (error) {
Alert.alert("Error", String(error));
}
};
const flagEntries: Array<{ key: keyof FeatureFlags; label: string; description: string }> = [
{
key: "enableMultiAgent",
label: "Multi-Agent",
description: "Allow SDK to pick from available agents when no agentId is set",
},
{
key: "enableMultiModalInput",
label: "Multi-Modal Input",
description: "Enable camera and image attachments",
},
{
key: "enablePDFUpload",
label: "PDF Upload",
description: "Enable PDF file upload in conversations",
},
{
key: "enableVoice",
label: "Voice Input",
description: "Enable microphone for voice input",
},
{
key: "enableCustomViewProvider",
label: "Custom View Provider",
description: "Enable custom React Native views for SDK output",
},
];
return (
<View style={styles.container}>
<Text style={styles.title}>Feature Flags</Text>
{flagEntries.map(({ key, label, description }) => (
<View key={key} style={styles.flagRow}>
<View style={styles.flagInfo}>
<Text style={styles.flagLabel}>{label}</Text>
<Text style={styles.flagDesc}>{description}</Text>
</View>
<Switch value={flags[key]} onValueChange={() => toggleFlag(key)} />
</View>
))}
{dirty && (
<View style={styles.actions}>
<Button title="Save (Apply on Next Configure)" onPress={handleSave} />
<View style={styles.spacer} />
<Button title="Save and Apply Now" onPress={handleSaveAndReconfigure} />
</View>
)}
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, padding: 16 },
title: { fontSize: 22, fontWeight: "bold", marginBottom: 16 },
flagRow: {
flexDirection: "row",
alignItems: "center",
paddingVertical: 12,
borderBottomWidth: 1,
borderBottomColor: "#eee",
},
flagInfo: { flex: 1 },
flagLabel: { fontSize: 16, fontWeight: "600" },
flagDesc: { fontSize: 12, color: "#666", marginTop: 2 },
actions: { marginTop: 24 },
spacer: { height: 12 },
});
Employee Agent with Mobile SDK Auth
Full Employee Agent integration using the Salesforce Mobile SDK for OAuth authentication, with state management for login/logout flows.
import React, { useState, useEffect } from "react";
import { View, Button, Text, Alert, ActivityIndicator, StyleSheet } from "react-native";
import {
AgentforceService,
isEmployeeAgentAuthSupported,
hasEmployeeAgentSession,
loginForEmployeeAgent,
logoutEmployeeAgent,
getEmployeeAgentCredentials,
AuthCredentials,
} from "@salesforce/react-native-agentforce";
const AGENT_ID = "0Xxxx0000001234AAA";
export default function EmployeeAgentWithAuth() {
const [authSupported, setAuthSupported] = useState(false);
const [loggedIn, setLoggedIn] = useState(false);
const [loading, setLoading] = useState(true);
const [credentials, setCredentials] = useState<AuthCredentials | null>(null);
useEffect(() => {
checkAuthState();
}, []);
const checkAuthState = async () => {
setLoading(true);
try {
const supported = await isEmployeeAgentAuthSupported();
setAuthSupported(supported);
if (supported) {
const session = await hasEmployeeAgentSession();
setLoggedIn(session);
if (session) {
const creds = await getEmployeeAgentCredentials();
setCredentials(creds);
}
}
} catch (error) {
console.error("Auth check failed:", error);
} finally {
setLoading(false);
}
};
const handleLogin = async () => {
try {
setLoading(true);
const creds = await loginForEmployeeAgent();
setCredentials(creds);
setLoggedIn(true);
} catch (error) {
Alert.alert("Login Failed", String(error));
} finally {
setLoading(false);
}
};
const handleLogout = async () => {
try {
await AgentforceService.closeConversation();
await logoutEmployeeAgent();
await AgentforceService.resetSettings();
setCredentials(null);
setLoggedIn(false);
} catch (error) {
Alert.alert("Logout Failed", String(error));
}
};
const handleLaunch = async () => {
if (!credentials) {
Alert.alert("Error", "Not logged in");
return;
}
try {
await AgentforceService.configure({
type: "employee",
instanceUrl: credentials.instanceUrl,
organizationId: credentials.organizationId,
userId: credentials.userId,
agentId: AGENT_ID,
accessToken: credentials.accessToken,
});
await AgentforceService.launchConversation();
} catch (error) {
Alert.alert("Launch Failed", String(error));
}
};
if (loading) {
return (
<View style={styles.center}>
<ActivityIndicator size="large" />
</View>
);
}
if (!authSupported) {
return (
<View style={styles.center}>
<Text>Employee Agent auth is not available in this build.</Text>
<Text style={styles.hint}>Include the Mobile SDK to enable it.</Text>
</View>
);
}
return (
<View style={styles.container}>
<Text style={styles.title}>Employee Agent</Text>
{loggedIn ? (
<>
<Text style={styles.status}>Logged in as: {credentials?.userId}</Text>
<Text style={styles.status}>Org: {credentials?.organizationId}</Text>
<View style={styles.buttonRow}>
<Button title="Launch Agent" onPress={handleLaunch} />
<Button title="Logout" onPress={handleLogout} color="red" />
</View>
</>
) : (
<>
<Text style={styles.status}>Not logged in</Text>
<Button title="Login with Salesforce" onPress={handleLogin} />
</>
)}
</View>
);
}
const styles = StyleSheet.create({
center: { flex: 1, justifyContent: "center", alignItems: "center" },
container: { flex: 1, padding: 24, justifyContent: "center" },
title: { fontSize: 24, fontWeight: "bold", marginBottom: 16 },
status: { fontSize: 14, color: "#666", marginBottom: 8 },
hint: { fontSize: 12, color: "#999", marginTop: 8 },
buttonRow: { flexDirection: "row", gap: 16, marginTop: 16 },
});
Complete HomeScreen Integration
A production-ready example combining Service Agent, Employee Agent, all delegates, context variables, and settings management in a single screen.
import React, { useState, useEffect, useRef } from "react";
import {
View,
Text,
Button,
Alert,
ScrollView,
Linking,
StyleSheet,
AppRegistry,
} from "react-native";
import {
AgentforceService,
isEmployeeAgentAuthSupported,
hasEmployeeAgentSession,
loginForEmployeeAgent,
logoutEmployeeAgent,
getEmployeeAgentCredentials,
AuthCredentials,
FeatureFlags,
LogLevel,
NavigationRequest,
ViewProviderComponentData,
} from "@salesforce/react-native-agentforce";
// Custom View Component
function CustomAgentforceView({ componentData }: { componentData: ViewProviderComponentData }) {
return (
<View style={{ padding: 8, backgroundColor: "#e8f0fe", borderRadius: 6, margin: 4 }}>
<Text style={{ fontWeight: "bold" }}>[Custom] {componentData.definition}</Text>
<Text>{JSON.stringify(componentData.properties, null, 2)}</Text>
</View>
);
}
AppRegistry.registerComponent("CustomAgentforceView", () => CustomAgentforceView);
// Config
const SERVICE_CONFIG = {
type: "service" as const,
serviceApiURL: "https://mycompany-support.my.salesforce-scrt.com",
organizationId: "00Dxx0000001234EAA",
esDeveloperName: "My_Service_Agent",
};
export default function HomeScreen() {
// State
const [configured, setConfigured] = useState(false);
const [mode, setMode] = useState<"service" | "employee" | null>(null);
const [authSupported, setAuthSupported] = useState(false);
const [loggedIn, setLoggedIn] = useState(false);
const [credentials, setCredentials] = useState<AuthCredentials | null>(null);
const [flags, setFlags] = useState<FeatureFlags | null>(null);
const logCount = useRef(0);
// Initialize
useEffect(() => {
initializeApp();
return () => {
AgentforceService.clearLoggerDelegate();
AgentforceService.clearNavigationDelegate();
};
}, []);
const initializeApp = async () => {
// Set up delegates first
setupDelegates();
// Check current state
const info = await AgentforceService.getConfigurationInfo();
setConfigured(info.configured);
setMode(info.mode);
const storedFlags = await AgentforceService.getFeatureFlags();
setFlags(storedFlags);
// Check Employee Agent auth
const supported = await isEmployeeAgentAuthSupported();
setAuthSupported(supported);
if (supported) {
const session = await hasEmployeeAgentSession();
setLoggedIn(session);
if (session) {
setCredentials(await getEmployeeAgentCredentials());
}
}
};
const setupDelegates = () => {
// Logger
AgentforceService.setLoggerDelegate({
onLog(level: LogLevel, message: string, error?: string) {
logCount.current++;
if (level === "error") {
console.error(`[SDK ERROR] ${message}`, error || "");
}
},
});
// Navigation
AgentforceService.setNavigationDelegate({
onNavigate(request: NavigationRequest) {
switch (request.type) {
case "link":
if (request.uri) Linking.openURL(request.uri as string);
break;
case "record":
Alert.alert("Record", `${request.objectType}: ${request.recordId}`);
break;
default:
console.log("Navigation:", request.type, request);
}
},
});
// View Provider
AgentforceService.setViewProviderDelegate({
componentMap: {
"copilot/richText": "CustomAgentforceView",
},
}).catch(console.error);
};
// Service Agent
const handleConfigureService = async () => {
try {
await AgentforceService.configure({
...SERVICE_CONFIG,
featureFlags: flags || undefined,
});
setConfigured(true);
setMode("service");
Alert.alert("Configured", "Service Agent ready");
} catch (error) {
Alert.alert("Error", String(error));
}
};
// Employee Agent
const handleLogin = async () => {
try {
const creds = await loginForEmployeeAgent();
setCredentials(creds);
setLoggedIn(true);
} catch (error) {
Alert.alert("Login Failed", String(error));
}
};
const handleConfigureEmployee = async () => {
if (!credentials) {
Alert.alert("Error", "Login first");
return;
}
try {
const agentId = await AgentforceService.getEmployeeAgentId();
await AgentforceService.configure({
type: "employee",
instanceUrl: credentials.instanceUrl,
organizationId: credentials.organizationId,
userId: credentials.userId,
agentId: agentId || undefined,
accessToken: credentials.accessToken,
featureFlags: flags || undefined,
});
setConfigured(true);
setMode("employee");
Alert.alert("Configured", "Employee Agent ready");
} catch (error) {
Alert.alert("Error", String(error));
}
};
// Conversations
const handleLaunch = async () => {
try {
await AgentforceService.launchConversation();
} catch (error) {
Alert.alert("Launch Error", String(error));
}
};
const handleNewConversation = async () => {
try {
await AgentforceService.startNewConversation();
} catch (error) {
Alert.alert("Error", String(error));
}
};
const handleSetContext = async () => {
try {
await AgentforceService.setAdditionalContext({
variables: [
{ name: "screen", type: "Text", value: "HomeScreen" },
{ name: "timestamp", type: "DateTime", value: new Date().toISOString() },
{ name: "userAgent", type: "Text", value: "react-native-agentforce-example" },
],
});
Alert.alert("Context Set", "3 variables sent to the agent");
} catch (error) {
Alert.alert("Error", String(error));
}
};
// Cleanup
const handleReset = async () => {
if (loggedIn) {
await logoutEmployeeAgent();
}
await AgentforceService.resetSettings();
setConfigured(false);
setMode(null);
setLoggedIn(false);
setCredentials(null);
Alert.alert("Reset", "All settings cleared");
};
return (
<ScrollView style={styles.container} contentContainerStyle={styles.content}>
<Text style={styles.title}>Agentforce Demo</Text>
{/* Status */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Status</Text>
<Text>Configured: {configured ? "Yes" : "No"}</Text>
<Text>Mode: {mode || "None"}</Text>
<Text>Log messages received: {logCount.current}</Text>
</View>
{/* Service Agent */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Service Agent</Text>
<Button title="Configure Service Agent" onPress={handleConfigureService} />
</View>
{/* Employee Agent */}
{authSupported && (
<View style={styles.section}>
<Text style={styles.sectionTitle}>Employee Agent</Text>
<Text>Logged in: {loggedIn ? "Yes" : "No"}</Text>
{!loggedIn ? (
<Button title="Login" onPress={handleLogin} />
) : (
<Button title="Configure Employee Agent" onPress={handleConfigureEmployee} />
)}
</View>
)}
{/* Conversation Actions */}
{configured && (
<View style={styles.section}>
<Text style={styles.sectionTitle}>Conversation</Text>
<Button title="Launch Conversation" onPress={handleLaunch} />
<View style={styles.spacer} />
<Button title="New Conversation" onPress={handleNewConversation} />
<View style={styles.spacer} />
<Button title="Set Context Variables" onPress={handleSetContext} />
</View>
)}
{/* Reset */}
<View style={styles.section}>
<Button title="Reset Everything" onPress={handleReset} color="red" />
</View>
</ScrollView>
);
}
const styles = StyleSheet.create({
container: { flex: 1 },
content: { padding: 16, paddingBottom: 48 },
title: { fontSize: 28, fontWeight: "bold", marginBottom: 24 },
section: { marginBottom: 24 },
sectionTitle: { fontSize: 18, fontWeight: "600", marginBottom: 8 },
spacer: { height: 8 },
});