Source: https://cli.nylas.com/guides/newsletter-digest-agent-account

# Build an Email Digest Agent

A weekly digest to an opt-in list is a small recurring job that's easy to get wrong: stale recipients, a missed unsubscribe, a send that double-fires. An agent account turns it into a clean scheduled loop. This guide builds a digest agent on a dedicated sender that resolves the recipient list from a contacts group, drops anyone who opted out, renders a stored template per recipient, and sends DKIM-signed on a schedule — with a policy cap so a bug can't blast the whole list twice.

Written by [Nick Barraclough](https://cli.nylas.com/authors/nick-barraclough) Product Manager

Updated June 8, 2026

> **TL;DR:** A digest agent resolves a subscriber group with `nylas contacts search --group`, drops opt-outs against a suppression list, and sends a templated `nylas email send --template-id` per recipient on a cron schedule. A workspace policy caps the volume.

## What is an email digest agent?

An email digest agent sends a recurring, templated summary to an opt-in list — a weekly product digest, a team status roundup, a subscriber newsletter. It resolves who should receive it, removes anyone who unsubscribed, renders the same template for each recipient, and sends on a schedule. Built on an agent account, the sender is a managed identity and the recipient list lives in the agent's own contacts directory.

The job is small but unforgiving: send to a stale list and you hit dead addresses; miss an unsubscribe and you violate the recipient's choice and [CAN-SPAM rules](https://www.ftc.gov/business-guidance/resources/can-spam-act-compliance-guide-business); let the loop double-fire and everyone gets two copies. The agent-account version handles each of those with a contacts group, a suppression list, and a workspace send cap.

Digest flow: resolve the subscriber group, drop opt-outs, render the template, and send DKIM-signedResolve groupcontacts searchDrop opt-outssuppression listRendertemplate-idSendDKIM-signed

## Why send the digest from an agent account?

A digest needs three things a raw script doesn't give you for free: authenticated delivery, a recipient source of truth, and a volume ceiling. An agent account provides all three — every send is DKIM-signed on the managed domain, the recipient list is a contacts group on the same grant, and a workspace policy caps how much the sender can send in a day.

That send cap is the safety net a digest specifically needs. The classic newsletter failure is a loop bug that sends the whole list twice, or a hundred times. With the cap enforced at the workspace layer — outside the sending code — a runaway loop hits a hard ceiling instead of mailing your entire audience into marking you as spam.

## How do I set up the sender and subscriber group?

Create the sender, then a contacts group to hold subscribers. The [`nylas agent account create`](https://cli.nylas.com/docs/commands/agent-account-create) call provisions the identity in under 2 seconds, and `nylas contacts groups` organizes the recipients:

```bash
nylas agent account create digest@yourapp.nylas.email

# Subscribers live in a contacts group on the same grant
nylas contacts groups list --json | jq '.[] | {id, name}'
```

Add a subscriber by creating a contact and placing it in the group when someone opts in. Keeping the list in contacts rather than a hard-coded array means the digest always sends to the current membership — add a subscriber today, and the next run includes them automatically. The [contacts directory guide](https://cli.nylas.com/guides/agent-account-contacts) covers adding and grouping contacts.

## How does the agent resolve the recipient list?

At send time, the agent turns the group into a current list of addresses with `nylas contacts search --group`. The `--has-email` filter drops any contact with no address, so the list is only deliverable recipients:

```bash
nylas contacts search --group grp_abc123 --has-email --json \
  | jq -r '.[].emails[0].email'
```

Resolving the list fresh on every run is what keeps it accurate — a digest that reads a current group never mails a subscriber who was removed last week. The group id stays constant in your script; the membership behind it changes as people join and leave.

## How does the agent honor unsubscribes?

Every digest must offer a way out, and honoring it is non-negotiable — both for trust and for [CAN-SPAM compliance](https://www.ftc.gov/business-guidance/resources/can-spam-act-compliance-guide-business), which requires honoring an opt-out within 10 business days. Keep a suppression list of opted-out addresses and check it before every send, so an unsubscribe takes effect on the next run regardless of whether the contact is still in the group:

```bash
# unsubscribed.txt: one opted-out address per line
# Skip a recipient if they're on the suppression list
grep -qxF "$email" ./unsubscribed.txt 2>/dev/null && continue
```

A suppression list beats removing the contact from the group because it's a permanent record: even if someone is later re-added to the group by mistake, the suppression check still drops them. Wire your unsubscribe link to append the address to this file, and the next digest run honors it automatically.

## How does the agent send the digest?

The send loop ties it together: resolve the group, skip suppressed addresses, and send a templated message to each remaining recipient. A stored template keeps every copy identical, and per-recipient `--template-data` merges in anything personal. The loop runs under `set -euo pipefail`:

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

group_id="grp_abc123"
suppress="./unsubscribed.txt"

nylas contacts search --group "$group_id" --has-email --json \
  | jq -r '.[].emails[0].email' \
  | while IFS= read -r email; do
      # Honor opt-outs
      grep -qxF "$email" "$suppress" 2>/dev/null && continue

      # Tolerate a per-recipient failure instead of aborting the whole run
      if ! nylas email send --to "$email" \
        --subject "Your weekly digest" \
        --template-id tpl_digest \
        --template-data "{\"email\": \"$email\"}"; then
        echo "skip: send failed for $email" >&2
        continue
      fi
    done
```

Each recipient is one send, which keeps a bounce or a template error isolated to a single address instead of failing the whole run. For a large list, the workspace send cap protects you from a loop bug, and the `--schedule` flag can stagger delivery across a window rather than firing every message at once. The [email templates guide](https://cli.nylas.com/guides/email-templates-cli) covers authoring the digest template.

## How do I schedule the digest?

A digest is defined by its cadence, so the loop belongs in cron. A weekly digest is a single crontab line; the script resolves the current group and suppression list on every run, so the schedule is the only thing you set:

```bash
# crontab: send the digest every Monday at 8am
0 8 * * 1 /usr/local/bin/send-digest.sh
```

Because the script reads the live group each run, you never edit the cron job to change recipients — membership changes in contacts, and the schedule stays put. For unattended reliability patterns — exit codes, retries, and avoiding a double-send on a rerun — see [Build Reliable Email Automation](https://cli.nylas.com/guides/build-reliable-email-automation).

## Keeping the digest compliant and contained

A few controls keep a digest agent on the right side of both deliverability and the law. Honor unsubscribes via the suppression list on every send. Include a valid physical postal address in every message, which CAN-SPAM requires. Cap volume with a workspace policy so a bug can't mail the list repeatedly. And send only opt-in mail — a digest is for people who asked for it, not a cold list. The policy cap is one command:

```bash
nylas agent policy create --data '{
  "name": "Digest sender cap",
  "limits": { "limit_count_daily_message_per_grant": 10000 }
}'
```

Set the cap above your list size with headroom but below a runaway. A digest is commercial mail, not transactional, so the unsubscribe path and postal address aren't optional — but with DKIM authentication, a clear opt-out, and a volume ceiling, it behaves like the well-run permission-based sender it should be. For the full policy and rule reference, see [Agent Rules and Policies](https://cli.nylas.com/guides/agent-rules-and-policies).

## Next steps

- [Give Your AI Agent a Contacts Directory](https://cli.nylas.com/guides/agent-account-contacts) — add and group the subscribers this digest resolves
- [Email Templates from the CLI](https://cli.nylas.com/guides/email-templates-cli) — author and version the digest template
- [Build a Transactional Email Agent](https://cli.nylas.com/guides/transactional-email-agent-account) — the same sender patterns for event-triggered receipts and alerts
- [Build Reliable Email Automation](https://cli.nylas.com/guides/build-reliable-email-automation) — exit codes, retries, and idempotency for the scheduled loop
- [Agent Rules and Policies](https://cli.nylas.com/guides/agent-rules-and-policies) — the send caps that keep the digest from flooding the list
- [Full command reference](https://cli.nylas.com/docs/commands) — every `nylas contacts` and `nylas email send` flag
- [Nylas v3 API documentation](https://developer.nylas.com/) — the contacts and send APIs behind these commands
