# Build your conversational Action (Dialogflow)

You define the conversation for your Action with [fulfillment](https://developers.google.com/assistant/actions/dialogflow/fulfillment),
which is code deployed as a webhook that contains your Dialogflow agent's
conversational logic. Fulfillment tells your Action what to do when users make
requests.

For Interactive Canvas, your fulfillment also communicates information about your
web app to the Assistant. You can use an `HtmlResponse` to tell the Assistant to
render your web app. An `HtmlResponse` can also provide updates to `data`, which
your web app custom logic uses to make changes to your web app.

This page goes over how to use `HtmlResponse` in your fulfillment and general
guidelines for using this response type.

> [!NOTE]
> **Note:** To ensure the user's device can support an `HtmlResponse`, make sure the [capability](https://developers.google.com/assistant/df-asdk/surface-capabilities) `actions.capability.INTERACTIVE_CANVAS` is available. If not, use a [rich response](https://developers.google.com/assistant/df-asdk/rich-responses) instead.

## HTML responses

To relay information about your web app to the Assistant, you must include an
[`HtmlResponse`](https://actions-on-google.github.io/actions-on-google-nodejs/2.10.0/classes/_service_actionssdk_conversation_response_html_.htmlresponse.html)
in your intent-specific fulfillment. An `HtmlResponse` can contain the URL
of the web app and data that updates the web app. When you send
an `HtmlResponse`, the following steps occur:

1. The fulfillment of the matched intent sends an `HtmlResponse` to the device.
2. The device uses the URL in the `HtmlResponse` to load the web app.
3. The `data` JSON payload is passed to the web app in a callback.
4. Your conversational Action sends a new `HtmlResponse` to send updates or
   load new states.

   > [!NOTE]
   > **Note:** If the value for the `url` field changes in subsequent fulfillment responses, the web app reloads. If the same value for `url` is provided or if `url` is excluded as part of a subsequent response, the existing web app is kept on the device.

### Sample fulfillment

The following excerpt from the [sample](https://github.com/actions-on-google/dialogflow-interactive-canvas-nodejs)
fulfillment code shows how to implement `HtmlResponse`:

    const functions = require('firebase-functions');
    const {dialogflow, HtmlResponse} = require('actions-on-google');

    const app = dialogflow({debug: true});
    app.intent('welcome', (conv) => {
      conv.ask('Welcome! Do you want me to change color or pause spinning?');
      conv.ask(new HtmlResponse({
        url: 'https://your-web-app.com',
      }));
    });

    // map of human speakable colors to color values
    const tints = {
      red: 0xFF0000,
      green: 0x00FF00,
      blue: 0x0000FF,
    };
    app.intent('color', (conv, {color}) => {
      if (color in tints) {
        conv.ask(`Ok, I changed my color to ${color}. What else?`);
        conv.ask(new HtmlResponse({
          data: {
            tint: tints[color],
          },
        }));
        return;
      }
      conv.ask(`Sorry, I don't know that color. What else?`);
      conv.ask(new HtmlResponse({
        data: {
          query: conv.query,
        },
      }));
    });
    app.intent('start', (conv) => {
      conv.ask(`Ok, I'm spinning. What else?`);
      conv.ask(new HtmlResponse({
        data: {
          spin: true,
        },
      }));
    });

    app.intent('pause', (conv) => {
      conv.ask(`Ok, I paused spinning. What else?`);
      conv.ask(new HtmlResponse({
        data: {
          spin: false,
        },
      }));
    });

    app.intent('restart game', (conv) => {
      conv.ask(new HtmlResponse({
        data: {
          command: 'RESTART_GAME',
        },
      }));
    });

    exports.conversation = functions.https.onRequest(app);

### `welcome` intent

In the snippet above, the fulfillment for the `welcome` intent sends an
`HtmlResponse` with the URL for the web app. The Assistant receives this
and loads the HTML and JavaScript at that address.

    ...
    app.intent('welcome', (conv) => {
      conv.ask('Welcome! Do you want me to change color or pause spinning?');
      conv.ask(new HtmlResponse({
        url: 'https://your-web-app.com',
      }));
    });
    ...

### Other intents

The `HtmlResponse` in the fulfillment for other intents passes variable values
(`tint` or `spin` in the sample) to the web app. The custom logic for the web
app uses these values to update elements (animations, color, etc):

    ...
    app.intent('start', (conv) => {
      conv.ask(`Ok, I'm spinning. What else?`);
      conv.ask(new HtmlResponse({
        data: {
          spin: true,
        },
      }));
    });
    ...

### Guidelines and restrictions

Keep the following guidelines and restrictions for `HtmlResponse` in mind
when building your fulfillment:

- Each intent in your fulfillment must include an `HtmlResponse`. If an intent does not include an `HtmlResponse`, your web app closes.
- You only need to include your web app URL in the first intent you send to the user (this is normally the `Welcome` intent).
- `HtmlResponse` must be 50kb or smaller in size.