Source: https://cli.nylas.com/guides/email-address-validation-cli

# Validate Email Addresses from the CLI

Validate an email address from the terminal before you send. Combine an RFC 5322 syntax check with a dig MX DNS lookup to reject malformed addresses and dead domains, then hand only deliverable recipients to nylas email send so bad addresses never become hard bounces.

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

Updated June 9, 2026

> **TL;DR:** Validate an address in two cheap steps before you send: a regex syntax check against the `RFC 5322` shape, then a `dig MX` lookup that proves the domain can actually receive mail. Only the survivors reach `nylas email send`. There's one check most people skip that catches the costliest bounces — it's the last section.

## What does it mean to validate an email address?

Validating an email address means proving two things before you send: that the string matches the `RFC 5322` address grammar, and that its domain can accept mail — normally via an MX record, though RFC 5321 falls back to the domain's A/AAAA record when none is published. Syntax alone is not enough — a perfectly shaped address at a dead domain still bounces.

The two checks catch different failures. Syntax validation rejects typos like a missing `@` or a trailing comma. The DNS check flags domains with no mail exchanger, the routing target [RFC 5321](https://datatracker.ietf.org/doc/html/rfc5321) defines for SMTP. It also falls back to a domain's A/AAAA record when no MX is published, so treat a missing MX as a strong signal, not proof of undeliverability. Mailbox-level existence is a third tier you cannot prove without sending, so most teams stop at syntax plus MX. A typical bad-list still contains 5–15% addresses that fail one of the first two checks, and every one of those is a bounce you can avoid in milliseconds.

## How do I check email syntax with a regex?

A shell regex is the fastest syntax gate. The pattern below accepts the common `local@domain.tld` shape from the [RFC 5322](https://datatracker.ietf.org/doc/html/rfc5322) grammar and rejects the obvious failures — no `@`, spaces, or a missing top-level domain. It runs in under 1 millisecond per address with zero network calls.

The full RFC 5322 grammar permits quoted local parts and comments that almost no real mailbox uses, so a pragmatic pattern is the right trade-off. The [RFC 3696](https://datatracker.ietf.org/doc/html/rfc3696) errata note that a local part can be up to 64 octets and a full address up to 254, which the script also enforces. Save the function and pipe any address through it before the network step.

```bash
# Pragmatic RFC 5322 syntax check: shape + length limits
valid_syntax() {
  local addr="$1"
  # 254-char total, 64-char local part, one @, a dotted domain
  [ "${#addr}" -le 254 ] || return 1
  printf '%s' "$addr" | grep -Eq '^[A-Za-z0-9._%+-]{1,64}@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$'
}

valid_syntax "ada@example.com" && echo "syntax ok"
valid_syntax "ada@@example"    || echo "rejected: bad shape"
```

## Why do I need a dig MX lookup, not just syntax?

A `dig MX` lookup proves the recipient domain actually accepts mail. Syntax tells you the string is shaped like an address; the MX record tells you a server exists to receive it. A domain with no MX record — and no usable A/AAAA fallback, which [RFC 5321 section 5](https://datatracker.ietf.org/doc/html/rfc5321)permits when no MX exists — cannot receive mail, a strong hard-bounce signal.

This is the check that catches expensive mistakes: a real domain that lapsed, a typo'd TLD like `.con` that resolves to nothing, or a parked domain with web records but no mail exchanger. The lookup adds one DNS round trip, usually 20–80 milliseconds, and is cached by your resolver. An MX record is the DNS type described in [RFC 1035](https://datatracker.ietf.org/doc/html/rfc1035) that names the host responsible for a domain's mail. The function below treats a missing MX as undeliverable — a strong signal, though a domain with only an A/AAAA record can still receive mail, so use it as a filter, not absolute proof.

```bash
# DNS deliverability check: does the domain publish an MX record?
has_mx() {
  local domain="${1#*@}"
  [ -n "$(dig +short MX "$domain")" ]
}

has_mx "ada@gmail.com"          && echo "gmail.com has MX"
has_mx "ada@no-such-domain.con" || echo "rejected: no mail server"
```

## How do I gate nylas email send on validation?

The payoff is a pre-flight gate: run both checks, and only call `nylas email send` when an address passes. The command sends across providers like Gmail and Microsoft 365 without SMTP setup, and the `--yes` flag skips the confirmation prompt so the script runs unattended in a cron job or CI step.

Wiring the two functions in front of the send turns an open mailing loop into a filtered one. Each address costs roughly 80 milliseconds to clear, and a rejected address never touches your sending reputation. The script reuses `valid_syntax` and `has_mx` from the steps above, so the validation logic lives in one place.

```bash
# Validate, then send only the addresses that pass both checks
send_if_valid() {
  local to="$1"
  if ! valid_syntax "$to"; then echo "skip (syntax): $to"; return; fi
  if ! has_mx "$to";       then echo "skip (no MX): $to"; return; fi
  nylas email send --to "$to" \
    --subject "Welcome aboard" \
    --body "Thanks for signing up." \
    --yes
}

while read -r addr; do send_if_valid "$addr"; done < recipients.txt
```

## What catches the bounces validation misses?

Validation rejects malformed and dead-domain addresses, but it cannot prove a specific mailbox exists — only sending and watching the response can. The check most people skip is reconciliation: search your own sent and received mail for bounce notifications, then drop those recipients from the next run. This closes the loop on the 1–3% of syntactically valid, MX-backed addresses that still fail at the mailbox.

The `nylas email search` command finds bounce messages by their sender, usually `mailer-daemon`, and the `--json` flag makes the results machine-readable for a suppression list. A mailbox that returns a 550 hard bounce should never be sent to again; repeated hard bounces are the fastest way to damage domain reputation.

```bash
# Pull recent bounce notifications to build a suppression list
nylas email search "*" --from "mailer-daemon" --limit 50 --json
```

## Next steps

- [SMTP Reply Codes Explained](https://cli.nylas.com/guides/smtp-reply-codes-explained) — Decode SMTP reply codes
- [Improve email deliverability from the CLI](https://cli.nylas.com/guides/email-deliverability-cli) — SPF, DKIM, and DMARC checks that keep validated mail in the inbox
- [Email bounce codes reference](https://cli.nylas.com/guides/email-bounce-codes-reference) — decode 5xx hard bounces and 4xx soft bounces to feed your suppression list
- [Debug email delivery from the CLI](https://cli.nylas.com/guides/debug-email-delivery-cli) — trace why a sent message never arrived
- [Pipe email into SQLite](https://cli.nylas.com/guides/email-to-sqlite) — store validated recipients and bounce history in a local database
- [Relay email to a webhook](https://cli.nylas.com/guides/email-to-webhook-relay) — forward bounce events to a downstream service in real time
- [Command reference](https://cli.nylas.com/docs/commands) — every flag, subcommand, and example
- [RFC 5322: Internet Message Format](https://datatracker.ietf.org/doc/html/rfc5322) — the address grammar your syntax check follows
- [RFC 5321: SMTP](https://datatracker.ietf.org/doc/html/rfc5321) — how MX records route mail to a domain
- [RFC 1035: Domain Names](https://datatracker.ietf.org/doc/html/rfc1035) — the MX resource record your dig lookup queries
