# React (1.4.4) > React SDK Integration Guide - Voice-enabled AI agent with real-time communication capabilities URL: /embed/integration/react/v1.4.4 Markdown: /embed/integration/react/v1.4.4.md # 🚀 Complete Integration Guide [#-complete-integration-guide] ## 📦 Installation [#-installation] ```bash npm install @revrag-ai/embed-react # or yarn add @revrag-ai/embed-react ``` ## ⚠️ Important: CSS Import (REQUIRED) [#️-important-css-import-required] **The CSS file MUST be imported** for the widget to display correctly. Without it, the widget will appear unstyled. ### Option 1: Global Import (Recommended) [#option-1-global-import-recommended] **React/Vite:** ```tsx // src/main.tsx or src/index.tsx import '@revrag-ai/embed-react/style.css'; import App from './App'; import ReactDOM from 'react-dom/client'; ReactDOM.createRoot(document.getElementById('root')!).render(); ``` **Next.js App Router:** ```tsx // app/layout.tsx import '@revrag-ai/embed-react/style.css'; export default function RootLayout({ children }) { return ( {children} ); } ``` **Next.js Pages Router:** ```tsx // pages/_app.tsx import '@revrag-ai/embed-react/style.css'; import type { AppProps } from 'next/app'; export default function App({ Component, pageProps }: AppProps) { return ; } ``` ### Option 2: Component-Level Import [#option-2-component-level-import] ```tsx // YourComponent.tsx import { EmbedButton, useInitialize } from '@revrag-ai/embed-react'; import '@revrag-ai/embed-react/style.css'; ``` ### Option 3: CSS File Import [#option-3-css-file-import] ```css /* In your global styles.css */ @import '@revrag-ai/embed-react/style.css'; ``` *** ## 🎯 Basic Usage [#-basic-usage] ### Fixed Positioning (Default) [#fixed-positioning-default] ```tsx import { EmbedButton, useInitialize } from '@revrag-ai/embed-react'; import '@revrag-ai/embed-react/style.css'; function App() { const { isInitialized } = useInitialize("your-api-key"); if (!isInitialized) { return
Loading AI Assistant...
; } return (

My Application

{/* Widget will appear at bottom-right corner */}
); } ``` ### Embedded Positioning [#embedded-positioning] ```tsx import { EmbedButton, useInitialize } from '@revrag-ai/embed-react'; import '@revrag-ai/embed-react/style.css'; function HelpSection() { const { isInitialized } = useInitialize("your-api-key"); if (!isInitialized) return null; return (

Need Help?

Chat with our AI assistant

{/* Widget will appear at bottom-left of this container */}
); } ``` *** ## 🧩 EmbedProvider (Recommended) [#-embedprovider-recommended] Instead of manually placing `` on every page, wrap your app once with `EmbedProvider` and let it handle widget visibility automatically. **BEST PRACTICE:** Use `EmbedProvider` to automatically manage the EmbedButton based on the current route. The `EmbedProvider` component: * ✅ **Automatically tracks** route changes and shows/hides the widget * ✅ **Conditionally renders** the EmbedButton based on the current page * ✅ **Supports delays** before the button appears on a route * ✅ **Supports visibility groups** for per-route delay, continuity, and position * ✅ **Built-in component** - no need to create a custom provider ### Basic Setup [#basic-setup] ```tsx import { EmbedProvider, useInitialize } from '@revrag-ai/embed-react'; import '@revrag-ai/embed-react/style.css'; function App() { const { isInitialized } = useInitialize("your-api-key"); if (!isInitialized) return
Loading...
; return ( {/* Your app / router */} ); } ``` ### Show on specific routes only [#show-on-specific-routes-only] ```tsx {/* Widget only visible on the listed routes */} ``` ### EmbedProvider Props [#embedprovider-props] | Property | Type | Required | Description | | ----------------------------- | -------------------------------------- | -------- | ------------------------------------------------------------ | | `children` | ReactNode | ✅ | Your app or router | | `appVersion` | string | ✅ | App version string used in analytics | | `includeScreens` | string\[] | ❌ | Routes where the button appears. Omit to show on all routes | | `matchMode` | 'exact' \| 'prefix' | ❌ | Route matching mode (default: 'exact') | | `currentPath` | string | ❌ | Current route path (auto-detected if not provided) | | `usePathHook` | () => string | ❌ | Custom hook for path detection (e.g., Next.js `usePathname`) | | `embedButtonDelayMs` | number | ❌ | Delay (ms) before the button appears on a route | | `embedButtonVisibilityConfig` | EmbedButtonVisibilityConfig | ❌ | Advanced per-group delay, continuity, and inset config | | `embedButtonPosition` | 'top' \| 'bottom' \| 'left' \| 'right' | ❌ | Button position for fixed positioning | Advanced usage guide → *** ## 🎨 Responsive Behavior [#-responsive-behavior] ### Fixed Positioning [#fixed-positioning] * **Desktop (> 500px)**: Widget stays at specified position * **Mobile (≤ 500px)**: Widget expands to full width with 1rem padding from edges ### Embedded Positioning [#embedded-positioning-1] * **Wide parent (> 400px)**: Widget aligns to left/right based on `side` prop * **Narrow parent (≤ 400px)**: Widget auto-centers with equal padding *** ## 🔧 API Reference [#-api-reference] ### EmbedButton Props [#embedbutton-props] ```tsx interface EmbedButtonProps { // Positioning mode positioning?: 'fixed' | 'embedded'; // default: 'fixed' // Position configuration (for fixed mode) position?: { top?: string; bottom?: string; left?: string; right?: string; }; // Easy positioning (for embedded mode) side?: 'left' | 'right'; // Offset from bottom (useful for bottom navbars) bottomOffset?: number; // in pixels // Custom className className?: string; } ``` ### Hooks [#hooks] #### useInitialize [#useinitialize] ```tsx const { isInitialized, error } = useInitialize(apiKey: string); ``` Initializes the SDK with your API key. Must be called before using any other SDK features. #### useEmbed [#useembed] ```tsx const { currentPath } = useEmbed(); ``` Reads `currentPath` from `EmbedProvider` context. Useful for accessing the current route within your components. #### useLiveKit [#uselivekit] ```tsx const { connect, disconnect, toggleMute } = useLiveKit(); ``` Provides programmatic control over voice calls: * `connect()`: Initiate a voice call * `disconnect()`: End the current voice call * `toggleMute()`: Mute/unmute the microphone #### useSSRSafe / useBrowserSafe [#usessrsafe--usebrowsersafe] ```tsx const isMounted = useSSRSafe(); // or const isMounted = useBrowserSafe(); ``` SSR-safe mount detection hooks. Returns `true` when component is mounted on the client side. Useful for Next.js and other SSR frameworks to prevent hydration mismatches. *** ## 📡 Event Management [#-event-management] The SDK provides a powerful event system for tracking user data, custom events, and listening to agent state changes. ### EventKeys [#eventkeys] Available event types: ```tsx import { EventKeys } from '@revrag-ai/embed-react'; EventKeys.USER_DATA // 'user_data' - User identification and profile data EventKeys.CUSTOM_EVENT // 'custom_event' - Custom application events EventKeys.AGENT_CONNECTED // 'agent_start' - Voice agent connection (auto-tracked) EventKeys.AGENT_DISCONNECTED // 'agent_end' - Voice agent disconnection (auto-tracked) EventKeys.ANALYTICS_DATA // 'analytics_data' ``` **Note**: Only `USER_DATA` , `CUSTOM_EVENT` and `ANALYTICS_DATA` are available for manual use. Agent connection events are automatically tracked by the SDK and can be listened to via callbacks. *** ### Sending Events with embedEvent API [#sending-events-with-embedevent-api] The `embedEvent` object provides methods for sending events to track user data and custom application events. #### Send User Data [#send-user-data] ```tsx import { embedEvent, EventKeys } from '@revrag-ai/embed-react'; // Send user data const response = await embedEvent.event({ eventKey: EventKeys.USER_DATA, data: { app_user_id: 'user-123', email: 'user@example.com', name: 'John Doe', plan: 'premium' } }); if (response.success) { console.log('User data sent successfully'); } ``` #### Send Custom Events [#send-custom-events] ```tsx import { embedEvent, EventKeys } from '@revrag-ai/embed-react'; // Track custom application event await embedEvent.event({ eventKey: EventKeys.CUSTOM_EVENT, data: { event_name: 'purchase_completed', product_id: 'prod-123', amount: 99.99 } }); ``` #### Send Analytics Events [#send-analytics-events] ```tsx import { embedEvent, EventKeys } from '@revrag-ai/embed-react'; // Track custom application event await embedEvent.event({ eventKey: EventKeys.ANALYTICS_DATA, data: { event_name: 'purchase_completed', // event_name is compulsory in analytics_data event product_id: 'prod-123', amount: 99.99 } }); ``` #### Event Method Signature [#event-method-signature] ```tsx embedEvent.event(params: UpdateDataRequest): Promise interface UpdateDataRequest { eventKey: EventKey; // Event type from EventKeys data: { app_user_id?: string; // User ID (required for USER_DATA) [key: string]: unknown; // Additional data }; session_id?: string; // Optional session ID } interface ApiResponse { success: boolean; data?: unknown; message?: string; error?: string; } ``` *** ### Listening to Agent Events [#listening-to-agent-events] Monitor voice agent connection status in real-time. **These events are automatically sent to your backend AND emitted locally** for you to listen to. #### Available Event Types for Listening [#available-event-types-for-listening] ```tsx import { EventKeys } from '@revrag-ai/embed-react'; // Available events for listening: EventKeys.AGENT_CONNECTED // 'agent_start' - Voice agent connected EventKeys.AGENT_DISCONNECTED // 'agent_end' - Voice agent disconnected ``` **Automatic Backend Sync:** * Agent events are **automatically sent to your backend** with `app_user_id` * Events are **also emitted locally** for real-time UI updates * Backend receives all event data including timestamps and metadata * No manual API calls needed - it's all handled automatically #### Event Listener Methods [#event-listener-methods] ```tsx import { embedEvent } from '@revrag-ai/embed-react'; // Add event listener embedEvent.addCallback(callback); // Remove event listener embedEvent.removeCallback(callback); ``` #### Basic Event Listening Example [#basic-event-listening-example] ```tsx import React, { useEffect } from 'react'; import { embedEvent, EventKeys, EmbedButton } from '@revrag-ai/embed-react'; function MyComponent() { useEffect(() => { // Define event handler const handleAgentEvent = (event) => { if (event.type === EventKeys.AGENT_CONNECTED) { console.log('✅ Agent connected:', event.data); // Update UI to show agent is available } if (event.type === EventKeys.AGENT_DISCONNECTED) { console.log('❌ Agent disconnected:', event.data); // Update UI to show agent is unavailable } }; // Register callback embedEvent.addCallback(handleAgentEvent); // Cleanup return () => { embedEvent.removeCallback(handleAgentEvent); }; }, []); return ; } ``` #### Complete Agent Monitoring Example [#complete-agent-monitoring-example] ```tsx import React, { useEffect, useState } from 'react'; import { embedEvent, EventKeys, EmbedButton } from '@revrag-ai/embed-react'; function VoiceAgentMonitor() { const [agentStatus, setAgentStatus] = useState<'idle' | 'connected' | 'disconnected'>('idle'); const [agentIdentity, setAgentIdentity] = useState(''); const [connectionTime, setConnectionTime] = useState(null); useEffect(() => { const handleEvent = (event) => { // Handle agent connection if (event.type === EventKeys.AGENT_CONNECTED) { console.log('✅ Agent connected:', event); console.log('Identity:', event.data?.identity); console.log('Metadata:', event.data?.metadata); console.log('Timestamp:', event.timestamp); setAgentStatus('connected'); setAgentIdentity(event.data?.identity || 'Unknown'); setConnectionTime(new Date(event.timestamp)); // Update UI - show green indicator, enable features, etc. // Example: Show notification, start analytics timer } // Handle agent disconnection if (event.type === EventKeys.AGENT_DISCONNECTED) { console.log('❌ Agent disconnected:', event); console.log('Identity:', event.data?.identity); console.log('Metadata:', event.data?.metadata); console.log('Timestamp:', event.timestamp); setAgentStatus('disconnected'); // Calculate call duration if needed if (connectionTime) { const duration = Date.now() - connectionTime.getTime(); console.log('Call duration:', duration / 1000, 'seconds'); } // Update UI - show gray indicator, disable features, etc. // Example: Show feedback form, log analytics } }; // Register event listener embedEvent.addCallback(handleEvent); // Cleanup listener on unmount return () => { embedEvent.removeCallback(handleEvent); }; }, [connectionTime]); return (
Agent Status: {agentStatus}
{agentStatus === 'connected' && (

✓ Voice agent is active

Identity: {agentIdentity}

{connectionTime && (

Connected at: {connectionTime.toLocaleTimeString()}

)}
)}
); } ``` #### Advanced: Multiple Event Listeners [#advanced-multiple-event-listeners] ```tsx import { useEffect } from 'react'; import { embedEvent, EventKeys } from '@revrag-ai/embed-react'; function MyApp() { useEffect(() => { // Analytics tracking const analyticsCallback = (event) => { if (event.type === EventKeys.AGENT_CONNECTED) { // Track to analytics service analytics.track('voice_agent_connected', { identity: event.data?.identity, timestamp: event.timestamp, }); } if (event.type === EventKeys.AGENT_DISCONNECTED) { analytics.track('voice_agent_disconnected', { identity: event.data?.identity, timestamp: event.timestamp, }); } }; // UI updates const uiCallback = (event) => { if (event.type === EventKeys.AGENT_CONNECTED) { showNotification('Voice agent connected'); } if (event.type === EventKeys.AGENT_DISCONNECTED) { showNotification('Voice agent disconnected'); } }; // Register multiple callbacks embedEvent.addCallback(analyticsCallback); embedEvent.addCallback(uiCallback); // Cleanup return () => { embedEvent.removeCallback(analyticsCallback); embedEvent.removeCallback(uiCallback); }; }, []); return ; } ``` #### Use Cases for Agent Events [#use-cases-for-agent-events] **AGENT\_CONNECTED:** * Show visual indicators (green dot, badge) * Enable voice-related features in UI * Start analytics timers * Update user presence status * Show notifications to user * Pause background music/media **AGENT\_DISCONNECTED:** * Update UI to show agent unavailable * Log analytics (call duration, success) * Show feedback forms * Resume background media * Clean up resources * Save conversation state #### What Gets Sent to Backend [#what-gets-sent-to-backend] When an agent event fires, the SDK automatically sends this data to your backend: ```typescript { event_id: "evt_1234567890_abc", // Unique event ID type: "agent_start", // Event type (agent_start or agent_end) app_user_id: "user-123", // Auto-added from USER_DATA session_id: "embed_session_...", // Session identifier timestamp: "2024-01-15T10:30:00Z", // ISO timestamp sdk: { sdk_name: "@revrag-ai/embed-react", sdk_version: "1.3.7", platform: "web" }, data: { identity: "agent-001", // Agent identity metadata: { // Additional metadata // ... agent-specific data } } } ``` This allows you to: * Track agent usage analytics * Monitor call durations * Understand user engagement patterns * Build reports on voice agent interactions * Audit agent connections #### Event Data Structure [#event-data-structure] ```typescript // Agent Connected Event interface AgentConnectedEvent { type: 'agent_start'; target: string; timestamp: number; // Unix timestamp data?: { identity?: string; // Agent identity metadata?: Record; }; userId?: string; sessionId: string; metadata?: Record; } // Agent Disconnected Event interface AgentDisconnectedEvent { type: 'agent_end'; target: string; timestamp: number; // Unix timestamp data?: { identity?: string; // Agent identity metadata?: Record; }; userId?: string; sessionId: string; metadata?: Record; } ``` #### Handling Connection Errors [#handling-connection-errors] ```tsx import { useEffect, useState } from 'react'; import { embed, EventKeys, EmbedButton } from '@revrag-ai/embed-react'; function AgentWithErrorHandling() { const [error, setError] = useState(null); useEffect(() => { const handleEvent = (event) => { try { if (event.type === EventKeys.AGENT_CONNECTED) { setError(null); // Handle successful connection } if (event.type === EventKeys.AGENT_DISCONNECTED) { // Check if disconnection was due to error if (event.data?.metadata?.error) { setError('Agent connection lost: ' + event.data.metadata.error); } } } catch (err) { console.error('Error handling agent event:', err); setError('Failed to process agent event'); } }; embed.addCallback(handleEvent); return () => embed.removeCallback(handleEvent); }, []); return (
{error && (
{error}
)}
); } ``` *** ## 📱 Mobile Optimization [#-mobile-optimization] The widget automatically adjusts for mobile devices: ### Extra Small Screens (≤ 375px) [#extra-small-screens--375px] * iPhone SE, small Android devices * Reduced padding and font sizes * Optimized button and text layouts ### Small Screens (376px - 500px) [#small-screens-376px---500px] * Standard smartphones * Balanced sizing for readability *** ## 🎯 Common Use Cases [#-common-use-cases] ### 1. Customer Support Widget with User Tracking [#1-customer-support-widget-with-user-tracking] ```tsx import { EmbedButton, useInitialize, embedEvent, EventKeys } from '@revrag-ai/embed-react'; import { useEffect } from 'react'; function App() { const { isInitialized } = useInitialize("your-api-key"); const currentUser = useAuthUser(); // Your auth hook useEffect(() => { if (isInitialized && currentUser) { // Send user data when user logs in embedEvent.event({ eventKey: EventKeys.USER_DATA, data: { app_user_id: currentUser.id, email: currentUser.email, name: currentUser.name, subscription_tier: currentUser.plan } }); } }, [isInitialized, currentUser]); return ( <> {isInitialized && ( )} ); } ``` ### 2. E-commerce with Purchase Tracking [#2-e-commerce-with-purchase-tracking] ```tsx import { EmbedButton, useInitialize, embedEvent, EventKeys } from '@revrag-ai/embed-react'; function CheckoutPage() { const { isInitialized } = useInitialize("your-api-key"); const handleCheckout = async (orderData) => { // ... process order // Track purchase event await embedEvent.event({ eventKey: EventKeys.CUSTOM_EVENT, data: { event_name: 'purchase_completed', order_id: orderData.id, total: orderData.total, items: orderData.items.length } }); }; if (!isInitialized) return null; return (
); } ``` ### 3. Help Section Widget with Agent Status [#3-help-section-widget-with-agent-status] ```tsx import { EmbedButton, useInitialize, embedEvent, EventKeys } from '@revrag-ai/embed-react'; import { useState, useEffect } from 'react'; function HelpPage() { const { isInitialized } = useInitialize("your-api-key"); const [agentConnected, setAgentConnected] = useState(false); useEffect(() => { const handleAgentEvent = (event) => { if (event.type === EventKeys.AGENT_CONNECTED) { setAgentConnected(true); } if (event.type === EventKeys.AGENT_DISCONNECTED) { setAgentConnected(false); } }; embedEvent.addCallback(handleAgentEvent); return () => embedEvent.removeCallback(handleAgentEvent); }, []); return (

Help & Support

{agentConnected && (
🟢 Agent Connected
)}
{isInitialized && ( )}
); } ``` ### 4. With Bottom Navigation [#4-with-bottom-navigation] ```tsx function MobileApp() { const { isInitialized } = useInitialize("your-api-key"); return ( <> {/* height: 60px */} {isInitialized && ( )} ); } ``` ### 5. Multi-Department Support [#5-multi-department-support] ```tsx function SupportPage() { const { isInitialized } = useInitialize("your-api-key"); if (!isInitialized) return
Loading...
; return (

Sales Support

Questions about pricing and plans

Technical Support

Help with technical issues

); } ``` ### 6. Contextual Events Based on User Actions [#6-contextual-events-based-on-user-actions] ```tsx import { embedEvent, EventKeys, EmbedButton, useInitialize } from '@revrag-ai/embed-react'; function ProductPage({ product }) { const { isInitialized } = useInitialize("your-api-key"); const handleAddToCart = async () => { // Send custom event when user adds to cart await embedEvent.event({ eventKey: EventKeys.CUSTOM_EVENT, data: { event_name: 'product_added_to_cart', product_id: product.id, product_name: product.name, price: product.price } }); }; return (
{isInitialized && }
); } ``` *** ## 🔄 Complete Integration Example [#-complete-integration-example] Here's a complete example showing initialization, user tracking, event listening, and the widget all working together: ```tsx import React, { useEffect, useState } from 'react'; import { EmbedButton, useInitialize, embedEvent, EventKeys } from '@revrag-ai/embed-react'; import '@revrag-ai/embed-react/style.css'; function App() { const { isInitialized, isLoading, error } = useInitialize("your-api-key"); const [agentStatus, setAgentStatus] = useState('idle'); const [userDataSent, setUserDataSent] = useState(false); // Initialize user data when SDK is ready useEffect(() => { if (isInitialized && !userDataSent) { initializeUserData(); } }, [isInitialized, userDataSent]); // Listen to agent events useEffect(() => { const handleAgentEvent = (event) => { if (event.type === EventKeys.AGENT_CONNECTED) { console.log('Agent connected:', event); setAgentStatus('connected'); // Show notification, enable features, etc. } if (event.type === EventKeys.AGENT_DISCONNECTED) { console.log('Agent disconnected:', event); setAgentStatus('disconnected'); // Show feedback form, log analytics, etc. } }; embedEvent.addCallback(handleAgentEvent); return () => embedEvent.removeCallback(handleAgentEvent); }, []); const initializeUserData = async () => { try { // Send user data first (required) await embedEvent.event({ eventKey: EventKeys.USER_DATA, data: { app_user_id: 'user-123', email: 'user@example.com', name: 'John Doe', subscription: 'premium' } }); setUserDataSent(true); } catch (error) { console.error('Failed to initialize user data:', error); } }; // Send custom event on button click const handlePurchase = async () => { await embedEvent.event({ eventKey: EventKeys.CUSTOM_EVENT, data: { event_name: 'purchase_completed', amount: 99.99, product_id: 'prod-123' } }); }; // Handle loading states if (isLoading) { return
Initializing AI Assistant...
; } if (error) { return
Error: {error}
; } if (!isInitialized || !userDataSent) { return
Loading...
; } return (

My Application

Agent Status: {agentStatus}
{/* AI Widget - will appear at bottom-right */}
); } export default App; ``` ### Key Points in This Example: [#key-points-in-this-example] 1. **✅ CSS Import**: Imported at the top of the file 2. **✅ SDK Initialization**: Using `useInitialize` hook with loading/error states 3. **✅ User Data**: Sent first before rendering the widget 4. **✅ Event Listeners**: Set up to monitor agent connection status 5. **✅ Custom Events**: Tracked when user performs actions 6. **✅ Widget Rendering**: Only rendered after successful initialization *** ## ⚡ Framework-Specific Notes [#-framework-specific-notes] ### React + Vite [#react--vite] ✅ Works perfectly with no additional configuration ### Next.js [#nextjs] ✅ Fully compatible with both App Router and Pages Router ⚠️ Import CSS in root layout or \_app file ### Create React App [#create-react-app] ✅ Works out of the box ⚠️ Import CSS in index.tsx ### Tailwind CSS Projects [#tailwind-css-projects] ✅ No conflicts! The library uses custom `embed-*` prefixed classes ✅ Your Tailwind styles won't affect the widget ✅ Widget styles won't affect your app *** ## 🐛 Troubleshooting [#-troubleshooting] ### Widget appears unstyled [#widget-appears-unstyled] **Solution**: Make sure you've imported the CSS file: ```tsx import '@revrag-ai/embed-react/style.css'; ``` ### Widget not appearing [#widget-not-appearing] **Solution**: Ensure `useInitialize` has completed: ```tsx const { isInitialized } = useInitialize("your-api-key"); if (!isInitialized) return
Loading...
; ``` ### Widget overlaps with bottom navigation [#widget-overlaps-with-bottom-navigation] **Solution**: Use the `bottomOffset` prop: ```tsx ``` ### Widget too small on mobile [#widget-too-small-on-mobile] **Solution**: This is handled automatically! The widget is fully responsive. ### Events not being sent [#events-not-being-sent] **Solution**: Ensure you've sent USER\_DATA event first with `app_user_id`: ```tsx await embedEvent.event({ eventKey: EventKeys.USER_DATA, data: { app_user_id: 'user-123', // ... other data } }); ``` ### Agent event listeners not firing [#agent-event-listeners-not-firing] **Solution**: Make sure callbacks are registered before agent connection: ```tsx useEffect(() => { const handleEvent = (event) => { // Your event handling logic }; embedEvent.addCallback(handleEvent); return () => embedEvent.removeCallback(handleEvent); }, []); ``` ### "User identity not found" error [#user-identity-not-found-error] **Solution**: Send USER\_DATA event before any other events: ```tsx // ✅ Correct order await embedEvent.event({ eventKey: EventKeys.USER_DATA, data: { app_user_id: 'user-123' } }); await embedEvent.event({ eventKey: EventKeys.CUSTOM_EVENT, data: { ... } }); // ❌ Wrong order await embedEvent.event({ eventKey: EventKeys.CUSTOM_EVENT, data: { ... } }); // Error! ``` ### Custom events being blocked [#custom-events-being-blocked] **Solution**: Only `USER_DATA` and `CUSTOM_EVENT` are allowed for manual sending. Agent events (`AGENT_CONNECTED`, `AGENT_DISCONNECTED`) are auto-tracked and can only be listened to, not manually sent. *** ## 📋 Checklist [#-checklist] Before deploying, ensure: **Basic Setup:** * [ ] CSS file is imported * [ ] API key is configured * [ ] `isInitialized` is checked before rendering * [ ] Parent container has `position: relative` (for embedded mode) * [ ] Parent container has sufficient height (for embedded mode) * [ ] Bottom offset is set if you have bottom navigation **Event System:** * [ ] USER\_DATA event sent first with `app_user_id` * [ ] USER\_DATA sent before rendering EmbedButton * [ ] Event listeners registered before agent connection * [ ] Event listeners cleaned up on component unmount * [ ] Custom events include proper context (screen, flow) **Production Readiness:** * [ ] Error handling for failed event sends * [ ] Loading states during SDK initialization * [ ] Agent connection status displayed to users * [ ] Analytics tracking for agent events * [ ] Proper cleanup of callbacks on unmount *** ## 🆘 Support [#-support] * 📧 Issues: [GitHub Issues](https://github.com/revrag-ai/embed-react/issues) * 📖 Docs: [GitHub README](https://github.com/revrag-ai/embed-react) * 💬 Discussions: [GitHub Discussions](https://github.com/revrag-ai/embed-react/discussions) *** ## 🎉 You're All Set! [#-youre-all-set] The widget is now ready to use. It's: * ✅ Fully responsive * ✅ Framework-agnostic * ✅ Tailwind-compatible * ✅ Production-ready * ✅ Mobile-optimized * ✅ Real-time event tracking * ✅ Voice agent monitoring * ✅ User context aware ### Quick Reference [#quick-reference] **Import everything you need:** ```tsx import { EmbedButton, // The main widget component EmbedProvider, // Built-in provider for automatic widget management useInitialize, // SDK initialization hook embedEvent, // Event management API EventKeys, // Event type constants } from '@revrag-ai/embed-react'; import '@revrag-ai/embed-react/style.css'; ``` **Initialize and track:** ```tsx // 1. Initialize SDK const { isInitialized } = useInitialize("your-api-key"); // 2. Send user data await embedEvent.event({ eventKey: EventKeys.USER_DATA, data: { app_user_id: 'user-123' } }); // 3. Listen to agent events embedEvent.addCallback((event) => { if (event.type === EventKeys.AGENT_CONNECTED) { console.log('Agent connected!'); } }); // 4. Render widget ``` Happy coding! 🚀