DEV Community

Rohan Rajpal
Rohan Rajpal

Posted on

WhatsApp AI Chatbot: Let's build one with the API

Hey there, I'm Rohan, founder of Spur, a WhatsApp API & Instagram Comment/DM Automation Software.

A few weeks ago, I wrote a blog on where you should build a WhatsApp integration or not. That was a high-level article on the topic, today we'll be learning on how do you get started with the WhatsApp API?

Let's make a WhatsApp AI Chatbot

Today, we'll make a simple bot that runs on a Node server, takes in webhooks from the WhatsApp Cloud API, reads the messages & uses the
OpenAI GPT 4o to send a response.

Want to jump straight to the code? Link is at the bottom.

Get the WhatsApp Test Number & Token

This is fairly straightforward, and Meta already has a guide on this. Going forward, I'm going to assume

  1. You have created a Meta App
  2. Added the WhatsApp product
  3. Got a test number and can send messages from it

For the token, you can use the temporary token, we'll cover deploying to production in another tutorial someday.

Setting up the Node Server

if you dont have pnpm setup, this is the easiest way

corepack enable
corepack prepare pnpm@latest --activate
Enter fullscreen mode Exit fullscreen mode

now we start

mkdir whatsapp-ai-bot

# intialize npm project
pnpm init

# make the server file
touch index.js

# env file
touch .env
Enter fullscreen mode Exit fullscreen mode

Install the required dependencies now:

pnpm install express dotenv openai
Enter fullscreen mode Exit fullscreen mode

Never commit API keys. To keep sensitive information like API keys out of the codebase, we will use environment variables. Create a .env file in the root directory of your project and add the following line:

OPENAI_API_KEY=<your-openai-api-key>
Enter fullscreen mode Exit fullscreen mode

Now use the created index.js file and add the following code

import express from 'express';
import { config } from 'dotenv';
import OpenAI from 'openai';

// Load environment variables
config();

// Create a web server
const app = express();
const port = process.env.PORT || 3034;
app.listen(port, () => {
  console.log(`Server running on port ${port}`);
});

// Add middleware to parse JSON bodies
app.use(express.json());

// Initialize OpenAI API
const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
});

app.get('/webhooks', (req, res) => {
  if (
    req.query['hub.mode'] === 'subscribe' &&
    req.query['hub.verify_token'] === '1234'
  ) {
    res.send(req.query['hub.challenge']);
  } else {
    res.sendStatus(400);
  }
});

app.post('/webhooks', async (req, res) => {
  const body = req.body.entry[0].changes[0];
  if (body.field !== 'messages') {
    // not from the messages webhook so dont process
    return res.sendStatus(200);
  }

  if (!body.value.messages) {
    return res.sendStatus(200);
  }

  const text = body.value.messages
    .map((message) => message.text.body)
    .join('\n\n');

  console.log(text);

  const completion = await openai.chat.completions.create({
    model: 'gpt-4o-mini',
    messages: [{ role: 'user', content: text }],
  });

  // Extract the response from the OpenAI completion
  const aiResponse = completion.choices[0].message.content;

  // Extract the phone number from the incoming message
  const phoneNumber = body.value.messages[0].from;

  // Prepare the message payload
  const messagePayload = {
    messaging_product: 'whatsapp',
    to: phoneNumber,
    type: 'text',
    text: {
      body: aiResponse,
    },
  };

  // Send the response back to WhatsApp
  try {
    const response = await fetch(
      `https://graph.facebook.com/v20.0/${process.env.PHONE_NUMBER_ID}/messages`,
      {
        method: 'POST',
        headers: {
          Authorization: `Bearer ${process.env.SYSTEM_TOKEN}`,
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(messagePayload),
      },
    );

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    console.log('Message sent successfully');
  } catch (error) {
    console.error('Failed to send message:', error);
  }

  res.sendStatus(200);
});
Enter fullscreen mode Exit fullscreen mode

and also modify the package.json to

{
  "name": "whatsapp-ai-bot",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "node server.js",
    "dev": "nodemon server.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "type": "module",
  "packageManager": "pnpm@9.6.0",
  "dependencies": {
    "dotenv": "^16.4.5",
    "express": "^4.19.2",
    "openai": "^4.53.2"
  },
  "devDependencies": {
    "nodemon": "^3.1.4"
  }
}
Enter fullscreen mode Exit fullscreen mode

Understanding how the code works

Image description

  1. We first create a "GET" endpoint so that when you set the webhook in the Facebook app, the challenge passes. I've also included a value for the verify token in the .env.example value that you can use.
  2. Then comes the POST endpoint, which does the actual magic. It takes in the text, passes it onto ChatGPT and sends that response to the user.

Next Steps

This example is just the tip of the iceberg, there are so so many things you can do to improve this

  1. To implement idempotency, WhatsApp webhooks are not unique and can repeat.
  2. Add an in memory chat history
  3. Persist that memory with Redis/relational databases
  4. Leverage rich content like buttons etc. as responses (this will require OpenAI functions to build)

Conclusion

So that's about it. If you're also interested in the article I wrote about WhatsApp integration, you can check it out here.

I've also shared the code of the working example on Spur's GitHub organization.

Top comments (0)