Source: https://cli.nylas.com/guides/sales-followup-email-cli

# Automate Sales Follow-Up Emails (CLI)

Build a sales follow-up cadence from a shell script: send a personalized first touch, track opens and clicks, read replies on a schedule, and stop the sequence the moment a prospect answers — without standing up SMTP or a CRM.

Written by [Caleb Geene](https://cli.nylas.com/authors/caleb-geene) Director, Site Reliability Engineering

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

Updated June 9, 2026

> **TL;DR:** Send each follow-up touch with [`nylas email send --track-opens --track-links`](https://cli.nylas.com/docs/commands/email-send), then read replies on a cron with `nylas email search`. One signal decides whether the next touch ever leaves the queue — the `thread.replied` webhook that stops the sequence is in the last section.

## How do I send a tracked sales follow-up from the terminal?

Send a sales follow-up by passing the prospect, a subject, and an HTML body to `nylas email send` with `--track-opens` and `--track-links`. Delivery goes over your connected provider's API, so there's no SMTP host or TLS certificate to manage, and the open pixel plus rewritten links are injected automatically.

Sales cadences live or die on persistence. Brevet's widely cited analysis found 80% of sales need five follow-ups after the first meeting, yet 44% of reps stop after one. Tracking tells you which prospects opened touch one, so you spend the next four on people who showed intent. The `--track-label` flag tags the send so you can group all opens for one campaign later.

```bash
nylas email send \
  --to jordan@acme.example \
  --subject "Following up on your API evaluation" \
  --body "<p>Hi Jordan, circling back on the trial. Want a 15-min walkthrough?</p>" \
  --track-opens --track-links \
  --track-label q3-outbound \
  --metadata campaign=q3-outbound --metadata step=1 \
  --yes
```

That single command replaces a marketing-automation seat for low-volume outbound, and it works the same across Gmail, Outlook, and any IMAP account. Per [the Gmail API sending docs](https://developers.google.com/workspace/gmail/api/guides/sending), messages sent through the API land in the account's Sent folder, so the thread stays visible to the rep in their normal inbox.

## How do I personalize each follow-up at send time?

Personalize a follow-up by interpolating prospect fields into the subject and body before the send command runs. A shell loop over a CSV of leads turns one template into dozens of unique messages, each with the prospect's name, company, and the specific product they evaluated. Generic blasts get ignored; a 2023 vendor study put personalized subject lines at a 26% higher open rate than untargeted ones.

The script below reads a three-column CSV and sends a tagged first touch per row. Each send carries `--metadata` pairs so you can filter the campaign later — metadata supports up to 50 key-value pairs, and keys `key1` through `key5` are indexed and filterable.

```bash
#!/usr/bin/env bash
set -euo pipefail

# leads.csv columns: email,first_name,company
tail -n +2 leads.csv | while IFS=, read -r email first company; do
  nylas email send \
    --to "$email" \
    --subject "Quick follow-up for $company" \
    --body "<p>Hi $first, following up on $company's evaluation.</p>" \
    --track-opens --track-links \
    --metadata key1=q3-outbound --metadata key2=step1 \
    --yes
  sleep 2
done
```

The two-second pause keeps you under provider send-rate ceilings on shared accounts. For richer templating with reusable layouts, render a [hosted template](https://cli.nylas.com/guides/send-email-with-templates-cli) instead of inline HTML and pass variables with `--template-data`.

## How do I advance the cadence to the next touch?

Advance the cadence by scheduling each subsequent touch on the day it's due. Pass `--schedule` a relative duration like `3d` or an absolute time, and the provider holds the message until then. A typical B2B cadence runs four to six touches spaced two to four days apart over a two-week window.

The command below queues touch two for three days out and keeps the same tracking and metadata so opens roll up under one campaign. Scheduled sends are queued provider-side, so the script doesn't need to stay running. You can list everything pending with `nylas email scheduled` and cancel a touch if the prospect replies first.

```bash
nylas email send \
  --to jordan@acme.example \
  --subject "Re: Following up on your API evaluation" \
  --body "<p>Hi Jordan, still happy to set up that walkthrough this week.</p>" \
  --track-opens --track-links \
  --metadata key1=q3-outbound --metadata key2=step2 \
  --schedule 3d \
  --yes

# Review everything queued for later delivery
nylas email scheduled list --json
```

Scheduling instead of sleeping a shell process means a laptop closing or a CI runner timing out won't drop touch two. The provider owns the queue until the send fires.

## How do I detect when a prospect replies?

Detect replies by searching the campaign thread for inbound messages on a schedule. Run `nylas email search` filtered to the prospect's address, or list the thread and check whether the latest message came from them. A cron job every 15 minutes is enough for a cadence measured in days, and each poll returns in under a second.

The snippet below searches for any message from a prospect since the campaign started. If the search returns a hit, the prospect has engaged and the next scheduled touch should be canceled. Reading the reply lets the rep answer in-thread with `nylas email reply`, which preserves the conversation's threading via the original message's reply identifier.

```bash
# Poll for an inbound reply from one prospect since the campaign started
HITS=$(nylas email search "*" \
  --from jordan@acme.example \
  --after 2026-06-01 \
  --json | jq 'length')

if [ "$HITS" -gt 0 ]; then
  echo "Prospect replied — stop the cadence"
fi
```

Polling is simple but lossy: a reply that lands two minutes after a 15-minute tick still sends the queued touch. For a sequence that stops the instant a prospect answers, switch from polling to the push trigger in the next section.

## How do I stop the sequence the moment someone replies?

Stop the sequence by subscribing to the `thread.replied` webhook trigger, which fires the moment a new message joins a thread you started. Nylas pushes the event to your URL with an HMAC signature, so your handler cancels the queued touch in real time instead of waiting up to 15 minutes for the next poll.

Create the subscription with `nylas webhook create` and at least one trigger; run `nylas webhook triggers --category thread` to confirm the exact name. For local development, the built-in server tunnels a public URL and verifies the signature on every event, so you test the full path without deploying.

```bash
# Subscribe to reply events for the sending account
nylas webhook create \
  --url https://hooks.example.com/cadence \
  --triggers thread.replied \
  --description "Stop cadence on prospect reply"

# Develop locally: tunnel a public URL and verify signatures
nylas webhook server --tunnel cloudflared --secret "$WEBHOOK_SECRET"
```

When the event arrives, your handler maps the thread ID back to the campaign and deletes the matching scheduled send. The [RFC 8058 one-click unsubscribe](https://datatracker.ietf.org/doc/html/rfc8058) header pairs well here: honor opt-outs the same way you honor replies, by removing the prospect from the queue. Microsoft documents the equivalent send path in [the Graph sendMail reference](https://learn.microsoft.com/en-us/graph/api/user-sendmail), so the same cadence logic works whether the rep's account is Outlook or Gmail.

## Next steps

- [Push Email into a Coda Doc (CLI)](https://cli.nylas.com/guides/email-to-coda) — Pull email as JSON with the Nylas CLI and insert one row per…
- [Personalize outbound email](https://cli.nylas.com/guides/personalize-outbound-email-cli) — interpolate prospect fields into each touch from a CSV or API
- [Track email opens and replies](https://cli.nylas.com/guides/track-email-opens-replies-cli) — read open and click signals to score who's engaged
- [Mail merge from the terminal](https://cli.nylas.com/guides/email-mail-merge-cli) — send a batch from a spreadsheet with per-row fields
- [Send email with hosted templates](https://cli.nylas.com/guides/send-email-with-templates-cli) — reuse a layout and pass variables instead of inline HTML
- [Tag emails with metadata](https://cli.nylas.com/guides/tag-emails-with-metadata-cli) — group a campaign with indexed, filterable key-value pairs
- [Command reference](https://cli.nylas.com/docs/commands) — every flag, subcommand, and example
- [Gmail API sending docs](https://developers.google.com/workspace/gmail/api/guides/sending) — how API sends land in the Sent folder and the thread
- [Microsoft Graph sendMail](https://learn.microsoft.com/en-us/graph/api/user-sendmail) — the Outlook-side equivalent send endpoint
- [RFC 8058](https://datatracker.ietf.org/doc/html/rfc8058) — one-click unsubscribe, the opt-out signal to honor like a reply
