To integrate Sunshine Conversations (formerly Smooch) with Bolt.new, create an app in the Zendesk Sunshine Conversations dashboard, get your App ID and API key credentials, then embed the Web Messenger JavaScript widget via useEffect or build custom messaging flows using the REST API in Next.js API routes. The widget embed works in the Bolt preview; receiving webhook notifications from incoming messages requires a deployed URL.
Omnichannel Messaging with Sunshine Conversations in Bolt.new
Sunshine Conversations (formerly Smooch, acquired by Zendesk) solves a real problem for modern apps: your users are on many different messaging channels — some prefer WhatsApp, others Facebook Messenger, some use SMS, and enterprise customers want email. Sunshine Conversations provides a single unified API that abstracts over all these channels. You send a message via the Sunshine Conversations API once, and it gets delivered to the channel the user is on. Replies from any channel flow back to your app through a unified webhook. You build your messaging logic once.
For Bolt.new, there are two distinct integration paths. The Web Messenger widget is a JavaScript chat interface that you embed directly in your app. It creates a floating chat button that opens a real-time conversation panel — similar to Intercom or Zendesk Chat. The widget embeds via a script tag (injected through useEffect in React) and works immediately in Bolt's WebContainer preview without any server-side setup. Customer conversations appear in your Sunshine Conversations dashboard, and you or your agents respond there.
The REST API path is for programmatic messaging: sending automated notifications, creating conversations, and building custom chat UIs. API calls use HTTP Basic authentication with your App ID and API key — outbound HTTPS requests that work perfectly in Bolt's WebContainer. Incoming messages from users (webhooks) require a publicly accessible server to receive, which means webhook handling must be set up after deploying to Netlify or Bolt Cloud. Plan for deployment from the start when building webhook-dependent features.
Integration method
Sunshine Conversations has two integration paths for Bolt.new: the Web Messenger JavaScript widget (embed via useEffect script injection — works immediately in the Bolt preview) and the REST API (create/send messages via Next.js API routes using your App ID and API key pair as HTTP Basic auth). Receiving webhook events from incoming messages requires a deployed URL, as Bolt's WebContainer cannot accept incoming HTTP connections.
Prerequisites
- A Sunshine Conversations account — access via Zendesk at smooch.io or through Zendesk platform settings
- A Sunshine Conversations App created in the dashboard with your App ID noted
- An API key pair (key ID and secret) created in the Sunshine Conversations app settings
- A Bolt.new project using Next.js for the REST API approach, or Vite for embed-only
- A deployed URL (Netlify or Bolt Cloud) before setting up webhook handlers for incoming messages
Step-by-step guide
Create a Sunshine Conversations App and Get API Credentials
Create a Sunshine Conversations App and Get API Credentials
Log in to your Sunshine Conversations account at smooch.io or via your Zendesk account under the Sunshine Conversations section. If you do not have an account, sign up at smooch.io — Sunshine Conversations has a free developer tier. In the dashboard, click 'Create App' and give your app a name (typically your product or company name). After creating the app, you land on the app overview page. Copy your App ID — it is displayed prominently and looks like a short alphanumeric string (e.g., 5e6c3dab95943a0010b8d8f2). Next, create API key credentials. Go to the app's Settings → API Keys. Click 'Create API Key'. Give the key a name like 'Bolt Dev App'. The dashboard displays a key ID and key secret — copy both immediately. Sunshine Conversations uses HTTP Basic authentication: the key ID is the username and the key secret is the password. The Base64-encoded combination goes in the Authorization header. Add to your .env file: SMOOCH_APP_ID (your App ID), SMOOCH_KEY_ID (the API key ID), and SMOOCH_KEY_SECRET (the API key secret). For the Web Messenger widget, you also need NEXT_PUBLIC_SMOOCH_APP_ID (same value as SMOOCH_APP_ID but available client-side). The App ID is safe to expose publicly — it's in the client-side widget configuration. The key ID and secret are server-side only.
Set up the project for Sunshine Conversations integration. Create a .env.local file with SMOOCH_APP_ID=your_app_id, NEXT_PUBLIC_SMOOCH_APP_ID=your_app_id, SMOOCH_KEY_ID=your_key_id, SMOOCH_KEY_SECRET=your_key_secret. Create a src/lib/smooch.ts helper that exports a smoochFetch(path, options?) function calling https://api.smooch.io/v2/apps/${SMOOCH_APP_ID}/{path} with Basic auth using base64(keyId:keySecret) in the Authorization header. Export SMOOCH_APP_ID as a named constant. Use TypeScript.
Paste this in Bolt.new chat
1// src/lib/smooch.ts2const APP_ID = process.env.SMOOCH_APP_ID;3const BASE_URL = `https://api.smooch.io/v2/apps/${APP_ID}`;45function getBasicAuth(): string {6 const keyId = process.env.SMOOCH_KEY_ID;7 const keySecret = process.env.SMOOCH_KEY_SECRET;8 if (!keyId || !keySecret) throw new Error('Smooch API credentials not configured');9 return 'Basic ' + Buffer.from(`${keyId}:${keySecret}`).toString('base64');10}1112export async function smoochFetch<T>(13 path: string,14 options: RequestInit = {}15): Promise<T> {16 const response = await fetch(`${BASE_URL}/${path}`, {17 ...options,18 headers: {19 Authorization: getBasicAuth(),20 'Content-Type': 'application/json',21 ...options.headers,22 },23 });2425 if (!response.ok) {26 const error = await response.json().catch(() => ({ error: response.statusText }));27 throw new Error(`Smooch API ${response.status}: ${JSON.stringify(error)}`);28 }2930 return response.json();31}3233export const SMOOCH_APP_ID = APP_ID;Pro tip: Sunshine Conversations uses your App ID in every API URL path, not just as a query parameter. Make sure SMOOCH_APP_ID is correct — a wrong App ID causes 404 errors that look like credential failures.
Expected result: Your .env file has all four required variables. The smoochFetch helper handles Basic auth encoding automatically. Your App ID and API key pair are ready for both the widget embed and REST API calls.
Embed the Web Messenger Widget
Embed the Web Messenger Widget
The Sunshine Conversations Web Messenger is a prebuilt chat widget that loads via a script tag, renders a floating chat button, and handles the full conversation flow — message threading, typing indicators, file attachments, and channel switching. Embedding it in a Bolt.new React app requires injecting the script via useEffect since React's JSX cannot directly include external script tags. The Sunshine Conversations widget script loads from a CDN and requires your App ID to initialize. Once loaded, it creates a global window.Smooch object. You call window.Smooch.init() with your App ID to start the widget. You can optionally identify the current user (their email, name, and a unique ID from your auth system) so conversations are linked to known users in your dashboard. The widget works immediately in Bolt's WebContainer preview. The chat button appears, conversations are created in your Sunshine Conversations dashboard, and you can respond from the dashboard to test the flow. No deployment is needed to see the widget working — it uses WebSocket connections for real-time messaging, which Bolt's WebContainer supports. For production, the widget should be initialized with the logged-in user's details (externalId matching your user ID, email, and name) so conversations in Sunshine Conversations are linked to your user accounts. Anonymous sessions are possible but limit your ability to follow up with users.
Create a ChatWidget component at src/components/ChatWidget.tsx. Use useEffect to inject the Sunshine Conversations Web Messenger script (https://cdn.smooch.io/smooch.min.js) into document.head. After the script loads, call window.Smooch.init({ appId: process.env.NEXT_PUBLIC_SMOOCH_APP_ID, customColors: { brandColor: '2563eb', conversationColor: '2563eb', actionColor: '2563eb' }, canUserSeeConversationList: false, welcomeMessage: 'Hi! How can we help you today?' }). Add the component to the root layout so it appears on every page. Handle the case where the script is already loaded to prevent duplicate init.
Paste this in Bolt.new chat
1// src/components/ChatWidget.tsx2'use client';3import { useEffect } from 'react';45declare global {6 interface Window {7 Smooch: {8 init: (config: object) => Promise<void>;9 destroy: () => void;10 identify: (user: { externalId: string; email?: string; displayName?: string }) => void;11 };12 }13}1415interface ChatWidgetProps {16 userId?: string;17 userEmail?: string;18 userName?: string;19}2021export function ChatWidget({ userId, userEmail, userName }: ChatWidgetProps) {22 const appId = process.env.NEXT_PUBLIC_SMOOCH_APP_ID;2324 useEffect(() => {25 if (!appId || document.getElementById('smooch-script')) return;2627 const script = document.createElement('script');28 script.id = 'smooch-script';29 script.src = 'https://cdn.smooch.io/smooch.min.js';30 script.onload = async () => {31 await window.Smooch.init({32 appId,33 customColors: {34 brandColor: '2563eb',35 conversationColor: '2563eb',36 actionColor: '2563eb',37 },38 canUserSeeConversationList: false,39 welcomeMessage: 'Hi! How can we help you today?',40 });4142 // Identify the user if authenticated43 if (userId) {44 window.Smooch.identify({45 externalId: userId,46 email: userEmail,47 displayName: userName,48 });49 }50 };5152 document.head.appendChild(script);5354 return () => {55 if (window.Smooch) window.Smooch.destroy();56 document.getElementById('smooch-script')?.remove();57 };58 }, [appId, userId, userEmail, userName]);5960 return null; // Widget renders itself into the DOM61}Pro tip: Call window.Smooch.identify() with the logged-in user's ID and email to link conversations to known users. Without identification, conversations appear as anonymous sessions in your Sunshine Conversations dashboard, making it harder to provide personalized support.
Expected result: A floating chat button appears in the bottom-right corner of your app. Clicking it opens a chat panel. Messages sent by users appear in your Sunshine Conversations dashboard. The widget works immediately in Bolt's preview.
Send Messages via the REST API
Send Messages via the REST API
For programmatic messaging — automated notifications, outbound campaigns, or building a custom chat UI — use the Sunshine Conversations REST API from Next.js API routes. The API allows you to create users, create conversations, and post messages to any channel. The core messaging workflow: create a user (or look up an existing one by external ID), create or retrieve a conversation for that user, then post a message to the conversation. The message is delivered to the user via whatever channel they are on — web chat, WhatsApp, SMS, etc. This is the power of Sunshine Conversations' unified API: you post one message and it's delivered to the right channel automatically. Message types supported by the REST API include text messages, image attachments, file attachments, action buttons (postback, link, buy), carousels (scrollable list of items with images and buttons), and forms (structured data collection). For simple notifications, text messages are sufficient. For richer engagement (onboarding flows, interactive surveys), carousels and action buttons create much better user experiences. All REST API calls must happen server-side in Next.js API routes because they use your API key secret. Never call the Sunshine Conversations API directly from client-side React components.
Create /api/smooch/message/route.ts that accepts { externalUserId, text, channelType? } in the POST body. The route should: (1) look up or create a user by externalId using GET then POST /users, (2) get or create their conversation using GET /users/{userId}/conversations, (3) send a text message via POST /conversations/{conversationId}/messages with author.type='business'. Return the message ID on success. Also create /api/smooch/conversations/route.ts that GET-fetches all conversations for admin inbox view, returning conversation ID, last message, participant info, and channel type.
Paste this in Bolt.new chat
1// app/api/smooch/message/route.ts2import { NextRequest, NextResponse } from 'next/server';3import { smoochFetch } from '@/lib/smooch';45export async function POST(request: NextRequest) {6 const { externalUserId, text } = await request.json();7 if (!externalUserId || !text) {8 return NextResponse.json({ error: 'externalUserId and text are required' }, { status: 400 });9 }1011 try {12 // Step 1: Find or create user13 let user: any;14 try {15 user = await smoochFetch<any>(`users/${externalUserId}`);16 } catch {17 user = await smoochFetch<any>('users', {18 method: 'POST',19 body: JSON.stringify({ externalId: externalUserId }),20 });21 }2223 const userId = user.user?.id ?? user.id;2425 // Step 2: Get or create conversation26 const conversationsData = await smoochFetch<any>(`users/${userId}/conversations`);27 let conversationId: string;2829 if (conversationsData.conversations?.length > 0) {30 conversationId = conversationsData.conversations[0].id;31 } else {32 const newConv = await smoochFetch<any>('conversations', {33 method: 'POST',34 body: JSON.stringify({35 type: 'personal',36 participants: [{ userId, subscribeSDKClient: false }],37 }),38 });39 conversationId = newConv.conversation.id;40 }4142 // Step 3: Send message43 const message = await smoochFetch<any>(`conversations/${conversationId}/messages`, {44 method: 'POST',45 body: JSON.stringify({46 author: { type: 'business' },47 content: { type: 'text', text },48 }),49 });5051 return NextResponse.json({ messageId: message.message.id, conversationId });52 } catch (err: any) {53 return NextResponse.json({ error: err.message }, { status: 500 });54 }55}Pro tip: Sunshine Conversations distinguishes between 'personal' conversations (one-on-one between a user and your business) and 'sdkGroup' conversations (group chats). For support and notifications, always use personal conversations.
Expected result: The /api/smooch/message endpoint finds or creates a user, creates or retrieves their conversation, and sends a text message that appears in the Sunshine Conversations dashboard. Messages sent via API appear in the same unified inbox as messages from the Web Messenger widget.
Set Up Webhook Handlers for Incoming Messages
Set Up Webhook Handlers for Incoming Messages
When users reply to your messages or send new messages via any channel, Sunshine Conversations POSTs a webhook event to your server. Webhook URLs must be publicly accessible — Bolt's WebContainer cannot receive incoming HTTP connections, so webhook setup requires a deployed application. After deploying to Netlify or Bolt Cloud, go to your Sunshine Conversations app settings → Webhooks. Click 'Add webhook'. Enter your deployed URL: https://your-app.netlify.app/api/smooch/webhook. Select the triggers you want to receive: conversation:message (new message from user), conversation:read (user read your message), and any others relevant to your use case. Save the webhook configuration. In your app, create the webhook handler API route at /api/smooch/webhook. Sunshine Conversations includes a X-API-Key header in webhook requests for verification — check that this header matches your secret key to verify the request is genuinely from Sunshine Conversations. Parse the event body to extract the conversation ID, message content, user ID, and channel type, then process accordingly (save to database, trigger notifications, queue a response). For development testing without a deployed URL, use ngrok or Cloudflare Tunnel to create a temporary public URL forwarding to your local Next.js server — then register that temporary URL as the webhook endpoint. This is the fastest way to test webhook behavior without a full deploy.
Create /api/smooch/webhook/route.ts that handles Sunshine Conversations webhook events. Parse the JSON body to extract event.type and determine if it's a new message (conversation:message). If it is, extract: conversationId, userId, message text, and channel type from the event payload. Save the incoming message to a Supabase 'messages' table with fields: conversation_id, user_id, content, channel, direction='inbound', created_at. Return 200 OK. Also create a /api/smooch/webhook/route.ts that verifies the request came from Sunshine Conversations using the X-API-Key header compared to SMOOCH_WEBHOOK_SECRET env var.
Paste this in Bolt.new chat
1// app/api/smooch/webhook/route.ts2import { NextRequest, NextResponse } from 'next/server';34export async function POST(request: NextRequest) {5 // Verify webhook is from Sunshine Conversations6 const apiKey = request.headers.get('x-api-key');7 const expectedSecret = process.env.SMOOCH_WEBHOOK_SECRET;89 if (expectedSecret && apiKey !== expectedSecret) {10 return new NextResponse('Unauthorized', { status: 401 });11 }1213 const event = await request.json();1415 // Handle different event types16 switch (event.events?.[0]?.type) {17 case 'conversation:message': {18 const message = event.events[0].payload.message;19 const conversation = event.payload?.conversation;2021 // Only process messages from users, not from your business22 if (message?.author?.type === 'user') {23 console.log('New message from user:', {24 conversationId: conversation?.id,25 userId: message.author.userId,26 content: message.content?.text,27 channel: message.source?.type,28 });2930 // TODO: Save to database, trigger notification, queue auto-response31 }32 break;33 }3435 case 'conversation:read':36 console.log('Message read by user:', event.events[0].payload);37 break;3839 default:40 console.log('Unhandled event type:', event.events?.[0]?.type);41 }4243 return NextResponse.json({ ok: true });44}Pro tip: Register your deployed webhook URL in Sunshine Conversations before testing. Webhooks during development in Bolt's WebContainer will not be received — the WebContainer cannot accept incoming HTTP connections. Use ngrok for local webhook testing: ngrok http 3000 gives you a public URL to register temporarily.
Expected result: After deploying and registering the webhook URL in Sunshine Conversations, your /api/smooch/webhook endpoint receives events when users send messages or read your messages. Webhook data is processed and saved to your database for building the admin inbox view.
Common use cases
Embedded Live Chat Support Widget
Add a live chat widget to your app using Sunshine Conversations' Web Messenger. The widget appears as a floating button, opens a chat panel, and connects customers to your support team across any channel. Conversations are managed in the Sunshine Conversations dashboard.
Add a Sunshine Conversations live chat widget to my app. Create a ChatWidget React component that uses useEffect to inject the Sunshine Conversations Web Messenger script into the page. The widget should load with my App ID from NEXT_PUBLIC_SMOOCH_APP_ID. Configure the widget with a custom icon color matching our brand (#2563eb), a welcome message 'Hi! How can we help you today?', and the company name from an env variable. Load the widget on every page via the root layout. Don't show the widget on the admin pages.
Copy this prompt to try it in Bolt.new
Automated WhatsApp Notifications
Send order confirmations, shipping updates, and alerts to users on WhatsApp by creating a Sunshine Conversations user linked to their WhatsApp number and sending messages via the REST API. The messages appear in the user's WhatsApp app.
Build a notification system that sends order status updates via WhatsApp using Sunshine Conversations API. Create /api/messaging/notify/route.ts that accepts { userId, phone, message } and does: (1) create a Sunshine Conversations user if they don't exist via POST /v2/apps/{appId}/users, (2) link their WhatsApp channel via POST /v2/apps/{appId}/users/{userId}/channels, (3) send a message via POST /v2/apps/{appId}/conversations/{conversationId}/messages. Store SMOOCH_APP_ID and SMOOCH_API_KEY in .env. Trigger this from the order confirmation flow.
Copy this prompt to try it in Bolt.new
Multichannel Customer Inbox
Build an admin dashboard that shows all customer conversations across channels — WhatsApp, SMS, web chat, and Facebook Messenger — in one unified inbox. Use the Sunshine Conversations API to list conversations, fetch messages, and reply to any channel from your app.
Create an admin messaging inbox at /admin/inbox using Sunshine Conversations API. Fetch the latest 20 conversations from GET /v2/apps/{appId}/conversations, sorted by most recent message. For each conversation, show: the customer's name or phone number, their channel icon (WhatsApp, SMS, etc.), the last message preview, and timestamp. Clicking a conversation loads the full message history. Add a reply text field that calls POST /v2/apps/{appId}/conversations/{id}/messages to send a response. Refresh the inbox every 30 seconds to show new messages.
Copy this prompt to try it in Bolt.new
Troubleshooting
Web Messenger widget shows error 'App not found' and fails to load
Cause: The NEXT_PUBLIC_SMOOCH_APP_ID environment variable contains an incorrect or outdated App ID. Sunshine Conversations App IDs are specific to each app you create in the dashboard.
Solution: Go to your Sunshine Conversations dashboard, open your app, and copy the App ID from the Overview page. Verify it matches NEXT_PUBLIC_SMOOCH_APP_ID in your .env file exactly. App IDs are case-sensitive alphanumeric strings.
REST API calls return 401 Unauthorized with 'Invalid credentials' even though key ID and secret look correct
Cause: The HTTP Basic auth encoding is incorrect. Basic auth requires base64 encoding of 'keyId:keySecret' (colon-separated), not the key ID and secret separately.
Solution: Verify that the Authorization header is formatted as 'Basic ' + base64(keyId + ':' + keySecret). A common mistake is encoding them separately or using the wrong separator.
1// Correct Basic auth encoding2const auth = Buffer.from(`${process.env.SMOOCH_KEY_ID}:${process.env.SMOOCH_KEY_SECRET}`).toString('base64');3const headers = { 'Authorization': `Basic ${auth}` };Webhook events are not arriving at /api/smooch/webhook even though it's registered
Cause: The webhook URL was registered while testing in the Bolt WebContainer preview — Bolt's preview URLs are dynamic and ephemeral, so Sunshine Conversations cannot reach them. Webhook delivery requires a stable, publicly accessible URL.
Solution: Deploy your app to Netlify or Bolt Cloud, then update the webhook URL in Sunshine Conversations to your deployed domain (e.g., https://your-app.netlify.app/api/smooch/webhook). In the Sunshine Conversations dashboard, go to Settings → Webhooks, delete the old URL, and add the new deployed URL. Webhook delivery begins within seconds of updating.
Best practices
- Always deploy before configuring Sunshine Conversations webhooks — the WebContainer preview URL is dynamic and cannot receive incoming webhook events.
- Initialize the Web Messenger with user identification (externalId, email, displayName) for authenticated users so conversations are linked to known accounts in your dashboard.
- Keep SMOOCH_KEY_ID and SMOOCH_KEY_SECRET strictly server-side in API routes — these credentials grant full API access including creating messages and accessing all conversation history.
- Handle idempotency for webhook events — Sunshine Conversations may deliver the same event more than once. Save a webhook event ID and check for duplicates before processing.
- Use the externalId field when creating Sunshine Conversations users to link them to your existing user IDs — this prevents duplicate user creation and maintains conversation history correctly.
- Cache conversation IDs for known users rather than looking them up on every message send — the user → conversation lookup involves two API calls and can be optimized with a simple in-memory or database cache.
- Configure separate webhook endpoints for different event types if your app processes them differently — this makes webhook handler code cleaner and easier to test.
Alternatives
Zendesk is the parent platform that includes Sunshine Conversations — if you need a full helpdesk ticketing system alongside messaging, use Zendesk directly.
LiveChat is a simpler dedicated live chat product with a clean embed API, better for basic chat-only use cases without multi-channel requirements.
Intercom offers a similar chat widget with CRM features, better for product-led growth apps that want to combine messaging with user analytics and feature tours.
Twilio provides direct SMS and WhatsApp messaging APIs without the unified inbox layer, giving lower-level control for apps that only need specific channel messaging without omnichannel routing.
Frequently asked questions
Can I use Sunshine Conversations in Bolt.new during development without deploying?
The Web Messenger widget embed works immediately in Bolt's WebContainer preview — the widget loads from Sunshine Conversations' CDN, and you can send and receive messages in real time. REST API calls for sending messages also work in the preview since they are outbound HTTPS requests. Only incoming webhooks (for receiving user messages in your server) require a deployed URL, as Bolt's WebContainer cannot accept incoming HTTP connections.
Does Sunshine Conversations work with WhatsApp?
Yes — Sunshine Conversations supports WhatsApp Business as one of its channel integrations. After connecting a WhatsApp Business Account in your Sunshine Conversations dashboard, you can send messages to WhatsApp users via the same REST API used for web chat. Users reply via WhatsApp and their messages appear in your unified inbox alongside web chat messages.
What is the relationship between Smooch and Sunshine Conversations?
Smooch was an independent company that built a unified messaging API. Zendesk acquired Smooch in 2019 and rebranded it as Zendesk Sunshine Conversations. The API, SDKs, and overall architecture remain the same — the main change is that access is now through the Zendesk platform. The old smooch.io domain still redirects to the product. Most community documentation still refers to it as Smooch.
Is Sunshine Conversations free to use?
Sunshine Conversations has a developer/sandbox tier that allows testing integrations for free. Production use requires a Zendesk Sunshine Conversations plan, which is priced based on message volumes and channels. Check the current pricing at smooch.io or the Zendesk pricing page — pricing structures have changed since the Zendesk acquisition.
How do I test webhook events from Sunshine Conversations during development?
Use ngrok (ngrok.com — free tier available) to create a temporary public URL that forwards to your local Next.js server running on port 3000. Run 'npx ngrok http 3000' to get a URL like https://abc123.ngrok.io, then register https://abc123.ngrok.io/api/smooch/webhook in Sunshine Conversations. This lets you receive and debug webhook events locally without deploying. Note that ngrok URLs change on each run — update the webhook URL each time.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation