Conversation Extensions

Conversation Extensions are custom interactive experiences that sit on top of the chat window. They combine the intimacy and contextuality of conversational interfaces with the richness and flexibility of traditional web UIs. They’re intended to help users complete more complex tasks and transactions, ones that would be too tedious to complete by exchanging multiple messages back and forth.

These extensions benefit from the context of the conversation itself. They can authenticate users, tailor offers to them based on the preferences they’ve expressed and post messages back into the conversation.

Below are some examples of Conversation Extensions you could send to users. View our examples repo for even more use cases.

Multi-Step Flow

Using this feature, a hotel brand can create (or repurpose) a reservation tool that allows users to make a booking without ever leaving their messaging app of choice. From selecting a room, to picking dates and paying for the reservation, multi-step tasks can be carried out seamlessly through an intuitive workflow.

Hotel Booking

Multi-step flow - Hotel booking

Grab the code on github

Restaurant Reservation

Multi-step flow - Restaurant reservation Grab the code on github

Service Subscription

Multi-step flow - Service subscription Grab the code on github

Other Use Cases

User Select Menu

Selectable Menu


Datepicker Grab the code on github

Survey (NPS score)

You could also build an NPS survey right into the conversation, helping you improve survey response rates as users can reply on the spot.

Webview Survey

Example code

Conversation Extensions are powered by Web technologies and are implemented using JavaScript, CSS and HTML. They are sent to end users by including a Webview action when sending a message. A Webview action can be displayed in different sizes by using the size parameter. The size can be set to ‘compact’, ‘tall’ or ‘full’.

To help you get started, we’ve created an example application for you to see how you can implement a Conversation Extension end to end. It implements an in-conversation date picker using Node.js + express in the backend and React in the frontend.

Webview date picker

The Sunshine Conversations Client API library is used to send the extension to the client and to echo the selected date in the conversation.

The Webview SDK is used to programmatically set the title of the extension header and to close it once the user completes their action.


  1. To start, create your node server and listen for webhook calls:

    // server/app.js
    const path = require('path');
    const express = require('express');
    const bodyParser = require('body-parser');
    const SunshineConversationsApi = require('sunshine-conversations-client');
    const app = express();
    app.use('*', bodyParser.json());'/user-message', (req, res) => {
        console.log('received user message');
    module.exports = app;
  2. In your frontend code, initialize the Web Messenger with the integrationId of your choice:

    // src/components/App.js
        integrationId: '<your_integration_id>'
    }).then(function() {
        // Your code after init is complete
  3. Setup your webhook in the Sunshine Conversations dashboard (or via API call):

    Create a webhook

  4. Include the Webview SDK script in your page:

    <!-- public/index.html -->
        (function(d, s, id) {
            var js,
                fjs = d.getElementsByTagName(s)[0];
            if (d.getElementById(id)) {
            js = d.createElement(s);
   = id;
            js.src = '';
            fjs.parentNode.insertBefore(js, fjs);
        })(document, 'script', 'WebviewSdkScript');
  5. Respond to messages with a webview action, adding userId to the query:

    // server/app.js
    const defaultClient = SunshineConversationsApi.ApiClient.instance;
    const basicAuth = defaultClient.authentications["basicAuth"];
    basicAuth.username = KEY_ID;
    basicAuth.password = SECRET;'/user-message', (req, res) => {
        let messagePost = new SunshineConversationsApi.MessagePost();
        messagePost.setAuthor({ type: "business" });
            type: "text",
            text: "Please select your flight date.",
            actions: [
                type: 'webview',
                text: 'Select Date',
                size: 'tall',
                uri: `http://localhost:3000/webview?userId=${userId}&conversationId=${conversationId}`,
                fallback: ''
        await messagesApiInstance.postMessage(appId, conversationId, messagePost);
  6. In your extension, have your user complete their required action and then notify the server:

    // src/components/DatePicker.js
    fetch('/date-selected', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        body: JSON.stringify({
            userId: qs.parse('?', '')).userId,
            conversationId: qs.parse('?', '')).conversationId
    }).then(() => {
            () => console.log('success!'),
            (e) => console.log('failure', e)
  7. Listen for the call from the extension and react to it in the server:

    // server/app.js'/date-selected', async (req, res) => {
        const { date, userId, conversationId } = req.body;
        let messagePost = new SunshineConversationsApi.MessagePost();
        messagePost.setAuthor({ type: 'business', userId });
        messagePost.setContent({ type: 'text', text: `You have selected ${date}. Enjoy your flight!` });
        await messagesApiInstance.postMessage(appId, conversationId, messagePost);

Webview SDK

The Webview SDK library extends the functionality of Conversation Extensions to allow you to perform additional actions like setting the Extension header title and closing it programmatically.

To use the Webview SDK, add the following script tag to your html page:

    (function(d, s, id) {
        var js,
            fjs = d.getElementsByTagName(s)[0];
        if (d.getElementById(id)) {
        js = d.createElement(s); = id;
        js.src = '';
        fjs.parentNode.insertBefore(js, fjs);
    })(document, 'script', 'WebviewSdkScript');

To know when the library is fully loaded, you can define a webviewSdkInit like this in your <body>

    window.webviewSdkInit = function(WebviewSdk){' '}
        // the SDK is passed in parameter to this callback
        // and is also available at `window.WebviewSdk` for convenience

Once loaded, the library is available in the global scope as WebviewSdk.



hasFeature lets you check if a feature is implemented/available or not for the platform the sdk is currently running in. For instance, you could do WebviewSdk.hasFeature('close') before calling WebviewSdk.close() in order to adjust your webview behavior in case the feature is not supported. This method is important as not all platforms will support features as we add them.


close tells the platform to close the current webview.


setTitle tells the platform to update the container header title. You should not have to call this on your own as your document title will be used automatically as the webview title. It will also be updated automatically on some platforms. You can call this function if you want to do something special with the title of the webview.