Skill: flashlabs-ai/flashrev-mailer (version 2.0.1)

--- name: flashrev-mailer description: Use this skill when an AI agent needs to plan, build, commit, monitor, or follow up on FlashRev-powered email outreach via the flashrev-mailer npm CLI (v2.0+). Triggers on requests involving cold email, outreach campaigns, multi-step follow-up sequences, AI auto-reply, prospect reply triage, or mailbox-pool drip sending. The CLI delegates send timing and reply tracking to the FlashRev backend sequence engine; live sends require explicit per-batch user approval; AI auto-reply prompts must be shown verbatim and approved before enabling. Agents should invoke with FLASHREV_MAILER_AI_MODE=1 (or --ai-mode) so all list/view outputs and errors are JSON-structured. --- # FlashRev AI Mailer (v2.0.1) Use the `flashrev-mailer` CLI to build personalized email sequences with FlashRev mailbox pools, multi-step follow-up, event tracking (open/click/reply), and optional LLM auto-reply. v2 delegates send timing and reply tracking to the FlashRev backend; the CLI orchestrates building, commit, and inspection. ## Security posture This skill creates and runs outbound email campaigns. The following constraints apply to every workflow and are non-negotiable: - **API key** lives only in the operator's shell environment (`FLASHREV_API_KEY` or the name set via `flashrev.apiKeyEnv`). It is never written to `.flashrev/config.json`, campaign exports, send logs, or chat output. - **Live sending requires explicit human approval per batch.** The agent must run `--dry-run --yes` first, show rendered drafts and the destination mailbox to the user, and only proceed with `--live --yes` after the user approves drafts, schedule, and recipient set. The agent never auto-escalates batch size, never auto-resumes after pause without confirmation. - **`ai-auto-reply enable` is high-risk.** Once enabled, the LLM will reply to prospects on the user's behalf using the saved prompt. The agent must: 1. Show the full prompt verbatim to the user before calling `enable` 2. Never write or modify the prompt without the user's explicit text 3. Default to `disable` if there is any ambiguity - **`pause` / `resume`** change production state. Confirm with the user before running either. - **`reply --mail-id <ID>`** sends a real email to a prospect. Show the user the prospect's identity (from `inbox`) + the proposed reply body before sending. - **`reschedule`** changes when emails go out. Confirm new window with user. - **`send --live --send-now`** bypasses the working-time-window (sends step 1 immediately, may include weekends or off-hours). Confirm with user before using. - **`delete`** is irreversible. Even with `--yes`, list what will be deleted (local path + sequenceId + contact/step counts) in chat and get explicit confirmation from the user. Already-sent emails are not recalled. - **Recipient gating**: only `emailSyntaxValid=true` contacts are committed. `send --live` triggers backend deliverability check automatically (dispatch-svc EmailVerifyService); undeliverable contacts are flagged errorContacts and skipped. `validate` is optional and only useful as a pre-commit preview for cold lists. - **Failure mode is stop-and-ask**, not retry-with-different-input. If a prerequisite or confirmation is missing, halt the workflow and surface the gap to the user. ## Agent invocation convention Agents calling this CLI should set **either**: - Environment variable: `FLASHREV_MAILER_AI_MODE=1` (recommended; once per shell) - Per-call flag: `--ai-mode` on every invocation When enabled, the CLI **forces JSON output** on `list / outbox / inbox / status / mailboxes / ai-auto-reply show / export` AND **wraps errors in JSON** to stderr: ```json { "error": { "code": "...", "message": "...", "remediation": "..." } } ``` Without ai-mode, output is human-formatted tables — fine for users at a terminal, fragile for agents to parse. ## Prerequisites These must be set up by the **human operator**. The agent's role is to **verify** each one and **guide the user** to fix anything missing — never attempt to install software, generate API keys, or modify the shell environment unattended. - **Node.js ≥ 20** is on `PATH`. - **CLI is installed globally**: `npm install -g flashrev-ai-mailer`. Verify with `flashrev-mailer --help`. Confirm version 2.x: `flashrev-mailer --help 2>&1 | head -1` should not look like the v1 single-shot CLI. - **FlashRev API key** generated at https://info.flashlabs.ai/settings/privateApps and exported as `export FLASHREV_API_KEY="..."`. Persist in `~/.zshrc` or `~/.bashrc` per user preference. Never persist inside any config file. - **Base URL initialized** once per workspace: `flashrev-mailer init --base-url "https://open-ai-api.flashlabs.ai"` (or the test/staging URL provided by your FlashRev team). - **Outbound network access** to the FlashRev API host. - **Workspace is writable** — campaign state and inbox cache live under the current working directory at `.flashrev/`. Always run `flashrev-mailer doctor --check-api` before the first command. This verifies endpoints AND triggers backend lazy-init of a default schedule ("Default Business Hours") for new accounts. If a check fails, **stop the workflow, tell the user the exact missing prerequisite, and wait** — do not proceed to `import` / `send` until the user reports it resolved. ## Required confirmations Each item below is a business decision the agent must explicitly align with the user before proceeding — do not assume defaults: - Contact source approved (CSV, TSV, public CSV URL, Google Sheets export, Clay export). - The email column and personalization variables are understood. Personalization uses `{{ FirstName }}` / `{{ LastName }}` / `{{ Company }}` / `{{ Title }}` syntax (displayProperty names from the FlashRev contact property whitelist). - Campaign goal, offer, tone, sender identity, call-to-action approved. - For multi-step follow-up: each step's content, `--delay-days`, and whether it's a reply (same thread, no subject) or a new thread are confirmed. - Sending schedule (timezone + window + weekdays + holidays-skip policy) approved. - **AI auto-reply prompt** (if used) approved verbatim. Show the full prompt text to the user before `ai-auto-reply enable`. The prompt is ≤ 2000 chars. - User has reviewed drafts (`--dry-run`) and explicitly approved committing (`--live`). ## Workflow A — Single-step campaign (v1 style, still works) ```bash flashrev-mailer doctor --check-api flashrev-mailer mailboxes # pick an addressId flashrev-mailer import --campaign CAMPAIGN_ID --source contacts.csv flashrev-mailer validate --campaign CAMPAIGN_ID --limit 200 # optional (backend auto-checks on send --live) flashrev-mailer draft --campaign CAMPAIGN_ID \ --subject "Quick idea for {{ Company }}" \ --body "Hi {{ FirstName }}, ..." flashrev-mailer send --campaign CAMPAIGN_ID --dry-run --yes --mailbox ADDR_ID # SHOW USER # After user approves drafts: flashrev-mailer send --campaign CAMPAIGN_ID --live --yes --mailbox ADDR_ID flashrev-mailer status --campaign CAMPAIGN_ID ``` The agent shows the user the dry-run table and waits for explicit "yes, send" before `--live`. ## Workflow B — Multi-step follow-up ```bash flashrev-mailer import --campaign CAMPAIGN_ID --source contacts.csv # Step 1 (new thread) flashrev-mailer draft --campaign CAMPAIGN_ID \ --subject "Hi {{ FirstName }}" --body "..." # Step 2 (3-day follow-up, same thread, no subject — backend uses "Re: ...") flashrev-mailer draft --campaign CAMPAIGN_ID --step 2 --delay-days 3 \ --reply-to 1 --body "Just following up..." # Optionally step 3 etc — use --step N --delay-days N --reply-to <prev> flashrev-mailer send --campaign CAMPAIGN_ID --dry-run --yes --mailbox ADDR_ID # User approves drafts + delay structure: flashrev-mailer send --campaign CAMPAIGN_ID --live --yes --mailbox ADDR_ID flashrev-mailer status --campaign CAMPAIGN_ID ``` **Rules the agent must enforce when building follow-up:** - `--reply-to N` requires step N to already exist; if not, draft step N first. - `--reply-to N` rejects `--subject` (backend auto-sets `Re: <step N subject>`). - Confirm with user: "step 2 will be sent N days after step 1 unless the prospect replies first — is that what you want?" ## Workflow C — AI auto-reply LLM-driven replies trigger when prospects respond, using the prompt the user provides. **Independent** from `prospectRepliesEnabled` (which stops the sequence on reply). The two can be combined. ```bash # At commit time (all params on the send command): flashrev-mailer send --campaign CAMPAIGN_ID --live --yes --mailbox ADDR_ID \ --ai-auto-reply --ai-auto-reply-prompt "You are a friendly SDR..." # Or after commit (REQUIRES TWO-STEP FLOW — see below): # Step 1: probe — show the prompt to the user (CLI prints to stderr, then errors out) flashrev-mailer ai-auto-reply enable --campaign CAMPAIGN_ID --prompt "..." # → CLI prints the full prompt to stderr + a warning + errors out # Step 2: confirm — user has reviewed and approved; agent adds --yes flashrev-mailer ai-auto-reply enable --campaign CAMPAIGN_ID --prompt "..." --yes flashrev-mailer ai-auto-reply show --campaign CAMPAIGN_ID flashrev-mailer ai-auto-reply disable --campaign CAMPAIGN_ID --yes # prompt preserved; --yes required flashrev-mailer ai-auto-reply enable --campaign CAMPAIGN_ID --yes # reuses prompt; --yes still required ``` **Mandatory two-step flow for `enable`** (CLI-enforced, not just a guideline): 1. **Probe step**: agent runs `enable ... --prompt "..."` **without `--yes`**. CLI will print the full prompt + warning to stderr and error out. 2. **Show stderr output to the user verbatim**, get explicit "yes I approve this prompt". 3. **Confirm step**: agent re-runs `enable ... --prompt "..." --yes`. **Hard agent rules:** - Never invoke `ai-auto-reply enable ... --yes` in a single call without first showing the user the probe-step output. The CLI's two-step flow exists so this cannot happen silently. - Never paraphrase or auto-generate prompts. The user supplies the exact text. - If the user asks "make a prompt for me", help them draft it conversationally, but only call `enable` after they confirm the final text. - `disable` also requires `--yes` (changes production sequence behavior). ## Workflow D — Triage replies, reply directly After `send --live`, replies will arrive over hours/days. The agent helps the user triage: ```bash flashrev-mailer inbox --campaign CAMPAIGN_ID # see all replies flashrev-mailer inbox --campaign CAMPAIGN_ID --sentiment positive # only positives flashrev-mailer inbox --campaign CAMPAIGN_ID --intent Interested # To reply to a specific message: flashrev-mailer reply --mail-id <MAIL_ID> --body "Thanks {{...}}" # (MAIL_ID is the first column of `inbox` output) # Mark as read without replying: flashrev-mailer inbox --mark-read <MAIL_ID> # View full event timeline for a sent message: flashrev-mailer outbox --view <MAIL_ID> ``` **`reply` rules:** - Show the user the inbox row first (from / snippet / sentiment / intent). - Show the user the proposed reply body and wait for "yes". - `reply` sends to the prospect's email (preserves thread). ## Workflow E — Operational control ```bash flashrev-mailer pause --campaign CAMPAIGN_ID # stop future steps flashrev-mailer resume --campaign CAMPAIGN_ID # resume flashrev-mailer list # all local campaigns flashrev-mailer list --remote # also pull backend sources=ai_mailer flashrev-mailer steps list --campaign CAMPAIGN_ID # view current step list flashrev-mailer steps remove --campaign CAMPAIGN_ID --step N # delete step N (effective at next send --live) flashrev-mailer reschedule --campaign CAMPAIGN_ID \ --send-start 09:00 --send-end 18:00 --weekdays mon-fri \ --timezone "America/New_York" # or numeric id: --timezone 22 # Add contacts to existing campaign (default append; --overwrite to clear entire campaign) flashrev-mailer import --campaign CAMPAIGN_ID --source new-contacts.csv flashrev-mailer send --campaign CAMPAIGN_ID --live --yes # Delete a campaign (irreversible, --yes required) flashrev-mailer delete --campaign CAMPAIGN_ID --yes # default: local + backend flashrev-mailer delete --campaign CAMPAIGN_ID --keep-local --yes # backend only flashrev-mailer delete --campaign CAMPAIGN_ID --keep-remote --yes # local only ``` `reschedule` creates a private schedule and applies it; existing schedules referenced by other sequences are not touched. `steps remove` takes effect on the next `send --live` via sequence/edit's full-replace semantics — already-sent emails are not recalled; contacts on the deleted step auto-shift to the next step. ### Scheduling timezone (default behavior) If `--timezone` is omitted, the CLI resolves in this order: 1. **Your FlashRev profile's `timezoneId`** (auto-fetched from `GET /engage/api/v1/user/setting/profile`, cached 24h at `.flashrev/profile-cache.json`) 2. **Fallback `id=22 America/New_York`** (when profile is unreachable) `--timezone` accepts two formats: - Numeric id (recommended for scripts, e.g. `--timezone 22`) — from the `id` field of `GET /engage/api/v1/timezone/list` - IANA zoneId string (recommended for humans, e.g. `--timezone "Europe/London"`) — CLI looks it up in the list and resolves to the corresponding id Invalid values (not in timezone-list) are rejected at `send --live` with candidate examples in the error message. CLI-managed fixed schedules are named by timezone slug (`cli-anytime-<tz-slug>` / `cli-default-<tz-slug>`); different timezones do not share the same schedule. ## Commands the agent runs vs. commands requiring confirmation **Read-only (agent may run freely):** - `init`, `doctor`, `doctor --check-api` - `mailboxes`, `validate` - `import` (local-only), `draft` (local-only) - `status`, `list`, `list --remote` - `outbox`, `outbox --view` - `inbox`, `inbox --view` - `ai-auto-reply show` - `export` **Requires explicit user "yes" per call:** - `send --dry-run --yes` - `send --live --yes` ← never auto-escalate - `send --live --send-now` (extra confirm: "this bypasses the working-time window") - `pause`, `resume` - `reply --mail-id <ID>` - `ai-auto-reply enable` (show prompt verbatim first) - `ai-auto-reply disable` - `reschedule` - `inbox --mark-read` - `steps remove` (mutates campaign structure) - `delete --yes` (irreversible; list contents in chat first and re-confirm) ## Notes on backend semantics worth telling the user - `sources="ai_mailer"` is auto-tagged by the CLI so `list --remote` only shows what the CLI created. The user's own web-created sequences are filtered out. - After `send --live`, the campaign appears in the FlashRev web UI sequence list. Editing it there will not break the CLI, but the CLI re-commits go through a Full-Replace edit — show user the diff first if they've modified the sequence in the web UI. - `--send-now` makes step 1 fire immediately (skips delay + working-time window + holiday calendar). Later steps follow their normal `--delay-days`. - Prospect replies stop the sequence (default `prospectRepliesEnabled=1`). If `ai-auto-reply enable` is on, the LLM will first send an auto-reply, *then* the sequence stops further outgoing steps for that contact. ## Failure recovery - `send --live` failed midway: re-run; the CLI's edit-path detects the existing `sequenceId` and merges; no duplicate emails. - Unknown error from backend (`code: 400, msg: "Unknown error !"`): the backend swallowed the real error. Check `.flashrev/campaigns/<id>/campaign.json` → `steps[]` for missing fields (see `references/api_contract.md` for the full required-field list). - `list` shows a campaign but `outbox/inbox` returns 0: the sequence was paused or deleted server-side. Run `status` to confirm. For full request/response shapes, paths, and known field traps, see [references/api_contract.md](references/api_contract.md).


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)