Dialogflow is a chatbot building framework that helps you build and deploy your own chatbots to multiple platforms like Google Assistant, Facebook Messenger, Telegram, Twitter, Slack, Line, Viber and many others. It is powered by a Machine Learning based NLU (Natural Language Understanding). The feature rich Dialogflow lets you create chatbot with ease.
In this blog, we are going to take a look into one pretty useful feature of Dialogflow: Fulfillment. Using Fulfillment you can communicate with your server/database to generate dynamic responses. In Fulfillment, we develop a webhook on our server which accepts request from Dialogflow, process the request and respond with Dialogflow compatible JSON.
Let’s dig deeper into webhook to understand how it works and what are the benefits a chatbot developer can leverage using it.
A webhook is a user defined HTTP callback that is automatically invoked whenever certain criteria is fulfilled. A webhook can be created in any server side programming language like Python, PHP or Node.js. You can read more about webhook here.
In Dialogflow, a webhook can be used to fetch data from your server whenever a certain intent having webhook enabled (don’t worry, we will see that) is invoked. The information from the intent is passed to the webhook service to receive result.
We are going to use Ngrok, (a web tunneling tool) that can be used to call webhook from your local server. You can download Ngrok from ngrok.io.
Let’s start building our own Dialogflow agent with webhook using Python (Flask) and deploy it using Ngrok.
Download and install python from here as per your supported OS.
Flask is a lightweight web framework that can be used to create webhook service which can communicate with external application (in our case Dialogflow’s agent). To use flask in our app, first we need to install it. It can be easily installed using “pip”. If “pip” is not installed then you will need to install it first.
pip install Flask
For more information you can visit: https://pypi.org/project/Flask
Let’s create a basic flask app.
# import flask dependencies
from flask import Flask
# initialize the flask app
app = Flask(__name__)
# default route
@app.route('/')
def index():
return 'Hello World!'
# create a route for webhook
@app.route('/webhook')
def webhook():
return 'Hello World!'
# run the app
if __name__ == '__main__':
app.run()
Now, run the app using the following command.
python app.py or FLASK_APP=hello.py flask run
Our basic app is working now, but as it is on local system it can not be accessed from outside world. To integrate it as webhook for Dialogflow, we need to make it live/online. For that, we use ngrok.
Ngrok is a web tunnelling tool that provides a way to test APIs and webhooks from local server. There are two versions available. I am using the free version with registration on ngrok.io.
To run ngrok, use the following command: ngrok http <port_number>
e.g. ngrok http 5000 (for flask app)
Dialogflow provides an option on the left sidebar known as Fulfillment. Just enter your webhook url generated by ngrok and your are done.
Be sure to add the URL with /webhook like https://836b69cc.ngrok.io/webhook and not just https://836b69cc.ngrok.io/ as we are going to handle the request on /webhook route not the index route.
If you provide URL without webhook then you may get error like “Webhook call failed. Error: 405 Method Not Allowed.”.
Now, we need to enable the webhook for intents that needs to communicate with our server data. To do that, open the intent for which you want to enable webhook, scroll down to the end and enable the “Enable webhook call for this intent” switch.
Whenever this intent is invoked, it will send request to your webhook and respond as per the response set from the webhook.
Training phrases are the sentences that helps the agent to identify/invoke the particular intent.
We have to set up “Actions and Parameters” in the intent which can be used on our webhook to process the request.
In our example, we have setup “get_results” as “Action”. So whenever this intent will call our webhook, in POST request, we’ll get “get_results” as “action”. If there are multiple intents invoking webhook then we can use “action” to differentiate them and based upon that generate different responses.
We can also pass “Parameters” to our webhook. To do that, we can define Parameter name and its value.
Both Actions and Parameters are optional values so webhook call will work even if you don’t pass them. To differentiate intents without action, you can simply check the intent name in the request JSON.
Webhook responses should be in a proper JSON format so that Dialogflow can understand what to display to the user.
You can refer various JSON formats for webhook on this github page.
We are going to build some responses in python using this formats. Types of responses available are: Simple Response, Basic Card, Suggestions, List Card, Browse Carousel, Carousel Response. We will be building all these responses in the upcoming blog.
Below code will prepare a simple JSON response with fulfillment text for Dialogflow. The user will receive “This is a response from webhook.” as response for this intent.
# import flask dependencies
from flask import Flask, request, make_response, jsonify
# initialize the flask app
app = Flask(__name__)
# default route
@app.route('/')
def index():
return 'Hello World!'
# function for responses
def results():
# build a request object
req = request.get_json(force=True)
# fetch action from json
action = req.get('queryResult').get('action')
# return a fulfillment response
return {'fulfillmentText': 'This is a response from webhook.'}
# create a route for webhook
@app.route('/webhook', methods=['GET', 'POST'])
def webhook():
# return response
return make_response(jsonify(results()))
# run the app
if __name__ == '__main__':
app.run()
You can see that we have fetched “action” from the request using
action = req.get('queryResult').get('action')
In our example, we haven’t used action, but if you want you can use it for your purpose.
Using console on right side of the window, we can invoke the intent and check the response. For our example, the response will be as below:
Clicking on “DIAGNOSTIC INFO” button, we can view the all detail about request being sent from Dialogflow and response being sent from our webhook. “DIAGNOSTIC INFO” can also be very useful for debugging, if you have any error in your webhook.
Using this tutorial you can create a basic webhook. If you want to enhance your webhook or make it more powerful then Python Library we created to simplify building the JSON responses for Dialogflow can be pretty useful.
Hope this tutorial will be helpful to you in integrating webhook in your dialogflow app. If you have any question then post it in the comment. We’ll respond to it soon.
(Update: 27th December, 2019)
If you receive “Webhook call failed. Error: PERMISSION_DENIED.” then please check that the URL you are providing as webhook must start with HTTPS not HTTP. It will fix your “Webhook call failed. Error: PERMISSION_DENIED.” error.
So, it should be like https://836b69cc.ngrok.io/webhook not http://836b69cc.ngrok.io/webhook.
Feel free to comment your doubts/questions. We would be glad to help you.
If you are looking for Chatbot Development or Natural Language Processing services then do contact us or send your requirement at letstalk@pragnakalp.com. We would be happy to offer our expert services.
Fill up the form below to download your PythonDemo agent and Webhook for Dialogflow.
Our experts in Generative AI, Python Programming, and Chatbot Development can help you build innovative solutions and scale your business faster.
41 Comments
Pragnakalp Techlabs
March 25, 2019How can we make our bot memorise information which was talked over in past. So that next time when user comes online, my bot says about things which were talked last time.
For example, How was your experience last time.
How was your shopping yesterday.
Something like that ?
Do we need to build our DB for that ? And bot can look up DB what was talked over last time ?
Pragnakalp Techlabs
April 1, 2019Hello Rishabh,
To save user conversation you can create DB on your server and save it using Webhook. When the next time user comes in, you can ask for identification and based upon that user’s past conversation can be fetched again using webhook.
There is also an option of UserStorage if you want to store small amount of data. Check https://developers.google.com/actions/assistant/save-data for more information.
Thanks
Pragnakalp Techlabs
April 16, 2019I am performing everything as said in the tutorial, but I am receiving Error 405 Method not allowed.
Pragnakalp Techlabs
April 17, 2019Hello Mohammed,
Can you please confirm that for /webhook path you are declaring methods properly like below?
@app.route(‘/webhook’, methods=[‘GET’, ‘POST’])
If this doesn’t fix your issue then share your webhook file code so that we can help you further.
Pragnakalp Techlabs
April 27, 2019Hi, I’ve done everything as said in the tutorial, but I am receiving this message POST /webhook 404 NOT FOUND.
Pragnakalp Techlabs
April 29, 2019Hello Sofia,
The 404 NOT found could be happening for multiple reasons. Can you share your code of app.py file please? We’ll be able to assist better after checking code.
Pragnakalp Techlabs
May 6, 2019HI, I am performing everything as said in the tutorial, but I am receiving Error 500 Method not allowed.
Pragnakalp Techlabs
May 9, 2019Hello Lee,
Can you please confirm
@app.route(‘/webhook’, methods=[‘GET’, ‘POST’])
line is properly written in your code?
If it is proper, then do share your code so that we can help you further.
Pragnakalp Techlabs
May 6, 2019Hi I want to set a variable and also go to a specific intent. How can I do this?
Pragnakalp Techlabs
May 9, 2019Hello Jeffer,
To go to specific intent you will need to use output context and input context.
“I want to set a variable” – This is not fully clear to us. Can you please provide more detail about what you are trying to do so that we can guide you further?
Pragnakalp Techlabs
June 3, 2019from flask import Flask, request, make_response, jsonify
app = Flask(__name__)
@app.route(‘/’)
def hello_world():
return ‘Hello World!’
#functions for responses
def results():
req = request.get_json(force=True)
action = req.get(‘queryResult’).get(‘action’)
return {‘fulfillmentText’: ‘This is a response from webhook.’}
#create a route for webhooks
@app.route(‘/webhook’, methods=[‘GET’, ‘POST’])
def webhook():
return make_response(jsonify(results()))
if __name__ == ‘__main__’:
app.run()
Here is my code.
But there’s error.
“webhookStatus”: {
“code”: 9,
“message”: “Webhook call failed. Error: 405 Method Not Allowed.”
}
Pragnakalp Techlabs
June 8, 2019Hello PP,
Looks like you are missing /webhook in the fulfillment URL you are providing in Dialogflow.
Can you confirm you are providing fulfillment URL like https://836b69cc.ngrok.io/webhook and not just https://836b69cc.ngrok.io/ ?
Pragnakalp Techlabs
June 10, 2019How I can create FulfillmentMessage response with json of Line
Pragnakalp Techlabs
June 10, 2019Hello Chanon,
Follow our blog and use the code given at the end of the code. It will create a JSON for you with a fulfillment message ‘This is a response from webhook.’
Pragnakalp Techlabs
June 14, 2019How can I send image files as a response.(looking to integrate dialogflow and twilio for whatsapp)
Pragnakalp Techlabs
June 17, 2019Hello Akshaya,
You can try Basic Card response using custom payload. Sample JSON for basic card could be as below:
{
“payload”: {
“google”: {
“expectUserResponse”: true,
“richResponse”: {
“items”: [
{
“simpleResponse”: {
“textToSpeech”: “This is a Basic Card:”
}
},
{
“basicCard”: {
“title”: “Card Title”,
“image”: {
“url”: “https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png”,
“accessibilityText”: “Google Logo”
},
“buttons”: [
{
“title”: “Button Title”,
“openUrlAction”: {
“url”: “https://www.google.com”
}
}
],
“imageDisplayOptions”: “WHITE”
}
}
]
}
}
}
}
Pragnakalp Techlabs
June 21, 2019400 BAD REQUEST
im geting this response. does this mean there is a problem with chatfuel? im not using dialog flow, but chatfuel.
Pragnakalp Techlabs
June 30, 2019502 bad gateway error
Pragnakalp Techlabs
November 26, 2019Could you solve this? im having the same
Pragnakalp Techlabs
November 26, 2019Hello mengy,
The error 502 could be happening for multiple reasons. Can you share your code of webhook file please? We’ll be able to assist better after checking code.
Pragnakalp Techlabs
April 30, 2020Can I add a web crawler or spider to my we hook so that I can use my agent for submission or update of information on a website… Let’s say using selenium
Pragnakalp Techlabs
May 5, 2020Hello Abdul,
While I am not fully clear why do you need to user a web crawler or spider in your webhook, technically it is feasible, you can add such thing in the webhook.
But there is one limitation from Dialogflow that you must send response from webhook within 5 seconds or it will generate error. So, make sure your webhook responds within stipulated time or your chatbot will end conversation abruptly.
Pragnakalp Techlabs
July 3, 2019I tried like you said in the tutorial but I just got this error:
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
127.0.0.1 – – [02/Jul/2019 22:27:12] “POST /webhook HTTP/1.1” 405 –
127.0.0.1 – – [02/Jul/2019 22:27:27] “POST /webhook HTTP/1.1” 405 –
HTTP Requests
————-
POST /webhook 405 METHOD NOT ALLOWED
POST /webhook 405 METHOD NOT ALLOWED
This is my code:
from flask import Flask
app = Flask(__name__)
@app.route(‘/’)
def index():
return ‘Hello World!’
def resultado():
req=request.get_json(force=True)
action = req.get(‘queryResult’).get(‘action’)
return{‘fulfillmentText’: ‘Esta es una respuesta a partir de un webhook’}
@app.route(‘/webhook’, methods=[‘GET’, ‘POST’])
def webhook():
return make_response(jsonify(results()))
if __name__ == ‘__main__’:
app.run()
Pragnakalp Techlabs
August 16, 2019Hello,
I have followed your post and its working perfectly. Thank you so much for this.
However when I’m trying to pass a value, For Ex- if I’m adding two numbers and passing the ‘sum’ value in the fulfillment text, it is not displaying that. Please help
Pragnakalp Techlabs
August 19, 2019Hello Ganesh,
Can you share your code here? So that we can help you further.
Thanks
Pragnakalp Techlabs
October 13, 2019The error I am getting is “Webhook call failed. Error: 500 Internal Server Error.”. in the dialogflow fulfilment response.
The following is my code:
import os
from flask import Flask
from flask import request
app = Flask(__name__)
@app.route(‘/webhook’, methods=[‘POST’,’GET’])
def hello():
os.system(“ls”)
os.system(“echo ‘hello world'”)
return os
if __name__ == ‘__main__’:
port = int(os.getenv(‘PORT’, 5000))
print(“Starting app on port %d” % port)
app.run(debug=True, port=port, host=’0.0.0.0′)
Pragnakalp Techlabs
October 16, 2019Hello mankaran singh,
In your code, you are trying to return os module. but the return type must be a string, dictionary, tuple, Response instance, or WSGI callable.
Pragnakalp Techlabs
October 15, 2019Hi, is it possible to have multiple users using our agent at the same time?
If so, how to make the agent know which user is sending requests?
Pragnakalp Techlabs
October 17, 2019Hello Cong,
Yes, it is possible to have multiple users using our agent at the same time. Dialogflow will take care of it automatically.
And if you have integrated webhook, then you can separate user wise request using the session ID passed in the request JSON.
Hope that answers your question. Feel free to ask further if there is confusion.
Pragnakalp Techlabs
March 10, 2020How to parse parameter and detect action in flask to response user based on different question
Pragnakalp Techlabs
March 11, 2020Hello Abhishek,
You will receive the “parameters” and “action” values in the JSON. As you can see in our example, you can parse the action value like this:
# build a request object
req = request.get_json(force=True)
# fetch action from json
action = req.get(‘queryResult’).get(‘action’)
Same way, you will be able to find the parameters value too. If this doesn’t work then please paste the code you have written. It will help us find the issue and assist you better.
Pragnakalp Techlabs
October 23, 2019I was able to run the terminal command but it is not showing the text response.
But when I remove ” process = subprocess.call([‘ls’],shell=True)” then it shows the fulfillment response in dialogflow.
I want them both at the same time
@app.route(‘/webhook’, methods=[‘POST’,’GET’])
def hello():
#process = subprocess.call([‘ls’],shell=True)
#time.sleep(10.0)
fulfillmentText = {‘fulfillmentText’: ‘Response from webhook.’}
process = subprocess.call([‘ls’],shell=True)
time.sleep(10.0)
return fulfillmentText, process
Pragnakalp Techlabs
December 17, 2019Hi, how can implement suggestion chips for a given intent on the webhook? How about action cards with links and images? (using flask)
Pragnakalp Techlabs
March 11, 2020Hello Lemon,
For that you can use our response library: https://github.com/pragnakalp/dialogflow-webhook-response-libary-in-python . In this library, there is support of suggestion chips, links, images, etc. You will just need to pass the values properly and it will prepare required JSON.
Pragnakalp Techlabs
April 16, 2020hi i want to change the intent based on webhook like i want to make a followup code can u please give a short example
Pragnakalp Techlabs
April 22, 2020Hello Nisam,
We can set the context using webhook. So, if you want to change the flow of conversation using webhook, then we would suggest you to try setting context from webhook.
Let us know whether that solves your problem or not.
Pragnakalp Techlabs
April 25, 2020Can you please tell how to implement dialog flow with webhook with mysql database when multiple intents are present in an agent …some sample code example will definitely help me .
Pragnakalp Techlabs
May 5, 2020Hello Pooja,
In your webhook you can connect MySQL or any other database you want. But I am not pretty clear what do you mean by “when multiple intents are present in an agent”.
If you want to perform different action from webhook for different intent then you can give value of “Action” in the intent and use that in your webhook to differentiate the response for different intent.
Pragnakalp Techlabs
April 27, 2020This is great!
If you want even quicker access to tunnels alongside like direct access to the ngrok API and all its features through Python, check out pyngrok: https://pyngrok.readthedocs.io/en/latest/ Full disclosure, I am its developer. Hope others also find it useful!
Pragnakalp Techlabs
June 13, 2020I want to capture certain parameters which I believe will be done by :
req.get(‘queryResult’).get(‘parameters’)
I want to modify certain parameters and send the response containing the modified parameters. How do I return this in fulfillment text?
And from the request body, there are multiple parameter fields. Which one am I supposed to access? I
P.S I would also be grateful if I can be provided a link fo detailed tutorial and docs for this purpose and such like
Pragnakalp Techlabs
July 29, 2020really nice!
i taken Ngrok port 80…server is getting the data to Dailogeflow..but not displaying in Reponse..but in Diagnostic info in Fulfillment status showing success..ple help me this…