List view
Understanding Nobi
Understanding Nobi
Getting Started
Getting Started
Implement Nobi On Your Site
Implement Nobi On Your Site
Answer Customer Questions With Your Knowledge Base
Answer Customer Questions With Your Knowledge Base
Control How Nobi Shows Products With Merchandising
Control How Nobi Shows Products With Merchandising
Analyze Your Data
Analyze Your Data
Add Your Own Custom Actions To Nobi
Add Your Own Custom Actions To Nobi
Control How Nobi Responds With Override Rules
Control How Nobi Responds With Override Rules
Beta Products
Beta Products
Developers Guide
Developers Guide
References
References
Custom Actions Use Cases And Examples
Custom Actions can automate virtually any browser-side workflow. This page shows real-world examples with complete, working code you can adapt for your business.
Lead Generation & Contact Forms
Capture customer information the moment they express interest, without redirecting to a separate form page.
When to Use This
Perfect for:
- Service businesses capturing consultation requests
- B2B companies generating sales leads
- High-ticket items requiring personal follow-up
- Any business where immediate lead capture matters
Business Impact
- Higher completion rates - Forms appear in context, at the moment of interest
- Lower abandonment - No navigation away from conversation
- Faster response times - Leads captured immediately, routed to your CRM
- Better qualification - Natural conversation reveals intent before form submission
Complete Working Example
window.NobiTools = [ { name: "submitContactForm", description: "Submit a contact form to get in touch with the sales team. Collects name, email, phone, and message from customers who want to speak with us, request a quote, schedule a consultation, or learn more about our services", parameters: { type: "object", properties: { name: { type: "string", description: "Customer's full name" }, email: { type: "string", description: "Customer's email address for follow-up" }, phone: { type: "string", description: "Customer's phone number (optional)" }, message: { type: "string", description: "What the customer wants to discuss or ask about" } }, required: ["name", "email", "message"] }, handler: async function(args) { try { // Submit to your backend API const response = await fetch('/api/contact', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: args.name, email: args.email, phone: args.phone || '', message: args.message, source: 'nobi_custom_action', timestamp: new Date().toISOString() }) }); if (!response.ok) { throw new Error('Failed to submit contact form'); } const data = await response.json(); return { success: true, confirmationId: data.id, message: `Thanks ${args.name}! We've received your message and someone from our team will reach out to ${args.email} within 24 hours. Your reference number is ${data.id}.` }; } catch (error) { console.error('Contact form error:', error); return { success: false, message: "We're sorry, there was an issue submitting your message. Please email us directly at [email protected] or call (555) 123-4567." }; } } } ];
How It Works
User interaction:
- Customer: "I'd like to speak with someone about your services"
- Nobi recognizes this matches the contact form tool
- Form appears asking for name, email, phone (optional), and message
- Customer fills form and submits
- Handler sends data to your backend API
- Nobi confirms: "Thanks John! We've received your message..."
What the LLM extracts from conversation:
- If customer mentions their name in chat, that field is pre-filled
- If they describe what they want, the message field is pre-filled
- Only missing required fields appear in the form
Customization Tips
Connect to your CRM:
// Example: Send to HubSpot const response = await fetch('<https://api.hubspot.com/contacts/v1/contact>', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer YOUR_API_KEY' }, body: JSON.stringify({ properties: [ { property: 'email', value: args.email }, { property: 'firstname', value: args.name }, { property: 'phone', value: args.phone }, { property: 'message', value: args.message } ] }) });
Add lead source tracking:
body: JSON.stringify({ name: args.name, email: args.email, message: args.message, lead_source: 'nobi_chat', page_url: window.location.href, utm_source: getUTMParameter('utm_source'), utm_campaign: getUTMParameter('utm_campaign') })
Trigger email notifications:
// After successful submission if (data.success) { // Send notification to sales team await fetch('/api/notify-sales', { method: 'POST', body: JSON.stringify({ lead_id: data.id, priority: args.message.includes('urgent') ? 'high' : 'normal' }) }); }
Shipping & Delivery Calculator
Let customers calculate shipping costs and delivery times without leaving the conversation.
When to Use This
Perfect for:
- E-commerce stores with variable shipping costs
- Products with size/weight-based shipping
- International shipping with country-specific rates
- Businesses offering multiple shipping speed options
Business Impact
- Reduce cart abandonment - Answer pricing questions before checkout
- Set expectations - Show delivery times upfront
- Increase conversions - Remove uncertainty about total cost
- Fewer support tickets - Automated answers to "how much is shipping?"
Complete Working Example
window.NobiTools = [ { name: "calculateShipping", description: "Calculate shipping cost and estimated delivery time based on the customer's destination. Use when customers ask about shipping costs, delivery times, or how long shipping takes to their location", parameters: { type: "object", properties: { zipCode: { type: "string", description: "Destination ZIP or postal code" }, country: { type: "string", description: "Destination country (default to US if not specified)" }, shippingMethod: { type: "string", description: "Shipping speed: 'standard' (5-7 days), 'express' (2-3 days), or 'overnight' (1 day)" } }, required: ["zipCode", "shippingMethod"] }, handler: async function(args) { try { // Get current cart contents const cart = window.shopData?.cart || { items: [], subtotal: 0 }; // Call your shipping API const response = await fetch('/api/shipping/calculate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ destination: { zipCode: args.zipCode, country: args.country || 'US' }, method: args.shippingMethod, cartSubtotal: cart.subtotal, items: cart.items }) }); if (!response.ok) { throw new Error('Unable to calculate shipping'); } const data = await response.json(); // Build response message let message = `${args.shippingMethod.charAt(0).toUpperCase() + args.shippingMethod.slice(1)} shipping to ${args.zipCode}`; if (data.cost === 0) { message += ` is FREE! 🎉`; } else { message += `: $${data.cost.toFixed(2)}`; } message += ` Estimated delivery: ${data.estimatedDays} business days.`; return { success: true, cost: data.cost, estimatedDays: data.estimatedDays, method: args.shippingMethod, message: message }; } catch (error) { console.error('Shipping calculation error:', error); return { success: false, message: "I'm having trouble calculating shipping for that location. Please try again or contact us at [email protected] for a quote." }; } } } ];
How It Works
User interaction:
- Customer: "How much would express shipping to 90210 cost?"
- Nobi extracts: zipCode = "90210", shippingMethod = "express"
- Handler calls your shipping API with cart data
- Nobi responds: "Express shipping to 90210: $12.99. Estimated delivery: 2-3 business days."
Smart extraction:
- "How long does shipping take?" → Form asks for ZIP and method
- "Shipping to Chicago" → Extracts city, asks for specific ZIP and method
- "How much is overnight shipping?" → Extracts method, asks for ZIP
Customization Tips
Add free shipping threshold:
handler: async function(args) { const cart = window.shopData?.cart; const freeShippingThreshold = 50; if (cart.subtotal >= freeShippingThreshold) { return { success: true, cost: 0, message: `Your order qualifies for FREE ${args.shippingMethod} shipping! (orders over $${freeShippingThreshold})` }; } // Continue with normal calculation... }
Handle international shipping:
if (args.country && args.country !== 'US') { // Different API endpoint or logic for international const response = await fetch('/api/shipping/international', { method: 'POST', body: JSON.stringify({ country: args.country, zipCode: args.zipCode, weight: calculateCartWeight(cart.items) }) }); }
Show multiple shipping options:
// Calculate all options at once const methods = ['standard', 'express', 'overnight']; const quotes = await Promise.all( methods.map(method => calculateShipping(args.zipCode, method)) ); return { success: true, message: `Shipping to ${args.zipCode}:\\n• Standard (5-7 days): $${quotes[0].cost}\\n• Express (2-3 days): $${quotes[1].cost}\\n• Overnight: $${quotes[2].cost}` };
Appointment Booking
Enable customers to book appointments directly through conversation, with real-time availability checking.
When to Use This
Perfect for:
- Service businesses (salons, consultations, repairs)
- Healthcare providers (doctors, dentists, therapists)
- Professional services (lawyers, accountants, coaches)
- Retail with appointment-based services
Business Impact
- 24/7 booking - Accept appointments even when you're closed
- Reduce phone scheduling - Automate routine booking workflows
- Higher conversion - Book while customer is engaged
- Fewer no-shows - Instant confirmation with appointment details
Complete Working Example
window.NobiTools = [ { name: "bookAppointment", description: "Book an appointment for a specific service, date, and time. Use when customers want to schedule an appointment, book a consultation, reserve a time slot, or set up a meeting", parameters: { type: "object", properties: { service: { type: "string", description: "Type of service: 'consultation', 'haircut', 'repair', 'checkup', etc." }, date: { type: "string", description: "Preferred appointment date (YYYY-MM-DD format or natural language like 'next Tuesday')" }, time: { type: "string", description: "Preferred time (e.g., '2:00 PM', '14:00', 'morning', 'afternoon')" }, name: { type: "string", description: "Customer's full name" }, phone: { type: "string", description: "Customer's phone number for appointment confirmation" }, notes: { type: "string", description: "Any special requests or additional information (optional)" } }, required: ["service", "date", "time", "name", "phone"] }, handler: async function(args) { try { // Step 1: Check availability const availabilityResponse = await fetch('/api/appointments/check-availability', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ service: args.service, date: args.date, time: args.time }) }); const availability = await availabilityResponse.json(); if (!availability.available) { return { success: false, message: `I'm sorry, but ${args.time} on ${args.date} is not available. Here are some open times that day: ${availability.alternativeTimes.join(', ')}. Would you like to book one of these instead?` }; } // Step 2: Create booking const bookingResponse = await fetch('/api/appointments/book', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ service: args.service, date: args.date, time: availability.normalizedTime, // Use exact time from availability check customerName: args.name, customerPhone: args.phone, notes: args.notes || '', source: 'nobi_custom_action' }) }); if (!bookingResponse.ok) { throw new Error('Failed to create booking'); } const booking = await bookingResponse.json(); return { success: true, confirmationNumber: booking.confirmationNumber, appointmentDate: booking.date, appointmentTime: booking.time, message: `Perfect! Your ${args.service} appointment is confirmed for ${booking.formattedDate} at ${booking.formattedTime}. Confirmation number: ${booking.confirmationNumber}. We've sent a confirmation text to ${args.phone}. See you then!` }; } catch (error) { console.error('Appointment booking error:', error); return { success: false, message: "I'm having trouble booking that appointment right now. Please call us at (555) 123-4567 to schedule, or try again in a few minutes." }; } } } ];
How It Works
User interaction:
- Customer: "I need a haircut next Tuesday at 2pm"
- Nobi extracts: service = "haircut", date = "next Tuesday", time = "2pm"
- Form appears asking for name and phone (missing required fields)
- Customer fills form and submits
- Handler checks availability → available
- Handler creates booking
- Nobi confirms: "Perfect! Your haircut appointment is confirmed for Tuesday, March 12th at 2:00 PM..."
Availability handling:
If requested time isn't available:
If requested time isn't available:
- Handler returns alternative times
- Nobi: "I'm sorry, but 2:00 PM is not available. Here are some open times: 10:00 AM, 1:00 PM, 3:30 PM. Would you like one of these?"
- Customer: "I'll take 3:30 PM"
- Handler books the alternative time
Customization Tips
Add buffer time between appointments:
// Check availability API includes buffer const availabilityResponse = await fetch('/api/appointments/check-availability', { method: 'POST', body: JSON.stringify({ service: args.service, date: args.date, time: args.time, bufferMinutes: 15 // Require 15 min between appointments }) });
Send confirmation emails/SMS:
// After successful booking if (booking.success) { // Send confirmation SMS await fetch('/api/sms/send', { method: 'POST', body: JSON.stringify({ to: args.phone, message: `Appointment confirmed: ${args.service} on ${booking.formattedDate} at ${booking.formattedTime}. Confirmation: ${booking.confirmationNumber}` }) }); // Send confirmation email if email was collected if (args.email) { await fetch('/api/email/send-confirmation', { method: 'POST', body: JSON.stringify({ to: args.email, appointmentDetails: booking }) }); } }
Add reminders:
// Schedule reminder for 24 hours before await fetch('/api/appointments/schedule-reminder', { method: 'POST', body: JSON.stringify({ appointmentId: booking.id, reminderTime: '24_hours_before', customerPhone: args.phone }) });
Inventory Checker
Let customers check real-time product availability at specific store locations.
When to Use This
Perfect for:
- Retail with multiple store locations
- Buy online, pick up in store (BOPIS)
- Products with limited stock or high demand
- Regional availability differences
Business Impact
- Drive store traffic - Show what's in stock nearby
- Reduce wasted trips - Customers know before they go
- Increase online-to-offline - Bridge digital and physical shopping
- Better inventory management - Surface availability data customers need
Complete Working Example
window.NobiTools = [ { name: "checkInventory", description: "Check if a specific product is in stock at a store location. Use when customers ask about availability, stock status, if we have something in store, or where they can find a product", parameters: { type: "object", properties: { productId: { type: "string", description: "Product SKU, ID, or name to check" }, location: { type: "string", description: "Store location: 'downtown', 'mall', 'airport', 'west side', or specific city" }, zipCode: { type: "string", description: "Customer's ZIP code to find nearest store (optional if location specified)" } }, required: ["productId"] }, handler: async function(args) { try { // Determine which store(s) to check let storesToCheck = []; if (args.location) { // Check specific location storesToCheck = [args.location]; } else if (args.zipCode) { // Find nearest stores by ZIP const nearbyResponse = await fetch(`/api/stores/nearby?zip=${args.zipCode}`); const nearby = await nearbyResponse.json(); storesToCheck = nearby.stores.map(s => s.id); } else { // Check all stores storesToCheck = ['all']; } // Check inventory const response = await fetch('/api/inventory/check', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ productId: args.productId, stores: storesToCheck }) }); if (!response.ok) { throw new Error('Unable to check inventory'); } const data = await response.json(); if (!data.found) { return { success: false, message: `I couldn't find a product with that ID or name. Could you provide the product name or SKU?` }; } // Build response based on availability const available = data.results.filter(r => r.inStock && r.quantity > 0); const unavailable = data.results.filter(r => !r.inStock || r.quantity === 0); if (available.length === 0) { let message = `${data.productName} is currently out of stock`; if (args.location) { message += ` at our ${args.location} location`; } else { message += ` at all locations`; } message += `. Would you like me to notify you when it's back in stock?`; return { success: true, inStock: false, productName: data.productName, message: message }; } // Build availability message let message = `Great news! ${data.productName} is in stock:\\n\\n`; available.forEach(store => { message += `• ${store.storeName}: ${store.quantity} available`; if (store.distance) { message += ` (${store.distance} miles away)`; } message += `\\n`; }); if (unavailable.length > 0 && args.location === undefined) { message += `\\nOut of stock at: ${unavailable.map(s => s.storeName).join(', ')}`; } message += `\\nWould you like me to reserve one for pickup?`; return { success: true, inStock: true, productName: data.productName, availableLocations: available, message: message }; } catch (error) { console.error('Inventory check error:', error); return { success: false, message: "I'm having trouble checking inventory right now. Please call your nearest store or try again in a moment." }; } } } ];
How It Works
User interaction:
- Customer: "Do you have the red dress in stock at your downtown location?"
- Nobi extracts: productId = "red dress", location = "downtown"
- Handler checks inventory at downtown store
- Nobi responds: "Great news! Red Midi Dress is in stock: Downtown: 3 available. Would you like me to reserve one?"
Multiple locations:
- Customer: "Where can I find the blue sneakers near 90210?"
- Nobi extracts: productId = "blue sneakers", zipCode = "90210"
- Handler finds nearby stores and checks each
- Nobi responds with list of stores that have it in stock, sorted by distance
Customization Tips
Add reserve functionality:
// After showing availability, add a follow-up action window.NobiTools.push({ name: "reserveProduct", description: "Reserve a product for store pickup", parameters: { type: "object", properties: { productId: { type: "string", description: "Product to reserve" }, storeId: { type: "string", description: "Store location" }, customerName: { type: "string", description: "Customer name" }, customerPhone: { type: "string", description: "Phone for pickup notification" } }, required: ["productId", "storeId", "customerName", "customerPhone"] }, handler: async function(args) { const response = await fetch('/api/inventory/reserve', { method: 'POST', body: JSON.stringify(args) }); const data = await response.json(); return { success: true, message: `Reserved! We're holding ${data.productName} at ${data.storeName} for 24 hours. We'll text you at ${args.customerPhone} when it's ready for pickup.` }; } });
Show online availability:
// Check both store and online inventory const [storeInventory, onlineInventory] = await Promise.all([ fetch('/api/inventory/stores', { body: JSON.stringify({ productId: args.productId }) }), fetch('/api/inventory/online', { body: JSON.stringify({ productId: args.productId }) }) ]); let message = ''; if (storeData.available) { message += `In store: ${storeData.locations.join(', ')}\\n`; } if (onlineData.available) { message += `Online: ${onlineData.quantity} available for shipping`; }
Add product recommendations:
if (!data.found || available.length === 0) { // Suggest alternatives const alternatives = await fetch(`/api/products/similar?id=${args.productId}`); const similar = await alternatives.json(); return { success: true, message: `That item is out of stock, but here are some similar options: ${similar.products.map(p => p.name).join(', ')}. Would you like to see any of these?` }; }
Bonus Example: Newsletter Signup
Simple, no-backend newsletter signup using a third-party service.
Complete Working Example
window.NobiTools = [ { name: "subscribeNewsletter", description: "Subscribe customer to our email newsletter when they want to receive updates, promotions, or stay in touch", parameters: { type: "object", properties: { email: { type: "string", description: "Customer's email address" }, firstName: { type: "string", description: "Customer's first name (optional)" } }, required: ["email"] }, handler: async function(args) { try { // Example: Mailchimp API const response = await fetch('/api/newsletter/subscribe', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email: args.email, firstName: args.firstName || '', source: 'nobi_chat' }) }); if (!response.ok) { const error = await response.json(); if (error.title === 'Member Exists') { return { success: true, message: `${args.email} is already subscribed! You won't miss any updates.` }; } throw new Error('Subscription failed'); } return { success: true, message: `Thanks${args.firstName ? ' ' + args.firstName : ''}! You're now subscribed to our newsletter at ${args.email}. Check your inbox for a confirmation email.` }; } catch (error) { console.error('Newsletter subscription error:', error); return { success: false, message: "There was an issue subscribing you to our newsletter. Please try again or email us at [email protected]" }; } } } ];
Bonus Example: Store Hours & Contact Info
Super simple, no-backend tool that provides information.
Complete Working Example
window.NobiTools = [ { name: "getStoreHours", description: "Tell customer our store hours, when we're open, business hours, or operating hours", parameters: { type: "object", properties: {}, required: [] }, handler: function(args) { const today = new Date().getDay(); // 0 = Sunday, 6 = Saturday const hour = new Date().getHours(); let message = "Our hours are:\\n"; message += "• Monday-Friday: 9 AM - 6 PM\\n"; message += "• Saturday: 10 AM - 4 PM\\n"; message += "• Sunday: Closed\\n\\n"; // Tell them if we're open right now if (today === 0) { message += "We're currently closed (Sunday). We'll be open Monday at 9 AM."; } else if (today === 6) { if (hour >= 10 && hour < 16) { message += "We're open right now!"; } else { message += "We're currently closed. We'll reopen at 10 AM."; } } else { if (hour >= 9 && hour < 18) { message += "We're open right now!"; } else if (hour < 9) { message += "We're currently closed. We'll open at 9 AM."; } else { message += "We're currently closed. We'll reopen tomorrow at 9 AM."; } } message += "\\n\\nYou can also call us at (555) 123-4567 or email [email protected]"; return { success: true, message: message }; } } ];
Tips for Creating Your Own
Start with Business Goals
Ask yourself:
- What actions do customers currently have to leave chat to complete?
- What questions do customers ask that require back-and-forth?
- What friction points exist in your customer journey?
- What manual processes could be automated?
Keep It Simple Initially
For your first Custom Action:
- Choose something low-risk (not payment processing)
- Pick a clear use case (contact form, not "do everything")
- Start without requiring too many parameters
- Test thoroughly before expanding
Write Good Descriptions
The
description field is crucial for the AI to understand when to use your tool:Bad: "Submit form"
Good: "Submit a contact form to get in touch with the sales team when customers want to speak with us, request a quote, schedule a consultation, or learn more"
Good: "Submit a contact form to get in touch with the sales team when customers want to speak with us, request a quote, schedule a consultation, or learn more"
Include:
- What the tool does
- When it should be used
- Synonyms and variations of the request
Handle Errors Gracefully
Always provide fallback options:
catch (error) { return { success: false, message: "I'm having trouble with that right now. You can also call us at (555) 123-4567 or email [email protected]" }; }
Test with Real Queries
After implementing, test with natural language variations:
- "Can I talk to someone?"
- "I need help"
- "Contact sales"
- "Get in touch"
- "Speak with a person"
All should trigger your contact form tool.
Monitor and Iterate
Track:
- How often the tool is invoked
- Completion rates
- Common errors
- User feedback
Use this data to refine your implementation.
Custom Actions Use Cases And ExamplesLead Generation & Contact FormsWhen to Use ThisBusiness ImpactComplete Working ExampleHow It WorksCustomization TipsShipping & Delivery CalculatorWhen to Use ThisBusiness ImpactComplete Working ExampleHow It WorksCustomization TipsAppointment BookingWhen to Use ThisBusiness ImpactComplete Working ExampleHow It WorksCustomization TipsInventory CheckerWhen to Use ThisBusiness ImpactComplete Working ExampleHow It WorksCustomization TipsBonus Example: Newsletter SignupComplete Working ExampleBonus Example: Store Hours & Contact InfoComplete Working ExampleTips for Creating Your OwnStart with Business GoalsKeep It Simple InitiallyWrite Good DescriptionsHandle Errors GracefullyTest with Real QueriesMonitor and Iterate