# WhatsApp Business API Webhook Integration with Data API

This tutorial walks through integrating the WhatsApp Business API --- specifically, Cloud API --- and webhook setup in front of the MongoDB Atlas Data API.

The most interesting thing is we are going to use MongoDB Custom HTTPS Endpoints and Atlas Functions.

The WhatsApp Business Cloud API is intended for people developing for themselves or their organization and is also similar for Business Solution Providers (BSPs).

Webhook will trigger whenever a business phone number receives a message, updates the current state of the sent message, and more.

We will examine a way to set up webhooks to connect with WhatsApp, in addition to how to set up a function that sends/receives messages and stores them in the MongoDB database.

## Prerequisites

The core requirement is a Meta Business account. If you don't have a business account, then you can also use the Test Business account that is provided by Meta. Refer to the article Create a WhatsApp Business Platform account for more information.

WhatsApp Business Cloud API is a part of Meta's Graph API, so you need to set up a Meta Developer account and a Meta developer app. You can follow the instructions from the Get Started with Cloud API, hosted by Meta guide and complete all the steps explained in the docs to set everything up. When you create your application, make sure that you create an "Enterprise" application and that you add WhatsApp as a service. Once your application is created, find the following and store them somewhere.

-  Access token: You can use a temporary access token from your developer app > WhatsApp > Getting Started page, or you can generate a permanent access token.

-  Phone number ID: You can find it from your developer app > WhatsApp > Getting Started page. It has the label "Phone number ID", and can be found under the "From" section.

Next, you'll need to set up a MongoDB Atlas account, which you can learn how to do using the MongoDB Getting Started with Atlas article. Once your cluster is ready, create a database called `WhatsApp` and a collection called `messages`. You can leave the collection empty for now.

Once you have set up your MongoDB Atlas cluster, refer to the article on how to create an App Services app to create your MongoDB App Services application. On the wizard screen asking you for the type of application to build, choose the  "Build your own App" template.

## Verification Requests endpoint for webhook

The first thing you need to configure in the WhatsApp application is a verification request endpoint. This endpoint will validate the key and provides security to your application so that not everyone can use your endpoints to send messages.

When you configure a webhook in the WhatsApp Developer App Dashboard, it will send a GET request to the Verification Requests endpoint. Let's write the logic for this endpoint in a function and then create a custom HTTPS endpoint in Atlas.

To create a function in Atlas, use the "App Services" > "Functions" menu under the BUILD section. From that screen, click on the "Create New Function" button and it will show the Add Function page.

Here, you will see two tabs: "Settings" and "Function Editor." Start with the "Settings" tab and  let's configure the required details:

-  Name: Set the Function Name to `webhook_get`.

-  Authentication: Select `System`. It will bypass the rule and authentication when our endpoint hits the function.

To write the code, we need to click on the "Function Editor" tab. You need to replace the code in your editor. Below is the brief of our code and how it works.

You need to set a secret value for `VERIFY_TOKEN`. You can pick any random value for this field, and you will need to add it to your WhatsApp webhook configuration later on.

The request receives three query parameters: `hub.mode`, `hub.verify_token`, and `hub.challenge`.

We need to check if `hub.mode` is `subscribe` and that the `hub.verify_token` value matches the `VERIFY_TOKEN`. If so, we return the `hub.challenge` value as a response. Otherwise, the response is forbidden.

```javascript
// this function Accepts GET requests at the /webhook endpoint. You need this URL to set up the webhook initially, refer to the guide https://developers.facebook.com/docs/graph-api/webhooks/getting-started#verification-requests
exports = function({ query, headers, body }, response) {
  /**
  * UPDATE YOUR VERIFY TOKEN
  * This will be the Verify Token value when you set up the webhook
  **/
  const VERIFY_TOKEN = "12345";

  // Parse params from the webhook verification request
  let mode = query"hub.mode"],
     token = query["hub.verify_token"],
     challenge = query["hub.challenge"];
  // Check the mode and token values are correct
  if (mode == "subscribe" && token == VERIFY_TOKEN) {
    // Respond with 200 OK and challenge token from the request
    response.setStatusCode(200);
    response.setBody(challenge);
  } else {
    // Responds with '403 Forbidden' if verify tokens do not match
    response.setStatusCode(403);
  }
};

```

Now, we are all ready with the function. Click on the "Save" button above the tabs section, and use the "Deploy" button in the blue bar at the top to deploy your changes.

Now, let's create a custom HTTPS endpoint to expose this function to the web. From the left navigation bar, follow the "App Services" > "HTTPS Endpoints" link, and then click on the "Add an Endpoint" button. It will show the Add Endpoint page.

Let's configure the details step by step:

1.  Route: This is the name of your endpoint. Set it to `/webhook`.

2.  Operation Type under Endpoint Settings: This is the read-only callback URL for an HTTPS endpoint. Copy the URL and store it somewhere. The WhatsApp Webhook configuration will need it.

3.  HTTP Method under Endpoint Settings: Select the "GET" method from the dropdown.

4.  Respond With Result under Endpoint Settings: Set it to "On" because WhatsApp requires the response with the exact status code.

5.  Function: You will see the previously created function `webhook_get`. Select it.

We're all done. We just need to click on the "Save" button at the bottom, and deploy the application.

Wow, that was quick! Now you can go to [WhatsApp > Configuration under Meta Developer App and set up the Callback URL that we have generated in the above custom endpoint creation's second point. Click Verify Token, and enter the value that you have specified in the `VERIFY_TOKEN` constant variable of the function you just created.

## Event Notifications webhook endpoint

The Event Notifications endpoint is a POST request. Whenever new events occur, it will send a notification to the callback URL. We will cover two types of notifications: received messages and message status notifications if you have subscribed to the `messages` object under the WhatsApp Business Account product. First, we will design the schema and write the logic for this endpoint in a function and then create a custom HTTPS endpoint in Atlas.

Let's design our sample database schema and see how we will store the sent/received messages in our MongoDB collection for future use. You can reply to the received messages and see whether the user has read the sent message.

### Sent message document:

```json
{
    type: "sent", // this is we sent a message from our WhatsApp business account to the user
    messageId: "", // message id that is from sent message object
    contact: "", // user's phone number included country code
    businessPhoneId: "", // WhatsApp Business Phone ID
    message: {
        // message content whatever we sent
    },
    status: "initiated | sent | received | delivered | read | failed", // message read status by user
    createdAt: ISODate(), // created date
    updatedAt: ISODate() // updated date - whenever message status changes
}
```

### Received message document:

```json
{
    type: "received", // this is we received a message from the user
    messageId: "", // message id that is from the received message object
    contact: "", // user's phone number included country code
    businessPhoneId: "", // WhatsApp Business Phone ID
    message: {
        // message content whatever we received from the user
    },
    status: "ok | failed", // is the message ok or has an error
    createdAt: ISODate() // created date
}
```

Let's create another function in Atlas. As before, go to the functions screen, and click the  "Create New Function" button. It will show the Add Function page. Use the following settings for this new function.

-  Name: Set the Function Name to `webhook_post`.

-  Authentication: Select `System`. It will bypass the rule and authentication when our endpoint hits the function.

To write code, we need to click on the "Function Editor" tab. You just need to replace the code in your editor. Below is the brief of our code and how it works.

In short, this function will do either an update operation if the notification is for a message status update, or an insert operation if a new message is received.

```javascript
// Accepts POST requests at the /webhook endpoint, and this will trigger when a new message is received or message status changes, refer to the guide https://developers.facebook.com/docs/graph-api/webhooks/getting-started#event-notifications
exports = function({ query, headers, body }, response) {
    body = JSON.parse(body.text());
    if (body.object && body.entry) {
        // Find the name of the MongoDB service you want to use (see "Linked Data Sources" tab)
        const clusterName = "mongodb-atlas",
                  dbName = "WhatsApp",
                  collName = "messages";
        body.entry.map(function(entry) {
            entry.changes.map(function(change) {
              // Message status notification
              if (change.field == "messages" && change.value.statuses) {
                change.value.statuses.map(function(status) {
                  // Update the status of a message
                  context.services.get(clusterName).db(dbName).collection(collName).updateOne(
                    { messageId: status.id },
                    {
                      $set: {
                        "status": status.status,
                        "updatedAt": new Date(parseInt(status.timestamp)*1000)
                      }
                    }
                  );
                });
              }
              // Received message notification
              else if (change.field == "messages" && change.value.messages) { 
                change.value.messages.map(function(message) {
                  let status = "ok";
                  // Any error
                  if (message.errors) {
                    status = "failed";
                  }
                  // Insert the received message
                  context.services.get(clusterName).db(dbName).collection(collName).insertOne({ 
                    "type": "received", // this is we received a message from the user
                    "messageId": message.id, // message id that is from the received message object
                    "contact": message.from, // user's phone number included country code
                    "businessPhoneId": change.value.metadata.phone_number_id, // WhatsApp Business Phone ID
                    "message": message, // message content whatever we received from the user
                    "status": status, // is the message ok or has an error
                    "createdAt": new Date(parseInt(message.timestamp)*1000) // created date
                  });
                });
              }
            });
        });
    }
    response.setStatusCode(200);  
};

```

Now, we are all set with the function. Click on the "Save" button above the tabs section.

Just like before, let's create a custom HTTPS endpoint in the "HTTPS Endpoints" tab. Click on the "Add an Endpoint" button, and it will show the Add Endpoint page.

Let's configure the details step by step:

1.  Route: Set it to `/webhook`.

2.  HTTP Method under Endpoint Settings: Select the "POST" method from the dropdown.

3.  Respond With Result under Endpoint Settings: Set it to `On`.

4.  Function: You will see the previously created function `webhook_post`. Select it.

We're all done. We just need to click on the "Save" button at the bottom, and then deploy the application again.

Excellent! We have just developed a webhook for sending and receiving messages and updating in the database, as well. So, you can list the conversation, see who replied to your message, and follow up.

## Send Message endpoint

Send Message Endpoint is a POST request, almost similar to the Send Messages of the WhatsApp Business API. The purpose of this endpoint is to send and store the message with `messageId` in the collection so the Event Notifications Webhook Endpoint can update the status of the message in the same document that we already developed in the previous point. We will write the logic for this endpoint in a function and then create a custom HTTPS endpoint in Atlas.

Let's create a new function in Atlas with the following settings.

-  Name: Set the Function Name to `send_message`.

-  Authentication: Select "System." It will bypass the rule and authentication when our endpoint hits the function.

You need to replace the code in your editor. Below is the brief of our code and how it works.

The request params should be:

-   body: The request body should be the same as the WhatsApp Send Message API.

-   headers: Pass Authorization Bearer token. You can use a temporary or permanent token. For more details, read the prerequisites section.

-   query: Pass the business phone ID in `businessPhoneId` property. For how to access it, read the prerequisites section.

This function uses the `https` node module to call the send message API of WhatsApp business. If the message is sent successfully, then insert a document in the collection with the messageId.

```javascript
// Accepts POST requests at the /send_message endpoint, and this will allow you to send messages the same as documentation https://developers.facebook.com/docs/whatsapp/cloud-api/guides/send-messages
exports = function({ query, headers, body }, response) {
    response.setHeader("Content-Type", "application/json");
    body = body.text();
    // Business phone ID is required
    if (!query.businessPhoneId) {
      response.setStatusCode(400);
      response.setBody(JSON.stringify({
        message: "businessPhoneId is required, you can pass in query params!" 
      }));
      return;
    }
    // Find the name of the MongoDB service you want to use (see "Linked Data Sources" tab)
    const clusterName = "mongodb-atlas",
              dbName = "WhatsApp",
              collName = "messages",
              https = require("https");
    // Prepare request options
    const options = { 
      hostname: "graph.facebook.com",
      port: 443, 
      path: `/v15.0/${query.businessPhoneId}/messages`,
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "Authorization": headers.Authorization,
        "Content-Length": Buffer.byteLength(body)
      }
    };
    const req = https.request(options, (res) => {
      response.setStatusCode(res.statusCode);
      res.setEncoding('utf8');
      let data = ];
      res.on('data', (chunk) => {
          data.push(chunk);
      }); 
      res.on('end', () => {
        if (res.statusCode == 200) {
          let bodyJson = JSON.parse(body);
          let stringData = JSON.parse(data[0]);
          // Insert the message
          context.services.get(clusterName).db(dbName).collection(collName).insertOne({ 
            "type": "sent", // this is we sent a message from our WhatsApp business account to the user
            "messageId": stringData.messages[0].id, // message id that is from the received message object
            "contact": bodyJson.to, // user's phone number included country code
            "businessPhoneId": query.businessPhoneId, // WhatsApp Business Phone ID
            "message": bodyJson, // message content whatever we received from the user
            "status": "initiated", // default status
            "createdAt": new Date() // created date
          });
        } 
        response.setBody(data[0]);
      }); 
    });
    req.on('error', (e) => {
      response.setStatusCode(e.statusCode);
      response.setBody(JSON.stringify(e));
    });
    // Write data to the request body
    req.write(body);
    req.end();  
};
```

Now, we are all ready with the function. Click on the "Save" button above the tabs section.

Let's create a custom HTTPS endpoint for this function with the following settings.

1.  Route: Set it to `/send_message`.

2.  HTTP Method under Endpoint Settings: Select the "POST" method from the dropdown.

3.  Respond With Result under Endpoint Settings: Set it to "On."

4.  Function: You will see the previously created function `send_message`. Select it.

We're all done. We just need to click on the "Save" button at the bottom.

Refer to the below curl request example. This will send a default welcome template message to the users. You just need to replace your value inside the `<>` brackets.

```bash
curl --location '?businessPhoneId=' \
  --header 'Authorization: Bearer ' \
  --header 'Content-Type: application/json' \
  --data '{ \
      "messaging_product": "whatsapp", \
      "to": "", \
      "type": "template", \
      "template": { \
          "name": "hello_world", \
          "language": { "code": "en_US" } \
      } \
  }'
```

Great! We have just developed an endpoint that sends messages to the user's WhatsApp account from your business phone number.

## Conclusion

In this tutorial, we developed three custom HTTPS endpoints and their functions in MongoDB Atlas. One is Verification Requests, which verifies the request from WhatsApp > Developer App's webhook configuration using Verify Token. The second is Event Notifications, which can read sent messages and status updates,  receive messages, and store them in MongoDB's collection. The third is Send Message, which can send messages from your WhatsApp business phone number to the user's WhatsApp account.

Apart from these things, we have built a collection for messages. You can use it for many use cases, like designing a chat conversation page where you can see the conversation and reply back to the user. You can also build your own chatbot to reply to users.

If you have any questions or feedback, check out the [MongoDB Community Forums and let us know what you think.