Source: https://cli.nylas.com/guides/secure-email-handling-cli

Guide

# Secure CLI Email: API Keys and Audit Logging

Automated email workflows handle sensitive data. This guide covers the operational security practices needed to run Nylas CLI in production: API key rotation without downtime, audit logging that satisfies SOC 2, credential storage best practices, data handling policies, and permission scoping. Works across all major email providers.

Written by [Prem Keshari](https://cli.nylas.com/authors/prem-keshari) • Senior SRE

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

Updated April 11, 2026

Verified

 —

CLI

3.1.1

 ·

Gmail, Outlook

 ·

last tested

April 11, 2026

> **TL;DR:** Store API keys with `0600` permissions or in the system keychain. Rotate keys every 90 days with zero-downtime overlap. Log all email access operations (but never content) to a tamper-evident audit trail. In CI/CD, pass credentials via environment variables.

## Where Nylas CLI stores credentials

Nylas CLI stores API keys and OAuth grant tokens in `~/.nylas/config.json`. The file is created with `0600` permissions (read/write for the current user only). Verify:

```bash
# Check config file permissions
ls -la ~/.nylas/config.json
# Expected: -rw------- 1 youruser yourgroup ... config.json

# Fix permissions if needed
chmod 600 ~/.nylas/config.json

# View current auth (never displays full key)
nylas auth whoami
```

In CI/CD environments, always use environment variables instead of config files:

```bash
# CI/CD: pass API key via environment variable
export NYLAS_API_KEY="$NYLAS_API_KEY_SECRET"
nylas auth config --api-key "$NYLAS_API_KEY"
nylas auth whoami
```

## Rotate API keys without downtime

According to NIST SP 800-57, cryptographic keys should be rotated at least every 90 days. Nylas supports concurrent active keys during the rotation window:

File: `rotate-key.sh`

```bash
#!/bin/bash
# rotate-key.sh — zero-downtime API key rotation

# Step 1: Generate new key in Nylas dashboard
# Step 2: Update CLI config
nylas auth config --api-key "$NEW_API_KEY"

# Step 3: Verify
if nylas auth whoami > /dev/null 2>&1; then
  echo "New key verified"
else
  echo "ERROR: rolling back"
  nylas auth config --api-key "$OLD_API_KEY"
  exit 1
fi

# Step 4: Test read operation
if nylas email list --limit 1 --json > /dev/null 2>&1; then
  echo "Read confirmed with new key"
else
  echo "ERROR: rolling back"
  nylas auth config --api-key "$OLD_API_KEY"
  exit 1
fi

echo "Rotation complete. Revoke old key in dashboard."
```

## Build an audit trail for SOC 2

SOC 2 Type II requires an audit trail of all access to sensitive data. Log every operation with metadata but never log email content or PII:

File: `audit_wrapper.py`

```python
#!/usr/bin/env python3
"""Audit-logged Nylas CLI wrapper."""
import json, subprocess, os, datetime

AUDIT_LOG = os.path.expanduser("~/.nylas/audit.log")

def nylas_audited(args: list[str]) -> subprocess.CompletedProcess:
    result = subprocess.run(["nylas"] + args, capture_output=True, text=True)
    # Log metadata only — no email content or PII
    entry = {
        "timestamp": datetime.datetime.utcnow().isoformat() + "Z",
        "user": os.getenv("USER", "unknown"),
        "command": " ".join(args),
        "exit_code": result.returncode,
        "hostname": os.uname().nodename,
    }
    with open(AUDIT_LOG, "a") as f:
        f.write(json.dumps(entry) + "\n")
    return result

result = nylas_audited(["email", "list", "--limit", "10", "--json"])
messages = json.loads(result.stdout)
print(f"Read {len(messages)} messages (logged to audit trail)")
```

## Scope permissions with minimum-privilege grants

Create separate grants for different use cases. A monitoring script shouldn’t have send permissions:

```bash
# List active grants
nylas auth list

# Check scopes for current grant
nylas auth whoami --json | jq '.scopes'

# Revoke a grant when no longer needed
nylas auth logout --grant-id <grant-id>
```

## Data handling policies

- **Never log email bodies, subjects, or sender addresses.** Log message IDs and counts only.
- **Sanitize before piping to external services.** Strip PII before sending to LLMs, analytics, or logging.
- **Use temporary files with restricted permissions.** `mktemp` + `chmod 600` + delete after processing.
- **Don’t store email content in version control.** Add output files to `.gitignore`.

```bash
# Safe: process without persisting
EMAILS=$(nylas email list --json --limit 10)
echo "Processed $(echo "$EMAILS" | jq 'length') messages"

# If temp files needed, use restrictive permissions
TMPFILE=$(mktemp /tmp/nylas-XXXXXX.json)
chmod 600 "$TMPFILE"
nylas email list --json --limit 10 > "$TMPFILE"
# ... process ...
rm -f "$TMPFILE"
```

## CI/CD security patterns

File: `.github/workflows/report.yml`

```yaml
# GitHub Actions example
name: Email Report
on:
  schedule:
    - cron: '0 9 * * 1'
jobs:
  report:
    runs-on: ubuntu-latest
    steps:
      - name: Install Nylas CLI
        run: curl -fsSL https://cli.nylas.com/install.sh | bash
      - name: Configure auth
        run: nylas auth config --api-key "$NYLAS_API_KEY"
        env:
          NYLAS_API_KEY: ${{ secrets.NYLAS_API_KEY }}
      - name: Generate report (no email content in logs)
        run: |
          COUNT=$(nylas email list --json --limit 100 | jq 'length')
          echo "Inbox: $COUNT recent messages"
```

## SOC 2 compliance checklist

| Requirement | Implementation |
| --- | --- |
| Access control | Per-user API keys, minimum-privilege scopes |
| Audit trail | Log all operations to tamper-evident file |
| Key rotation | Every 90 days with zero-downtime overlap |
| Credential storage | Config at 0600 permissions, env vars in CI/CD |
| Data minimization | Never log email content or PII |
| Encryption in transit | All API calls use TLS 1.2+ |
| Incident response | Revoke grants immediately on breach |

## Next steps

- [Email deliverability from the CLI](https://cli.nylas.com/guides/email-deliverability-cli) — verify SPF, DKIM, DMARC for your sending domain
- [Debug invisible characters](https://cli.nylas.com/guides/debugging-invisible-characters-email) — find hidden Unicode that can bypass security filters
- [Agent-first email design](https://cli.nylas.com/guides/agent-first-email-design) — secure patterns for AI agents accessing email
- [Full command reference](https://cli.nylas.com/docs/commands)
