Setting Up Actions
This guide walks through implementing actions in your application. For a conceptual overview of what actions are and why they matter, see Actions.
Build with AI
Use this prompt with Cursor or Claude Code to automatically generate actions tailored to your application:
# Build Pillar SDK Actions for My App
I want to implement Pillar SDK actions for my application. Help me create a comprehensive set of actions that will make my app's co-pilot useful.
## Pillar SDK Overview
Pillar actions are defined using `defineActions()` and registered with handlers. Here's the pattern:
```tsx
// lib/pillar/actions.ts
import { defineActions } from '@pillar-ai/sdk';
export const actions = defineActions({
action_name: {
description: 'AI-friendly description of what this does',
type: 'navigate' | 'trigger_action' | 'inline_ui' | 'external_link' | 'copy_text',
examples: ['phrases users might say'],
path: '/route', // for navigate
autoRun: true, // execute without clicking
autoComplete: true, // mark done immediately
dataSchema: { ... }, // JSON Schema for data extraction
requiredContext: { ... }, // context filtering
handler: (data) => { ... },
},
});
```
### Action Types
- **navigate**: Go to a page (use with `path` and `autoRun: true`)
- **trigger_action**: Run custom logic (modals, wizards, toggles)
- **inline_ui**: Show interactive UI in chat
- **external_link**: Open URL in new tab
- **copy_text**: Copy to clipboard
### Handler Pattern
```tsx
// components/PillarActionHandlers.tsx
import { usePillar } from '@pillar-ai/react';
export function PillarActionHandlers() {
const { pillar } = usePillar();
const router = useRouter();
useEffect(() => {
if (!pillar) return;
const handlers = [
pillar.onTask('navigate', ({ path }) => {
router.push(path);
return { success: true };
}),
// ... more handlers
];
return () => handlers.forEach(unsub => unsub?.());
}, [pillar, router]);
return null;
}
```
## Your Task
1. **Analyze my codebase** to understand:
- Framework (Next.js, React, Vue, etc.)
- Routing pattern and all navigable pages
- Modal/dialog systems (what modals exist?)
- Key user flows and features
- Authentication and role patterns
2. **Identify valuable actions** in these categories:
**Navigation Actions** (type: 'navigate')
- All main pages users might want to reach
- Settings sections (account, billing, notifications, etc.)
- Dashboard views and detail pages
- Use `autoRun: true` for simple navigations
**Trigger Actions** (type: 'trigger_action')
- Modals: invite users, create items, confirmations
- Wizards: onboarding, setup flows, multi-step processes
- Feature toggles: dark mode, sidebar, notifications
- Actions that open forms or dialogs
**Context-Filtered Actions**
- Admin-only actions with `requiredContext: { userRole: 'admin' }`
- Feature-gated actions based on subscription/plan
- Page-specific actions
3. **Generate implementation files:**
a. `lib/pillar/actions.ts`:
- 15-25 well-described actions
- Clear, specific descriptions that help AI matching
- Example phrases for each action (3-5 per action)
- dataSchema for actions that need extracted data
b. `components/PillarActionHandlers.tsx`:
- Centralized handler component
- Proper cleanup with useEffect
- Error handling with user-friendly messages
- Router integration for navigation
4. **For each action, include:**
- A specific description (not generic)
- Multiple example phrases users might say
- The right action type
- Any required context filtering
## Start Here
First, explore my codebase:
1. Look at the routing structure (pages/, app/, or routes/)
2. Find modal/dialog components
3. Identify settings pages and their sections
4. Look for admin-only features
5. Find any existing navigation patterns
Then ask me about:
- What are the main features of my app?
- What user roles exist?
- What actions do users ask about most often?
Defining Actions
Define and export your actions:
// lib/pillar/actions/index.tsimport type { SyncActionDefinitions } from '@pillar-ai/sdk';export const actions = {open_settings: {description: 'Navigate to the settings page',type: 'navigate' as const,path: '/settings',autoRun: true,autoComplete: true,},invite_member: {description: 'Open the invite team member modal',examples: ['invite someone to my team','add a new user','how do I invite people?',],type: 'trigger_action' as const,},view_billing: {description: 'Navigate to billing and subscription settings',type: 'navigate' as const,path: '/settings/billing',autoRun: true,autoComplete: true,},} as const satisfies SyncActionDefinitions;export default actions;
Action Types
| Type | Description | Use Case |
|---|---|---|
navigate | Navigate to a page in your app | Settings, dashboard, detail pages |
trigger_action | Run custom logic | Open modals, start wizards, toggle features |
inline_ui | Show interactive UI in chat | Forms, confirmations, previews |
external_link | Open URL in new tab | Documentation, external resources |
copy_text | Copy text to clipboard | API keys, code snippets |
Action Properties
{// Requireddescription: string, // AI uses this to match user intenttype: ActionType, // One of the types abovehandler: (data) => void, // Your execution logic// Optionalexamples?: string[], // Example phrases that trigger this actionpath?: string, // For navigate actionsexternalUrl?: string, // For external_link actionsautoRun?: boolean, // Execute without user clicking (default: false)autoComplete?: boolean, // Mark done immediately (default: false)dataSchema?: object, // JSON Schema for data extractiondefaultData?: object, // Default values passed to handlerrequiredContext?: object, // Context required for action to be available}
Registering Handlers
When you initialize the Pillar SDK, you can register an action handler via the onTask prop:
import { Pillar } from '@pillar-ai/sdk';// Initialize the SDKconst pillar = await Pillar.init({helpCenter: '...',publicKey: '...',edgeTrigger: { enabled: true },panel: { position: 'right' },});// Register task handlerspillar.onTask('navigate', ({ path }) => {window.location.href = path;});pillar.onTask('invite_member', ({ email, role }) => {showInviteModal({ email, role });});// Or handle all tasks with a single handlerpillar.on('task:execute', (task) => {switch (task.name) {case 'navigate':window.location.href = task.data.path;break;case 'invite_member':showInviteModal(task.data);break;}});
Handler Return Values
Handlers can return execution status:
// Successreturn { success: true };// Success with messagereturn { success: true, message: 'Invite sent!' };// Failurereturn { success: false, message: 'Something went wrong' };
Wizard Actions
For actions that open multi-step flows (wizards, modals with multiple steps), signal completion when the user finishes:
// When your wizard/modal flow completes, signal to Pillarfunction handleWizardComplete(source) {// 1. Your app logicsaveSource(source);// 2. Tell Pillar the action is donepillar.completeAction('add_source', true, { sourceId: source.id });}// If the user cancels the wizardfunction handleWizardCancel() {pillar.completeAction('add_source', false);}
For simple actions (navigation, toggles), the SDK handles completion automatically when your handler returns { success: true }. You only need to call completeAction() for flows where the SDK can't know when the user is "done"—like multi-step wizards or forms that require user input.
Built-in Handlers
Pillar provides fallback handlers for common action types:
| Type | Default Behavior |
|---|---|
navigate | window.location.href = path |
external_link | window.open(url, '_blank') |
copy_text | navigator.clipboard.writeText(text) |
Register your own handlers to override these defaults.
Type-Safe Handlers
Get full TypeScript inference for your action data:
import { usePillar } from '@pillar-ai/react';import type { actions } from '@/lib/pillar/actions';function ActionHandlers() {const { onTask } = usePillar<typeof actions>();useEffect(() => {// TypeScript knows the data shape for each actiononTask('invite_member', (data) => {console.log(data.email); // Typed!console.log(data.role); // Typed!});}, [onTask]);return null;}
Data Extraction
Define a schema to have the AI extract structured data from user messages:
add_source: {description: 'Add a new knowledge source',type: 'trigger_action',dataSchema: {type: 'object',properties: {url: {type: 'string',description: 'URL of the source to add',},name: {type: 'string',description: 'Display name for the source',},},required: ['url'],},handler: (data) => {openAddSourceWizard({prefillUrl: data.url,prefillName: data.name,});},}
Now when a user says "Add my docs site at docs.example.com", the AI extracts the URL and passes it to your handler.
Context-Based Action Filtering
Use requiredContext to control which actions the AI suggests based on user context. This is useful for:
- Admin-only actions
- Feature-gated actions based on user plan
- Actions that only make sense on certain pages
Admin-Only Actions
delete_user: {description: 'Delete a user from the organization',type: 'trigger_action',requiredContext: { userRole: 'admin' },handler: ({ userId }) => deleteUser(userId),}
This action will only be suggested when the user's context includes userRole: 'admin'.
Setting Context in Your App
For requiredContext to work, you need to set the user's context:
// In your appimport { usePillar } from '@pillar-ai/react';function UserContextSync() {const { pillar } = usePillar();const { user } = useAuth();useEffect(() => {if (user && pillar) {pillar.setContext({userRole: user.role, // 'admin', 'member', 'viewer', etc.});}}, [pillar, user]);return null;}
How It Works
- You define actions with
requiredContext: { userRole: 'admin' } - Your app calls
pillar.setContext({ userRole: user.role }) - When searching for actions, Pillar filters out actions where the user doesn't match the required context
- Only matching actions are suggested by the AI
Multiple Context Requirements
You can require multiple context fields:
billing_settings: {description: 'Access billing settings',type: 'navigate',path: '/settings/billing',requiredContext: {userRole: 'admin',userState: 'active', // Not during trial},}
See Context API for all available context properties.
Best Practices
Write Clear Descriptions
The AI matches user queries to your action descriptions. Be specific:
// Good - specific about when to usedescription: 'Navigate to billing page. Suggest when user asks about payments, invoices, or subscription.'// Less helpful - too genericdescription: 'Go to billing'
Add Example Phrases
Help the AI understand different ways users might phrase requests:
examples: ['invite someone to my team','add a new team member','how do I add users?','share access with someone',]
Handle Errors Gracefully
pillar.onTask('export_data', async (data) => {try {await exportData(data);return { success: true };} catch (error) {console.error('Export failed:', error);return {success: false,message: 'Export failed. Please try again.',};}});
Close Panel When Appropriate
For actions that take over the screen:
pillar.onTask('start_tour', () => {pillar.close(); // Close panel firststartOnboardingTour();return { success: true };});
Next Steps
- Sync your actions to Pillar's backend with the CLI
- Learn how the AI combines actions into Plans for multi-step tasks
- Create Custom Cards for rich confirmation UI
- Pass Context to help the AI suggest relevant actions