Start building your own chatbot now >

By the end of this tutorial, you will be able to build a fully functional movie bot! It will able to make movie recommendations based on several criteria. We’re using Recast.AI platform to build the bot and The Movie Database for information on movies.

Here’s a demo chat with Movie Bot:

movie bot - demo GIF

Movie bot in action

Need to see it to believe it? That’s wise.



Interacting with third party APIs (such as The Movie Database) allows for much more interesting use cases that simple QnA chatbots. With Bot Skills, we added the option to call webhooks directly from the builder, which makes it even easier.

Today’s bot requires several steps:

  1. Extracting key pieces of information in a sentence
  2. Building the bot flow (triggers, requirements, actions)
  3. Creating and connecting a bot API able to fetch data from The Movie Database

You’ll need a Recast.AI account, Node.JS and potentially Ngrok for testing.

Before we jump in, please check this guide instead if you are looking for a guide detailing the creation of your first bot.

This tutorial covers Node.JS only. If you would rather code with Python, check this tutorial covering a similar use case.

Let’s get to it!


Intents are helpful to determine the overall meaning of a sentence. For our use case, knowing that the user wants to watch something is not enough.

We need to know what the users want to watch.

Entities are designed to solve this problem: they extract key information in a sentence.

Intents make you understand that you have to do something.Entities help you actually do something.

Let’s imagine you are a telco company providing phone and internet access, and your bot has an intent that understands when people are complaining about an outage:

movie bot - Entities extract key information from a sentence

Entities extract key information from a sentence

The entities extracted will help understand what is going wrong, where and since when.

For our movie bot, we will try to extract 3 key pieces of information:

  1. What the user wants to watch (movie vs TV show)
  2. What genre they are looking for
  3. In which language


To help you speed up your development, Recast.AI extracts several entities by default : Dates, locations, phone numbers… An exhaustive list is available here.

The Language entity will be helpful:

movie bot Gold Entities

Gold Entities – Language

See the little star next to the entity name? It differentiates a gold entity from a custom one.

We will use it to fulfill our third requirement: the movie language.


We will create custom entities to extract the information we need. As with intents, training is very important: the more examples you add to your bot, the more accurate it gets.

Training your entities can happen through multiple intents. Entities are independent of intents.

For our movie bot, we only need one intent, discover-movies, and 3 entities:

  • movie to identify that the user wants to watch a movie
  • tv for the same purpose but with TV shows
  • genre

Open the intent discover-movies and add expressions. Make sure to cover every possibility, this means a healthy mix of expressions with:

  • No entities at all: “My boyfriend wants to watch something tonight”
  • One entity: “I want to watch a movie”
  • Many entities: “Can you recommend me some French drama TV shows?”

To tag your expressions, select the text you want to tag and type your entity name:

movie bot - tagging custom entities

Tagging custom entities

You should add many more examples: 15 would be nice, but a production-ready bot would require at least 40 examples to perform well.

You can see here that “French” was detected as a nationality, not a language, because that’s what it is in this context. When building the bot flow, we’ll make sure to check for these two entities.


Since we just need to make sure all our criteria are filled before calling a Node.JS API, the build part will be rather simple.

We will just need one skill, let’s call it get-movies.

You can find an example of a configured skill here.


We want to trigger this skill if the intent @discover is present:

movie bot triggers

Message triggers


This tab helps you collect data before moving to Actions.We want to make sure the user specifies a medium, a genre, and a language before moving on:

movie bot - requirements 1


The requirements will be checked one by one. They can all be fulfilled on the first message, for example if the user says I want to watch a crime movie in English, then the Actions will be triggered immediately.

For each Requirement, you can choose to send a message if it is complete or if it is missing.

Sending messages when a requirement is complete can make your bot more lively: A crime movie? I love them too!, but are almost mandatory when the requirement is missing: You need to ask your users to fill what you need to know.

For example, I send quick replies with suggested genres if #genre is missing:

movie bot requirements 2

Conditional message if a requirement is missing

Once you have setup questions for the 3 groups of entities, go to the Actions tab.


Once the requirements are fulfilled, we want to call our API to actually perform the search.

Create a CALL WEHBOOK action. You can either type a full URL (eg:, or a relative url (/discover-movies). Recast will use the parameter Bot base URL in you bot settings when you type a relative URL.

Next, add an action RESET to empty the memory once the call has been made.

movie bot actions


If you don’t have a public server, or if you want to test your bot during development, ngrok is a very handy tool: It creates a public URL for you and forwards requests to your computer.

Once you installed it, run

ngrok http 5000

And copy the Forwarding URL ( All requests made to these URL will be forwarded to the port 5000 of your computer.

All your bot needs now is its API to get your movies!


The NodeJS part of this bot is fairly simple: It will behave as an HTTP proxy between Recast.AI and The Movie Database.

When your application receives a request from Recast, it sends a search query to the Movie Database with the criteria of your user and formats the JSON answer to the Recast’s message format.

movie bot API diagram

Bot API diagram

Start by scaffolding your project:

mkdir movie-bot && cd movie-bot
npm init
npm install --save express body-parser axios
touch index.js config.js discoverMovie.js

You will need a token to use the Movie Database API, go here to generate one, and fill your config.js file:

module.exports = {
   PORT: 5000,

Let’s create an Express application to handle the requests from Recast:

// index.js
const express = require('express');
const bodyParser = require('body-parser');
const config = require('./config.js');
const discoverMovie = require('./discoverMovie.js');

const app = express();

// Recast will send a post request to /errors to notify important errors
// described in a json body'/errors', (req, res) => {

We asked Recast to send a POST request to /discover-movies when a user has filled his search criterias.

The main goal of our controller is to pick and format the preferences from the memory to send them to the Movie Database’s API:

// index.js'/discover-movies', (req, res) => {
  console.log('[POST] /discover-movies');
  const memory = req.body.conversation.memory;
  const movie =;
  const tv =;

  // Check for the presence of entities movie or tv
  // If both are present, we prioritize movie
  const kind = movie ? 'movie' : 'tv';

  const genre = memory.genre;
  const genreId = constants.getGenreId(genre.value);

  const language = memory.language;
  const nationality = memory.nationality;

  // Similar to movie and tv, we prioritize language over nationality
  const isoCode = language
    ? language.short.toLowerCase()
    : nationality.short.toLowerCase();

  return discoverMovie(kind, genreId, isoCode)
    .then((carouselle) => res.json({
     replies: carouselle,
    .catch((err) => console.error('movieApi::discoverMovie error: ', err));

app.listen(config.PORT, () => console.log(`App started on port ${config.PORT}`));

There are two functions here that we have not declared yet: getGenreId and discoverMovie.

We need getGenreId because the Movie Database can’t search for a specific genre based on its English name, but rather on a custom number.

Here is how to translate a genre name to its id:

// index.js
const movieGenres = [
   { id: 12, name: 'Adventure' },
   { id: 14, name: 'Fantasy' },
   { id: 16, name: 'Animated' },
   { id: 16, name: 'Animation' },
   { id: 18, name: 'Drama' },
   { id: 27, name: 'Horror' },
   { id: 28, name: 'Action' },
   { id: 35, name: 'Comedy' },
   { id: 36, name: 'History' },
   { id: 37, name: 'Western' },
   { id: 53, name: 'Thriller' },
   { id: 80, name: 'Crime' },
   { id: 99, name: 'Documentary' },
   { id: 878, name: 'SF' },
   { id: 878, name: 'Sci Fi' },
   { id: 878, name: 'Sci-Fi' },
   { id: 878, name: 'Science Fiction' },
   { id: 9648, name: 'Mystery' },
   { id: 10402, name: 'Music' },
   { id: 10749, name: 'Romance' },
   { id: 10749, name: 'Romantic' },
   { id: 10751, name: 'Family' },
   { id: 10752, name: 'War' },
   { id: 10770, name: 'TV Movie' },

// Find the moviedb id of a genre entity
function getGenreId(genre) {
   const row = movieGenres.find(function(elem) {
    return === genre.toLowerCase();

   if (row) {
   return null;

Now that we have extracted and formatted all the filters of the request, we need to send the request to the Movie Database and format the answer:

// discoverMovie.js
const axios = require('axios');
const config = require('./config.js');

function discoverMovie(kind, genreId, language) {
 return axios.get(`${kind}`, {
   params: {
    api_key: config.MOVIEDB_TOKEN,
    sort_by: 'popularity.desc',
    include_adult: false,
    with_genres: genreId,
    with_original_language: language,
 }).then(response => {
   results =
   if (results.length === 0) {
    return [{
     type: 'quickReplies',
     content: {
         title: 'Sorry, but I could not find any results for your request :(',
         buttons: [{ title: 'Start over', value: 'Start over' }],

   const cards = results.slice(0, 10).map(movie => ({
    title: movie.title ||,
    subtitle: movie.overview,
    imageUrl: `${movie.poster_path}`,
    buttons: [
         type: 'web_url',
         value: `${}`,
         title: 'View More',

   return [
       type: 'text',
       content: "Here's what I found for you!",
     { type: 'carousel', content: cards },

module.exports = discoverMovie;

Start your application by running: node index.js

All being well, you should see: App started on port 5000

Movie recommendation, weather, health, traffic… With third-party APIs, everything is possible! Now that you’re familiar with the workflow, we can’t wait to hear from you about what you’re building!

🤖 Happy bot building 🤖

Have an issue with your bot? Is something unclear in this tutorial? Feel free to comment or join our Slack to discuss it.

Want to build your own conversational bot? Get started with Recast.AI !

  • Augusto Vera

    whenever I start the server side with “nodejs index.js”, the response is: “App started on port undefined”, and I have already set the config.js file with port 5000, I even opened it in my ubuntu firewall.

    • Nathan Grasset

      Hi Augusto,
      I made a mistake in the code snippet, the line:
      app.listen(config.port, () => console.log(`App started on port ${config.port}`));
      Should have been:
      app.listen(config.PORT, () => console.log(`App started on port ${config.PORT}`));
      I will update the snippet very soon, thanks for your help 🙂

  • Augusto Vera

    Also I assume all code examples that start with the comment “// index.js” all go inside the indes.js file. ¿Am I right?

  • Anonymous

    Not it started correctly in port 5000 🙂

  • Anonymous

    Another question, the skill get-movies contains a webhook url action, which in turn contains this relative URL: “/discover-movies”, I understand that this should be the URL that points to the node.js code which in turn calls the moviedatabase API, so it should be replaced by the temporary URL provided by NGROK, right?

  • Anonymous

    Hi again Nathan, you have as well another typo in the scaffolding of the project when you create the files with “touch”, the “discoverMovie.js” module is spelled “dicoverMovie.js” in your example, so the main module won’t find it.

  • Augusto Vera

    Also, when you setup the NGROK, the temporary URL it provides should be placed in the web hook action of the get-movies skill in the build section of the bot right?, so that the bot can effectively call the node.js gateway which in turn connects to the movie database API.

    • Brice Berdah

      Hello Augusto,

      You have two different URL/path to set up:

      1/ Your bot base URL: that’s t the base route that will be used for every API call made by the bot. This is your ngrok server URL.

      2/ For each API call, the relative path. You can set this up directly in the builder. Here is how it looks like for the /discover-movies route:

  • Nick

    I am excited to add a ChatBot to my catalog website. But independent of Messenger, Skype, or the others. I just want a little Chat Box in the lower right hand corner, that a Bot guides the visitor through with his or her comments about my products – and some suggestive selling, like the example of the movie DB, No signing in to Messenger at all. My visitors aren’t going to want to do that. I am also concerned that that might run afoul of the upcoming US and EU privacy rules/laws.

    • Brice Berdah

      We just launched the webchat feature that allow you to do just that! Now once your bot is done, you just have to copy/paste a code snippet on your website to include the chat bubble for your visitors

  • Theo

    Hello !
    I think there are code sections in “CREATING THE MOVIE BOT API” but I can’t see it… 🙁 I tried on firefox and chrome…

    • Brice Berdah

      We had some issues with our code snippets, we’re sincerely sorry for it. It’s fixed now.

  • Purav

    I went through all the steps and provided in tutorial and still cannot get the chatbot to show up on the webpage. Every time I type in localhost:5000 or the URL provided by ngrok, I get 404 error in my browser’s console. I double checked all of my work and went through ngrok and node.js installation on my computer, still stuck on the issue. Please help me out with this!

  • Anonymous

    Awsome! Hey where do I get info to download the completed project? I am finding a bit difficult to put the pieces together. Thank you

  • Piccosoft

    Your blog is very nice… Thanks for sharing your information…

  • Rahul Issar

    It doesn’t seem to be working for me it says “Cannot GET /” when I click on the url which is given to me from ngrok

  • Anonymous

    I’ve followed the tutorial. But unfortunately my web hook URL doesn’t getting any request from chat bot :(.
    URL :
    It works well when i trying using browser.

  • Anonymous

    Hi, guys
    I have followed the tutorial and try to implement the “Call WebHook”. But it is strange that my web Hook URL doesn’t getting fired. My webhook url is

  • Anonymous

    Or WebHook URL must be under domain “*”?

  • Anonymous


    I am using ngrok to expose my localhost and make the webhook url publicly accessible.
    But ngrok session expires in every 8 hours. Hence, every time I need to update the URL on Recast to call my api.
    Can you suggest a work around to overcome this situation.

    • Everistus Olumese

      You can host your webserver on heroku and supply the url heroku gives you as the webhook

  • Anonymous

    how do i set up Bot webhook base URL and Callback URL ?

  • Anonymous

    I am trying this tutorial.
    And this is my Movie API URL “” , but my concern is where to put this url. In which file; index.js,discoverMovie.js config.js and i see MOVIEDB_TOKEN: ‘XXX’, in config.js. What is MOVIEDB_TOKEN? Is it PAI key?

  • Alex

    Is this tutorial still valid? I’m also getting 404.

  • Anonymous

    npm install discoverMovie.js throws 404. No package available says.
    What to do?

  • Anonymous

    Hello, there issue : “Could not parse API response as JSON”

  • Anonymous

    Is this tutorial still valid

  • Anonymous

    I keep getting the same error…”Could not parse API response as JSON”

  • wcp5sw0chookbera7rzd7c84k9df4w9pqff


  • zhavxh5xm9t5gzwvcc4ycxdppui09heav0k


  • wumkati9zl6htb97pohap9q126vcmtkrk8ax


  • comment

    Add your comment here…

  • BIlly

    I think this is a good addition to my blog 😀
    Thank you

  • mobdro

    Awesome info share. Share with my friends.

This site uses Akismet to reduce spam. Learn how your comment data is processed.

This website uses cookies

Please confirm that you accept cookies to monitor the performance of our website