Install
openclaw skills install flashrev-mailerUse 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.
openclaw skills install flashrev-mailerUse 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.
This skill creates and runs outbound email campaigns. The following constraints apply to every workflow and are non-negotiable:
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.--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:
enabledisable if there is any ambiguitypause / 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.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.Agents calling this CLI should set either:
FLASHREV_MAILER_AI_MODE=1 (recommended; once per shell)--ai-mode on every invocationWhen enabled, the CLI forces JSON output on list / outbox / inbox / status / mailboxes / ai-auto-reply show / export AND wraps errors in JSON
to stderr:
{ "error": { "code": "...", "message": "...", "remediation": "..." } }
Without ai-mode, output is human-formatted tables — fine for users at a terminal, fragile for agents to parse.
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.
PATH.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.export FLASHREV_API_KEY="...". Persist in ~/.zshrc or ~/.bashrc per
user preference. Never persist inside any config file.flashrev-mailer init --base-url "https://open-ai-api.flashlabs.ai" (or
the test/staging URL provided by your FlashRev team)..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.
Each item below is a business decision the agent must explicitly align with the user before proceeding — do not assume defaults:
{{ FirstName }} / {{ LastName }} / {{ Company }} / {{ Title }}
syntax (displayProperty names from the FlashRev contact property whitelist).--delay-days, and whether
it's a reply (same thread, no subject) or a new thread are confirmed.ai-auto-reply enable. The prompt is ≤ 2000 chars.--dry-run) and explicitly approved committing
(--live).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.
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>).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.
# 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):
enable ... --prompt "..." without --yes.
CLI will print the full prompt + warning to stderr and error out.enable ... --prompt "..." --yes.Hard agent rules:
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.enable after they confirm the final text.disable also requires --yes (changes production sequence behavior).After send --live, replies will arrive over hours/days. The agent helps the
user triage:
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:
reply sends to the prospect's email (preserves thread).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.
If --timezone is omitted, the CLI resolves in this order:
timezoneId (auto-fetched from
GET /engage/api/v1/user/setting/profile, cached 24h at
.flashrev/profile-cache.json)id=22 America/New_York (when profile is unreachable)--timezone accepts two formats:
--timezone 22) — from the id
field of GET /engage/api/v1/timezone/list--timezone "Europe/London")
— CLI looks it up in the list and resolves to the corresponding idInvalid 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.
Read-only (agent may run freely):
init, doctor, doctor --check-apimailboxes, validateimport (local-only), draft (local-only)status, list, list --remoteoutbox, outbox --viewinbox, inbox --viewai-auto-reply showexportRequires explicit user "yes" per call:
send --dry-run --yessend --live --yes ← never auto-escalatesend --live --send-now (extra confirm: "this bypasses the working-time window")pause, resumereply --mail-id <ID>ai-auto-reply enable (show prompt verbatim first)ai-auto-reply disablerescheduleinbox --mark-readsteps remove (mutates campaign structure)delete --yes (irreversible; list contents in chat first and re-confirm)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.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.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.send --live failed midway: re-run; the CLI's edit-path detects the
existing sequenceId and merges; no duplicate emails.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.