Source: https://cli.nylas.com/guides/google-cloud-build-email

# Send Email from Google Cloud Build

Send build failure emails from Google Cloud Build with Nylas CLI. Pull the API key from Secret Manager and run the send command from a final on-failure step, no SMTP relay.

Written by [Nick Barraclough](https://cli.nylas.com/authors/nick-barraclough) Product Manager

Reviewed by [Qasim Muhammad](https://cli.nylas.com/authors/qasim-muhammad)

Updated June 9, 2026

> **TL;DR:** Store `NYLAS_API_KEY` in Secret Manager, install the CLI in a build step, and call [`nylas email send`](https://cli.nylas.com/docs/commands/email-send) from a final step that fires only when an earlier step failed. The failure gate is a one-line shell trick shown below.

## Why send a cloud build email notification from the CLI?

A cloud build email notification reaches people who do not watch the Google Cloud console: support, on-call, or a product owner. Nylas CLI sends that alert from a single build step. It pulls an API key from Secret Manager, installs in seconds, and calls `nylas email send` when a build fails, with no SMTP relay, Pub/Sub topic, or Cloud Run service to maintain.

Google's built-in SMTP notifier requires three managed resources and a stored relay password. The CLI replaces all of that with one container step that runs the send command and exits. The rest of this guide wires that step into a `cloudbuild.yaml` file with a working failure gate.

## How do I store the API key in Secret Manager?

Secret Manager is Google Cloud's managed store for API keys, passwords, and certificates, and Cloud Build reads from it natively. Create one secret for the Nylas API key and one for the sender grant ID. The build references each secret version by name, so rotating a credential never touches a build file.

Grant the Cloud Build service account the Secret Manager Secret Accessor role (roles/secretmanager.secretAccessor) on each secret. The default service account is PROJECT_NUMBER@cloudbuild.gserviceaccount.com. Without that binding, the build fails with a 403 permission error before it ever calls the send step, which is the most common first-run mistake.

Reference secrets through the availableSecrets block at the top of the build config. Cloud Build mounts each secret as an environment variable for the steps that opt in with secretEnv. Values never appear in build logs, and they are not stored in the build trigger or the source repository.

Create the two secrets and grant the build service account access. The commands below run once during setup and assume your project number is in `$PROJECT_NUMBER`. Replace the secret values with your real API key and grant ID.

```bash
# Create secrets (run once)
printf '%s' "$NYLAS_API_KEY" | gcloud secrets create nylas-api-key --data-file=-
printf '%s' "$NYLAS_GRANT_ID" | gcloud secrets create nylas-grant-id --data-file=-

# Let Cloud Build read them
gcloud secrets add-iam-policy-binding nylas-api-key \
  --member="serviceAccount:$PROJECT_NUMBER@cloudbuild.gserviceaccount.com" \
  --role="roles/secretmanager.secretAccessor"
```

## How do I add a send step to a Cloud Build pipeline?

A Cloud Build step is a container that runs one command. Cloud Build stops the build the moment a step exits non-zero, so a failing test step would halt the pipeline before any later notification step runs. Cloud Build also has no if: failure() keyword like GitHub Actions, so the failure gate is built from a shell status file instead.

The pattern: the build step catches its own exit code and writes a sentinel file only on success, so the step itself always exits 0 and the pipeline continues. The notification step then checks for that file. If the file is missing, the build failed, so the step sends the alert and exits 1 to mark the overall build failed.

Each Cloud Build step shares the /workspace directory, which persists across steps in a single build. That shared volume is how the sentinel file survives between the build step and the notification step. The default build timeout is 600 seconds (10 minutes), so keep the install and send fast.

This `cloudbuild.yaml` runs a build step, then a notification step gated on a sentinel file. The send step reads both secrets through `secretEnv`, installs the CLI, and calls `nylas email send` only when the success marker is absent. The grant ID is the first positional argument to the send command.

```yaml
steps:
  - id: build
    name: 'gcr.io/cloud-builders/docker'
    entrypoint: bash
    args:
      - -c
      - |
        # run the real work; record status instead of failing the step
        if npm ci && npm test; then touch /workspace/ok; fi

  - id: notify-on-failure
    name: 'gcr.io/cloud-builders/curl'
    entrypoint: bash
    secretEnv: ['NYLAS_API_KEY', 'NYLAS_GRANT_ID']
    args:
      - -c
      - |
        test -f /workspace/ok && exit 0
        curl -fsSL https://cli.nylas.com/install.sh | bash
        export PATH="$HOME/.config/nylas/bin:$PATH"
        nylas email send "$$NYLAS_GRANT_ID" \
          --to "oncall@example.com" \
          --subject "Build failed: $REPO_NAME @ $SHORT_SHA" \
          --body "Log: https://console.cloud.google.com/cloud-build/builds/$BUILD_ID" \
          --yes \
          --json
        exit 1  # propagate the failure so the build is still marked failed

availableSecrets:
  secretManager:
    - versionName: projects/$PROJECT_ID/secrets/nylas-api-key/versions/latest
      env: 'NYLAS_API_KEY'
    - versionName: projects/$PROJECT_ID/secrets/nylas-grant-id/versions/latest
      env: 'NYLAS_GRANT_ID'
```

## Why skip the SMTP relay for build alerts?

Google Cloud Build's built-in SMTP notifier needs a running Cloud Run service, a Pub/Sub topic, and an SMTP relay with a password, per Google's notification docs. That is three managed resources and a long-lived credential to deliver one email. A single build step that calls the send command removes all of it.

The CLI sends through an OAuth2 grant, so there is no SMTP password to store, no port 587 to open, and no TLS certificate to manage on the build worker. Authentication tokens refresh automatically every 3,600 seconds, and the API key in Secret Manager is the only secret the build needs.

Skipping the relay also removes a failure mode: an SMTP relay that is down hides the original build failure behind a delivery error. With one send step reading from Secret Manager, the build either sends the alert or logs a clear API error in the same run you are already watching.

## What should a Cloud Build failure email include?

A Cloud Build email should be readable in ten seconds. Put the trigger name, branch, short commit SHA, and a link to the build log near the top. Cloud Build exposes these as substitution variables: $BUILD_ID, $REPO_NAME, $BRANCH_NAME, $SHORT_SHA, and $PROJECT_ID. Inject them into the subject and body at send time.

Link directly to the build log in the Google Cloud console. The URL pattern is https://console.cloud.google.com/cloud-build/builds/$BUILD_ID and it deep-links to the failing step. A recipient should reach the logs in one click without searching the console for the right run among dozens.

Keep the body plain and skip dumping full logs into the email. Logs live in Cloud Logging with 30 days of retention by default. The alert points to the build and names the next action, such as who owns the failing service. That turns a status message into a routable task.

## How do I send a richer HTML deploy report?

For deployment builds, an HTML body carries more context than a one-line subject. Write a short HTML file inside the build step, then pass its contents to the send command with --body, which accepts HTML or plain text and auto-detects the format.

Keep the HTML small: a heading, two paragraphs, and a link to the build. Heavy HTML and inline styles raise spam scores and slow the build step. A 1 KB HTML body renders cleanly in Gmail, Outlook, and most mobile clients without triggering the clipping that Gmail applies to messages over 102 KB.

Generate the HTML at build time so it always carries the current commit and trigger. Hard-coded report templates drift from reality, but a body built from substitution variables stays accurate on every run. Store the template logic in the build config, not in a separate repository the build cannot reach.

Write the HTML inside the step and pass it to the send command with `--body`, which accepts HTML or plain text. The body below uses Cloud Build substitution variables so each message carries the live commit and branch. This runs in the same `notify-on-failure` step.

```bash
cat > /workspace/report.html <<HTML
<h1>Build failed</h1>
<p>Trigger: $TRIGGER_NAME on branch $BRANCH_NAME</p>
<p><a href="https://console.cloud.google.com/cloud-build/builds/$BUILD_ID">Open the build log</a></p>
HTML

nylas email send "$$NYLAS_GRANT_ID" \
  --to "oncall@example.com" \
  --subject "Build failed: $REPO_NAME @ $SHORT_SHA" \
  --body "$(cat /workspace/report.html)" \
  --yes \
  --json
```

## How do I operate Cloud Build alerts over time?

Use a dedicated automation sender account, not a developer mailbox. A shared automation grant is easier to audit and revoke, and the From identity makes it obvious the message came from a build, not a person. When team members change, you rotate one Secret Manager version instead of editing build files.

Track send failures separately from build failures. If a build fails and the send step also fails, you need both facts. Write the send command JSON output to the build log so an engineer can confirm whether the alert went out. The CLI returns a message ID on success, which is the proof the notification was delivered.

Rotate the API key on a schedule and after staff changes. Add a new Secret Manager version, point the build at the new version, and run a manual build against a test trigger to confirm the message lands. Secret Manager keeps prior versions, so a bad rotation rolls back in seconds rather than requiring a code change.

## References for this build

- [Cloud Build configuration schema](https://cloud.google.com/build/docs/build-config-file-schema) -- steps, substitution variables, and availableSecrets
- [Use secrets in Cloud Build](https://cloud.google.com/build/docs/securing-builds/use-secrets) -- secretEnv mounting and Secret Accessor role
- [Cloud Build SMTP notifications](https://cloud.google.com/build/docs/configuring-notifications/configure-smtp) -- the relay-based path the CLI replaces

## Next steps

- [Send Email from Azure Pipelines](https://cli.nylas.com/guides/azure-pipelines-email-notifications) — Install the Nylas CLI in an Azure Pipelines job, authenticate from…
- [Send Email from Buildkite Pipelines](https://cli.nylas.com/guides/buildkite-email-notifications) — Send build failure email from a Buildkite pipeline with Nylas CLI
- [GitHub Actions email notifications](https://cli.nylas.com/guides/github-actions-email-notifications) -- the same pattern with if: failure()
- [CircleCI email notifications](https://cli.nylas.com/guides/circleci-email-notifications) -- on-failure alerts in CircleCI
- [Kubernetes CronJob email](https://cli.nylas.com/guides/kubernetes-cronjob-email) -- scheduled job alerts from a cluster
- [Email to GitHub issues](https://cli.nylas.com/guides/email-to-github-issues) -- turn build failures into tracked work
- [Email to Asana tasks](https://cli.nylas.com/guides/email-to-asana-tasks) -- route failure alerts into a task board
- [Send email from the terminal](https://cli.nylas.com/guides/send-email-from-terminal) -- full send command reference
- [Command reference](https://cli.nylas.com/docs/commands) -- email send flags and JSON output
