If you've built and published a Chat app that isn't a
Google Workspace add-on, this page shows how to convert it to a
Google Workspace add-on that extends Google Chat.

By converting, your Google Chat app can use the Google Workspace add-ons
framework, opening up new possibilities for integration and features within
Google Chat and across Google Workspace. For example,
you can distribute a single Google Workspace add-on
through Google Workspace Marketplace that extends Chat apps
alongside other Google Workspace host applications, like Gmail,
Calendar, and Docs.

## Limitations

Before starting the conversion, review the
[Limitations and considerations for Google Workspace add-ons](https://developers.google.com/workspace/chat/interact-users-overview#limitations-known-issues)
to ensure your Chat app that isn't an
add-on can be converted without losing essential
functionality.

## Step 1: Copy your existing Google Chat app code

The conversion process requires code changes. To avoid affecting your live
Google Chat app, create and work on a copy of your code.

### Apps Script

1. Open your existing Google Chat app Google Apps Script project.
2. At the left, click **Overview** .
3. At the right, click **Make a copy** .
4. At the left, click **Project Settings** .
5. Under **Google Cloud project** , click **Change project**.
6. Enter the same project number associated with your existing Google Chat app project.
7. Click **Set project**.

### HTTP

Create a fork or copy of your existing codebase and deploy it as a new service,
separate from your live Google Chat app.

If your app is deployed on Google Cloud and relies on features tied to
the Google Cloud project (for example, the default App Engine identity),
the new code should be deployed on a service associated with the existing
Google Chat app project.

## Step 2: Modify the copied code

Google Workspace add-ons that extend Google Chat use different request and
response structures compared to Chat apps that aren't
add-ons. You need to update your code to use
[Google Workspace add-on event objects (`EventObject`)](https://developers.google.com/workspace/add-ons/concepts/event-objects)
instead of
[Google Chat API interaction events (`Event`)](https://developers.google.com/workspace/chat/api/reference/rest/v1/Event)
for requests and responses.
Use the [Code conversion guide](https://developers.google.com/workspace/chat/convert#code-conversion-guide) to modify your code.

## Step 3: Enable the Google Workspace add-on configuration for test users

Use the Google Cloud console to configure the Google Workspace add-on settings for
your Google Chat app:

1. Go to the Google Chat API configuration page in the Google Cloud console.

   [Go to Google Chat API configuration page](https://console.cloud.google.com/apis/api/chat.googleapis.com/hangouts-chat)
2. Under **Interactive Features** , turn on **Enable Interactive features**.

3. Under **Convert to Google Workspace add-on** , click
   **Convert to add-on**.

4. Enable **Turn on add-on configuration settings**.

5. In the **Visibility** section, add the email addresses of your test users.

6. If necessary, update **Connection Settings** with the deployment endpoint URL or Apps Script deployment ID of your
   copied and modified Google Chat app code from Step 2.

7. Click **Save and test**.

## Step 4: Test the converted app

Test the Google Workspace add-on functionality thoroughly using the test user
accounts configured in Step 3. Verify all features and interactions.

## Step 5: Complete the conversion for all users

After you've verified that the converted Google Workspace add-on works correctly,
you can make it available to all users.

> [!WARNING]
> **Warning:** This process is irreversible. Only proceed after confirming that your converted Google Workspace add-on is fully tested and functional.

1. Go to the Google Chat API configuration page in the Google Cloud console.

   [Go to Google Chat API configuration page](https://console.cloud.google.com/apis/api/chat.googleapis.com/hangouts-chat)
2. Under **Interactive Features** , click **Convert to add-on**. A side panel opens.

3. In the side panel, click **Convert to add-on**.

4. Type your Project ID and click **Convert**.

Your Google Chat app is now a Google Workspace add-on that extends
Google Chat.

## Optional: Clean up or free unused Google Cloud resources

Optionally, after converting your Google Chat app to an
Google Workspace add-on, to avoid
incurring charges to your Google Cloud account for the resources used by the
Google Chat app that are no longer in use, consider turning them
off.

## Code conversion guide

This section details the mapping between the Google Chat API interaction
[`Event`](https://developers.google.com/workspace/chat/api/reference/rest/v1/Event)
format and the Google Workspace add-on
[`EventObject`](https://developers.google.com/workspace/add-ons/concepts/event-objects)
format.

### Request mapping

The following table shows how fields in the Google Chat API
[`Event`](https://developers.google.com/workspace/chat/api/reference/rest/v1/Event) for a
Chat app that isn't an add-on
map to the corresponding fields in the Google Workspace add-on
[`EventObject`](https://developers.google.com/workspace/add-ons/concepts/event-objects).

| Chat app that isn't an add-on ([`Event`](https://developers.google.com/workspace/chat/api/reference/rest/v1/Event) field) | Google Workspace add-on [`EventObject`](https://developers.google.com/workspace/add-ons/concepts/event-objects) field | Notes |
|---|---|---|
| `action.actionMethodName` | N/A | For card interactions, the method name can be passed as a parameter in `commonEventObject.parameters`. See [Open an initial dialog](https://developers.google.com/workspace/chat/dialogs#open-dialog). |
| `action.parameters` | `commonEventObject.parameters` |   |
| `appCommandMetadata` | `chat.appCommandPayload.appCommandMetadata` |   |
| `common` | `commonEventObject` |   |
| `configCompleteRedirectUrl` | - `chat.appCommandPayload.configCompleteRedirectUri` - `chat.addedToSpacePayload.configCompleteRedirectUri` - `chat.messagePayload.configCompleteRedirectUri` | Available in different payloads depending on the event type. |
| `dialogEventType` | - `chat.appCommandPayload.dialogEventType` - `chat.buttonClickedPayload.dialogEventType` | Available in different payloads depending on the event type. |
| `eventTime` | `chat.eventTime` |   |
| `isDialogEvent` | - `chat.appCommandPayload.isDialogEvent` - `chat.buttonClickedPayload.isDialogEvent` | Available in different payloads depending on the event type. |
| `message` | - `chat.messagePayload.message` - `chat.buttonClickedPayload.message` - `chat.appCommandPayload.message` | Available in different payloads depending on the event type. |
| `space` | - `chat.messagePayload.space` - `chat.addedToSpacePayload.space` - `chat.removedFromSpacePayload.space` - `chat.buttonClickedPayload.space` - `chat.widgetUpdatedPayload.space` - `chat.appCommandPayload.space` |   |
| `thread` | - `chat.messagePayload.message.thread` - `chat.buttonClickedPayload.message.thread` - `chat.appCommandPayload.message.thread` | Available in different payloads depending on the event type. |
| `threadKey` | - `chat.messagePayload.message.thread.threadKey` - `chat.buttonClickedPayload.message.thread.threadKey` - `chat.appCommandPayload.message.threadKey` | Available in different payloads depending on the event type. |
| `token` | N/A | Verification is handled differently, see [Request Verification for HTTP Apps](https://developers.google.com/workspace/chat/convert#request-verification). |
| `type` | N/A | The event type can be deduced from the [trigger](https://developers.google.com/workspace/chat/receive-respond-interactions#triggers). |
| `user` | `chat.user` |   |

### Request mapping by use case

The following table shows the differences in request payloads for common use
cases between Chat apps that aren't add-ons and
Google Workspace add-ons that extend Google Chat.

> [!NOTE]
> **Note:** For [Chat app homepages](https://developers.google.com/workspace/chat/send-app-home-card-message), add-ons route **App Home** events to a dedicated App Home endpoint or function, and the `EventObject` contains `commonEventObject` along with `chat.user` and `chat.eventTime`.

| Use Case | Chat app that isn't an add-on ([`Event`](https://developers.google.com/workspace/chat/api/reference/rest/v1/Event) Payload) | Google Workspace add-on [`EventObject`](https://developers.google.com/workspace/add-ons/concepts/event-objects) Payload |
|---|---|---|
| App added to space | ```json { "type": "ADDED_TO_SPACE", "space": { ... } } ``` | ```json { "chat": { "addedToSpacePayload": { "space": { ... } } } } ``` |
| Remove app from space | ```json { "type": "REMOVED_FROM_SPACE", "space": { ... } } ``` | ```json { "chat": { "removedFromSpacePayload": { "space": { ... } } } } ``` |
| User @-mentions an app | ```json { "type": "MESSAGE", "message": { ... }, "space": { ... }, "configCompleteRedirectUrl": "..." } ``` | ```json { "chat": { "messagePayload": { "message": { ... }, "space": { ... }, "configCompleteRedirectUri": "..." } } } ``` |
| User @-mentions an app to add it to the space | You need to handle one request from Google Chat: ```json { "type": "ADDED_TO_SPACE", "space": { ... }, "message": { ... } } ``` | You must handle two requests from Google Chat. First request: ```json { "chat": { "addedToSpacePayload": { "space": { ... }, "interactionAdd": true } } } ``` Second request: ```json { "chat": { "messagePayload": { "message": { ... }, "space": { ... } } } } ``` |
| Slash command | ```json { "type": "MESSAGE", "message": { "slashCommand": { ... } }, "space": { ... } } ``` | ```json { "chat": { "appCommandPayload": { "message": { ... }, "space": { ... }, "appCommandMetadata": { ... } } } } ``` |
| Slash command to add an app to the space | You need to handle one request from Google Chat: ```json { "type": "ADDED_TO_SPACE", "space": { ... }, "message": { "slashCommand": { ... } } } ``` | You must handle two requests from Google Chat. First request: ```json { "chat": { "addedToSpacePayload": { "space": { ... }, "interactionAdd": true } } } ``` Second request: ```json { "chat": { "appCommandPayload": { "message": { ... }, "space": { ... }, "appCommandMetadata": { ... } } } } ``` |
| User clicks a button on a card or dialog | ```json { "type": "CARD_CLICKED", "common": { ... }, "space": { ... }, "message": { ... }, "isDialogEvent": "...", "dialogEventType": "..." } ``` For dialog events, `common.formInputs` contains widget values. Google Apps Script example: ```json { "type": "CARD_CLICKED", "common": { "formInputs": { "contactName": { "": { "stringInputs": { "value": ["Kai 0"] }} } } }, "space": { ... }, "message": { ... }, "isDialogEvent": true, "dialogEventType": "..." } ``` | ```json { "commonEventObject": { ... }, "chat": { "buttonClickedPayload": { "message": { ... }, "space": { ... }, "isDialogEvent": "...", "dialogEventType": "..." } } } ``` For dialog events, `commonEventObject.formInputs` contains widget values. Google Apps Script example: ```json { "commonEventObject": { "formInputs": { "contactName": { "stringInputs": { "value": ["Kai 0"] } } } }, "chat": { "buttonClickedPayload": { "message": { ... }, "space": { ... }, "isDialogEvent": "true", "dialogEventType": "..." } } } ``` |
| User submits information in an app home card | ```json { "type": "SUBMIT_FORM", "common": { ... }, "space": { ... }, "message": { ... }, "isDialogEvent": "...", "dialogEventType": "..." } ``` | ```json { "commonEventObject": { ... }, "chat": { "buttonClickedPayload": { "message": { ... }, "space": { ... }, "isDialogEvent": "...", "dialogEventType": "SUBMIT_DIALOG" } } } ``` |
| User invokes an app command using a quick command | ```json { "type": "APP_COMMAND", "space": { ... }, "isDialogEvent": "...", "dialogEventType": "..." } ``` | ```json { "chat": { "appCommandPayload": { "message": { ... }, "space": { ... }, "appCommandMetadata": { ... } } } } ``` |
| Link preview | ```json { "type": "MESSAGE", "message": { "matchedUrl": "..." }, "space": { ... } } ``` | ```json { "chat": { "messagePayload": { "message": { "matchedUrl": "..." }, "space": { ... } } } } ``` |
| User updates a widget in a card message or dialog | ```json { "type": "WIDGET_UPDATED", "space": { ... }, "common": { ... } } ``` | ```json { "commonEventObject": { ... }, "chat": { "widgetUpdatedPayload": { "space": { ... } } } } ``` |

### Response mapping by use case

Google Workspace add-ons that extend Google Chat return actions instead of a
`Message` object. The following table maps Google Chat API `Message` response types
for a Chat app that isn't an
add-on to their Google Workspace add-on action
equivalents.

| Use Case | Chat app that isn't an add-on ([`Message`](https://developers.google.com/workspace/chat/api/reference/rest/v1/spaces.messages#Message) Response) | Google Workspace add-on [Chat action](https://developers.google.com/workspace/chat/receive-respond-interactions#deliver-response) response |
|---|---|---|
| Create a message in the invoked space | ```json { "actionResponse": { "type": "NEW_MESSAGE" }, "text": "..." } ``` `actionResponse` is optional. To learn more, see [Respond to a slash command](https://developers.google.com/workspace/chat/commands#slash). | ```json { "hostAppDataAction": { "chatDataAction": { "createMessageAction": { "message": { "text": "..." } } } } } ``` To learn more, see [Reply with a message](https://developers.google.com/workspace/chat/receive-respond-interactions#reply-message). |
| Update a message | ```json { "actionResponse": { "type": "UPDATE_MESSAGE" }, "text": "..." } ``` To learn more, see [Update a message](https://developers.google.com/workspace/chat/update-messages). | ```json { "hostAppDataAction": { "chatDataAction": { "updateMessageAction": { "message": { "text": "..." } } } } } ``` To learn more, see [Update a message](https://developers.google.com/workspace/chat/receive-respond-interactions#update-message). |
| Link preview | ```json { "actionResponse": { "type": "UPDATE_USER_MESSAGE_CARDS" }, "cardsV2": [{ ... }] } ``` To learn more, see [Preview links](https://developers.google.com/workspace/chat/preview-links). | ```json { "hostAppDataAction": { "chatDataAction": { "updateInlinePreviewAction": { "cardsV2": [{ ... }] } } } } ``` To learn more, see [Preview links](https://developers.google.com/workspace/chat/preview-links). |
| Open an initial dialog | ```json { "actionResponse": { "type": "DIALOG", "dialogAction": { "dialog": { "body": { /* Card object */ } } } } } ``` To learn more, see [Open interactive dialogs](https://developers.google.com/workspace/chat/dialogs#open-dialog). | ```json { "action": { "navigations": [{ "pushCard": { /* Card object */ } }] } } ``` The card you push can contain widgets with `onClick` actions. For HTTP Google Workspace add-ons, configure these actions to call a function endpoint: ```json { "onClick": { "action": { "function": "https://...", "parameters": [{ "key": "clickedButton", "value": "submit" }] } } } ``` To learn more, see [Open interactive dialogs](https://developers.google.com/workspace/chat/dialogs#open-dialog). |
| Close a dialog | ```json { "actionResponse": { "type": "DIALOG", "dialogAction": { "actionStatus": { "userFacingMessage": "..." } } } } ``` To learn more, see [Close a dialog](https://developers.google.com/workspace/chat/dialogs#close). | ```json { "action": { "navigations": [{ "endNavigation": "CLOSE_DIALOG" }], "notification": { "text": "..."} } } ``` To learn more, see [Close a dialog](https://developers.google.com/workspace/chat/dialogs#close). |
| Connect to an external system (Request config) | ```json { "actionResponse": { "type": "REQUEST_CONFIG", "url": "..." } } ``` To learn more, see [Connect to an external system (Chat apps that aren't add-ons)](https://developers.google.com/workspace/chat/connect-web-services-tools#not-add-on-connect-web-services). | ```json { "basic_authorization_prompt": { "authorization_url": "...", "resource": "..." } } ``` To learn more, see [Connect a Chat app with other services and tools](https://developers.google.com/workspace/chat/connect-web-services-tools#basic-auth-chat). |
| Autocomplete items on interactive widgets | ```json { "actionResponse": { "type": "UPDATE_WIDGET", "updatedWidget": { "suggestions": { "items": ["..."] }, "widget": "widget_id" } } } ``` To learn more, see [Add a multiselect menu](https://developers.google.com/workspace/chat/design-interactive-card-dialog#multiselect-menu). | ```json { "action": { "modifyOperations": [{ "updateWidget": { "widgetId": "widget_id", "selectionInputWidgetSuggestions": { "suggestions": ["..."] } } }] } } ``` To learn more, see [Read form data input by users on cards](https://developers.google.com/workspace/chat/read-form-data). |

### Handle card interactions on messages created prior to conversion

When you convert an HTTP Chat app that isn't an
add-on to a Google Workspace add-on, card
interactions on messages created before the conversion require special handling.
add-ons use a full HTTP URL for a card's `action.function`, while
Chat apps that aren't add-ons use a function name.
The following table summarizes these differences.

|   | Chat app that isn't an add-on | Google Workspace add-on that extends Google Chat |
|---|---|---|
| **Configuration** | You configure a single endpoint for all events in the Google Cloud console. When implementing card interactions, a card's `action` only contains the name of the function to execute. The common HTTP endpoint is invoked for card click events. To learn more, see [Open interactive dialogs](https://developers.google.com/workspace/chat/dialogs#open-dialog). <br /> <br /> ```json { "onClick": { "action": { "function": "submit" } } } ``` | You can optionally configure per-event endpoints in the Google Cloud console, but this doesn't include card click events. When implementing card interactions, a card's `action` must contain the full URL of the HTTP endpoint to invoke. You can set a unique HTTP endpoint per button, or use a common endpoint and pass the action as a parameter in `action.parameters`. To learn more, see [Open interactive dialogs](https://developers.google.com/workspace/chat/dialogs#open-dialog). <br /> <br /> ```json { "onClick": { "action": { "function": "https://...", "parameters": [{ "key": "method", "value": "submit" }] } } } ``` |

To ensure card interactions are functional for messages created before the conversion, configure a **Card Interaction URL** on the Google Chat API configuration page.

This URL is only used for interactions on messages created before you converted your app. When a user interacts with one of these messages, the original `action.function` value is passed as a parameter called `__action_method_name__`.

#### Example: Card click

If you configured the **Card Interaction URL** as `https://.../card-interaction-handler`, and a user clicks a card on a historical message with the following action:

    {
      "onClick": {
        "action": {
         "function": "submit"
        }
      }
    }

An event is delivered to your configured **Card Interaction URL** with the following format:

    {
      "commonEventObject": {
        "parameters": {
          "__action_method_name__": "submit"
        }
      },
      "chat": {
        "buttonClickedPayload": { ... }
      }
    }

#### Example: Multi-select menu

If a user interacts with a multi-select menu with an external data source:

    {
      "selectionInput": {
        "name": "contacts",
        "type": "MULTI_SELECT",
        "externalDataSource": {
          "function": "getContacts"
        }
      }
    }

An event is delivered to your configured **Card Interaction URL** with the following format:

    {
      "commonEventObject": {
        "parameters": {
          "__action_method_name__": "getContacts",
        }
      },
      "chat": {
        "widgetUpdatedPayload": { ... }
      }
    }

If you turn on **Use common HTTP endpoint url for all triggers** for your HTTP
triggers, then the common URL is also used for **Button Clicked** events.

### Verify requests for HTTP Google Workspace add-ons that extend Chat

For HTTP-based Google Chat apps, the logic to verify that requests originate
from Google needs to be updated when converting to a Google Workspace add-on.

- Verification for HTTP Chat apps that aren't add-ons: [Verify requests from Google Chat](https://developers.google.com/workspace/chat/verify-requests-from-chat)
- Google Workspace add-on HTTP verification: [Verifying requests from Google](https://developers.google.com/workspace/add-ons/how-tos/verify-requests)

The key differences in request verification are:

| App Type | Supported Audience | Service Account Email |
|---|---|---|
| Chat app that isn't an add-on | Project number | `chat@system.gserviceaccount.com` |
| Google Workspace add-on extending Google Chat | HTTP endpoint only | Per-project service account email |

The unique service account email for your Google Workspace add-on can be found in the
**Convert to Google Workspace add-ons** section on the Google Chat API configuration page
in the Google Cloud console.

To verify requests in your upgraded Google Workspace add-on:

1. If using Cloud Run functions, grant the `roles/cloudfunctions.invoker` role to the per-addon service account. See [Authorize access with IAM](https://cloud.google.com/functions/docs/securing/managing-access-iam).
2. Update your token verification code to use the Google Workspace add-on service account email to verify the signature of the Bearer token. See [Validate requests from Google](https://developers.google.com/workspace/add-ons/guides/alternate-runtimes#validate-requests-from-google).