To integrate Zendesk Sunshine Conversations (formerly Smooch) with Lovable, create an app in the Sunshine Conversations Dashboard to get App ID and API key credentials, store them in Cloud → Secrets, then build Edge Functions that use JWT authentication to manage conversations, send messages, and unify messaging across SMS, WhatsApp, Facebook Messenger, and web channels. This extends your Zendesk integration with a true omnichannel messaging layer.
Build unified omnichannel messaging into Lovable with Sunshine Conversations API
Sunshine Conversations (formerly Smooch, acquired by Zendesk in 2019) is the messaging infrastructure layer that powers Zendesk's omnichannel capabilities. While Zendesk Support is a ticket-based system for structured support workflows, Sunshine Conversations is a real-time messaging API that unifies conversations from SMS, WhatsApp, Facebook Messenger, Instagram Direct, LINE, WeChat, and web widgets into a single platform. For Lovable apps that need to send and receive messages across multiple customer channels, Sunshine Conversations is the architectural foundation.
The core concept in Sunshine Conversations is the 'conversation' — a container for messages between a user and a business, which can span multiple channels. A single user might start a conversation on WhatsApp, continue it via SMS, and receive follow-up via the web widget, all appearing as one unified thread. This makes Sunshine Conversations uniquely powerful for businesses serving customers across multiple communication channels.
Authentication uses JWT (JSON Web Tokens) generated from your App ID, Key ID, and Key Secret. Unlike static API tokens, JWTs are short-lived (typically signed with a 60-second expiry for API calls). The Edge Function generates a new JWT for each API request using the stored credentials — this is a slightly more complex auth pattern than Bearer tokens but provides better security.
For Lovable apps, the most common use cases are: creating a unified customer messaging inbox that shows all channels in one place, triggering outbound messages across channels from app events, and building channel-agnostic customer profiles linked to your Supabase user records.
Integration method
Sunshine Conversations has no native Lovable connector. All messaging operations are proxied through Lovable Edge Functions that call the Sunshine Conversations API v2 using JWT authentication. App credentials (Key ID and Key Secret) are stored in Cloud → Secrets, and the Edge Function generates a short-lived JWT token for each API call using Deno.env.get() to retrieve the secrets.
Prerequisites
- A Lovable project with Lovable Cloud enabled (Edge Functions require a deployed app)
- A Zendesk account with Sunshine Conversations enabled (requires Zendesk Suite or a standalone Sunshine Conversations plan)
- Access to the Sunshine Conversations Dashboard at app.smooch.io or via Zendesk Admin Center
- Basic familiarity with JWT (JSON Web Tokens) — the Edge Function generates these automatically from your credentials
Step-by-step guide
Create a Sunshine Conversations app and generate API key credentials
Create a Sunshine Conversations app and generate API key credentials
Sunshine Conversations uses app-based architecture — each integration creates its own app with isolated credentials. Each app has an App ID and can have multiple API key pairs (Key ID + Key Secret), which are used to generate JWT tokens for authentication. Access the Sunshine Conversations Dashboard. If you have a Zendesk account with Sunshine Conversations, go to Zendesk Admin Center → Channels → Sunshine Conversations, then click 'Open Dashboard'. Alternatively, log in directly at app.smooch.io. In the dashboard, click 'Create new app' (or select an existing app if you already have one set up). Give the app a name like 'Lovable Integration'. After creation, you will see the App ID — a long alphanumeric string like 'acme_5f3a2...' — copy this. Next, generate API key credentials. In the app settings, go to the 'Settings' tab and find the 'API Keys' section. Click 'Create new API key'. Give it a name like 'Lovable Edge Function'. The dashboard shows a Key ID and Key Secret. Copy both immediately — the Key Secret is shown only once and cannot be retrieved after closing the dialog. The credentials you need are: - App ID: identifies which app you are connecting to - Key ID: the JWT 'kid' header value - Key Secret: used to sign the JWT JWT tokens for Sunshine Conversations are generated with scope 'app' for full access or 'appUser' for user-specific access. For server-side Edge Function calls (full app access), always use scope 'app'.
Pro tip: The Key Secret is shown only once at generation time. Copy it to a password manager immediately before storing it in Cloud → Secrets. If you lose the secret, you must generate a new key pair and update your secrets — there is no way to retrieve an existing secret.
Expected result: A Sunshine Conversations app is created. You have the App ID, Key ID, and Key Secret. All three are ready to be stored in Cloud → Secrets.
Store credentials in Cloud Secrets and understand JWT generation
Store credentials in Cloud Secrets and understand JWT generation
Add the three Sunshine Conversations credentials to Lovable's Cloud Secrets panel. In Lovable, click '+' → Cloud panel → Secrets. Add: - SUNSHINE_APP_ID — the App ID from the Sunshine Conversations Dashboard - SUNSHINE_KEY_ID — the API Key ID (starts with 'app_') - SUNSHINE_KEY_SECRET — the API Key Secret Sunshine Conversations uses JWT tokens for API authentication, which differs from most APIs that use static tokens. Each API request requires a freshly-signed JWT. The JWT payload includes: - scope: 'app' (for full app-level access) - Optional: sub (user ID for user-scoped tokens) The JWT is signed with HMAC-SHA256 using the Key Secret. The JWT header includes 'alg: HS256' and 'kid: YOUR_KEY_ID'. In Deno, you can use the djwt library or manually construct the JWT using the WebCrypto API. For the Edge Function, import the djwt library from deno.land/x to simplify JWT generation. The signed JWT is included as a Bearer token in the Authorization header of all API requests: Authorization: Bearer YOUR_JWT. API base URL: https://api.smooch.io — this is consistent regardless of which Zendesk data center hosts your account. Sunshine Conversations API v2 paths: /v2/apps/{appId}/conversations, /v2/apps/{appId}/users, /v2/apps/{appId}/conversations/{conversationId}/messages.
Pro tip: JWT tokens for Sunshine Conversations should be generated fresh for each API request in production. The short-lived nature of JWTs is a security feature — a leaked token becomes useless quickly. Never cache or reuse JWTs across requests.
Expected result: Three secrets (SUNSHINE_APP_ID, SUNSHINE_KEY_ID, SUNSHINE_KEY_SECRET) appear in Cloud → Secrets with masked values.
Create the Sunshine Conversations API proxy Edge Function with JWT auth
Create the Sunshine Conversations API proxy Edge Function with JWT auth
Build the Edge Function that generates JWTs and proxies calls to the Sunshine Conversations API. The JWT generation is the most technically distinct part of this integration compared to other CRM APIs. The JWT structure for Sunshine Conversations: - Header: { alg: 'HS256', typ: 'JWT', kid: KEY_ID } - Payload: { scope: 'app', iat: now, exp: now + 60 } - Signature: HMAC-SHA256 of header.payload using KEY_SECRET Deno has built-in WebCrypto for HMAC signing. Alternatively, import djwt from deno.land/x/djwt@v3.0.0. The Sunshine Conversations API v2 has a clean REST structure. Key operations: list conversations (GET /v2/apps/{appId}/conversations), get conversation messages (GET /v2/apps/{appId}/conversations/{conversationId}/messages), create/update user profiles (POST /v2/apps/{appId}/users, PATCH /v2/apps/{appId}/users/{userId}), send message (POST /v2/apps/{appId}/conversations/{conversationId}/messages). Message objects have a 'content' field with type and text: { type: 'text', text: 'Hello' }. For richer messages (image, file, carousel), the content object structure varies by type — check the Sunshine Conversations API documentation for each type. For the send message endpoint, also specify the 'author' field: { type: 'business' } for messages sent by your app, or { type: 'user', userId: id } for user-initiated messages.
Create an Edge Function called sunshine-conversations at supabase/functions/sunshine-conversations/index.ts. Accept POST with { action, params }. Support actions: list_conversations, get_messages, send_message, create_user, get_user. Generate a JWT using SUNSHINE_KEY_ID and SUNSHINE_KEY_SECRET with scope 'app'. Include SUNSHINE_APP_ID in API URLs. Use the djwt library from deno.land/x for JWT generation. Return the API response. Include CORS headers.
Paste this in Lovable chat
1import { serve } from 'https://deno.land/std@0.168.0/http/server.ts'2import { create, getNumericDate } from 'https://deno.land/x/djwt@v3.0.0/mod.ts'34const corsHeaders = {5 'Access-Control-Allow-Origin': '*',6 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',7}89const SUNSHINE_BASE = 'https://api.smooch.io'1011async function generateJWT(): Promise<string> {12 const keyId = Deno.env.get('SUNSHINE_KEY_ID')!13 const keySecret = Deno.env.get('SUNSHINE_KEY_SECRET')!14 if (!keyId || !keySecret) throw new Error('Sunshine Conversations credentials not configured')1516 const key = await crypto.subtle.importKey(17 'raw',18 new TextEncoder().encode(keySecret),19 { name: 'HMAC', hash: 'SHA-256' },20 false,21 ['sign', 'verify']22 )2324 return create(25 { alg: 'HS256', typ: 'JWT', kid: keyId },26 { scope: 'app', iat: getNumericDate(0), exp: getNumericDate(60) },27 key28 )29}3031async function sunshineApi(path: string, method = 'GET', body?: unknown) {32 const jwt = await generateJWT()33 const resp = await fetch(`${SUNSHINE_BASE}${path}`, {34 method,35 headers: {36 Authorization: `Bearer ${jwt}`,37 'Content-Type': 'application/json',38 },39 body: body ? JSON.stringify(body) : undefined,40 })41 return resp.json()42}4344serve(async (req) => {45 if (req.method === 'OPTIONS') return new Response('ok', { headers: corsHeaders })4647 try {48 const { action, params = {} } = await req.json()49 const appId = Deno.env.get('SUNSHINE_APP_ID')!50 let result5152 switch (action) {53 case 'list_conversations':54 result = await sunshineApi(`/v2/apps/${appId}/conversations?page[size]=${params.pageSize || 25}`)55 break5657 case 'get_messages':58 result = await sunshineApi(`/v2/apps/${appId}/conversations/${params.conversationId}/messages`)59 break6061 case 'send_message':62 result = await sunshineApi(`/v2/apps/${appId}/conversations/${params.conversationId}/messages`, 'POST', {63 author: { type: 'business' },64 content: { type: 'text', text: params.text },65 })66 break6768 case 'create_user':69 result = await sunshineApi(`/v2/apps/${appId}/users`, 'POST', {70 externalId: params.externalId,71 profile: {72 displayName: params.displayName,73 email: params.email,74 avatarUrl: params.avatarUrl || '',75 },76 })77 break7879 case 'get_user':80 result = await sunshineApi(`/v2/apps/${appId}/users/${params.userId}`)81 break8283 default:84 return new Response(JSON.stringify({ error: 'Unknown action' }), {85 status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' },86 })87 }8889 return new Response(JSON.stringify(result), {90 headers: { ...corsHeaders, 'Content-Type': 'application/json' },91 })92 } catch (error) {93 return new Response(JSON.stringify({ error: error.message }), {94 status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' },95 })96 }97})Pro tip: The djwt library version matters — use v3.0.0 or later which supports the WebCrypto-based key import pattern shown above. Older versions use a different key format that may not work in Deno's strict security environment.
Expected result: The sunshine-conversations Edge Function deploys in Cloud → Edge Functions. Test with action 'list_conversations' — you should see conversations from your Sunshine Conversations app (or an empty array if none exist yet).
Build the unified messaging inbox in Lovable
Build the unified messaging inbox in Lovable
With the Edge Function deployed, use Lovable's chat to build the messaging inbox UI. The inbox consists of a conversation list panel and a message thread panel. For the conversation list, call list_conversations on mount. Each conversation object from Sunshine Conversations includes: id, displayName (the customer's name), lastMessage (preview), activeSwitchboardIntegration (which channel is active), and participants. Render each conversation as a list item with the channel icon, customer name, last message preview, and relative timestamp. For the message thread, call get_messages when a conversation is selected. Messages have a 'content' object with type and text, an 'author' object indicating business vs user, and a 'received' timestamp. Render as a chat bubble thread with user messages on the left and business messages on the right. For sending replies, use the send_message action with the selected conversation ID. After sending, append the new message to the local state immediately for optimistic UI, then refresh the message list to confirm. For real-time updates (new incoming messages appearing without page refresh), configure a Sunshine Conversations webhook pointing to a Lovable Edge Function that stores new messages in Supabase and broadcasts them via Supabase Realtime. This requires an additional webhook receiver Edge Function beyond the scope of this guide — RapidDev's team can help design the real-time webhook architecture for production messaging inboxes.
Build a messaging inbox page using the sunshine-conversations Edge Function. Left panel: conversation list showing customer name, channel type, last message preview, and timestamp. Right panel: message thread when a conversation is selected, with chat bubbles (user left, business right) and a reply input at the bottom. Load conversations on mount. Load messages when a conversation is clicked. Send replies via the send_message action.
Paste this in Lovable chat
Pro tip: Sunshine Conversations returns message timestamps as Unix timestamps (seconds since epoch). Convert to readable dates with: new Date(message.received * 1000).toLocaleTimeString(). Do not forget the *1000 conversion from seconds to milliseconds.
Expected result: The messaging inbox shows your Sunshine Conversations conversation list. Clicking a conversation loads its message history. Sending a reply from the inbox delivers the message via Sunshine Conversations and it appears in the appropriate customer's messaging app.
Common use cases
Send order confirmation messages via customer's preferred channel
When an order is placed in your Lovable app, determine the customer's preferred messaging channel (WhatsApp, SMS, or web) from their profile, then send an order confirmation through that channel via Sunshine Conversations. The message appears in the customer's messaging app and in your Sunshine Conversations inbox. This replaces transactional emails with conversational messages on the channels customers actually use.
Create an Edge Function called sunshine-send-message that accepts { userId, channel, text, metadata } and sends a message to the user's Sunshine Conversations conversation using SUNSHINE_APP_ID, SUNSHINE_KEY_ID, and SUNSHINE_KEY_SECRET from secrets. Generate a JWT for authentication. Support channel types: whatsapp, sms, web. Return the message ID. Call this after order creation in my checkout flow.
Copy this prompt to try it in Lovable
Build a unified inbox showing all channel messages
Create an internal inbox page in your Lovable app that shows all customer conversations from all channels — WhatsApp, SMS, Facebook Messenger, and web widget — in a single sorted list. Agents can click into any conversation to see the full message history and send replies through whichever channel the customer used. This gives your team a single interface rather than switching between multiple messaging apps.
Create Edge Functions to list all Sunshine Conversations and fetch messages for a specific conversation. Build an inbox page with a conversation list on the left showing customer name, last message preview, channel icon, and timestamp. When a conversation is selected, show the full message thread on the right with a reply input. On submit, send the reply via the Sunshine Conversations API.
Copy this prompt to try it in Lovable
Create user profiles and link them to Supabase accounts
When a new user registers in your Lovable app, create a corresponding Sunshine Conversations user profile with your app's user ID as the external_id. This links future messages from any channel (WhatsApp, SMS, web) to the correct user in your Supabase database. When a customer messages you on WhatsApp, you can look up their Supabase account and provide personalized context to your support team.
Create an Edge Function called sunshine-create-user that accepts { externalId, displayName, email, avatarUrl } and creates a Sunshine Conversations user profile using SUNSHINE_APP_ID, SUNSHINE_KEY_ID, and SUNSHINE_KEY_SECRET from secrets. Return the Sunshine user ID. Call this during user registration alongside Supabase Auth account creation.
Copy this prompt to try it in Lovable
Troubleshooting
API returns 401 Unauthorized with 'Authorization token is invalid' error
Cause: The JWT is malformed, expired, or signed with the wrong key. Common causes are incorrect Key Secret, wrong Key ID in the JWT header, or JWT expiry time already passed.
Solution: Verify SUNSHINE_KEY_ID and SUNSHINE_KEY_SECRET in Cloud → Secrets match the values from the Sunshine Conversations Dashboard. Ensure the JWT exp (expiry) is set to a future time (current time + 60 seconds). Check that the kid field in the JWT header exactly matches your Key ID string, including any prefix like 'app_'.
API returns 404 for all conversation and message endpoints
Cause: The SUNSHINE_APP_ID is incorrect or the endpoint URL does not include the App ID in the path.
Solution: Verify SUNSHINE_APP_ID in Cloud → Secrets. All Sunshine Conversations API v2 endpoints include the App ID in the path: /v2/apps/{appId}/conversations. The App ID is visible in the Sunshine Conversations Dashboard in the app settings. Ensure the app ID does not include extra whitespace.
Messages sent via API do not appear in the customer's messaging app (WhatsApp, SMS)
Cause: The conversation is not linked to the correct channel integration, or the channel (WhatsApp, SMS) has not been configured in the Sunshine Conversations Dashboard.
Solution: In the Sunshine Conversations Dashboard, verify that the channel integrations (WhatsApp, Twilio SMS, etc.) are properly configured and connected. Each channel requires platform-specific setup (WhatsApp Business API credentials, Twilio account SID, etc.). The conversation must have the target channel as an active integration.
djwt import fails with 'Module not found' error in the Edge Function
Cause: The djwt module URL in the import statement is incorrect or the Deno runtime cannot resolve it.
Solution: Ensure you are using the exact URL: https://deno.land/x/djwt@v3.0.0/mod.ts. Deno module imports are version-pinned with the @version syntax. If you see resolution errors, try an alternative: https://deno.land/x/djwt@v3.0.2/mod.ts. You can also implement JWT generation manually using Deno's WebCrypto API without any external library.
Best practices
- Generate fresh JWTs for each API request rather than caching them — JWT short expiry is a security feature that limits the impact of any token leakage.
- Store the Key Secret only in Cloud → Secrets and never log or expose it — unlike static API tokens, you cannot regenerate it if it is compromised without breaking all active integrations.
- Use externalId when creating Sunshine Conversations user profiles, matching your Supabase user IDs — this enables reliable user lookup without storing the Sunshine user ID separately.
- Configure Sunshine Conversations webhooks to push new message events to a Lovable Edge Function receiver rather than polling for new messages — webhooks are real-time and far more efficient.
- Test channel integrations (WhatsApp, SMS) in the Sunshine Conversations Dashboard before building the Lovable UI — channel configuration issues are separate from API integration issues.
- Handle the different message content types (text, image, file, form, carousel) in your React component — not all messages are plain text, and rendering unknown types as empty can confuse users.
- Log the App ID and conversation ID in Edge Function error responses to simplify debugging — Sunshine Conversations errors are more diagnostic with these identifiers included.
Alternatives
Choose Zendesk Support directly if you need ticket-based support workflows with SLAs and agent queues — Sunshine Conversations is the messaging layer that sits beneath Zendesk Support.
Choose Intercom if you want a single product for in-app messaging, help center, and customer support without the complexity of integrating a separate messaging layer.
Choose Twilio directly if you need programmatic SMS and WhatsApp messaging without the conversation management layer — Twilio is simpler and lower cost for pure message delivery.
Frequently asked questions
What is the relationship between Smooch, Sunshine Conversations, and Zendesk?
Smooch was an independent omnichannel messaging platform acquired by Zendesk in 2019. After the acquisition, it was rebranded as Zendesk Sunshine Conversations. The underlying API and platform remain largely the same as the original Smooch product — the API base URL (api.smooch.io) and many documentation references still use the Smooch name. Sunshine Conversations is now sold as a component of Zendesk Suite or as a standalone add-on.
Do I need Zendesk to use Sunshine Conversations?
Not necessarily. Sunshine Conversations was historically available as a standalone platform and some legacy Smooch customers still use it independently. However, new signups typically go through Zendesk Suite plans. If you are building a new integration, you will likely need a Zendesk Suite plan that includes Sunshine Conversations. Check with Zendesk sales for current standalone availability and pricing.
How is JWT authentication different from API key authentication?
JWT (JSON Web Token) authentication requires generating a signed token for each API request, rather than sending a static key. JWTs are signed with your Key Secret using HMAC-SHA256 and include an expiry time (typically 60 seconds). This means a leaked JWT becomes useless quickly. The tradeoff is slightly more complexity in the Edge Function — you need cryptographic signing rather than a simple header injection. The Edge Function code in this guide handles the JWT generation automatically.
Can I use Sunshine Conversations for WhatsApp Business messaging?
Yes — WhatsApp Business is one of Sunshine Conversations' supported channels. Setting it up requires a Meta Business Manager account, WhatsApp Business API access (either through Meta directly or a BSP like Twilio), and configuration in the Sunshine Conversations Dashboard. Once the WhatsApp integration is connected, messages sent via the API in this guide will be delivered via WhatsApp to your customers.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation