<br />

> [!NOTE]
> **Try the snippets:** Import, deploy, and explore code snippets in context using a new
> Dialogflow agent.
> <button class="devsite-dialog-button" data-modal-dialog-id="one-click-modal-dialog">Explore in Dialogflow</button>

### Explore in Dialogflow

Click **Continue** to import our Conversation exits sample in Dialogflow. Then, follow the
steps below to deploy and test the sample:

1. Enter an agent name and create a new Dialogflow agent for the sample.
2. After the agent is done importing, click **Go to agent**.
3. From the main navigation menu, go to **Fulfillment**.
4. Enable the **Inline Editor** , then click **Deploy**. The editor contains the sample code.
5. From the main navigation menu, go to **Integrations** , then click **Google
   Assistant**.
6. In the modal window that appears, enable **Auto-preview changes** and click **Test** to open the Actions simulator.
7. In the simulator, enter `Talk to my test app` to test the sample!
[Continue](https://console.dialogflow.com/api-client/#/oneclick?templateUrl=https:%2F%2Fstorage.googleapis.com%2Fdialogflow-oneclick%2Fone_click_artifacts%2FexitsOneClick.zip) <button class="devsite-dialog-close">Cancel</button>

<br />

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](https://developers.google.com/assistant/df-asdk/simple-responses.md.txt)
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 a 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

```javascript
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

```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

   ```javascript
   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

   ```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!"
             }
           }
         ]
       }
     }
   }
   ```