diff --git a/README.md b/README.md index 753f35f..5afd052 100644 --- a/README.md +++ b/README.md @@ -7,22 +7,193 @@ [![GitHub Issues](https://img.shields.io/github/issues/ballerina-platform/ballerina-library/module/hubspot.marketing.emails.svg?label=Open%20Issues)](https://github.com/ballerina-platform/ballerina-library/labels/module%hubspot.marketing.emails) ## Overview +[HubSpot](https://www.hubspot.com) is an AI-powered customer relationship management (CRM) platform. -[//]: # (TODO: Add overview mentioning the purpose of the module, supported REST API versions, and other high-level details.) +The `ballerinax/hubspot.marketing.emails` package offers APIs to connect and interact with the [HubSpot Marketing Emails API](https://developers.hubspot.com/docs/reference/api/marketing/emails/marketing-emails) endpoints, specifically based on the [HubSpot REST API](https://developers.hubspot.com/docs/reference/api/overview). + +Using this API, users can develop applications easily that enables you to track marketing emails. ## Setup guide -[//]: # (TODO: Add detailed steps to obtain credentials and configure the module.) +To use the HubSpot Marketing Events connector, you must have access to the HubSpot API through a HubSpot developer account and a HubSpot App under it. Therefore you need to register for a developer account at HubSpot if you don't have one already. + +### Step 1: Create/Login to a HubSpot Developer Account + +If you have an account already, go to the [HubSpot developer portal](https://app.hubspot.com/) + +If you don't have a HubSpot Developer Account you can sign up to a free account [here](https://developers.hubspot.com/get-started) + +### Step 2 (Optional): Create a [Developer Test Account](https://developers.hubspot.com/beta-docs/getting-started/account-types#developer-test-accounts) under your account + +Within app developer accounts, you can create a [developer test account](https://developers.hubspot.com/beta-docs/getting-started/account-types#developer-test-accounts) under your account to test apps and integrations without affecting any real HubSpot data. + +> **Note:** These accounts are only for development and testing purposes. In production you should not use Developer Test Accounts. + +1. Go to Test Account section from the left sidebar. + + ![Hubspot Developer Portal](https://github.com/ballerina-platform/module-ballerinax-hubspot.marketing.emails/tree/main/docs/setup/resources/test_acc_1.png) + +2. Click Create developer test account. + + ![Hubspot Developer Test Account](https://github.com/ballerina-platform/module-ballerinax-hubspot.marketing.emails/tree/main/docs/setup/resources/test_acc_2.png) + +3. In the dialogue box, give a name to your test account and click create. + + ![Hubspot Developer Test Account](https://github.com/ballerina-platform/module-ballerinax-hubspot.marketing.emails/tree/main/docs/setup/resources/test_acc_3.png) + +### Step 3: Create a HubSpot App under your account. + +1. In your developer account, navigate to the "Apps" section. Click on "Create App" + + ![Hubspot Create App](https://github.com/ballerina-platform/module-ballerinax-hubspot.marketing.emails/tree/main/docs/setup/resources/create_app_1.png ) + +2. Provide the necessary details, including the app name and description. + +### Step 4: Configure the Authentication Flow. + +1. Move to the Auth Tab. (Second tab next to App Info) + + ![Hubspot Developer Config Auth](https://github.com/ballerina-platform/module-ballerinax-hubspot.marketing.emails/tree/main/docs/setup/resources/auth_section.png ) + +2. In the Scopes section, add the following scope for your app using the "Add new scope" button. + + * `content` + * `transactional-email` (Optional, see note below) + * `marketing-email` (Optional, see note below) + +> **Note:** To use the `publish` and `unpublish` endpoints add one of `transactional-email` or `marketing-email` scopes. However a Hubspot Enterprise Account or Trasactional Email Add-on enabled is required to use these two endpoints. + + ![Hubspot Developer App Add Scopes](https://github.com/ballerina-platform/module-ballerinax-hubspot.marketing.emails/tree/main/docs/setup/resources/scopes.png ) + +4. Add your Redirect URI in the relevant section. You can also use `localhost` addresses for local development purposes. Click Create App. + + ![Hubspot Create Developer App](https://github.com/ballerina-platform/module-ballerinax-hubspot.marketing.emails/tree/main/docs/setup/resources/create_app_final.png ) + +### Step 5: Get your Client ID and Client Secret + +- Navigate to the Auth section of your app. Make sure to save the provided Client ID and Client Secret. + + ![Hubspot Get Credentials](https://github.com/ballerina-platform/module-ballerinax-hubspot.marketing.emails/tree/main/docs/setup/resources/get_credentials.png ) + +### Step 6: Setup Authentication Flow + +Before proceeding with the Quickstart, ensure you have obtained the Access Token using the following steps: + +1. Create an authorization URL using the following format: + + ``` + https://app.hubspot.com/oauth/authorize?client_id=&scope=&redirect_uri= + ``` + + Replace the ``, `` and `` with your specific value. + +> **Note:** If you are using a `localhost` redirect url, make sure to have a listener running at the relevant port before executing the next step. + +2. Paste it in the browser and select your developer test account to install the app when prompted. + + ![Hubspot Get Auth Code](https://github.com/ballerina-platform/module-ballerinax-hubspot.marketing.emails/tree/main/docs/setup/resources/install_app.png) + +3. A code will be displayed in the browser. Copy the code. + +4. Run the following curl command. Replace the ``, ` and `` with your specific value. Use the code you received in the above step 3 as the ``. + + - Linux/macOS + + ```bash + curl --request POST \ + --url https://api.hubapi.com/oauth/v1/token \ + --header 'content-type: application/x-www-form-urlencoded' \ + --data 'grant_type=authorization_code&code=&redirect_uri=&client_id=&client_secret=' + ``` + + - Windows + + ```bash + curl --request POST ^ + --url https://api.hubapi.com/oauth/v1/token ^ + --header 'content-type: application/x-www-form-urlencoded' ^ + --data 'grant_type=authorization_code&code=&redirect_uri=&client_id=&client_secret=' + ``` + + This command will return the access token necessary for API calls. + + ```json + { + "token_type": "bearer", + "refresh_token": "", + "access_token": "", + "expires_in": 1800 + } + ``` + +5. Store the access token securely for use in your application. ## Quickstart -[//]: # (TODO: Add a quickstart guide to demonstrate a basic functionality of the module, including sample code snippets.) +To use the `HubSpot Marketing Emails` connector in your Ballerina application, update the `.bal` file as follows: + +### Step 1: Import the module + +Import the `hubspot.marketing.emails` module and `oauth2` module. + +```ballerina +import ballerinax/hubspot.marketing.emails as hsmemails; +import ballerina/oauth2; +``` + +### Step 2: Instantiate a new connector + +1. Create a `Config.toml` file and, configure the obtained credentials in the above steps as follows: + + ```toml + clientId = + clientSecret = + refreshToken = + ``` + +2. Instantiate a `hsmevents:ConnectionConfig` with the obtained credentials and initialize the connector with it. + + ```ballerina + configurable string clientId = ?; + configurable string clientSecret = ?; + configurable string refreshToken = ?; + + final hsmemails:ConnectionConfig hsmeventsConfig = { + auth : { + clientId, + clientSecret, + refreshToken, + credentialBearer: oauth2:POST_BODY_BEARER + } + }; + + final hsmemails:Client hsmemailClient = check new (hsmeventsConfig); + ``` + +### Step 3: Invoke the connector operation + +Now, utilize the available connector operations. A sample usecase is shown below. + +#### Get statistics for Marketing Emails + +```ballerina +public function main() returns error? { + hsmemails:AggregateEmailStatistics emailStatistics = check hsmemailClient->/marketing/v3/emails/statistics/list({}, + { + startTimestamp: "2024-12-12T04:27:02Z", + endTimestamp: "2024-12-19T04:27:02Z" + }); +} +``` + ## Examples -The `HubSpot Marketing Emails` connector provides practical examples illustrating usage in various scenarios. Explore these [examples](https://github.com/module-ballerinax-hubspot.marketing.emails/tree/main/examples/), covering the following use cases: +The `Hubspot Marketing Emails` connector provides practical examples illustrating usage in various scenarios. Explore these examples, covering use cases: + +1. [Bulk Change Reply To Email](https://github.com/ballerina-platform/module-ballerinax-hubspot.marketing.emails/tree/main/examples/bulk_change_reply_email/) - Change the Reply To and Custom Reply To email address of all draft emails -[//]: # (TODO: Add examples) +2. [Marketing Email Statistics Logger](https://github.com/ballerina-platform/module-ballerinax-hubspot.marketing.emails/tree/main/examples/email_stat_logger/) - Retrieve and log the statistics of marketing emails during a specific time period. ## Build from the source diff --git a/ballerina/Ballerina.toml b/ballerina/Ballerina.toml index 4b38731..8a7c166 100644 --- a/ballerina/Ballerina.toml +++ b/ballerina/Ballerina.toml @@ -5,12 +5,12 @@ name = "hubspot.marketing.emails" version = "0.1.0" license = ["Apache-2.0"] authors = ["Ballerina"] -keywords = [] -# icon = "icon.png" # TODO: update icon.png +keywords = ["hubspot", "crm", "marketing", "emails"] +# icon = "icon.png" # TODO: Add icon repository = "https://github.com/ballerina-platform/module-ballerinax-hubspot.marketing.emails" [build-options] observabilityIncluded = true -[platform.java21] +[platform.java17] graalvmCompatible = true diff --git a/ballerina/Dependencies.toml b/ballerina/Dependencies.toml new file mode 100644 index 0000000..5fbc28b --- /dev/null +++ b/ballerina/Dependencies.toml @@ -0,0 +1,336 @@ +# AUTO-GENERATED FILE. DO NOT MODIFY. + +# This file is auto-generated by Ballerina for managing dependency versions. +# It should not be modified by hand. + +[ballerina] +dependencies-toml-version = "2" +distribution-version = "2201.10.0" + +[[package]] +org = "ballerina" +name = "auth" +version = "2.12.0" +dependencies = [ + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.array"}, + {org = "ballerina", name = "lang.string"}, + {org = "ballerina", name = "log"} +] + +[[package]] +org = "ballerina" +name = "cache" +version = "3.8.0" +dependencies = [ + {org = "ballerina", name = "constraint"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "task"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "constraint" +version = "1.5.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "crypto" +version = "2.7.2" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "file" +version = "1.10.0" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "os"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "http" +version = "2.12.4" +dependencies = [ + {org = "ballerina", name = "auth"}, + {org = "ballerina", name = "cache"}, + {org = "ballerina", name = "constraint"}, + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "file"}, + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "jwt"}, + {org = "ballerina", name = "lang.array"}, + {org = "ballerina", name = "lang.decimal"}, + {org = "ballerina", name = "lang.int"}, + {org = "ballerina", name = "lang.regexp"}, + {org = "ballerina", name = "lang.runtime"}, + {org = "ballerina", name = "lang.string"}, + {org = "ballerina", name = "lang.value"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "mime"}, + {org = "ballerina", name = "oauth2"}, + {org = "ballerina", name = "observe"}, + {org = "ballerina", name = "time"}, + {org = "ballerina", name = "url"} +] +modules = [ + {org = "ballerina", packageName = "http", moduleName = "http"}, + {org = "ballerina", packageName = "http", moduleName = "http.httpscerr"} +] + +[[package]] +org = "ballerina" +name = "io" +version = "1.6.3" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.value"} +] + +[[package]] +org = "ballerina" +name = "jballerina.java" +version = "0.0.0" + +[[package]] +org = "ballerina" +name = "jwt" +version = "2.13.0" +dependencies = [ + {org = "ballerina", name = "cache"}, + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.int"}, + {org = "ballerina", name = "lang.string"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "lang.__internal" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.object"} +] + +[[package]] +org = "ballerina" +name = "lang.array" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.__internal"} +] + +[[package]] +org = "ballerina" +name = "lang.decimal" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "lang.error" +version = "0.0.0" +scope = "testOnly" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "lang.int" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.__internal"}, + {org = "ballerina", name = "lang.object"} +] + +[[package]] +org = "ballerina" +name = "lang.object" +version = "0.0.0" + +[[package]] +org = "ballerina" +name = "lang.regexp" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "lang.runtime" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "lang.string" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.regexp"} +] + +[[package]] +org = "ballerina" +name = "lang.value" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "log" +version = "2.10.0" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.value"}, + {org = "ballerina", name = "observe"} +] + +[[package]] +org = "ballerina" +name = "mime" +version = "2.10.1" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.int"}, + {org = "ballerina", name = "log"} +] + +[[package]] +org = "ballerina" +name = "oauth2" +version = "2.12.0" +dependencies = [ + {org = "ballerina", name = "cache"}, + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "time"}, + {org = "ballerina", name = "url"} +] +modules = [ + {org = "ballerina", packageName = "oauth2", moduleName = "oauth2"} +] + +[[package]] +org = "ballerina" +name = "observe" +version = "1.3.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "os" +version = "1.8.0" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"} +] +modules = [ + {org = "ballerina", packageName = "os", moduleName = "os"} +] + +[[package]] +org = "ballerina" +name = "task" +version = "2.5.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "test" +version = "0.0.0" +scope = "testOnly" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.array"}, + {org = "ballerina", name = "lang.error"} +] +modules = [ + {org = "ballerina", packageName = "test", moduleName = "test"} +] + +[[package]] +org = "ballerina" +name = "time" +version = "2.5.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] +modules = [ + {org = "ballerina", packageName = "time", moduleName = "time"} +] + +[[package]] +org = "ballerina" +name = "url" +version = "2.4.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] +modules = [ + {org = "ballerina", packageName = "url", moduleName = "url"} +] + +[[package]] +org = "ballerinai" +name = "observe" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "observe"} +] +modules = [ + {org = "ballerinai", packageName = "observe", moduleName = "observe"} +] + +[[package]] +org = "ballerinax" +name = "hubspot.marketing.emails" +version = "0.1.0" +dependencies = [ + {org = "ballerina", name = "http"}, + {org = "ballerina", name = "oauth2"}, + {org = "ballerina", name = "os"}, + {org = "ballerina", name = "test"}, + {org = "ballerina", name = "time"}, + {org = "ballerina", name = "url"}, + {org = "ballerinai", name = "observe"} +] +modules = [ + {org = "ballerinax", packageName = "hubspot.marketing.emails", moduleName = "hubspot.marketing.emails"} +] + diff --git a/ballerina/Module.md b/ballerina/Module.md index 87db98e..d00626d 100644 --- a/ballerina/Module.md +++ b/ballerina/Module.md @@ -1,17 +1,189 @@ ## Overview +[HubSpot](https://www.hubspot.com) is an AI-powered customer relationship management (CRM) platform. -[//]: # (TODO: Add overview mentioning the purpose of the module, supported REST API versions, and other high-level details.) +The `ballerinax/hubspot.marketing.emails` package offers APIs to connect and interact with the [HubSpot Marketing Emails API](https://developers.hubspot.com/docs/reference/api/marketing/emails/marketing-emails) endpoints, specifically based on the [HubSpot REST API](https://developers.hubspot.com/docs/reference/api/overview). + +Using this API, users can develop applications easily that enables you to track marketing emails. ## Setup guide -[//]: # (TODO: Add detailed steps to obtain credentials and configure the module.) +To use the HubSpot Marketing Events connector, you must have access to the HubSpot API through a HubSpot developer account and a HubSpot App under it. Therefore you need to register for a developer account at HubSpot if you don't have one already. + +### Step 1: Create/Login to a HubSpot Developer Account + +If you have an account already, go to the [HubSpot developer portal](https://app.hubspot.com/) + +If you don't have a HubSpot Developer Account you can sign up to a free account [here](https://developers.hubspot.com/get-started) + +### Step 2 (Optional): Create a [Developer Test Account](https://developers.hubspot.com/beta-docs/getting-started/account-types#developer-test-accounts) under your account + +Within app developer accounts, you can create a [developer test account](https://developers.hubspot.com/beta-docs/getting-started/account-types#developer-test-accounts) under your account to test apps and integrations without affecting any real HubSpot data. + +> **Note:** These accounts are only for development and testing purposes. In production you should not use Developer Test Accounts. + +1. Go to Test Account section from the left sidebar. + + ![Hubspot Developer Portal](https://github.com/ballerina-platform/module-ballerinax-hubspot.marketing.emails/tree/main/docs/setup/resources/test_acc_1.png) + +2. Click Create developer test account. + + ![Hubspot Developer Test Account](https://github.com/ballerina-platform/module-ballerinax-hubspot.marketing.emails/tree/main/docs/setup/resources/test_acc_2.png) + +3. In the dialogue box, give a name to your test account and click create. + + ![Hubspot Developer Test Account](https://github.com/ballerina-platform/module-ballerinax-hubspot.marketing.emails/tree/main/docs/setup/resources/test_acc_3.png) + +### Step 3: Create a HubSpot App under your account. + +1. In your developer account, navigate to the "Apps" section. Click on "Create App" + + ![Hubspot Create App](https://github.com/ballerina-platform/module-ballerinax-hubspot.marketing.emails/tree/main/docs/setup/resources/create_app_1.png ) + +2. Provide the necessary details, including the app name and description. + +### Step 4: Configure the Authentication Flow. + +1. Move to the Auth Tab. (Second tab next to App Info) + + ![Hubspot Developer Config Auth](https://github.com/ballerina-platform/module-ballerinax-hubspot.marketing.emails/tree/main/docs/setup/resources/auth_section.png ) + +2. In the Scopes section, add the following scope for your app using the "Add new scope" button. + + * `content` + * `transactional-email` (Optional, see note below) + * `marketing-email` (Optional, see note below) + +> **Note:** To use the `publish` and `unpublish` endpoints add one of `transactional-email` or `marketing-email` scopes. However a Hubspot Enterprise Account or Trasactional Email Add-on enabled is required to use these two endpoints. + + ![Hubspot Developer App Add Scopes](https://github.com/ballerina-platform/module-ballerinax-hubspot.marketing.emails/tree/main/docs/setup/resources/scopes.png ) + + +4. Add your Redirect URI in the relevant section. You can also use `localhost` addresses for local development purposes. Click Create App. + + ![Hubspot Create Developer App](https://github.com/ballerina-platform/module-ballerinax-hubspot.marketing.emails/tree/main/docs/setup/resources/create_app_final.png ) + +### Step 5: Get your Client ID and Client Secret + +- Navigate to the Auth section of your app. Make sure to save the provided Client ID and Client Secret. + + ![Hubspot Get Credentials](https://github.com/ballerina-platform/module-ballerinax-hubspot.marketing.emails/tree/main/docs/setup/resources/get_credentials.png ) + +### Step 6: Setup Authentication Flow + +Before proceeding with the Quickstart, ensure you have obtained the Access Token using the following steps: + +1. Create an authorization URL using the following format: + + ``` + https://app.hubspot.com/oauth/authorize?client_id=&scope=&redirect_uri= + ``` + + Replace the ``, `` and `` with your specific value. + +> **Note:** If you are using a `localhost` redirect url, make sure to have a listener running at the relevant port before executing the next step. + +2. Paste it in the browser and select your developer test account to install the app when prompted. + + ![Hubspot Get Auth Code](https://github.com/ballerina-platform/module-ballerinax-hubspot.marketing.emails/tree/main/docs/setup/resources/install_app.png) + +3. A code will be displayed in the browser. Copy the code. + +4. Run the following curl command. Replace the ``, ` and `` with your specific value. Use the code you received in the above step 3 as the ``. + + - Linux/macOS + + ```bash + curl --request POST \ + --url https://api.hubapi.com/oauth/v1/token \ + --header 'content-type: application/x-www-form-urlencoded' \ + --data 'grant_type=authorization_code&code=&redirect_uri=&client_id=&client_secret=' + ``` + + - Windows + + ```bash + curl --request POST ^ + --url https://api.hubapi.com/oauth/v1/token ^ + --header 'content-type: application/x-www-form-urlencoded' ^ + --data 'grant_type=authorization_code&code=&redirect_uri=&client_id=&client_secret=' + ``` + + This command will return the access token necessary for API calls. + + ```json + { + "token_type": "bearer", + "refresh_token": "", + "access_token": "", + "expires_in": 1800 + } + ``` + +5. Store the access token securely for use in your application. ## Quickstart -[//]: # (TODO: Add a quickstart guide to demonstrate a basic functionality of the module, including sample code snippets.) +To use the `HubSpot Marketing Emails` connector in your Ballerina application, update the `.bal` file as follows: + +### Step 1: Import the module + +Import the `hubspot.marketing.emails` module and `oauth2` module. + +```ballerina +import ballerinax/hubspot.marketing.emails as hsmemails; +import ballerina/oauth2; +``` + +### Step 2: Instantiate a new connector + +1. Create a `Config.toml` file and, configure the obtained credentials in the above steps as follows: + + ```toml + clientId = + clientSecret = + refreshToken = + ``` + +2. Instantiate a `hsmevents:ConnectionConfig` with the obtained credentials and initialize the connector with it. + + ```ballerina + configurable string clientId = ?; + configurable string clientSecret = ?; + configurable string refreshToken = ?; + + final hsmemails:ConnectionConfig hsmeventsConfig = { + auth : { + clientId, + clientSecret, + refreshToken, + credentialBearer: oauth2:POST_BODY_BEARER + } + }; + + final hsmemails:Client hsmemailClient = check new (hsmeventsConfig); + ``` + +### Step 3: Invoke the connector operation + +Now, utilize the available connector operations. A sample usecase is shown below. + +#### Get statistics for Marketing Emails + +```ballerina +public function main() returns error? { + hsmemails:AggregateEmailStatistics emailStatistics = check hsmemailClient->/marketing/v3/emails/statistics/list({}, + { + startTimestamp: "2024-12-12T04:27:02Z", + endTimestamp: "2024-12-19T04:27:02Z" + }); +} +``` + ## Examples -The `HubSpot Marketing Emails` connector provides practical examples illustrating usage in various scenarios. Explore these [examples](https://github.com/module-ballerinax-hubspot.marketing.emails/tree/main/examples/), covering the following use cases: +The `Hubspot Marketing Emails` connector provides practical examples illustrating usage in various scenarios. Explore these examples, covering use cases: + +1. [Bulk Change Reply To Email](https://github.com/ballerina-platform/module-ballerinax-hubspot.marketing.emails/tree/main/examples/bulk_change_reply_email/) - Change the Reply To and Custom Reply To email address of all draft emails -[//]: # (TODO: Add examples) +2. [Marketing Email Statistics Logger](https://github.com/ballerina-platform/module-ballerinax-hubspot.marketing.emails/tree/main/examples/email_stat_logger/) - Retrieve and log the statistics of marketing emails during a specific time period. diff --git a/ballerina/Package.md b/ballerina/Package.md index 87db98e..67be08e 100644 --- a/ballerina/Package.md +++ b/ballerina/Package.md @@ -1,17 +1,188 @@ ## Overview +[HubSpot](https://www.hubspot.com) is an AI-powered customer relationship management (CRM) platform. -[//]: # (TODO: Add overview mentioning the purpose of the module, supported REST API versions, and other high-level details.) +The `ballerinax/hubspot.marketing.emails` package offers APIs to connect and interact with the [HubSpot Marketing Emails API](https://developers.hubspot.com/docs/reference/api/marketing/emails/marketing-emails) endpoints, specifically based on the [HubSpot REST API](https://developers.hubspot.com/docs/reference/api/overview). + +Using this API, users can develop applications easily that enables you to track marketing emails. ## Setup guide -[//]: # (TODO: Add detailed steps to obtain credentials and configure the module.) +To use the HubSpot Marketing Events connector, you must have access to the HubSpot API through a HubSpot developer account and a HubSpot App under it. Therefore you need to register for a developer account at HubSpot if you don't have one already. + +### Step 1: Create/Login to a HubSpot Developer Account + +If you have an account already, go to the [HubSpot developer portal](https://app.hubspot.com/) + +If you don't have a HubSpot Developer Account you can sign up to a free account [here](https://developers.hubspot.com/get-started) + +### Step 2 (Optional): Create a [Developer Test Account](https://developers.hubspot.com/beta-docs/getting-started/account-types#developer-test-accounts) under your account + +Within app developer accounts, you can create a [developer test account](https://developers.hubspot.com/beta-docs/getting-started/account-types#developer-test-accounts) under your account to test apps and integrations without affecting any real HubSpot data. + +> **Note:** These accounts are only for development and testing purposes. In production you should not use Developer Test Accounts. + +1. Go to Test Account section from the left sidebar. + + ![Hubspot Developer Portal](https://github.com/ballerina-platform/module-ballerinax-hubspot.marketing.emails/tree/main/docs/setup/resources/test_acc_1.png) + +2. Click Create developer test account. + + ![Hubspot Developer Test Account](https://github.com/ballerina-platform/module-ballerinax-hubspot.marketing.emails/tree/main/docs/setup/resources/test_acc_2.png) + +3. In the dialogue box, give a name to your test account and click create. + + ![Hubspot Developer Test Account](https://github.com/ballerina-platform/module-ballerinax-hubspot.marketing.emails/tree/main/docs/setup/resources/test_acc_3.png) + +### Step 3: Create a HubSpot App under your account. + +1. In your developer account, navigate to the "Apps" section. Click on "Create App" + + ![Hubspot Create App](https://github.com/ballerina-platform/module-ballerinax-hubspot.marketing.emails/tree/main/docs/setup/resources/create_app_1.png ) + +2. Provide the necessary details, including the app name and description. + +### Step 4: Configure the Authentication Flow. + +1. Move to the Auth Tab. (Second tab next to App Info) + + ![Hubspot Developer Config Auth](https://github.com/ballerina-platform/module-ballerinax-hubspot.marketing.emails/tree/main/docs/setup/resources/auth_section.png ) + +2. In the Scopes section, add the following scope for your app using the "Add new scope" button. + + * `content` + * `transactional-email` (Optional, see note below) + * `marketing-email` (Optional, see note below) + +> **Note:** To use the `publish` and `unpublish` endpoints add one of `transactional-email` or `marketing-email` scopes. However a Hubspot Enterprise Account or Trasactional Email Add-on enabled is required to use these two endpoints. + + ![Hubspot Developer App Add Scopes](https://github.com/ballerina-platform/module-ballerinax-hubspot.marketing.emails/tree/main/docs/setup/resources/scopes.png ) + +4. Add your Redirect URI in the relevant section. You can also use `localhost` addresses for local development purposes. Click Create App. + + ![Hubspot Create Developer App](https://github.com/ballerina-platform/module-ballerinax-hubspot.marketing.emails/tree/main/docs/setup/resources/create_app_final.png ) + +### Step 5: Get your Client ID and Client Secret + +- Navigate to the Auth section of your app. Make sure to save the provided Client ID and Client Secret. + + ![Hubspot Get Credentials](https://github.com/ballerina-platform/module-ballerinax-hubspot.marketing.emails/tree/main/docs/setup/resources/get_credentials.png ) + +### Step 6: Setup Authentication Flow + +Before proceeding with the Quickstart, ensure you have obtained the Access Token using the following steps: + +1. Create an authorization URL using the following format: + + ``` + https://app.hubspot.com/oauth/authorize?client_id=&scope=&redirect_uri= + ``` + + Replace the ``, `` and `` with your specific value. + +> **Note:** If you are using a `localhost` redirect url, make sure to have a listener running at the relevant port before executing the next step. + +2. Paste it in the browser and select your developer test account to install the app when prompted. + + ![Hubspot Get Auth Code](https://github.com/ballerina-platform/module-ballerinax-hubspot.marketing.emails/tree/main/docs/setup/resources/install_app.png) + +3. A code will be displayed in the browser. Copy the code. + +4. Run the following curl command. Replace the ``, ` and `` with your specific value. Use the code you received in the above step 3 as the ``. + + - Linux/macOS + + ```bash + curl --request POST \ + --url https://api.hubapi.com/oauth/v1/token \ + --header 'content-type: application/x-www-form-urlencoded' \ + --data 'grant_type=authorization_code&code=&redirect_uri=&client_id=&client_secret=' + ``` + + - Windows + + ```bash + curl --request POST ^ + --url https://api.hubapi.com/oauth/v1/token ^ + --header 'content-type: application/x-www-form-urlencoded' ^ + --data 'grant_type=authorization_code&code=&redirect_uri=&client_id=&client_secret=' + ``` + + This command will return the access token necessary for API calls. + + ```json + { + "token_type": "bearer", + "refresh_token": "", + "access_token": "", + "expires_in": 1800 + } + ``` + +5. Store the access token securely for use in your application. ## Quickstart -[//]: # (TODO: Add a quickstart guide to demonstrate a basic functionality of the module, including sample code snippets.) +To use the `HubSpot Marketing Emails` connector in your Ballerina application, update the `.bal` file as follows: + +### Step 1: Import the module + +Import the `hubspot.marketing.emails` module and `oauth2` module. + +```ballerina +import ballerinax/hubspot.marketing.emails as hsmemails; +import ballerina/oauth2; +``` + +### Step 2: Instantiate a new connector + +1. Create a `Config.toml` file and, configure the obtained credentials in the above steps as follows: + + ```toml + clientId = + clientSecret = + refreshToken = + ``` + +2. Instantiate a `hsmevents:ConnectionConfig` with the obtained credentials and initialize the connector with it. + + ```ballerina + configurable string clientId = ?; + configurable string clientSecret = ?; + configurable string refreshToken = ?; + + final hsmemails:ConnectionConfig hsmeventsConfig = { + auth : { + clientId, + clientSecret, + refreshToken, + credentialBearer: oauth2:POST_BODY_BEARER + } + }; + + final hsmemails:Client hsmemailClient = check new (hsmeventsConfig); + ``` + +### Step 3: Invoke the connector operation + +Now, utilize the available connector operations. A sample usecase is shown below. + +#### Get statistics for Marketing Emails + +```ballerina +public function main() returns error? { + hsmemails:AggregateEmailStatistics emailStatistics = check hsmemailClient->/marketing/v3/emails/statistics/list({}, + { + startTimestamp: "2024-12-12T04:27:02Z", + endTimestamp: "2024-12-19T04:27:02Z" + }); +} +``` + ## Examples -The `HubSpot Marketing Emails` connector provides practical examples illustrating usage in various scenarios. Explore these [examples](https://github.com/module-ballerinax-hubspot.marketing.emails/tree/main/examples/), covering the following use cases: +The `Hubspot Marketing Emails` connector provides practical examples illustrating usage in various scenarios. Explore these examples, covering use cases: + +1. [Bulk Change Reply To Email](https://github.com/ballerina-platform/module-ballerinax-hubspot.marketing.emails/tree/main/examples/bulk_change_reply_email/) - Change the Reply To and Custom Reply To email address of all draft emails -[//]: # (TODO: Add examples) +2. [Marketing Email Statistics Logger](https://github.com/ballerina-platform/module-ballerinax-hubspot.marketing.emails/tree/main/examples/email_stat_logger/) - Retrieve and log the statistics of marketing emails during a specific time period. diff --git a/ballerina/client.bal b/ballerina/client.bal index 66cdc3f..f10e84f 100644 --- a/ballerina/client.bal +++ b/ballerina/client.bal @@ -1,4 +1,7 @@ -// Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). +// AUTO-GENERATED FILE. DO NOT MODIFY. +// This file is auto-generated by the Ballerina OpenAPI tool. + +// Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com). // // WSO2 LLC. licenses this file to you under the Apache License, // Version 2.0 (the "License"); you may not use this file except @@ -13,3 +16,353 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. + +import ballerina/http; + +public isolated client class Client { + final http:Client clientEp; + final readonly & ApiKeysConfig? apiKeyConfig; + # Gets invoked to initialize the `connector`. + # + # + config - The configurations to be used when initializing the `connector` + # + serviceUrl - URL of the target service + # + return - An error if connector initialization failed + public isolated function init(ConnectionConfig config, string serviceUrl = "https://api.hubapi.com/marketing/v3/emails") returns error? { + http:ClientConfiguration httpClientConfig = {httpVersion: config.httpVersion, timeout: config.timeout, forwarded: config.forwarded, poolConfig: config.poolConfig, compression: config.compression, circuitBreaker: config.circuitBreaker, retryConfig: config.retryConfig, validation: config.validation}; + do { + if config.http1Settings is ClientHttp1Settings { + ClientHttp1Settings settings = check config.http1Settings.ensureType(ClientHttp1Settings); + httpClientConfig.http1Settings = {...settings}; + } + if config.http2Settings is http:ClientHttp2Settings { + httpClientConfig.http2Settings = check config.http2Settings.ensureType(http:ClientHttp2Settings); + } + if config.cache is http:CacheConfig { + httpClientConfig.cache = check config.cache.ensureType(http:CacheConfig); + } + if config.responseLimits is http:ResponseLimitConfigs { + httpClientConfig.responseLimits = check config.responseLimits.ensureType(http:ResponseLimitConfigs); + } + if config.secureSocket is http:ClientSecureSocket { + httpClientConfig.secureSocket = check config.secureSocket.ensureType(http:ClientSecureSocket); + } + if config.proxy is http:ProxyConfig { + httpClientConfig.proxy = check config.proxy.ensureType(http:ProxyConfig); + } + } + if config.auth is ApiKeysConfig { + self.apiKeyConfig = (config.auth).cloneReadOnly(); + } else { + httpClientConfig.auth = config.auth; + self.apiKeyConfig = (); + } + http:Client httpEp = check new (serviceUrl, httpClientConfig); + self.clientEp = httpEp; + return; + } + + # Delete a marketing email. + # + # + emailId - The ID of the marketing email to delete. + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request + # + return - No content + resource isolated function delete [string emailId](map headers = {}, *DeleteEmailidQueries queries) returns http:Response|error { + string resourcePath = string `/${getEncodedUri(emailId)}`; + map headerValues = {...headers}; + if self.apiKeyConfig is ApiKeysConfig { + headerValues["private-app-legacy"] = self.apiKeyConfig?.private\-app\-legacy; + } + resourcePath = resourcePath + check getPathForQueryParam(queries); + map httpHeaders = getMapForHeaders(headerValues); + return self.clientEp->delete(resourcePath, headers = httpHeaders); + } + + # Get all marketing emails for a HubSpot account. + # + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request + # + return - successful operation + resource isolated function get .(map headers = {}, *GetQueries queries) returns CollectionResponseWithTotalPublicEmailForwardPaging|error { + string resourcePath = string `/`; + map headerValues = {...headers}; + if self.apiKeyConfig is ApiKeysConfig { + headerValues["private-app-legacy"] = self.apiKeyConfig?.private\-app\-legacy; + } + map queryParamEncoding = {"sort": {style: FORM, explode: true}, "includedProperties": {style: FORM, explode: true}}; + resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); + map httpHeaders = getMapForHeaders(headerValues); + return self.clientEp->get(resourcePath, httpHeaders); + } + + # Get the details of a specified marketing email. + # + # + emailId - The marketing email ID. + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request + # + return - successful operation + resource isolated function get [string emailId](map headers = {}, *GetEmailidQueries queries) returns PublicEmail|error { + string resourcePath = string `/${getEncodedUri(emailId)}`; + map headerValues = {...headers}; + if self.apiKeyConfig is ApiKeysConfig { + headerValues["private-app-legacy"] = self.apiKeyConfig?.private\-app\-legacy; + } + map queryParamEncoding = {"includedProperties": {style: FORM, explode: true}}; + resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); + map httpHeaders = getMapForHeaders(headerValues); + return self.clientEp->get(resourcePath, httpHeaders); + } + + resource isolated function get [string emailId]/ab\-test/get\-variation(map headers = {}) returns PublicEmail|error { + string resourcePath = string `/${getEncodedUri(emailId)}/ab-test/get-variation`; + map headerValues = {...headers}; + if self.apiKeyConfig is ApiKeysConfig { + headerValues["private-app-legacy"] = self.apiKeyConfig?.private\-app\-legacy; + } + map httpHeaders = getMapForHeaders(headerValues); + return self.clientEp->get(resourcePath, httpHeaders); + } + + # Get draft version of a marketing email + # + # + emailId - The marketing email ID. + # + headers - Headers to be sent with the request + # + return - successful operation + resource isolated function get [string emailId]/draft(map headers = {}) returns PublicEmail|error { + string resourcePath = string `/${getEncodedUri(emailId)}/draft`; + map headerValues = {...headers}; + if self.apiKeyConfig is ApiKeysConfig { + headerValues["private-app-legacy"] = self.apiKeyConfig?.private\-app\-legacy; + } + map httpHeaders = getMapForHeaders(headerValues); + return self.clientEp->get(resourcePath, httpHeaders); + } + + # Get revisions of a marketing email + # + # + emailId - The marketing email ID. + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request + # + return - successful operation + resource isolated function get [string emailId]/revisions(map headers = {}, *GetEmailidRevisionsQueries queries) returns CollectionResponseWithTotalVersionPublicEmail|error { + string resourcePath = string `/${getEncodedUri(emailId)}/revisions`; + map headerValues = {...headers}; + if self.apiKeyConfig is ApiKeysConfig { + headerValues["private-app-legacy"] = self.apiKeyConfig?.private\-app\-legacy; + } + resourcePath = resourcePath + check getPathForQueryParam(queries); + map httpHeaders = getMapForHeaders(headerValues); + return self.clientEp->get(resourcePath, httpHeaders); + } + + # Get a revision of a marketing email. + # + # + emailId - The marketing email ID. + # + revisionId - The ID of a revision. + # + headers - Headers to be sent with the request + # + return - successful operation + resource isolated function get [string emailId]/revisions/[string revisionId](map headers = {}) returns VersionPublicEmail|error { + string resourcePath = string `/${getEncodedUri(emailId)}/revisions/${getEncodedUri(revisionId)}`; + map headerValues = {...headers}; + if self.apiKeyConfig is ApiKeysConfig { + headerValues["private-app-legacy"] = self.apiKeyConfig?.private\-app\-legacy; + } + map httpHeaders = getMapForHeaders(headerValues); + return self.clientEp->get(resourcePath, httpHeaders); + } + + # Get aggregated statistic intervals. + # + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request + # + return - successful operation + resource isolated function get statistics/histogram(map headers = {}, *GetStatisticsHistogramQueries queries) returns CollectionResponseWithTotalEmailStatisticIntervalNoPaging|error { + string resourcePath = string `/statistics/histogram`; + map headerValues = {...headers}; + if self.apiKeyConfig is ApiKeysConfig { + headerValues["private-app-legacy"] = self.apiKeyConfig?.private\-app\-legacy; + } + map queryParamEncoding = {"emailIds": {style: FORM, explode: true}}; + resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); + map httpHeaders = getMapForHeaders(headerValues); + return self.clientEp->get(resourcePath, httpHeaders); + } + + # Get aggregated statistics. + # + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request + # + return - successful operation + resource isolated function get statistics/list(map headers = {}, *GetStatisticsListQueries queries) returns AggregateEmailStatistics|error { + string resourcePath = string `/statistics/list`; + map headerValues = {...headers}; + if self.apiKeyConfig is ApiKeysConfig { + headerValues["private-app-legacy"] = self.apiKeyConfig?.private\-app\-legacy; + } + map queryParamEncoding = {"emailIds": {style: FORM, explode: true}}; + resourcePath = resourcePath + check getPathForQueryParam(queries, queryParamEncoding); + map httpHeaders = getMapForHeaders(headerValues); + return self.clientEp->get(resourcePath, httpHeaders); + } + + # Update a marketing email. + # + # + emailId - The ID of the marketing email that should get updated + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request + # + payload - A marketing email object with properties that should overwrite the corresponding properties of the marketing email. + # + return - successful operation + resource isolated function patch [string emailId](EmailUpdateRequest payload, map headers = {}, *PatchEmailidQueries queries) returns PublicEmail|error { + string resourcePath = string `/${getEncodedUri(emailId)}`; + map headerValues = {...headers}; + if self.apiKeyConfig is ApiKeysConfig { + headerValues["private-app-legacy"] = self.apiKeyConfig?.private\-app\-legacy; + } + resourcePath = resourcePath + check getPathForQueryParam(queries); + map httpHeaders = getMapForHeaders(headerValues); + http:Request request = new; + json jsonBody = payload.toJson(); + request.setPayload(jsonBody, "application/json"); + return self.clientEp->patch(resourcePath, request, httpHeaders); + } + + # Create or update draft version + # + # + emailId - The marketing email ID. + # + headers - Headers to be sent with the request + # + payload - A marketing email object with properties that should overwrite the corresponding properties in the email's current draft. + # + return - successful operation + resource isolated function patch [string emailId]/draft(EmailUpdateRequest payload, map headers = {}) returns PublicEmail|error { + string resourcePath = string `/${getEncodedUri(emailId)}/draft`; + map headerValues = {...headers}; + if self.apiKeyConfig is ApiKeysConfig { + headerValues["private-app-legacy"] = self.apiKeyConfig?.private\-app\-legacy; + } + map httpHeaders = getMapForHeaders(headerValues); + http:Request request = new; + json jsonBody = payload.toJson(); + request.setPayload(jsonBody, "application/json"); + return self.clientEp->patch(resourcePath, request, httpHeaders); + } + + # Create a new marketing email. + # + # + headers - Headers to be sent with the request + # + return - successful operation + resource isolated function post .(EmailCreateRequest payload, map headers = {}) returns PublicEmail|error { + string resourcePath = string `/`; + map headerValues = {...headers}; + if self.apiKeyConfig is ApiKeysConfig { + headerValues["private-app-legacy"] = self.apiKeyConfig?.private\-app\-legacy; + } + map httpHeaders = getMapForHeaders(headerValues); + http:Request request = new; + json jsonBody = payload.toJson(); + request.setPayload(jsonBody, "application/json"); + return self.clientEp->post(resourcePath, request, httpHeaders); + } + + # Reset Draft + # + # + emailId - The marketing email ID. + # + headers - Headers to be sent with the request + # + return - No content + resource isolated function post [string emailId]/draft/reset(map headers = {}) returns http:Response|error { + string resourcePath = string `/${getEncodedUri(emailId)}/draft/reset`; + map headerValues = {...headers}; + if self.apiKeyConfig is ApiKeysConfig { + headerValues["private-app-legacy"] = self.apiKeyConfig?.private\-app\-legacy; + } + map httpHeaders = getMapForHeaders(headerValues); + http:Request request = new; + return self.clientEp->post(resourcePath, request, httpHeaders); + } + + # Publish or send a marketing email. + # + # + headers - Headers to be sent with the request + # + return - No content + resource isolated function post [string emailId]/publish(map headers = {}) returns http:Response|error { + string resourcePath = string `/${getEncodedUri(emailId)}/publish`; + map headerValues = {...headers}; + if self.apiKeyConfig is ApiKeysConfig { + headerValues["private-app-legacy"] = self.apiKeyConfig?.private\-app\-legacy; + } + map httpHeaders = getMapForHeaders(headerValues); + http:Request request = new; + return self.clientEp->post(resourcePath, request, httpHeaders); + } + + resource isolated function post [string emailId]/revisions/[int revisionId]/restore\-to\-draft(map headers = {}) returns PublicEmail|error { + string resourcePath = string `/${getEncodedUri(emailId)}/revisions/${getEncodedUri(revisionId)}/restore-to-draft`; + map headerValues = {...headers}; + if self.apiKeyConfig is ApiKeysConfig { + headerValues["private-app-legacy"] = self.apiKeyConfig?.private\-app\-legacy; + } + map httpHeaders = getMapForHeaders(headerValues); + http:Request request = new; + return self.clientEp->post(resourcePath, request, httpHeaders); + } + + # Restore a revision of a marketing email + # + # + emailId - The marketing email ID. + # + revisionId - The ID of a revision. + # + headers - Headers to be sent with the request + # + return - No content + resource isolated function post [string emailId]/revisions/[string revisionId]/restore(map headers = {}) returns http:Response|error { + string resourcePath = string `/${getEncodedUri(emailId)}/revisions/${getEncodedUri(revisionId)}/restore`; + map headerValues = {...headers}; + if self.apiKeyConfig is ApiKeysConfig { + headerValues["private-app-legacy"] = self.apiKeyConfig?.private\-app\-legacy; + } + map httpHeaders = getMapForHeaders(headerValues); + http:Request request = new; + return self.clientEp->post(resourcePath, request, httpHeaders); + } + + # Unpublish or cancel a marketing email. + # + # + headers - Headers to be sent with the request + # + return - No content + resource isolated function post [string emailId]/unpublish(map headers = {}) returns http:Response|error { + string resourcePath = string `/${getEncodedUri(emailId)}/unpublish`; + map headerValues = {...headers}; + if self.apiKeyConfig is ApiKeysConfig { + headerValues["private-app-legacy"] = self.apiKeyConfig?.private\-app\-legacy; + } + map httpHeaders = getMapForHeaders(headerValues); + http:Request request = new; + return self.clientEp->post(resourcePath, request, httpHeaders); + } + + resource isolated function post ab\-test/create\-variation(AbTestCreateRequestVNext payload, map headers = {}) returns PublicEmail|error { + string resourcePath = string `/ab-test/create-variation`; + map headerValues = {...headers}; + if self.apiKeyConfig is ApiKeysConfig { + headerValues["private-app-legacy"] = self.apiKeyConfig?.private\-app\-legacy; + } + map httpHeaders = getMapForHeaders(headerValues); + http:Request request = new; + json jsonBody = payload.toJson(); + request.setPayload(jsonBody, "application/json"); + return self.clientEp->post(resourcePath, request, httpHeaders); + } + + # Clone a marketing email. + # + # + headers - Headers to be sent with the request + # + return - successful operation + resource isolated function post clone(ContentCloneRequestVNext payload, map headers = {}) returns PublicEmail|error { + string resourcePath = string `/clone`; + map headerValues = {...headers}; + if self.apiKeyConfig is ApiKeysConfig { + headerValues["private-app-legacy"] = self.apiKeyConfig?.private\-app\-legacy; + } + map httpHeaders = getMapForHeaders(headerValues); + http:Request request = new; + json jsonBody = payload.toJson(); + request.setPayload(jsonBody, "application/json"); + return self.clientEp->post(resourcePath, request, httpHeaders); + } +} diff --git a/ballerina/tests/mock_service.bal b/ballerina/tests/mock_service.bal new file mode 100644 index 0000000..23e419e --- /dev/null +++ b/ballerina/tests/mock_service.bal @@ -0,0 +1,500 @@ +// Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com). +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import ballerina/http; + +service / on new http:Listener(8080) { + + resource function post .(http:Caller caller, http:Request req) returns error? { + // Simulate the response for creating a new email + json payload = check req.getJsonPayload(); + string subject = check payload.subject.ensureType(); + string emailName = check payload.'name.ensureType(); + json response = { + "id": "test-email-id", + "name": emailName, + "subject": subject, + "feedbackSurveyId": "survey-id", + "publishDate": "2023-01-01T10:00:00Z", + "isTransactional": false, + "language": "en-us", + "type": "MARKETING_SINGLE_SEND_API", + "content": { + "html": "

Test Email

", + "plainText": "Test Email" + }, + "businessUnitId": "business-unit-id", + "webversion": { + "url": "https://example.com/webversion", + "enabled": true + }, + "workflowNames": ["workflow1", "workflow2"], + "archived": false, + "createdAt": "2023-09-01T10:00:00Z", + "stats": { + "counters": { + "sent": 1, + "open": 0, + "delivered": 0, + "bounce": 1, + "unsubscribed": 0, + "click": 0, + "reply": 0, + "dropped": 0, + "selected": 1, + "spamreport": 0, + "suppressed": 0, + "hardbounced": 0, + "softbounced": 1, + "pending": 0, + "contactslost": 0, + "notsent": 0 + }, + "deviceBreakdown": { + "open_device_type": { + "computer": 0, + "mobile": 0, + "unknown": 0 + }, + "click_device_type": { + "computer": 0, + "mobile": 0, + "unknown": 0 + } + }, + "qualifierStats": {}, + "ratios": { + "clickratio": 0, + "clickthroughratio": 0, + "deliveredratio": 0, + "openratio": 0, + "replyratio": 0, + "unsubscribedratio": 0, + "spamreportratio": 0, + "bounceratio": 100, + "hardbounceratio": 0, + "softbounceratio": 100, + "contactslostratio": 0, + "pendingratio": 0, + "notsentratio": 0 + } + }, + "jitterSendTime": false, + "from": { + "email": "sender@example.com", + "name": "Sender Name" + }, + "state": "DRAFT", + "createdById": "user-id", + "updatedAt": "2025-01-01T10:00:00Z", + "rssData": { + "feedUrl": "https://example.com/rss", + "feedTitle": "RSS Feed" + }, + "publishedAt": "2023-10-01T10:00:00Z", + "publishedById": "publisher-id", + "isPublished": true, + "testing": { + "abTest": true, + "testName": "A/B Test" + }, + "updatedById": "updater-id", + "folderId": 123, + "subscriptionDetails": { + "officeLocationId": "office-id", + "preferencesGroupId": "preferences-group-id", + "subscriptionId": "subscription-id" + }, + "activeDomain": "example.com", + "campaign": "campaign-id", + "to": { + "contactIds": { + "included": ["contact1", "contact2"], + "excluded": ["contact3"] + }, + "contactLists": { + "included": ["list1", "list2"], + "excluded": ["list3"] + }, + "limitSendFrequency": true, + "suppressGraymail": false + }, + "subcategory": "batch", + "campaignName": "Campaign Name", + "sendOnPublish": true + }; + + check caller->respond(response); + } + + resource function post clone(http:Caller caller, http:Request req) returns error? { + // Simulate the response for creating a new email + json payload = check req.getJsonPayload(); + string emailName = check payload.'cloneName.ensureType(); + string emailId = check payload.id.ensureType(); + json response = { + "id": emailId + "_clone", + "name": emailName, + "subject": "Cloned email from mock_service!", + "feedbackSurveyId": "survey-id", + "publishDate": "2023-01-01T10:00:00Z", + "isTransactional": false, + "language": "en-us", + "type": "MARKETING_SINGLE_SEND_API", + "content": { + "html": "

Test Email

", + "plainText": "Test Email" + }, + "businessUnitId": "business-unit-id", + "webversion": { + "url": "https://example.com/webversion", + "enabled": true + }, + "workflowNames": ["workflow1", "workflow2"], + "archived": false, + "createdAt": "2023-09-01T10:00:00Z", + "stats": { + "counters": { + "sent": 1, + "open": 0, + "delivered": 0, + "bounce": 1, + "unsubscribed": 0, + "click": 0, + "reply": 0, + "dropped": 0, + "selected": 1, + "spamreport": 0, + "suppressed": 0, + "hardbounced": 0, + "softbounced": 1, + "pending": 0, + "contactslost": 0, + "notsent": 0 + }, + "deviceBreakdown": { + "open_device_type": { + "computer": 0, + "mobile": 0, + "unknown": 0 + }, + "click_device_type": { + "computer": 0, + "mobile": 0, + "unknown": 0 + } + }, + "qualifierStats": {}, + "ratios": { + "clickratio": 0, + "clickthroughratio": 0, + "deliveredratio": 0, + "openratio": 0, + "replyratio": 0, + "unsubscribedratio": 0, + "spamreportratio": 0, + "bounceratio": 100, + "hardbounceratio": 0, + "softbounceratio": 100, + "contactslostratio": 0, + "pendingratio": 0, + "notsentratio": 0 + } + }, + "jitterSendTime": false, + "from": { + "email": "sender@example.com", + "name": "Sender Name" + }, + "state": "DRAFT", + "createdById": "user-id", + "updatedAt": "2025-01-01T10:00:00Z", + "rssData": { + "feedUrl": "https://example.com/rss", + "feedTitle": "RSS Feed" + }, + "publishedAt": "2023-10-01T10:00:00Z", + "publishedById": "publisher-id", + "isPublished": true, + "testing": { + "abTest": true, + "testName": "A/B Test" + }, + "updatedById": "updater-id", + "folderId": 123, + "subscriptionDetails": { + "officeLocationId": "office-id", + "preferencesGroupId": "preferences-group-id", + "subscriptionId": "subscription-id" + }, + "activeDomain": "example.com", + "campaign": "campaign-id", + "to": { + "contactIds": { + "included": ["contact1", "contact2"], + "excluded": ["contact3"] + }, + "contactLists": { + "included": ["list1", "list2"], + "excluded": ["list3"] + }, + "limitSendFrequency": true, + "suppressGraymail": false + }, + "subcategory": "batch", + "campaignName": "Campaign Name", + "sendOnPublish": true + }; + + check caller->respond(response); + } + + resource function get .(http:Caller caller, http:Request req) returns error? { + // Simulate the response for listing all emails + json response = { + total: 2, + results: [ + { + "id": "test-email-id", + "name": "emailName", + "subject": "subject", + "feedbackSurveyId": "survey-id", + "publishDate": "2023-01-01T10:00:00Z", + "isTransactional": false, + "language": "en-us", + "type": "MARKETING_SINGLE_SEND_API", + "content": { + "html": "

Test Email

", + "plainText": "Test Email" + }, + "businessUnitId": "business-unit-id", + "webversion": { + "url": "https://example.com/webversion", + "enabled": true + }, + "workflowNames": ["workflow1", "workflow2"], + "archived": false, + "createdAt": "2023-09-01T10:00:00Z", + "stats": { + "counters": { + "sent": 1, + "open": 0, + "delivered": 0, + "bounce": 1, + "unsubscribed": 0, + "click": 0, + "reply": 0, + "dropped": 0, + "selected": 1, + "spamreport": 0, + "suppressed": 0, + "hardbounced": 0, + "softbounced": 1, + "pending": 0, + "contactslost": 0, + "notsent": 0 + }, + "deviceBreakdown": { + "open_device_type": { + "computer": 0, + "mobile": 0, + "unknown": 0 + }, + "click_device_type": { + "computer": 0, + "mobile": 0, + "unknown": 0 + } + }, + "qualifierStats": {}, + "ratios": { + "clickratio": 0, + "clickthroughratio": 0, + "deliveredratio": 0, + "openratio": 0, + "replyratio": 0, + "unsubscribedratio": 0, + "spamreportratio": 0, + "bounceratio": 100, + "hardbounceratio": 0, + "softbounceratio": 100, + "contactslostratio": 0, + "pendingratio": 0, + "notsentratio": 0 + } + }, + "jitterSendTime": false, + "from": { + "email": "sender@example.com", + "name": "Sender Name" + }, + "state": "DRAFT", + "createdById": "user-id", + "updatedAt": "2025-01-01T10:00:00Z", + "rssData": { + "feedUrl": "https://example.com/rss", + "feedTitle": "RSS Feed" + }, + "publishedAt": "2023-10-01T10:00:00Z", + "publishedById": "publisher-id", + "isPublished": true, + "testing": { + "abTest": true, + "testName": "A/B Test" + }, + "updatedById": "updater-id", + "folderId": 123, + "subscriptionDetails": { + "officeLocationId": "office-id", + "preferencesGroupId": "preferences-group-id", + "subscriptionId": "subscription-id" + }, + "activeDomain": "example.com", + "campaign": "campaign-id", + "to": { + "contactIds": { + "included": ["contact1", "contact2"], + "excluded": ["contact3"] + }, + "contactLists": { + "included": ["list1", "list2"], + "excluded": ["list3"] + }, + "limitSendFrequency": true, + "suppressGraymail": false + }, + "subcategory": "batch", + "campaignName": "Campaign Name", + "sendOnPublish": true + }, + { + "id": "test-email-id", + "name": "emailName", + "subject": "subject", + "feedbackSurveyId": "survey-id", + "publishDate": "2023-01-01T10:00:00Z", + "isTransactional": false, + "language": "en-us", + "type": "MARKETING_SINGLE_SEND_API", + "content": { + "html": "

Test Email

", + "plainText": "Test Email" + }, + "businessUnitId": "business-unit-id", + "webversion": { + "url": "https://example.com/webversion", + "enabled": true + }, + "workflowNames": ["workflow1", "workflow2"], + "archived": false, + "createdAt": "2023-09-01T10:00:00Z", + "stats": { + "counters": { + "sent": 1, + "open": 0, + "delivered": 0, + "bounce": 1, + "unsubscribed": 0, + "click": 0, + "reply": 0, + "dropped": 0, + "selected": 1, + "spamreport": 0, + "suppressed": 0, + "hardbounced": 0, + "softbounced": 1, + "pending": 0, + "contactslost": 0, + "notsent": 0 + }, + "deviceBreakdown": { + "open_device_type": { + "computer": 0, + "mobile": 0, + "unknown": 0 + }, + "click_device_type": { + "computer": 0, + "mobile": 0, + "unknown": 0 + } + }, + "qualifierStats": {}, + "ratios": { + "clickratio": 0, + "clickthroughratio": 0, + "deliveredratio": 0, + "openratio": 0, + "replyratio": 0, + "unsubscribedratio": 0, + "spamreportratio": 0, + "bounceratio": 100, + "hardbounceratio": 0, + "softbounceratio": 100, + "contactslostratio": 0, + "pendingratio": 0, + "notsentratio": 0 + } + }, + "jitterSendTime": false, + "from": { + "email": "sender@example.com", + "name": "Sender Name" + }, + "state": "DRAFT", + "createdById": "user-id", + "updatedAt": "2025-01-01T10:00:00Z", + "rssData": { + "feedUrl": "https://example.com/rss", + "feedTitle": "RSS Feed" + }, + "publishedAt": "2023-10-01T10:00:00Z", + "publishedById": "publisher-id", + "isPublished": true, + "testing": { + "abTest": true, + "testName": "A/B Test" + }, + "updatedById": "updater-id", + "folderId": 123, + "subscriptionDetails": { + "officeLocationId": "office-id", + "preferencesGroupId": "preferences-group-id", + "subscriptionId": "subscription-id" + }, + "activeDomain": "example.com", + "campaign": "campaign-id", + "to": { + "contactIds": { + "included": ["contact1", "contact2"], + "excluded": ["contact3"] + }, + "contactLists": { + "included": ["list1", "list2"], + "excluded": ["list3"] + }, + "limitSendFrequency": true, + "suppressGraymail": false + }, + "subcategory": "batch", + "campaignName": "Campaign Name", + "sendOnPublish": true + } + ] + }; + check caller->respond(response); + } +} diff --git a/ballerina/tests/test.bal b/ballerina/tests/test.bal new file mode 100644 index 0000000..91b72af --- /dev/null +++ b/ballerina/tests/test.bal @@ -0,0 +1,239 @@ +// Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com). +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import ballerina/http; +import ballerina/oauth2; +import ballerina/test; +import ballerina/time; + +configurable string clientId = ?; +configurable string clientSecret = ?; +configurable string refreshToken = ?; + +// isLiveSever is set to false by default, set to true in Config.toml +configurable boolean isLiveServer = false; +configurable string serviceUrl = isLiveServer ? "https://api.hubapi.com/marketing/v3/emails" : "http://localhost:8080"; + +OAuth2RefreshTokenGrantConfig auth = { + clientId, + clientSecret, + refreshToken, + credentialBearer: oauth2:POST_BODY_BEARER // this line should be added to create auth object. +}; + +final Client hubspotClient = check new ({auth}, serviceUrl); + +// Change this value and test +final int days = 40; + +string testEmailId = ""; +string cloneEmailId = ""; +string draftSubject = "New draft subject"; + +@test:Config { + groups: ["mock_tests"] +} +public function testCreateEmailEp() returns error? { + // Create a new email. Will throw error if the response type is not PublicEmail + PublicEmail response = check hubspotClient->/.post({ + name: "Test email using API", + subject: "Marketing Email using API!" + }); + + // Store the id of the created email for use in other testcases + testEmailId = response.id; +} + +@test:Config { + groups: ["mock_tests"], + dependsOn: [testCreateEmailEp] +} +public function testCloneEmailEp() returns error? { + // Clone the email created in testCreateEmailEp testcase. + // Will throw an error if return type does not match PublicEmail + PublicEmail response = check hubspotClient->/clone.post({ + id: testEmailId, + cloneName: "Cloned Email" + }); + + // Store the cloned email's id for use in other testcases + cloneEmailId = response.id; +} + +@test:Config { + dependsOn: [testCreateEmailEp, testCloneEmailEp], + enable: isLiveServer +} +public function testRetrieveEmailEp() returns error? { + // Retrieve test email and cloned email + PublicEmail response = check hubspotClient->/[testEmailId]; + PublicEmail clone_response = check hubspotClient->/[cloneEmailId]; + test:assertEquals(response.id, testEmailId); + test:assertEquals(clone_response.id, cloneEmailId); +} + +@test:Config { + dependsOn: [testCreateEmailEp], + enable: isLiveServer +} +public function testCreateDraftEp() returns error? { + // Create a draft of the email + PublicEmail response = check hubspotClient->/[testEmailId]/draft.patch({ + subject: draftSubject + }); + + // Check that the subject has been updated correctly + test:assertEquals(response.subject, draftSubject); + + // Get the draft using the get draft endpoint + PublicEmail draftResponse = check hubspotClient->/[testEmailId]/draft(); + + // Retrieve the original email + PublicEmail originalResponse = check hubspotClient->/[testEmailId]; + + // Assert that the subject is different in draft + test:assertNotEquals(draftResponse.subject, originalResponse.subject); +} + +@test:Config { + dependsOn: [testCreateDraftEp], + enable: isLiveServer +} +public function testResetDraftEp() returns error? { + http:Response response = check hubspotClient->/[testEmailId]/draft/reset.post(); + + // Assert that the status code is 204 + test:assertEquals(response.statusCode, 204); + + // Retrieve the email from the draft endpoint and check that the subject is changed back + PublicEmail draftResponse = check hubspotClient->/[testEmailId]/draft(); + test:assertNotEquals(draftResponse.subject, draftSubject); +} + +@test:Config { + dependsOn: [testCreateDraftEp], + enable: isLiveServer +} +public function testUpdateandRestoreEps() returns error? { + // Update email subject + PublicEmail response = check hubspotClient->/[testEmailId].patch({ + subject: "Updated Subject" + }); + + // Retrieve the email and check the subject + PublicEmail updatedResponse = check hubspotClient->/[testEmailId]; + test:assertEquals(updatedResponse.subject, "Updated Subject"); + + // Update the subject again to make another revision + PublicEmail updatedResposne = check hubspotClient->/[testEmailId].patch({ + subject: "Updated Subject, again!" + }); + + // Retrieve the email and check the subject + PublicEmail twiceUpdatedResponse = check hubspotClient->/[testEmailId]; + test:assertEquals(twiceUpdatedResponse.subject, "Updated Subject, again!"); + + // Get all revisions are fetched using the /revisions endpoint + CollectionResponseWithTotalVersionPublicEmail allRevisions = check hubspotClient->/[testEmailId]/revisions(); + + // Restore back to the first revision + http:Response firstRevisionRestored = check hubspotClient->/[testEmailId]/revisions/[allRevisions.results[1].id]/restore.post({}); + + // Check that the response status is 204 + test:assertEquals(firstRevisionRestored.statusCode, 204); + + // Verify that the subject is same as it was in the first revision + PublicEmail restoredVersion = check hubspotClient->/[testEmailId]; + test:assertEquals(restoredVersion.subject, "Updated Subject"); + +} + +@test:Config { + groups: ["mock_tests"], + dependsOn: [testCreateEmailEp, testCloneEmailEp] +} +public function testEmailsEp() returns error? { + CollectionResponseWithTotalPublicEmailForwardPaging response = check hubspotClient->/(); + test:assertEquals(response.total, response.results.length()); +} + +@test:Config { + dependsOn: [testCreateEmailEp], + enable: isLiveServer +} +public function testGetDraftEp() returns error? { + PublicEmail response = check hubspotClient->/[testEmailId]/draft(); + test:assertEquals(response.id, testEmailId); +} + +@test:Config { + dependsOn: [testCreateEmailEp], + enable: isLiveServer +} +isolated function testListEp() returns error? { + AggregateEmailStatistics response = check hubspotClient->/statistics/list({}, + { + startTimestamp: time:utcToString(time:utcAddSeconds(time:utcNow(), -86400 * days)), + endTimestamp: time:utcToString(time:utcNow()) + } + ); + + // Check that each part of the response response is not null + test:assertTrue(response.aggregate !is ()); + test:assertTrue(response.campaignAggregations !is ()); + test:assertTrue(response.emails !is ()); +} + +@test:Config { + dependsOn: [testCreateEmailEp], + enable: isLiveServer +} +isolated function testHistogramEp() returns error? { + CollectionResponseWithTotalEmailStatisticIntervalNoPaging response = check + hubspotClient->/statistics/histogram({}, + { + startTimestamp: time:utcToString(time:utcAddSeconds(time:utcNow(), -86400 * days)), + endTimestamp: time:utcToString(time:utcNow()), + interval: "DAY" + } + ); + + // If there are no emails sent within the time span, the response will be of length 1 + // Else it should be of length equal to interval * timespan + 1 + // Example: Length of results array should be 24*3 if the interval is HOUR and + // duration between startTimestamp and endTimestamp is 3 days + // In this case it should be equal to number of days + 1 + if response.results.length() > 1 { + test:assertEquals(response.results.length(), days + 1); + test:assertEquals(response.total, days + 1); + } +} + +@test:Config { + dependsOn: [testCloneEmailEp, testUpdateandRestoreEps, testEmailsEp, testRetrieveEmailEp, testHistogramEp], + enable: isLiveServer +} +public function testDeleteEndpoint() returns error? { + // Delete the created email and its clone + http:Response response_test_email = check hubspotClient->/[testEmailId].delete(); + http:Response response_clone_email = check hubspotClient->/[cloneEmailId].delete(); + + // Check if the response status is 204 + test:assertEquals(response_test_email.statusCode, 204); + + // Check if the response status is 204 + test:assertEquals(response_clone_email.statusCode, 204); +} diff --git a/ballerina/types.bal b/ballerina/types.bal new file mode 100644 index 0000000..4398060 --- /dev/null +++ b/ballerina/types.bal @@ -0,0 +1,596 @@ +// AUTO-GENERATED FILE. DO NOT MODIFY. +// This file is auto-generated by the Ballerina OpenAPI tool. + +// Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com). +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import ballerina/http; + +# Data structure representing the to fields of the email. +public type PublicEmailToDetails record { + PublicEmailRecipients contactIlsLists?; + # + boolean limitSendFrequency?; + # + boolean suppressGraymail?; + PublicEmailRecipients contactIds?; + PublicEmailRecipients contactLists?; +}; + +public type PublicDividerStyleSettings record { + record {} color?; + string lineType?; + int:Signed32 height?; +}; + +# Response object for collections of marketing emails with pagination information. +public type CollectionResponseWithTotalVersionPublicEmail record { + # Total number of content emails. + int:Signed32 total; + Paging paging?; + # Collection of emails. + VersionPublicEmail[] results; +}; + +# A marketing email +public type PublicEmail record { + # The ID of the feedback survey linked to the email. + string feedbackSurveyId?; + # The subject of the email. + string subject; + # The date and time the email is scheduled for, in ISO8601 representation. This is only used in local time or scheduled emails. + string publishDate?; + # Returns whether the email is a transactional email or not. This is read only. + boolean isTransactional?; + "af"|"af-na"|"af-za"|"agq"|"agq-cm"|"ak"|"ak-gh"|"am"|"am-et"|"ann"|"ann-ng"|"ar"|"ar-001"|"ar-ae"|"ar-bh"|"ar-dj"|"ar-dz"|"ar-eg"|"ar-eh"|"ar-er"|"ar-il"|"ar-iq"|"ar-jo"|"ar-km"|"ar-kw"|"ar-lb"|"ar-ly"|"ar-ma"|"ar-mr"|"ar-om"|"ar-ps"|"ar-qa"|"ar-sa"|"ar-sd"|"ar-so"|"ar-ss"|"ar-sy"|"ar-td"|"ar-tn"|"ar-ye"|"as"|"asa"|"asa-tz"|"ast"|"ast-es"|"as-in"|"az"|"az-az"|"bas"|"bas-cm"|"be"|"bem"|"bem-zm"|"bez"|"bez-tz"|"be-by"|"bg"|"bgc"|"bgc-in"|"bg-bg"|"bho"|"bho-in"|"bm"|"bm-ml"|"bn"|"bn-bd"|"bn-in"|"bo"|"bo-cn"|"bo-in"|"br"|"brx"|"brx-in"|"br-fr"|"bs"|"bs-ba"|"ca"|"ca-ad"|"ca-es"|"ca-fr"|"ca-it"|"ccp"|"ccp-bd"|"ccp-in"|"ce"|"ceb"|"ceb-ph"|"ce-ru"|"cgg"|"cgg-ug"|"chr"|"chr-us"|"ckb"|"ckb-iq"|"ckb-ir"|"cs"|"cs-cz"|"cu"|"cu-ru"|"cv"|"cv-ru"|"cy"|"cy-gb"|"da"|"dav"|"dav-ke"|"da-dk"|"da-gl"|"de"|"de-at"|"de-be"|"de-ch"|"de-de"|"de-gr"|"de-it"|"de-li"|"de-lu"|"dje"|"dje-ne"|"doi"|"doi-in"|"dsb"|"dsb-de"|"dua"|"dua-cm"|"dyo"|"dyo-sn"|"dz"|"dz-bt"|"ebu"|"ebu-ke"|"ee"|"ee-gh"|"ee-tg"|"el"|"el-cy"|"el-gr"|"en"|"en-001"|"en-150"|"en-ae"|"en-ag"|"en-ai"|"en-as"|"en-at"|"en-au"|"en-bb"|"en-be"|"en-bi"|"en-bm"|"en-bs"|"en-bw"|"en-bz"|"en-ca"|"en-cc"|"en-ch"|"en-ck"|"en-cm"|"en-cn"|"en-cx"|"en-cy"|"en-de"|"en-dg"|"en-dk"|"en-dm"|"en-ee"|"en-er"|"en-fi"|"en-fj"|"en-fk"|"en-fm"|"en-fr"|"en-gb"|"en-gd"|"en-gg"|"en-gh"|"en-gi"|"en-gm"|"en-gu"|"en-gy"|"en-hk"|"en-ie"|"en-il"|"en-im"|"en-in"|"en-io"|"en-je"|"en-jm"|"en-ke"|"en-ki"|"en-kn"|"en-ky"|"en-lc"|"en-lr"|"en-ls"|"en-lu"|"en-mg"|"en-mh"|"en-mo"|"en-mp"|"en-ms"|"en-mt"|"en-mu"|"en-mv"|"en-mw"|"en-mx"|"en-my"|"en-na"|"en-nf"|"en-ng"|"en-nl"|"en-nr"|"en-nu"|"en-nz"|"en-pg"|"en-ph"|"en-pk"|"en-pn"|"en-pr"|"en-pw"|"en-rw"|"en-sb"|"en-sc"|"en-sd"|"en-se"|"en-sg"|"en-sh"|"en-si"|"en-sl"|"en-ss"|"en-sx"|"en-sz"|"en-tc"|"en-tk"|"en-to"|"en-tt"|"en-tv"|"en-tz"|"en-ug"|"en-um"|"en-us"|"en-vc"|"en-vg"|"en-vi"|"en-vu"|"en-ws"|"en-za"|"en-zm"|"en-zw"|"eo"|"eo-001"|"es"|"es-419"|"es-ar"|"es-bo"|"es-br"|"es-bz"|"es-cl"|"es-co"|"es-cr"|"es-cu"|"es-do"|"es-ea"|"es-ec"|"es-es"|"es-gq"|"es-gt"|"es-hn"|"es-ic"|"es-mx"|"es-ni"|"es-pa"|"es-pe"|"es-ph"|"es-pr"|"es-py"|"es-sv"|"es-us"|"es-uy"|"es-ve"|"et"|"et-ee"|"eu"|"eu-es"|"ewo"|"ewo-cm"|"fa"|"fa-af"|"fa-ir"|"ff"|"ff-bf"|"ff-cm"|"ff-gh"|"ff-gm"|"ff-gn"|"ff-gw"|"ff-lr"|"ff-mr"|"ff-ne"|"ff-ng"|"ff-sl"|"ff-sn"|"fi"|"fil"|"fil-ph"|"fi-fi"|"fo"|"fo-dk"|"fo-fo"|"fr"|"frr"|"frr-de"|"fr-be"|"fr-bf"|"fr-bi"|"fr-bj"|"fr-bl"|"fr-ca"|"fr-cd"|"fr-cf"|"fr-cg"|"fr-ch"|"fr-ci"|"fr-cm"|"fr-dj"|"fr-dz"|"fr-fr"|"fr-ga"|"fr-gf"|"fr-gn"|"fr-gp"|"fr-gq"|"fr-ht"|"fr-km"|"fr-lu"|"fr-ma"|"fr-mc"|"fr-mf"|"fr-mg"|"fr-ml"|"fr-mq"|"fr-mr"|"fr-mu"|"fr-nc"|"fr-ne"|"fr-pf"|"fr-pm"|"fr-re"|"fr-rw"|"fr-sc"|"fr-sn"|"fr-sy"|"fr-td"|"fr-tg"|"fr-tn"|"fr-vu"|"fr-wf"|"fr-yt"|"fur"|"fur-it"|"fy"|"fy-nl"|"ga"|"ga-gb"|"ga-ie"|"gd"|"gd-gb"|"gl"|"gl-es"|"gsw"|"gsw-ch"|"gsw-fr"|"gsw-li"|"gu"|"guz"|"guz-ke"|"gu-in"|"gv"|"gv-im"|"ha"|"haw"|"haw-us"|"ha-gh"|"ha-ne"|"ha-ng"|"he"|"hi"|"hi-in"|"hr"|"hr-ba"|"hr-hr"|"hsb"|"hsb-de"|"hu"|"hu-hu"|"hy"|"hy-am"|"ia"|"ia-001"|"id"|"ig"|"ig-ng"|"ii"|"ii-cn"|"id-id"|"is"|"is-is"|"it"|"it-ch"|"it-it"|"it-sm"|"it-va"|"he-il"|"ja"|"ja-jp"|"jgo"|"jgo-cm"|"yi"|"yi-001"|"jmc"|"jmc-tz"|"jv"|"jv-id"|"ka"|"kab"|"kab-dz"|"kam"|"kam-ke"|"ka-ge"|"kde"|"kde-tz"|"kea"|"kea-cv"|"kgp"|"kgp-br"|"khq"|"khq-ml"|"ki"|"ki-ke"|"kk"|"kkj"|"kkj-cm"|"kk-kz"|"kl"|"kln"|"kln-ke"|"kl-gl"|"km"|"km-kh"|"kn"|"kn-in"|"ko"|"kok"|"kok-in"|"ko-kp"|"ko-kr"|"ks"|"ksb"|"ksb-tz"|"ksf"|"ksf-cm"|"ksh"|"ksh-de"|"ks-in"|"ku"|"ku-tr"|"kw"|"kw-gb"|"ky"|"ky-kg"|"lag"|"lag-tz"|"lb"|"lb-lu"|"lg"|"lg-ug"|"lkt"|"lkt-us"|"ln"|"ln-ao"|"ln-cd"|"ln-cf"|"ln-cg"|"lo"|"lo-la"|"lrc"|"lrc-iq"|"lrc-ir"|"lt"|"lt-lt"|"lu"|"luo"|"luo-ke"|"luy"|"luy-ke"|"lu-cd"|"lv"|"lv-lv"|"mai"|"mai-in"|"mas"|"mas-ke"|"mas-tz"|"mdf"|"mdf-ru"|"mer"|"mer-ke"|"mfe"|"mfe-mu"|"mg"|"mgh"|"mgh-mz"|"mgo"|"mgo-cm"|"mg-mg"|"mi"|"mi-nz"|"mk"|"mk-mk"|"ml"|"ml-in"|"mn"|"mni"|"mni-in"|"mn-mn"|"mr"|"mr-in"|"ms"|"ms-bn"|"ms-id"|"ms-my"|"ms-sg"|"mt"|"mt-mt"|"mua"|"mua-cm"|"my"|"my-mm"|"mzn"|"mzn-ir"|"naq"|"naq-na"|"nb"|"nb-no"|"nb-sj"|"nd"|"nds"|"nds-de"|"nds-nl"|"nd-zw"|"ne"|"ne-in"|"ne-np"|"nl"|"nl-aw"|"nl-be"|"nl-bq"|"nl-ch"|"nl-cw"|"nl-lu"|"nl-nl"|"nl-sr"|"nl-sx"|"nmg"|"nmg-cm"|"nn"|"nnh"|"nnh-cm"|"nn-no"|"no"|"no-no"|"nus"|"nus-ss"|"nyn"|"nyn-ug"|"oc"|"oc-es"|"oc-fr"|"om"|"om-et"|"om-ke"|"or"|"or-in"|"os"|"os-ge"|"os-ru"|"pa"|"pa-in"|"pa-pk"|"pcm"|"pcm-ng"|"pis"|"pis-sb"|"pl"|"pl-pl"|"prg"|"prg-001"|"ps"|"ps-af"|"ps-pk"|"pt"|"pt-ao"|"pt-br"|"pt-ch"|"pt-cv"|"pt-gq"|"pt-gw"|"pt-lu"|"pt-mo"|"pt-mz"|"pt-pt"|"pt-st"|"pt-tl"|"qu"|"qu-bo"|"qu-ec"|"qu-pe"|"raj"|"raj-in"|"rm"|"rm-ch"|"rn"|"rn-bi"|"ro"|"rof"|"rof-tz"|"ro-md"|"ro-ro"|"ru"|"ru-by"|"ru-kg"|"ru-kz"|"ru-md"|"ru-ru"|"ru-ua"|"rw"|"rwk"|"rwk-tz"|"rw-rw"|"sa"|"sah"|"sah-ru"|"saq"|"saq-ke"|"sat"|"sat-in"|"sa-in"|"sbp"|"sbp-tz"|"sc"|"sc-it"|"sd"|"sd-in"|"sd-pk"|"se"|"seh"|"seh-mz"|"ses"|"ses-ml"|"se-fi"|"se-no"|"se-se"|"sg"|"sg-cf"|"shi"|"shi-ma"|"si"|"si-lk"|"sk"|"sk-sk"|"sl"|"sl-si"|"smn"|"smn-fi"|"sms"|"sms-fi"|"sn"|"sn-zw"|"so"|"so-dj"|"so-et"|"so-ke"|"so-so"|"sq"|"sq-al"|"sq-mk"|"sq-xk"|"sr"|"sr-ba"|"sr-cs"|"sr-me"|"sr-rs"|"sr-xk"|"su"|"su-id"|"sv"|"sv-ax"|"sv-fi"|"sv-se"|"sw"|"sw-cd"|"sw-ke"|"sw-tz"|"sw-ug"|"sy"|"ta"|"ta-in"|"ta-lk"|"ta-my"|"ta-sg"|"te"|"teo"|"teo-ke"|"teo-ug"|"te-in"|"tg"|"tg-tj"|"th"|"th-th"|"ti"|"ti-er"|"ti-et"|"tk"|"tk-tm"|"tl"|"to"|"tok"|"tok-001"|"to-to"|"tr"|"tr-cy"|"tr-tr"|"tt"|"tt-ru"|"twq"|"twq-ne"|"tzm"|"tzm-ma"|"ug"|"ug-cn"|"uk"|"uk-ua"|"ur"|"ur-in"|"ur-pk"|"uz"|"uz-af"|"uz-uz"|"vai"|"vai-lr"|"vi"|"vi-vn"|"vo"|"vo-001"|"vun"|"vun-tz"|"wae"|"wae-ch"|"wo"|"wo-sn"|"xh"|"xh-za"|"xog"|"xog-ug"|"yav"|"yav-cm"|"yo"|"yo-bj"|"yo-ng"|"yrl"|"yrl-br"|"yrl-co"|"yrl-ve"|"yue"|"yue-cn"|"yue-hk"|"zgh"|"zgh-ma"|"zh"|"zh-cn"|"zh-hans"|"zh-hant"|"zh-hk"|"zh-mo"|"zh-sg"|"zh-tw"|"zu"|"zu-za" language?; + # The email type, this is derived from other properties on the email such as subcategory. + "AB_EMAIL"|"BATCH_EMAIL"|"LOCALTIME_EMAIL"|"AUTOMATED_AB_EMAIL"|"BLOG_EMAIL"|"BLOG_EMAIL_CHILD"|"RSS_EMAIL"|"RSS_EMAIL_CHILD"|"RESUBSCRIBE_EMAIL"|"OPTIN_EMAIL"|"OPTIN_FOLLOWUP_EMAIL"|"AUTOMATED_EMAIL"|"FEEDBACK_CES_EMAIL"|"FEEDBACK_CUSTOM_EMAIL"|"FEEDBACK_CUSTOM_SURVEY_EMAIL"|"FEEDBACK_NPS_EMAIL"|"FOLLOWUP_EMAIL"|"LEADFLOW_EMAIL"|"SINGLE_SEND_API"|"MARKETING_SINGLE_SEND_API"|"SMTP_TOKEN"|"TICKET_EMAIL"|"MEMBERSHIP_REGISTRATION_EMAIL"|"MEMBERSHIP_PASSWORD_SAVED_EMAIL"|"MEMBERSHIP_PASSWORD_RESET_EMAIL"|"MEMBERSHIP_EMAIL_VERIFICATION_EMAIL"|"MEMBERSHIP_PASSWORDLESS_AUTH_EMAIL"|"MEMBERSHIP_REGISTRATION_FOLLOW_UP_EMAIL"|"MEMBERSHIP_OTP_LOGIN_EMAIL"|"MEMBERSHIP_FOLLOW_UP_EMAIL"|"MEMBERSHIP_VERIFICATION_EMAIL" 'type?; + PublicEmailContent content; + string businessUnitId?; + PublicWebversionDetails webversion?; + string[] workflowNames?; + # Determines if the email is archived or not. + boolean archived?; + # The date and time of the email's creation, in ISO8601 representation. + string createdAt?; + EmailStatisticsData stats?; + boolean jitterSendTime?; + PublicEmailFromDetails 'from; + # The email ID. + string id; + # The email state. + "AUTOMATED"|"AUTOMATED_DRAFT"|"AUTOMATED_SENDING"|"AUTOMATED_FOR_FORM"|"AUTOMATED_FOR_FORM_BUFFER"|"AUTOMATED_FOR_FORM_DRAFT"|"AUTOMATED_FOR_FORM_LEGACY"|"BLOG_EMAIL_DRAFT"|"BLOG_EMAIL_PUBLISHED"|"DRAFT"|"DRAFT_AB"|"DRAFT_AB_VARIANT"|"ERROR"|"LOSER_AB_VARIANT"|"PAGE_STUB"|"PRE_PROCESSING"|"PROCESSING"|"PUBLISHED"|"PUBLISHED_AB"|"PUBLISHED_AB_VARIANT"|"PUBLISHED_OR_SCHEDULED"|"RSS_TO_EMAIL_DRAFT"|"RSS_TO_EMAIL_PUBLISHED"|"SCHEDULED"|"SCHEDULED_AB"|"SCHEDULED_OR_PUBLISHED"|"AUTOMATED_AB"|"AUTOMATED_AB_VARIANT"|"AUTOMATED_DRAFT_AB"|"AUTOMATED_DRAFT_ABVARIANT"|"AUTOMATED_LOSER_ABVARIANT" state; + # The id of the user who created the email. + string createdById?; + # The date and time of the last update to the email, in ISO8601 representation. + string updatedAt?; + PublicRssEmailDetails rssData?; + # The date and time the email was published at, in ISO8601 representation. + string publishedAt?; + string publishedById?; + # Returns the published status of the email. This is read only. + boolean isPublished?; + PublicEmailTestingDetails testing?; + # The id of the user who last updated the email. + string updatedById?; + int folderId?; + PublicEmailSubscriptionDetails subscriptionDetails?; + string deletedAt?; + # The name of the email, as displayed on the email dashboard. + string name; + # The active domain of the email. + string activeDomain?; + # The campaign id on the email. + string campaign?; + PublicEmailToDetails to; + # The email subcategory. + string subcategory; + string campaignName?; + # Determines whether the email will be sent immediately on publish. + boolean sendOnPublish?; +}; + +# Represents the Queries record for the operation: get-/{emailId}/revisions +public type GetEmailidRevisionsQueries record { + # The cursor token value to get the previous set of results. You can get this from the `paging.prev.before` JSON property of a paged response containing more results. + string before?; + # The maximum number of results to return. Default is 100. + int:Signed32 'limit?; + # The cursor token value to get the next set of results. You can get this from the `paging.next.after` JSON property of a paged response containing more results. + string after?; +}; + +# Represents the Queries record for the operation: patch-/{emailId} +public type PatchEmailidQueries record { + # Whether to return only results that have been archived. + boolean archived?; +}; + +# Request body object for creating A/B tests. +public type AbTestCreateRequestVNext record { + string variationName; + # ID of the object to test. + string contentId; +}; + +# Represents the Queries record for the operation: get-/{emailId} +public type GetEmailidQueries record { + boolean workflowNames?; + # Whether to return only results that have been archived. + boolean archived?; + boolean marketingCampaignNames?; + # Include statistics with email + boolean includeStats?; + string[] includedProperties?; +}; + +public type EmailStatisticInterval record { + Interval interval?; + EmailStatisticsData aggregations?; +}; + +# Response object for collections of marketing emails with pagination information. +public type CollectionResponseWithTotalPublicEmailForwardPaging record { + # Total number of content emails. + int:Signed32 total; + ForwardPaging paging?; + # Collection of emails. + PublicEmail[] results; +}; + +public type ForwardPaging record { + NextPage next?; +}; + +public type PublicButtonStyleSettings record { + string backgroundColor?; + PublicFontStyle fontStyle?; + int:Signed32 cornerRadius?; +}; + +# Data structure representing the subscription fields of the email. +public type PublicEmailSubscriptionDetails record { + # ID of the selected office location. + string officeLocationId?; + # + string preferencesGroupId?; + # ID of the subscription. + string subscriptionId?; +}; + +# Properties of a marketing email you can update via the API. +public type EmailUpdateRequest record { + PublicRssEmailDetails rssData?; + # The subject of the email. + string subject?; + PublicEmailTestingDetails testing?; + # The date and time the email is scheduled for, in ISO8601 representation. This is only used in local time or scheduled emails. + string publishDate?; + "af"|"af-na"|"af-za"|"agq"|"agq-cm"|"ak"|"ak-gh"|"am"|"am-et"|"ann"|"ann-ng"|"ar"|"ar-001"|"ar-ae"|"ar-bh"|"ar-dj"|"ar-dz"|"ar-eg"|"ar-eh"|"ar-er"|"ar-il"|"ar-iq"|"ar-jo"|"ar-km"|"ar-kw"|"ar-lb"|"ar-ly"|"ar-ma"|"ar-mr"|"ar-om"|"ar-ps"|"ar-qa"|"ar-sa"|"ar-sd"|"ar-so"|"ar-ss"|"ar-sy"|"ar-td"|"ar-tn"|"ar-ye"|"as"|"asa"|"asa-tz"|"ast"|"ast-es"|"as-in"|"az"|"az-az"|"bas"|"bas-cm"|"be"|"bem"|"bem-zm"|"bez"|"bez-tz"|"be-by"|"bg"|"bgc"|"bgc-in"|"bg-bg"|"bho"|"bho-in"|"bm"|"bm-ml"|"bn"|"bn-bd"|"bn-in"|"bo"|"bo-cn"|"bo-in"|"br"|"brx"|"brx-in"|"br-fr"|"bs"|"bs-ba"|"ca"|"ca-ad"|"ca-es"|"ca-fr"|"ca-it"|"ccp"|"ccp-bd"|"ccp-in"|"ce"|"ceb"|"ceb-ph"|"ce-ru"|"cgg"|"cgg-ug"|"chr"|"chr-us"|"ckb"|"ckb-iq"|"ckb-ir"|"cs"|"cs-cz"|"cu"|"cu-ru"|"cv"|"cv-ru"|"cy"|"cy-gb"|"da"|"dav"|"dav-ke"|"da-dk"|"da-gl"|"de"|"de-at"|"de-be"|"de-ch"|"de-de"|"de-gr"|"de-it"|"de-li"|"de-lu"|"dje"|"dje-ne"|"doi"|"doi-in"|"dsb"|"dsb-de"|"dua"|"dua-cm"|"dyo"|"dyo-sn"|"dz"|"dz-bt"|"ebu"|"ebu-ke"|"ee"|"ee-gh"|"ee-tg"|"el"|"el-cy"|"el-gr"|"en"|"en-001"|"en-150"|"en-ae"|"en-ag"|"en-ai"|"en-as"|"en-at"|"en-au"|"en-bb"|"en-be"|"en-bi"|"en-bm"|"en-bs"|"en-bw"|"en-bz"|"en-ca"|"en-cc"|"en-ch"|"en-ck"|"en-cm"|"en-cn"|"en-cx"|"en-cy"|"en-de"|"en-dg"|"en-dk"|"en-dm"|"en-ee"|"en-er"|"en-fi"|"en-fj"|"en-fk"|"en-fm"|"en-fr"|"en-gb"|"en-gd"|"en-gg"|"en-gh"|"en-gi"|"en-gm"|"en-gu"|"en-gy"|"en-hk"|"en-ie"|"en-il"|"en-im"|"en-in"|"en-io"|"en-je"|"en-jm"|"en-ke"|"en-ki"|"en-kn"|"en-ky"|"en-lc"|"en-lr"|"en-ls"|"en-lu"|"en-mg"|"en-mh"|"en-mo"|"en-mp"|"en-ms"|"en-mt"|"en-mu"|"en-mv"|"en-mw"|"en-mx"|"en-my"|"en-na"|"en-nf"|"en-ng"|"en-nl"|"en-nr"|"en-nu"|"en-nz"|"en-pg"|"en-ph"|"en-pk"|"en-pn"|"en-pr"|"en-pw"|"en-rw"|"en-sb"|"en-sc"|"en-sd"|"en-se"|"en-sg"|"en-sh"|"en-si"|"en-sl"|"en-ss"|"en-sx"|"en-sz"|"en-tc"|"en-tk"|"en-to"|"en-tt"|"en-tv"|"en-tz"|"en-ug"|"en-um"|"en-us"|"en-vc"|"en-vg"|"en-vi"|"en-vu"|"en-ws"|"en-za"|"en-zm"|"en-zw"|"eo"|"eo-001"|"es"|"es-419"|"es-ar"|"es-bo"|"es-br"|"es-bz"|"es-cl"|"es-co"|"es-cr"|"es-cu"|"es-do"|"es-ea"|"es-ec"|"es-es"|"es-gq"|"es-gt"|"es-hn"|"es-ic"|"es-mx"|"es-ni"|"es-pa"|"es-pe"|"es-ph"|"es-pr"|"es-py"|"es-sv"|"es-us"|"es-uy"|"es-ve"|"et"|"et-ee"|"eu"|"eu-es"|"ewo"|"ewo-cm"|"fa"|"fa-af"|"fa-ir"|"ff"|"ff-bf"|"ff-cm"|"ff-gh"|"ff-gm"|"ff-gn"|"ff-gw"|"ff-lr"|"ff-mr"|"ff-ne"|"ff-ng"|"ff-sl"|"ff-sn"|"fi"|"fil"|"fil-ph"|"fi-fi"|"fo"|"fo-dk"|"fo-fo"|"fr"|"frr"|"frr-de"|"fr-be"|"fr-bf"|"fr-bi"|"fr-bj"|"fr-bl"|"fr-ca"|"fr-cd"|"fr-cf"|"fr-cg"|"fr-ch"|"fr-ci"|"fr-cm"|"fr-dj"|"fr-dz"|"fr-fr"|"fr-ga"|"fr-gf"|"fr-gn"|"fr-gp"|"fr-gq"|"fr-ht"|"fr-km"|"fr-lu"|"fr-ma"|"fr-mc"|"fr-mf"|"fr-mg"|"fr-ml"|"fr-mq"|"fr-mr"|"fr-mu"|"fr-nc"|"fr-ne"|"fr-pf"|"fr-pm"|"fr-re"|"fr-rw"|"fr-sc"|"fr-sn"|"fr-sy"|"fr-td"|"fr-tg"|"fr-tn"|"fr-vu"|"fr-wf"|"fr-yt"|"fur"|"fur-it"|"fy"|"fy-nl"|"ga"|"ga-gb"|"ga-ie"|"gd"|"gd-gb"|"gl"|"gl-es"|"gsw"|"gsw-ch"|"gsw-fr"|"gsw-li"|"gu"|"guz"|"guz-ke"|"gu-in"|"gv"|"gv-im"|"ha"|"haw"|"haw-us"|"ha-gh"|"ha-ne"|"ha-ng"|"he"|"hi"|"hi-in"|"hr"|"hr-ba"|"hr-hr"|"hsb"|"hsb-de"|"hu"|"hu-hu"|"hy"|"hy-am"|"ia"|"ia-001"|"id"|"ig"|"ig-ng"|"ii"|"ii-cn"|"id-id"|"is"|"is-is"|"it"|"it-ch"|"it-it"|"it-sm"|"it-va"|"he-il"|"ja"|"ja-jp"|"jgo"|"jgo-cm"|"yi"|"yi-001"|"jmc"|"jmc-tz"|"jv"|"jv-id"|"ka"|"kab"|"kab-dz"|"kam"|"kam-ke"|"ka-ge"|"kde"|"kde-tz"|"kea"|"kea-cv"|"kgp"|"kgp-br"|"khq"|"khq-ml"|"ki"|"ki-ke"|"kk"|"kkj"|"kkj-cm"|"kk-kz"|"kl"|"kln"|"kln-ke"|"kl-gl"|"km"|"km-kh"|"kn"|"kn-in"|"ko"|"kok"|"kok-in"|"ko-kp"|"ko-kr"|"ks"|"ksb"|"ksb-tz"|"ksf"|"ksf-cm"|"ksh"|"ksh-de"|"ks-in"|"ku"|"ku-tr"|"kw"|"kw-gb"|"ky"|"ky-kg"|"lag"|"lag-tz"|"lb"|"lb-lu"|"lg"|"lg-ug"|"lkt"|"lkt-us"|"ln"|"ln-ao"|"ln-cd"|"ln-cf"|"ln-cg"|"lo"|"lo-la"|"lrc"|"lrc-iq"|"lrc-ir"|"lt"|"lt-lt"|"lu"|"luo"|"luo-ke"|"luy"|"luy-ke"|"lu-cd"|"lv"|"lv-lv"|"mai"|"mai-in"|"mas"|"mas-ke"|"mas-tz"|"mdf"|"mdf-ru"|"mer"|"mer-ke"|"mfe"|"mfe-mu"|"mg"|"mgh"|"mgh-mz"|"mgo"|"mgo-cm"|"mg-mg"|"mi"|"mi-nz"|"mk"|"mk-mk"|"ml"|"ml-in"|"mn"|"mni"|"mni-in"|"mn-mn"|"mr"|"mr-in"|"ms"|"ms-bn"|"ms-id"|"ms-my"|"ms-sg"|"mt"|"mt-mt"|"mua"|"mua-cm"|"my"|"my-mm"|"mzn"|"mzn-ir"|"naq"|"naq-na"|"nb"|"nb-no"|"nb-sj"|"nd"|"nds"|"nds-de"|"nds-nl"|"nd-zw"|"ne"|"ne-in"|"ne-np"|"nl"|"nl-aw"|"nl-be"|"nl-bq"|"nl-ch"|"nl-cw"|"nl-lu"|"nl-nl"|"nl-sr"|"nl-sx"|"nmg"|"nmg-cm"|"nn"|"nnh"|"nnh-cm"|"nn-no"|"no"|"no-no"|"nus"|"nus-ss"|"nyn"|"nyn-ug"|"oc"|"oc-es"|"oc-fr"|"om"|"om-et"|"om-ke"|"or"|"or-in"|"os"|"os-ge"|"os-ru"|"pa"|"pa-in"|"pa-pk"|"pcm"|"pcm-ng"|"pis"|"pis-sb"|"pl"|"pl-pl"|"prg"|"prg-001"|"ps"|"ps-af"|"ps-pk"|"pt"|"pt-ao"|"pt-br"|"pt-ch"|"pt-cv"|"pt-gq"|"pt-gw"|"pt-lu"|"pt-mo"|"pt-mz"|"pt-pt"|"pt-st"|"pt-tl"|"qu"|"qu-bo"|"qu-ec"|"qu-pe"|"raj"|"raj-in"|"rm"|"rm-ch"|"rn"|"rn-bi"|"ro"|"rof"|"rof-tz"|"ro-md"|"ro-ro"|"ru"|"ru-by"|"ru-kg"|"ru-kz"|"ru-md"|"ru-ru"|"ru-ua"|"rw"|"rwk"|"rwk-tz"|"rw-rw"|"sa"|"sah"|"sah-ru"|"saq"|"saq-ke"|"sat"|"sat-in"|"sa-in"|"sbp"|"sbp-tz"|"sc"|"sc-it"|"sd"|"sd-in"|"sd-pk"|"se"|"seh"|"seh-mz"|"ses"|"ses-ml"|"se-fi"|"se-no"|"se-se"|"sg"|"sg-cf"|"shi"|"shi-ma"|"si"|"si-lk"|"sk"|"sk-sk"|"sl"|"sl-si"|"smn"|"smn-fi"|"sms"|"sms-fi"|"sn"|"sn-zw"|"so"|"so-dj"|"so-et"|"so-ke"|"so-so"|"sq"|"sq-al"|"sq-mk"|"sq-xk"|"sr"|"sr-ba"|"sr-cs"|"sr-me"|"sr-rs"|"sr-xk"|"su"|"su-id"|"sv"|"sv-ax"|"sv-fi"|"sv-se"|"sw"|"sw-cd"|"sw-ke"|"sw-tz"|"sw-ug"|"sy"|"ta"|"ta-in"|"ta-lk"|"ta-my"|"ta-sg"|"te"|"teo"|"teo-ke"|"teo-ug"|"te-in"|"tg"|"tg-tj"|"th"|"th-th"|"ti"|"ti-er"|"ti-et"|"tk"|"tk-tm"|"tl"|"to"|"tok"|"tok-001"|"to-to"|"tr"|"tr-cy"|"tr-tr"|"tt"|"tt-ru"|"twq"|"twq-ne"|"tzm"|"tzm-ma"|"ug"|"ug-cn"|"uk"|"uk-ua"|"ur"|"ur-in"|"ur-pk"|"uz"|"uz-af"|"uz-uz"|"vai"|"vai-lr"|"vi"|"vi-vn"|"vo"|"vo-001"|"vun"|"vun-tz"|"wae"|"wae-ch"|"wo"|"wo-sn"|"xh"|"xh-za"|"xog"|"xog-ug"|"yav"|"yav-cm"|"yo"|"yo-bj"|"yo-ng"|"yrl"|"yrl-br"|"yrl-co"|"yrl-ve"|"yue"|"yue-cn"|"yue-hk"|"zgh"|"zgh-ma"|"zh"|"zh-cn"|"zh-hans"|"zh-hant"|"zh-hk"|"zh-mo"|"zh-sg"|"zh-tw"|"zu"|"zu-za" language?; + int businessUnitId?; + PublicEmailContent content?; + PublicWebversionDetails webversion?; + # Determines if the email is archived or not. + boolean archived?; + PublicEmailSubscriptionDetails subscriptionDetails?; + # The active domain of the email. + string activeDomain?; + # The name of the email, as displayed on the email dashboard. + string name?; + # The ID of the campaign this email is associated to. + string campaign?; + PublicEmailFromDetails 'from?; + boolean jitterSendTime?; + # The email state. + "AUTOMATED"|"AUTOMATED_DRAFT"|"AUTOMATED_SENDING"|"AUTOMATED_FOR_FORM"|"AUTOMATED_FOR_FORM_BUFFER"|"AUTOMATED_FOR_FORM_DRAFT"|"AUTOMATED_FOR_FORM_LEGACY"|"BLOG_EMAIL_DRAFT"|"BLOG_EMAIL_PUBLISHED"|"DRAFT"|"DRAFT_AB"|"DRAFT_AB_VARIANT"|"ERROR"|"LOSER_AB_VARIANT"|"PAGE_STUB"|"PRE_PROCESSING"|"PROCESSING"|"PUBLISHED"|"PUBLISHED_AB"|"PUBLISHED_AB_VARIANT"|"PUBLISHED_OR_SCHEDULED"|"RSS_TO_EMAIL_DRAFT"|"RSS_TO_EMAIL_PUBLISHED"|"SCHEDULED"|"SCHEDULED_AB"|"SCHEDULED_OR_PUBLISHED"|"AUTOMATED_AB"|"AUTOMATED_AB_VARIANT"|"AUTOMATED_DRAFT_AB"|"AUTOMATED_DRAFT_ABVARIANT"|"AUTOMATED_LOSER_ABVARIANT" state?; + PublicEmailToDetails to?; + # The email subcategory. + "ab_master"|"ab_variant"|"ab_loser_variant"|"page_stub"|"landing_page"|"site_page"|"legacy_page"|"ab_master_site_page"|"ab_variant_site_page"|"ab_loser_variant_site_page"|"performable_landing_page"|"performable_landing_page_cutover"|"staged_page"|"automated"|"automated_for_deal"|"automated_for_form"|"automated_for_form_legacy"|"automated_for_form_buffer"|"automated_for_form_draft"|"rss_to_email"|"rss_to_email_child"|"blog_email"|"blog_email_child"|"optin_email"|"optin_followup_email"|"batch"|"resubscribe_email"|"unsubscribe_confirmation_email"|"resubscribe_confirmation_email"|"single_send_api"|"marketing_single_send_api"|"smtp_token"|"localtime"|"automated_for_ticket"|"automated_for_leadflow"|"automated_for_feedback_ces"|"automated_for_feedback_nps"|"automated_for_feedback_custom"|"membership_registration"|"membership_password_saved"|"membership_password_reset"|"membership_otp_login"|"membership_passwordless_auth"|"membership_email_verification"|"membership_registration_follow_up"|"membership_verification"|"membership_follow_up"|"ticket_closed_kickback_email"|"ticket_opened_kickback_email"|"automated_for_custom_survey"|"discardable_stub"|"normal_blog_post"|"legacy_blog_post"|"imported_blog_post"|"automated_ab_master"|"automated_ab_variant"|"web_interactive"|"portal_content"|"page_instance_layout"|"kb_article_instance_layout"|"kb_listing"|"kb_search_results"|"kb_support_form"|"case_study"|"case_study_listing"|"case_study_instance_layout"|"UNKNOWN" subcategory?; + # Determines whether the email will be sent immediately on publish. + boolean sendOnPublish?; +}; + +# Properties of a marketing email you can set when creating a marketing email. +public type EmailCreateRequest record { + # The ID of the feedback survey linked to the email. + string feedbackSurveyId?; + PublicRssEmailDetails rssData?; + # The subject of the email. + string subject?; + PublicEmailTestingDetails testing?; + # The date and time the email is scheduled for, in ISO8601 representation. This is only used in local time or scheduled emails. + string publishDate?; + "af"|"af-na"|"af-za"|"agq"|"agq-cm"|"ak"|"ak-gh"|"am"|"am-et"|"ann"|"ann-ng"|"ar"|"ar-001"|"ar-ae"|"ar-bh"|"ar-dj"|"ar-dz"|"ar-eg"|"ar-eh"|"ar-er"|"ar-il"|"ar-iq"|"ar-jo"|"ar-km"|"ar-kw"|"ar-lb"|"ar-ly"|"ar-ma"|"ar-mr"|"ar-om"|"ar-ps"|"ar-qa"|"ar-sa"|"ar-sd"|"ar-so"|"ar-ss"|"ar-sy"|"ar-td"|"ar-tn"|"ar-ye"|"as"|"asa"|"asa-tz"|"ast"|"ast-es"|"as-in"|"az"|"az-az"|"bas"|"bas-cm"|"be"|"bem"|"bem-zm"|"bez"|"bez-tz"|"be-by"|"bg"|"bgc"|"bgc-in"|"bg-bg"|"bho"|"bho-in"|"bm"|"bm-ml"|"bn"|"bn-bd"|"bn-in"|"bo"|"bo-cn"|"bo-in"|"br"|"brx"|"brx-in"|"br-fr"|"bs"|"bs-ba"|"ca"|"ca-ad"|"ca-es"|"ca-fr"|"ca-it"|"ccp"|"ccp-bd"|"ccp-in"|"ce"|"ceb"|"ceb-ph"|"ce-ru"|"cgg"|"cgg-ug"|"chr"|"chr-us"|"ckb"|"ckb-iq"|"ckb-ir"|"cs"|"cs-cz"|"cu"|"cu-ru"|"cv"|"cv-ru"|"cy"|"cy-gb"|"da"|"dav"|"dav-ke"|"da-dk"|"da-gl"|"de"|"de-at"|"de-be"|"de-ch"|"de-de"|"de-gr"|"de-it"|"de-li"|"de-lu"|"dje"|"dje-ne"|"doi"|"doi-in"|"dsb"|"dsb-de"|"dua"|"dua-cm"|"dyo"|"dyo-sn"|"dz"|"dz-bt"|"ebu"|"ebu-ke"|"ee"|"ee-gh"|"ee-tg"|"el"|"el-cy"|"el-gr"|"en"|"en-001"|"en-150"|"en-ae"|"en-ag"|"en-ai"|"en-as"|"en-at"|"en-au"|"en-bb"|"en-be"|"en-bi"|"en-bm"|"en-bs"|"en-bw"|"en-bz"|"en-ca"|"en-cc"|"en-ch"|"en-ck"|"en-cm"|"en-cn"|"en-cx"|"en-cy"|"en-de"|"en-dg"|"en-dk"|"en-dm"|"en-ee"|"en-er"|"en-fi"|"en-fj"|"en-fk"|"en-fm"|"en-fr"|"en-gb"|"en-gd"|"en-gg"|"en-gh"|"en-gi"|"en-gm"|"en-gu"|"en-gy"|"en-hk"|"en-ie"|"en-il"|"en-im"|"en-in"|"en-io"|"en-je"|"en-jm"|"en-ke"|"en-ki"|"en-kn"|"en-ky"|"en-lc"|"en-lr"|"en-ls"|"en-lu"|"en-mg"|"en-mh"|"en-mo"|"en-mp"|"en-ms"|"en-mt"|"en-mu"|"en-mv"|"en-mw"|"en-mx"|"en-my"|"en-na"|"en-nf"|"en-ng"|"en-nl"|"en-nr"|"en-nu"|"en-nz"|"en-pg"|"en-ph"|"en-pk"|"en-pn"|"en-pr"|"en-pw"|"en-rw"|"en-sb"|"en-sc"|"en-sd"|"en-se"|"en-sg"|"en-sh"|"en-si"|"en-sl"|"en-ss"|"en-sx"|"en-sz"|"en-tc"|"en-tk"|"en-to"|"en-tt"|"en-tv"|"en-tz"|"en-ug"|"en-um"|"en-us"|"en-vc"|"en-vg"|"en-vi"|"en-vu"|"en-ws"|"en-za"|"en-zm"|"en-zw"|"eo"|"eo-001"|"es"|"es-419"|"es-ar"|"es-bo"|"es-br"|"es-bz"|"es-cl"|"es-co"|"es-cr"|"es-cu"|"es-do"|"es-ea"|"es-ec"|"es-es"|"es-gq"|"es-gt"|"es-hn"|"es-ic"|"es-mx"|"es-ni"|"es-pa"|"es-pe"|"es-ph"|"es-pr"|"es-py"|"es-sv"|"es-us"|"es-uy"|"es-ve"|"et"|"et-ee"|"eu"|"eu-es"|"ewo"|"ewo-cm"|"fa"|"fa-af"|"fa-ir"|"ff"|"ff-bf"|"ff-cm"|"ff-gh"|"ff-gm"|"ff-gn"|"ff-gw"|"ff-lr"|"ff-mr"|"ff-ne"|"ff-ng"|"ff-sl"|"ff-sn"|"fi"|"fil"|"fil-ph"|"fi-fi"|"fo"|"fo-dk"|"fo-fo"|"fr"|"frr"|"frr-de"|"fr-be"|"fr-bf"|"fr-bi"|"fr-bj"|"fr-bl"|"fr-ca"|"fr-cd"|"fr-cf"|"fr-cg"|"fr-ch"|"fr-ci"|"fr-cm"|"fr-dj"|"fr-dz"|"fr-fr"|"fr-ga"|"fr-gf"|"fr-gn"|"fr-gp"|"fr-gq"|"fr-ht"|"fr-km"|"fr-lu"|"fr-ma"|"fr-mc"|"fr-mf"|"fr-mg"|"fr-ml"|"fr-mq"|"fr-mr"|"fr-mu"|"fr-nc"|"fr-ne"|"fr-pf"|"fr-pm"|"fr-re"|"fr-rw"|"fr-sc"|"fr-sn"|"fr-sy"|"fr-td"|"fr-tg"|"fr-tn"|"fr-vu"|"fr-wf"|"fr-yt"|"fur"|"fur-it"|"fy"|"fy-nl"|"ga"|"ga-gb"|"ga-ie"|"gd"|"gd-gb"|"gl"|"gl-es"|"gsw"|"gsw-ch"|"gsw-fr"|"gsw-li"|"gu"|"guz"|"guz-ke"|"gu-in"|"gv"|"gv-im"|"ha"|"haw"|"haw-us"|"ha-gh"|"ha-ne"|"ha-ng"|"he"|"hi"|"hi-in"|"hr"|"hr-ba"|"hr-hr"|"hsb"|"hsb-de"|"hu"|"hu-hu"|"hy"|"hy-am"|"ia"|"ia-001"|"id"|"ig"|"ig-ng"|"ii"|"ii-cn"|"id-id"|"is"|"is-is"|"it"|"it-ch"|"it-it"|"it-sm"|"it-va"|"he-il"|"ja"|"ja-jp"|"jgo"|"jgo-cm"|"yi"|"yi-001"|"jmc"|"jmc-tz"|"jv"|"jv-id"|"ka"|"kab"|"kab-dz"|"kam"|"kam-ke"|"ka-ge"|"kde"|"kde-tz"|"kea"|"kea-cv"|"kgp"|"kgp-br"|"khq"|"khq-ml"|"ki"|"ki-ke"|"kk"|"kkj"|"kkj-cm"|"kk-kz"|"kl"|"kln"|"kln-ke"|"kl-gl"|"km"|"km-kh"|"kn"|"kn-in"|"ko"|"kok"|"kok-in"|"ko-kp"|"ko-kr"|"ks"|"ksb"|"ksb-tz"|"ksf"|"ksf-cm"|"ksh"|"ksh-de"|"ks-in"|"ku"|"ku-tr"|"kw"|"kw-gb"|"ky"|"ky-kg"|"lag"|"lag-tz"|"lb"|"lb-lu"|"lg"|"lg-ug"|"lkt"|"lkt-us"|"ln"|"ln-ao"|"ln-cd"|"ln-cf"|"ln-cg"|"lo"|"lo-la"|"lrc"|"lrc-iq"|"lrc-ir"|"lt"|"lt-lt"|"lu"|"luo"|"luo-ke"|"luy"|"luy-ke"|"lu-cd"|"lv"|"lv-lv"|"mai"|"mai-in"|"mas"|"mas-ke"|"mas-tz"|"mdf"|"mdf-ru"|"mer"|"mer-ke"|"mfe"|"mfe-mu"|"mg"|"mgh"|"mgh-mz"|"mgo"|"mgo-cm"|"mg-mg"|"mi"|"mi-nz"|"mk"|"mk-mk"|"ml"|"ml-in"|"mn"|"mni"|"mni-in"|"mn-mn"|"mr"|"mr-in"|"ms"|"ms-bn"|"ms-id"|"ms-my"|"ms-sg"|"mt"|"mt-mt"|"mua"|"mua-cm"|"my"|"my-mm"|"mzn"|"mzn-ir"|"naq"|"naq-na"|"nb"|"nb-no"|"nb-sj"|"nd"|"nds"|"nds-de"|"nds-nl"|"nd-zw"|"ne"|"ne-in"|"ne-np"|"nl"|"nl-aw"|"nl-be"|"nl-bq"|"nl-ch"|"nl-cw"|"nl-lu"|"nl-nl"|"nl-sr"|"nl-sx"|"nmg"|"nmg-cm"|"nn"|"nnh"|"nnh-cm"|"nn-no"|"no"|"no-no"|"nus"|"nus-ss"|"nyn"|"nyn-ug"|"oc"|"oc-es"|"oc-fr"|"om"|"om-et"|"om-ke"|"or"|"or-in"|"os"|"os-ge"|"os-ru"|"pa"|"pa-in"|"pa-pk"|"pcm"|"pcm-ng"|"pis"|"pis-sb"|"pl"|"pl-pl"|"prg"|"prg-001"|"ps"|"ps-af"|"ps-pk"|"pt"|"pt-ao"|"pt-br"|"pt-ch"|"pt-cv"|"pt-gq"|"pt-gw"|"pt-lu"|"pt-mo"|"pt-mz"|"pt-pt"|"pt-st"|"pt-tl"|"qu"|"qu-bo"|"qu-ec"|"qu-pe"|"raj"|"raj-in"|"rm"|"rm-ch"|"rn"|"rn-bi"|"ro"|"rof"|"rof-tz"|"ro-md"|"ro-ro"|"ru"|"ru-by"|"ru-kg"|"ru-kz"|"ru-md"|"ru-ru"|"ru-ua"|"rw"|"rwk"|"rwk-tz"|"rw-rw"|"sa"|"sah"|"sah-ru"|"saq"|"saq-ke"|"sat"|"sat-in"|"sa-in"|"sbp"|"sbp-tz"|"sc"|"sc-it"|"sd"|"sd-in"|"sd-pk"|"se"|"seh"|"seh-mz"|"ses"|"ses-ml"|"se-fi"|"se-no"|"se-se"|"sg"|"sg-cf"|"shi"|"shi-ma"|"si"|"si-lk"|"sk"|"sk-sk"|"sl"|"sl-si"|"smn"|"smn-fi"|"sms"|"sms-fi"|"sn"|"sn-zw"|"so"|"so-dj"|"so-et"|"so-ke"|"so-so"|"sq"|"sq-al"|"sq-mk"|"sq-xk"|"sr"|"sr-ba"|"sr-cs"|"sr-me"|"sr-rs"|"sr-xk"|"su"|"su-id"|"sv"|"sv-ax"|"sv-fi"|"sv-se"|"sw"|"sw-cd"|"sw-ke"|"sw-tz"|"sw-ug"|"sy"|"ta"|"ta-in"|"ta-lk"|"ta-my"|"ta-sg"|"te"|"teo"|"teo-ke"|"teo-ug"|"te-in"|"tg"|"tg-tj"|"th"|"th-th"|"ti"|"ti-er"|"ti-et"|"tk"|"tk-tm"|"tl"|"to"|"tok"|"tok-001"|"to-to"|"tr"|"tr-cy"|"tr-tr"|"tt"|"tt-ru"|"twq"|"twq-ne"|"tzm"|"tzm-ma"|"ug"|"ug-cn"|"uk"|"uk-ua"|"ur"|"ur-in"|"ur-pk"|"uz"|"uz-af"|"uz-uz"|"vai"|"vai-lr"|"vi"|"vi-vn"|"vo"|"vo-001"|"vun"|"vun-tz"|"wae"|"wae-ch"|"wo"|"wo-sn"|"xh"|"xh-za"|"xog"|"xog-ug"|"yav"|"yav-cm"|"yo"|"yo-bj"|"yo-ng"|"yrl"|"yrl-br"|"yrl-co"|"yrl-ve"|"yue"|"yue-cn"|"yue-hk"|"zgh"|"zgh-ma"|"zh"|"zh-cn"|"zh-hans"|"zh-hant"|"zh-hk"|"zh-mo"|"zh-sg"|"zh-tw"|"zu"|"zu-za" language?; + int businessUnitId?; + PublicEmailContent content?; + PublicWebversionDetails webversion?; + # Determines if the email is archived or not. + boolean archived?; + PublicEmailSubscriptionDetails subscriptionDetails?; + # The active domain of the email. + string activeDomain?; + # The name of the email, as displayed on the email dashboard. + string name; + # The ID of the campaign this email is associated to. + string campaign?; + PublicEmailFromDetails 'from?; + boolean jitterSendTime?; + # The email state. + "AUTOMATED"|"AUTOMATED_DRAFT"|"AUTOMATED_SENDING"|"AUTOMATED_FOR_FORM"|"AUTOMATED_FOR_FORM_BUFFER"|"AUTOMATED_FOR_FORM_DRAFT"|"AUTOMATED_FOR_FORM_LEGACY"|"BLOG_EMAIL_DRAFT"|"BLOG_EMAIL_PUBLISHED"|"DRAFT"|"DRAFT_AB"|"DRAFT_AB_VARIANT"|"ERROR"|"LOSER_AB_VARIANT"|"PAGE_STUB"|"PRE_PROCESSING"|"PROCESSING"|"PUBLISHED"|"PUBLISHED_AB"|"PUBLISHED_AB_VARIANT"|"PUBLISHED_OR_SCHEDULED"|"RSS_TO_EMAIL_DRAFT"|"RSS_TO_EMAIL_PUBLISHED"|"SCHEDULED"|"SCHEDULED_AB"|"SCHEDULED_OR_PUBLISHED"|"AUTOMATED_AB"|"AUTOMATED_AB_VARIANT"|"AUTOMATED_DRAFT_AB"|"AUTOMATED_DRAFT_ABVARIANT"|"AUTOMATED_LOSER_ABVARIANT" state?; + PublicEmailToDetails to?; + # The email subcategory. + "ab_master"|"ab_variant"|"ab_loser_variant"|"page_stub"|"landing_page"|"site_page"|"legacy_page"|"ab_master_site_page"|"ab_variant_site_page"|"ab_loser_variant_site_page"|"performable_landing_page"|"performable_landing_page_cutover"|"staged_page"|"automated"|"automated_for_deal"|"automated_for_form"|"automated_for_form_legacy"|"automated_for_form_buffer"|"automated_for_form_draft"|"rss_to_email"|"rss_to_email_child"|"blog_email"|"blog_email_child"|"optin_email"|"optin_followup_email"|"batch"|"resubscribe_email"|"unsubscribe_confirmation_email"|"resubscribe_confirmation_email"|"single_send_api"|"marketing_single_send_api"|"smtp_token"|"localtime"|"automated_for_ticket"|"automated_for_leadflow"|"automated_for_feedback_ces"|"automated_for_feedback_nps"|"automated_for_feedback_custom"|"membership_registration"|"membership_password_saved"|"membership_password_reset"|"membership_otp_login"|"membership_passwordless_auth"|"membership_email_verification"|"membership_registration_follow_up"|"membership_verification"|"membership_follow_up"|"ticket_closed_kickback_email"|"ticket_opened_kickback_email"|"automated_for_custom_survey"|"discardable_stub"|"normal_blog_post"|"legacy_blog_post"|"imported_blog_post"|"automated_ab_master"|"automated_ab_variant"|"web_interactive"|"portal_content"|"page_instance_layout"|"kb_article_instance_layout"|"kb_listing"|"kb_search_results"|"kb_support_form"|"case_study"|"case_study_listing"|"case_study_instance_layout"|"UNKNOWN" subcategory?; + # Determines whether the email will be sent immediately on publish. + boolean sendOnPublish?; +}; + +# OAuth2 Refresh Token Grant Configs +public type OAuth2RefreshTokenGrantConfig record {| + *http:OAuth2RefreshTokenGrantConfig; + # Refresh URL + string refreshUrl = "https://api.hubapi.com/oauth/v1/token"; +|}; + +# Model definition for a marketing email version. Contains metadata describing the version of the marketing email. It can be used to view edit history of a marketing email. +public type VersionPublicEmail record { + # ID of this marketing email version. + string id; + VersionUser user; + PublicEmail 'object; + string updated; +}; + +# Provides a set of configurations for controlling the behaviours when communicating with a remote HTTP endpoint. +@display {label: "Connection Config"} +public type ConnectionConfig record {| + # Provides Auth configurations needed when communicating with a remote HTTP endpoint. + http:BearerTokenConfig|OAuth2RefreshTokenGrantConfig|ApiKeysConfig auth; + # The HTTP version understood by the client + http:HttpVersion httpVersion = http:HTTP_2_0; + # Configurations related to HTTP/1.x protocol + ClientHttp1Settings http1Settings?; + # Configurations related to HTTP/2 protocol + http:ClientHttp2Settings http2Settings?; + # The maximum time to wait (in seconds) for a response before closing the connection + decimal timeout = 60; + # The choice of setting `forwarded`/`x-forwarded` header + string forwarded = "disable"; + # Configurations associated with request pooling + http:PoolConfiguration poolConfig?; + # HTTP caching related configurations + http:CacheConfig cache?; + # Specifies the way of handling compression (`accept-encoding`) header + http:Compression compression = http:COMPRESSION_AUTO; + # Configurations associated with the behaviour of the Circuit Breaker + http:CircuitBreakerConfig circuitBreaker?; + # Configurations associated with retrying + http:RetryConfig retryConfig?; + # Configurations associated with inbound response size limits + http:ResponseLimitConfigs responseLimits?; + # SSL/TLS-related options + http:ClientSecureSocket secureSocket?; + # Proxy server related options + http:ProxyConfig proxy?; + # Enables the inbound payload validation functionality which provided by the constraint package. Enabled by default + boolean validation = true; +|}; + +# Model definition for a version user. Contains addition information about the user who created a version. +public type VersionUser record { + # The user's full name. + string fullName; + # The user ID. + string id; + # The user email. + string email; +}; + +# Data structure representing lists of IDs that should be included and excluded. +public type PublicEmailRecipients record { + # Included IDs. + string[] include?; + # Excluded IDs. + string[] exclude?; +}; + +# Response object for collections of EmailStatisticIntervals. +public type CollectionResponseWithTotalEmailStatisticIntervalNoPaging record { + # Total number of objects. + int:Signed32 total; + # Collection of objects. + EmailStatisticInterval[] results; +}; + +# Represents the Queries record for the operation: get-/statistics/list +public type GetStatisticsListQueries record { + # Filter by email IDs. Only include statistics of emails with these IDs. + int[] emailIds?; + # Specifies which email properties should be returned. All properties will be returned by default. + string property?; + # The end timestamp of the time span, in ISO8601 representation. + string endTimestamp?; + # The start timestamp of the time span, in ISO8601 representation. + string startTimestamp?; +}; + +public type PublicFontStyle record { + int:Signed32 size?; + string color?; + boolean underline?; + boolean bold?; + boolean italic?; + string font?; +}; + +# Contains information pagination of results. +public type Paging record { + NextPage next; + PreviousPage prev?; +}; + +# Represents the Queries record for the operation: get-/statistics/histogram +public type GetStatisticsHistogramQueries record { + # Filter by email IDs. Only include statistics of emails with these IDs. + int[] emailIds?; + # The interval to aggregate statistics for. + "YEAR"|"QUARTER"|"MONTH"|"WEEK"|"DAY"|"HOUR"|"QUARTER_HOUR"|"MINUTE"|"SECOND" interval?; + # The end timestamp of the time span, in ISO8601 representation. + string endTimestamp?; + # The start timestamp of the time span, in ISO8601 representation. + string startTimestamp?; +}; + +public type PublicEmailStyleSettings record { + string emailBodyPadding?; + string colorPickerFavorite5?; + string bodyColor?; + string colorPickerFavorite6?; + string backgroundImage?; + string emailBodyWidth?; + string secondaryFont?; + string primaryAccentColor?; + string colorPickerFavorite3?; + string primaryFontLineHeight?; + string colorPickerFavorite4?; + string secondaryFontColor?; + string colorPickerFavorite1?; + string colorPickerFavorite2?; + string bodyBorderColor?; + int:Signed32 bodyBorderWidth?; + PublicFontStyle linksFont?; + string backgroundColor?; + string backgroundImageType?; + PublicDividerStyleSettings dividerStyleSettings?; + string secondaryAccentColor?; + string secondaryFontLineHeight?; + int:Signed32 primaryFontSize?; + int:Signed32 secondaryFontSize?; + string primaryFontColor?; + PublicFontStyle headingOneFont?; + string primaryFont?; + PublicFontStyle headingTwoFont?; + PublicButtonStyleSettings buttonStyleSettings?; + string bodyBorderColorChoice?; +}; + +# Proxy server configurations to be used with the HTTP client endpoint. +public type ProxyConfig record {| + # Host name of the proxy server + string host = ""; + # Proxy server port + int port = 0; + # Proxy server username + string userName = ""; + # Proxy server password + @display {label: "", kind: "password"} + string password = ""; +|}; + +# AB testing related data. This property is only returned for AB type emails. +public type PublicEmailTestingDetails record { + # Version of the email that should be sent if the results are inconclusive after the test period, master or variant. + "master"|"variant"|"loser_variant"|"mab_master"|"mab_variant"|"automated_master"|"automated_variant"|"automated_loser_variant" abSamplingDefault?; + # Version of the email that should be sent if there are too few recipients to conduct an AB test. + "master"|"variant"|"loser_variant"|"mab_master"|"mab_variant"|"automated_master"|"automated_variant"|"automated_loser_variant" abSampleSizeDefault?; + # Status of the AB test. + "master"|"variant"|"loser_variant"|"mab_master"|"mab_variant"|"automated_master"|"automated_variant"|"automated_loser_variant" abStatus?; + # The size of your test group. + int:Signed32 abTestPercentage?; + # Time limit on gathering test results. After this time is up, the winning version will be sent to the remaining contacts. + int:Signed32 hoursToWait?; + # The ID of the AB test. + string testId?; + # Metric to determine the version that will be sent to the remaining contacts. + "CLICKS_BY_OPENS"|"CLICKS_BY_DELIVERED"|"OPENS_BY_DELIVERED" abSuccessMetric?; +}; + +public type PublicWebversionDetails record { + # + string domain?; + # + string redirectToPageId?; + boolean isPageRedirected?; + # + string redirectToUrl?; + # + string title?; + # + string metaDescription?; + boolean pageExpiryEnabled?; + # + string slug?; + string url?; + # + string expiresAt?; +}; + +public type EmailStatisticsData record { + # Statistics by device. + record {|record {|int...;|}...;|} deviceBreakdown; + # Number of emails that were dropped and bounced. + record {|record {|int...;|}...;|} qualifierStats; + # Counters like number of `sent`, `open` or `delivered`. + record {|int...;|} counters; + # Ratios like `openratio` or `clickratio` + record {|float...;|} ratios; +}; + +# RSS related data if it is a blog or rss email. +public type PublicRssEmailDetails record { + # + int:Signed32 blogImageMaxWidth?; + # + string blogEmailType?; + # + string hubspotBlogId?; + # + string rssEntryTemplate?; + # + record {|record {}...;|} timing?; + # + int:Signed32 maxEntries?; + # + boolean useHeadlineAsSubject?; + # + string blogLayout?; + # + string url?; +}; + +# Provides settings related to HTTP/1.x protocol. +public type ClientHttp1Settings record {| + # Specifies whether to reuse a connection for multiple requests + http:KeepAlive keepAlive = http:KEEPALIVE_AUTO; + # The chunking behaviour of the request + http:Chunking chunking = http:CHUNKING_AUTO; + # Proxy server related options + ProxyConfig proxy?; +|}; + +# Aggregated statistics for the given interval, plus the IDs of emails that were sent during that interval. +public type AggregateEmailStatistics record { + # List of email IDs that were sent during the time span. + int[] emails?; + # The aggregated statistics per campaign. + record {|EmailStatisticsData...;|} campaignAggregations?; + EmailStatisticsData aggregate?; +}; + +# Contains information about the previous page. +public type PreviousPage record { + # The cursor token value to get the previous set of results. Use this value as query parameter (&before=...) to obtain the previous page. + string before; + # The link to the previous page. + string link?; +}; + +# Data structure representing the from fields on the email. +public type PublicEmailFromDetails record { + # The reply to recipients will see. + string customReplyTo?; + # The name recipients will see. + string fromName?; + # The from address and reply to email address (if no customReplyTo defined) recipients will see. + string replyTo?; +}; + +# Represents the Queries record for the operation: get-/ +public type GetQueries record { + # Only return emails last updated after the specified time. + string updatedAfter?; + # Filter by published/draft emails. All emails will be returned if not present. + boolean isPublished?; + # Specifies which fields to use for sorting results. Valid fields are `name`, `createdAt`, `updatedAt`, `createdBy`, `updatedBy`. `createdAt` will be used by default. + string[] sort?; + # Only return emails created after the specified time. + string createdAfter?; + # Email types to be filtered by. Multiple types can be included. All emails will be returned if not present. + "AB_EMAIL"|"BATCH_EMAIL"|"LOCALTIME_EMAIL"|"AUTOMATED_AB_EMAIL"|"BLOG_EMAIL"|"BLOG_EMAIL_CHILD"|"RSS_EMAIL"|"RSS_EMAIL_CHILD"|"RESUBSCRIBE_EMAIL"|"OPTIN_EMAIL"|"OPTIN_FOLLOWUP_EMAIL"|"AUTOMATED_EMAIL"|"FEEDBACK_CES_EMAIL"|"FEEDBACK_CUSTOM_EMAIL"|"FEEDBACK_CUSTOM_SURVEY_EMAIL"|"FEEDBACK_NPS_EMAIL"|"FOLLOWUP_EMAIL"|"LEADFLOW_EMAIL"|"SINGLE_SEND_API"|"MARKETING_SINGLE_SEND_API"|"SMTP_TOKEN"|"TICKET_EMAIL"|"MEMBERSHIP_REGISTRATION_EMAIL"|"MEMBERSHIP_PASSWORD_SAVED_EMAIL"|"MEMBERSHIP_PASSWORD_RESET_EMAIL"|"MEMBERSHIP_EMAIL_VERIFICATION_EMAIL"|"MEMBERSHIP_PASSWORDLESS_AUTH_EMAIL"|"MEMBERSHIP_REGISTRATION_FOLLOW_UP_EMAIL"|"MEMBERSHIP_OTP_LOGIN_EMAIL"|"MEMBERSHIP_FOLLOW_UP_EMAIL"|"MEMBERSHIP_VERIFICATION_EMAIL" 'type?; + string[] includedProperties?; + boolean workflowNames?; + # Only return emails created at exactly the specified time. + string createdAt?; + # Only return emails last updated before the specified time. + string updatedBefore?; + # Specifies whether to return archived emails. Defaults to `false`. + boolean archived?; + boolean marketingCampaignNames?; + # Include statistics with emails. + boolean includeStats?; + # The maximum number of results to return. Default is 100. + int:Signed32 'limit?; + # Only return emails created before the specified time. + string createdBefore?; + # The cursor token value to get the next set of results. You can get this from the `paging.next.after` JSON property of a paged response containing more results. + string after?; + # Only return emails last updated at exactly the specified time. + string updatedAt?; +}; + +# Request body object for cloning marketing emails. +public type ContentCloneRequestVNext record { + # Name of the cloned email. + string cloneName?; + # ID of the email to be cloned. + string id; +}; + +# An email field whose value is controlled by one or more smart rules. +public type SmartEmailField record { +}; + +# Contains information about the next page. +public type NextPage record { + # The link to the next page. + string link?; + # The cursor token value to get the next set of results. Use this value as query parameter (&after=...) to obtain the next page. + string after; +}; + +# Provides API key configurations needed when communicating with a remote HTTP endpoint. +public type ApiKeysConfig record {| + string private\-app\-legacy; +|}; + +# Represents the Queries record for the operation: delete-/{emailId} +public type DeleteEmailidQueries record { + # Whether to return only results that have been archived. + boolean archived?; +}; + +# Data structure representing the content of the email. +public type PublicEmailContent record { + # + record {|SmartEmailField...;|} smartFields?; + # + record {|record {}...;|} themeSettingsValues?; + # + record {|record {}...;|} flexAreas?; + # + record {|record {}...;|} widgets?; + # + string plainTextVersion?; + # + string templatePath?; + # + record {|record {}...;|} widgetContainers?; + PublicEmailStyleSettings styleSettings?; +}; + +public type Interval record { + string 'start; + string end; +}; diff --git a/ballerina/utils.bal b/ballerina/utils.bal new file mode 100644 index 0000000..5d8e591 --- /dev/null +++ b/ballerina/utils.bal @@ -0,0 +1,234 @@ +// AUTO-GENERATED FILE. DO NOT MODIFY. +// This file is auto-generated by the Ballerina OpenAPI tool. + +// Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com). +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import ballerina/url; + +type SimpleBasicType string|boolean|int|float|decimal; + +# Represents encoding mechanism details. +type Encoding record { + # Defines how multiple values are delimited + string style = FORM; + # Specifies whether arrays and objects should generate as separate fields + boolean explode = true; + # Specifies the custom content type + string contentType?; + # Specifies the custom headers + map headers?; +}; + +enum EncodingStyle { + DEEPOBJECT, FORM, SPACEDELIMITED, PIPEDELIMITED +} + +final Encoding & readonly defaultEncoding = {}; + +# Serialize the record according to the deepObject style. +# +# + parent - Parent record name +# + anyRecord - Record to be serialized +# + return - Serialized record as a string +isolated function getDeepObjectStyleRequest(string parent, record {} anyRecord) returns string { + string[] recordArray = []; + foreach [string, anydata] [key, value] in anyRecord.entries() { + if value is SimpleBasicType { + recordArray.push(parent + "[" + key + "]" + "=" + getEncodedUri(value.toString())); + } else if value is SimpleBasicType[] { + recordArray.push(getSerializedArray(parent + "[" + key + "]" + "[]", value, DEEPOBJECT, true)); + } else if value is record {} { + string nextParent = parent + "[" + key + "]"; + recordArray.push(getDeepObjectStyleRequest(nextParent, value)); + } else if value is record {}[] { + string nextParent = parent + "[" + key + "]"; + recordArray.push(getSerializedRecordArray(nextParent, value, DEEPOBJECT)); + } + recordArray.push("&"); + } + _ = recordArray.pop(); + return string:'join("", ...recordArray); +} + +# Serialize the record according to the form style. +# +# + parent - Parent record name +# + anyRecord - Record to be serialized +# + explode - Specifies whether arrays and objects should generate separate parameters +# + return - Serialized record as a string +isolated function getFormStyleRequest(string parent, record {} anyRecord, boolean explode = true) returns string { + string[] recordArray = []; + if explode { + foreach [string, anydata] [key, value] in anyRecord.entries() { + if value is SimpleBasicType { + recordArray.push(key, "=", getEncodedUri(value.toString())); + } else if value is SimpleBasicType[] { + recordArray.push(getSerializedArray(key, value, explode = explode)); + } else if value is record {} { + recordArray.push(getFormStyleRequest(parent, value, explode)); + } + recordArray.push("&"); + } + _ = recordArray.pop(); + } else { + foreach [string, anydata] [key, value] in anyRecord.entries() { + if value is SimpleBasicType { + recordArray.push(key, ",", getEncodedUri(value.toString())); + } else if value is SimpleBasicType[] { + recordArray.push(getSerializedArray(key, value, explode = false)); + } else if value is record {} { + recordArray.push(getFormStyleRequest(parent, value, explode)); + } + recordArray.push(","); + } + _ = recordArray.pop(); + } + return string:'join("", ...recordArray); +} + +# Serialize arrays. +# +# + arrayName - Name of the field with arrays +# + anyArray - Array to be serialized +# + style - Defines how multiple values are delimited +# + explode - Specifies whether arrays and objects should generate separate parameters +# + return - Serialized array as a string +isolated function getSerializedArray(string arrayName, anydata[] anyArray, string style = "form", boolean explode = true) returns string { + string key = arrayName; + string[] arrayValues = []; + if anyArray.length() > 0 { + if style == FORM && !explode { + arrayValues.push(key, "="); + foreach anydata i in anyArray { + arrayValues.push(getEncodedUri(i.toString()), ","); + } + } else if style == SPACEDELIMITED && !explode { + arrayValues.push(key, "="); + foreach anydata i in anyArray { + arrayValues.push(getEncodedUri(i.toString()), "%20"); + } + } else if style == PIPEDELIMITED && !explode { + arrayValues.push(key, "="); + foreach anydata i in anyArray { + arrayValues.push(getEncodedUri(i.toString()), "|"); + } + } else if style == DEEPOBJECT { + foreach anydata i in anyArray { + arrayValues.push(key, "[]", "=", getEncodedUri(i.toString()), "&"); + } + } else { + foreach anydata i in anyArray { + arrayValues.push(key, "=", getEncodedUri(i.toString()), "&"); + } + } + _ = arrayValues.pop(); + } + return string:'join("", ...arrayValues); +} + +# Serialize the array of records according to the form style. +# +# + parent - Parent record name +# + value - Array of records to be serialized +# + style - Defines how multiple values are delimited +# + explode - Specifies whether arrays and objects should generate separate parameters +# + return - Serialized record as a string +isolated function getSerializedRecordArray(string parent, record {}[] value, string style = FORM, boolean explode = true) returns string { + string[] serializedArray = []; + if style == DEEPOBJECT { + int arayIndex = 0; + foreach var recordItem in value { + serializedArray.push(getDeepObjectStyleRequest(parent + "[" + arayIndex.toString() + "]", recordItem), "&"); + arayIndex = arayIndex + 1; + } + } else { + if !explode { + serializedArray.push(parent, "="); + } + foreach var recordItem in value { + serializedArray.push(getFormStyleRequest(parent, recordItem, explode), ","); + } + } + _ = serializedArray.pop(); + return string:'join("", ...serializedArray); +} + +# Get Encoded URI for a given value. +# +# + value - Value to be encoded +# + return - Encoded string +isolated function getEncodedUri(anydata value) returns string { + string|error encoded = url:encode(value.toString(), "UTF8"); + if encoded is string { + return encoded; + } else { + return value.toString(); + } +} + +# Generate query path with query parameter. +# +# + queryParam - Query parameter map +# + encodingMap - Details on serialization mechanism +# + return - Returns generated Path or error at failure of client initialization +isolated function getPathForQueryParam(map queryParam, map encodingMap = {}) returns string|error { + string[] param = []; + if queryParam.length() > 0 { + param.push("?"); + foreach var [key, value] in queryParam.entries() { + if value is () { + _ = queryParam.remove(key); + continue; + } + Encoding encodingData = encodingMap.hasKey(key) ? encodingMap.get(key) : defaultEncoding; + if value is SimpleBasicType { + param.push(key, "=", getEncodedUri(value.toString())); + } else if value is SimpleBasicType[] { + param.push(getSerializedArray(key, value, encodingData.style, encodingData.explode)); + } else if value is record {} { + if encodingData.style == DEEPOBJECT { + param.push(getDeepObjectStyleRequest(key, value)); + } else { + param.push(getFormStyleRequest(key, value, encodingData.explode)); + } + } else { + param.push(key, "=", value.toString()); + } + param.push("&"); + } + _ = param.pop(); + } + string restOfPath = string:'join("", ...param); + return restOfPath; +} + +# Generate header map for given header values. +# +# + headerParam - Headers map +# + return - Returns generated map or error at failure of client initialization +isolated function getMapForHeaders(map headerParam) returns map { + map headerMap = {}; + foreach var [key, value] in headerParam.entries() { + if value is SimpleBasicType[] { + headerMap[key] = from SimpleBasicType data in value + select data.toString(); + } else { + headerMap[key] = value.toString(); + } + } + return headerMap; +} diff --git a/build-config/resources/Ballerina.toml b/build-config/resources/Ballerina.toml index 790613a..a85f213 100644 --- a/build-config/resources/Ballerina.toml +++ b/build-config/resources/Ballerina.toml @@ -5,12 +5,12 @@ name = "hubspot.marketing.emails" version = "@toml.version@" license = ["Apache-2.0"] authors = ["Ballerina"] -keywords = [] # TODO: Add keywords +keywords = ["hubspot", "crm", "marketing", "emails"] # icon = "icon.png" # TODO: Add icon repository = "https://github.com/ballerina-platform/module-ballerinax-hubspot.marketing.emails" [build-options] observabilityIncluded = true -[platform.java21] +[platform.java17] graalvmCompatible = true diff --git a/docs/license.txt b/docs/license.txt index 921a7a1..adfc413 100644 --- a/docs/license.txt +++ b/docs/license.txt @@ -1,7 +1,7 @@ // AUTO-GENERATED FILE. DO NOT MODIFY. // This file is auto-generated by the Ballerina OpenAPI tool. -// Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). +// Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com). // // WSO2 LLC. licenses this file to you under the Apache License, // Version 2.0 (the "License"); you may not use this file except diff --git a/docs/setup/resources/auth_section.png b/docs/setup/resources/auth_section.png new file mode 100644 index 0000000..14306db Binary files /dev/null and b/docs/setup/resources/auth_section.png differ diff --git a/docs/setup/resources/create_app_1.png b/docs/setup/resources/create_app_1.png new file mode 100644 index 0000000..dc6c160 Binary files /dev/null and b/docs/setup/resources/create_app_1.png differ diff --git a/docs/setup/resources/create_app_2.png b/docs/setup/resources/create_app_2.png new file mode 100644 index 0000000..fbe1ec4 Binary files /dev/null and b/docs/setup/resources/create_app_2.png differ diff --git a/docs/setup/resources/create_app_final.png b/docs/setup/resources/create_app_final.png new file mode 100644 index 0000000..88713af Binary files /dev/null and b/docs/setup/resources/create_app_final.png differ diff --git a/docs/setup/resources/create_app_new.png b/docs/setup/resources/create_app_new.png new file mode 100644 index 0000000..028038e Binary files /dev/null and b/docs/setup/resources/create_app_new.png differ diff --git a/docs/setup/resources/get_credentials.png b/docs/setup/resources/get_credentials.png new file mode 100644 index 0000000..0c587a9 Binary files /dev/null and b/docs/setup/resources/get_credentials.png differ diff --git a/docs/setup/resources/install_app.png b/docs/setup/resources/install_app.png new file mode 100644 index 0000000..6a77bec Binary files /dev/null and b/docs/setup/resources/install_app.png differ diff --git a/docs/setup/resources/scopes.png b/docs/setup/resources/scopes.png new file mode 100644 index 0000000..8d9da9a Binary files /dev/null and b/docs/setup/resources/scopes.png differ diff --git a/docs/setup/resources/test_acc_1.png b/docs/setup/resources/test_acc_1.png new file mode 100644 index 0000000..d9317bd Binary files /dev/null and b/docs/setup/resources/test_acc_1.png differ diff --git a/docs/setup/resources/test_acc_2.png b/docs/setup/resources/test_acc_2.png new file mode 100644 index 0000000..7d3acfc Binary files /dev/null and b/docs/setup/resources/test_acc_2.png differ diff --git a/docs/setup/resources/test_acc_3.png b/docs/setup/resources/test_acc_3.png new file mode 100644 index 0000000..5301aa7 Binary files /dev/null and b/docs/setup/resources/test_acc_3.png differ diff --git a/docs/spec/openapi.yaml b/docs/spec/openapi.yaml new file mode 100644 index 0000000..0796a91 --- /dev/null +++ b/docs/spec/openapi.yaml @@ -0,0 +1,3115 @@ +{ + "openapi" : "3.0.1", + "info" : { + "title" : "Marketing Emails", + "version" : "v3", + "x-hubspot-product-tier-requirements" : { + "marketing" : "FREE" + }, + "x-hubspot-documentation-banner" : "PUBLIC_BETA", + "x-hubspot-api-use-case" : "You want to programmatically create marketing emails, then retrieve their send statistics at a later time.", + "x-hubspot-related-documentation" : [ { + "name" : "Marketing Emails Guide", + "url" : "https://developers.hubspot.com/beta-docs/guides/api/marketing/emails/marketing-emails" + } ], + "x-hubspot-introduction" : "Use the marketing email API to create, manage, and retrieve details about marketing emails. You can also this use API to retrieve statistics related to email sends, similar to what you can see in HubSpot." + }, + "servers" : [ { + "url" : "https://api.hubapi.com/marketing/v3/emails" + } ], + "tags" : [ { + "name" : "Statistics" + }, { + "name" : "Marketing Emails" + } ], + "paths" : { + "/statistics/list" : { + "get" : { + "tags" : [ "Statistics" ], + "summary" : "Get aggregated statistics.", + "description" : "Use this endpoint to get aggregated statistics of emails sent in a specified time span. It also returns the list of emails that were sent during the time span.", + "operationId" : "get-/statistics/list", + "parameters" : [ { + "name" : "startTimestamp", + "in" : "query", + "description" : "The start timestamp of the time span, in ISO8601 representation.", + "required" : false, + "style" : "form", + "explode" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "endTimestamp", + "in" : "query", + "description" : "The end timestamp of the time span, in ISO8601 representation.", + "required" : false, + "style" : "form", + "explode" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "emailIds", + "in" : "query", + "description" : "Filter by email IDs. Only include statistics of emails with these IDs.", + "required" : false, + "style" : "form", + "explode" : true, + "schema" : { + "type" : "array", + "items" : { + "type" : "integer", + "format" : "int64" + } + } + }, { + "name" : "property", + "in" : "query", + "description" : "Specifies which email properties should be returned. All properties will be returned by default.", + "required" : false, + "style" : "form", + "explode" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "successful operation", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/AggregateEmailStatistics" + } + } + } + }, + "default" : { + "$ref" : "#/components/responses/Error" + } + }, + "security" : [ { + "private_apps_legacy" : [ "content" ] + }, { + "oauth2_legacy" : [ "content" ] + } ] + } + }, + "/{emailId}/publish" : { + "post" : { + "tags" : [ "Marketing Emails" ], + "summary" : "Publish or send a marketing email.", + "description" : "If you have a Marketing Hub Enterprise account or the transactional email add-on, you can use this endpoint to publish an automated email or send/schedule a regular email.", + "operationId" : "post-/{emailId}/publish", + "parameters" : [ { + "name" : "emailId", + "in" : "path", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "204" : { + "description" : "No content", + "content" : { } + }, + "default" : { + "$ref" : "#/components/responses/Error" + } + }, + "security" : [ { + "oauth2_legacy" : [ "transactional-email" ] + }, { + "oauth2_legacy" : [ "marketing-email" ] + }, { + "private_apps_legacy" : [ "marketing-email" ] + }, { + "private_apps_legacy" : [ "transactional-email" ] + } ], + "x-hubspot-rate-limit-exemptions" : [ "ten-secondly" ] + } + }, + "/ab-test/create-variation" : { + "post" : { + "tags" : [ "Marketing Emails" ], + "summary" : "Create an A/B test variation of a marketing email.", + "description" : "Create a variation of a marketing email for an A/B test. The new variation will be created as a draft. If an active variation already exists, a new one won't be created.", + "operationId" : "post-/ab-test/create-variation", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/AbTestCreateRequestVNext" + } + } + }, + "required" : true + }, + "responses" : { + "201" : { + "description" : "successful operation", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/PublicEmail" + } + } + } + }, + "default" : { + "$ref" : "#/components/responses/Error" + } + }, + "security" : [ { + "private_apps_legacy" : [ "content" ] + }, { + "oauth2_legacy" : [ "content" ] + } ] + } + }, + "/statistics/histogram" : { + "get" : { + "tags" : [ "Statistics" ], + "summary" : "Get aggregated statistic intervals.", + "description" : "Get aggregated statistics in intervals for a specified time span. Each interval contains aggregated statistics of the emails that were sent in that time.", + "operationId" : "get-/statistics/histogram", + "parameters" : [ { + "name" : "interval", + "in" : "query", + "description" : "The interval to aggregate statistics for.", + "required" : false, + "style" : "form", + "explode" : true, + "schema" : { + "type" : "string", + "enum" : [ "YEAR", "QUARTER", "MONTH", "WEEK", "DAY", "HOUR", "QUARTER_HOUR", "MINUTE", "SECOND" ] + } + }, { + "name" : "startTimestamp", + "in" : "query", + "description" : "The start timestamp of the time span, in ISO8601 representation.", + "required" : false, + "style" : "form", + "explode" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "endTimestamp", + "in" : "query", + "description" : "The end timestamp of the time span, in ISO8601 representation.", + "required" : false, + "style" : "form", + "explode" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "emailIds", + "in" : "query", + "description" : "Filter by email IDs. Only include statistics of emails with these IDs.", + "required" : false, + "style" : "form", + "explode" : true, + "schema" : { + "type" : "array", + "items" : { + "type" : "integer", + "format" : "int64" + } + } + } ], + "responses" : { + "200" : { + "description" : "successful operation", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/CollectionResponseWithTotalEmailStatisticIntervalNoPaging" + }, + "example" : { + "total" : 3, + "results" : [ { + "aggregateStatistic" : { + "counters" : { + "sent" : 704, + "open" : 443, + "delivered" : 704, + "bounce" : 0, + "unsubscribed" : 0, + "click" : 8, + "dropped" : 1, + "selected" : 705, + "spamreport" : 0, + "suppressed" : 0, + "hardbounced" : 0, + "softbounced" : 0, + "pending" : 0, + "contactslost" : 0, + "notsent" : 1 + }, + "deviceBreakdown" : { + "open_device_type" : { + "computer" : 44, + "mobile" : 8, + "unknown" : 391 + }, + "click_device_type" : { + "computer" : 6, + "mobile" : 2, + "unknown" : 0 + } + }, + "qualifierStats" : { }, + "ratios" : { + "clickratio" : 1.136, + "clickthroughratio" : 1.806, + "deliveredratio" : 100, + "openratio" : 62.926, + "unsubscribedratio" : 0, + "spamreportratio" : 0, + "bounceratio" : 0, + "hardbounceratio" : 0, + "softbounceratio" : 0, + "contactslostratio" : 0, + "pendingratio" : 0, + "notsentratio" : 0.142 + } + }, + "interval" : { + "end" : "2020-06-01T04:00:00Z", + "start" : "2020-05-01T04:00:00Z" + } + }, { + "aggregations" : { + "counters" : { + "sent" : 5, + "open" : 4, + "delivered" : 4, + "bounce" : 1, + "unsubscribed" : 0, + "click" : 0, + "dropped" : 1, + "selected" : 6, + "spamreport" : 0, + "suppressed" : 0, + "hardbounced" : 1, + "softbounced" : 0, + "pending" : 0, + "contactslost" : 1, + "notsent" : 1 + }, + "deviceBreakdown" : { + "open_device_type" : { + "computer" : 0, + "mobile" : 0, + "unknown" : 4 + }, + "click_device_type" : { + "computer" : 0, + "mobile" : 0, + "unknown" : 0 + } + }, + "qualifierStats" : { }, + "ratios" : { + "clickratio" : 0, + "clickthroughratio" : 0, + "deliveredratio" : 80, + "openratio" : 100, + "unsubscribedratio" : 0, + "spamreportratio" : 0, + "bounceratio" : 20, + "hardbounceratio" : 20, + "softbounceratio" : 0, + "contactslostratio" : 20, + "pendingratio" : 0, + "notsentratio" : 16.667 + } + }, + "interval" : { + "end" : "2020-07-01T04:00:00Z", + "start" : "2020-06-01T04:00:00Z" + } + }, { + "aggregations" : { + "counters" : { }, + "deviceBreakdown" : { }, + "qualifierStats" : { }, + "ratios" : { } + }, + "interval" : { + "end" : "2020-07-02T00:00:00Z", + "start" : "2020-07-01T04:00:00Z" + } + } ] + } + } + } + }, + "default" : { + "$ref" : "#/components/responses/Error" + } + }, + "security" : [ { + "private_apps_legacy" : [ "content" ] + }, { + "oauth2_legacy" : [ "content" ] + } ] + } + }, + "/{emailId}/ab-test/get-variation" : { + "get" : { + "tags" : [ "Marketing Emails" ], + "summary" : "Get the variation of a an A/B marketing email", + "description" : "This endpoint lets you obtain the variation of an A/B marketing email. If the email is variation A (master) it will return variation B (variant) and vice versa.", + "operationId" : "get-/{emailId}/ab-test/get-variation", + "parameters" : [ { + "name" : "emailId", + "in" : "path", + "description" : "The ID of an A/B marketing email.", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "successful operation", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/PublicEmail" + } + } + } + }, + "default" : { + "$ref" : "#/components/responses/Error" + } + }, + "security" : [ { + "private_apps_legacy" : [ "content" ] + }, { + "oauth2_legacy" : [ "content" ] + } ] + } + }, + "/{emailId}/draft/reset" : { + "post" : { + "tags" : [ "Marketing Emails" ], + "summary" : "Reset Draft", + "description" : "Resets the draft back to a copy of the live object.", + "operationId" : "post-/{emailId}/draft/reset", + "parameters" : [ { + "name" : "emailId", + "in" : "path", + "description" : "The marketing email ID.", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "204" : { + "description" : "No content", + "content" : { } + }, + "default" : { + "$ref" : "#/components/responses/Error" + } + }, + "security" : [ { + "private_apps_legacy" : [ "content" ] + }, { + "oauth2_legacy" : [ "content" ] + } ] + } + }, + "/{emailId}/revisions/{revisionId}/restore-to-draft" : { + "post" : { + "tags" : [ "Marketing Emails" ], + "summary" : "Restore a revision of a marketing email to DRAFT state", + "description" : "Restores a previous revision of a marketing email to DRAFT state. If there is currently something in the draft for that object, it is overwritten. ", + "operationId" : "post-/{emailId}/revisions/{revisionId}/restore-to-draft", + "parameters" : [ { + "name" : "emailId", + "in" : "path", + "description" : "The marketing email ID.", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "revisionId", + "in" : "path", + "description" : "The ID of a revision.", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "integer", + "format" : "int64" + } + } ], + "responses" : { + "200" : { + "description" : "successful operation", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/PublicEmail" + } + } + } + }, + "default" : { + "$ref" : "#/components/responses/Error" + } + }, + "security" : [ { + "private_apps_legacy" : [ "content" ] + }, { + "oauth2_legacy" : [ "content" ] + } ] + } + }, + "/{emailId}/draft" : { + "get" : { + "tags" : [ "Marketing Emails" ], + "summary" : "Get draft version of a marketing email", + "description" : "Get the draft version of an email (if it exists). If no draft version exists, the published email is returned.", + "operationId" : "get-/{emailId}/draft", + "parameters" : [ { + "name" : "emailId", + "in" : "path", + "description" : "The marketing email ID.", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "successful operation", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/PublicEmail" + } + } + } + }, + "default" : { + "$ref" : "#/components/responses/Error" + } + }, + "security" : [ { + "private_apps_legacy" : [ "content" ] + }, { + "oauth2_legacy" : [ "content" ] + } ] + }, + "patch" : { + "tags" : [ "Marketing Emails" ], + "summary" : "Create or update draft version", + "description" : "Create or update the draft version of a marketing email. If no draft exists, the system creates a draft from the current “live” email then applies the request body to that draft. The draft version only lives on the buffer—the email is not cloned.", + "operationId" : "patch-/{emailId}/draft", + "parameters" : [ { + "name" : "emailId", + "in" : "path", + "description" : "The marketing email ID.", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "description" : "A marketing email object with properties that should overwrite the corresponding properties in the email's current draft.", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/EmailUpdateRequest" + } + } + }, + "required" : true + }, + "responses" : { + "200" : { + "description" : "successful operation", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/PublicEmail" + } + } + } + }, + "default" : { + "$ref" : "#/components/responses/Error" + } + }, + "security" : [ { + "private_apps_legacy" : [ "content" ] + }, { + "oauth2_legacy" : [ "content" ] + } ] + } + }, + "/{emailId}/revisions" : { + "get" : { + "tags" : [ "Marketing Emails" ], + "summary" : "Get revisions of a marketing email", + "description" : "Get a list of all versions of a marketing email, with each entry including the full state of that particular version. The current revision has the ID -1.", + "operationId" : "get-/{emailId}/revisions", + "parameters" : [ { + "name" : "emailId", + "in" : "path", + "description" : "The marketing email ID.", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "after", + "in" : "query", + "description" : "The cursor token value to get the next set of results. You can get this from the `paging.next.after` JSON property of a paged response containing more results.", + "required" : false, + "style" : "form", + "explode" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "before", + "in" : "query", + "description" : "The cursor token value to get the previous set of results. You can get this from the `paging.prev.before` JSON property of a paged response containing more results.", + "required" : false, + "style" : "form", + "explode" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "limit", + "in" : "query", + "description" : "The maximum number of results to return. Default is 100.", + "required" : false, + "style" : "form", + "explode" : true, + "schema" : { + "type" : "integer", + "format" : "int32" + } + } ], + "responses" : { + "200" : { + "description" : "successful operation", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/CollectionResponseWithTotalVersionPublicEmail" + } + } + } + }, + "default" : { + "$ref" : "#/components/responses/Error" + } + }, + "security" : [ { + "private_apps_legacy" : [ "content" ] + }, { + "oauth2_legacy" : [ "content" ] + } ] + } + }, + "/{emailId}/unpublish" : { + "post" : { + "tags" : [ "Marketing Emails" ], + "summary" : "Unpublish or cancel a marketing email.", + "description" : "If you have a Marketing Hub Enterprise account or the transactional email add-on, you can use this endpoint to unpublish an automated email or cancel a regular email. If the email is already in the process of being sent, canceling might not be possible.", + "operationId" : "post-/{emailId}/unpublish", + "parameters" : [ { + "name" : "emailId", + "in" : "path", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "204" : { + "description" : "No content", + "content" : { } + }, + "default" : { + "$ref" : "#/components/responses/Error" + } + }, + "security" : [ { + "oauth2_legacy" : [ "transactional-email" ] + }, { + "oauth2_legacy" : [ "marketing-email" ] + }, { + "private_apps_legacy" : [ "marketing-email" ] + }, { + "private_apps_legacy" : [ "transactional-email" ] + } ], + "x-hubspot-rate-limit-exemptions" : [ "ten-secondly" ] + } + }, + "/{emailId}/revisions/{revisionId}" : { + "get" : { + "tags" : [ "Marketing Emails" ], + "summary" : "Get a revision of a marketing email.", + "description" : "Get a specific revision of a marketing email.", + "operationId" : "get-/{emailId}/revisions/{revisionId}", + "parameters" : [ { + "name" : "emailId", + "in" : "path", + "description" : "The marketing email ID.", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "revisionId", + "in" : "path", + "description" : "The ID of a revision.", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "successful operation", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/VersionPublicEmail" + } + } + } + }, + "default" : { + "$ref" : "#/components/responses/Error" + } + }, + "security" : [ { + "private_apps_legacy" : [ "content" ] + }, { + "oauth2_legacy" : [ "content" ] + } ] + } + }, + "/clone" : { + "post" : { + "tags" : [ "Marketing Emails" ], + "summary" : "Clone a marketing email.", + "description" : "This will create a duplicate email with the same properties as the original, with the exception of a unique ID.", + "operationId" : "post-/clone", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/ContentCloneRequestVNext" + } + } + }, + "required" : true + }, + "responses" : { + "200" : { + "description" : "successful operation", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/PublicEmail" + } + } + } + }, + "default" : { + "$ref" : "#/components/responses/Error" + } + }, + "security" : [ { + "private_apps_legacy" : [ "content" ] + }, { + "oauth2_legacy" : [ "content" ] + } ] + } + }, + "/" : { + "get" : { + "tags" : [ "Marketing Emails" ], + "summary" : "Get all marketing emails for a HubSpot account.", + "description" : "The results can be filtered, allowing you to find a specific set of emails. See the table below for a full list of filtering options.", + "operationId" : "get-/", + "parameters" : [ { + "name" : "createdAt", + "in" : "query", + "description" : "Only return emails created at exactly the specified time.", + "required" : false, + "style" : "form", + "explode" : true, + "schema" : { + "type" : "string", + "format" : "datetime" + } + }, { + "name" : "createdAfter", + "in" : "query", + "description" : "Only return emails created after the specified time.", + "required" : false, + "style" : "form", + "explode" : true, + "schema" : { + "type" : "string", + "format" : "datetime" + } + }, { + "name" : "createdBefore", + "in" : "query", + "description" : "Only return emails created before the specified time.", + "required" : false, + "style" : "form", + "explode" : true, + "schema" : { + "type" : "string", + "format" : "datetime" + } + }, { + "name" : "updatedAt", + "in" : "query", + "description" : "Only return emails last updated at exactly the specified time.", + "required" : false, + "style" : "form", + "explode" : true, + "schema" : { + "type" : "string", + "format" : "datetime" + } + }, { + "name" : "updatedAfter", + "in" : "query", + "description" : "Only return emails last updated after the specified time.", + "required" : false, + "style" : "form", + "explode" : true, + "schema" : { + "type" : "string", + "format" : "datetime" + } + }, { + "name" : "updatedBefore", + "in" : "query", + "description" : "Only return emails last updated before the specified time.", + "required" : false, + "style" : "form", + "explode" : true, + "schema" : { + "type" : "string", + "format" : "datetime" + } + }, { + "name" : "sort", + "in" : "query", + "description" : "Specifies which fields to use for sorting results. Valid fields are `name`, `createdAt`, `updatedAt`, `createdBy`, `updatedBy`. `createdAt` will be used by default.", + "required" : false, + "style" : "form", + "explode" : true, + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + }, { + "name" : "after", + "in" : "query", + "description" : "The cursor token value to get the next set of results. You can get this from the `paging.next.after` JSON property of a paged response containing more results.", + "required" : false, + "style" : "form", + "explode" : true, + "schema" : { + "type" : "string" + } + }, { + "name" : "limit", + "in" : "query", + "description" : "The maximum number of results to return. Default is 100.", + "required" : false, + "style" : "form", + "explode" : true, + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "name" : "includeStats", + "in" : "query", + "description" : "Include statistics with emails.", + "required" : false, + "style" : "form", + "explode" : true, + "schema" : { + "type" : "boolean" + } + }, { + "name" : "marketingCampaignNames", + "in" : "query", + "required" : false, + "style" : "form", + "explode" : true, + "schema" : { + "type" : "boolean" + } + }, { + "name" : "workflowNames", + "in" : "query", + "required" : false, + "style" : "form", + "explode" : true, + "schema" : { + "type" : "boolean" + } + }, { + "name" : "type", + "in" : "query", + "description" : "Email types to be filtered by. Multiple types can be included. All emails will be returned if not present.", + "required" : false, + "style" : "form", + "explode" : true, + "schema" : { + "type" : "string", + "enum" : [ "AB_EMAIL", "BATCH_EMAIL", "LOCALTIME_EMAIL", "AUTOMATED_AB_EMAIL", "BLOG_EMAIL", "BLOG_EMAIL_CHILD", "RSS_EMAIL", "RSS_EMAIL_CHILD", "RESUBSCRIBE_EMAIL", "OPTIN_EMAIL", "OPTIN_FOLLOWUP_EMAIL", "AUTOMATED_EMAIL", "FEEDBACK_CES_EMAIL", "FEEDBACK_CUSTOM_EMAIL", "FEEDBACK_CUSTOM_SURVEY_EMAIL", "FEEDBACK_NPS_EMAIL", "FOLLOWUP_EMAIL", "LEADFLOW_EMAIL", "SINGLE_SEND_API", "MARKETING_SINGLE_SEND_API", "SMTP_TOKEN", "TICKET_EMAIL", "MEMBERSHIP_REGISTRATION_EMAIL", "MEMBERSHIP_PASSWORD_SAVED_EMAIL", "MEMBERSHIP_PASSWORD_RESET_EMAIL", "MEMBERSHIP_EMAIL_VERIFICATION_EMAIL", "MEMBERSHIP_PASSWORDLESS_AUTH_EMAIL", "MEMBERSHIP_REGISTRATION_FOLLOW_UP_EMAIL", "MEMBERSHIP_OTP_LOGIN_EMAIL", "MEMBERSHIP_FOLLOW_UP_EMAIL", "MEMBERSHIP_VERIFICATION_EMAIL" ] + } + }, { + "name" : "isPublished", + "in" : "query", + "description" : "Filter by published/draft emails. All emails will be returned if not present.", + "required" : false, + "style" : "form", + "explode" : true, + "schema" : { + "type" : "boolean" + } + }, { + "name" : "includedProperties", + "in" : "query", + "required" : false, + "style" : "form", + "explode" : true, + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + }, { + "name" : "archived", + "in" : "query", + "description" : "Specifies whether to return archived emails. Defaults to `false`.", + "required" : false, + "style" : "form", + "explode" : true, + "schema" : { + "type" : "boolean" + } + } ], + "responses" : { + "200" : { + "description" : "successful operation", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/CollectionResponseWithTotalPublicEmailForwardPaging" + }, + "example" : { + "total" : 1, + "results" : [ { + "activeDomain" : "", + "archived" : false, + "content" : { + "templatePath" : "generated_layouts/4701002617.html", + "widgets" : { + "Social_Sharing" : { + "body" : { }, + "child_css" : { }, + "css" : { }, + "id" : "Social_Sharing", + "label" : "Social Sharing", + "name" : "Social_Sharing", + "type" : "social_sharing" + }, + "column1" : { + "body" : { + "html" : "" + }, + "child_css" : { }, + "css" : { }, + "id" : "column1", + "label" : "Column Above Body", + "name" : "column1", + "type" : "rich_text" + }, + "hs_email_body" : { + "body" : { + "html" : "

Hi {{contact.firstname}},

\n

This is an example of an email in HubSpot. You can add and format text, include your own images, and even personalize the message with details about the recipient, like their {{contact.company}} or {{contact.city}}.

\n

In HubSpot, the Email tool is closely linked with the Lists and Workflows tools. You can create a list of recipients on the \"Sending\" tab up above. If you want this email to be part of a nurturing series, you can make it part of a workflow.

\n

Need help? Check out the documentation on creating an email, creating a list, or creating a workflow.

" + }, + "child_css" : { }, + "css" : { }, + "id" : "hs_email_body", + "label" : "Main Email Body", + "name" : "hs_email_body", + "type" : "rich_text" + }, + "logo_image" : { + "body" : { + "alt" : "vast.png", + "height" : 135, + "link" : "", + "override_inherited_src" : false, + "src" : "", + "suppress_company_name" : false, + "width" : 413 + }, + "child_css" : { }, + "css" : { }, + "id" : "logo_image", + "label" : "Logo", + "name" : "logo_image", + "type" : "logo" + }, + "module_148465958537693" : { + "body" : { + "html" : "

Something Powerful

\n

Tell The Reader More

\n

The headline and subheader tells us what you're offering, and the form header closes the deal. Over here you can explain why your offer is so great it's worth filling out a form for.

\n

Remember:

\n
    \n
  • Bullets are great
  • \n
  • For spelling out benefits and
  • \n
  • Turning visitors into leads.
  • \n
" + }, + "child_css" : { }, + "css" : { }, + "id" : "module_148465958537693", + "label" : "Rich Text", + "name" : "module_148465958537693", + "type" : "rich_text" + }, + "preview_text" : { + "body" : { + "value" : "" + }, + "child_css" : { }, + "css" : { }, + "id" : "preview_text", + "label" : "Preview Text This will be used as the preview text that displays in some email clients", + "name" : "preview_text", + "type" : "text" + }, + "social_sharing_label" : { + "body" : { }, + "child_css" : { }, + "css" : { }, + "id" : "social_sharing_label", + "label" : "Social Sharing Label", + "name" : "social_sharing_label", + "type" : "text" + } + } + }, + "createdAt" : "2017-02-16T13:05:18.390Z", + "createdById" : 2983922, + "from" : { + "fromName" : "Bruce Wayne", + "replyTo" : "test@yahoo.com" + }, + "id" : "4613448134", + "isPublished" : true, + "isTransactional" : false, + "name" : "Fail dmarc", + "sendOnPublish" : true, + "state" : "PUBLISHED", + "stats" : { + "counters" : { + "sent" : 1, + "open" : 0, + "delivered" : 0, + "bounce" : 1, + "unsubscribed" : 0, + "click" : 0, + "reply" : 0, + "dropped" : 0, + "selected" : 1, + "spamreport" : 0, + "suppressed" : 0, + "hardbounced" : 0, + "softbounced" : 1, + "pending" : 0, + "contactslost" : 0, + "notsent" : 0 + }, + "deviceBreakdown" : { + "open_device_type" : { + "computer" : 0, + "mobile" : 0, + "unknown" : 0 + }, + "click_device_type" : { + "computer" : 0, + "mobile" : 0, + "unknown" : 0 + } + }, + "qualifierStats" : { }, + "ratios" : { + "clickratio" : 0, + "clickthroughratio" : 0, + "deliveredratio" : 0, + "openratio" : 0, + "replyratio" : 0, + "unsubscribedratio" : 0, + "spamreportratio" : 0, + "bounceratio" : 100, + "hardbounceratio" : 0, + "softbounceratio" : 100, + "contactslostratio" : 0, + "pendingratio" : 0, + "notsentratio" : 0 + } + }, + "subcategory" : "batch", + "subject" : "Subject", + "subscriptionDetails" : { + "officeLocationId" : "4701002382", + "subscriptionId" : 1264909 + }, + "to" : { + "contactIds" : { + "include" : [ 51 ] + }, + "contactLists" : { }, + "suppressGraymail" : true + }, + "type" : "BATCH_EMAIL", + "updatedAt" : "2017-02-16T13:08:24.853Z", + "updatedById" : 2983922 + } ], + "paging" : { + "next" : { + "after" : "MjA%3D", + "link" : "https://api.hubapi.com/marketing/v3/emails/emails?after=MjA%3D" + }, + "prev" : { + "before" : "MTA%3D", + "link" : "https://api.hubapi.com/marketing/v3/emails/emails?after=MTA%3D" + } + } + } + } + } + }, + "default" : { + "$ref" : "#/components/responses/Error" + } + }, + "security" : [ { + "private_apps_legacy" : [ "content" ] + }, { + "oauth2_legacy" : [ "content" ] + } ] + }, + "post" : { + "tags" : [ "Marketing Emails" ], + "summary" : "Create a new marketing email.", + "description" : "Use this endpoint to create a new marketing email.", + "operationId" : "post-/", + "parameters" : [ ], + "requestBody" : { + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/EmailCreateRequest" + } + } + }, + "required" : true + }, + "responses" : { + "201" : { + "description" : "successful operation", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/PublicEmail" + } + } + } + }, + "default" : { + "$ref" : "#/components/responses/Error" + } + }, + "security" : [ { + "private_apps_legacy" : [ "content" ] + }, { + "oauth2_legacy" : [ "content" ] + } ] + } + }, + "/{emailId}/revisions/{revisionId}/restore" : { + "post" : { + "tags" : [ "Marketing Emails" ], + "summary" : "Restore a revision of a marketing email", + "description" : "Restores a previous revision of a marketing email. The current revision becomes old, and the restored revision is given a new version number.", + "operationId" : "post-/{emailId}/revisions/{revisionId}/restore", + "parameters" : [ { + "name" : "emailId", + "in" : "path", + "description" : "The marketing email ID.", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "revisionId", + "in" : "path", + "description" : "The ID of a revision.", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "204" : { + "description" : "No content", + "content" : { } + }, + "default" : { + "$ref" : "#/components/responses/Error" + } + }, + "security" : [ { + "private_apps_legacy" : [ "content" ] + }, { + "oauth2_legacy" : [ "content" ] + } ] + } + }, + "/{emailId}" : { + "get" : { + "tags" : [ "Marketing Emails" ], + "summary" : "Get the details of a specified marketing email.", + "description" : "Get the details for a marketing email.", + "operationId" : "get-/{emailId}", + "parameters" : [ { + "name" : "emailId", + "in" : "path", + "description" : "The marketing email ID.", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "includeStats", + "in" : "query", + "description" : "Include statistics with email", + "required" : false, + "style" : "form", + "explode" : true, + "schema" : { + "type" : "boolean" + } + }, { + "name" : "marketingCampaignNames", + "in" : "query", + "required" : false, + "style" : "form", + "explode" : true, + "schema" : { + "type" : "boolean" + } + }, { + "name" : "workflowNames", + "in" : "query", + "required" : false, + "style" : "form", + "explode" : true, + "schema" : { + "type" : "boolean" + } + }, { + "name" : "includedProperties", + "in" : "query", + "required" : false, + "style" : "form", + "explode" : true, + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + }, { + "name" : "archived", + "in" : "query", + "description" : "Whether to return only results that have been archived.", + "required" : false, + "style" : "form", + "explode" : true, + "schema" : { + "type" : "boolean" + } + } ], + "responses" : { + "200" : { + "description" : "successful operation", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/PublicEmail" + } + } + } + }, + "default" : { + "$ref" : "#/components/responses/Error" + } + }, + "security" : [ { + "private_apps_legacy" : [ "content" ] + }, { + "oauth2_legacy" : [ "content" ] + } ] + }, + "delete" : { + "tags" : [ "Marketing Emails" ], + "summary" : "Delete a marketing email.", + "operationId" : "delete-/{emailId}", + "parameters" : [ { + "name" : "emailId", + "in" : "path", + "description" : "The ID of the marketing email to delete.", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "archived", + "in" : "query", + "description" : "Whether to return only results that have been archived.", + "required" : false, + "style" : "form", + "explode" : true, + "schema" : { + "type" : "boolean" + } + } ], + "responses" : { + "204" : { + "description" : "No content", + "content" : { } + }, + "default" : { + "$ref" : "#/components/responses/Error" + } + }, + "security" : [ { + "private_apps_legacy" : [ "content" ] + }, { + "oauth2_legacy" : [ "content" ] + } ] + }, + "patch" : { + "tags" : [ "Marketing Emails" ], + "summary" : "Update a marketing email.", + "description" : "Change properties of a marketing email.", + "operationId" : "patch-/{emailId}", + "parameters" : [ { + "name" : "emailId", + "in" : "path", + "description" : "The ID of the marketing email that should get updated", + "required" : true, + "style" : "simple", + "explode" : false, + "schema" : { + "type" : "string" + } + }, { + "name" : "archived", + "in" : "query", + "description" : "Whether to return only results that have been archived.", + "required" : false, + "style" : "form", + "explode" : true, + "schema" : { + "type" : "boolean" + } + } ], + "requestBody" : { + "description" : "A marketing email object with properties that should overwrite the corresponding properties of the marketing email.", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/EmailUpdateRequest" + } + } + }, + "required" : true + }, + "responses" : { + "200" : { + "description" : "successful operation", + "content" : { + "application/json" : { + "schema" : { + "$ref" : "#/components/schemas/PublicEmail" + } + } + } + }, + "default" : { + "$ref" : "#/components/responses/Error" + } + }, + "security" : [ { + "private_apps_legacy" : [ "content" ] + }, { + "oauth2_legacy" : [ "content" ] + } ] + } + } + }, + "components" : { + "schemas" : { + "PublicEmailToDetails" : { + "type" : "object", + "properties" : { + "contactIlsLists" : { + "$ref" : "#/components/schemas/PublicEmailRecipients" + }, + "limitSendFrequency" : { + "type" : "boolean", + "description" : "" + }, + "suppressGraymail" : { + "type" : "boolean", + "description" : "" + }, + "contactIds" : { + "$ref" : "#/components/schemas/PublicEmailRecipients" + }, + "contactLists" : { + "$ref" : "#/components/schemas/PublicEmailRecipients" + } + }, + "description" : "Data structure representing the to fields of the email." + }, + "PublicDividerStyleSettings" : { + "type" : "object", + "properties" : { + "color" : { + "type" : "object", + "properties" : { } + }, + "lineType" : { + "type" : "string" + }, + "height" : { + "type" : "integer", + "format" : "int32" + } + } + }, + "CollectionResponseWithTotalVersionPublicEmail" : { + "required" : [ "results", "total" ], + "type" : "object", + "properties" : { + "total" : { + "type" : "integer", + "description" : "Total number of content emails.", + "format" : "int32" + }, + "paging" : { + "$ref" : "#/components/schemas/Paging" + }, + "results" : { + "type" : "array", + "description" : "Collection of emails.", + "items" : { + "$ref" : "#/components/schemas/VersionPublicEmail" + } + } + }, + "description" : "Response object for collections of marketing emails with pagination information." + }, + "PublicEmail" : { + "required" : [ "content", "from", "id", "name", "state", "subcategory", "subject", "to" ], + "type" : "object", + "properties" : { + "feedbackSurveyId" : { + "type" : "string", + "description" : "The ID of the feedback survey linked to the email." + }, + "subject" : { + "type" : "string", + "description" : "The subject of the email.", + "example" : "My subject" + }, + "publishDate" : { + "type" : "string", + "description" : "The date and time the email is scheduled for, in ISO8601 representation. This is only used in local time or scheduled emails.", + "format" : "datetime", + "example" : "2020-11-30T18:44:20.387+00:00" + }, + "isTransactional" : { + "type" : "boolean", + "description" : "Returns whether the email is a transactional email or not. This is read only.", + "example" : false + }, + "language" : { + "type" : "string", + "example" : "en", + "enum" : [ "af", "af-na", "af-za", "agq", "agq-cm", "ak", "ak-gh", "am", "am-et", "ann", "ann-ng", "ar", "ar-001", "ar-ae", "ar-bh", "ar-dj", "ar-dz", "ar-eg", "ar-eh", "ar-er", "ar-il", "ar-iq", "ar-jo", "ar-km", "ar-kw", "ar-lb", "ar-ly", "ar-ma", "ar-mr", "ar-om", "ar-ps", "ar-qa", "ar-sa", "ar-sd", "ar-so", "ar-ss", "ar-sy", "ar-td", "ar-tn", "ar-ye", "as", "asa", "asa-tz", "ast", "ast-es", "as-in", "az", "az-az", "bas", "bas-cm", "be", "bem", "bem-zm", "bez", "bez-tz", "be-by", "bg", "bgc", "bgc-in", "bg-bg", "bho", "bho-in", "bm", "bm-ml", "bn", "bn-bd", "bn-in", "bo", "bo-cn", "bo-in", "br", "brx", "brx-in", "br-fr", "bs", "bs-ba", "ca", "ca-ad", "ca-es", "ca-fr", "ca-it", "ccp", "ccp-bd", "ccp-in", "ce", "ceb", "ceb-ph", "ce-ru", "cgg", "cgg-ug", "chr", "chr-us", "ckb", "ckb-iq", "ckb-ir", "cs", "cs-cz", "cu", "cu-ru", "cv", "cv-ru", "cy", "cy-gb", "da", "dav", "dav-ke", "da-dk", "da-gl", "de", "de-at", "de-be", "de-ch", "de-de", "de-gr", "de-it", "de-li", "de-lu", "dje", "dje-ne", "doi", "doi-in", "dsb", "dsb-de", "dua", "dua-cm", "dyo", "dyo-sn", "dz", "dz-bt", "ebu", "ebu-ke", "ee", "ee-gh", "ee-tg", "el", "el-cy", "el-gr", "en", "en-001", "en-150", "en-ae", "en-ag", "en-ai", "en-as", "en-at", "en-au", "en-bb", "en-be", "en-bi", "en-bm", "en-bs", "en-bw", "en-bz", "en-ca", "en-cc", "en-ch", "en-ck", "en-cm", "en-cn", "en-cx", "en-cy", "en-de", "en-dg", "en-dk", "en-dm", "en-ee", "en-er", "en-fi", "en-fj", "en-fk", "en-fm", "en-fr", "en-gb", "en-gd", "en-gg", "en-gh", "en-gi", "en-gm", "en-gu", "en-gy", "en-hk", "en-ie", "en-il", "en-im", "en-in", "en-io", "en-je", "en-jm", "en-ke", "en-ki", "en-kn", "en-ky", "en-lc", "en-lr", "en-ls", "en-lu", "en-mg", "en-mh", "en-mo", "en-mp", "en-ms", "en-mt", "en-mu", "en-mv", "en-mw", "en-mx", "en-my", "en-na", "en-nf", "en-ng", "en-nl", "en-nr", "en-nu", "en-nz", "en-pg", "en-ph", "en-pk", "en-pn", "en-pr", "en-pw", "en-rw", "en-sb", "en-sc", "en-sd", "en-se", "en-sg", "en-sh", "en-si", "en-sl", "en-ss", "en-sx", "en-sz", "en-tc", "en-tk", "en-to", "en-tt", "en-tv", "en-tz", "en-ug", "en-um", "en-us", "en-vc", "en-vg", "en-vi", "en-vu", "en-ws", "en-za", "en-zm", "en-zw", "eo", "eo-001", "es", "es-419", "es-ar", "es-bo", "es-br", "es-bz", "es-cl", "es-co", "es-cr", "es-cu", "es-do", "es-ea", "es-ec", "es-es", "es-gq", "es-gt", "es-hn", "es-ic", "es-mx", "es-ni", "es-pa", "es-pe", "es-ph", "es-pr", "es-py", "es-sv", "es-us", "es-uy", "es-ve", "et", "et-ee", "eu", "eu-es", "ewo", "ewo-cm", "fa", "fa-af", "fa-ir", "ff", "ff-bf", "ff-cm", "ff-gh", "ff-gm", "ff-gn", "ff-gw", "ff-lr", "ff-mr", "ff-ne", "ff-ng", "ff-sl", "ff-sn", "fi", "fil", "fil-ph", "fi-fi", "fo", "fo-dk", "fo-fo", "fr", "frr", "frr-de", "fr-be", "fr-bf", "fr-bi", "fr-bj", "fr-bl", "fr-ca", "fr-cd", "fr-cf", "fr-cg", "fr-ch", "fr-ci", "fr-cm", "fr-dj", "fr-dz", "fr-fr", "fr-ga", "fr-gf", "fr-gn", "fr-gp", "fr-gq", "fr-ht", "fr-km", "fr-lu", "fr-ma", "fr-mc", "fr-mf", "fr-mg", "fr-ml", "fr-mq", "fr-mr", "fr-mu", "fr-nc", "fr-ne", "fr-pf", "fr-pm", "fr-re", "fr-rw", "fr-sc", "fr-sn", "fr-sy", "fr-td", "fr-tg", "fr-tn", "fr-vu", "fr-wf", "fr-yt", "fur", "fur-it", "fy", "fy-nl", "ga", "ga-gb", "ga-ie", "gd", "gd-gb", "gl", "gl-es", "gsw", "gsw-ch", "gsw-fr", "gsw-li", "gu", "guz", "guz-ke", "gu-in", "gv", "gv-im", "ha", "haw", "haw-us", "ha-gh", "ha-ne", "ha-ng", "he", "hi", "hi-in", "hr", "hr-ba", "hr-hr", "hsb", "hsb-de", "hu", "hu-hu", "hy", "hy-am", "ia", "ia-001", "id", "ig", "ig-ng", "ii", "ii-cn", "id-id", "is", "is-is", "it", "it-ch", "it-it", "it-sm", "it-va", "he-il", "ja", "ja-jp", "jgo", "jgo-cm", "yi", "yi-001", "jmc", "jmc-tz", "jv", "jv-id", "ka", "kab", "kab-dz", "kam", "kam-ke", "ka-ge", "kde", "kde-tz", "kea", "kea-cv", "kgp", "kgp-br", "khq", "khq-ml", "ki", "ki-ke", "kk", "kkj", "kkj-cm", "kk-kz", "kl", "kln", "kln-ke", "kl-gl", "km", "km-kh", "kn", "kn-in", "ko", "kok", "kok-in", "ko-kp", "ko-kr", "ks", "ksb", "ksb-tz", "ksf", "ksf-cm", "ksh", "ksh-de", "ks-in", "ku", "ku-tr", "kw", "kw-gb", "ky", "ky-kg", "lag", "lag-tz", "lb", "lb-lu", "lg", "lg-ug", "lkt", "lkt-us", "ln", "ln-ao", "ln-cd", "ln-cf", "ln-cg", "lo", "lo-la", "lrc", "lrc-iq", "lrc-ir", "lt", "lt-lt", "lu", "luo", "luo-ke", "luy", "luy-ke", "lu-cd", "lv", "lv-lv", "mai", "mai-in", "mas", "mas-ke", "mas-tz", "mdf", "mdf-ru", "mer", "mer-ke", "mfe", "mfe-mu", "mg", "mgh", "mgh-mz", "mgo", "mgo-cm", "mg-mg", "mi", "mi-nz", "mk", "mk-mk", "ml", "ml-in", "mn", "mni", "mni-in", "mn-mn", "mr", "mr-in", "ms", "ms-bn", "ms-id", "ms-my", "ms-sg", "mt", "mt-mt", "mua", "mua-cm", "my", "my-mm", "mzn", "mzn-ir", "naq", "naq-na", "nb", "nb-no", "nb-sj", "nd", "nds", "nds-de", "nds-nl", "nd-zw", "ne", "ne-in", "ne-np", "nl", "nl-aw", "nl-be", "nl-bq", "nl-ch", "nl-cw", "nl-lu", "nl-nl", "nl-sr", "nl-sx", "nmg", "nmg-cm", "nn", "nnh", "nnh-cm", "nn-no", "no", "no-no", "nus", "nus-ss", "nyn", "nyn-ug", "oc", "oc-es", "oc-fr", "om", "om-et", "om-ke", "or", "or-in", "os", "os-ge", "os-ru", "pa", "pa-in", "pa-pk", "pcm", "pcm-ng", "pis", "pis-sb", "pl", "pl-pl", "prg", "prg-001", "ps", "ps-af", "ps-pk", "pt", "pt-ao", "pt-br", "pt-ch", "pt-cv", "pt-gq", "pt-gw", "pt-lu", "pt-mo", "pt-mz", "pt-pt", "pt-st", "pt-tl", "qu", "qu-bo", "qu-ec", "qu-pe", "raj", "raj-in", "rm", "rm-ch", "rn", "rn-bi", "ro", "rof", "rof-tz", "ro-md", "ro-ro", "ru", "ru-by", "ru-kg", "ru-kz", "ru-md", "ru-ru", "ru-ua", "rw", "rwk", "rwk-tz", "rw-rw", "sa", "sah", "sah-ru", "saq", "saq-ke", "sat", "sat-in", "sa-in", "sbp", "sbp-tz", "sc", "sc-it", "sd", "sd-in", "sd-pk", "se", "seh", "seh-mz", "ses", "ses-ml", "se-fi", "se-no", "se-se", "sg", "sg-cf", "shi", "shi-ma", "si", "si-lk", "sk", "sk-sk", "sl", "sl-si", "smn", "smn-fi", "sms", "sms-fi", "sn", "sn-zw", "so", "so-dj", "so-et", "so-ke", "so-so", "sq", "sq-al", "sq-mk", "sq-xk", "sr", "sr-ba", "sr-cs", "sr-me", "sr-rs", "sr-xk", "su", "su-id", "sv", "sv-ax", "sv-fi", "sv-se", "sw", "sw-cd", "sw-ke", "sw-tz", "sw-ug", "sy", "ta", "ta-in", "ta-lk", "ta-my", "ta-sg", "te", "teo", "teo-ke", "teo-ug", "te-in", "tg", "tg-tj", "th", "th-th", "ti", "ti-er", "ti-et", "tk", "tk-tm", "tl", "to", "tok", "tok-001", "to-to", "tr", "tr-cy", "tr-tr", "tt", "tt-ru", "twq", "twq-ne", "tzm", "tzm-ma", "ug", "ug-cn", "uk", "uk-ua", "ur", "ur-in", "ur-pk", "uz", "uz-af", "uz-uz", "vai", "vai-lr", "vi", "vi-vn", "vo", "vo-001", "vun", "vun-tz", "wae", "wae-ch", "wo", "wo-sn", "xh", "xh-za", "xog", "xog-ug", "yav", "yav-cm", "yo", "yo-bj", "yo-ng", "yrl", "yrl-br", "yrl-co", "yrl-ve", "yue", "yue-cn", "yue-hk", "zgh", "zgh-ma", "zh", "zh-cn", "zh-hans", "zh-hant", "zh-hk", "zh-mo", "zh-sg", "zh-tw", "zu", "zu-za" ] + }, + "type" : { + "type" : "string", + "description" : "The email type, this is derived from other properties on the email such as subcategory.", + "example" : "BATCH_EMAIL", + "enum" : [ "AB_EMAIL", "BATCH_EMAIL", "LOCALTIME_EMAIL", "AUTOMATED_AB_EMAIL", "BLOG_EMAIL", "BLOG_EMAIL_CHILD", "RSS_EMAIL", "RSS_EMAIL_CHILD", "RESUBSCRIBE_EMAIL", "OPTIN_EMAIL", "OPTIN_FOLLOWUP_EMAIL", "AUTOMATED_EMAIL", "FEEDBACK_CES_EMAIL", "FEEDBACK_CUSTOM_EMAIL", "FEEDBACK_CUSTOM_SURVEY_EMAIL", "FEEDBACK_NPS_EMAIL", "FOLLOWUP_EMAIL", "LEADFLOW_EMAIL", "SINGLE_SEND_API", "MARKETING_SINGLE_SEND_API", "SMTP_TOKEN", "TICKET_EMAIL", "MEMBERSHIP_REGISTRATION_EMAIL", "MEMBERSHIP_PASSWORD_SAVED_EMAIL", "MEMBERSHIP_PASSWORD_RESET_EMAIL", "MEMBERSHIP_EMAIL_VERIFICATION_EMAIL", "MEMBERSHIP_PASSWORDLESS_AUTH_EMAIL", "MEMBERSHIP_REGISTRATION_FOLLOW_UP_EMAIL", "MEMBERSHIP_OTP_LOGIN_EMAIL", "MEMBERSHIP_FOLLOW_UP_EMAIL", "MEMBERSHIP_VERIFICATION_EMAIL" ] + }, + "content" : { + "$ref" : "#/components/schemas/PublicEmailContent" + }, + "businessUnitId" : { + "type" : "string" + }, + "webversion" : { + "$ref" : "#/components/schemas/PublicWebversionDetails" + }, + "workflowNames" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "archived" : { + "type" : "boolean", + "description" : "Determines if the email is archived or not.", + "example" : false + }, + "createdAt" : { + "type" : "string", + "description" : "The date and time of the email's creation, in ISO8601 representation.", + "format" : "datetime", + "example" : "2020-11-30T18:43:17.136+00:00" + }, + "stats" : { + "$ref" : "#/components/schemas/EmailStatisticsData" + }, + "jitterSendTime" : { + "type" : "boolean" + }, + "from" : { + "$ref" : "#/components/schemas/PublicEmailFromDetails" + }, + "id" : { + "type" : "string", + "description" : "The email ID.", + "example" : "38175169118" + }, + "state" : { + "type" : "string", + "description" : "The email state.", + "example" : "DRAFT", + "enum" : [ "AUTOMATED", "AUTOMATED_DRAFT", "AUTOMATED_SENDING", "AUTOMATED_FOR_FORM", "AUTOMATED_FOR_FORM_BUFFER", "AUTOMATED_FOR_FORM_DRAFT", "AUTOMATED_FOR_FORM_LEGACY", "BLOG_EMAIL_DRAFT", "BLOG_EMAIL_PUBLISHED", "DRAFT", "DRAFT_AB", "DRAFT_AB_VARIANT", "ERROR", "LOSER_AB_VARIANT", "PAGE_STUB", "PRE_PROCESSING", "PROCESSING", "PUBLISHED", "PUBLISHED_AB", "PUBLISHED_AB_VARIANT", "PUBLISHED_OR_SCHEDULED", "RSS_TO_EMAIL_DRAFT", "RSS_TO_EMAIL_PUBLISHED", "SCHEDULED", "SCHEDULED_AB", "SCHEDULED_OR_PUBLISHED", "AUTOMATED_AB", "AUTOMATED_AB_VARIANT", "AUTOMATED_DRAFT_AB", "AUTOMATED_DRAFT_ABVARIANT", "AUTOMATED_LOSER_ABVARIANT" ] + }, + "createdById" : { + "type" : "string", + "description" : "The id of the user who created the email.", + "example" : "3739732" + }, + "updatedAt" : { + "type" : "string", + "description" : "The date and time of the last update to the email, in ISO8601 representation.", + "format" : "datetime", + "example" : "2020-11-30T18:44:20.384+00:00" + }, + "rssData" : { + "$ref" : "#/components/schemas/PublicRssEmailDetails" + }, + "publishedAt" : { + "type" : "string", + "description" : "The date and time the email was published at, in ISO8601 representation.", + "format" : "datetime" + }, + "publishedById" : { + "type" : "string" + }, + "isPublished" : { + "type" : "boolean", + "description" : "Returns the published status of the email. This is read only.", + "example" : false + }, + "testing" : { + "$ref" : "#/components/schemas/PublicEmailTestingDetails" + }, + "updatedById" : { + "type" : "string", + "description" : "The id of the user who last updated the email.", + "example" : "3739732" + }, + "folderId" : { + "type" : "integer", + "format" : "int64" + }, + "subscriptionDetails" : { + "$ref" : "#/components/schemas/PublicEmailSubscriptionDetails" + }, + "deletedAt" : { + "type" : "string", + "format" : "datetime" + }, + "name" : { + "type" : "string", + "description" : "The name of the email, as displayed on the email dashboard.", + "example" : "My subject" + }, + "activeDomain" : { + "type" : "string", + "description" : "The active domain of the email.", + "example" : "test.hs-sites.com" + }, + "campaign" : { + "type" : "string", + "description" : "The campaign id on the email.", + "example" : "1b7f51a6-33c1-44d6-ba28-fe81f655dced" + }, + "to" : { + "$ref" : "#/components/schemas/PublicEmailToDetails" + }, + "subcategory" : { + "type" : "string", + "description" : "The email subcategory.", + "example" : "batch" + }, + "campaignName" : { + "type" : "string" + }, + "sendOnPublish" : { + "type" : "boolean", + "description" : "Determines whether the email will be sent immediately on publish.", + "example" : true + } + }, + "description" : "A marketing email", + "example" : { + "activeDomain" : "test.hs-sites.com", + "archived" : false, + "campaign" : "1b7f51a6-33c1-44d6-ba28-fe81f655dced", + "content" : { + "flexAreas" : { + "main" : { + "boxed" : false, + "isSingleColumnFullWidth" : false, + "sections" : [ { + "columns" : [ { + "id" : "column_1606761806181_0", + "widgets" : [ "module_160676180617911" ], + "width" : 12 + } ], + "id" : "section_1606761806181", + "style" : { + "backgroundColor" : "", + "backgroundType" : "CONTENT" + } + }, { + "columns" : [ { + "id" : "column-0-1", + "widgets" : [ "module-0-1-1" ], + "width" : 12 + } ], + "id" : "section-0", + "style" : { + "backgroundType" : "CONTENT", + "paddingBottom" : "40px", + "paddingTop" : "40px" + } + }, { + "columns" : [ { + "id" : "column-1-1", + "widgets" : [ "module-1-1-1" ], + "width" : 12 + } ], + "id" : "section-1", + "style" : { + "backgroundColor" : "", + "backgroundType" : "CONTENT", + "paddingBottom" : "0px", + "paddingTop" : "0px" + } + } ] + } + }, + "plainTextVersion" : "This is custom! View in browser ({{view_as_page_url}})\n\nHello {{ contact.firstname }},\n\nPlain text emails have minimal formatting so your reader can really focus on what you have to say. Introduce yourself and explain why you’re reaching out.\n\nEvery email should try to lead the reader to some kind of action. Use this space to describe why the reader should want to click on the link below. Put the link on its own line to really draw their eye to it.\n\nLink text\n\nNow it’s time to wrap up your email. Before your signature, thank the recipient for reading. You can also invite them to send this email to any of their colleagues who might be interested.\n\nAll the best,\n\nYour full name\n\nYour job title\n\nOther contact information\n\n{{site_settings.company_name}}, {{site_settings.company_street_address_1}}, {{site_settings.company_street_address_2}}, {{site_settings.company_city}}, {{site_settings.company_state}} {{site_settings.company_zip}}, {{site_settings.company_country}}, {{site_settings.company_phone}}\n\nUnsubscribe ({{unsubscribe_link_all}})\n\nManage preferences ({{unsubscribe_link}})", + "styleSettings" : { }, + "templatePath" : "@hubspot/email/dnd/plain_text.html", + "widgets" : { + "module-0-1-1" : { + "body" : { + "css_class" : "dnd-module", + "html" : "

Hello {{ contact.firstname }},

Plain text emails have minimal formatting so your reader can really focus on what you have to say. Introduce yourself and explain why you’re reaching out.

Every email should try to lead the reader to some kind of action. Use this space to describe why the reader should want to click on the link below. Put the link on its own line to really draw their eye to it.

Link text

Now it’s time to wrap up your email. Before your signature, thank the recipient for reading. You can also invite them to send this email to any of their colleagues who might be interested.

All the best,
Your full name
Your job title
Other contact information

", + "i18nKey" : "richText.plainText", + "path" : "@hubspot/rich_text", + "schema_version" : 2 + }, + "child_css" : { }, + "css" : { }, + "id" : "module-0-1-1", + "module_id" : 1155639, + "name" : "module-0-1-1", + "order" : 2, + "styles" : { }, + "type" : "module" + }, + "module-1-1-1" : { + "body" : { + "align" : "center", + "css_class" : "dnd-module", + "font" : { + "color" : "#23496d", + "font" : "Arial, sans-serif", + "size" : { + "units" : "px", + "value" : 12 + } + }, + "link_font" : { + "color" : "#00a4bd", + "font" : "Helvetica,Arial,sans-serif", + "size" : { + "units" : "px", + "value" : 12 + }, + "styles" : { + "bold" : false, + "italic" : false, + "underline" : true + } + }, + "path" : "@hubspot/email_footer", + "schema_version" : 2, + "unsubscribe_link_type" : "both" + }, + "child_css" : { }, + "css" : { }, + "id" : "module-1-1-1", + "module_id" : 2869621, + "name" : "module-1-1-1", + "order" : 3, + "styles" : { }, + "type" : "module" + }, + "module_160676180617911" : { + "body" : { + "font" : { + "color" : "#00a4bd", + "font" : "Arial, sans-serif", + "size" : { + "units" : "px", + "value" : 12 + }, + "styles" : { + "bold" : false, + "italic" : false, + "underline" : true + } + }, + "hs_enable_module_padding" : false, + "hs_wrapper_css" : { } + }, + "child_css" : { }, + "css" : { }, + "id" : "module_160676180617911", + "module_id" : 2794854, + "name" : "module_160676180617911", + "styles" : { }, + "type" : "module" + }, + "preview_text" : { + "body" : { + "value" : "" + }, + "child_css" : { }, + "css" : { }, + "id" : "preview_text", + "label" : "Preview Text This will be used as the preview text that displays in some email clients", + "name" : "preview_text", + "order" : 0, + "styles" : { }, + "type" : "text" + } + } + }, + "createdAt" : "2020-11-30T18:43:17.136Z", + "createdById" : 3739732, + "from" : { + "fromName" : "Bruce Wayne", + "replyTo" : "test@hubspot.com" + }, + "id" : "38175169118", + "isPublished" : false, + "isTransactional" : false, + "language" : "en", + "name" : "My subject", + "publishDate" : "2020-11-30T18:44:20.387Z", + "sendOnPublish" : true, + "state" : "DRAFT", + "subcategory" : "batch", + "subject" : "My subject", + "subscriptionDetails" : { + "officeLocationId" : "5449392956" + }, + "to" : { + "contactIds" : { }, + "contactLists" : { + "exclude" : [ 1 ], + "include" : [ 5 ] + }, + "suppressGraymail" : true + }, + "type" : "BATCH_EMAIL", + "updatedAt" : "2020-11-30T18:44:20.384Z", + "updatedById" : 3739732, + "webversion" : { + "expiresAt" : "2020-11-30T18:44:20.387Z", + "metaDescription" : "", + "redirectToPageId" : 0, + "redirectToUrl" : "http://www.example.org" + } + } + }, + "AbTestCreateRequestVNext" : { + "required" : [ "contentId", "variationName" ], + "type" : "object", + "properties" : { + "variationName" : { + "type" : "string" + }, + "contentId" : { + "type" : "string", + "description" : "ID of the object to test." + } + }, + "description" : "Request body object for creating A/B tests.", + "example" : { + "variantName" : "AB Test - Number 7", + "contentId" : "7" + } + }, + "EmailStatisticInterval" : { + "type" : "object", + "properties" : { + "interval" : { + "$ref" : "#/components/schemas/Interval" + }, + "aggregations" : { + "$ref" : "#/components/schemas/EmailStatisticsData" + } + } + }, + "ErrorDetail" : { + "required" : [ "message" ], + "type" : "object", + "properties" : { + "subCategory" : { + "type" : "string", + "description" : "A specific category that contains more specific detail about the error" + }, + "code" : { + "type" : "string", + "description" : "The status code associated with the error detail" + }, + "in" : { + "type" : "string", + "description" : "The name of the field or parameter in which the error was found." + }, + "context" : { + "type" : "object", + "additionalProperties" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "description" : "Context about the error condition", + "example" : { + "missingScopes" : [ "scope1", "scope2" ] + } + }, + "message" : { + "type" : "string", + "description" : "A human readable message describing the error along with remediation steps where appropriate" + } + } + }, + "CollectionResponseWithTotalPublicEmailForwardPaging" : { + "required" : [ "results", "total" ], + "type" : "object", + "properties" : { + "total" : { + "type" : "integer", + "description" : "Total number of content emails.", + "format" : "int32" + }, + "paging" : { + "$ref" : "#/components/schemas/ForwardPaging" + }, + "results" : { + "type" : "array", + "description" : "Collection of emails.", + "items" : { + "$ref" : "#/components/schemas/PublicEmail" + } + } + }, + "description" : "Response object for collections of marketing emails with pagination information." + }, + "ForwardPaging" : { + "type" : "object", + "properties" : { + "next" : { + "$ref" : "#/components/schemas/NextPage" + } + } + }, + "PublicButtonStyleSettings" : { + "type" : "object", + "properties" : { + "backgroundColor" : { + "type" : "string" + }, + "fontStyle" : { + "$ref" : "#/components/schemas/PublicFontStyle" + }, + "cornerRadius" : { + "type" : "integer", + "format" : "int32" + } + } + }, + "EmailUpdateRequest" : { + "type" : "object", + "properties" : { + "rssData" : { + "$ref" : "#/components/schemas/PublicRssEmailDetails" + }, + "subject" : { + "type" : "string", + "description" : "The subject of the email." + }, + "testing" : { + "$ref" : "#/components/schemas/PublicEmailTestingDetails" + }, + "publishDate" : { + "type" : "string", + "description" : "The date and time the email is scheduled for, in ISO8601 representation. This is only used in local time or scheduled emails.", + "format" : "datetime" + }, + "language" : { + "type" : "string", + "enum" : [ "af", "af-na", "af-za", "agq", "agq-cm", "ak", "ak-gh", "am", "am-et", "ann", "ann-ng", "ar", "ar-001", "ar-ae", "ar-bh", "ar-dj", "ar-dz", "ar-eg", "ar-eh", "ar-er", "ar-il", "ar-iq", "ar-jo", "ar-km", "ar-kw", "ar-lb", "ar-ly", "ar-ma", "ar-mr", "ar-om", "ar-ps", "ar-qa", "ar-sa", "ar-sd", "ar-so", "ar-ss", "ar-sy", "ar-td", "ar-tn", "ar-ye", "as", "asa", "asa-tz", "ast", "ast-es", "as-in", "az", "az-az", "bas", "bas-cm", "be", "bem", "bem-zm", "bez", "bez-tz", "be-by", "bg", "bgc", "bgc-in", "bg-bg", "bho", "bho-in", "bm", "bm-ml", "bn", "bn-bd", "bn-in", "bo", "bo-cn", "bo-in", "br", "brx", "brx-in", "br-fr", "bs", "bs-ba", "ca", "ca-ad", "ca-es", "ca-fr", "ca-it", "ccp", "ccp-bd", "ccp-in", "ce", "ceb", "ceb-ph", "ce-ru", "cgg", "cgg-ug", "chr", "chr-us", "ckb", "ckb-iq", "ckb-ir", "cs", "cs-cz", "cu", "cu-ru", "cv", "cv-ru", "cy", "cy-gb", "da", "dav", "dav-ke", "da-dk", "da-gl", "de", "de-at", "de-be", "de-ch", "de-de", "de-gr", "de-it", "de-li", "de-lu", "dje", "dje-ne", "doi", "doi-in", "dsb", "dsb-de", "dua", "dua-cm", "dyo", "dyo-sn", "dz", "dz-bt", "ebu", "ebu-ke", "ee", "ee-gh", "ee-tg", "el", "el-cy", "el-gr", "en", "en-001", "en-150", "en-ae", "en-ag", "en-ai", "en-as", "en-at", "en-au", "en-bb", "en-be", "en-bi", "en-bm", "en-bs", "en-bw", "en-bz", "en-ca", "en-cc", "en-ch", "en-ck", "en-cm", "en-cn", "en-cx", "en-cy", "en-de", "en-dg", "en-dk", "en-dm", "en-ee", "en-er", "en-fi", "en-fj", "en-fk", "en-fm", "en-fr", "en-gb", "en-gd", "en-gg", "en-gh", "en-gi", "en-gm", "en-gu", "en-gy", "en-hk", "en-ie", "en-il", "en-im", "en-in", "en-io", "en-je", "en-jm", "en-ke", "en-ki", "en-kn", "en-ky", "en-lc", "en-lr", "en-ls", "en-lu", "en-mg", "en-mh", "en-mo", "en-mp", "en-ms", "en-mt", "en-mu", "en-mv", "en-mw", "en-mx", "en-my", "en-na", "en-nf", "en-ng", "en-nl", "en-nr", "en-nu", "en-nz", "en-pg", "en-ph", "en-pk", "en-pn", "en-pr", "en-pw", "en-rw", "en-sb", "en-sc", "en-sd", "en-se", "en-sg", "en-sh", "en-si", "en-sl", "en-ss", "en-sx", "en-sz", "en-tc", "en-tk", "en-to", "en-tt", "en-tv", "en-tz", "en-ug", "en-um", "en-us", "en-vc", "en-vg", "en-vi", "en-vu", "en-ws", "en-za", "en-zm", "en-zw", "eo", "eo-001", "es", "es-419", "es-ar", "es-bo", "es-br", "es-bz", "es-cl", "es-co", "es-cr", "es-cu", "es-do", "es-ea", "es-ec", "es-es", "es-gq", "es-gt", "es-hn", "es-ic", "es-mx", "es-ni", "es-pa", "es-pe", "es-ph", "es-pr", "es-py", "es-sv", "es-us", "es-uy", "es-ve", "et", "et-ee", "eu", "eu-es", "ewo", "ewo-cm", "fa", "fa-af", "fa-ir", "ff", "ff-bf", "ff-cm", "ff-gh", "ff-gm", "ff-gn", "ff-gw", "ff-lr", "ff-mr", "ff-ne", "ff-ng", "ff-sl", "ff-sn", "fi", "fil", "fil-ph", "fi-fi", "fo", "fo-dk", "fo-fo", "fr", "frr", "frr-de", "fr-be", "fr-bf", "fr-bi", "fr-bj", "fr-bl", "fr-ca", "fr-cd", "fr-cf", "fr-cg", "fr-ch", "fr-ci", "fr-cm", "fr-dj", "fr-dz", "fr-fr", "fr-ga", "fr-gf", "fr-gn", "fr-gp", "fr-gq", "fr-ht", "fr-km", "fr-lu", "fr-ma", "fr-mc", "fr-mf", "fr-mg", "fr-ml", "fr-mq", "fr-mr", "fr-mu", "fr-nc", "fr-ne", "fr-pf", "fr-pm", "fr-re", "fr-rw", "fr-sc", "fr-sn", "fr-sy", "fr-td", "fr-tg", "fr-tn", "fr-vu", "fr-wf", "fr-yt", "fur", "fur-it", "fy", "fy-nl", "ga", "ga-gb", "ga-ie", "gd", "gd-gb", "gl", "gl-es", "gsw", "gsw-ch", "gsw-fr", "gsw-li", "gu", "guz", "guz-ke", "gu-in", "gv", "gv-im", "ha", "haw", "haw-us", "ha-gh", "ha-ne", "ha-ng", "he", "hi", "hi-in", "hr", "hr-ba", "hr-hr", "hsb", "hsb-de", "hu", "hu-hu", "hy", "hy-am", "ia", "ia-001", "id", "ig", "ig-ng", "ii", "ii-cn", "id-id", "is", "is-is", "it", "it-ch", "it-it", "it-sm", "it-va", "he-il", "ja", "ja-jp", "jgo", "jgo-cm", "yi", "yi-001", "jmc", "jmc-tz", "jv", "jv-id", "ka", "kab", "kab-dz", "kam", "kam-ke", "ka-ge", "kde", "kde-tz", "kea", "kea-cv", "kgp", "kgp-br", "khq", "khq-ml", "ki", "ki-ke", "kk", "kkj", "kkj-cm", "kk-kz", "kl", "kln", "kln-ke", "kl-gl", "km", "km-kh", "kn", "kn-in", "ko", "kok", "kok-in", "ko-kp", "ko-kr", "ks", "ksb", "ksb-tz", "ksf", "ksf-cm", "ksh", "ksh-de", "ks-in", "ku", "ku-tr", "kw", "kw-gb", "ky", "ky-kg", "lag", "lag-tz", "lb", "lb-lu", "lg", "lg-ug", "lkt", "lkt-us", "ln", "ln-ao", "ln-cd", "ln-cf", "ln-cg", "lo", "lo-la", "lrc", "lrc-iq", "lrc-ir", "lt", "lt-lt", "lu", "luo", "luo-ke", "luy", "luy-ke", "lu-cd", "lv", "lv-lv", "mai", "mai-in", "mas", "mas-ke", "mas-tz", "mdf", "mdf-ru", "mer", "mer-ke", "mfe", "mfe-mu", "mg", "mgh", "mgh-mz", "mgo", "mgo-cm", "mg-mg", "mi", "mi-nz", "mk", "mk-mk", "ml", "ml-in", "mn", "mni", "mni-in", "mn-mn", "mr", "mr-in", "ms", "ms-bn", "ms-id", "ms-my", "ms-sg", "mt", "mt-mt", "mua", "mua-cm", "my", "my-mm", "mzn", "mzn-ir", "naq", "naq-na", "nb", "nb-no", "nb-sj", "nd", "nds", "nds-de", "nds-nl", "nd-zw", "ne", "ne-in", "ne-np", "nl", "nl-aw", "nl-be", "nl-bq", "nl-ch", "nl-cw", "nl-lu", "nl-nl", "nl-sr", "nl-sx", "nmg", "nmg-cm", "nn", "nnh", "nnh-cm", "nn-no", "no", "no-no", "nus", "nus-ss", "nyn", "nyn-ug", "oc", "oc-es", "oc-fr", "om", "om-et", "om-ke", "or", "or-in", "os", "os-ge", "os-ru", "pa", "pa-in", "pa-pk", "pcm", "pcm-ng", "pis", "pis-sb", "pl", "pl-pl", "prg", "prg-001", "ps", "ps-af", "ps-pk", "pt", "pt-ao", "pt-br", "pt-ch", "pt-cv", "pt-gq", "pt-gw", "pt-lu", "pt-mo", "pt-mz", "pt-pt", "pt-st", "pt-tl", "qu", "qu-bo", "qu-ec", "qu-pe", "raj", "raj-in", "rm", "rm-ch", "rn", "rn-bi", "ro", "rof", "rof-tz", "ro-md", "ro-ro", "ru", "ru-by", "ru-kg", "ru-kz", "ru-md", "ru-ru", "ru-ua", "rw", "rwk", "rwk-tz", "rw-rw", "sa", "sah", "sah-ru", "saq", "saq-ke", "sat", "sat-in", "sa-in", "sbp", "sbp-tz", "sc", "sc-it", "sd", "sd-in", "sd-pk", "se", "seh", "seh-mz", "ses", "ses-ml", "se-fi", "se-no", "se-se", "sg", "sg-cf", "shi", "shi-ma", "si", "si-lk", "sk", "sk-sk", "sl", "sl-si", "smn", "smn-fi", "sms", "sms-fi", "sn", "sn-zw", "so", "so-dj", "so-et", "so-ke", "so-so", "sq", "sq-al", "sq-mk", "sq-xk", "sr", "sr-ba", "sr-cs", "sr-me", "sr-rs", "sr-xk", "su", "su-id", "sv", "sv-ax", "sv-fi", "sv-se", "sw", "sw-cd", "sw-ke", "sw-tz", "sw-ug", "sy", "ta", "ta-in", "ta-lk", "ta-my", "ta-sg", "te", "teo", "teo-ke", "teo-ug", "te-in", "tg", "tg-tj", "th", "th-th", "ti", "ti-er", "ti-et", "tk", "tk-tm", "tl", "to", "tok", "tok-001", "to-to", "tr", "tr-cy", "tr-tr", "tt", "tt-ru", "twq", "twq-ne", "tzm", "tzm-ma", "ug", "ug-cn", "uk", "uk-ua", "ur", "ur-in", "ur-pk", "uz", "uz-af", "uz-uz", "vai", "vai-lr", "vi", "vi-vn", "vo", "vo-001", "vun", "vun-tz", "wae", "wae-ch", "wo", "wo-sn", "xh", "xh-za", "xog", "xog-ug", "yav", "yav-cm", "yo", "yo-bj", "yo-ng", "yrl", "yrl-br", "yrl-co", "yrl-ve", "yue", "yue-cn", "yue-hk", "zgh", "zgh-ma", "zh", "zh-cn", "zh-hans", "zh-hant", "zh-hk", "zh-mo", "zh-sg", "zh-tw", "zu", "zu-za" ] + }, + "businessUnitId" : { + "type" : "integer", + "format" : "int64" + }, + "content" : { + "$ref" : "#/components/schemas/PublicEmailContent" + }, + "webversion" : { + "$ref" : "#/components/schemas/PublicWebversionDetails" + }, + "archived" : { + "type" : "boolean", + "description" : "Determines if the email is archived or not." + }, + "subscriptionDetails" : { + "$ref" : "#/components/schemas/PublicEmailSubscriptionDetails" + }, + "activeDomain" : { + "type" : "string", + "description" : "The active domain of the email." + }, + "name" : { + "type" : "string", + "description" : "The name of the email, as displayed on the email dashboard." + }, + "campaign" : { + "type" : "string", + "description" : "The ID of the campaign this email is associated to." + }, + "from" : { + "$ref" : "#/components/schemas/PublicEmailFromDetails" + }, + "jitterSendTime" : { + "type" : "boolean" + }, + "state" : { + "type" : "string", + "description" : "The email state.", + "enum" : [ "AUTOMATED", "AUTOMATED_DRAFT", "AUTOMATED_SENDING", "AUTOMATED_FOR_FORM", "AUTOMATED_FOR_FORM_BUFFER", "AUTOMATED_FOR_FORM_DRAFT", "AUTOMATED_FOR_FORM_LEGACY", "BLOG_EMAIL_DRAFT", "BLOG_EMAIL_PUBLISHED", "DRAFT", "DRAFT_AB", "DRAFT_AB_VARIANT", "ERROR", "LOSER_AB_VARIANT", "PAGE_STUB", "PRE_PROCESSING", "PROCESSING", "PUBLISHED", "PUBLISHED_AB", "PUBLISHED_AB_VARIANT", "PUBLISHED_OR_SCHEDULED", "RSS_TO_EMAIL_DRAFT", "RSS_TO_EMAIL_PUBLISHED", "SCHEDULED", "SCHEDULED_AB", "SCHEDULED_OR_PUBLISHED", "AUTOMATED_AB", "AUTOMATED_AB_VARIANT", "AUTOMATED_DRAFT_AB", "AUTOMATED_DRAFT_ABVARIANT", "AUTOMATED_LOSER_ABVARIANT" ] + }, + "to" : { + "$ref" : "#/components/schemas/PublicEmailToDetails" + }, + "subcategory" : { + "type" : "string", + "description" : "The email subcategory.", + "enum" : [ "ab_master", "ab_variant", "ab_loser_variant", "page_stub", "landing_page", "site_page", "legacy_page", "ab_master_site_page", "ab_variant_site_page", "ab_loser_variant_site_page", "performable_landing_page", "performable_landing_page_cutover", "staged_page", "automated", "automated_for_deal", "automated_for_form", "automated_for_form_legacy", "automated_for_form_buffer", "automated_for_form_draft", "rss_to_email", "rss_to_email_child", "blog_email", "blog_email_child", "optin_email", "optin_followup_email", "batch", "resubscribe_email", "unsubscribe_confirmation_email", "resubscribe_confirmation_email", "single_send_api", "marketing_single_send_api", "smtp_token", "localtime", "automated_for_ticket", "automated_for_leadflow", "automated_for_feedback_ces", "automated_for_feedback_nps", "automated_for_feedback_custom", "membership_registration", "membership_password_saved", "membership_password_reset", "membership_otp_login", "membership_passwordless_auth", "membership_email_verification", "membership_registration_follow_up", "membership_verification", "membership_follow_up", "ticket_closed_kickback_email", "ticket_opened_kickback_email", "automated_for_custom_survey", "discardable_stub", "normal_blog_post", "legacy_blog_post", "imported_blog_post", "automated_ab_master", "automated_ab_variant", "web_interactive", "portal_content", "page_instance_layout", "kb_article_instance_layout", "kb_listing", "kb_search_results", "kb_support_form", "case_study", "case_study_listing", "case_study_instance_layout", "UNKNOWN" ] + }, + "sendOnPublish" : { + "type" : "boolean", + "description" : "Determines whether the email will be sent immediately on publish." + } + }, + "description" : "Properties of a marketing email you can update via the API.", + "example" : { + "activeDomain" : "test.hs-sites.com", + "archived" : false, + "campaign" : "1b7f51a6-33c1-44d6-ba28-fe81f655dced", + "content" : { + "flexAreas" : { + "main" : { + "boxed" : false, + "isSingleColumnFullWidth" : false, + "sections" : [ { + "columns" : [ { + "id" : "column_1606761806181_0", + "widgets" : [ "module_160676180617911" ], + "width" : 12 + } ], + "id" : "section_1606761806181", + "style" : { + "backgroundColor" : "", + "backgroundType" : "CONTENT" + } + }, { + "columns" : [ { + "id" : "column-0-1", + "widgets" : [ "module-0-1-1" ], + "width" : 12 + } ], + "id" : "section-0", + "style" : { + "backgroundType" : "CONTENT", + "paddingBottom" : "40px", + "paddingTop" : "40px" + } + }, { + "columns" : [ { + "id" : "column-1-1", + "widgets" : [ "module-1-1-1" ], + "width" : 12 + } ], + "id" : "section-1", + "style" : { + "backgroundColor" : "", + "backgroundType" : "CONTENT", + "paddingBottom" : "0px", + "paddingTop" : "0px" + } + } ] + } + }, + "plainTextVersion" : "This is custom! View in browser ({{view_as_page_url}})\n\nHello {{ contact.firstname }},\n\nPlain text emails have minimal formatting so your reader can really focus on what you have to say. Introduce yourself and explain why you’re reaching out.\n\nEvery email should try to lead the reader to some kind of action. Use this space to describe why the reader should want to click on the link below. Put the link on its own line to really draw their eye to it.\n\nLink text\n\nNow it’s time to wrap up your email. Before your signature, thank the recipient for reading. You can also invite them to send this email to any of their colleagues who might be interested.\n\nAll the best,\n\nYour full name\n\nYour job title\n\nOther contact information\n\n{{site_settings.company_name}}, {{site_settings.company_street_address_1}}, {{site_settings.company_street_address_2}}, {{site_settings.company_city}}, {{site_settings.company_state}} {{site_settings.company_zip}}, {{site_settings.company_country}}, {{site_settings.company_phone}}\n\nUnsubscribe ({{unsubscribe_link_all}})\n\nManage preferences ({{unsubscribe_link}})", + "styleSettings" : { }, + "widgets" : { + "module-0-1-1" : { + "body" : { + "css_class" : "dnd-module", + "html" : "

Hello {{ contact.firstname }},

Plain text emails have minimal formatting so your reader can really focus on what you have to say. Introduce yourself and explain why you’re reaching out.

Every email should try to lead the reader to some kind of action. Use this space to describe why the reader should want to click on the link below. Put the link on its own line to really draw their eye to it.

Link text

Now it’s time to wrap up your email. Before your signature, thank the recipient for reading. You can also invite them to send this email to any of their colleagues who might be interested.

All the best,
Your full name
Your job title
Other contact information

", + "i18nKey" : "richText.plainText", + "path" : "@hubspot/rich_text", + "schema_version" : 2 + }, + "child_css" : { }, + "css" : { }, + "id" : "module-0-1-1", + "module_id" : 1155639, + "name" : "module-0-1-1", + "order" : 2, + "styles" : { }, + "type" : "module" + }, + "module-1-1-1" : { + "body" : { + "align" : "center", + "css_class" : "dnd-module", + "font" : { + "color" : "#23496d", + "font" : "Arial, sans-serif", + "size" : { + "units" : "px", + "value" : 12 + } + }, + "link_font" : { + "color" : "#00a4bd", + "font" : "Helvetica,Arial,sans-serif", + "size" : { + "units" : "px", + "value" : 12 + }, + "styles" : { + "bold" : false, + "italic" : false, + "underline" : true + } + }, + "path" : "@hubspot/email_footer", + "schema_version" : 2, + "unsubscribe_link_type" : "both" + }, + "child_css" : { }, + "css" : { }, + "id" : "module-1-1-1", + "module_id" : 2869621, + "name" : "module-1-1-1", + "order" : 3, + "styles" : { }, + "type" : "module" + }, + "module_160676180617911" : { + "body" : { + "font" : { + "color" : "#00a4bd", + "font" : "Arial, sans-serif", + "size" : { + "units" : "px", + "value" : 12 + }, + "styles" : { + "bold" : false, + "italic" : false, + "underline" : true + } + }, + "hs_enable_module_padding" : false, + "hs_wrapper_css" : { } + }, + "child_css" : { }, + "css" : { }, + "id" : "module_160676180617911", + "module_id" : 2794854, + "name" : "module_160676180617911", + "styles" : { }, + "type" : "module" + }, + "preview_text" : { + "body" : { + "value" : "" + }, + "child_css" : { }, + "css" : { }, + "id" : "preview_text", + "label" : "Preview Text This will be used as the preview text that displays in some email clients", + "name" : "preview_text", + "order" : 0, + "styles" : { }, + "type" : "text" + } + } + }, + "createdAt" : "2020-11-30T18:43:17.136Z", + "createdById" : 3739732, + "from" : { + "fromName" : "Bruce Wayne", + "replyTo" : "test@hubspot.com" + }, + "name" : "My subject", + "publishDate" : "2023-11-30T18:44:20.387Z", + "sendOnPublish" : true, + "state" : "DRAFT", + "subcategory" : "batch", + "subject" : "My subject", + "subscriptionDetails" : { + "officeLocationId" : "5449392956" + }, + "to" : { + "contactIds" : { }, + "contactLists" : { + "exclude" : [ 1 ], + "include" : [ 5 ] + }, + "suppressGraymail" : true + }, + "webversion" : { + "expiresAt" : "2020-11-30T18:44:20.387Z", + "metaDescription" : "", + "redirectToPageId" : 0, + "redirectToUrl" : "http://www.example.org" + } + } + }, + "PublicEmailSubscriptionDetails" : { + "type" : "object", + "properties" : { + "officeLocationId" : { + "type" : "string", + "description" : "ID of the selected office location." + }, + "preferencesGroupId" : { + "type" : "string", + "description" : "" + }, + "subscriptionId" : { + "type" : "string", + "description" : "ID of the subscription." + } + }, + "description" : "Data structure representing the subscription fields of the email." + }, + "EmailCreateRequest" : { + "required" : [ "name" ], + "type" : "object", + "properties" : { + "feedbackSurveyId" : { + "type" : "string", + "description" : "The ID of the feedback survey linked to the email." + }, + "rssData" : { + "$ref" : "#/components/schemas/PublicRssEmailDetails" + }, + "subject" : { + "type" : "string", + "description" : "The subject of the email." + }, + "testing" : { + "$ref" : "#/components/schemas/PublicEmailTestingDetails" + }, + "publishDate" : { + "type" : "string", + "description" : "The date and time the email is scheduled for, in ISO8601 representation. This is only used in local time or scheduled emails.", + "format" : "datetime" + }, + "language" : { + "type" : "string", + "enum" : [ "af", "af-na", "af-za", "agq", "agq-cm", "ak", "ak-gh", "am", "am-et", "ann", "ann-ng", "ar", "ar-001", "ar-ae", "ar-bh", "ar-dj", "ar-dz", "ar-eg", "ar-eh", "ar-er", "ar-il", "ar-iq", "ar-jo", "ar-km", "ar-kw", "ar-lb", "ar-ly", "ar-ma", "ar-mr", "ar-om", "ar-ps", "ar-qa", "ar-sa", "ar-sd", "ar-so", "ar-ss", "ar-sy", "ar-td", "ar-tn", "ar-ye", "as", "asa", "asa-tz", "ast", "ast-es", "as-in", "az", "az-az", "bas", "bas-cm", "be", "bem", "bem-zm", "bez", "bez-tz", "be-by", "bg", "bgc", "bgc-in", "bg-bg", "bho", "bho-in", "bm", "bm-ml", "bn", "bn-bd", "bn-in", "bo", "bo-cn", "bo-in", "br", "brx", "brx-in", "br-fr", "bs", "bs-ba", "ca", "ca-ad", "ca-es", "ca-fr", "ca-it", "ccp", "ccp-bd", "ccp-in", "ce", "ceb", "ceb-ph", "ce-ru", "cgg", "cgg-ug", "chr", "chr-us", "ckb", "ckb-iq", "ckb-ir", "cs", "cs-cz", "cu", "cu-ru", "cv", "cv-ru", "cy", "cy-gb", "da", "dav", "dav-ke", "da-dk", "da-gl", "de", "de-at", "de-be", "de-ch", "de-de", "de-gr", "de-it", "de-li", "de-lu", "dje", "dje-ne", "doi", "doi-in", "dsb", "dsb-de", "dua", "dua-cm", "dyo", "dyo-sn", "dz", "dz-bt", "ebu", "ebu-ke", "ee", "ee-gh", "ee-tg", "el", "el-cy", "el-gr", "en", "en-001", "en-150", "en-ae", "en-ag", "en-ai", "en-as", "en-at", "en-au", "en-bb", "en-be", "en-bi", "en-bm", "en-bs", "en-bw", "en-bz", "en-ca", "en-cc", "en-ch", "en-ck", "en-cm", "en-cn", "en-cx", "en-cy", "en-de", "en-dg", "en-dk", "en-dm", "en-ee", "en-er", "en-fi", "en-fj", "en-fk", "en-fm", "en-fr", "en-gb", "en-gd", "en-gg", "en-gh", "en-gi", "en-gm", "en-gu", "en-gy", "en-hk", "en-ie", "en-il", "en-im", "en-in", "en-io", "en-je", "en-jm", "en-ke", "en-ki", "en-kn", "en-ky", "en-lc", "en-lr", "en-ls", "en-lu", "en-mg", "en-mh", "en-mo", "en-mp", "en-ms", "en-mt", "en-mu", "en-mv", "en-mw", "en-mx", "en-my", "en-na", "en-nf", "en-ng", "en-nl", "en-nr", "en-nu", "en-nz", "en-pg", "en-ph", "en-pk", "en-pn", "en-pr", "en-pw", "en-rw", "en-sb", "en-sc", "en-sd", "en-se", "en-sg", "en-sh", "en-si", "en-sl", "en-ss", "en-sx", "en-sz", "en-tc", "en-tk", "en-to", "en-tt", "en-tv", "en-tz", "en-ug", "en-um", "en-us", "en-vc", "en-vg", "en-vi", "en-vu", "en-ws", "en-za", "en-zm", "en-zw", "eo", "eo-001", "es", "es-419", "es-ar", "es-bo", "es-br", "es-bz", "es-cl", "es-co", "es-cr", "es-cu", "es-do", "es-ea", "es-ec", "es-es", "es-gq", "es-gt", "es-hn", "es-ic", "es-mx", "es-ni", "es-pa", "es-pe", "es-ph", "es-pr", "es-py", "es-sv", "es-us", "es-uy", "es-ve", "et", "et-ee", "eu", "eu-es", "ewo", "ewo-cm", "fa", "fa-af", "fa-ir", "ff", "ff-bf", "ff-cm", "ff-gh", "ff-gm", "ff-gn", "ff-gw", "ff-lr", "ff-mr", "ff-ne", "ff-ng", "ff-sl", "ff-sn", "fi", "fil", "fil-ph", "fi-fi", "fo", "fo-dk", "fo-fo", "fr", "frr", "frr-de", "fr-be", "fr-bf", "fr-bi", "fr-bj", "fr-bl", "fr-ca", "fr-cd", "fr-cf", "fr-cg", "fr-ch", "fr-ci", "fr-cm", "fr-dj", "fr-dz", "fr-fr", "fr-ga", "fr-gf", "fr-gn", "fr-gp", "fr-gq", "fr-ht", "fr-km", "fr-lu", "fr-ma", "fr-mc", "fr-mf", "fr-mg", "fr-ml", "fr-mq", "fr-mr", "fr-mu", "fr-nc", "fr-ne", "fr-pf", "fr-pm", "fr-re", "fr-rw", "fr-sc", "fr-sn", "fr-sy", "fr-td", "fr-tg", "fr-tn", "fr-vu", "fr-wf", "fr-yt", "fur", "fur-it", "fy", "fy-nl", "ga", "ga-gb", "ga-ie", "gd", "gd-gb", "gl", "gl-es", "gsw", "gsw-ch", "gsw-fr", "gsw-li", "gu", "guz", "guz-ke", "gu-in", "gv", "gv-im", "ha", "haw", "haw-us", "ha-gh", "ha-ne", "ha-ng", "he", "hi", "hi-in", "hr", "hr-ba", "hr-hr", "hsb", "hsb-de", "hu", "hu-hu", "hy", "hy-am", "ia", "ia-001", "id", "ig", "ig-ng", "ii", "ii-cn", "id-id", "is", "is-is", "it", "it-ch", "it-it", "it-sm", "it-va", "he-il", "ja", "ja-jp", "jgo", "jgo-cm", "yi", "yi-001", "jmc", "jmc-tz", "jv", "jv-id", "ka", "kab", "kab-dz", "kam", "kam-ke", "ka-ge", "kde", "kde-tz", "kea", "kea-cv", "kgp", "kgp-br", "khq", "khq-ml", "ki", "ki-ke", "kk", "kkj", "kkj-cm", "kk-kz", "kl", "kln", "kln-ke", "kl-gl", "km", "km-kh", "kn", "kn-in", "ko", "kok", "kok-in", "ko-kp", "ko-kr", "ks", "ksb", "ksb-tz", "ksf", "ksf-cm", "ksh", "ksh-de", "ks-in", "ku", "ku-tr", "kw", "kw-gb", "ky", "ky-kg", "lag", "lag-tz", "lb", "lb-lu", "lg", "lg-ug", "lkt", "lkt-us", "ln", "ln-ao", "ln-cd", "ln-cf", "ln-cg", "lo", "lo-la", "lrc", "lrc-iq", "lrc-ir", "lt", "lt-lt", "lu", "luo", "luo-ke", "luy", "luy-ke", "lu-cd", "lv", "lv-lv", "mai", "mai-in", "mas", "mas-ke", "mas-tz", "mdf", "mdf-ru", "mer", "mer-ke", "mfe", "mfe-mu", "mg", "mgh", "mgh-mz", "mgo", "mgo-cm", "mg-mg", "mi", "mi-nz", "mk", "mk-mk", "ml", "ml-in", "mn", "mni", "mni-in", "mn-mn", "mr", "mr-in", "ms", "ms-bn", "ms-id", "ms-my", "ms-sg", "mt", "mt-mt", "mua", "mua-cm", "my", "my-mm", "mzn", "mzn-ir", "naq", "naq-na", "nb", "nb-no", "nb-sj", "nd", "nds", "nds-de", "nds-nl", "nd-zw", "ne", "ne-in", "ne-np", "nl", "nl-aw", "nl-be", "nl-bq", "nl-ch", "nl-cw", "nl-lu", "nl-nl", "nl-sr", "nl-sx", "nmg", "nmg-cm", "nn", "nnh", "nnh-cm", "nn-no", "no", "no-no", "nus", "nus-ss", "nyn", "nyn-ug", "oc", "oc-es", "oc-fr", "om", "om-et", "om-ke", "or", "or-in", "os", "os-ge", "os-ru", "pa", "pa-in", "pa-pk", "pcm", "pcm-ng", "pis", "pis-sb", "pl", "pl-pl", "prg", "prg-001", "ps", "ps-af", "ps-pk", "pt", "pt-ao", "pt-br", "pt-ch", "pt-cv", "pt-gq", "pt-gw", "pt-lu", "pt-mo", "pt-mz", "pt-pt", "pt-st", "pt-tl", "qu", "qu-bo", "qu-ec", "qu-pe", "raj", "raj-in", "rm", "rm-ch", "rn", "rn-bi", "ro", "rof", "rof-tz", "ro-md", "ro-ro", "ru", "ru-by", "ru-kg", "ru-kz", "ru-md", "ru-ru", "ru-ua", "rw", "rwk", "rwk-tz", "rw-rw", "sa", "sah", "sah-ru", "saq", "saq-ke", "sat", "sat-in", "sa-in", "sbp", "sbp-tz", "sc", "sc-it", "sd", "sd-in", "sd-pk", "se", "seh", "seh-mz", "ses", "ses-ml", "se-fi", "se-no", "se-se", "sg", "sg-cf", "shi", "shi-ma", "si", "si-lk", "sk", "sk-sk", "sl", "sl-si", "smn", "smn-fi", "sms", "sms-fi", "sn", "sn-zw", "so", "so-dj", "so-et", "so-ke", "so-so", "sq", "sq-al", "sq-mk", "sq-xk", "sr", "sr-ba", "sr-cs", "sr-me", "sr-rs", "sr-xk", "su", "su-id", "sv", "sv-ax", "sv-fi", "sv-se", "sw", "sw-cd", "sw-ke", "sw-tz", "sw-ug", "sy", "ta", "ta-in", "ta-lk", "ta-my", "ta-sg", "te", "teo", "teo-ke", "teo-ug", "te-in", "tg", "tg-tj", "th", "th-th", "ti", "ti-er", "ti-et", "tk", "tk-tm", "tl", "to", "tok", "tok-001", "to-to", "tr", "tr-cy", "tr-tr", "tt", "tt-ru", "twq", "twq-ne", "tzm", "tzm-ma", "ug", "ug-cn", "uk", "uk-ua", "ur", "ur-in", "ur-pk", "uz", "uz-af", "uz-uz", "vai", "vai-lr", "vi", "vi-vn", "vo", "vo-001", "vun", "vun-tz", "wae", "wae-ch", "wo", "wo-sn", "xh", "xh-za", "xog", "xog-ug", "yav", "yav-cm", "yo", "yo-bj", "yo-ng", "yrl", "yrl-br", "yrl-co", "yrl-ve", "yue", "yue-cn", "yue-hk", "zgh", "zgh-ma", "zh", "zh-cn", "zh-hans", "zh-hant", "zh-hk", "zh-mo", "zh-sg", "zh-tw", "zu", "zu-za" ] + }, + "businessUnitId" : { + "type" : "integer", + "format" : "int64" + }, + "content" : { + "$ref" : "#/components/schemas/PublicEmailContent" + }, + "webversion" : { + "$ref" : "#/components/schemas/PublicWebversionDetails" + }, + "archived" : { + "type" : "boolean", + "description" : "Determines if the email is archived or not." + }, + "subscriptionDetails" : { + "$ref" : "#/components/schemas/PublicEmailSubscriptionDetails" + }, + "activeDomain" : { + "type" : "string", + "description" : "The active domain of the email." + }, + "name" : { + "type" : "string", + "description" : "The name of the email, as displayed on the email dashboard." + }, + "campaign" : { + "type" : "string", + "description" : "The ID of the campaign this email is associated to." + }, + "from" : { + "$ref" : "#/components/schemas/PublicEmailFromDetails" + }, + "jitterSendTime" : { + "type" : "boolean" + }, + "state" : { + "type" : "string", + "description" : "The email state.", + "enum" : [ "AUTOMATED", "AUTOMATED_DRAFT", "AUTOMATED_SENDING", "AUTOMATED_FOR_FORM", "AUTOMATED_FOR_FORM_BUFFER", "AUTOMATED_FOR_FORM_DRAFT", "AUTOMATED_FOR_FORM_LEGACY", "BLOG_EMAIL_DRAFT", "BLOG_EMAIL_PUBLISHED", "DRAFT", "DRAFT_AB", "DRAFT_AB_VARIANT", "ERROR", "LOSER_AB_VARIANT", "PAGE_STUB", "PRE_PROCESSING", "PROCESSING", "PUBLISHED", "PUBLISHED_AB", "PUBLISHED_AB_VARIANT", "PUBLISHED_OR_SCHEDULED", "RSS_TO_EMAIL_DRAFT", "RSS_TO_EMAIL_PUBLISHED", "SCHEDULED", "SCHEDULED_AB", "SCHEDULED_OR_PUBLISHED", "AUTOMATED_AB", "AUTOMATED_AB_VARIANT", "AUTOMATED_DRAFT_AB", "AUTOMATED_DRAFT_ABVARIANT", "AUTOMATED_LOSER_ABVARIANT" ] + }, + "to" : { + "$ref" : "#/components/schemas/PublicEmailToDetails" + }, + "subcategory" : { + "type" : "string", + "description" : "The email subcategory.", + "enum" : [ "ab_master", "ab_variant", "ab_loser_variant", "page_stub", "landing_page", "site_page", "legacy_page", "ab_master_site_page", "ab_variant_site_page", "ab_loser_variant_site_page", "performable_landing_page", "performable_landing_page_cutover", "staged_page", "automated", "automated_for_deal", "automated_for_form", "automated_for_form_legacy", "automated_for_form_buffer", "automated_for_form_draft", "rss_to_email", "rss_to_email_child", "blog_email", "blog_email_child", "optin_email", "optin_followup_email", "batch", "resubscribe_email", "unsubscribe_confirmation_email", "resubscribe_confirmation_email", "single_send_api", "marketing_single_send_api", "smtp_token", "localtime", "automated_for_ticket", "automated_for_leadflow", "automated_for_feedback_ces", "automated_for_feedback_nps", "automated_for_feedback_custom", "membership_registration", "membership_password_saved", "membership_password_reset", "membership_otp_login", "membership_passwordless_auth", "membership_email_verification", "membership_registration_follow_up", "membership_verification", "membership_follow_up", "ticket_closed_kickback_email", "ticket_opened_kickback_email", "automated_for_custom_survey", "discardable_stub", "normal_blog_post", "legacy_blog_post", "imported_blog_post", "automated_ab_master", "automated_ab_variant", "web_interactive", "portal_content", "page_instance_layout", "kb_article_instance_layout", "kb_listing", "kb_search_results", "kb_support_form", "case_study", "case_study_listing", "case_study_instance_layout", "UNKNOWN" ] + }, + "sendOnPublish" : { + "type" : "boolean", + "description" : "Determines whether the email will be sent immediately on publish." + } + }, + "description" : "Properties of a marketing email you can set when creating a marketing email.", + "example" : { + "activeDomain" : "test.hs-sites.com", + "archived" : false, + "campaign" : "1b7f51a6-33c1-44d6-ba28-fe81f655dced", + "content" : { + "flexAreas" : { + "main" : { + "boxed" : false, + "isSingleColumnFullWidth" : false, + "sections" : [ { + "columns" : [ { + "id" : "column_1606761806181_0", + "widgets" : [ "module_160676180617911" ], + "width" : 12 + } ], + "id" : "section_1606761806181", + "style" : { + "backgroundColor" : "", + "backgroundType" : "CONTENT" + } + }, { + "columns" : [ { + "id" : "column-0-1", + "widgets" : [ "module-0-1-1" ], + "width" : 12 + } ], + "id" : "section-0", + "style" : { + "backgroundType" : "CONTENT", + "paddingBottom" : "40px", + "paddingTop" : "40px" + } + }, { + "columns" : [ { + "id" : "column-1-1", + "widgets" : [ "module-1-1-1" ], + "width" : 12 + } ], + "id" : "section-1", + "style" : { + "backgroundColor" : "", + "backgroundType" : "CONTENT", + "paddingBottom" : "0px", + "paddingTop" : "0px" + } + } ] + } + }, + "plainTextVersion" : "This is custom! View in browser ({{view_as_page_url}})\n\nHello {{ contact.firstname }},\n\nPlain text emails have minimal formatting so your reader can really focus on what you have to say. Introduce yourself and explain why you’re reaching out.\n\nEvery email should try to lead the reader to some kind of action. Use this space to describe why the reader should want to click on the link below. Put the link on its own line to really draw their eye to it.\n\nLink text\n\nNow it’s time to wrap up your email. Before your signature, thank the recipient for reading. You can also invite them to send this email to any of their colleagues who might be interested.\n\nAll the best,\n\nYour full name\n\nYour job title\n\nOther contact information\n\n{{site_settings.company_name}}, {{site_settings.company_street_address_1}}, {{site_settings.company_street_address_2}}, {{site_settings.company_city}}, {{site_settings.company_state}} {{site_settings.company_zip}}, {{site_settings.company_country}}, {{site_settings.company_phone}}\n\nUnsubscribe ({{unsubscribe_link_all}})\n\nManage preferences ({{unsubscribe_link}})", + "styleSettings" : { }, + "widgets" : { + "module-0-1-1" : { + "body" : { + "css_class" : "dnd-module", + "html" : "

Hello {{ contact.firstname }},

Plain text emails have minimal formatting so your reader can really focus on what you have to say. Introduce yourself and explain why you’re reaching out.

Every email should try to lead the reader to some kind of action. Use this space to describe why the reader should want to click on the link below. Put the link on its own line to really draw their eye to it.

Link text

Now it’s time to wrap up your email. Before your signature, thank the recipient for reading. You can also invite them to send this email to any of their colleagues who might be interested.

All the best,
Your full name
Your job title
Other contact information

", + "i18nKey" : "richText.plainText", + "path" : "@hubspot/rich_text", + "schema_version" : 2 + }, + "child_css" : { }, + "css" : { }, + "id" : "module-0-1-1", + "module_id" : 1155639, + "name" : "module-0-1-1", + "order" : 2, + "styles" : { }, + "type" : "module" + }, + "module-1-1-1" : { + "body" : { + "align" : "center", + "css_class" : "dnd-module", + "font" : { + "color" : "#23496d", + "font" : "Arial, sans-serif", + "size" : { + "units" : "px", + "value" : 12 + } + }, + "link_font" : { + "color" : "#00a4bd", + "font" : "Helvetica,Arial,sans-serif", + "size" : { + "units" : "px", + "value" : 12 + }, + "styles" : { + "bold" : false, + "italic" : false, + "underline" : true + } + }, + "path" : "@hubspot/email_footer", + "schema_version" : 2, + "unsubscribe_link_type" : "both" + }, + "child_css" : { }, + "css" : { }, + "id" : "module-1-1-1", + "module_id" : 2869621, + "name" : "module-1-1-1", + "order" : 3, + "styles" : { }, + "type" : "module" + }, + "module_160676180617911" : { + "body" : { + "font" : { + "color" : "#00a4bd", + "font" : "Arial, sans-serif", + "size" : { + "units" : "px", + "value" : 12 + }, + "styles" : { + "bold" : false, + "italic" : false, + "underline" : true + } + }, + "hs_enable_module_padding" : false, + "hs_wrapper_css" : { } + }, + "child_css" : { }, + "css" : { }, + "id" : "module_160676180617911", + "module_id" : 2794854, + "name" : "module_160676180617911", + "styles" : { }, + "type" : "module" + }, + "preview_text" : { + "body" : { + "value" : "" + }, + "child_css" : { }, + "css" : { }, + "id" : "preview_text", + "label" : "Preview Text This will be used as the preview text that displays in some email clients", + "name" : "preview_text", + "order" : 0, + "styles" : { }, + "type" : "text" + } + } + }, + "createdAt" : "2020-11-30T18:43:17.136Z", + "createdById" : 3739732, + "from" : { + "fromName" : "Bruce Wayne", + "replyTo" : "test@hubspot.com" + }, + "name" : "My subject", + "publishDate" : "2023-11-30T18:44:20.387Z", + "sendOnPublish" : true, + "state" : "DRAFT", + "subcategory" : "batch", + "subject" : "My subject", + "subscriptionDetails" : { + "officeLocationId" : "5449392956" + }, + "to" : { + "contactIds" : { }, + "contactLists" : { + "exclude" : [ 1 ], + "include" : [ 5 ] + }, + "suppressGraymail" : true + }, + "webversion" : { + "expiresAt" : "2020-11-30T18:44:20.387Z", + "metaDescription" : "", + "redirectToPageId" : 0, + "redirectToUrl" : "http://www.example.org" + } + } + }, + "VersionPublicEmail" : { + "required" : [ "id", "object", "updated", "user" ], + "type" : "object", + "properties" : { + "id" : { + "type" : "string", + "description" : "ID of this marketing email version." + }, + "user" : { + "$ref" : "#/components/schemas/VersionUser" + }, + "object" : { + "$ref" : "#/components/schemas/PublicEmail" + }, + "updated" : { + "type" : "string", + "format" : "datetime" + } + }, + "description" : "Model definition for a marketing email version. Contains metadata describing the version of the marketing email. It can be used to view edit history of a marketing email." + }, + "VersionUser" : { + "required" : [ "email", "fullName", "id" ], + "type" : "object", + "properties" : { + "fullName" : { + "type" : "string", + "description" : "The user's full name." + }, + "id" : { + "type" : "string", + "description" : "The user ID." + }, + "email" : { + "type" : "string", + "description" : "The user email." + } + }, + "description" : "Model definition for a version user. Contains addition information about the user who created a version." + }, + "CollectionResponseWithTotalEmailStatisticIntervalNoPaging" : { + "required" : [ "results", "total" ], + "type" : "object", + "properties" : { + "total" : { + "type" : "integer", + "description" : "Total number of objects.", + "format" : "int32" + }, + "results" : { + "type" : "array", + "description" : "Collection of objects.", + "items" : { + "$ref" : "#/components/schemas/EmailStatisticInterval" + } + } + }, + "description" : "Response object for collections of EmailStatisticIntervals." + }, + "PublicEmailRecipients" : { + "type" : "object", + "properties" : { + "include" : { + "type" : "array", + "description" : "Included IDs.", + "items" : { + "type" : "string" + } + }, + "exclude" : { + "type" : "array", + "description" : "Excluded IDs.", + "items" : { + "type" : "string" + } + } + }, + "description" : "Data structure representing lists of IDs that should be included and excluded." + }, + "PublicFontStyle" : { + "type" : "object", + "properties" : { + "size" : { + "type" : "integer", + "format" : "int32" + }, + "color" : { + "type" : "string" + }, + "underline" : { + "type" : "boolean" + }, + "bold" : { + "type" : "boolean" + }, + "italic" : { + "type" : "boolean" + }, + "font" : { + "type" : "string" + } + } + }, + "Paging" : { + "required" : [ "next" ], + "type" : "object", + "properties" : { + "next" : { + "$ref" : "#/components/schemas/NextPage" + }, + "prev" : { + "$ref" : "#/components/schemas/PreviousPage" + } + }, + "description" : "Contains information pagination of results." + }, + "Error" : { + "required" : [ "category", "correlationId", "message" ], + "type" : "object", + "properties" : { + "subCategory" : { + "type" : "string", + "description" : "A specific category that contains more specific detail about the error" + }, + "context" : { + "type" : "object", + "additionalProperties" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "description" : "Context about the error condition", + "example" : { + "missingScopes" : [ "scope1", "scope2" ], + "invalidPropertyName" : [ "propertyValue" ] + } + }, + "correlationId" : { + "type" : "string", + "description" : "A unique identifier for the request. Include this value with any error reports or support tickets", + "format" : "uuid", + "example" : "aeb5f871-7f07-4993-9211-075dc63e7cbf" + }, + "links" : { + "type" : "object", + "additionalProperties" : { + "type" : "string" + }, + "description" : "A map of link names to associated URIs containing documentation about the error or recommended remediation steps", + "example" : { + "knowledge-base" : "https://www.hubspot.com/products/service/knowledge-base" + } + }, + "message" : { + "type" : "string", + "description" : "A human readable message describing the error along with remediation steps where appropriate", + "example" : "Invalid input (details will vary based on the error)" + }, + "category" : { + "type" : "string", + "description" : "The error category", + "example" : "VALIDATION_ERROR" + }, + "errors" : { + "type" : "array", + "description" : "further information about the error", + "items" : { + "$ref" : "#/components/schemas/ErrorDetail" + } + } + }, + "example" : { + "message" : "Invalid input (details will vary based on the error)", + "correlationId" : "aeb5f871-7f07-4993-9211-075dc63e7cbf", + "category" : "VALIDATION_ERROR", + "links" : { + "knowledge-base" : "https://www.hubspot.com/products/service/knowledge-base" + } + } + }, + "PublicEmailStyleSettings" : { + "type" : "object", + "properties" : { + "emailBodyPadding" : { + "type" : "string" + }, + "colorPickerFavorite5" : { + "type" : "string" + }, + "bodyColor" : { + "type" : "string" + }, + "colorPickerFavorite6" : { + "type" : "string" + }, + "backgroundImage" : { + "type" : "string" + }, + "emailBodyWidth" : { + "type" : "string" + }, + "secondaryFont" : { + "type" : "string" + }, + "primaryAccentColor" : { + "type" : "string" + }, + "colorPickerFavorite3" : { + "type" : "string" + }, + "primaryFontLineHeight" : { + "type" : "string" + }, + "colorPickerFavorite4" : { + "type" : "string" + }, + "secondaryFontColor" : { + "type" : "string" + }, + "colorPickerFavorite1" : { + "type" : "string" + }, + "colorPickerFavorite2" : { + "type" : "string" + }, + "bodyBorderColor" : { + "type" : "string" + }, + "bodyBorderWidth" : { + "type" : "integer", + "format" : "int32" + }, + "linksFont" : { + "$ref" : "#/components/schemas/PublicFontStyle" + }, + "backgroundColor" : { + "type" : "string" + }, + "backgroundImageType" : { + "type" : "string" + }, + "dividerStyleSettings" : { + "$ref" : "#/components/schemas/PublicDividerStyleSettings" + }, + "secondaryAccentColor" : { + "type" : "string" + }, + "secondaryFontLineHeight" : { + "type" : "string" + }, + "primaryFontSize" : { + "type" : "integer", + "format" : "int32" + }, + "secondaryFontSize" : { + "type" : "integer", + "format" : "int32" + }, + "primaryFontColor" : { + "type" : "string" + }, + "headingOneFont" : { + "$ref" : "#/components/schemas/PublicFontStyle" + }, + "primaryFont" : { + "type" : "string" + }, + "headingTwoFont" : { + "$ref" : "#/components/schemas/PublicFontStyle" + }, + "buttonStyleSettings" : { + "$ref" : "#/components/schemas/PublicButtonStyleSettings" + }, + "bodyBorderColorChoice" : { + "type" : "string" + } + } + }, + "PublicEmailTestingDetails" : { + "type" : "object", + "properties" : { + "abSamplingDefault" : { + "type" : "string", + "description" : "Version of the email that should be sent if the results are inconclusive after the test period, master or variant.", + "enum" : [ "master", "variant", "loser_variant", "mab_master", "mab_variant", "automated_master", "automated_variant", "automated_loser_variant" ] + }, + "abSampleSizeDefault" : { + "type" : "string", + "description" : "Version of the email that should be sent if there are too few recipients to conduct an AB test.", + "enum" : [ "master", "variant", "loser_variant", "mab_master", "mab_variant", "automated_master", "automated_variant", "automated_loser_variant" ] + }, + "abStatus" : { + "type" : "string", + "description" : "Status of the AB test.", + "enum" : [ "master", "variant", "loser_variant", "mab_master", "mab_variant", "automated_master", "automated_variant", "automated_loser_variant" ] + }, + "abTestPercentage" : { + "type" : "integer", + "description" : "The size of your test group.", + "format" : "int32" + }, + "hoursToWait" : { + "type" : "integer", + "description" : "Time limit on gathering test results. After this time is up, the winning version will be sent to the remaining contacts.", + "format" : "int32" + }, + "testId" : { + "type" : "string", + "description" : "The ID of the AB test." + }, + "abSuccessMetric" : { + "type" : "string", + "description" : "Metric to determine the version that will be sent to the remaining contacts.", + "enum" : [ "CLICKS_BY_OPENS", "CLICKS_BY_DELIVERED", "OPENS_BY_DELIVERED" ] + } + }, + "description" : "AB testing related data. This property is only returned for AB type emails." + }, + "PublicWebversionDetails" : { + "type" : "object", + "properties" : { + "domain" : { + "type" : "string", + "description" : "" + }, + "redirectToPageId" : { + "type" : "string", + "description" : "" + }, + "isPageRedirected" : { + "type" : "boolean" + }, + "redirectToUrl" : { + "type" : "string", + "description" : "" + }, + "title" : { + "type" : "string", + "description" : "" + }, + "metaDescription" : { + "type" : "string", + "description" : "" + }, + "pageExpiryEnabled" : { + "type" : "boolean" + }, + "slug" : { + "type" : "string", + "description" : "" + }, + "url" : { + "type" : "string" + }, + "expiresAt" : { + "type" : "string", + "description" : "", + "format" : "datetime" + } + } + }, + "EmailStatisticsData" : { + "required" : [ "counters", "deviceBreakdown", "qualifierStats", "ratios" ], + "type" : "object", + "properties" : { + "deviceBreakdown" : { + "type" : "object", + "additionalProperties" : { + "type" : "object", + "additionalProperties" : { + "type" : "integer", + "format" : "int64" + } + }, + "description" : "Statistics by device." + }, + "qualifierStats" : { + "type" : "object", + "additionalProperties" : { + "type" : "object", + "additionalProperties" : { + "type" : "integer", + "format" : "int64" + } + }, + "description" : "Number of emails that were dropped and bounced." + }, + "counters" : { + "type" : "object", + "additionalProperties" : { + "type" : "integer", + "format" : "int64" + }, + "description" : "Counters like number of `sent`, `open` or `delivered`." + }, + "ratios" : { + "type" : "object", + "additionalProperties" : { + "type" : "number", + "format": "float" + }, + "description" : "Ratios like `openratio` or `clickratio`" + } + } + }, + "PublicRssEmailDetails" : { + "type" : "object", + "properties" : { + "blogImageMaxWidth" : { + "type" : "integer", + "description" : "", + "format" : "int32" + }, + "blogEmailType" : { + "type" : "string", + "description" : "" + }, + "hubspotBlogId" : { + "type" : "string", + "description" : "" + }, + "rssEntryTemplate" : { + "type" : "string", + "description" : "" + }, + "timing" : { + "type" : "object", + "additionalProperties" : { + "type" : "object", + "properties" : { } + }, + "description" : "" + }, + "maxEntries" : { + "type" : "integer", + "description" : "", + "format" : "int32" + }, + "useHeadlineAsSubject" : { + "type" : "boolean", + "description" : "" + }, + "blogLayout" : { + "type" : "string", + "description" : "" + }, + "url" : { + "type" : "string", + "description" : "" + } + }, + "description" : "RSS related data if it is a blog or rss email." + }, + "AggregateEmailStatistics" : { + "type" : "object", + "properties" : { + "emails" : { + "type" : "array", + "description" : "List of email IDs that were sent during the time span.", + "items" : { + "type" : "integer", + "format" : "int64" + } + }, + "campaignAggregations" : { + "type" : "object", + "additionalProperties" : { + "$ref" : "#/components/schemas/EmailStatisticsData" + }, + "description" : "The aggregated statistics per campaign." + }, + "aggregate" : { + "$ref" : "#/components/schemas/EmailStatisticsData" + } + }, + "description" : "Aggregated statistics for the given interval, plus the IDs of emails that were sent during that interval." + }, + "PreviousPage" : { + "required" : [ "before" ], + "type" : "object", + "properties" : { + "before" : { + "type" : "string", + "description" : "The cursor token value to get the previous set of results. Use this value as query parameter (&before=...) to obtain the previous page." + }, + "link" : { + "type" : "string", + "description" : "The link to the previous page." + } + }, + "description" : "Contains information about the previous page." + }, + "PublicEmailFromDetails" : { + "type" : "object", + "properties" : { + "customReplyTo" : { + "type" : "string", + "description" : "The reply to recipients will see." + }, + "fromName" : { + "type" : "string", + "description" : "The name recipients will see." + }, + "replyTo" : { + "type" : "string", + "description" : "The from address and reply to email address (if no customReplyTo defined) recipients will see." + } + }, + "description" : "Data structure representing the from fields on the email." + }, + "ContentCloneRequestVNext" : { + "required" : [ "id" ], + "type" : "object", + "properties" : { + "cloneName" : { + "type" : "string", + "description" : "Name of the cloned email." + }, + "id" : { + "type" : "string", + "description" : "ID of the email to be cloned." + } + }, + "description" : "Request body object for cloning marketing emails." + }, + "NextPage" : { + "required" : [ "after" ], + "type" : "object", + "properties" : { + "link" : { + "type" : "string", + "description" : "The link to the next page." + }, + "after" : { + "type" : "string", + "description" : "The cursor token value to get the next set of results. Use this value as query parameter (&after=...) to obtain the next page." + } + }, + "description" : "Contains information about the next page." + }, + "SmartEmailField" : { + "type" : "object", + "properties" : { }, + "description" : "An email field whose value is controlled by one or more smart rules." + }, + "Interval" : { + "required" : [ "end", "start" ], + "type" : "object", + "properties" : { + "start" : { + "type" : "string", + "format" : "datetime" + }, + "end" : { + "type" : "string", + "format" : "datetime" + } + } + }, + "PublicEmailContent" : { + "type" : "object", + "properties" : { + "smartFields" : { + "type" : "object", + "additionalProperties" : { + "$ref" : "#/components/schemas/SmartEmailField" + }, + "description" : "" + }, + "themeSettingsValues" : { + "type" : "object", + "additionalProperties" : { + "type" : "object", + "properties" : { } + }, + "description" : "" + }, + "flexAreas" : { + "type" : "object", + "additionalProperties" : { + "type" : "object", + "properties" : { } + }, + "description" : "" + }, + "widgets" : { + "type" : "object", + "additionalProperties" : { + "type" : "object", + "properties" : { } + }, + "description" : "" + }, + "plainTextVersion" : { + "type" : "string", + "description" : "" + }, + "templatePath" : { + "type" : "string", + "description" : "" + }, + "widgetContainers" : { + "type" : "object", + "additionalProperties" : { + "type" : "object", + "properties" : { } + }, + "description" : "" + }, + "styleSettings" : { + "$ref" : "#/components/schemas/PublicEmailStyleSettings" + } + }, + "description" : "Data structure representing the content of the email." + } + }, + "responses" : { + "Error" : { + "description" : "An error occurred.", + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/Error" + } + } + } + } + }, + "securitySchemes" : { + "oauth2_legacy" : { + "type" : "oauth2", + "flows" : { + "authorizationCode" : { + "authorizationUrl" : "https://app.hubspot.com/oauth/authorize", + "tokenUrl" : "https://api.hubapi.com/oauth/v1/token", + "scopes" : { + "content" : "Read from and write to my Content", + "marketing-email" : "Marketing Single Send API access", + "transactional-email" : "Transactional Emails" + } + } + } + }, + "private_apps_legacy" : { + "type" : "apiKey", + "name" : "private-app-legacy", + "in" : "header" + } + } + } +} \ No newline at end of file diff --git a/docs/spec/sanitations.md b/docs/spec/sanitations.md index eaf3d32..cdc1ebd 100644 --- a/docs/spec/sanitations.md +++ b/docs/spec/sanitations.md @@ -1,24 +1,140 @@ -_Author_: \ -_Created_: \ -_Updated_: \ +_Author_: Lakindu Kariyawasam \ +_Created_: 2024/12/18 \ +_Updated_: 2025/01/08 \ _Edition_: Swan Lake # Sanitation for OpenAPI specification This document records the sanitation done on top of the official OpenAPI specification from HubSpot Marketing Emails. -The OpenAPI specification is obtained from (TODO: Add source link). +The OpenAPI specification is obtained from [Hubspot Github Public API Spec Collection](https://github.com/HubSpot/HubSpot-public-api-spec-collection/blob/main/PublicApiSpecs/Marketing/Marketing%20Emails/Rollouts/145892/v3/marketingEmails.json). \ These changes are done in order to improve the overall usability, and as workarounds for some known language limitations. -[//]: # (TODO: Add sanitation details) -1. -2. -3. +1. Change the `url` property of the servers object +- **Original**: +```https://api.hubspot.com``` + +- **Updated**: +```https://api.hubapi.com/marketing/v3/emails``` + +- **Reason**: This change of adding the common prefix `marketing/v3/emails` to the base url makes it easier to access endpoints using the client. + +2. Update the API Paths +- **Original**: Paths included common prefix above in each endpoint. (eg: ```/marketing/v3/emails/clone```) + +- **Updated**: Common prefix is now removed from the endpoints as it is included in the base URL. + - **Original**: ```/marketing/v3/emails/clone``` + - **Updated**: ```/clone``` + +- **Reason**: This change simplifies the API p aths, making them shorter and more readable. + +3. Add `"format":"float"` to `components->statistics->EmailStatisticsData->properties->ratios->additionalProperties` +- **Original**: +```json +"ratios" : { + "type" : "object", + "additionalProperties" : { + "type" : "number" + }, + "description" : "Ratios like `openratio` or `clickratio`" + } +``` + +- **Updated**: +```json +"ratios" : { + "type" : "object", + "additionalProperties" : { + "type" : "number", + "format": "float" + }, + "description" : "Ratios like `openratio` or `clickratio`" + } +``` + +- **Reason**: This change ensures that the type is generated correctly to handle the payload from the response. + +4. Change `"type":"object"` to `"type":"string"` in `components->schemas->PublicButtonStyleSettings->properties->backgroundColor` +- **Original**: +```json +"backgroundColor" : { + "type" : "object", + "properties" : { } + } +``` + +- **Updated**: +```json +"backgroundColor" : { + "type" : "string" + }, +``` + +- **Reason**: The API returns backgroundColor as a string (eg: "#FF234A"). This change ensures that the type is generated correctly to handle the payload from the response. + +5. Remove `sendOnPublish` from the required list in `components->schemas->PublicEmail` +- **Original**: +```json + "PublicEmail" : { + "required" : [ "content", "from", "id", "name", "sendOnPublish", "state", "subcategory", "subject", "to" ], + ... + } + +``` + +- **Updated**: +```json + "PublicEmail" : { + "required" : [ "content", "from", "id", "name", "state", "subcategory", "subject", "to" ], + ... + } +``` + +- **Reason**: The API does not return on the sendOnPublish field for some emails. This leads to a payload binding error as it is marked as required in the API specification. Removing it from the required list ensures that the types are generated correctly to handle the payload from the response. + +6. Change `updatedAt` to `updated` in `components->schemas->VersionPublicEmail->properties` +- **Original**: +```json +"VersionPublicEmail" : { + "required" : [ "id", "object", "updatedAt", "user" ], + "updatedAt" : { + "type" : "string", + "format" : "date-time" + }, + ... + }, +``` +- **Updated**: +```json +"VersionPublicEmail" : { + "required" : [ "id", "object", "updated", "user" ], + "updated" : { + "type" : "string", + "format" : "date-time" + }, + ... + }, +``` + +- **Reason**: The API does not return a `updatedAt` field but rather a `updated` field for each result of the results array in the response of the /[emailId]/revisions endpoint. This change ensures that the type is generated correctly to handle the payload from the response. + +7. Change `"date-time"` to `"datetime"` throughout the specification +- **Original**: +```json + "format": "date-time" +``` +- **Updated**: +```json + "format": "datetime" +``` + +- **Reason**: The specification originally uses `"date-time"` which is unsupported by the openapi generator tool. This change to `"datetime"` ensures it is handled correctly. + ## OpenAPI cli command The following command was used to generate the Ballerina client from the OpenAPI specification. The command should be executed from the repository root directory. ```bash -# TODO: Add OpenAPI CLI command used to generate the client +bal openapi -i docs/spec/openapi.yaml --license docs/license.txt --mode client -o ballerina ``` Note: The license year is hardcoded to 2024, change if necessary. diff --git a/examples/README.md b/examples/README.md index c50d300..698cbb3 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,14 +1,23 @@ # Examples -The `ballerinax/hubspot.marketing.emails` connector provides practical examples illustrating usage in various scenarios. +The `ballerinax/hubspot.marketing.emails` connector provides practical examples illustrating usage in various scenarios. Explore these examples, covering use cases: + +1. [Bulk Change Reply To Email](./bulk_change_reply_email/) - Change the Reply To and Custom Reply To email address of all draft emails + +2. [Marketing Email Statistics Logger](./email_stat_logger/) - Retrieve and log the statistics of marketing emails during a specific time period. -[//]: # (TODO: Add examples) -1. -2. ## Prerequisites -[//]: # (TODO: Add prerequisites) +1. Generate Hubspot credentials to authenticate the connector as described in the [Setup guide](../ballerina/Package.md#setup-guide). + +2. For each example, create a `Config.toml` file the related configuration. Here's an example of how your `Config.toml` file should look: + + ```toml + clientId = "" + clientSecret = "" + refreshToken = "" + ``` ## Running an example diff --git a/examples/bulk_change_reply_email/Ballerina.toml b/examples/bulk_change_reply_email/Ballerina.toml new file mode 100644 index 0000000..30d948d --- /dev/null +++ b/examples/bulk_change_reply_email/Ballerina.toml @@ -0,0 +1,8 @@ +[package] +org = "wso2" +name = "bulk_change_reply_email" +version = "0.1.0" +distribution = "2201.10.3" + +[build-options] +observabilityIncluded = true diff --git a/examples/bulk_change_reply_email/Dependencies.toml b/examples/bulk_change_reply_email/Dependencies.toml new file mode 100644 index 0000000..cd6f873 --- /dev/null +++ b/examples/bulk_change_reply_email/Dependencies.toml @@ -0,0 +1,315 @@ +# AUTO-GENERATED FILE. DO NOT MODIFY. + +# This file is auto-generated by Ballerina for managing dependency versions. +# It should not be modified by hand. + +[ballerina] +dependencies-toml-version = "2" +distribution-version = "2201.10.3" + +[[package]] +org = "ballerina" +name = "auth" +version = "2.12.0" +dependencies = [ + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.array"}, + {org = "ballerina", name = "lang.string"}, + {org = "ballerina", name = "log"} +] + +[[package]] +org = "ballerina" +name = "cache" +version = "3.8.0" +dependencies = [ + {org = "ballerina", name = "constraint"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "task"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "constraint" +version = "1.5.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "crypto" +version = "2.7.2" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "file" +version = "1.10.0" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "os"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "http" +version = "2.12.4" +dependencies = [ + {org = "ballerina", name = "auth"}, + {org = "ballerina", name = "cache"}, + {org = "ballerina", name = "constraint"}, + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "file"}, + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "jwt"}, + {org = "ballerina", name = "lang.array"}, + {org = "ballerina", name = "lang.decimal"}, + {org = "ballerina", name = "lang.int"}, + {org = "ballerina", name = "lang.regexp"}, + {org = "ballerina", name = "lang.runtime"}, + {org = "ballerina", name = "lang.string"}, + {org = "ballerina", name = "lang.value"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "mime"}, + {org = "ballerina", name = "oauth2"}, + {org = "ballerina", name = "observe"}, + {org = "ballerina", name = "time"}, + {org = "ballerina", name = "url"} +] + +[[package]] +org = "ballerina" +name = "io" +version = "1.6.3" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.value"} +] +modules = [ + {org = "ballerina", packageName = "io", moduleName = "io"} +] + +[[package]] +org = "ballerina" +name = "jballerina.java" +version = "0.0.0" + +[[package]] +org = "ballerina" +name = "jwt" +version = "2.13.0" +dependencies = [ + {org = "ballerina", name = "cache"}, + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.int"}, + {org = "ballerina", name = "lang.string"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "lang.__internal" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.object"} +] + +[[package]] +org = "ballerina" +name = "lang.array" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.__internal"} +] + +[[package]] +org = "ballerina" +name = "lang.decimal" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "lang.int" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.__internal"}, + {org = "ballerina", name = "lang.object"} +] + +[[package]] +org = "ballerina" +name = "lang.object" +version = "0.0.0" + +[[package]] +org = "ballerina" +name = "lang.regexp" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "lang.runtime" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "lang.string" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.regexp"} +] + +[[package]] +org = "ballerina" +name = "lang.value" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "log" +version = "2.10.0" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.value"}, + {org = "ballerina", name = "observe"} +] + +[[package]] +org = "ballerina" +name = "mime" +version = "2.10.1" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.int"}, + {org = "ballerina", name = "log"} +] + +[[package]] +org = "ballerina" +name = "oauth2" +version = "2.12.0" +dependencies = [ + {org = "ballerina", name = "cache"}, + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "time"}, + {org = "ballerina", name = "url"} +] +modules = [ + {org = "ballerina", packageName = "oauth2", moduleName = "oauth2"} +] + +[[package]] +org = "ballerina" +name = "observe" +version = "1.3.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "os" +version = "1.8.0" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "task" +version = "2.5.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "time" +version = "2.5.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "url" +version = "2.4.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerinai" +name = "observe" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "observe"} +] +modules = [ + {org = "ballerinai", packageName = "observe", moduleName = "observe"} +] + +[[package]] +org = "ballerinax" +name = "hubspot.marketing.emails" +version = "0.1.0" +dependencies = [ + {org = "ballerina", name = "http"}, + {org = "ballerina", name = "oauth2"}, + {org = "ballerina", name = "time"}, + {org = "ballerina", name = "url"}, + {org = "ballerinai", name = "observe"} +] +modules = [ + {org = "ballerinax", packageName = "hubspot.marketing.emails", moduleName = "hubspot.marketing.emails"} +] + +[[package]] +org = "wso2" +name = "bulk_change_reply_email" +version = "0.1.0" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "oauth2"}, + {org = "ballerinai", name = "observe"}, + {org = "ballerinax", name = "hubspot.marketing.emails"} +] +modules = [ + {org = "wso2", packageName = "bulk_change_reply_email", moduleName = "bulk_change_reply_email"} +] + diff --git a/examples/bulk_change_reply_email/bulk_change_reply_email.md b/examples/bulk_change_reply_email/bulk_change_reply_email.md new file mode 100644 index 0000000..c58787b --- /dev/null +++ b/examples/bulk_change_reply_email/bulk_change_reply_email.md @@ -0,0 +1,27 @@ +## Bulk Change ReplyTo and CustomReplyTo in Draft Emails + +This use case shows how the Hubspot Marketing Emails API can be used to change the 'reply to' email address of all draft emails. This example leverages the endpoints of the Hubpsot Marketing Emails API to automate what would be a time consuming task if done manually. + +## Prerequisites + +### 1. Setup Hubspot developer account + +Refer to the [Setup guide](../../ballerina/Package.md#setup-guide) to obtain necessary credentials (client Id, client secret, tokens). + +### 2. Configuration + +Create a `Config.toml` file in the example's root directory and, provide your Hubspot account related configurations as follows: + +```toml +clientId = "" +clientSecret = "" +refreshToken = "" +``` + +## Run the example + +Execute the following command to run the example: + +```bash +bal run +``` \ No newline at end of file diff --git a/examples/bulk_change_reply_email/main.bal b/examples/bulk_change_reply_email/main.bal new file mode 100644 index 0000000..feae241 --- /dev/null +++ b/examples/bulk_change_reply_email/main.bal @@ -0,0 +1,58 @@ +// Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com). +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import ballerina/io; +import ballerina/oauth2; +import ballerinax/hubspot.marketing.emails as hsmemails; + +configurable string clientId = ?; +configurable string clientSecret = ?; +configurable string refreshToken = ?; + +public function main() returns error? { + + // Create the config for authorization to the API + hsmemails:OAuth2RefreshTokenGrantConfig auth = { + clientId, + clientSecret, + refreshToken, + credentialBearer: oauth2:POST_BODY_BEARER // this line should be added to create auth object. + }; + + hsmemails:Client hubspotMarketingEmailClient = check new ({auth}); + + // Get all marketing emails + hsmemails:CollectionResponseWithTotalPublicEmailForwardPaging emailsResponse = check hubspotMarketingEmailClient->/({}); + + // Change this to your new email email address + // NOTE: To add an email with your custom domain, the domain must first + // be connected to Hubspot. + // https://knowledge.hubspot.com/marketing-email/manage-email-authentication-in-hubspot + final string newReplyToEmailAddress = "new_reply_address@example.com"; + + foreach hsmemails:PublicEmail email in emailsResponse.results { + // Apply the change only to draft emails + if email.state == "DRAFT" { + hsmemails:PublicEmail updated = check hubspotMarketingEmailClient->/[email.id].patch({ + 'from: { + replyTo: newReplyToEmailAddress, + customReplyTo: newReplyToEmailAddress + } + }); + io:println(string `Updated email ${updated.id} replyTo and customReplyTo address: ${newReplyToEmailAddress}`); + } + } +} diff --git a/examples/email_stat_logger/Ballerina.toml b/examples/email_stat_logger/Ballerina.toml new file mode 100644 index 0000000..7cb7095 --- /dev/null +++ b/examples/email_stat_logger/Ballerina.toml @@ -0,0 +1,8 @@ +[package] +org = "wso2" +name = "email_stat_logger" +version = "0.1.0" +distribution = "2201.10.3" + +[build-options] +observabilityIncluded = true diff --git a/examples/email_stat_logger/Dependencies.toml b/examples/email_stat_logger/Dependencies.toml new file mode 100644 index 0000000..9d8d88b --- /dev/null +++ b/examples/email_stat_logger/Dependencies.toml @@ -0,0 +1,315 @@ +# AUTO-GENERATED FILE. DO NOT MODIFY. + +# This file is auto-generated by Ballerina for managing dependency versions. +# It should not be modified by hand. + +[ballerina] +dependencies-toml-version = "2" +distribution-version = "2201.10.3" + +[[package]] +org = "ballerina" +name = "auth" +version = "2.12.0" +dependencies = [ + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.array"}, + {org = "ballerina", name = "lang.string"}, + {org = "ballerina", name = "log"} +] + +[[package]] +org = "ballerina" +name = "cache" +version = "3.8.0" +dependencies = [ + {org = "ballerina", name = "constraint"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "task"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "constraint" +version = "1.5.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "crypto" +version = "2.7.2" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "file" +version = "1.10.0" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "os"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "http" +version = "2.12.4" +dependencies = [ + {org = "ballerina", name = "auth"}, + {org = "ballerina", name = "cache"}, + {org = "ballerina", name = "constraint"}, + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "file"}, + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "jwt"}, + {org = "ballerina", name = "lang.array"}, + {org = "ballerina", name = "lang.decimal"}, + {org = "ballerina", name = "lang.int"}, + {org = "ballerina", name = "lang.regexp"}, + {org = "ballerina", name = "lang.runtime"}, + {org = "ballerina", name = "lang.string"}, + {org = "ballerina", name = "lang.value"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "mime"}, + {org = "ballerina", name = "oauth2"}, + {org = "ballerina", name = "observe"}, + {org = "ballerina", name = "time"}, + {org = "ballerina", name = "url"} +] + +[[package]] +org = "ballerina" +name = "io" +version = "1.6.3" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.value"} +] +modules = [ + {org = "ballerina", packageName = "io", moduleName = "io"} +] + +[[package]] +org = "ballerina" +name = "jballerina.java" +version = "0.0.0" + +[[package]] +org = "ballerina" +name = "jwt" +version = "2.13.0" +dependencies = [ + {org = "ballerina", name = "cache"}, + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.int"}, + {org = "ballerina", name = "lang.string"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "lang.__internal" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.object"} +] + +[[package]] +org = "ballerina" +name = "lang.array" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.__internal"} +] + +[[package]] +org = "ballerina" +name = "lang.decimal" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "lang.int" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.__internal"}, + {org = "ballerina", name = "lang.object"} +] + +[[package]] +org = "ballerina" +name = "lang.object" +version = "0.0.0" + +[[package]] +org = "ballerina" +name = "lang.regexp" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "lang.runtime" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "lang.string" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.regexp"} +] + +[[package]] +org = "ballerina" +name = "lang.value" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "log" +version = "2.10.0" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.value"}, + {org = "ballerina", name = "observe"} +] + +[[package]] +org = "ballerina" +name = "mime" +version = "2.10.1" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.int"}, + {org = "ballerina", name = "log"} +] + +[[package]] +org = "ballerina" +name = "oauth2" +version = "2.12.0" +dependencies = [ + {org = "ballerina", name = "cache"}, + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "time"}, + {org = "ballerina", name = "url"} +] +modules = [ + {org = "ballerina", packageName = "oauth2", moduleName = "oauth2"} +] + +[[package]] +org = "ballerina" +name = "observe" +version = "1.3.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "os" +version = "1.8.0" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "task" +version = "2.5.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "time" +version = "2.5.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "url" +version = "2.4.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerinai" +name = "observe" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "observe"} +] +modules = [ + {org = "ballerinai", packageName = "observe", moduleName = "observe"} +] + +[[package]] +org = "ballerinax" +name = "hubspot.marketing.emails" +version = "0.1.0" +dependencies = [ + {org = "ballerina", name = "http"}, + {org = "ballerina", name = "oauth2"}, + {org = "ballerina", name = "time"}, + {org = "ballerina", name = "url"}, + {org = "ballerinai", name = "observe"} +] +modules = [ + {org = "ballerinax", packageName = "hubspot.marketing.emails", moduleName = "hubspot.marketing.emails"} +] + +[[package]] +org = "wso2" +name = "email_stat_logger" +version = "0.1.0" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "oauth2"}, + {org = "ballerinai", name = "observe"}, + {org = "ballerinax", name = "hubspot.marketing.emails"} +] +modules = [ + {org = "wso2", packageName = "email_stat_logger", moduleName = "email_stat_logger"} +] + diff --git a/examples/email_stat_logger/email_stat_logger.md b/examples/email_stat_logger/email_stat_logger.md new file mode 100644 index 0000000..fb5eeec --- /dev/null +++ b/examples/email_stat_logger/email_stat_logger.md @@ -0,0 +1,27 @@ +## Retrieve and Log Statistics for Marketing Emails + +This use case shows how the Hubspot Marketing Emails API can be used to retrieve and log statistics on marketing email campaigns. This example leverages the endpoints of the Hubpsot Marketing Emails API to easily retrieve the data for any time period. + +## Prerequisites + +### 1. Setup Hubspot developer account + +Refer to the [Setup guide](../../ballerina/Package.md#setup-guide) to obtain necessary credentials (client Id, client secret, tokens). + +### 2. Configuration + +Create a `Config.toml` file in the example's root directory and, provide your Hubspot account related configurations as follows: + +```toml +clientId = "" +clientSecret = "" +refreshToken = "" +``` + +## Run the example + +Execute the following command to run the example: + +```bash +bal run +``` diff --git a/examples/email_stat_logger/main.bal b/examples/email_stat_logger/main.bal new file mode 100644 index 0000000..28d9e4f --- /dev/null +++ b/examples/email_stat_logger/main.bal @@ -0,0 +1,73 @@ +// Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com). +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import ballerina/io; +import ballerina/oauth2; +import ballerinax/hubspot.marketing.emails as hsmemails; + +configurable string clientId = ?; +configurable string clientSecret = ?; +configurable string refreshToken = ?; + +public function main() returns error? { + + // Create the config for authorization to the API + hsmemails:OAuth2RefreshTokenGrantConfig auth = { + clientId, + clientSecret, + refreshToken, + credentialBearer: oauth2:POST_BODY_BEARER // this line should be added to create auth object. + }; + + // Initialize the Hubspot Marketing Email Client + hsmemails:Client hubspotMarketingEmailClient = check new ({auth}); + + // Change these values + // Timestamps must be in ISO 8601 format + string startTimestamp = "2025-01-01T10:00:00Z"; + string endTimestamp = "2025-01-31T10:00:00Z"; + "YEAR"|"QUARTER"|"MONTH"|"WEEK"|"DAY"|"HOUR"|"QUARTER_HOUR"|"MINUTE"|"SECOND" interval = "DAY"; + + + // First get the aggregated stats from Hubspot + hsmemails:AggregateEmailStatistics aggreagate = check hubspotMarketingEmailClient->/statistics/list({},{ + startTimestamp, + endTimestamp + }); + + io:println(string `***Aggregated Statistics for the Time Period From ${startTimestamp} To ${endTimestamp}`); + + io:println("IDs of the emails sent, "); + io:println(aggreagate.emails); + + io:print("Aggrgated Statistics: "); + io:println(aggreagate.aggregate); + + hsmemails:CollectionResponseWithTotalEmailStatisticIntervalNoPaging histogram = check + hubspotMarketingEmailClient->/statistics/histogram({}, + { + startTimestamp, + endTimestamp, + interval + } + ); + + io:println(`***Statistics for the Time Period from ${startTimestamp} To ${endTimestamp} for each Interval: ${interval}`); + + foreach hsmemails:EmailStatisticInterval data_record in histogram.results { + io:println("Interval: ", data_record.interval, " Statistics: ", data_record.aggregations); + } +}