# How to Integrate Retool with Zendesk Sunshine Conversations

- Tool: Retool
- Difficulty: Advanced
- Time required: 35 minutes
- Last updated: April 2026

## TL;DR

Connect Retool to Zendesk Sunshine Conversations (formerly Smooch) using a REST API Resource with app key and secret authentication. Build omnichannel messaging operations panels that manage conversations across WhatsApp, Messenger, SMS, web chat, and other channels — view conversation threads, send messages, manage switchboard handoffs, and monitor messaging workflow performance from a central Retool dashboard.

## Build a Sunshine Conversations Omnichannel Messaging Panel in Retool

Zendesk Sunshine Conversations serves as the messaging backbone for organizations that support customers across multiple channels simultaneously — WhatsApp for mobile customers, Facebook Messenger for social engagement, SMS for service notifications, and web chat for website visitors. While Zendesk's native agent interface handles many workflows, operations teams building custom messaging pipelines, monitoring conversation queues, or managing programmatic messaging workflows need a more flexible interface.

A Retool integration with Sunshine Conversations enables building custom omnichannel operations panels: a conversation monitoring dashboard that shows active conversations across all channels with the ability to view message threads and send responses; a switchboard management panel that controls conversation routing between human agents and automated bots; and a messaging analytics view that tracks conversation volumes by channel, response time distributions, and message delivery rates.

Sunshine Conversations uses a data model centered around Apps (your messaging configuration), Users (customers who have messaged you), and Conversations (threaded message histories). A single user can have conversations on multiple channels simultaneously — each represented as a separate conversation with a 'channel' type indicator. The Retool dashboard needs to account for this multi-channel architecture, presenting conversations in a way that gives operations staff a unified customer view across all their messaging touchpoints.

## Before you start

- A Zendesk account with Sunshine Conversations (formerly Smooch) enabled — Sunshine Conversations is included in Zendesk Suite or available as a standalone purchase
- At least one Sunshine Conversations App configured with messaging channels — create and configure apps at app.smooch.io or through the Zendesk Admin Center
- Sunshine Conversations API credentials: App ID and App Secret (found in your Sunshine Conversations app settings under API Keys section)
- A Retool account with permission to create Resources and Configuration Variables
- Basic understanding of messaging concepts: conversations, messages, users, and the switchboard pattern

## Step-by-step guide

### 1. Obtain Sunshine Conversations API credentials

Sunshine Conversations uses two levels of API credentials depending on what you need to access. App-level credentials (App ID + App Secret) are scoped to a single Sunshine Conversations app and its conversations. Account-level API keys provide access across all apps in your account and are required for app management operations. For most Retool operations dashboards, app-level credentials are sufficient and more secure due to their limited scope. To obtain app-level credentials: log in to app.smooch.io (or access Sunshine Conversations through Zendesk Admin Center → Channels → Sunshine Conversations). Navigate to your app → Settings → API Keys. You will see your App ID at the top of the page (format: 5c7mklmx6o2fxxxxx or similar). Click 'Add Key' to create a new API key, give it a name like 'Retool Integration', and copy the secret value shown — this is displayed only once. If you are accessing via Zendesk Admin Center, navigate to Admin Center → Apps and Integrations → Sunshine Conversations → API Keys. The authentication pattern for the Sunshine Conversations REST API uses HTTP Basic Auth where the username is your App ID and the password is the API key secret. For account-level access (needed for multi-app dashboards), generate account-level API keys from the same section. In Retool's Settings → Configuration Variables, add: SUNSHINE_APP_ID and SUNSHINE_APP_SECRET as separate secret variables. The API endpoint is https://api.smooch.io for all operations.

**Expected result:** Sunshine Conversations App ID and App Secret are stored as secret configuration variables in Retool, ready for use in the REST API Resource configuration.

### 2. Create the Sunshine Conversations REST API Resource in Retool

In Retool, click the Resources tab and Add Resource. Select REST API. Name it 'Sunshine Conversations API'. In the Base URL field, enter https://api.smooch.io. This is the global API endpoint for all Sunshine Conversations operations — conversations, messages, users, webhooks, and switchboard are all accessible under this base URL. Scroll to the Authentication section and select Basic Auth from the dropdown. In the Username field, enter {{ retoolContext.configVars.SUNSHINE_APP_ID }}. In the Password field, enter {{ retoolContext.configVars.SUNSHINE_APP_SECRET }}. Scroll to the Headers section. Add a default header: Content-Type = application/json. Add a second header: Accept = application/json. The Sunshine Conversations API may also require an API version header depending on whether you are using v1 or v2 endpoints — check your Sunshine Conversations dashboard for the API version your account uses and add the appropriate version header if specified in the documentation (e.g., Smoke-API-Version = v2). Click Save Changes. Verify the resource by creating a test query: GET /v2/apps/{your-app-id}/conversations with a limit parameter. If the response returns a 'conversations' array (which may be empty if no conversations exist yet), the resource is configured correctly. Note that the API path includes your app ID: all Sunshine Conversations endpoints follow the pattern /v2/apps/{appId}/[resource], so you will include the app ID in every query path.

**Expected result:** A 'Sunshine Conversations API' resource is created and a test conversation list query returns a valid response (conversations array, even if empty).

### 3. Build queries to retrieve conversations and message threads

Create your core conversation data queries. First, create a query named 'getConversations': select the Sunshine Conversations API resource, Method GET, Path /v2/apps/{{ retoolContext.configVars.SUNSHINE_APP_ID }}/conversations. Add URL parameters: 'limit' = 100 and 'page[after]' = {{ pagination.cursor || '' }} for cursor-based pagination. Optionally add 'filter[isDefault]' = false to exclude the default conversation per user and focus on specific channel conversations. Enable 'Run query when app loads'. The response contains a 'conversations' array where each conversation object includes: id, type (personal/business), activeSwitchboardIntegration, metadata, createdAt, updatedAt, and a participants array. Write a transformer that flattens this into a displayable format. Create a second query named 'getMessages': Method GET, Path /v2/apps/{{ retoolContext.configVars.SUNSHINE_APP_ID }}/conversations/{{ conversationsTable.selectedRow?.id || '' }}/messages. Add URL parameter 'limit' = 50. Set this query to trigger when a conversation row is selected. The messages response contains a 'messages' array with objects including: id, author (name, type: user/business), content (text or structured), received, and channel information. Write a transformer that formats messages chronologically with sender names, message content, channel indicators, and timestamps.

```
// Transformer: normalize Sunshine Conversations list
const conversations = data.conversations || [];
return conversations.map(conv => {
  const lastMessage = conv.lastMessage || {};
  const participants = conv.participants || [];
  const userParticipant = participants.find(p => p.userExternalId) || participants[0] || {};

  return {
    id: conv.id,
    type: conv.type,
    user_id: userParticipant.userId || 'N/A',
    user_external_id: userParticipant.userExternalId || 'N/A',
    channel: conv.metadata?.channelType || lastMessage.source?.type || 'unknown',
    last_message_text: lastMessage.content?.text || '[non-text message]',
    last_message_at: lastMessage.received
      ? new Date(lastMessage.received).toLocaleString()
      : 'No messages',
    last_message_timestamp: lastMessage.received || '',
    active_integration: conv.activeSwitchboardIntegration?.name || 'None',
    created_at: new Date(conv.createdAt).toLocaleDateString()
  };
}).sort((a, b) =>
  new Date(b.last_message_timestamp) - new Date(a.last_message_timestamp)
);
```

**Expected result:** The getConversations query returns a list of conversations with channel type, last message, and switchboard status. Selecting a row triggers getMessages to load the conversation thread.

### 4. Build the omnichannel messaging dashboard UI

Build the two-panel conversation interface. On the left side, drag a Table component named 'conversationsTable' and bind it to {{ getConversations.data }}. Configure columns: channel (with conditional icon or color — map channel values to colors: whatsApp=green, messenger=blue, sms=orange, web=gray), user_external_id, last_message_text (truncated), and last_message_at. Add a Search Text Input for filtering by user ID or message content. Add a Segment Control or Select component for channel filtering — bind its change event to re-trigger getConversations with a channel filter parameter. On the right side, add a Container that shows when a conversation is selected. At the top, show conversation metadata in Text components: user ID, channel type, and active integration. Below, display a scrollable list of messages using a Listview or Repeater component. Each list item shows: sender name (formatted as '[User]' or '[Agent]'), message text, and timestamp. Apply different background colors to user vs business messages to create a chat-like visual distinction. At the bottom of the right panel, add a Multiline Text Input named 'replyInput' and a Send button. Create a send message query named 'sendMessage': Method POST, Path /v2/apps/{{ retoolContext.configVars.SUNSHINE_APP_ID }}/conversations/{{ conversationsTable.selectedRow.id }}/messages. Set Body Type to JSON and Body to a Sunshine Conversations message object: { "author": { "type": "business", "displayName": "Support Agent" }, "content": { "type": "text", "text": "{{ replyInput.value }}" } }. In sendMessage's On Success handler, trigger getMessages to refresh the thread and clear the reply input.

```
{
  "author": {
    "type": "business",
    "displayName": "{{ currentUser.fullName || 'Support Agent' }}"
  },
  "content": {
    "type": "text",
    "text": "{{ replyInput.value }}"
  }
}
```

**Expected result:** A two-panel messaging dashboard shows the conversation list on the left and message threads on the right, with a functional reply field that sends messages back through Sunshine Conversations.

### 5. Add switchboard management and conversation handoff controls

Switchboard is Sunshine Conversations' routing mechanism that controls which integration — a bot, a human agent queue, or an automated workflow — handles each conversation. Build switchboard management into your Retool dashboard to give operations staff control over conversation routing without needing to configure webhooks or modify code. First, understand the switchboard model: each conversation has an 'activeSwitchboardIntegration' that specifies which integration currently 'holds' the conversation. When a bot is active, the bot responds. When a human agent integration is active, agent replies are routed to the customer. To hand off a conversation from bot to human, use the Sunshine Conversations Switchboard Passthrough API. Create a query named 'passConversation': Method POST, Path /v2/apps/{{ retoolContext.configVars.SUNSHINE_APP_ID }}/conversations/{{ conversationsTable.selectedRow.id }}/passControl. Set Body Type to JSON and Body to: { "switchboardIntegration": "next", "metadata": { "handoffReason": "{{ handoffReasonSelect.value }}", "handoffBy": "{{ currentUser.email }}" } }. Add a Handoff section to your conversation detail panel with a Select component for the target integration (populated from a GET /v2/apps/{appId}/switchboard/integrations query), a reason Select (Bot Limit Reached, Customer Request, Escalation), and a Pass Control button that triggers passConversation. In the On Success handler, refresh getConversations. For dashboards managing complex multi-bot routing scenarios, RapidDev can help architect the full switchboard configuration and Retool operations layer.

```
// Switchboard integration list query
// GET /v2/apps/{appId}/switchboard/integrations
// Transformer: extract integration options for Select component
const integrations = data.switchboardIntegrations || [];
return integrations.map(integration => ({
  label: integration.name,
  value: integration.id,
  description: integration.deliverStandbyEvents ? 'Receives standby events' : 'Standard'
}));
```

**Expected result:** The dashboard includes switchboard controls that show the active integration and allow passing conversation control to a different integration with a documented handoff reason.

## Best practices

- Store Sunshine Conversations App ID and App Secret in Retool Configuration Variables marked as secret — messaging platform credentials provide full read/write access to all customer conversations and must be treated as highly sensitive
- Create dedicated API keys per environment (development, staging, production) and per team using the Retool dashboard — this limits blast radius if credentials are compromised and makes audit trails clearer
- Implement channel-type conditional formatting in conversation Tables (color-code by channel) so operators can instantly identify which channel a conversation is on before opening it — WhatsApp, Messenger, and web chat require different response tone considerations
- Add messaging window validation for WhatsApp conversations (24-hour customer service window) before enabling the reply button — sending outside the window causes API errors and creates a poor user experience for operators
- Use cursor-based pagination for conversation lists and cache for at least 30 seconds — high-volume messaging environments can have thousands of active conversations, and frequent uncached requests can cause significant API load
- Log all outgoing messages sent from Retool to Retool Database with the Retool user's email and timestamp — this creates an audit trail of agent interactions separate from Sunshine Conversations' own logs, useful for compliance
- Display the activeSwitchboardIntegration name prominently in the conversation detail panel so operators always know whether a bot or human is currently handling the conversation before intervening

## Use cases

### Build an omnichannel conversation monitoring dashboard

Create a Retool panel that displays all active Sunshine Conversations conversations across channels. Filter by channel type (WhatsApp, Messenger, SMS, web), conversation status (active/inactive), and last message timestamp. Select any conversation to view the full message thread. Include a reply form so operations staff can send messages from Retool without switching to another interface.

Prompt example:

```
Build a Retool dashboard showing all active Sunshine Conversations conversations, filterable by channel (WhatsApp, Messenger, SMS, web), with a conversation list Table and a message thread view when a conversation is selected. Include a reply text field and Send button.
```

### Build a switchboard conversation routing management panel

Create a Retool switchboard management panel that displays conversations currently queued in bot-handled workflows and allows operations managers to manually hand conversations off to human agents. Show the current switchboard state, which integration holds control, and provide one-click handoff actions to transfer control between integrations.

Prompt example:

```
Build a Retool panel showing Sunshine Conversations switchboard state for active conversations — display which integration controls each conversation (bot vs agent), queue depth, and a handoff button to transfer control to the live agent integration.
```

### Build a messaging volume and channel analytics dashboard

Create a Retool analytics panel that queries Sunshine Conversations for conversation and message counts over time, broken down by channel type. Display a Bar Chart of daily message volumes by channel, a Table of conversations per channel with average response time, and alerts for channels with unusual volume spikes that may indicate delivery issues.

Prompt example:

```
Build a Retool analytics dashboard showing Sunshine Conversations message volume over 30 days, broken down by channel (WhatsApp, Messenger, SMS), with a Bar Chart per channel and a summary Table of conversation counts and average first-response times.
```

## Troubleshooting

### Authentication returns 401 Unauthorized despite correct App ID and Secret

Cause: Sunshine Conversations API keys are scoped to specific apps. If the App ID in the resource username doesn't match the app the API secret was created under, authentication fails. Additionally, account-level API keys use a different ID format from app-level keys — using a key from one level with the ID from another causes authentication failure.

Solution: Verify in app.smooch.io or Zendesk Admin Center that the App ID shown at the top of your app's API Keys section exactly matches the SUNSHINE_APP_ID configuration variable. Confirm you are using the API key secret (the long random string shown once when creating the key) as the password, not the key name or the App ID itself. Try creating a fresh API key — copy the secret immediately as it is shown only once.

### getConversations returns empty array despite conversations existing in Zendesk

Cause: Sunshine Conversations conversation list may be empty if conversations are associated with specific users and you are querying without user context, or if your app's conversation filter excludes the conversations you expect to see. Also, conversations only appear in the API after at least one message has been exchanged.

Solution: Try querying for a specific user's conversations via GET /v2/apps/{appId}/users/{userId}/conversations instead of the top-level conversations endpoint. Use the Sunshine Conversations dashboard at app.smooch.io to verify that conversations exist under your app and note specific conversation IDs for testing. Ensure the query URL uses your actual App ID in the path, not a placeholder.

### Sending a message returns 409 Conflict or 'conversation not active'

Cause: Some Sunshine Conversations channels (particularly WhatsApp) enforce messaging window restrictions — businesses can only send messages within 24 hours of the last customer message. Attempting to send outside the messaging window returns a 409 or similar error. Some channels also require specific message templates for business-initiated messages.

Solution: Check the last_message_at timestamp before enabling the reply button — if the customer's last message is older than 24 hours and the channel is WhatsApp, inform the operator that the messaging window has closed and they need to use a WhatsApp template message instead of a free-text reply. For template messages, configure the content type as 'template' rather than 'text' in the send message query body.

```
// Check messaging window before enabling reply
const lastMessageAt = new Date(conversationsTable.selectedRow?.last_message_timestamp);
const hoursSinceLastMessage = (Date.now() - lastMessageAt.getTime()) / (1000 * 60 * 60);
const channel = conversationsTable.selectedRow?.channel;
const windowClosed = channel === 'whatsapp' && hoursSinceLastMessage > 24;
// Bind to replyButton's disabled property: {{ windowClosed }}
```

### Messages transformer returns '[non-text message]' for all messages despite visible conversation content

Cause: Sunshine Conversations messages have different content structures depending on the type: text messages have content.text, image messages have content.mediaUrl, carousel messages have content.items, and structured messages have entirely different schemas. A transformer that only checks content.text will show fallback text for all non-text message types.

Solution: Add message type handling to the transformer: check content.type before extracting text. For image messages (type: 'image'), display the mediaUrl. For carousel or compound messages, extract a summary description. For structured messages, display the action buttons as a text list.

```
// Handle multiple Sunshine Conversations message content types
const getMessageText = (content) => {
  if (!content) return '[empty message]';
  switch(content.type) {
    case 'text': return content.text;
    case 'image': return '[Image] ' + (content.mediaUrl || '');
    case 'file': return '[File] ' + (content.mediaUrl || '');
    case 'carousel': return '[Carousel: ' + (content.items?.length || 0) + ' items]';
    default: return '[' + (content.type || 'unknown') + ' message]';
  }
};
```

## Frequently asked questions

### What is the difference between Zendesk Sunshine Conversations and Zendesk Support?

Zendesk Support is the ticket-based helpdesk platform for managing customer service queues, SLA tracking, and agent workflows. Sunshine Conversations (formerly Smooch) is the messaging layer that enables real-time conversations across WhatsApp, Messenger, SMS, and other channels. In the Zendesk ecosystem, Sunshine Conversations feeds messages into Zendesk Support as tickets, but it also has its own API for direct message management, conversation routing (switchboard), and programmatic messaging without going through the ticket system.

### Does Retool have a native Sunshine Conversations connector?

No, Retool does not include a native Sunshine Conversations connector. Connect via a REST API Resource using Basic Auth with your Sunshine Conversations App ID as the username and App Secret as the password, pointing to https://api.smooch.io. All API requests proxy through Retool's server, so messaging credentials are never exposed to the users' browsers.

### Can I send WhatsApp messages from Retool through Sunshine Conversations?

Yes, but with constraints. WhatsApp's business messaging rules enforce a 24-hour customer service window — you can only send free-text replies within 24 hours of the customer's last message. Outside this window, you must send pre-approved WhatsApp message templates. In Retool, implement a check on the last message timestamp before enabling the reply input, and build a separate template selection interface for out-of-window conversations.

### How does the Sunshine Conversations switchboard work and how do I manage it from Retool?

The switchboard is Sunshine Conversations' conversation routing mechanism. Each conversation has an 'active integration' — typically a bot handling automated responses or a human agent queue. You control routing by calling the passControl API endpoint to transfer a conversation from one integration to another. In Retool, build a handoff button that POSTs to the passControl endpoint with your target integration ID. This triggers the receiving integration to start responding, enabling smooth bot-to-human handoffs from the Retool operations dashboard.

### How do I handle multiple Sunshine Conversations apps (for multi-brand or multi-region operations) in Retool?

Create separate Retool REST API Resources for each Sunshine Conversations app, or build a single resource and parameterize the App ID in every query path using a Select component. Store app configurations (App ID, App Secret, name, region) in Retool Database and load them into a Select at the top of your dashboard. When the selected app changes, update the App ID in all query paths via configuration variable or JavaScript query. This avoids creating dozens of resource configurations for multi-brand setups.

---

Source: https://www.rapidevelopers.com/retool-integrations/zendesk-sunshine-conversations
© RapidDev — https://www.rapidevelopers.com/retool-integrations/zendesk-sunshine-conversations
