Source: https://cli.nylas.com/guides/circleci-email-notifications

# Send Email from CircleCI Pipelines

Send build and deploy email alerts from CircleCI with Nylas CLI. Authenticate from a context env var and email on failure with a when: on_fail step, no SMTP host or orb.

Written by [Aaron de Mello](https://cli.nylas.com/authors/aaron-de-mello) Senior Engineering Manager

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

Updated June 9, 2026

> **TL;DR:** Store `NYLAS_API_KEY` in a CircleCI context, install the CLI in a job, and call [`nylas email send --yes`](https://cli.nylas.com/docs/commands/email-send) from a `when: on_fail` step. The payoff comes later: one alert per failed run, and no SMTP host or orb to maintain.

You want a failed CircleCI build to email the people who can act on it, and you do not want to register an SMTP host, store a mail password, or pull in a third-party orb. The Nylas CLI sends the CircleCI email notification from one job step: store an API key in a context, install the binary, and call `nylas email send` behind a `when: on_fail` guard. The same job runs on the default Docker executor and on a macOS runner.

## How do I authenticate the CLI in a CircleCI job?

A CircleCI job authenticates the CLI by reading an API key from an environment variable supplied by a context. Store the key as a context variable named `NYLAS_API_KEY`, set `NYLAS_GRANT_ID` for the sender account, and the binary picks both up automatically. No browser OAuth flow runs in CI, so the headless path is the only one that works.

CircleCI contexts hold shared secrets across projects in an organization. A context is scoped by security group, so only approved projects can read `NYLAS_API_KEY`. That is cleaner than per-project environment variables when several pipelines send through the same automation account, and it lets you rotate one secret instead of editing twenty project settings.

Set `NYLAS_DISABLE_KEYRING=true` in the job so the tool does not try to open a system keyring that does not exist on an ephemeral runner. The default Docker executor ships 2 vCPUs and 4 GB of RAM with no keyring service, and without that flag the first authenticated call can hang. The grant ID is the account that appears in the From header on every alert.

Set the three values in a context, then export the keyring flag in the job. The CLI reads `NYLAS_API_KEY` and `NYLAS_GRANT_ID` from the environment with no extra `auth config` call:

```bash
# Set once in the CircleCI context "nylas-ci":
#   NYLAS_API_KEY   = nyk_...
#   NYLAS_GRANT_ID  = <sender grant id>
#
# In the job, disable the keyring on the ephemeral runner:
export NYLAS_DISABLE_KEYRING=true

# Confirm auth resolves before sending:
nylas email folders list --json | head
```

## How do I email only when a CircleCI job fails?

Add a step with `when: on_fail` so it runs only after an earlier step exits non-zero. CircleCI evaluates the when attribute per step: `on_fail` runs when any prior step in the job failed, `on_success` runs when all prior steps passed, and `always` runs in both cases. One on_fail step at the end of a job sends a single alert instead of one per command.

Keep the notify step last in the job. Earlier steps run the build, the tests, or the deploy, and the email step reports the outcome. Because `on_fail` only triggers on a non-zero exit, a green pipeline sends nothing and stays quiet. CircleCI marks the job failed but still runs the on_fail step, so the alert reflects the real status.

Guard the install and send in the same on_fail step to avoid wasting runner minutes on green builds. The 2024 CircleCI State of Software Delivery report measured a median recovery time of roughly one hour after a failed default-branch run, so a fast, targeted email shortens the window before a human sees the break.

The send command runs inside the on_fail step. The grant ID is the first positional argument, `--to` sets the recipient, and `--yes` skips the confirmation prompt no runner can answer:

```bash
nylas email send "$NYLAS_GRANT_ID" \
  --to "$ALERT_EMAIL_TO" \
  --subject "CircleCI failed: $CIRCLE_PROJECT_REPONAME ($CIRCLE_BRANCH)" \
  --body "Build $CIRCLE_BUILD_NUM failed. Run: $CIRCLE_BUILD_URL" \
  --yes \
  --json
```

## What does a working.circleci/config.yml look like?

A working config defines one job that runs your build, then a final step with `when: on_fail` that installs the CLI and sends one email. The workflow attaches the context that holds `NYLAS_API_KEY` and `NYLAS_GRANT_ID`. CircleCI reads `.circleci/config.yml` from the repository root on every push, and version 2.1 has been the current schema since 2019.

The install script runs inside the job because CircleCI Docker executors start from a clean image on every run. It auto-detects the architecture, downloads the latest release from GitHub, and verifies the binary with a SHA-256 checksum in a few seconds. See [getting started](https://cli.nylas.com/guides/getting-started) for Homebrew and other install methods.

CircleCI exposes built-in variables such as `CIRCLE_PROJECT_REPONAME`, `CIRCLE_BRANCH`, `CIRCLE_SHA1`, and `CIRCLE_BUILD_URL` inside every job. Pass `CIRCLE_BUILD_URL` in the body so the recipient opens the exact failed run. Without `--yes` the send blocks forever and the job times out:

```yaml
version: 2.1

jobs:
  build:
    docker:
      - image: cimg/node:22.11
    steps:
      - checkout
      - run: npm ci
      - run: npm test

      - run:
          name: Email on failure
          when: on_fail
          command: |
            export NYLAS_DISABLE_KEYRING=true
            curl -fsSL https://cli.nylas.com/install.sh | bash
            export PATH="$HOME/.config/nylas/bin:$PATH"
            nylas email send "$NYLAS_GRANT_ID" \
              --to "$ALERT_EMAIL_TO" \
              --subject "CircleCI failed: $CIRCLE_PROJECT_REPONAME" \
              --body "Run: $CIRCLE_BUILD_URL" \
              --yes --json

workflows:
  ci:
    jobs:
      - build:
          context:
            - nylas-ci
```

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

Write a short HTML file in the job and pass its contents to `--body` — the flag accepts HTML or plain text and the CLI auto-detects which, so the tool sends a rich message. This keeps the YAML readable and lets you include a heading, the failing branch, the commit SHA, and a link to the run. A scannable report beats a wall of raw log output.

Keep the HTML small: a heading, two or three lines, and one link. Do not paste full build logs into the email, because CircleCI already stores logs for 30 days on the run page. The email points to that page; it does not replace it. A 10-second read with one click to the run is the target.

Use `--track-opens` when you must confirm an on-call engineer saw a high-severity alert. The CLI records an open event you can query later, which helps incident reviews where you prove the notification path worked. Reserve tracking for high-severity streams so routine build emails stay lightweight:

```bash
cat > report.html <<'HTML'
<h1>CircleCI build failed</h1>
<p>Branch failed during the test step. Open the run for logs.</p>
HTML

nylas email send "$NYLAS_GRANT_ID" \
  --to "$ALERT_EMAIL_TO" \
  --subject "Build failed: $CIRCLE_PROJECT_REPONAME" \
  --body "$(cat report.html)" \
  --track-opens \
  --yes --json
```

## Why run a separate channel test for CircleCI alerts?

Run a manual pipeline that sends a test email so you verify the alert path without breaking a real build on purpose. CircleCI triggers a pipeline from the API or the UI with a parameter, and a parameter-gated job can send one message with a clear subject like CircleCI email channel test. Run it after rotating the API key or changing the recipient list.

Track send failures separately from build failures. If a deploy fails and the email step also fails, you need both facts. Write the command output to the job with `--json` so the returned message ID lands in the CircleCI log. That gives engineering a way to confirm the alert was sent without searching inboxes manually.

Rotate the context secret on a schedule, such as every 90 days, and after team changes. A dedicated automation sender makes revocation clean: update one context variable, run the test pipeline, and confirm the message lands. Keep that drill separate from production failure alerts so a smoke test never reads like a real incident to the people on call.

```bash
# Parameter-gated smoke test, triggered manually:
nylas email send "$NYLAS_GRANT_ID" \
  --to "$ALERT_EMAIL_TO" \
  --subject "CircleCI email channel test" \
  --body "Channel verified at run $CIRCLE_BUILD_URL" \
  --yes --json
```

## 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
- [Send Email from Google Cloud Build](https://cli.nylas.com/guides/google-cloud-build-email) — Send build failure emails from Google Cloud Build with the CLI.
- [GitHub Actions email notifications](https://cli.nylas.com/guides/github-actions-email-notifications) – the same on-failure pattern with `if: failure()`
- [GitLab CI email notifications](https://cli.nylas.com/guides/gitlab-ci-email-notifications) – CI alerts with masked CI/CD variables
- [Jenkins email notifications](https://cli.nylas.com/guides/jenkins-email-notifications) – pipeline alerts without the Email Extension plugin
- [Email to Telegram notifications](https://cli.nylas.com/guides/email-to-telegram-notifications) – route alerts to a chat channel
- [Email to Mattermost notifications](https://cli.nylas.com/guides/email-to-mattermost-notifications) – forward build alerts to a team channel
- [Send email from the terminal](https://cli.nylas.com/guides/send-email-from-terminal) – full send command guide
- [Command reference](https://cli.nylas.com/docs/commands) – email send flags and JSON output
- [CircleCI contexts](https://circleci.com/docs/contexts/) – shared, security-scoped secrets such as `NYLAS_API_KEY`
- [CircleCI configuration reference](https://circleci.com/docs/configuration-reference/) – the `when: on_fail` step attribute and job schema
- [CircleCI environment variables](https://circleci.com/docs/env-vars/) – built-ins like `CIRCLE_BUILD_URL` and `CIRCLE_SHA1`
