# Start from the email template

A template is the smallest build that meets a requirement, but it's a starting point, not a finished Connector. This page takes the official email template from a fresh copy to a state you can stage, and shows how to prepare any template for your own environment on the way.

Zen Electron wants branded order confirmation emails sent through its own email service provider. In [Choose where it runs and how to adopt it](/learning-integrate-with-commercetools/integration-essentials/choose-where-it-runs.md), the team applied the adoption ladder to the order confirmation email. No public Connector fits Zen Electron's email service provider, and no configuration option closes the gap, so the team starts at step 4 with the [transactional email template](/connect/templates/transactional-emails.md). By the end of this page, you have your own repository with a tagged `v0.1.0` that is ready for the next page.

In this module, you build that Connector in your own GitHub repository, publish it for your own Projects, and then install and operate it the way any Client would. The hands-on steps apply the [Integrate transactional email](/guides/connect-email-integration.md) guide. Every page applies a concept from Integration essentials, links back to it, and doesn't teach it again.

## Prepare your tools

You need the following:

- A Node.js version that Connect supports. See [supported runtimes](/connect/supported-runtimes.md#javascripttypescript).
- Docker. The Connect CLI uses it to build and validate your application locally.
- A GitHub account for your own repository.
- An API Client for the CLI, as described in the following section.

Install the Connect CLI as described in [Install the Connect CLI](/connect/cli.md#install-the-connect-cli), and then run `commercetools --version` to confirm that the CLI is available.

### Create an API Client for the CLI

The CLI acts as an API Client in your Project. This is a different identity from the one the Connector runs as. Create a client in the Merchant Center [Developer settings](/merchant-center/developer-settings.md#api-clients) with the following scopes:

| Scope | Where this module uses it |
| --- | --- |
| `manage_connectors` | Stage and publish the Connector ([Stage, preview, and publish privately](/learning-integrate-with-commercetools/deploy-and-build-connectors/stage-preview-and-publish-privately.md)). |
| `manage_connectors_deployments` | Install and operate Deployments ([Install and test your Connector](/learning-integrate-with-commercetools/deploy-and-build-connectors/install-and-test-your-connector.md) and [Operate a Deployment](/learning-integrate-with-commercetools/deploy-and-build-connectors/operate-a-deployment.md)). |
| `manage_api_clients` | Deploy a Connector that uses `inheritAs` (see the warning after the next section). |
| `manage_orders` | Import the test Order that triggers the email ([Install and test your Connector](/learning-integrate-with-commercetools/deploy-and-build-connectors/install-and-test-your-connector.md)). |
| `manage_subscriptions` or `view_subscriptions` | Check the Subscription that the Connector creates ([Install and test your Connector](/learning-integrate-with-commercetools/deploy-and-build-connectors/install-and-test-your-connector.md#verify-the-resource-not-the-status)). |

[Hosts and authorization](/connect/hosts-and-authorization.md#authorization) describes the Connect scopes.

Then sign in:

```bash
commercetools auth login --client-credentials 
  --client-id <client-id> 
  --client-secret <client-secret> 
  --project-key <project-key> 
  --region <region>
```

Use your Project's Region for `--region`. [`commercetools auth login`](/connect/cli.md#commercetools-auth-login) describes each flag. Keep the client secret out of your repository.

## Create your repository

Scaffold the project from the email template. The CLI's `--template` value is `email-integration`, which differs from the documentation page name, `transactional-emails`:

```bash
commercetools connect init zen-order-confirmation --template email-integration
cd zen-order-confirmation
git init
git add .
git commit -m "Start from the email template"
```

Create an empty repository in your GitHub account, then push:

```bash
git branch -M main
git remote add origin git@github.com:<your-account>/zen-order-confirmation.git
git push -u origin main
```

You can also clone the [template repository](https://github.com/commercetools/connect-email-integration-template) and remove its history. The two routes produce the same code, apart from the `package-lock.json` file.

The template has one application, in a folder that matches its name in `connect.yaml`:

```text
zen-order-confirmation/
├── connect.yaml
└── mail-sender/
    ├── package.json
    ├── src/
    └── tests/
```

The template is JavaScript, not TypeScript. This module keeps its code in JavaScript.

## Read connect.yaml

The `connect.yaml` file at the root of the repository tells Connect what to build and how to run it. The template's file looks like this (abbreviated):

```yaml title="connect.yaml (unmodified template, abbreviated)"
deployAs:
  - name: mail-sender
    applicationType: event
    endpoint: /mailSender
    scripts:
      postDeploy: npm install && npm run connector:post-deploy
      preUndeploy: npm install && npm run connector:pre-undeploy
    configuration:
      standardConfiguration:
        - key: CTP_REGION
          description: commercetools Composable Commerce API region
      securedConfiguration:
        - key: CTP_PROJECT_KEY
        - key: CTP_CLIENT_ID
        - key: CTP_CLIENT_SECRET
        - key: CTP_SCOPE
        - key: EMAIL_PROVIDER_API_KEY
        - key: SENDER_EMAIL_ADDRESS
        - key: CUSTOMER_REGISTRATION_TEMPLATE_ID
        - key: CUSTOMER_EMAIL_TOKEN_CREATION_TEMPLATE_ID
        - key: CUSTOMER_PASSWORD_TOKEN_CREATION_TEMPLATE_ID
        - key: ORDER_CONFIRMATION_TEMPLATE_ID
        - key: ORDER_STATE_CHANGE_TEMPLATE_ID
        - key: ORDER_REFUND_TEMPLATE_ID
```

The real file has a `description` for every key. [Configure connect.yaml](/connect/development.md#configure-connectyaml) defines each field. In this template, note the following:

- **`name`** must match the application's folder, `mail-sender`.
- **`endpoint`** is `/mailSender`. The Express router in `mail-sender` must serve this path, or deliveries never reach the handler. The [email integration guide](/guides/connect-email-integration.md#configure-the-event-application) describes this requirement.
- **`scripts`** create and delete the template's Subscription.
- **`securedConfiguration`** holds every key except `CTP_REGION`, so the installer can't read those values back after setting them.

Whoever installs a Connector supplies a value for each key. The template asks for 13 values, four of which are an API Client's project key, ID, secret, and scope. The next section removes them.

## Generate credentials instead of collecting them

With the template's configuration, every installer must create an API Client and paste its credentials into the install form. The `inheritAs.apiClient.scopes` setting makes Connect create the API Client at deployment time, with only the scopes you list. Nobody handles a secret.

Change `connect.yaml` as follows:

```diff title="connect.yaml"
       securedConfiguration:
-        - key: CTP_PROJECT_KEY
-          description: commercetools Composable Commerce project key
-        - key: CTP_CLIENT_ID
-          description: commercetools Composable Commerce client ID
-        - key: CTP_CLIENT_SECRET
-          description: commercetools Composable Commerce client secret
-        - key: CTP_SCOPE
-          description: commercetools Composable Commerce client scope
         - key: EMAIL_PROVIDER_API_KEY
           description: The API key used to communicate with email provider
         # ...the remaining keys stay as they are...
         - key: ORDER_REFUND_TEMPLATE_ID
           description: Identifier of order refund email template stored in email service provider
+inheritAs:
+  apiClient:
+    scopes:
+      - manage_subscriptions
+      - view_orders
+      - view_customers
```

`inheritAs` is a top-level key, a sibling of `deployAs`. The `connect.yaml` file now asks installers for nine values.

Connect injects the `CTP_*` variables that [Variables available in the Connect application](/connect/modify-connector.md#variables-available-in-the-connect-application) lists. The template's `mail-sender/src/utils/config.utils.js` already reads `CTP_CLIENT_ID`, `CTP_CLIENT_SECRET`, `CTP_PROJECT_KEY`, and `CTP_SCOPE`, so the application code needs no change. The template doesn't read `CTP_API_URL` or `CTP_AUTH_URL`. Instead, the template builds both hosts from `CTP_REGION`, so `CTP_REGION` stays as a value that the installer enters.

The three scopes match what the application does: `manage_subscriptions` for the Subscription that `postDeploy` creates and `preUndeploy` deletes, `view_orders` for the Order the confirmation handler retrieves, and `view_customers` for the Customer. The [email integration guide](/guides/connect-email-integration.md#configure-the-event-application) maps each capability to its scope, and notes that flows creating Customer tokens need `manage_customers`. This module doesn't exercise those flows.

> **Warning:** Add `manage_api_clients` to the CLI's API Client, not to the Connector's `inheritAs.apiClient.scopes` list. Without it, a Deployment of this Connector fails with `403`. See [Automatically generate API Client credentials](/connect/modify-connector.md).

## Validate and update dependencies

Run the Connect validation locally. It's the same pipeline that Connect runs when you preview or publish a Connector. The [validation pipeline](/connect/validation-pipeline.md) page describes each step.

```bash
commercetools connect validate
```

Validation writes a `.connect/` folder with build and scan logs. Add `.connect/` to `.gitignore` so that you don't commit it.

Templates pin dependency versions at the time they're published, and new advisories appear afterwards. Before you build on any template, run the validation and expect software composition analysis (SCA) to flag dependencies that need updating. The [SCA step](/connect/validation-pipeline.md#software-composition-analysis-sca) scans development dependencies as well as production dependencies, so moving a package to `devDependencies` doesn't hide its findings. Only updating or removing the package changes the result.

Also check that the manifest separates runtime dependencies from test tooling. In the template's `mail-sender/package.json`:

- `sinon`, `supertest`, and `globals` are listed under `dependencies`, although only tests use them.
- `mocha` is listed too, but the tests run on Jest, so nothing uses it.

Remove `mocha`, move the test packages to `devDependencies` so that the manifest shows what the application needs at runtime, then apply the available security updates:

```bash
cd mail-sender
npm uninstall mocha
npm install --save-dev sinon supertest globals
npm audit fix
```

You can make the same change by editing `package.json` and running `npm install`. Commit the updated `package-lock.json`. Connect installs from it.

Run `commercetools connect validate` again from the repository root. If the SCA step still fails, run `npm audit` in `mail-sender` to see which packages are affected, then update or remove them. If an advisory has no fix available, check whether your code uses the affected package before you decide how to proceed.

SCA fails only on critical findings, so a passing SCA step doesn't mean the dependencies have no high-severity findings. Run `npm audit` and triage any remaining advisories before production use.

### Treat local SAST as an early warning

Local static application security testing (SAST) can report code smells, such as an empty constructor or high cognitive complexity, that the remote pipeline accepts. The remote pipeline runs when you preview or publish a Connector. Local analysis can be stricter than the remote gate.

You don't need to clear every local finding to continue. [Stage, preview, and publish privately](/learning-integrate-with-commercetools/deploy-and-build-connectors/stage-preview-and-publish-privately.md#trust-the-remote-report) shows how to read the remote report, which decides whether you can preview and publish. If you want a clean local run, remove empty constructors, extract duplicated logic into shared helpers, and split long functions into smaller ones.

## Adjust the destination validator for Google Cloud

The template's environment validation needs one change before it works in a Google Cloud Region. Connect sets the variable `CONNECT_SUBSCRIPTION_DESTINATION` to `GoogleCloudPubSub`, which is 17 characters. The template's validator in `mail-sender/src/validators/env.validators.js` accepts at most 10:

```js title="mail-sender/src/validators/env.validators.js (excerpt)"
optional(standardString)(
  ['connectSubscriptionDestination'],
  {
    code: 'InvalidSubscriptionDestination',
    message: 'Subscription destination is required.',
    referencedBy: 'environmentVariables',
  },
  { min: 2, max: 10 }
),
```

Reading the configuration then throws, `postDeploy` creates no Subscription, and the running application can't process events. An AWS Region isn't affected, because its destination value, `SNS`, is 3 characters.

Reproduce the failure locally before you change it. The command needs no deployment:

```bash
cd mail-sender
npm ci
CTP_CLIENT_ID=aaaaaaaaaaaaaaaaaaaaaaaa 
CTP_CLIENT_SECRET=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 
CTP_PROJECT_KEY=my-project CTP_REGION=europe-west1.gcp 
CONNECT_SUBSCRIPTION_DESTINATION=GoogleCloudPubSub 
CONNECT_GCP_TOPIC_NAME=event-abc-mail-sender 
CONNECT_GCP_PROJECT_ID=ct-connectors-preview 
node --input-type=module -e "import r from './src/utils/config.utils.js'; r(); console.log('ok')"
```

Before the change, the command throws. The error's `statusCode` is `InvalidEnvironmentVariablesError`, and its `errors` list contains the code `InvalidSubscriptionDestination`. That entry's message, `Subscription destination is required.`, is misleading: the value is set, but it's longer than the limit. Remove the upper limit. A separate validator, `validDestinations`, already restricts the value to `GoogleCloudPubSub` or `SNS`:

```js title="mail-sender/src/validators/env.validators.js (updated)"
  { min: 2, max: undefined }
```

Run the command again. It prints `ok`.

Templates change over time, so check the [template repository](https://github.com/commercetools/connect-email-integration-template) before you start. If your copy already allows the longer value, you can skip this step.

You can't see this problem from the Deployment. In [Install and test your Connector](/learning-integrate-with-commercetools/deploy-and-build-connectors/install-and-test-your-connector.md#verify-the-resource-not-the-status), the Deployment reports success while no Subscription exists. Making this change now saves a debugging session later.

## Tag your first release

Commit your changes and tag the release. Connect builds a Connector from a repository URL and a tag, so every version that you stage needs its own tag:

```bash
cd ..
git add .
git commit -m "Generate API Client credentials, update dependencies, adjust GCP destination validator"
git tag v0.1.0
git push origin main --tags
```

Zen Electron's order confirmation Connector now exists as `v0.1.0` in your repository. It still sends no real email: the template's `sendMail` method only logs. [Implement and test the event app to the contract](/learning-integrate-with-commercetools/deploy-and-build-connectors/implement-and-test-the-event-app.md) narrows the Connector to the Messages that Zen Electron sends email for and tests its mapping, and keeps the provider call stubbed so that you don't need a provider account.

## Other starting points

The email template is one of several ways to start a Connector:

- **Other templates.** `commercetools connect init` accepts other `--template` values, which [`commercetools connect init`](/connect/cli.md#commercetools-connect-init) lists. The values don't match the documentation page names, and `fulfilment-integration` has no documentation page. Compare the CLI reference with the [templates overview](/connect/templates/templates-overview.md).
- **An empty application.** [`commercetools connect application add`](/connect/cli.md#commercetools-connect-application-add) adds an application to an existing project.
- **A fork.** Fork an open-source Connector when it covers most of your requirement (step 3 of the adoption ladder).
- **An existing integration.** [Convert an existing integration](/connect/convert-existing-integration.md) describes how to add `connect.yaml` to code that you already run.

## Key takeaways

- A template is a starting point. Read, validate, and test it before you build on it, and expect to update its dependencies and configuration for your environment.
- Each `deployAs[].name` must match its folder, and the `endpoint` must match the path your router serves.
- `inheritAs.apiClient.scopes` makes Connect generate the Connector's API Client with only the scopes you list. The identity that deploys the Connector needs `manage_api_clients`.
- Software composition analysis scans dependencies and development dependencies, and fails on critical findings. Moving a package to `devDependencies` doesn't hide it: remove what you don't use and update what's vulnerable.
- Local analysis can be stricter than the remote pipeline. Use it as an early warning, and treat the remote report as the gate for preview and publish.
- A Deployment that reports success can still create no Subscription. In a Google Cloud Region, the template's destination validator rejects `GoogleCloudPubSub` until you remove the length limit.
- Updating the SDK packages to their current versions is a sensible follow-up. This module leaves it out to keep the tested path stable.

## Related pages

- [Area overview page with navigation](/learning-integrate-with-commercetools.md)
- [Previous page: Overview](/learning-integrate-with-commercetools/deploy-and-build-connectors/overview.md)
- [Next page: Implement and test the event app to the contract](/learning-integrate-with-commercetools/deploy-and-build-connectors/implement-and-test-the-event-app.md)
- [Search documentation and API specs](/search.md)