Conversation Exits

Try it in Dialogflow

Click Continue to deploy and test our Conversation exits sample in Dialogflow. Just follow the steps below.

  1. Enter an agent name to create a new Dialogflow agent or select an existing agent to overwrite it with this sample.
  2. Click Create agent from template.
  3. After the agent is done importing, click Go to agent.
  4. Click Fulfillment in the left navigation menu.
  5. Enable the Inline Editor and click Deploy. The editor contains the code from this page.
  6. Click Integrations in the left navigation menu.
  7. Click Google Assistant and enable Auto-preview changes.
  8. Click Test to open the Actions simulator and enter "Talk to my test app" to test the sample!
Continue

Whenever users want to exit mid-conversation, they can say things like:

  • "exit"
  • "cancel"
  • "stop"
  • "nevermind"
  • "goodbye"

By default, the Assistant exits your conversation and plays an earcon. To override this behavior, you can opt-in to receiving one last request that you can use to cleanup your fulfillment logic and respond to the user one last time. Your final response must be a single simple response with a 60-character limit on its textToSpeech and displayText values.

The maximum execution time allowed for conversation exit requests is 2 seconds; if no response is received, the conversation will end with the default exit earcon. If your custom cancel response fails (due to exceeding character limit, improper response type, etc) the conversation will also end with the default exit earcon.

Dialogflow

To receive conversation exit events:

  1. In the left navigation, click the + icon by the Intents menu item.
  2. Give the intent a name (like Exit Conversation) and click on the Events section.
  3. Enter actions_intent_CANCEL in the Add event field.
  4. Either specify a response under Response or set up your own response for this intent in your webhook code.
  5. Enable Set this intent as end of conversation.
  6. Click Save.
  7. In the left navigation, click Integrations.
  8. Choose Google Assistant and click Update draft, then Test to make sure the changes are reflected in your project.

When a user requests an conversation exit, the intent you created is triggered and your response is returned to the user. For example, here's some fulfillment code that uses the client library to handle a cancel intent and return a response.

Node.js
const {dialogflow} = require('actions-on-google');
const functions = require('firebase-functions');

const app = dialogflow({debug: true});

app.intent('Exit Conversation', (conv) => {
  conv.close(`Okay, talk to you next time!`);
});

exports.dialogflowFirebaseFulfillment = functions.https.onRequest(app);
Java
package com.example;

import com.google.actions.api.ActionRequest;
import com.google.actions.api.ActionResponse;
import com.google.actions.api.DialogflowApp;
import com.google.actions.api.ForIntent;
import com.google.actions.api.response.ResponseBuilder;

public class MyActionsApp extends DialogflowApp {

  @ForIntent("Exit Conversation")
  public ActionResponse exit(ActionRequest request) {
    ResponseBuilder responseBuilder = getResponseBuilder(request);
    responseBuilder.add("Okay, talk to you next time!");
    responseBuilder.endConversation();
    return responseBuilder.build();
  }
}
Request JSON

Note that the JSON below describes a webhook request.

{
  "responseId": "e0c9d47f-aa99-4d03-bd48-a64c79e9c2a2-712767ed",
  "queryResult": {
    "queryText": "actions_intent_CANCEL",
    "parameters": {},
    "allRequiredParamsPresent": true,
    "fulfillmentText": "Webhook failed for intent: Exit Conversation",
    "fulfillmentMessages": [
      {
        "text": {
          "text": [
            "Webhook failed for intent: Exit Conversation"
          ]
        }
      }
    ],
    "outputContexts": [
      {
        "name": "projects/df-exits-kohler/agent/sessions/ABwppHGVnlRtMz1RhGGmDOSlCFAbc2QaM12xYtw0raWvv-R5GYM6tkqiVOpIOexm_LTBCvmARwpbZ_onRuX0/contexts/actions_capability_audio_output"
      },
      {
        "name": "projects/df-exits-kohler/agent/sessions/ABwppHGVnlRtMz1RhGGmDOSlCFAbc2QaM12xYtw0raWvv-R5GYM6tkqiVOpIOexm_LTBCvmARwpbZ_onRuX0/contexts/actions_capability_media_response_audio"
      },
      {
        "name": "projects/df-exits-kohler/agent/sessions/ABwppHGVnlRtMz1RhGGmDOSlCFAbc2QaM12xYtw0raWvv-R5GYM6tkqiVOpIOexm_LTBCvmARwpbZ_onRuX0/contexts/actions_capability_account_linking"
      },
      {
        "name": "projects/df-exits-kohler/agent/sessions/ABwppHGVnlRtMz1RhGGmDOSlCFAbc2QaM12xYtw0raWvv-R5GYM6tkqiVOpIOexm_LTBCvmARwpbZ_onRuX0/contexts/actions_capability_screen_output"
      },
      {
        "name": "projects/df-exits-kohler/agent/sessions/ABwppHGVnlRtMz1RhGGmDOSlCFAbc2QaM12xYtw0raWvv-R5GYM6tkqiVOpIOexm_LTBCvmARwpbZ_onRuX0/contexts/actions_capability_web_browser"
      },
      {
        "name": "projects/df-exits-kohler/agent/sessions/ABwppHGVnlRtMz1RhGGmDOSlCFAbc2QaM12xYtw0raWvv-R5GYM6tkqiVOpIOexm_LTBCvmARwpbZ_onRuX0/contexts/google_assistant_input_type_keyboard"
      },
      {
        "name": "projects/df-exits-kohler/agent/sessions/ABwppHGVnlRtMz1RhGGmDOSlCFAbc2QaM12xYtw0raWvv-R5GYM6tkqiVOpIOexm_LTBCvmARwpbZ_onRuX0/contexts/actions_intent_cancel"
      }
    ],
    "intent": {
      "name": "projects/df-exits-kohler/agent/intents/b506991f-ad7a-4c4b-8adc-5e6c62727381",
      "displayName": "Exit Conversation"
    },
    "intentDetectionConfidence": 1,
    "languageCode": "en"
  },
  "originalDetectIntentRequest": {
    "source": "google",
    "version": "2",
    "payload": {
      "user": {
        "locale": "en-US",
        "userVerificationStatus": "VERIFIED"
      },
      "conversation": {
        "conversationId": "ABwppHGVnlRtMz1RhGGmDOSlCFAbc2QaM12xYtw0raWvv-R5GYM6tkqiVOpIOexm_LTBCvmARwpbZ_onRuX0",
        "type": "ACTIVE",
        "conversationToken": "[]"
      },
      "inputs": [
        {
          "intent": "actions.intent.CANCEL",
          "rawInputs": [
            {
              "inputType": "KEYBOARD",
              "query": "cancel"
            }
          ]
        }
      ],
      "surface": {
        "capabilities": [
          {
            "name": "actions.capability.AUDIO_OUTPUT"
          },
          {
            "name": "actions.capability.MEDIA_RESPONSE_AUDIO"
          },
          {
            "name": "actions.capability.ACCOUNT_LINKING"
          },
          {
            "name": "actions.capability.SCREEN_OUTPUT"
          },
          {
            "name": "actions.capability.WEB_BROWSER"
          }
        ]
      },
      "availableSurfaces": [
        {
          "capabilities": [
            {
              "name": "actions.capability.SCREEN_OUTPUT"
            },
            {
              "name": "actions.capability.WEB_BROWSER"
            },
            {
              "name": "actions.capability.AUDIO_OUTPUT"
            }
          ]
        }
      ]
    }
  },
  "session": "projects/df-exits-kohler/agent/sessions/ABwppHGVnlRtMz1RhGGmDOSlCFAbc2QaM12xYtw0raWvv-R5GYM6tkqiVOpIOexm_LTBCvmARwpbZ_onRuX0"
}
Response JSON

Note that the JSON below describes a webhook response.

{
  "payload": {
    "google": {
      "expectUserResponse": false,
      "richResponse": {
        "items": [
          {
            "simpleResponse": {
              "textToSpeech": "Okay, talk to you next time!"
            }
          }
        ]
      }
    }
  }
}

Actions SDK

To receive conversation exit intents:

  1. In a `conversations` object inside your action package, declare that you want to receive the `actions.intent.CANCEL` intent whenever a user wants to exit mid-conversation.

    
    {
      "actions": [
        {
          "description": "Default Welcome Intent",
          "name": "MAIN",
          "fulfillment": {
            "conversationName": "conversation_1"
          },
          "intent": {
            "name": "actions.intent.MAIN"
          }
        }
      ],
      "conversations": {
        "conversation_1": {
          "name": "conversation_1",
          "url": "YOUR_ENDPOINT_URL",
          "inDialogIntents": [
            {
              "name": "actions.intent.CANCEL"
            }
          ]
        }
      }
    }
    
  2. When you receive a request with the `actions.intent.CANCEL` intent, clean up any fulfillment logic that you'd like and return an appropriate exit phrase to the user. For example, here's some fulfillment code that uses the client library to handle a cancel intent.

    Node.js
    const {actionssdk} = require('actions-on-google');
    const functions = require('firebase-functions');
    
    const app = actionssdk({debug: true});
    
    app.intent('actions.intent.MAIN', (conv) => {
      conv.ask(`Hi! Try saying 'exit' or 'cancel'`);
    });
    
    app.intent('actions.intent.TEXT', (conv, input) => {
      conv.ask(`You said ${input}`);
      conv.ask(`Try saying 'exit' or 'cancel'`);
    });
    
    app.intent('actions.intent.CANCEL', (conv) => {
      conv.close(`Okay, talk to you next time!`);
    });
    
    exports.dialogflowFirebaseFulfillment = functions.https.onRequest(app);
    Java
    package com.example;
    
    import com.google.actions.api.ActionRequest;
    import com.google.actions.api.ActionResponse;
    import com.google.actions.api.ActionsSdkApp;
    import com.google.actions.api.ConstantsKt;
    import com.google.actions.api.ForIntent;
    import com.google.actions.api.response.ResponseBuilder;
    import com.google.actions.api.response.helperintent.Confirmation;
    import com.google.actions.api.response.helperintent.DateTimePrompt;
    import com.google.actions.api.response.helperintent.Permission;
    import com.google.actions.api.response.helperintent.Place;
    import com.google.api.services.actions_fulfillment.v2.model.DateTime;
    import com.google.api.services.actions_fulfillment.v2.model.Location;
    
    public class MyActionsApp extends ActionsSdkApp {
    
      @ForIntent("actions.intent.MAIN")
      public ActionResponse welcome(ActionRequest request) {
        ResponseBuilder responseBuilder = getResponseBuilder(request);
        responseBuilder.add("Hi! Try saying 'exit' or 'cancel'");
        return responseBuilder.build();
      }
    
      @ForIntent("actions.intent.TEXT")
      public ActionResponse fallback(ActionRequest request) {
        ResponseBuilder responseBuilder = getResponseBuilder(request);
        responseBuilder.add("You said " + request.getRawInput().getQuery());
        responseBuilder.add("Try saying 'exit' or 'cancel'");
        return responseBuilder.build();
      }
    
      @ForIntent("actions.intent.CANCEL")
      public ActionResponse exit(ActionRequest request) {
        ResponseBuilder responseBuilder = getResponseBuilder(request);
        responseBuilder.add("Okay, talk to you next time!");
        responseBuilder.endConversation();
        return responseBuilder.build();
      }
    
    }
    Request JSON

    Note that the JSON below describes a webhook request.

    {
      "user": {
        "locale": "en-US",
        "userVerificationStatus": "VERIFIED"
      },
      "conversation": {
        "conversationId": "ABwppHEtH6XptryyLGWDBOVnLgy8EkOclTPETd4lcLh_f6ghQAzzg0seUuq3YocyFT8B17N6AGD6YPPjQw5CXBM",
        "type": "NEW"
      },
      "inputs": [
        {
          "intent": "actions.intent.CANCEL",
          "rawInputs": [
            {
              "inputType": "KEYBOARD",
              "query": "exit"
            }
          ]
        }
      ],
      "surface": {
        "capabilities": [
          {
            "name": "actions.capability.SCREEN_OUTPUT"
          },
          {
            "name": "actions.capability.ACCOUNT_LINKING"
          },
          {
            "name": "actions.capability.MEDIA_RESPONSE_AUDIO"
          },
          {
            "name": "actions.capability.AUDIO_OUTPUT"
          },
          {
            "name": "actions.capability.WEB_BROWSER"
          }
        ]
      },
      "availableSurfaces": [
        {
          "capabilities": [
            {
              "name": "actions.capability.AUDIO_OUTPUT"
            },
            {
              "name": "actions.capability.SCREEN_OUTPUT"
            },
            {
              "name": "actions.capability.WEB_BROWSER"
            }
          ]
        }
      ]
    }
    Response JSON

    Note that the JSON below describes a webhook response.

    {
      "expectUserResponse": false,
      "finalResponse": {
        "richResponse": {
          "items": [
            {
              "simpleResponse": {
                "textToSpeech": "Okay, talk to you next time!"
              }
            }
          ]
        }
      }
    }