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

# Send Email from Buildkite Pipelines

Send build, test, and deploy email alerts from a Buildkite pipeline with Nylas CLI. Read credentials from a pipeline secret and send only when a command exits non-zero.

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` and a grant ID as Buildkite pipeline secrets, install the CLI in a step, and call [`nylas email send --yes`](https://cli.nylas.com/docs/commands/email-send) from a failure hook. The trick that keeps it from spamming on green builds is in the failure-only section below.

## Why send email from a Buildkite pipeline?

Buildkite runs your build steps on your own agents, which means there is no managed SMTP relay waiting to mail you. The Nylas CLI closes that gap: install it in a pipeline step, read an API key from a pipeline secret, and call `nylas email send` when a build fails. No SMTP host, no mail plugin, and no provider SDK in your scripts.

Buildkite agents are self-hosted, so the build that just broke ran on infrastructure you control, often inside a private VPC with no outbound SMTP route. The CLI sends over HTTPS to the Nylas API on port 443, which agents almost always already allow. One binary replaces a sidecar mail container and a hardcoded relay password. The alert lands with a real From identity for an operations inbox, a release owner, or an on-call engineer who does not watch the Buildkite dashboard.

## How do I store credentials as a Buildkite secret?

Buildkite exposes secrets to a step as environment variables through the agent, the Secrets feature, or an integration such as the AWS Secrets Manager or Vault hooks. The CLI reads `NYLAS_API_KEY` directly, so a single secret plus a grant ID is enough to send. Never paste a key into the pipeline YAML, because that file lives in your repository.

Use a dedicated automation account as the sender. A shared, auditable mailbox is easier to revoke than a developer's personal grant when a contractor rotates off the team. Keep three values out of version control: the API key, the grant ID of the sender, and the destination list. Buildkite masks known secret values in logs, but it cannot mask a body string you build at runtime, so avoid printing the message. The Secrets feature stores values encrypted and injects them only into the steps that request them, which is tighter than a plain agent environment hook.

```bash
# Configure the CLI from a pipeline secret (headless, no browser)
export NYLAS_API_KEY="$BUILDKITE_NYLAS_API_KEY"
export NYLAS_GRANT_ID="$BUILDKITE_NYLAS_GRANT_ID"
export NYLAS_DISABLE_KEYRING=1

nylas auth config --api-key "$NYLAS_API_KEY"
nylas doctor
```

## How do I send email only when a build fails?

A Buildkite command step exits non-zero when a command fails, and you can branch on that exit code in a single shell block. Run the build, capture `$?`, and call the CLI only when the code is not zero. This is the pattern that keeps green builds silent and reserves email for the failures a human must act on.

Buildkite also runs lifecycle hooks. A `pre-exit` hook fires after every command step on the agent, and the variable `BUILDKITE_COMMAND_EXIT_STATUS` holds the step exit code. That hook is the cleanest place to send a single failure email because it runs once per step regardless of how the command itself was written. The CLI call below installs in seconds with Homebrew and sends with `--yes` to skip the interactive confirmation that would otherwise hang a non-interactive agent.

```bash
steps:
  - label: ":hammer: build and test"
    command: |
      set +e
      npm ci && npm test
      STATUS=$?
      if [ "$STATUS" -ne 0 ]; then
        brew install nylas/nylas-cli/nylas
        export NYLAS_API_KEY="$BUILDKITE_NYLAS_API_KEY"
        nylas email send "$BUILDKITE_NYLAS_GRANT_ID" \
          --to "$ALERT_EMAIL_TO" \
          --subject "Build failed: $BUILDKITE_PIPELINE_SLUG #$BUILDKITE_BUILD_NUMBER" \
          --body "Branch $BUILDKITE_BRANCH failed. Open $BUILDKITE_BUILD_URL" \
          --yes
      fi
      exit $STATUS
```

Keep `exit $STATUS` as the last line so the step still reports the original failure to Buildkite. The email is a side effect, not a replacement for the build's own red status. See the install methods in [getting started](https://cli.nylas.com/guides/getting-started) if Homebrew is unavailable on your agent image.

## What should the failure email contain?

A useful build alert answers three questions in under ten seconds: what failed, where, and the link to the logs. Buildkite injects more than a dozen variables you can drop straight into the subject and body, including `BUILDKITE_BUILD_URL`, `BUILDKITE_BRANCH`, `BUILDKITE_COMMIT`, and `BUILDKITE_BUILD_CREATOR`. Front-load the pipeline name and build number in the subject so inbox filters work.

For deploy pipelines, send a short HTML body instead of plain text. Write the markup to a file, then pass it to `--body`, which accepts HTML or plain text, so the message renders with a heading and a clickable run link. Avoid pasting full logs into the email; an excerpt plus the `BUILDKITE_BUILD_URL` keeps the message small and points readers to the source of truth. The example below also adds `--metadata` so the sent message is searchable later by pipeline slug.

```bash
cat > alert.html <<'HTML'
<h1>Deploy failed</h1>
<p>The production deploy step did not finish.</p>
<p>Open the Buildkite build for logs and a retry.</p>
HTML

nylas email send "$BUILDKITE_NYLAS_GRANT_ID" \
  --to "$ALERT_EMAIL_TO" \
  --subject "Deploy failed: $BUILDKITE_PIPELINE_SLUG" \
  --body "$(cat alert.html)" \
  --metadata pipeline="$BUILDKITE_PIPELINE_SLUG" \
  --metadata build="$BUILDKITE_BUILD_NUMBER" \
  --yes
```

## How do I operate Buildkite alerts over time?

Verify the channel without breaking a build on purpose. Add a manual trigger step or a scheduled build that installs the CLI and sends a test message with a fixed subject such as `Buildkite email channel test`. Run it after rotating a secret or changing the agent image so a real failure is never the first time the path runs.

Track send failures separately from build failures. If a deploy fails and the email step also fails, you need both facts, so do not let an email error mask the build error. Rotate the API key on a schedule and after team changes; a dedicated automation grant makes that a one-line secret update rather than a code change. Keep recipient lists small and intentional, because a noisy alert stream gets filtered within weeks and the one failure that mattered goes unread. Pair email with a chat alert for severity-one pipelines so a single delivery problem never hides a production break.

## 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 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 pattern with `if: failure()`
- [CircleCI email notifications](https://cli.nylas.com/guides/circleci-email-notifications) — send alerts from a CircleCI job
- [Jenkins email notifications](https://cli.nylas.com/guides/jenkins-email-notifications) — post-build email from a Jenkinsfile
- [Send email from the terminal](https://cli.nylas.com/guides/send-email-from-terminal) — full `nylas email send` reference
- [Email deliverability with Nylas CLI](https://cli.nylas.com/guides/email-deliverability-cli) — keep CI alerts out of spam
- [Turn emails into GitHub issues](https://cli.nylas.com/guides/email-to-github-issues) — route inbound build replies to a tracker
- [Load email into Snowflake](https://cli.nylas.com/guides/email-to-snowflake) — archive alert history for analytics
- [Command reference](https://cli.nylas.com/docs/commands) — email send flags and JSON output
- [Buildkite secrets management](https://buildkite.com/docs/pipelines/security/secrets/managing) — storing and injecting credentials into steps
- [Buildkite conditionals](https://buildkite.com/docs/pipelines/configure/conditionals) — branching and exit-status handling in steps
- [RFC 5322](https://datatracker.ietf.org/doc/html/rfc5322) — the Internet Message Format for email headers and bodies
