https://a.storyblok.com/f/270183/1368x665/5fd62200f0/26apr_dev-blog_google-cloud-run_sms.jpg

How to Send and Receive SMS Messages with Node.js and Google Cloud Run

Publicado el April 21, 2026

Tiempo de lectura: 9 minutos

Back in 2019, I organised a series of events featuring a “Cloud Run Club” at Finsbury Square Garden and  Victoria in London, followed by a Google Cloud Run workshop. Today, there will be no running, but you'll learn how to build and deploy an SMS application using Node.js, Express, and the Vonage Messages API on Google Cloud Run.

Unfortunately, while running, I can't scale to zero, but Google Cloud Run lets you deploy containerized applications that scale automatically. You only pay for what you use. It's great for building webhooks and APIs since you don't need to manage servers. Your app scales to zero when there's no traffic.

Outline and Prerequisites

Project Structure

You can find the complete code on GitHub. The project tree looks as follows.

blog-messages_api-cloud_run-sms/

├── index.js, contains our JavaScript

├── package.json, for our dependencies

├── .env.example, you can copy and create a .env from it to add your environment variables

├── .gitignore, for any files or folders not to be committed

└── private.key

Create a Vonage Application

I'll show you how to create a new Vonage application from the dashboard in the steps below. Alternatively, you can use the CLI to create an application.

  • Para crear una aplicación, vaya a la sección Crear una aplicación en el panel de Vonage y define un nombre para tu aplicación.

  • Si tiene intención de utilizar una API que utilice Webhooks, necesitará una clave privada. Haga clic en "Generar clave pública y privada"; la descarga debería iniciarse automáticamente. Guárdela de forma segura; esta clave no puede volver a descargarse si se pierde. Seguirá la convención de nomenclatura private_<id de su aplicación>.key. Esta clave puede utilizarse ahora para autenticar llamadas a la API. Nota: La clave no funcionará hasta que se guarde la aplicación.

  • Elija las funciones que necesite (por ejemplo, Voice, Messages, RTC, etc.) y proporcione los webhooks necesarios (por ejemplo, URL de eventos, URL de respuestas o URL de mensajes entrantes). Estos se describirán en el tutorial.

  • Para guardar e implementar, haz clic en "Generar nueva aplicación" para finalizar la configuración. Tu aplicación ahora está lista para usar con las API de Vonage.

Messages API Capability and Configure Webhooks

Toggle the Messages option under capabilities. We will use the Messages API v1.

Set placeholder URLs for now. We'll update these after deployment:

  • Inbound URL: https://example.com/webhooks/inbound

  • Status URL: https://example.com/webhooks/status

Save your application and note down your Application ID.

Settings page showing message webhooks setup with fields for Inbound URL, Status URL, version selection, and enhanced security toggle.Messages Capability

Para comprar un número de teléfono virtual, vaya a su panel API y siga los pasos que se indican a continuación.

Steps on how to purchase a phone number from the dashboard, from selecting the number and confirming the selection.Purchase a phone number

  1. Vaya a su Panel API

  2. Vaya a CONSTRUIR Y GESTIONAR > Numbers > Comprar Numbers.

  3. Elija los atributos necesarios y haga clic en Buscar

  4. Pulse el botón Comprar junto al número que desee y valide su compra

  5. Para confirmar que ha adquirido el número virtual, vaya al menú de navegación de la izquierda, en CONSTRUIR Y GESTIONAR, haga clic en Numbers y, a continuación, en Sus Numbers.

Once we've selected our number, it’s time to link it to our Vonage application. Go to Your Numbers and click the pencil icon next to your number. Select your application under "Messages". At [00:01:19], you can see how to purchase and link a virtual number.

Set Messages API as Default

Go to Settings and scroll down to "SMS Settings". Make sure "Messages API" is selected as the default API for SMS, then click "Save changes".

Screenshot of SMS settings with a choice between SMS API and Messages API. Text explains usage affects webhook format.SMS SettingsVonage has two APIs for sending SMS: the SMS API and the Messages API. This tutorial uses the Messages API. If you don't switch this setting, your webhooks will receive a different payload format, and the code won't work correctly.

Initialize the Project

Create a directory for your application and change into it. Run npm init -y to initialize your project.

mkdir blog-messages_api-cloud_run-sms

cd blog-messages_api-cloud_run-sms

npm init -y

Install Dependencies

We are going to use the following dependencies in our project: express, @vonage/server-sdk, @vonage/jwt. You can install them by running the following command from your terminal or command prompt.

npm install express @vonage/server-sdk @vonage/jwt

Server Setup

Create the index.js file and initialize your server with the required packages. Configure a Vonage client using environment variables that are read from the .env file to be created from the .env.example file.

const express = require('express');

const path = require('path');

const { Vonage } = require('@vonage/server-sdk');

const { verifySignature } = require('@vonage/jwt');

const app = express();

app.use(express.json());

app.use(express.urlencoded({ extended: true }));

// Initialize Vonage client

const vonage = new Vonage({

  applicationId: process.env.VONAGE_APPLICATION_ID,

  privateKey: path.join(__dirname, 'private.key')

});

const FROM_NUMBER = process.env.VONAGE_NUMBER;

We use path.join(__dirname, 'private.key') to get the absolute path to the key file, so that it works both locally and when deployed to Cloud Run.

Add the Home Page

Add the form content and styling that the user must complete to send SMS messages. The user has to enter their phone number and a message, then click the submit button to send the SMS.

app.get('/', (req, res) => {

  res.send(`

    <!DOCTYPE html>

    <html>

    <head>

      <title>Vonage SMS on Cloud Run</title>

      <style>

        body { font-family: Arial, sans-serif; max-width: 600px; margin: 50px auto; padding: 20px; }

        h1 { color: #7b00ff; }

        form { display: flex; flex-direction: column; gap: 15px; }

        input, textarea { padding: 10px; font-size: 16px; }

        button { padding: 12px; background: #7b00ff; color: white; border: none; cursor: pointer; }

      </style>

    </head>

    <body>

      <h1> Send SMS with Vonage </h1>

      <form action="/send" method="POST">

        <label>Phone Number (e.g. 444155551234)</label>

        <input type="tel" name="to" required>

        <label>Message</label>

        <textarea name="text" rows="4" required></textarea>

        <button type="submit">Send SMS</button>

      </form>

    </body>

    </html>

  `);

});

Add the Send Endpoint

Add the endpoint that sends the SMS using the Vonage Messages API. The vonage.messages.send() method accepts an object with channel set to 'sms' and message_type set to 'text'. The response includes a message_uuid you can use to track the message.

app.post('/send', async (req, res) => {

  const { to, text } = req.body;

 try {

    const response = await vonage.messages.send({

      message_type: 'text',

      to: to,

      from: FROM_NUMBER,

      channel: 'sms',

      text: text

    });

    console.log('SMS sent:', response.message_uuid);

    res.sendSMS sent! Message ID: ${response.message_uuid});

  } catch (error) {

    console.error('Error sending SMS:', error);

    res.status(500).sendFailed to send SMS: ${error.message});

  }

});

Add Webhook Endpoints

When someone sends an SMS to your Vonage virtual number you previously purchased, the SMS message arrives at Vonage, Vonage sends a POST request to your Google Cloud Run /webhooks/inbound endpoint, and finally, your app processes the message and responds with 200 OK, if everything works well. Add the webhook endpoints that use this verification, as shown below. The status webhook (/webhooks/status) receives delivery receipts. You'll know whether your message was delivered or failed.

app.post('/webhooks/inbound', (req, res) => {

  verifyJWT(req);

  console.log('Inbound SMS received:');

  console.log('From:', req.body.from);

  console.log('Text:', req.body.text);

  

  res.status(200).send('OK');

});

app.post('/webhooks/status', (req, res) => {

  verifyJWT(req);

  console.log('Status update:', req.body.status);

  res.status(200).send('OK');

});

Agrega una función de verificación JWT para verificar si la solicitud entrante (por ejemplo, mensaje o llamada) proviene de Vonage.

La variable VONAGE_API_SIGNATURE_SECRET es el secreto utilizado para firmar la solicitud correspondiente al secreto de firma asociado a la clave API incluida en las reivindicaciones JWT. Puede identificar su secreto de firma en los Configuración del panel de control.

Puede obtener más información sobre verificar la solicitud.

const verifyJWT = (req) => {
  // Verify if the incoming request came from Vonage
  const jwtToken = req.headers.authorization.split(" ")[1];
  if(!verifySignature(jwtToken, process.env.VONAGE_API_SIGNATURE_SECRET)) {
    console.error("Signature does not match");
    throw new Error("Not a Vonage API request");
  }

  console.log("JWT verified");
}

Asegúrese de instalar la dependencia @vonage/jwt.

npm install @vonage/jwt

Start the Server

Cloud Run sets the PORT environment variable automatically, so we use that if available. Add the code to start the server:

const PORT = process.env.PORT || 8080;

app.listen(PORT, () => {

  console.logServer running on port ${PORT});

});

Limited Local Testing

You can run the app locally to check that it starts without errors, but you won't be able to send SMS messages yet. The Vonage Messages API needs to reach your webhook URLs to verify requests and send status updates. That's why we're deploying to Cloud Run first.

If you want to verify the app runs, run the JavaScript file.

node index.js

Open http://localhost:3000 to see the form. The app should start without errors, but sending messages will fail until we deploy and configure the webhooks.

Deploy to Google Cloud Run

Now that the app works locally, let's deploy it to Google Cloud Run, so it's accessible on the internet and can receive webhooks from Vonage. 

Set Up Google Cloud

If you don't have a Google Cloud account yet, create one as required in the prerequisites.

Install the Google Cloud CLI

Download and install the Google Cloud CLI for your operating system. In the next subsection, I’ll show how to do it on macOS, but you can refer to the link above for other OSes. I wanted to show the steps I’ve done myself because I ran into some errors, and share some tips that might help.

On macOS

# Download the archive (Apple Silicon)

curl -O https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-cli-darwin-arm.tar.gz

# Extract it

tar -xzf google-cloud-cli-darwin-arm.tar.gz

# Run the install script

./google-cloud-sdk/install.sh

In case there’s a Python version error: the gcloud CLI requires Python 3.10 or higher. If you see a TypeError about unsupported operand type(s) for |, your Python is too old. Install a newer version with Homebrew as shown below (or any other way you prefer) and point gcloud to it:

brew install python@3.12

export CLOUDSDK_PYTHON=$(brew --prefix python@3.12)/bin/python3.12

./google-cloud-sdk/install.sh

The install script will ask if you want to add gcloud to your PATH. Choose yes. Then restart your command prompt/ terminal or run:

source ~/.zshrc

Now initialize gcloud:

gcloud init

This will open a browser window to log in to your Google account and select or create a new Google Cloud project.

Create a Google Cloud Project

If you don't have a project yet, create one. The Project IDs must be globally unique, so you can add some random characters.

gcloud projects create vonage-sms-yourname --name="Vonage Messages API Demo"

gcloud config set project vonage-sms-yourname

You can also create a project from the Google Cloud Console.

Enable a Google Cloud Billing Account

Cloud Run requires billing to be enabled. Go to the Billing page and link a billing account to your project. 

Enable Required Google Cloud APIs

Enable the Cloud Build and Cloud Run APIs:

gcloud services enable cloudbuild.googleapis.com run.googleapis.com

Deploy to Google Cloud Run

For production applications, consider using Google Secret Manager to store sensitive values. For this tutorial, we'll pass environment variables directly.

Make sure your “private.key” file is in the project directory. It gets bundled with your source code during deployment.

Deploy:

gcloud run deploy vonage-sms \

  --source . \

  --region us-central1 \

  --allow-unauthenticated \

  --set-env-vars "VONAGE_APPLICATION_ID=your_app_id" \

  --set-env-vars "VONAGE_NUMBER=444155551234" \

  --set-env-vars "VONAGE_API_SIGNATURE_SECRET=your_signature_secret"

First deploy: you'll see a prompt about creating an Artifact Registry repository named cloud-run-source-deploy to store your container images. Type y to continue.

The first deployment takes a couple of minutes. When it's done, you'll see a URL like https://vonage-sms-abc123-uc.a.run.app.

Update Vonage Webhooks

Now that you have your Cloud Run URL, go back to your Vonage Application and update the webhook URLs:

  • Inbound URL: https://your-cloud-run-url/webhooks/inbound

  • Status URL: https://your-cloud-run-url/webhooks/status

Don't forget to click Save changes at the bottom right of the page.

Test It Out

It’s time to test everything we’ve been setting up and see things running!

Send an SMS

Open your Cloud Run URL in a browser (the URL you got after deployment, like https://vonage-sms-abc123-uc.a.run.app). You should see the SMS form we built. Enter your phone number and a message, then press send. If everything is configured correctly, you'll receive the SMS on your phone within a few seconds. You can check country-specific features and restrictions.

Receive an SMS

Now test the other direction. Send an SMS from your phone to the Vonage number you purchased and linked to the application you created. This triggers the inbound webhook we set up. You can check the logs in two places:

From the Command Line

gcloud run logs read vonage-sms --region us-central1

From the Vonage Dashboard

Go to your Vonage Application, click on your application, and check the logs section to see message activity and any webhook errors. You should see your inbound message logged with the sender's number and text.

Conclusion

You've built and deployed an SMS application on Google Cloud Run using the Vonage Messages API. The app can send messages through a web form and receive incoming messages through webhooks. 

¿Tienes alguna pregunta o algo que compartir? Únete a la conversación en Slack de la comunidad de Vonagey mantente actualizado con el Boletín para desarrolladoressíguenos en X (antes Twitter)suscríbete a nuestro canal de YouTube para ver tutoriales en video, y sigue la página de página para desarrolladores de Vonage en LinkedInun espacio para que los desarrolladores aprendan y se conecten con la comunidad. Mantente conectado, comparte tu progreso y entérate de las últimas noticias, consejos y eventos para desarrolladores.

Further Reading

Compartir:

https://a.storyblok.com/f/270183/400x400/3f6b0c045f/amanda-cavallaro.png
Amanda CavallaroDefensor del Desarrollador