Client Actions
The agent can return actions that your app should handle—navigation, opening modals, playing videos, and more.
How Actions Work
Actions are triggered by:
- Keyword triggers - Configured keywords that match user input (processed before AI)
- Intent triggers - AI-detected intents that match configured patterns (processed after AI)
See the Triggers API for creating and managing triggers programmatically.
User: "show me the pricing page"
↓
Keyword Match: "pricing"
↓
Action Triggered: { type: "navigate", payload: { route: "/pricing" } }
↓
Response includes action for client to handle
Response Format
When actions are triggered, they appear in the response:
{
"success": true,
"data": {
"message": "Sure! Let me take you to our pricing page.",
"sessionId": "sess_abc123",
"actions": [
{
"type": "navigate",
"payload": { "route": "/pricing" },
"label": "View Pricing",
"immediate": true
}
],
"intent": {
"name": "navigation_request",
"confidence": 0.95
}
}
}
Action Types
| Type | Description | Payload Fields |
|------|-------------|----------------|
| navigate | Navigate to app route | route, params |
| open_url | Open external URL | url |
| open_content | Display content | contentId |
| play_video | Play video | videoId, timestamp |
| show_product | Show product details | productId |
| add_to_cart | Add to cart | productId, quantity |
| open_modal | Open modal/dialog | modalId, modalData |
| trigger_event | Fire custom event | eventName, eventData |
| custom | App-specific action | customType, customData |
Action Properties
| Property | Type | Description |
|----------|------|-------------|
| type | string | Action type (see table above) |
| payload | object | Action-specific data |
| label | string | Optional display label for UI |
| priority | number | Higher = processed first |
| immediate | boolean | true = execute now, false = suggest to user |
Handling Actions
React/Next.js
import { useRouter } from 'next/navigation';
interface ClientAction {
type: string;
payload: Record<string, any>;
immediate?: boolean;
}
function useAgentActions() {
const router = useRouter();
const handleActions = (actions: ClientAction[]) => {
for (const action of actions) {
// Skip non-immediate actions (show as suggestions instead)
if (action.immediate === false) continue;
switch (action.type) {
case 'navigate':
if (action.payload.route) {
router.push(action.payload.route);
}
break;
case 'open_url':
if (action.payload.url) {
window.open(action.payload.url, '_blank');
}
break;
case 'play_video':
if (action.payload.videoId) {
// Your video player logic
openVideoPlayer(action.payload.videoId, action.payload.timestamp);
}
break;
case 'show_product':
if (action.payload.productId) {
showProductModal(action.payload.productId);
}
break;
case 'trigger_event':
if (action.payload.eventName) {
window.dispatchEvent(
new CustomEvent(action.payload.eventName, {
detail: action.payload.eventData,
})
);
}
break;
case 'custom':
handleCustomAction(action.payload);
break;
}
}
};
return { handleActions };
}
Using with Chat
const { handleActions } = useAgentActions();
async function sendMessage(text: string) {
const response = await fig1.chat(text, sessionId);
// Display the message
addMessage(response.message);
// Handle any actions
if (response.actions) {
handleActions(response.actions);
}
}
Handling Suggestions (Non-Immediate Actions)
Actions with immediate: false should be shown to the user as clickable suggestions:
function ActionSuggestions({ actions }: { actions: ClientAction[] }) {
const { handleActions } = useAgentActions();
const suggestions = actions.filter(a => a.immediate === false);
if (suggestions.length === 0) return null;
return (
<div className="flex gap-2 mt-2">
{suggestions.map((action, i) => (
<button
key={i}
onClick={() => handleActions([{ ...action, immediate: true }])}
className="px-3 py-1 bg-gray-100 rounded-full text-sm hover:bg-gray-200"
>
{action.label || action.type}
</button>
))}
</div>
);
}
Flutter Example
class Fig1ActionHandler {
final BuildContext context;
Fig1ActionHandler(this.context);
void handleActions(List<ClientAction> actions) {
for (final action in actions) {
if (!action.immediate) continue;
switch (action.type) {
case 'navigate':
final route = action.payload['route'] as String?;
if (route != null) {
Navigator.pushNamed(context, route);
}
break;
case 'open_url':
final url = action.payload['url'] as String?;
if (url != null) {
launchUrl(Uri.parse(url));
}
break;
case 'show_product':
final productId = action.payload['productId'] as String?;
if (productId != null) {
Navigator.pushNamed(context, '/product/$productId');
}
break;
}
}
}
}
Configuring Actions
Actions can be configured in two ways:
Via Dashboard
- Go to SDK Settings → Triggers tab
- Create a trigger (keyword or intent-based)
- Add actions to the trigger
- Configure payload and behavior
Via API
Use the Triggers API to programmatically manage triggers:
curl -X POST https://app.fig1.ai/api/sdk/triggers \
-H "Content-Type: application/json" \
-H "X-Fig1-API-Key: fig1_sdk_your_api_key" \
-d '{
"type": "keyword",
"trigger": { "value": "pricing" },
"response": { "message": "Navigating to pricing..." },
"actions": [{ "type": "navigate", "payload": { "route": "/pricing" } }],
"options": { "skipAgent": true }
}'
Keyword vs Intent Triggers
| Aspect | Keyword Triggers | Intent Triggers | |--------|------------------|-----------------| | When | Before AI processing | After AI processing | | Speed | Instant (no AI call) | Requires AI response | | Matching | Exact/phrase match | AI confidence-based | | Use Case | Navigation, known commands | Complex user requests | | Example | "pricing", "contact us" | "I want to buy something" |
Best Practices
- Use keyword triggers for navigation - They're faster than intent triggers
- Set
immediate: truefor navigation - Users expect immediate response - Use
immediate: falsefor suggestions - Let users choose - Handle all action types - Gracefully ignore unknown types
- Log actions for analytics - Track which actions users trigger