Guide

Email Deliverability from the CLI

Email deliverability is the measure of whether your emails reach the recipient's inbox or get filtered into spam, bounced, or silently dropped. It depends on three authentication protocols (SPF, DKIM, DMARC), sender reputation, and message content. This guide shows how to inspect and debug every layer from the command line using Nylas CLI and standard DNS tools.

SPF: who is allowed to send from your domain

Sender Policy Framework (SPF) is a DNS record that tells receiving mail servers which IP addresses are authorized to send email on behalf of your domain. When a server receives an email from your domain, it checks the SPF record to see if the sending server's IP is on the list.

# Check SPF record for any domain
dig +short TXT example.com | grep spf

# Example output:
# "v=spf1 include:_spf.google.com include:servers.mcsv.net ~all"

# Break down what this means:
# v=spf1                        → SPF version 1
# include:_spf.google.com       → Google Workspace can send for this domain
# include:servers.mcsv.net      → Mailchimp can send for this domain
# ~all                          → Soft-fail everything else (mark as suspicious but deliver)

The policy at the end matters:

  • -all — hard fail. Reject emails from unauthorized servers. Strongest protection.
  • ~all — soft fail. Accept but mark as suspicious. Most common during setup.
  • ?all — neutral. No policy. Effectively useless.
  • +all — allow all. Dangerous — anyone can spoof your domain.

Inspect SPF includes recursively

SPF records use include: directives that reference other domains. To see the full set of authorized IPs, you need to follow the chain:

# Follow the SPF include chain for Google Workspace
dig +short TXT _spf.google.com
# "v=spf1 include:_netblocks.google.com include:_netblocks2.google.com include:_netblocks3.google.com ~all"

# See the actual IP ranges
dig +short TXT _netblocks.google.com
# "v=spf1 ip4:35.190.247.0/24 ip4:64.233.160.0/19 ip4:66.102.0.0/20 ..."

# Check if you are close to the DNS lookup limit (max 10 lookups)
# Count the number of include/redirect/a/mx mechanisms
dig +short TXT yourdomain.com | grep -oE 'include:|redirect=|a:|mx:' | wc -l

SPF has a 10 DNS lookup limit. Exceeding it causes a permanent error (permerror), which means SPF effectively fails for every email. If you are using multiple email services (Google Workspace + Mailchimp + Salesforce + a transactional sender), you can hit this limit quickly.

DKIM: cryptographic email authentication

DomainKeys Identified Mail (DKIM) adds a cryptographic signature to outgoing emails. The sending server signs the message with a private key, and the receiving server verifies the signature using a public key published in DNS. This proves the email was actually sent by an authorized server and was not modified in transit.

# Check DKIM record for a domain
# You need to know the selector — common ones:
#   google / default   → Google Workspace
#   s1 / s2            → Microsoft 365
#   k1                 → Mailchimp
#   sm1 / sm2          → Salesforce

# Google Workspace DKIM
dig +short TXT google._domainkey.example.com

# Microsoft 365 DKIM
dig +short TXT selector1._domainkey.example.com

# Generic check (try common selectors)
for selector in google default s1 s2 selector1 selector2 k1 sm1; do
  RESULT=$(dig +short TXT "$selector._domainkey.example.com" 2>/dev/null)
  if [ -n "$RESULT" ]; then
    echo "Found DKIM for selector: $selector"
    echo "$RESULT"
    echo "---"
  fi
done

A valid DKIM record contains a public key (the p= field). If the record is empty or missing, DKIM signing is not configured for that selector.

DMARC: the policy that ties SPF and DKIM together

Domain-based Message Authentication, Reporting, and Conformance (DMARC) tells receiving servers what to do when an email fails SPF and DKIM checks. It also specifies where to send aggregate and forensic reports about authentication failures.

# Check DMARC record
dig +short TXT _dmarc.example.com

# Example output:
# "v=DMARC1; p=reject; rua=mailto:dmarc-reports@example.com; ruf=mailto:forensics@example.com; pct=100"

# Break down what this means:
# v=DMARC1                              → DMARC version 1
# p=reject                              → Reject emails that fail both SPF and DKIM
# rua=mailto:dmarc-reports@example.com  → Send aggregate reports here
# ruf=mailto:forensics@example.com      → Send forensic (failure) reports here
# pct=100                               → Apply to 100% of emails

DMARC policies in order of strictness:

  • p=none — monitor only. Receive reports but take no action. Start here.
  • p=quarantine — send failing emails to spam. Intermediate step.
  • p=reject — reject failing emails entirely. Full protection against spoofing.

Inspect authentication headers on received emails

The best way to verify your email authentication is working is to check the headers on emails you have received. Nylas CLI makes this straightforward:

# Read a message with full headers
nylas email read msg_abc123 --json \
  | jq '.headers'

# Look for specific authentication headers
nylas email read msg_abc123 --json \
  | jq '.headers | to_entries[] | select(.key | test("Authentication-Results|Received-SPF|DKIM-Signature|ARC"; "i"))'

# Example Authentication-Results header:
# Authentication-Results: mx.google.com;
#   dkim=pass header.i=@example.com header.s=google;
#   spf=pass (google.com: domain of sender@example.com designates 209.85.220.41 as permitted sender);
#   dmarc=pass (p=REJECT) header.from=example.com

The Authentication-Results header is the single most useful header for debugging deliverability. It shows the pass/fail status of SPF, DKIM, and DMARC in one place.

Send test emails and verify authentication

The fastest way to test deliverability is to send an email and check the headers on the receiving end:

# Send a test email
nylas email send \
  --to "test@gmail.com" \
  --subject "Deliverability test" \
  --body "Testing SPF, DKIM, and DMARC authentication."

# Then on the receiving end, check the authentication headers
# In Gmail: open the email → three dots → "Show original"
# Or if you have CLI access to the receiving account:
nylas email search "deliverability test" --limit 1 --json \
  | jq '.[0].headers | to_entries[] | select(.key | test("Authentication|SPF|DKIM|DMARC"; "i"))'

Diagnose common delivery failures

SPF failure: sending from an unauthorized server

# Symptom: emails going to spam or being rejected
# Check: is the sending server in the SPF record?

# Find your sending server's IP (from the email headers)
nylas email read msg_id --json \
  | jq -r '.headers["Received"]' \
  | grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+'

# Check if that IP is authorized by SPF
dig +short TXT yourdomain.com | grep spf
# If the sending IP is not in the SPF include chain, add it

DKIM failure: signature mismatch or missing key

# Check if the DKIM key exists for the selector in the email
# Find the selector from the DKIM-Signature header:
nylas email read msg_id --json \
  | jq -r '.headers["DKIM-Signature"]'
# Look for s=selector; in the output

# Then verify the public key exists in DNS
dig +short TXT selector._domainkey.yourdomain.com
# If empty: DKIM key not published. Add it to your DNS.

DMARC failure: alignment issues

# DMARC requires alignment: the domain in From must match
# the domain that passed SPF or DKIM

# Check DMARC alignment mode
dig +short TXT _dmarc.yourdomain.com
# aspf=r → relaxed SPF alignment (subdomains OK)
# aspf=s → strict SPF alignment (exact match only)
# adkim=r → relaxed DKIM alignment
# adkim=s → strict DKIM alignment

# If you send from marketing.example.com but SPF/DKIM
# is set up for example.com, strict alignment will fail

Check MX records and mail routing

MX (Mail Exchanger) records determine where incoming email is routed. If these are misconfigured, you will not receive email at all:

# Check MX records
dig +short MX example.com
# 10 alt1.aspmx.l.google.com.
# 5 aspmx.l.google.com.
# 20 alt2.aspmx.l.google.com.

# The number is the priority (lower = higher priority)
# Verify the MX servers are reachable
for mx in $(dig +short MX example.com | awk '{print $2}'); do
  echo -n "$mx: "
  dig +short A "$mx"
done

Verify TLS encryption

Modern email should always use TLS in transit. Check if a mail server supports STARTTLS:

# Check if a mail server supports STARTTLS
# Connect to the MX server on port 25 and check for STARTTLS
echo "EHLO test.example.com" | ncat -w 5 aspmx.l.google.com 25

# Check the TLS certificate
openssl s_client -connect aspmx.l.google.com:25 -starttls smtp < /dev/null 2>/dev/null \
  | openssl x509 -noout -subject -dates

Deliverability checklist

Run through this checklist when debugging delivery issues:

CheckCommandExpected result
SPF record existsdig +short TXT example.comContains v=spf1
SPF not too many lookupsCount includes10 or fewer
DKIM key publisheddig +short TXT selector._domainkey.example.comContains p= public key
DMARC policy setdig +short TXT _dmarc.example.comContains v=DMARC1
MX records resolvedig +short MX example.comReturns mail servers
Auth headers passnylas email read --jsonSPF=pass, DKIM=pass, DMARC=pass
TLS supportedopenssl s_client -starttls smtpValid certificate

Automate deliverability monitoring

Set up a script that periodically checks your email authentication and alerts on failures:

#!/bin/bash
# deliverability-check.sh — run with cron or in CI
DOMAIN="yourdomain.com"
ERRORS=0

# Check SPF
SPF=$(dig +short TXT "$DOMAIN" | grep "v=spf1")
if [ -z "$SPF" ]; then
  echo "FAIL: No SPF record found for $DOMAIN"
  ERRORS=$((ERRORS + 1))
else
  echo "PASS: SPF record found"
fi

# Check DMARC
DMARC=$(dig +short TXT "_dmarc.$DOMAIN")
if [ -z "$DMARC" ]; then
  echo "FAIL: No DMARC record found for $DOMAIN"
  ERRORS=$((ERRORS + 1))
else
  echo "PASS: DMARC record found"
  # Warn if policy is none
  if echo "$DMARC" | grep -q "p=none"; then
    echo "WARN: DMARC policy is 'none' — no enforcement"
  fi
fi

# Check DKIM (try common selectors)
DKIM_FOUND=false
for selector in google default s1 selector1; do
  DKIM=$(dig +short TXT "$selector._domainkey.$DOMAIN" 2>/dev/null)
  if [ -n "$DKIM" ]; then
    echo "PASS: DKIM record found for selector '$selector'"
    DKIM_FOUND=true
    break
  fi
done
if [ "$DKIM_FOUND" = false ]; then
  echo "WARN: No DKIM record found for common selectors"
fi

# Check MX
MX=$(dig +short MX "$DOMAIN")
if [ -z "$MX" ]; then
  echo "FAIL: No MX records found for $DOMAIN"
  ERRORS=$((ERRORS + 1))
else
  echo "PASS: MX records found ($(echo "$MX" | wc -l | tr -d ' ') servers)"
fi

# Summary
echo "---"
if [ $ERRORS -gt 0 ]; then
  echo "RESULT: $ERRORS failures found. Fix before sending email."
  exit 1
else
  echo "RESULT: All checks passed."
fi

Sending best practices from the CLI

When sending email through Nylas CLI, the authentication (SPF, DKIM) is handled by the provider. Your job is to ensure the content and sending patterns do not trigger spam filters:

  • Use a real reply-to address — do not send from no-reply addresses. Receiving servers score emails higher when replies are possible.
  • Keep subject lines honest — avoid all-caps, excessive punctuation, and spam trigger words in the subject.
  • Include plain text — when sending HTML email, always include a plain text alternative. Nylas CLI sends plain text by default.
  • Warm up new accounts — if using a new email account, start with low volume and increase gradually over 2-4 weeks.
  • Monitor bounces — check for bounce notifications and remove invalid addresses promptly.
# Send a well-formed email through authenticated channels
nylas email send \
  --to "recipient@company.com" \
  --subject "Q2 report summary" \
  --body "Hi Team, please find the Q2 summary below. Let me know if you have questions."

# Check for bounce notifications
nylas email search "delivery failure undeliverable returned mail" --json

Frequently asked questions

My SPF record is correct but emails still go to spam. Why?

SPF is necessary but not sufficient. Check DKIM and DMARC as well. Also check sender reputation — new domains or domains with a history of spam need time to build reputation. Content analysis (spam-like language, excessive links) can also trigger filters regardless of authentication.

How long does it take for DNS changes to propagate?

SPF, DKIM, and DMARC records typically propagate within 5-60 minutes, depending on TTL values. Use dig +trace TXT _dmarc.example.com to check propagation status from authoritative nameservers.

Do I need all three (SPF, DKIM, DMARC)?

Yes. Major providers (Google, Microsoft, Yahoo) require all three as of 2024. Missing any one of them significantly increases the chance of your emails being filtered or rejected.

How do I check deliverability for emails sent by AI agents?

The same way — check authentication headers on the receiving end. If your agent sends email through nylas email send, the email goes through the authenticated provider (Gmail, Outlook, etc.) with proper SPF, DKIM, and DMARC. The provider handles the authentication; you handle the content quality.


Next steps