Integrate Slack and SMS via Low-Code AI Studio
Published on May 21, 2024

Introduction

Small and medium businesses often lack resources for full-fledged (and expensive) customer support solutions. However, with Vonage's AI Studio low-code platform, you can build a custom, cost-effective customer experience solution tailored to your needs.

This tutorial will show you how to integrate SMS and Slack to create a powerful customer support system. By connecting SMS to Slack and Slack to SMS, you can enable your team to respond to customer inquiries directly from Slack.

Using your existing staff, you can connect with customers over SMS, Voice (phone), or WhatsApp and respond directly from Slack. The best part? You're only charged for what you use! This powerful solution is particularly beneficial for startups already using Slack for team communications.

Learn how to connect SMS and Slack for a live customer support failover. If your customers don't receive a satisfactory answer from the virtual agent, the conversation will seamlessly transfer to Slack, where any team member can assist!

TL;DR: Find the refactored server code on Github to follow along with the AI Studio and Slack setup without worrying about JavaScript programming.

Integrating SMS with Slack for Customer Support Overview

Initially, I aimed for a flow similar to the famous Giphy slash command for this SMS to Slack integration. However, custom slash commands are not allowed in threads by Slack (Giphy is a special case). Using a slash command in a regular channel response doesn't associate the response with a specific conversation. With simultaneous conversations, this approach would create a mess! To keep conversations threaded, we implemented the following workaround:

  1. A new message appears in the customer support channel with the conversation_id from a new SMS conversation.

  2. A team member clicks a Slack Shortcut on the message, which tells our application to link that conversation to the current Slack thread.

  3. The team member uses the /reply slash command followed by the session_id and their response.

  4. Our application handles the reply, sends it to AI Studio, and adds it as a response in the thread.

  5. Our application also handles customer responses and adds them to the correct thread.

  6. The conversation continues until the agent uses the /close_ticket slash command, terminating the session in AI Studio and adding a "conversation resolved" response in the Slack thread.

By following this approach, you can seamlessly integrate SMS and Slack for customer support, allowing your team to manage conversations directly from Slack while maintaining proper threading and context.

Prerequisites

Vonage API Account

To complete this tutorial, you will need a Vonage API account. If you don’t have one already, you can sign up today and start building with free credit. Once you have an account, you can find your API Key and API Secret at the top of the Vonage API Dashboard.

Setup

How to Setup a Node Application with Express

1. Create a new directory for your project and navigate into it using your terminal.

mkdir sms-to-slack
cd sms-to-slack

2. Initialize a new Node.js project and install dependencies

npm init -y
npm install express axios body-parser dotenv

3. Create Your Project Files

touch server.js .env .gitignore 

4. Open the project in your code editor of choice.

In the .gitignore, we’ll need to add a single line so git doesn’t accidentally share our .env credentials: 

.env

In our .env file we’ll need to add our secure values from both Slack and AI Studio. We’ll fill those out in the next steps. For now add:

SLACK_WEBHOOK_URL=""
AI_STUDIO_KEY=""

In the server.js file, add the following boilerplate:

// Import required modules
require('dotenv').config();
const express = require('express');
const axios = require('axios');
const bodyParser = require('body-parser');

// Create an Express application
const app = express();
const PORT = process.env.PORT || 3000;

// Import our secure ENV variables
const SLACK_WEBHOOK_URL = process.env.SLACK_WEBHOOK_URL;
const AI_STUDIO_KEY = process.env.AI_STUDIO_KEY;

// Middleware to parse JSON requests
app.use(express.json());

// Middleware to parse requests from Slack
const urlencodedParser = bodyParser.urlencoded({ extended: false })

// Define the /start endpoint
app.post('/start', (req, res) => {
    // Handle POST request to /start
    res.send('Start endpoint reached');
});

// Define the /inbound endpoint
app.post('/inbound', (req, res) => {
    // Handle POST request to /inbound
    res.send('Inbound endpoint reached');
});

// Define the /slack/start endpoint 
app.post('/slack/start', urlencodedParser, function (req, res){
res.send("Begin initiated");
});

// Define the /slack/message endpoint
app.post('/slack/message', urlencodedParser, function (req, res) {
  res.send("Response sent to user")
});

app.post('/slack/end', urlencodedParser, function (req, res) {
res.send("Conversation ended")
});

// Start the server
app.listen(PORT, () => {
    console.log(`Server is running on port ${PORT}`);
});

// We'll add our function declarations below here

In our boilerplate, we do 4 main things:

  1. Import our dependencies.

  • Express runs our server.

  • Axios allows us to make HTTP requests. HTTP requests allow us to pass information between Slack & AI Studio.

  • bodyParser helps us parse or clean up the data we get back from Slack. 

2. Configure our app’s server, middleware, and env variables.

3. Create endpoints for each of our application's functionalities.

4. Start our server.

How to Expose Our Application Through localtunnel

Our application requires passing data several times between AI Studio and Slack. We’ll use localtunnel to expose our localhost and create publicly accessible tunnels. In your terminal run the following command:

npx localtunnel --port 3000

This will create a tunnel URL that should look something like this:

➜  sms-to-slack npx localtunnel --port 3000

your url is: https://some-parks-beg.loca.lt

The url that starts with https and ends in loca.lt is your tunnel url. In the remainder of the article anytime that TUNNEL_URL is mentioned, this value should be used.

Open a second tab in your terminal for later.

How to Setup a Slack App

  1. Log in to your Slack Workspace and open your applications

  2. Click on “Create a New App”. Select the “From Scratch” option.

  3. Give your app a name and select the Workspace where you want to use it.

  4. Click “Create App”.

Ta-da! You now have your first Slack App! There’s one last thing you’ll need to do. You’ll need to enable Incoming Webhooks. Once you’ve toggled incoming webhooks on, scroll to the bottom of the page and click “Add New Webhook to Workspace”. You’ll be asked to select the desired channel and click “allow”. This will redirect you to the Incoming Webhooks page of your app, except now if you scroll to the bottom you will see a Webhook URL for your desired channel.

Copy the URL and add it as the value for your SLACK_WEBHOOK_URL in your .env file. It should look something like this:

SLACK_WEBHOOK_URL="https://hooks.slack.com/services/..."

How to Create an Inbound SMS Chatbot

To create your agent, follow the instructions found in the AI Studio documentation here. There are three important options for our agent, select:

  • Type: SMS

  • Template: Start From Scratch

  • Event: Inbound

The flow of our agent will be relatively simple.

  1. Upon the user messaging the agent, we’ll use a Collect Input node to ask what they need help with. The answer will be stored in a parameter called inquiry.

  2. Then we’ll use a conditional node to check if inquiry is equal to “escalate”.

  3. If it is, we’ll then send the user a message saying “Please hold while we connect you to a live agent”. If the inquiry is something else, we’ll mimic all other possible flows by simply sending a message “Thanks for using our service!” (Send Thank You node).

  4. The Please Hold node will connect to a Live Agent Routing Node.

  5. Finally, the Live Agent Routing Node and Send Thank You Node will connect to an End Conversation node.

Overview of AI Studio SMS AgentOverview of AI Studio SMS Agent

How to Setup a Live Agent Routing Node

In the Live Agent Routing Node, we need to do 3 things:

  1. Add your ‘TUNNEL_URL/start’ in the Start Connection EP field

  2. Add your ‘TUNNEL_URL/inbound’  in the Inbound Transfer EP field

  3. Select inquiry in the Transfer Parameters 

And hit “Save and Exit”.

Example of Live Agent Routing Node in AI StudioExample of Live Agent Routing Node in AI Studio

Next, add your AI Studio API Key to your .env file. You can find the X-Vgai-Key on the top right of your AI Studio canvas. Click on the "user" icon, and then "Generate API Key". 

Copy the X-Vgai-Key and now add it as the value for your AI_STUDIO_KEY in your .env file. It should look something like this:

AI_STUDIO_KEY="YOUR_KEY_VALUE"

Lastly, you’ll need to assign your Vonage Number to the agent. Follow the publishing instructions from the documentation.

How to Initiate a Slack Conversation Using Incoming Webhooks

The first action we need to create is connecting AI Studio to Slack. We will use the Live Agent’s Start Connection Endpoint and Slack’s Incoming Webhooks. We’ve already configured these in the setup but now we need to add the logic to actually connect them.

Our agent’s Start Connection Endpoint is configured to send a request to our /start endpoint. There we will send 2 pieces of information to Slack: the sessionId and the transcription of our conversation thus far. We’ll package this up in the data object according to what Slack is expecting with a little formatting. We’ll use the axios library to send all our POST requests.

This is how our /start endpoint now looks:

app.post('/start', (req, res) => {
  const sessionId = req.body.sessionId;
  const transcription =  handleTranscription(req.body.history.transcription);
  const data = { text: `Session: `${sessionId}`\nTranscription:${transcription}`, }
  axios.post(SLACK_WEBHOOK_URL, data)
  res.send('Start endpoint reached');
});

You’ll notice that we call the function handleTranscription. This function formats our raw transcription into something nice for Slack. You can add this at the bottom of our file.

const handleTranscription = (transcription = []) => {
  if (!transcription.length) return null;

  let strTrans = '```';

  for (const message of transcription) {
    for (const key in message) {
      strTrans += `\n${key}: ${message[key]}`;
    }
  }

  strTrans += '```';

  return strTrans;
};

How to Test Your SMS to Slack Connection

You can now test that your agent is connected to your Slack channel! In the second terminal tab that you opened before, run your application:

node server.js

Did your transcription appear in Slack? How cool!

Because we haven’t implemented a way to end conversations in AI Studio yet, each time you test you’ll need to manually end the conversation. You can do so by sending a POST request to the Stop Connection EP with a tool like Postman. You can find your active sessions under the Reports tab.

Additionally, each time you want to test a new change in your node application you’ll need to restart the server.

How to Create a Slack Message Shortcut

Now that we have initiated our conversation in Slack, you might think the next step would be to let our Slack teammates respond right? Well, we need to solve the problem first of somehow linking our Slack thread to our AI Studio conversation so we know how to organize our messages back and forth!

We’ll do this by creating a Slack Message Shortcut. This message shortcut will require our agent to “open a ticket” by just clicking a button on the new conversation. This will send a request to our application where we’ll store the conversation thread information in a new object in relation to the conversation_id.

How to Enable Slack App Interactivity

In order to create shortcuts, you’ll need to enable interactivity on your Slack app. You can see how to do so below. For the Request URL you’ll add your TUNNEL_URL followed by /slack/start. This will be the endpoint in our application where Slack will send a request whenever the shortcut is triggered.

Enable interactivity in your Slack AppEnable interactivity in your Slack App

Next, click on “Create New Shortcut” and fill it in:

Name: Start a ticket

Short Description: Creates a conversation for a customer inquiry

Callback ID: begin_response

Create Slack ShortcutCreate Slack Shortcut

Now we’ll update our application to use the information we receive back from Slack and link it to our AI Studio sessions. First, we’ll create a global object called SESSIONS. You can add this just above our endpoints:

const SESSIONS = {};

Inside of our endpoints we’re going to do 4 things:

  1. Handle the response we receive from Slack and extract thread_ts (timestamp) and session_id

  2. Create a new entry in SESSIONS for the current session/thread

  3. Prepare the data to be sent to Slack, which includes formatting our initialization message for our Slack thread

  4. Sending our request to Slack to post the initialization message in the correct thread

app.post('/slack/start', urlencodedParser, function (req, res){
  const response = JSON.parse(req.body.payload);
  const thread_ts = response.message.ts;
  const session_id = extractSessionId(response.message.text);
  newSession(session_id, thread_ts);

  const data = {
    "thread_ts": thread_ts,
    "text": `Session seen by <@${response.user.id}>`
  }

  axios.post(SLACK_WEBHOOK_URL, data);
  res.send("Begin initiated");

})

In our endpoint, we rely on two new functions. You can add these at the bottom of your file with the other functions. The first called extractSessionId which searches our payload from Slack for the current sessionId:

const extractSessionId = (input) => {
  const sessionIdPattern = /Session: `([0-9a-f-]{36})`/i;
  const match = input.match(sessionIdPattern);
  if (match && match[1]) {
    return match[1];
  }
  return null;
};

The second function newSession, creates a new entry in our global SESSIONS variable.

const newSession = (session_id, message_ts) => {
  SESSIONS[session_id] = {
    "session_id" : session_id,
    "thread_ts" : message_ts
  }
}

How to Create a Slack Slash Command for Message Replies

Now that our agent has initialized the conversation in our application, we want to let them actually respond to our customers. Let’s do that by creating our first slash command.

In our Slack application dashboard open the Slash Commands tab. Then click “Create New Command”. 

Create the following command:

Command: reply Request URL: TUNNEL_URL/slack/message Short Description: Reply to incoming inquiries Usage Hint: [session_id]

And click “Save”.

Create Slack Slash CommandCreate Slack Slash Command

Now we need to update our ‘/slack/message’ endpoint to handle the data being sent from Slack. We need to do two things:

  1. Grab the message and add it as a comment in the correct thread in Slack, along with some formatting

  2. Grab the message and send it to the correct session in AI Studio

We can do that with the following code:

app.post('/slack/message', urlencodedParser, function (req, res) {
  const response = req.body;
  const agentResponse = parseMessage(req.body.text);
  const session_id = agentResponse['sessionId'];
  const message = agentResponse['message'];
  const studio_data = { message_type: 'text', text: message };
  const thread_ts = SESSIONS[session_id].thread_ts;
  const slack_data = {
    "thread_ts": thread_ts,
    "text": `Response sent by <@${response.user_id}> \`\`\`${message}\`\`\``,
  }
  axios.post(`https://studio-api-eu.ai.vonage.com/live-agent/outbound/${session_id}`, studio_data, {
    headers: { 'X-Vgai-Key': AI_STUDIO_KEY }
  })
  axios.post(SLACK_WEBHOOK_URL, slack_data);
  res.send("Response sent to user")
})

You’ll notice that we rely on one last function called parseMessage to clean up our data from Slack. You can add it at the end of our file:

const parseMessage = (input) => {
  const [sessionId, ...messageParts] = input.split(' ');
  const message = messageParts.join(' ');

  // Check if the first part is a valid session ID format
  const sessionIdPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;

  if (sessionIdPattern.test(sessionId)) {
    return { sessionId, message };
  }

  // If the first part is not a valid session ID, treat the entire input as a message
  return { message: input };
};

You can now test sending message replies from Slack! How cool is it to receive it on the SMS side?!

How to Send SMS Replies to Slack

So we now almost have our full scenario working. But we’re missing one thing! We need to allow our users to send continuous messages into Slack to reply to our human agents. We can do this by updating our /inbound endpoint.

Here, we handle the data from AI Studio and pull out the message and session_id. Using the session_id we can lookup our thread_ts. Once we have the thread_ts, we can send off our message to the correct thread with a POST request to Slack.

You can update your code to the following:

app.post('/inbound', (req, res) => {
  // console.log("inbound endpoint", req.body)
  const message = req.body.text
  const session_id = req.body.sessionId;
  const thread_ts = SESSIONS[session_id].thread_ts;
  const data = {
    "thread_ts": thread_ts,
    "text": `Customer respsonse: \`\`\`${message}\`\`\``
  }
  axios.post(SLACK_WEBHOOK_URL, data );
  res.send('Inbound endpoint reached');
});

How to Create a Slack Slash Command to End a Conversation

You might think, “That’s it! I’m done!”. Well almost! Our Live Agent Node doesn’t know how long a conversation back and forth between AI Studio and some other interface might last. For that we must let AI Studio know when to end the conversation and exit the Live Agent Routing node.

We’re going to create another slash command, similar to how we created /reply. This time we will create a command for /close_ticket. Close ticket will again accept the session_id to know which session to end. 

Create the following command:

Command: /close_ticket Request URL: TUNNEL_URL/slack/end Short Description: Customer issue has been resolved Usage Hint: [session_id]

And click “Save”. 

Inside of our /slack/end endpoint we need to handle the request from Slack. We only need to do three things:

  1. Grab the session_id by using our parseMessage function again

  2. Send a request to the Stop Connection endpoint in AI Studio to tell it to stop the conversation for the correct session_id

  3. Update the human agent in Slack that the conversation has been ended by posting a message in the correct thread

To do so, update your /slack/end code to look like this:

app.post('/slack/end', urlencodedParser, function (req, res) {
  const agentResponse = parseMessage(req.body.text);
  const session_id = agentResponse['sessionId'];
  const data = {};
  axios.post(`https://studio-api-eu.ai.vonage.com/live-agent/disconnect/${session_id}`, data, {
    headers: { 'X-Vgai-Key': AI_STUDIO_KEY }
  })
  const thread_ts = SESSIONS[session_id].thread_ts;
  const slack_data = {
    "thread_ts": thread_ts,
    "text": `This conversation has been marked as resolved.`,
  }
  axios.post(SLACK_WEBHOOK_URL, slack_data);
  res.send("Conversation ended")
})

Conclusion

And that’s a wrap! You've successfully implemented a seamless SMS to Slack and Slack to SMS integration for customer support using Vonage's AI Studio low-code platform. Restart your Node.js server by running node server.js in your terminal, and test the complete flow by sending messages back and forth between your phone's SMS and Slack. Isn't it amazing?

To further enhance this application, consider leveraging Slack's robust Web API, which enables interactivity based on emoji reactions. Additionally, you could incorporate AI Studio's WhatsApp agents, providing customers with an additional communication channel.

Are you using other low-code or no-code solutions for customer engagement? Join the Vonage Developer Community Slack or follow us on X, formerly known as Twitter to share your exciting projects and insights!

Integrating SMS and Slack for customer support is just one example of how low-code platforms like AI Studio can streamline business processes and improve customer experiences. Explore more possibilities and stay updated with the latest developments in the low-code and no-code spaces.

Additional Resources

Benjamin AronovDeveloper Advocate

Benjamin Aronov is a developer advocate at Vonage. He is a proven community builder with a background in Ruby on Rails. Benjamin enjoys the beaches of Tel Aviv which he calls home. His Tel Aviv base allows him to meet and learn from some of the world's best startup founders. Outside of tech, Benjamin loves traveling the world in search of the perfect pain au chocolat.

Ready to start building?

Experience seamless connectivity, real-time messaging, and crystal-clear voice and video calls-all at your fingertips.

Subscribe to Our Developer Newsletter

Subscribe to our monthly newsletter to receive our latest updates on tutorials, releases, and events. No spam.