Retrieving Twilio MMS Image URLs with Node.js

July 28, 2020
Written by
Sam Agnew
Twilion

Copy of Generic Blog Header 4(1).png

When working with Twilio MMS, you often need to access a URL to the pictures contained in a message. Typically when the message is initially received, the webhook request that Twilio sends to your application will contain MediaUrl properties that you can use directly in your application. But if you want to use the REST API to access a URL to an image contained in a Message Resource, you need to do a little more work, as the only option available to you is the uri of the Media resource:

message.media().list().then(media => {
  media.forEach(m => {
    const url = 'https://api.twilio.com' + m.uri.replace('.json', '');
    console.log(url);
  });
});

Let's walk through a few different ways to retrieve a media URL from a Twilio MMS message in JavaScript. To try these examples for yourself, make sure you have a Twilio account anda phone number as well as Node.js and npm installed on your machine.

You'll also need to install the Twilio Node helper library:

npm install twilio@3.48.1

Accessing an image URL when an MMS message is received

If you want to access the image URL in a function that deals with a webhook request when a picture message is received, you might do something like this:

app.post('/', (req, res) => {
  if(req.body.NumMedia !== '0') {
    const url = req.body.MediaUrl0;
  }
  // Do other stuff
}

Let's walk through a quick example using the Express framework. If you want to try this for yourself, you'll need to install Express:

npm install express@4.17.1

For a basic web application that receives an MMS message containing an image and responds back with that same image, create a file called index.js and add the following code to it:

const express = require('express');
const bodyParser = require('body-parser');
const MessagingResponse = require('twilio').twiml.MessagingResponse;

const app = express();

app.use(bodyParser.urlencoded({ extended: false } ));

app.post('/', (req, res) => {
  const twiml = new MessagingResponse();

  if(req.body.NumMedia !== '0') {
    const url = req.body.MediaUrl0;

    twiml.message('Thanks for the image!').media(url);
  } else {
    twiml.message("Try sending a picture message.");
  }

  res.send(twiml.toString());
});

app.listen(3000, () => console.log('Example app listening on port 3000!'));

Run the file with the command node index.js in your terminal from the directory where the code exists.

To avoid having to deploy code just to test this out, we’ll use a nifty tool called ngrok to open a tunnel to our local machine.

Ngrok generates a custom forwarding URL that we will use to tell Twilio where to find our application. Download ngrok and run it in your terminal on port 3000:

./ngrok http 3000

Now we just need to point a phone number at our app.

Open the phone number configuration screen in your Twilio console. Scroll down to the “a message comes in” field. You should see something like this:

Twilio console

Enter the URL that was generated by ngrok, click save, then text a picture to your number to see it sent back to you from your Twilio number.

Accessing an image URL with the Twilio REST API

As mentioned before, the only thing that's available to you when it comes to messages that have already been received is the uri property on a Media resource, perhaps one that you retrieved from a Message resource.

If you just followed the previous steps to send an image to your Twilio phone number, you can access this using the Twilio REST API by fetching the message resource and building the media URL manually from this uri property.

Run the following Node code to fetch messages that were sent to your Twilio number that contain media and prints the URLs (if you just want to test this out and you haven't sent any media to your Twilio number, try texting an image right now):

// Grab your Account Sid and Auth Token from twilio.com/console and set them as
// Environment variables named: "TWILIO_ACCOUNT_SID" and "TWILIO_AUTH_TOKEN"

const client = require('twilio')();

client.messages.list({ to: 'your-twilio-number', limit: 5 }).then( messages => {
  messages.forEach(message => {
    if(message.numMedia !== '0') {
      message.media().list().then(media => {
        media.forEach(m => {
          const url = 'https://api.twilio.com' + m.uri.replace('.json', '');
          console.log(url);
        });
      });
    }
  });
});

This should print out all of the URLs for any message sent to your Twilio number (don't forget to replace the string in the code with your Twilio number) that contains media. If you want to view one of the images, copy the URL and paste it into a web browser.

That code might look a little messy because it's starting to fall prey to JavaScript's dreaded "callback hell" with all of the little steps involved. So using the async/await keywords, we can clean it up a bit:

// Grab your Account Sid and Auth Token from twilio.com/console and set them as
// Environment variables named: "TWILIO_ACCOUNT_SID" and "TWILIO_AUTH_TOKEN"

const client = require('twilio')();

(async () => {
  const messages = await client.messages.list({ to: 'your-twilio-number', limit: 5 });

  messages.forEach(async message => {
    if(message.numMedia !== '0') {
      const media = await message.media().list();

      media.forEach(m => {
        const url = 'https://api.twilio.com' + m.uri.replace('.json', '');
        console.log(url);
      });
    }
  });

})();

What do I use this for?

At this point, you now have access directly to the URLs of images sent to your Twilio phone numbers. From here you can do all kinds of things with it. Maybe you want to display them on a web page of an app you built for a local farmer's market, or maybe you want to download them to a machine to do more image processing/analysis on them.

Feel free to drop me a line if you have any question or just want to show off what you build: