Guessing Game Application Migration from Plivo XML to FlexML

If you have read the Migrating from Plivo to CarrierX Quick Start, you could see that Plivo XML and FlexML are not too much different. Both offer a special syntax to provide instructions, and all you need is to change the representation of these instructions in your code.

But when it comes to a real-case migration from Plivo to CarrierX, users might meet some difficulties.

Let’s see such a migration in details and learn how to solve the issues that arise so that your migrated application worked with CarrierX flawlessly.

Getting Application Source Code

We take the Guessing Game application from Plivo as an example. This sample application answers the call and offers the calling party to play a game: the application generates a random number between 1 and 100 and the calling party must guess what the number is. Additionally, the application has a route that explains the game rules. The calling party has 10 tries to guess the number. You can download the application source code at GitHub.

Once you download the application, you can see that it has the following structure:

Procfile
README.md
guess_game.py
requirements.txt

The only file we need to modify here is guess_game.py. All the routes used to send requests and responses for our application are here.

Modifying Application Routes

Let’s take a closer look at each of the routes in the file.

The application code file contains four routes:

And one function:

We will go step by step through each of the routes, check what each of them contains, and learn how to modify these routes and functions to migrate their code to FlexML.

1. index Route

The first route we are going to change is index. The application uses it to greet its users and give them initial instructions on where to start.

We modify the index route like this:

  1. The index route uses the ResponseElement() class to build the Plivo XML response to the call. In FlexML we do not need it, so we can remove this line.

  2. We remove the response.add() function which builds the response. In FlexML, we will build an XML response instead.

  3. Finally, we return the FlexML formatted string instead of the Plivo XML Response(). In it, we replace Plivo XML Speak element with the corresponding CarrierX FlexML Say verb, the GetDigits element with the Gather verb (dropping the unsupported retries attribute).

Plivo XML Python Code

@app.route('/', methods=['POST', 'GET'])
def index():
    response = plivoxml.ResponseElement()
    response.add(
        plivoxml.GetDigitsElement(
            action="/main_menu_response",
            method="POST",
            timeout="4",
            num_digits="4",
            retries="1",
        )
    )
    response.add(plivoxml.SpeakElement("Hello, welcome to Plivo guessing game app!"))
    response.add(plivoxml.SpeakElement("To play the game Press 1"))
    response.add(plivoxml.SpeakElement("To learn how to play Press 2"))
    response.add(plivoxml.SpeakElement("You can end this call at any time."))
    return Response(response.to_string(), mimetype='application/xml')

Corresponding FlexML Python Syntax

@app.route('/', methods=['POST', 'GET'])
def index():
    return '''
        <Response>
            <Gather action="/main_menu_response" method="POST" timeout="4" numDigits="4">
                <Say>Hello, welcome to CarrierX guessing game app!</Say>
                <Say>To play the game Press 1</Say>
                <Say>To learn how to play Press 2</Say>
                <Say>You can end this call at any time.</Say>
            </Gather>
        </Response>'''

2. main_menu_response Route

The main_menu_response route processes the response from the calling party and redirects them to the next instructions set depending on the entered digits.

Thus, we change this route the following way:

  1. The main_menu_response route receives the request arguments data from the previous route as request.form. In CarrierX FlexML, the POST requests send data in JSON format, which you can receive and parse using the Flask request module. Refer to the code below to see how we replace the definition of the post_args variable.

  2. We remove plivo.Response() used to build the Plivo XML response to the call as we are not going to use it.

  3. Next, we update the URL variable assigned if the calling party pressed 1: we replace absolute_action_url in Plivo with action_url in CarrierX as FlexML allows using the relative path. We also remove the response.add() function and do not return anything at this step.

  4. The same way, we update the URL variable assigned if the calling party pressed 2 and also remove the response.add() function here.

  5. Finally, we return the FlexML formatted string instead of the Plivo XML Response(). We will use the new action_url variables in our return string.

Plivo XML Python Code

@app.route('/main_menu_response', methods=['POST',])
def mm_response():
    post_args = request.form
    response = plivo.Response()
    input_digit = post_args.get('Digits', None)
    if input_digit != "1" and input_digit != "2":
        return exit_sequence()
    else:
        if input_digit == "1":
            absolute_action_url = url_for('play_game', _external=True)
            response.add(plivoxml.RedirectElement(absolute_action_url,method='POST'))
            return Response(response.to_string(), mimetype='application/xml')
        else:
            absolute_action_url = url_for('how_to_play', _external=True)
            response.add(plivoxml.RedirectElement(absolute_action_url,method='POST'))
            return Response(response.to_string(), mimetype='application/xml')

Corresponding FlexML Python Syntax

@app.route('/main_menu_response', methods=['POST',])
def mm_response():
    post_args = request.get_json()
    input_digit = post_args.get('Digits', None)
    if input_digit != "1" and input_digit != "2":
        return exit_sequence()
    else:
        if input_digit == "1":
            action_url = url_for('play_game')
        else:
            action_url = url_for('how_to_play')
        return f'''
            <Response>
                <Redirect>{action_url}</Redirect>
            </Response>'''

3. play_game Route

The play_game route contains the game logic:

@app.route('/play_game', methods=['POST',])
def play_game():
    if not request.args.get('guesses', None):
        secret = random.randint(1, 100)
        guesses = 10
        response = plivoxml.ResponseElement()
        absolute_action_url = url_for('play_game', _external=True,
                                      **{'secret': str(secret),
                                         'guesses': str(guesses)})
        response.add(
            plivoxml.GetDigitsElement(
                action=absolute_action_url,
                method="POST",
                timeout="10",
                num_digits="4",
                retries="1",)
        .add(
            plivoxml.SpeakElement(
                "I have thought of a secret number between one and one hundred. You have ten guesses to find it!")
                ).add(plivoxml.SpeakElement("You can make your guess now."))
                )
        return Response(response.to_string(), mimetype='application/xml')
    else:
        secret = int(request.args.get('secret', '0'))
        guesses = int(request.args.get('guesses', '0')) - 1
        absolute_action_url = url_for('play_game', _external=True,
                                      **{'secret': str(secret),
                                         'guesses': str(guesses)})
        input_num = request.form.get('Digits', "0")
        response = plivoxml.ResponseElement()
        try:
            input_num = int(input_num)
        except ValueError as e:
            print (e)
            return exit_sequence()
        if input_num == secret:
            response.add(plivoxml.SpeakElement("Congratulations! {} is the right number!"
                              " You have guessed"
                              " it in {} guesses - your score is {}.".format(secret, 10 - guesses, guesses + 1)))
            response.add(plivoxml.WaitElement(None).set_length(2))
            response.add(plivoxml.HangupElement())
            return Response(response.to_string(), mimetype='application/xml')
        else:
            if input_num > secret:
                answer = "Sorry, you guessed %d. The secret is lesser."
            else:
                answer = "Sorry, you guessed %d. The secret is greater."
            response.add(plivoxml.SpeakElement(answer+input_num))
            if guesses > 0:
                response.add(
                    plivoxml.GetDigitsElement(action=absolute_action_url, method='POST',timeout='10',num_digits='4',retries='1').add(
                        plivoxml.SpeakElement("You have {} guesses remaining! Guess again!".format(guesses))))
            else:
                response.add(plivoxml.WaitElement (None).set_length (1))
                response.add(plivoxml.SpeakElement('Sorry, you dont have any remaining guesses. The secret was {}' .format(secret)))
                response.add(plivoxml.HangupElement())
            return Response(response.to_string(), mimetype='application/xml')

As the route code is quite lengthy, we will split it into several parts and see what we need to change in each of these parts.

Route First-time Access

When the calling party accesses the play_game.py route for the first time, the application “thinks” of a secret number (generates a random integer between 1 and 100), informs the calling party and collects the entered digits. After that, it runs the route from the beginning once again, this time with the entered digits and available attempts as arguments.

We change the first part of the route the following way:

  1. The play_game route uses the ResponseElement() class to build the Plivo XML response to the call. In FlexML we do not need it, so we can remove this line.

  2. We replace absolute_action_url with action_url containing a relative URL and make it escaped with the help of escape().

  3. We remove the response.add() function which builds the response. In FlexML, we will build an XML response instead.

  4. Finally, we return the FlexML formatted string instead of the Plivo XML Response(). In it, we replace Plivo XML Speak element with the corresponding CarrierX FlexML Say verb, the GetDigits element with the Gather verb (dropping the unsupported retries attribute).

Plivo XML Python Code

@app.route('/play_game', methods=['POST',])
def play_game():
    if not request.args.get('guesses', None):
        secret = random.randint(1, 100)
        guesses = 10
        response = plivoxml.ResponseElement()
        absolute_action_url = url_for('play_game', _external=True,
                                      **{'secret': str(secret),
                                         'guesses': str(guesses)})
        response.add(
            plivoxml.GetDigitsElement(
                action=absolute_action_url,
                method="POST",
                timeout="10",
                num_digits="4",
                retries="1",)
        .add(
            plivoxml.SpeakElement(
                "I have thought of a secret number between one and one hundred. You have ten guesses to find it!")
                ).add(plivoxml.SpeakElement("You can make your guess now."))
                )
        return Response(response.to_string(), mimetype='application/xml')

Corresponding FlexML Python Syntax

@app.route('/play_game', methods=['POST'])
def play_game():
    if not request.args.get('guesses', None):
        secret = random.randint(1, 100)
        guesses = 10
        action_url = escape(url_for('play_game',
                                **{'secret': str(secret),
                                  'guesses': str(guesses)}))
        return f'''
            <Response>
                <Gather action="{action_url}" method="POST" timeout="10" numDigits="4">
                    <Say>I have thought of a secret number between one and one hundred. You have ten guesses to find it!</Say>
                    <Say>You can make your guess now.</Say>
                </Gather>
            </Response>'''

Correct Guess

If this is not the first time the calling party accesses the play_game.py route and they have already entered some digits, the application receives the digits pressed by the calling party. It checks the number, and counts the number of attempts the calling party has.

If the number equals the secret number, the application congratulates the calling party and hangs up after a two-second pause. The game is over.

We change this part of the play_game.py route the following way:

  1. First, we replace absolute_action_url with action_url containing a relative URL and make it escaped with the help of escape().

  2. The play_game.py route receives the request arguments data from the POST request as request.form. In CarrierX FlexML, the POST requests send data in JSON format, which you can receive and parse using the Flask request module. Refer to the code below to see how we replace the definition of the input_num variable.

  3. The route uses the ResponseElement() class to build the Plivo XML response to the call. In FlexML we do not need it, so we can remove this line.

  4. We remove the response.add() function which builds the response. In FlexML, we will build an XML response instead.

  5. Finally, we return the FlexML formatted string instead of the Plivo XML Response(). In it, we replace Plivo XML Speak element with the corresponding CarrierX FlexML Say verb, the Wait element with the Pause verb.

Plivo XML Python Code

else:
    secret = int(request.args.get('secret', '0'))
    guesses = int(request.args.get('guesses', '0')) - 1
    absolute_action_url = url_for('play_game', _external=True,
                                  **{'secret': str(secret),
                                    'guesses': str(guesses)})
    input_num = request.form.get('Digits', "0")
    response = plivoxml.ResponseElement()
    try:
        input_num = int(input_num)
    except ValueError as e:
        print (e)
        return exit_sequence()
    if input_num == secret:
        response.add(plivoxml.SpeakElement("Congratulations! {} is the right number!"
                          " You have guessed"
                          " it in {} guesses - your score is {}.".format(secret, 10 - guesses, guesses + 1)))
        response.add(plivoxml.WaitElement(None).set_length(2))
        response.add(plivoxml.HangupElement())
        return Response(response.to_string(), mimetype='application/xml')

Corresponding FlexML Python Syntax

else:
    secret = int(request.args.get('secret', '0'))
    guesses = int(request.args.get('guesses', '0')) - 1
    action_url = escape(url_for('play_game',
                            **{'secret': str(secret),
                              'guesses': str(guesses)}))
    data = request.get_json()
    input_num = data.get('Digits', '0')
    try:
        input_num = int(input_num)
    except ValueError as e:
        print (e)
        return exit_sequence()
    if input_num == secret:
        return f'''
            <Response>
                <Say>Congratulations! {secret} is the right number!</Say>
                <Say>You have guessed it in {10 - guesses} guesses - your score is {guesses + 1}.</Say>
                <Pause length="2"/>
                <Hangup/>
            </Response>'''

Wrong Guesses

When the number is bigger or smaller than the secret number, the application checks if there are any attempts available.

We change this part of the play_game.py route the following way:

  1. First, we modify the answer variable. It will now contain FlexML syntax with the Say verb.

  2. We will do the same for the else condition which also contains the answer variable declaration.

  3. We remove the response.add() function which builds the response. In FlexML, we will use the answer variable as a part of our response.

  4. We replace the response.add() function in the if guesses > 0 condition with the newly introduced guesses_remain variable. We will use this variable as a part of our response. It contains the FlexML formatted string in which we replace Plivo XML GetDigits element with the corresponding CarrierX FlexML Gather verb (dropping the unsupported retries attribute), the Speak element with the Say verb. We also use the action_url from the previous step for the Gather verb.

  5. We replace the response.add() function in the next else condition with the guesses_remain variable as well. It contains the FlexML formatted string in which we replace Plivo XML Wait element with the corresponding CarrierX FlexML Pause verb, the Speak element with the Say verb.

  6. Finally, we return the FlexML formatted string instead of the Plivo XML Response(). In it, we use our resulting values for the answer and guesses_remain variables.

Plivo XML Python Code

else:
    if input_num > secret:
        answer = "Sorry, you guessed %d. The secret is lesser."
    else:
        answer = "Sorry, you guessed %d. The secret is greater."
    response.add(plivoxml.SpeakElement(answer+input_num))
    if guesses > 0:
        response.add(
            plivoxml.GetDigitsElement(action=absolute_action_url, method='POST',timeout='10',num_digits='4',retries='1').add(
                plivoxml.SpeakElement("You have {} guesses remaining! Guess again!".format(guesses))))
    else:
        response.add(plivoxml.WaitElement (None).set_length (1))
        response.add(plivoxml.SpeakElement('Sorry, you dont have any remaining guesses. The secret was {}' .format(secret)))
        response.add(plivoxml.HangupElement())
    return Response(response.to_string(), mimetype='application/xml')

Corresponding FlexML Python Syntax

else:
    if input_num > secret:
        answer = f'<Say>Sorry, you guessed {input_num}. The secret is lesser.</Say>'
    else:
        answer = f'<Say>Sorry, you guessed {input_num}. The secret is greater.</Say>'
    if guesses > 0:
        guesses_remain = f'''
            <Gather action="{action_url}" method="POST" timeout="10" numDigits="4">
                <Say>You have {guesses} guesses remaining! Guess again!</Say>
            </Gather>'''
    else:
        guesses_remain = f'''
            <Pause length="1"/>
            <Say>Sorry, you don't have any remaining guesses. The secret was {secret}</Say>
            <Hangup/>'''
    return f'''
        <Response>
            {answer}
            {guesses_remain}
        </Response>'''
@app.route('/play_game', methods=['POST'])
def play_game():
    if not request.args.get('guesses', None):
        secret = random.randint(1, 100)
        guesses = 10
        action_url = escape(url_for('play_game',
                                **{'secret': str(secret),
                                  'guesses': str(guesses)}))
        return f'''
            <Response>
                <Gather action="{action_url}" method="POST" timeout="10" numDigits="4">
                    <Say>I have thought of a secret number between one and one hundred. You have ten guesses to find it!</Say>
                    <Say>You can make your guess now.</Say>
                </Gather>
            </Response>'''
    else:
        secret = int(request.args.get('secret', '0'))
        guesses = int(request.args.get('guesses', '0')) - 1
        action_url = escape(url_for('play_game',
                                **{'secret': str(secret),
                                  'guesses': str(guesses)}))
        data = request.get_json()
        input_num = data.get('Digits', '0')
        try:
            input_num = int(input_num)
        except ValueError as e:
            print (e)
            return exit_sequence()
        if input_num == secret:
            return f'''
                <Response>
                    <Say>Congratulations! {secret} is the right number!</Say>
                    <Say>You have guessed it in {10 - guesses} guesses - your score is {guesses + 1}.</Say>
                    <Pause length="2"/>
                    <Hangup/>
                </Response>'''
        else:
            if input_num > secret:
                answer = f'<Say>Sorry, you guessed {input_num}. The secret is lesser.</Say>'
            else:
                answer = f'<Say>Sorry, you guessed {input_num}. The secret is greater.</Say>'
            if guesses > 0:
                guesses_remain = f'''
                    <Gather action="{action_url}" method="POST" timeout="10" numDigits="4">
                        <Say>You have {guesses} guesses remaining! Guess again!</Say>
                    </Gather>'''
            else:
                guesses_remain = f'''
                    <Pause length="1"/>
                    <Say>Sorry, you don't have any remaining guesses. The secret was {secret}</Say>
                    <Hangup/>'''
            return f'''
                <Response>
                    {answer}
                    {guesses_remain}
                </Response>'''

4. how_to_play Route

The how_to_play route plays back the instructions explaining how to play the guessing game.

We change this route the following way:

  1. The how_to_play route uses the ResponseElement() class to build the Plivo XML response to the call. In FlexML we do not need it, so we can remove this line.

  2. Next, we update the URL variable used to redirect the calling party after the application finishes to read the instructions: we replace abs_red_url in Plivo with red_url in CarrierX as FlexML allows using the relative path.

  3. We remove the response.add() function which builds the response. In FlexML, we will build an XML response instead.

  4. Finally, we return the FlexML formatted string instead of the Plivo XML Response(). In it, we replace Plivo XML Speak element with the corresponding CarrierX FlexML Say verb, the Wait element with the Pause verb.

Plivo XML Python Code

@app.route('/how_to_play', methods=['POST'])
def how_to_play():
    response = plivoxml.ResponseElement()
    response.add(plivoxml.SpeakElement('I will think of a secret number that you will have to guess.'))
    response.add(plivoxml.SpeakElement('The number will be between one and one hundred.'))
    response.add(plivoxml.SpeakElement('To make your guess, just dial the digits and end with the hash sign.'))
    response.add(plivoxml.SpeakElement('For each wrong guess, I will tell you if you guessed lesser of greater.'))
    response.add(plivoxml.SpeakElement('You will have a maximum of ten chances to guess the number.'))
    response.add(plivoxml.WaitElement(None).set_length(6))
    response.add(plivoxml.SpeakElement('You will now be transferred to the main menu.'))
    abs_red_url = url_for('index', _external=True)
    response.add(plivoxml.RedirectElement(abs_red_url))
    return Response(response.to_string(), mimetype='application/xml')

Corresponding FlexML Python Syntax

@app.route('/how_to_play', methods=['POST'])
def how_to_play():
    red_url = url_for('index')
    return f'''
        <Response>
            <Say>I will think of a secret number that you will have to guess.</Say>
            <Say>The number will be between one and one hundred.</Say>
            <Say>To make your guess, just dial the digits and end with the hash sign.</Say>
            <Say>For each wrong guess, I will tell you if you guessed lesser of greater.</Say>
            <Say>You will have a maximum of ten chances to guess the number.</Say>
            <Pause length="6"/>
            <Say>You will now be transferred to the main menu.</Say>
            <Redirect>{red_url}</Redirect>
        </Response>'''

5. exit_sequence() Function

The application uses the exit_sequence() function to reject the call if the calling party enters anything but a digit. The main_menu_response and play_game routes use this function.

We change this function the following way:

  1. The exit_sequence() function uses the ResponseElement() class to build the Plivo XML response to the call. In FlexML we do not need it, so we can remove this line. We also remove the response.add() function which builds the response. In FlexML, we will build an XML response instead.

  2. We return the FlexML formatted string instead of the Plivo XML Response(). In it, we replace Plivo XML Speak element with the corresponding CarrierX FlexML Say verb, the Hangup element with the Reject verb (dropping the unsupported schedule attribute).

Plivo XML Python Code

def exit_sequence(msg="Oops! There was an error!"):
  response = plivoxml.ResponseElement()
  response.add(plivoxml.HangupElement(schedule=10, reason='rejected'))
  response.add(
  plivoxml.SpeakElement('We did not receive a valid response. We will hangup now.'))
  return Response(response.to_string(), mimetype='application/xml')

Corresponding FlexML Python Syntax

def exit_sequence(msg="Oops! There was an error!"):
  return '''
      <Response>
          <Say>We did not receive a valid response. We will hangup now.</Say>
          <Reject reason="rejected"/>
      </Response>'''

Now that we modified all the routes, we can safely remove the Plivo library import declaration from the beginning of the guess_game.py file:

from plivo import plivoxml
from flask import Flask, url_for, request
import random
import os
from xml.sax.saxutils import escape

app = Flask(__name__)

@app.route('/', methods=['POST', 'GET'])
def index():
    return '''
        <Response>
            <Gather action="/main_menu_response" method="POST" timeout="4" numDigits="4">
                <Say>Hello, welcome to CarrierX guessing game app!</Say>
                <Say>To play the game Press 1</Say>
                <Say>To learn how to play Press 2</Say>
                <Say>You can end this call at any time.</Say>
            </Gather>
        </Response>'''

def exit_sequence(msg="Oops! There was an error!"):
    return '''
        <Response>
            <Say>We did not receive a valid response. We will hangup now.</Say>
            <Reject reason="rejected"/>
        </Response>'''

@app.route('/main_menu_response', methods=['POST',])
def mm_response():
    post_args = request.get_json()
    print (post_args)
    print (request.data)
    input_digit = post_args.get('Digits', None)
    if input_digit != "1" and input_digit != "2":
        return exit_sequence()
    else:
        if input_digit == "1":
            action_url = url_for('play_game')
        else:
            action_url = url_for('how_to_play')
        return f'''
            <Response>
                <Redirect>{action_url}</Redirect>
            </Response>'''

@app.route('/play_game', methods=['POST'])
def play_game():
    if not request.args.get('guesses', None):
        secret = random.randint(1, 100)
        guesses = 10
        action_url = escape(url_for('play_game',
                                **{'secret': str(secret),
                                  'guesses': str(guesses)}))
        return f'''
            <Response>
                <Gather action="{action_url}" method="POST" timeout="10" numDigits="4">
                    <Say>I have thought of a secret number between one and one hundred. You have ten guesses to find it!</Say>
                    <Say>You can make your guess now.</Say>
                </Gather>
            </Response>'''
    else:
        secret = int(request.args.get('secret', '0'))
        guesses = int(request.args.get('guesses', '0')) - 1
        action_url = escape(url_for('play_game',
                                **{'secret': str(secret),
                                  'guesses': str(guesses)}))
        data = request.get_json()
        input_num = data.get('Digits', '0')
        try:
            input_num = int(input_num)
        except ValueError as e:
            print (e)
            return exit_sequence()
        if input_num == secret:
            return f'''
                <Response>
                    <Say>Congratulations! {secret} is the right number!</Say>
                    <Say>You have guessed it in {10 - guesses} guesses - your score is {guesses + 1}.</Say>
                    <Pause length="2"/>
                    <Hangup/>
                </Response>'''
        else:
            if input_num > secret:
                answer = f'<Say>Sorry, you guessed {input_num}. The secret is lesser.</Say>'
            else:
                answer = f'<Say>Sorry, you guessed {input_num}. The secret is greater.</Say>'
            if guesses > 0:
                guesses_remain = f'''
                    <Gather action="{action_url}" method="POST" timeout="10" numDigits="4">
                        <Say>You have {guesses} guesses remaining! Guess again!</Say>
                    </Gather>'''
            else:
                guesses_remain = f'''
                    <Pause length="1"/>
                    <Say>Sorry, you don't have any remaining guesses. The secret was {secret}</Say>
                    <Hangup/>'''
            return f'''
                <Response>
                    {answer}
                    {guesses_remain}
                </Response>'''

@app.route('/how_to_play', methods=['POST'])
def how_to_play():
    red_url = url_for('index')
    print(red_url)
    return f'''
        <Response>
            <Say>I will think of a secret number that you will have to guess.</Say>
            <Say>The number will be between one and one hundred.</Say>
            <Say>To make your guess, just dial the digits and end with the hash sign.</Say>
            <Say>For each wrong guess, I will tell you if you guessed lesser of greater.</Say>
            <Say>You will have a maximum of ten chances to guess the number.</Say>
            <Pause length="6"/>
            <Say>You will now be transferred to the main menu.</Say>
            <Redirect>{red_url}</Redirect>
        </Response>'''

if __name__ == "__main__":
    port = int(os.environ.get('PORT', 5000))
    app.debug = True
    app.run(host='0.0.0.0', port=port)

You can also remove the importing of Plivo modules from the requirements.txt file.

Finishing Migration

Now let’s test our application! You can run your local Flask server through terminal using the following command that will serve the created application to the incoming requests.

FLASK_APP=guess_game.py flask run

Now your application is running, but it is not visible to the outside world. We need to expose the localhost route publicly over the Internet so that your endpoint could access and load the FlexML instructions. To do this, we can use the free ngrok tool. If you choose to use ngrok, follow the instructions on their website to download it. Alternatively, you may expose the URL any other way you choose.

Once your Flask server is running, your terminal will list the port it is running on. Expose this port by running the following command through terminal. Make sure to replace 5000 (which is the Flask default port) with the port that your Flask server is running on.

./ngrok http 5000

When ngrok is run successfully, it will open a new terminal tab that will show you the http and https URLs that make the application publicly available over the Internet.

ngrok Terminal Window

Add the https ngrok link (in our sample it is https://d4d66ed41d49.ngrok.io/, as we chose / to be our default route) to either a FlexML endpoint or a DID associated with a FlexML endpoint. Refer to the FlexML Endpoint Quick Start Guide to learn how.

After that, call the associated DID to check how the application works.

Further Reading

You have successfully migrated the Guessing Game application from Plivo XML to FlexML!

Refer to the following pages to learn more about FlexML verbs and how to use them, and about ways to set up a FlexML endpoint:

Use our Migrating from Plivo XML to CarrierX Quick Start to learn more about other difficulties you can meet while migrating from Plivo XML to CarrierX and the ways to solve these issues.

Read other instructions on real-case migrations from Plivo XML to CarrierX here:

Refer to our other quick starts for instructions on how to work with CarrierX: