Response JSON format

When we issue the call to the REST API, we get a response in JSON format. You can actually figure out what the response will be by triggering the same intent within your Dialogflow console and clicking on the Diagnostic Info button in the simulator.

Here is the JSON for the intent:

  "responseId": "78901418-a397-42ae-b259-63de174c2770",
  "queryResult": {
    "queryText": "start over",
    "parameters": {},
    "allRequiredParamsPresent": true,
    "fulfillmentMessages": [
        "platform": "ACTIONS_ON_GOOGLE",
        "simpleResponses": {
          "simpleResponses": [
              "textToSpeech": "**Hello there!**\n\nDo you have any questions about my [courses]("
        "platform": "ACTIONS_ON_GOOGLE",
        "suggestions": {
          "suggestions": [
              "title": "Yes"
              "title": "No"
    "outputContexts": [
        "name": "projects/testagent-ed43c/agent/sessions/ab06d0e0-6cd7-115c-6a59-21d2f0071f3e/contexts/awaiting_has_questions",
        "lifespanCount": 1
    "intent": {
      "name": "projects/testagent-ed43c/agent/intents/694e1b85-a34d-440f-80b7-de4b9d5c1ebe",
      "displayName": "event.WEBCHAT_WELCOME"
    "intentDetectionConfidence": 1,
    "languageCode": "en"

There is quite a lot of information coming back in this JSON, as you can see. From this response, we are mainly interested in parsing out everything inside queryResult -> fulfillmentMessages object and rendering it on to the message window.

That’s what we do in this PHP code block below:

$dec = json_decode($contents);
$defaultResponse = '';
$hasDefaultResponse = false;
if (isset($dec->queryResult->fulfillmentText)) {
    $hasDefaultResponse = true;
    $defaultResponse = $dec->queryResult->fulfillmentText;
$isEndOfConversation = 0;
if (isset($dec->queryResult->diagnosticInfo->end_conversation)) {
    $isEndOfConversation = 1;
$messages = $dec->queryResult->fulfillmentMessages;
$action = $dec->queryResult->action;
$intentid = $dec->queryResult->intent->name;
$intentname = $dec->queryResult->intent->displayName;
$speech = '';
for ($idx = 0; $idx < count($messages); $idx++) {
    $obj = $messages[$idx];
    if ($obj->platform == 'ACTIONS_ON_GOOGLE') {
        $simpleResponses = $obj->simpleResponses;
        $speech = $simpleResponses->simpleResponses[0]->textToSpeech;
if ($hasDefaultResponse) {
    $response->defaultResponse = $defaultResponse;
$response->speech = $speech;
$response->messages = $messages;
$response->isEndOfConversation = $isEndOfConversation;
echo json_encode($response);

There is a lot going on here, because I also try and do the following:

  • there is a feature in Dialogflow that lets you mark a specific intent as end of conversation. I use this feature to mark the end of conversation, so I need to parse that information from the JSON response
  • when you use the Default response block in Dialogflow, the text in that response block goes into a different object (queryResult -> fulfillmentText) and needs to be parsed out separately

Once I parse everything into appropriate PHP variables, I then create a $response variable and do an echo json_encode($response) [the last line in the code] which sends this JSON object to my chatbot.js file for further processing.

Rendering the user controls inside the Message Window

Once the chatbot.js receives the JSON object as a response to its AJAX call (remember that?), it will parse all the information coming back and render the user interface objects appropriately.

As you can see in the code below, in lines 105 – 107, we get the response to the original POST request as JSON objects – responseObj.speech, responseObj.messages and responseObj.isEndOfConversation – which is echo’ed from the process.php file.

Once the JS file gets this information, it gets to work rendering everything inside the Message Window in the appropriate user control.

For example, here is how it renders the buttons (referred to as Suggestion Chips in Google Assistant):

function renderSuggestionChips(data,parent){
    data = data['suggestions'];
    var i, len = data['suggestions'].length;
    var buttonRowDiv = jQuery('<div/>',{
    var suggestionChipRowDiv = jQuery('<div/>',{
    for (i = 0; i < len; i++) {
        if (data["suggestions"][i]) {
            //make a button for it
            var buttonText = data["suggestions"][i]['title'];
            var button = jQuery('<button/>',{
                class:'btn btn-primary btn-sm gaSuggestionChipButton',
                var textToSubmit = this.textContent;
                window.currentSuggestionChips = null;
                $( "form" ).trigger( "submit" );
    window.currentSuggestionChips = suggestionChipRowDiv;
    //also disable the manual input

In the above code, there is actually quite a lot of stuff going on:

First, we get the length of the data[‘suggestions’] array since it tells us how many buttons there are.

Then we prepare the outer container divs.

Then we loop through the suggestions array and render the buttons.

Importantly, to imitate the behavior you see in the Google Assistant, we add code logic which makes the suggestion chips row disappear from the Message Window as soon as one of the buttons is clicked (this is done so that the user doesn’t get confused and go back and click on old buttons in the chat sequence).

In our case, we don’t want the user to type out a response when presented with button choices. So at the end of the function, I also disable the input box to prevent user typing into it.

The important takeaway from this code snippet is the following: you will do quite a bit of heavy lifting in your JS code to render the user controls in an appropriate fashion.

Wait, why not framework X?

Someone will look at all this and say “Aravind! What is the matter with you? Why don’t you use Angular/React/VueJS to take care of all this?”

In my view, these frameworks make very poor teaching tools. If you don’t believe me, go and look at every tutorial ever on Medium which uses these frameworks. The top comment usually goes like this (paraphrasing): “Now that the framework has been updated, you should use this feature X to do task A instead because of this Y reason since feature Z was deprecated in version 2019.February and now it is already version 2019.May”

Or worse: “It doesn’t work. <Error Message followed by an enormous code dump>. Can you please help me fix this ASAP?”

So you are quite welcome to translate it to the framework of your choice. The reason I have used vanilla JS (well, JQuery really) is because it is a good common denominator which anyone can modify for their purpose.