# Okidoki Widget SDK (full reference) > Full reference for the Okidoki chat widget: installation, custom tools, programmatic control, and the ask() API. To create an account and get a public key programmatically, see the install guide at https://okidoki.chat/llms.txt Important notes for LLM agents implementing features: - The widget loads via a script tag with `data-app-id` attribute (the value is your `pk_...` public key) - All methods are available on `window.OkidokiWidget` - Custom tools let the AI call your JavaScript functions - Tools run sequentially by default; use `parallel: true` for read-only tools - Use `reinitialize()` to switch apps/bots at runtime - Use `ask({ searchKnowledgeBase: true })` to query crawled website/documents ## Installation Add this script tag before ``: ```html ``` The widget auto-initializes. Get your public key from the Okidoki dashboard, or create one programmatically via the onboarding API described at https://okidoki.chat/llms.txt ## Custom Tools Register tools that the AI can call to interact with your page: ```javascript OkidokiWidget.registerTools([{ name: 'add_to_cart', description: 'Add a product to the shopping cart', input: { productId: 'The product ID to add', quantity: 'Number of items (optional, default: 1)' }, handler: async ({ productId, quantity = 1 }) => { await myStore.addToCart(productId, quantity); return { success: true, cartCount: myStore.getCartCount() }; } }]); ``` ### Input Schema Options ```javascript input: { // Simple string field (required) name: 'User name', // With type and options count: { description: 'Number of items', type: 'number', optional: true, default: 1 }, // Enum constraint priority: { description: 'Priority level', type: 'string', enum: ['low', 'medium', 'high'] } } ``` ## Execution Control Configure how tools execute: ```javascript OkidokiWidget.registerTools([{ name: 'translate_document', description: 'Translate the entire document', // Execution options timeout: 300000, // Max 5 min (default: 60s, max: 600000) lockGroup: 'doc_write', // Tools in same group can't run concurrently parallel: true, // Can run alongside any tool (for read-only) input: { targetLanguage: 'Target language code' }, handler: async ({ targetLanguage }) => { OkidokiWidget.setToolNotification('Translating...'); await translateDoc(targetLanguage); OkidokiWidget.setToolNotification(null); return { success: true }; } }]); ``` **Concurrency rules:** - Default: Tools run sequentially (one at a time) - `lockGroup`: Only blocks tools in the same group - `parallel: true`: Never blocks, never blocked (use for read-only) ## Widget API ### Initialization ```typescript // Switch to different app at runtime (multi-tenant, environment switching) OkidokiWidget.reinitialize(publicKey: string, apiUrl?: string): void // Example: Switch to different bot based on user context OkidokiWidget.reinitialize('OTHER_APP_PUBLIC_KEY'); ``` ### Language ```typescript OkidokiWidget.setLanguage(lang: 'en' | 'es'): void OkidokiWidget.getLanguage(): 'en' | 'es' ``` ### Actions ```typescript // Send form data to app owner's inbox OkidokiWidget.sendEmail(fields: Record): boolean // Open scheduling modal OkidokiWidget.scheduleMeeting(eventType?: string): void // Show/hide progress notification in chat OkidokiWidget.setToolNotification(message: string | null): void ``` ### Widget Control ```typescript // Get current widget state (null if not initialized) OkidokiWidget.getMode(): WidgetModeInfo | null interface WidgetModeInfo { active: 'text' | 'voice' | 'video'; // Current mode configured: string; // Backend config (e.g., 'CHAT_PLUS_VOICE') available: ('text' | 'voice' | 'video')[]; // Available modes isOpen: boolean; } // Open widget (optionally in specific mode) OkidokiWidget.openChat(options?: { mode?: 'text' | 'voice' | 'video' }): WidgetResult // Close widget OkidokiWidget.closeChat(): WidgetResult // Insert text into input (optionally auto-send) OkidokiWidget.insertMessage(text: string, options?: { send?: boolean }): WidgetResult // Clear input field OkidokiWidget.clearInput(): WidgetResult interface WidgetResult { success: boolean; error?: string; // Error message if success is false } ``` **Notes:** - `insertMessage` returns error in voice-only or video-only modes (no text input) - `openChat({ mode })` returns error if requested mode is not available - Use `getMode()` to check available modes before calling other methods ### Tool Management ```typescript OkidokiWidget.registerTools(tools: ToolDefinition[]): void OkidokiWidget.unregisterTools(names?: string[]): void // undefined = all OkidokiWidget.getRegisteredTools(): NormalizedTool[] OkidokiWidget.invokeTool(name: string, args?: Record): Promise ``` **Note:** `invokeTool` allows calling registered tools programmatically from your code, without going through the chat. Useful for reusing tool logic in buttons/UI elements. ## Programmatic AI (ask API) Call the LLM directly from your code with structured outputs: ```javascript const { array, object, string, number } = OkidokiWidget.helpers; const result = await OkidokiWidget.ask({ prompt: 'Find dates that need updating', context: documentContent, // Up to ~400k characters temperature: 0.2, // 0-1, lower = more deterministic output: { updates: array( object({ original: string('Original date text'), suggested: string('Updated date'), reason: string('Why update needed') }), 'List of date updates' ) } }); if (result.success) { // result.result is typed based on output schema for (const update of result.result.updates) { console.log(update.original, '->', update.suggested); } } ``` ### Knowledge Base Search (RAG) Query the app's crawled website and uploaded documents before answering: ```javascript const answer = await OkidokiWidget.ask({ prompt: 'What are your pricing plans?', searchKnowledgeBase: true // Enable RAG }); // answer.result = "Our plans start at $29/month..." // answer.sources = [{ title, url, type, relevance, snippet }] // Advanced options const result = await OkidokiWidget.ask({ prompt: 'Does this align with our policies?', context: documentText, searchKnowledgeBase: true, searchQuery: 'standard terms policies', // Custom search query searchSource: 'documents', // 'website' | 'documents' | 'all' searchLimit: 5 // Max results (1-5, default: 3) }); // Use relevance scores for confidence result.sources?.forEach(s => { const conf = s.relevance >= 0.85 ? 'high' : s.relevance >= 0.70 ? 'medium' : 'low'; console.log(`${s.title} (${conf} confidence)`); }); ``` **RAG Options:** - `searchKnowledgeBase`: boolean (default: false) - Enable knowledge base search - `searchQuery`: string (default: prompt) - Custom search query - `searchSource`: 'website' | 'documents' | 'all' (default: 'all') - `searchLimit`: number 1-5 (default: 3) - Max results to retrieve **Relevance Scores:** 0.85+ = high confidence, 0.70-0.85 = good, 0.55-0.70 = partial, <0.55 = low ### Schema Helpers ```javascript const { string, number, boolean, array, object, enum_ } = OkidokiWidget.helpers; // Simple types string('Description') number('Description') boolean('Description') // Enum enum_(['low', 'medium', 'high'], 'Priority level') // Nested object object({ name: string('Name'), age: number('Age') }, 'Person object') // Array of objects array( object({ id: string('ID'), price: number('Price in cents') }), 'List of items' ) ``` ### ask() Response ```typescript interface AskResponse { success: boolean; result?: T; // Parsed JSON if output schema provided messagesConsumed: number; error?: string; sources?: AskSource[]; // Only when searchKnowledgeBase=true } interface AskSource { title: string; // Document/page title url?: string; // URL if from website type: 'website' | 'document'; relevance: number; // 0-1 similarity score snippet: string; // ~200 char preview } ``` ## Optional - [Install guide for agents](https://okidoki.chat/llms.txt): How to create an account and get a public key programmatically - [Full Documentation](https://okidoki.chat/developers): Complete developer guide with examples - [Dashboard](https://okidoki.chat/admin): Get your public key and configure your widget - [Landing Page](https://okidoki.chat): Product overview and pricing