Skip to content
NewSee how
cli

Start, Pause, Resume Campaigns From the CLI Safely

Start pause campaigns CLI guide: the exact FirstSales commands, the launch gate, and the one verb an agent can run alone.

TL;DR: The FirstSales CLI gives you campaigns start, campaigns pause and campaigns resume, but the verbs are the easy part. Treat pause as the one lifecycle command an agent may run alone, and treat start as a human decision that needs a passing readiness check and a test email first.


#The one verb an agent can run alone

If you let an AI agent run your outbound, one rule covers most of the risk: the agent may stop sending on its own, and it may never start sending on its own.

That asymmetry is the whole design of this post. Stopping a campaign costs you a few hours of delayed sends. Starting a bad campaign costs you domain reputation, and reputation takes weeks to repair.

The numbers behind that asymmetry are published. Google's email sender guidelines tell senders to keep the spam rate reported in Postmaster Tools below 0.10% and to prevent it from ever reaching 0.30% or higher.

At 0.30%, three complaints per thousand messages put you in the danger zone. One careless launch to a bad list can do that in a single morning.

The FirstSales command line interface (CLI) is a thin, JSON-first wrapper over the public FirstSales Developer API. Its README describes its own working method in one line: "Inspect first. Mutate deliberately. Verify after every action."

This post turns that line into a runbook for the three lifecycle commands: campaigns start, campaigns pause and campaigns resume. You will see the exact commands, the reasoning behind each step, and the points where a human must stay in the loop.

Everything here uses placeholder IDs such as org_123, ws_123 and camp_123. Replace them with your own values, and never paste a real API key into a chat prompt or a script you commit.

#What the CLI gives you for the campaign lifecycle

The CLI package is @firstsales.io/cli and the binary is firstsales. You install it with npm i -g @firstsales.io/cli, and as of October 2026 the published version is 0.1.11.

The campaigns group contains the verbs you need for a full lifecycle. The table lists the ones this post uses.

CommandWhat it doesChanges state?
campaigns listLists campaigns in a workspaceNo
campaigns getReads one campaignNo
campaigns progressReads sending progressNo
campaigns analyticsReads performance numbersNo
campaigns eventsReads the campaign event streamNo
campaigns readinessRuns the readiness checkNo
campaigns prepare-launchPrepares the launch requestNo
campaigns prepare-test-emailPrepares a test sendNo
campaigns send-test-emailSends a test emailSends one email
campaigns startStarts the campaignYes
campaigns pausePauses the campaignYes
campaigns resumeResumes a paused campaignYes

The CLI does not hide anything behind magic. Each command maps to one public /api/v1 endpoint, and the API enforces authentication, scopes, tenant isolation and rate limits.

If you want the authoritative list for your installed version, run firstsales commands --json. The package skill describes that local command registry as the authority, so trust it over any blog post, including this one.

The CLI reference on the developer site documents the same surface.

#Set up the CLI with a key that can only do this job

Install the package and check the binary.

bash
npm i -g @firstsales.io/cli firstsales --help

Authentication uses a Developer API key. The CLI resolves the key in this order: the --api-key flag, then the FIRSTSALES_API_KEY environment variable, then a saved local profile.

Prefer the environment variable for agents and CI, because it keeps the key out of command history and out of the prompt.

bash
export FIRSTSALES_API_KEY="<YOUR_FIRSTSALES_API_KEY>" firstsales whoami --json firstsales doctor --pretty

whoami is the equivalent of npm whoami. It validates the key and prints the user, key, organization and workspace context without exposing the raw or hashed key.

doctor checks your setup. Run both before anything else, because a wrong workspace is the cheapest mistake to catch and the most expensive one to discover after a send.

#Create a key with the least access

An agent that only needs to pause campaigns should not carry a key that can delete contacts. The api-keys create command takes a scopes list and an access level.

bash
firstsales api-keys create \ --org org_123 \ --data '{"name":"Campaign operator","scopes":["campaigns:read"],"accessLevel":"workspace"}' \ --json

The example above shows the shape from the README, with a scope list trimmed to read access. Which scope your pause and start calls need is defined by the OpenAPI contract at developer.firstsales.io, so check it before you create the key.

The raw key appears once, at creation. It is never retrievable later, so store it in a secret manager and not in a note or a chat window.

Do not pass --idempotency-key to api-keys create. The README states that raw keys are reveal-once, which is why that flag is unsupported for this command.

If you want a deeper look at scoping keys for agents, the post on API keys for outbound agents covers it in full.

#Read the state before you touch it

Every safe lifecycle change starts with a read. The recommended agent loop in the CLI README is seven steps long, and the first two are "verify identity" and "read the resource state."

Start with the list.

bash
firstsales campaigns list --org org_123 --workspace ws_123 --json

Pick the campaign you mean, then read it.

bash
firstsales campaigns get --org org_123 --workspace ws_123 --campaign camp_123 --json firstsales campaigns progress --org org_123 --workspace ws_123 --campaign camp_123 --json

The output is compact JSON by default. Add --pretty when a human reads it, and --output table when you want a quick scan in a terminal.

Save the get output to a file before you change anything. It becomes your "before" snapshot, and it makes the post-change comparison a diff instead of a memory exercise.

bash
firstsales campaigns get --org org_123 --workspace ws_123 --campaign camp_123 --json > before.json

#Why the read matters more with an agent

A person who types campaigns pause knows which campaign they mean. An agent working from a long conversation can easily mix up two campaigns with similar names.

Reading first, and printing the campaign name back to the human, removes that class of error. The cost is one extra command.

The same habit protects you from acting on stale information. A campaign you paused yesterday may have been resumed by a teammate this morning.

#The launch gate before start

Two safety lines are non-negotiable in this runbook. Run campaigns readiness before campaigns start, and run campaigns send-test-email before campaigns start.

They are not formalities. Readiness catches configuration gaps that would otherwise surface as bounces, and the test email shows you what a real prospect will see.

bash
firstsales campaigns readiness --org org_123 --workspace ws_123 --campaign camp_123 --json

Read the whole response. A passing check is the only green light, and a warning you do not understand is a reason to stop and ask.

The companion posts go deeper on each gate. The pre-launch checklist from the terminal walks through a full preflight script, and sending a test email from Claude Code first covers the test send.

#Prepare the launch request

The CLI also exposes campaigns prepare-launch, which prepares the launch request that start consumes.

bash
firstsales campaigns prepare-launch --org org_123 --workspace ws_123 --campaign camp_123 --json

I cannot show you its response here, because the exact fields come from the live API and from your campaign. Run it against a campaign you own, read the output, and keep it next to the readiness output.

The reason this step exists is visible in the start body, which you will meet next. The start request must carry savedVersionId and readinessVersion, which means the launch is bound to a specific saved version of the campaign and a specific readiness result.

That binding is a good design. If someone edits the sequence after you ran readiness, a launch built from the old evidence should not go through.

#The test email

bash
firstsales campaigns prepare-test-email --org org_123 --workspace ws_123 --campaign camp_123 --json firstsales campaigns send-test-email --org org_123 --workspace ws_123 --campaign camp_123 --data-file test-email.json --json

The recipient and any other fields go in the request body. The CLI accepts a body with --data or --data-file, and the field names come from the OpenAPI contract, so check them there.

Run --dry-run first if you are unsure. Per the README, --dry-run prints the method, URL, redacted headers and body without sending the request, and it does not use auth or the network.

Open the received message in a real inbox. Check the sender name, the subject, the first line, the links and the unsubscribe mechanism.

#Start a campaign with campaigns start

Now the command everything else exists to protect.

bash
firstsales campaigns start \ --org org_123 \ --workspace ws_123 \ --campaign camp_123 \ --data-file launch.json \ --json

The README is explicit about the body. campaigns start requires a JSON body containing savedVersionId, readinessVersion, idempotencyKey and confirmation.

Without a body, the CLI returns a missing_required_body error. The troubleshooting table says to pass --data or --data-file with the required campaign launch evidence.

#Preview with a dry run

Use --dry-run with the same body before you execute. The README describes it as a way to inspect the tenant-bound request before execution.

bash
firstsales campaigns start \ --org org_123 \ --workspace ws_123 \ --campaign camp_123 \ --data-file launch.json \ --dry-run

Read the printed URL and body out loud, or paste them to the human who owns the launch decision. Check three things: the org and workspace in the URL, the campaign ID, and the version fields in the body.

#What goes in the four fields

I will be careful here, because the README names the fields and does not define their values. The names tell a clear story, though.

The names suggest that savedVersionId points at the saved version of the campaign you reviewed, and that readinessVersion points at the readiness result you accepted.

Likewise, idempotencyKey should make the start safe to retry, and confirmation should carry the explicit statement that a human approved the launch. These readings are inferences from the names, so check them against the OpenAPI contract.

Treat the first two as outputs of the commands you just ran, and copy them from those responses instead of typing them. Treat confirmation as the field only a person fills in.

If your agent builds launch.json, make it stop and show the file to you before the real call. That single pause is the cheapest guardrail in this entire workflow.

#Idempotency in practice

The idempotencyKey field matters because networks fail. If a start request times out, you do not know whether it landed.

Re-sending the same body with the same key lets the API recognise the retry instead of double-processing it. Pick a key that names the campaign and the launch, such as start-camp_123-2026-10-08, and reuse it only for retries of that exact launch.

The CLI also has a global --idempotency-key flag for supported retryable mutations. The README says the CLI never makes a key up for you, so you supply it.

#Verify after the start

The last step of the agent loop is "re-read state and verify the result." A successful exit code means the API accepted the request. It does not tell you the campaign behaves the way you expect.

bash
firstsales campaigns get --org org_123 --workspace ws_123 --campaign camp_123 --json > after.json diff before.json after.json firstsales campaigns progress --org org_123 --workspace ws_123 --campaign camp_123 --json firstsales campaigns events --org org_123 --workspace ws_123 --campaign camp_123 --json

The diff should show a status change and nothing surprising. If other fields moved, someone else edited the campaign between your two reads.

Watch the first sends. campaigns events gives you the event stream, and campaigns progress shows how far through the audience the campaign is.

#The first hour checklist

In the first hour after a start, look for four things. Sends are leaving at the pace you configured, bounces are low, no alert has fired, and the first replies look like replies to the message you intended.

The workspace alerts are one command away.

bash
firstsales alerts list --org org_123 --workspace ws_123 --json

The alerts group also has ack and resolve. Acknowledging an alert tells your team someone saw it, and resolving it should wait until the cause is fixed.

For benchmarks on what a healthy first hour looks like, the cold email bounce rate post lists the thresholds worth acting on.

#Pause a campaign the right way

Pausing is the verb you want to be fast, boring and reversible. The command is short.

bash
firstsales campaigns pause --org org_123 --workspace ws_123 --campaign camp_123 --json

The README includes this as an agent example, and the pattern around it matters. Read the campaign, pause it, then read it again.

bash
firstsales campaigns get --org org_123 --workspace ws_123 --campaign camp_123 --json firstsales campaigns pause --org org_123 --workspace ws_123 --campaign camp_123 --json firstsales campaigns get --org org_123 --workspace ws_123 --campaign camp_123 --json

The second get proves the pause took effect. Without it, you are trusting an exit code.

#When to pause

Write the pause conditions down before the campaign starts, so nobody argues about them while the numbers are bad. Four triggers cover most incidents.

The first is a spam complaint rate approaching Google's limits. Google says to keep the rate below 0.10% and never let it reach 0.30%, and Postmaster Tools is where you read it.

The second is a bounce spike. A sudden jump usually means a bad list segment or a broken domain, and the deliverability incident response post describes the triage.

The third is an authentication failure. Outlook began rejecting non-compliant mail from high-volume senders on May 5, 2025, with the error "550; 5.7.515 Access denied, sending domain does not meet the required authentication level," according to the Microsoft Defender for Office 365 blog.

The fourth is a content error you discover after launch, such as a wrong merge field, a dead link or a claim you cannot support. A paused campaign costs hours. A wrong claim sent to a thousand people costs trust.

Opt-outs have a clock. The FTC's CAN-SPAM compliance guide says you must honor an opt-out request within 10 business days, that the law makes no exception for business-to-business email, and that each separate violating email can carry penalties of up to $53,088.

If you find that opt-outs are not being honored, pause first and investigate second. Our post on cold email compliance penalties covers the fines in more detail.

#Why the agent may pause alone

A pause is reversible, narrow and safe. It cannot send an email, delete data or spend money.

That makes it the right verb to delegate. Give the agent a clear rule, such as "pause any campaign whose bounce rate crosses the threshold in this file," and let it act.

What the agent must do after it pauses is tell you. A silent pause is a hidden delay, and a hidden delay is a missed meeting.

#Resume without repeating the original mistake

Resuming looks like the mirror image of pausing, and it is not. A pause stops the cause from growing. A resume restarts the cause unless you removed it.

bash
firstsales campaigns resume --org org_123 --workspace ws_123 --campaign camp_123 --json

Before you run it, answer one question in writing: what changed since the pause? If the answer is "nothing," do not resume.

#A resume checklist

Re-run campaigns readiness and read it again, because the state of your domains, mailboxes or lists may have shifted while the campaign sat idle.

Check the cause. If the pause came from a bounce spike, confirm the bad segment is gone. If it came from authentication, run firstsales email-auth status and confirm the records pass.

Send a fresh test email if you edited any copy. Then resume, and watch the first hour exactly as you did after the original start.

#Resume in smaller steps

If the cause was a deliverability problem, restarting at full volume invites a repeat. Many teams resume with fewer sending accounts or a lower daily cap, then step back up over several days.

The email warm-up statistics and emails per inbox per day posts cover sensible ramps.

#Exit codes and scripts

The CLI is designed for scripts. It returns a stable exit code on every call, and it prints JSON errors on non-zero exits.

CodeMeaning
0ok
1runtime error
2usage error
3auth error
4not found
5rate limited

A pause wrapper script can act on those codes. This one retries once on a rate limit and stops loudly on everything else.

bash
#!/usr/bin/env bash set -u ORG="org_123"; WS="ws_123"; CAMP="${1:?campaign id required}" pause_once() { firstsales campaigns pause --org "$ORG" --workspace "$WS" --campaign "$CAMP" --json } pause_once code=$? if [ "$code" -eq 5 ]; then sleep 30 pause_once code=$? fi if [ "$code" -ne 0 ]; then echo "pause failed with exit code $code for $CAMP" >&2 exit "$code" fi firstsales campaigns get --org "$ORG" --workspace "$WS" --campaign "$CAMP" --json > "after-pause-$CAMP.json" echo "paused $CAMP, state saved to after-pause-$CAMP.json"

The script uses only commands and exit codes from the README. It does not parse any field of the JSON, because I will not guess response shapes.

The exit codes changed in version 0.1.1, according to the README, so scripts written for the 0.1.0 scheme need a migration.

#Keep start out of cron

Do not schedule campaigns start. A scheduled start removes the human decision this whole post defends.

Scheduled reads are fine. A cron job that runs campaigns progress and alerts list, then pings you, is a good use of the CLI. The post on weekly campaign reports with cron and the CLI shows the pattern.

#Let Claude Code drive, with rails

Claude Code can run shell commands, so it can run the CLI directly. The package even ships three Claude Code skills: firstsales-cli, firstsales-product-mcp and firstsales-developer-api.

The firstsales-cli skill lists the operating rules the agent should follow. Verify identity first, read the current state before mutation, prefer --json, use --dry-run before risky writes, and use --confirm only for intentional destructive commands.

Those rules fit this runbook well. They are also a reminder that --confirm belongs to deletes, not to starts.

#Rails worth adding

First, restrict the permissions in your agent's settings so that campaigns start always prompts a human, while read commands and campaigns pause run freely. Most coding agents let you allow or deny shell commands by pattern.

Second, tell the agent the rule in plain words in your project instructions: "You may pause. You may never start. When a start is needed, build launch.json, run the dry run, show me both, and wait."

Third, make the agent print the campaign name and ID before every mutation. If the name looks wrong to you, you catch the mistake before the call.

The Model Context Protocol specification supports the same instinct. Its section on tools says that for trust and safety, "there SHOULD always be a human in the loop with the ability to deny tool invocations," as written in the MCP tools specification.

For a broader treatment of guardrails, read guardrails for email-sending agents.

#A sample prompt

You can give Claude Code a prompt like this. It names the allowed commands and the stop points.

You are operating FirstSales for workspace ws_123 in org org_123. Allowed without asking: whoami, campaigns list/get/progress/analytics/events, alerts list, campaigns pause. Never run without my explicit yes: campaigns start, campaigns resume, any delete, any api-keys command. Before any change, print the campaign name and ID, and save the current state to a file. After any change, read the state again and tell me what differed. Use the FIRSTSALES_API_KEY environment variable. Never print it.

The prompt contains no secret. The key lives in the environment, and the agent never sees its value.

#Where MCP fits

The FirstSales Product MCP server is a separate surface from the CLI. It is firstsales-product, a streamable HTTP server at https://api.app.firstsales.io/mcp, and it uses OAuth rather than a Developer API key.

The tool list depends on your tenant and your grant, so the live tools/list call is the authority. Check it for your account before you plan around any specific tool.

In a connected session, we confirmed tools for listing and reading campaigns, reading campaign analytics, progress and events, and reading or updating a campaign workflow. Creating a campaign through MCP is not confirmed, and this post does not rely on it.

That split gives you a clean division of labor. Use the CLI to create, start, pause and resume. Use MCP when you want an MCP-aware assistant to watch a campaign and talk about it.

The package skill says it directly: if a desired action is mutating, destructive, billing-related or approval-backed, switch to the CLI or Developer API instead of assuming MCP support. Developer API keys are not accepted by Product MCP, and MCP tokens are not Developer API credentials.

If you are new to the protocol, what MCP is for sales teams explains it without jargon, and which MCP tools an agent should get helps you decide what to expose.

#Who may run which verb

The table below sums up the permission model this post argues for. It uses a check mark for "allowed alone" and a cross for "needs a human."

ActionAgent aloneHuman approvalWhy
whoami, doctor✓Not neededRead-only identity checks
campaigns list, get, progress, analytics, events✓Not neededRead-only state
alerts list✓Not neededRead-only signal
campaigns readiness✓Not neededCheck only, informs the human
campaigns send-test-email✗RequiredSends a real email, even if only one
campaigns start✗RequiredStarts real outbound to real people
campaigns pause✓Notify afterReversible and narrow
campaigns resume✗RequiredRestarts the cause unless fixed
contacts delete✗RequiredCannot be undone
api-keys create, revoke✗RequiredChanges who can act

The only cell that looks asymmetric is pause, and that is deliberate. It is the single mutation where speed beats ceremony.

#Same idea as a state diagram

The diagram below is a mental model, not the API's status names. It shows which transitions need a person.

stateDiagram-v2
    [*] --> Drafted
    Drafted --> Ready: readiness passes and test email reviewed
    Ready --> Running: start (human approves)
    Running --> Paused: pause (agent or human)
    Paused --> Running: resume (human approves)
    Paused --> Drafted: edit copy or lists
    Running --> [*]: campaign finishes

Notice that every arrow into Running carries a human approval, and the only arrow out of it that anyone can take alone is pause.

#Common mistakes

Most lifecycle incidents come from a short list of habits. Each one is easy to avoid once you can name it.

#Starting without reading the dry run

The dry run exists so the request can be read before it matters. Skipping it turns a reviewable action into a leap.

#Trusting the exit code as proof

Exit code 0 means the API accepted the call. Only a second get shows the resulting state.

#Pausing without telling anyone

A silent pause is a hidden delay. Message the campaign owner the moment you pause.

#Resuming at full volume after a deliverability incident

The cause may still be present. Resume smaller, then ramp up.

#Putting the API key in the prompt

A key in a prompt lands in logs, transcripts and sometimes screenshots. Keep it in the environment and rotate it through api-keys if it ever leaks.

#Reusing one idempotency key for different launches

An idempotency key identifies one operation. Reuse across different launches can make the API treat your second launch as a retry of the first.

#Scheduling the start

Cron is excellent for reports and terrible for launches. Keep the human on the start.

For the wider pattern of automation going wrong, see unsupervised AI outbound, and for the cultural side, human-in-the-loop cold email.

#A full session, end to end

Here is the whole flow in one place, using the commands from this post. It assumes the campaign was already created with firstsales campaigns create.

bash
# 1. Identity and setup firstsales whoami --json firstsales doctor --pretty # 2. Read state and keep a snapshot firstsales campaigns get --org org_123 --workspace ws_123 --campaign camp_123 --json > before.json # 3. The gate firstsales campaigns readiness --org org_123 --workspace ws_123 --campaign camp_123 --json firstsales campaigns prepare-launch --org org_123 --workspace ws_123 --campaign camp_123 --json firstsales campaigns send-test-email --org org_123 --workspace ws_123 --campaign camp_123 --data-file test-email.json --json # 4. Human reviews the received email and the readiness output # 5. Dry run, then start firstsales campaigns start --org org_123 --workspace ws_123 --campaign camp_123 --data-file launch.json --dry-run firstsales campaigns start --org org_123 --workspace ws_123 --campaign camp_123 --data-file launch.json --json # 6. Verify firstsales campaigns get --org org_123 --workspace ws_123 --campaign camp_123 --json > after.json diff before.json after.json firstsales campaigns progress --org org_123 --workspace ws_123 --campaign camp_123 --json firstsales alerts list --org org_123 --workspace ws_123 --json

The sequence has two human checkpoints, the review before step 5 and the check after step 6. Everything else can be scripted.

If your campaign does not exist yet, the companion post on building a campaign from Claude Code shows the creation side.

#What to monitor in the first day

A start is not finished when the command returns. The first day tells you whether the gate did its job.

Check progress every hour for the first few hours. A stalled campaign points at a configuration issue, while a campaign that runs too fast points at a pacing setting you did not intend.

Check replies as they arrive. The first replies tell you whether the message landed as written, and the reply handling playbook helps you sort them.

Compare your early numbers against realistic benchmarks. The cold email benchmarks post gives you a reference so a good day does not feel bad and a bad day does not feel normal.

If anything looks off, you already know the move: pause, read, fix, test, resume smaller.

#FAQs

#What does firstsales campaigns start need in its body?

The README says the body must contain savedVersionId, readinessVersion, idempotencyKey and confirmation. Pass it with --data or --data-file.

#Can I preview a start without sending it?

Yes. Add --dry-run with the same body, and the CLI prints the method, URL, redacted headers and body without sending the request.

#Which command stops a running campaign?

firstsales campaigns pause --org <org> --workspace <workspace> --campaign <campaign>
. Read the campaign before and after to confirm the state changed.

#Does pausing delete anything?

No. Pausing stops further sends and leaves the campaign, its contacts and its history in place, and campaigns resume restarts it.

#Should an AI agent be allowed to start campaigns?

Not on its own. A start sends real email to real people, so a human should approve it after readiness and a test email pass.

#Should an AI agent be allowed to pause campaigns?

Yes, with a written rule and a notification. Pause is reversible and narrow, which makes it the safest verb to delegate.

#How do I know the pause worked?

Run campaigns get after the pause and compare it with the snapshot you saved earlier. The exit code alone does not prove the state.

#What should I check before I resume?

Re-run campaigns readiness, confirm the cause of the pause is fixed, and send a fresh test email if you changed copy. Then resume at a lower volume.

#What is the idempotency key for?

It makes a retry safe. If a request times out, sending the same body with the same key lets the API recognise the retry.

#Does the CLI generate idempotency keys for me?

No. The README states the CLI never makes a key up, so you supply one that names the operation.

#What do the CLI exit codes mean?

0 is ok, 1 a runtime error, 2 a usage error, 3 an auth error, 4 not found and 5 rate limited. Scripts can branch on them.

#Can MCP start or pause a campaign?

Do not plan on it. The Product MCP tool list is per tenant and per grant, and creating campaigns through MCP is not confirmed, so use the CLI for lifecycle changes and check tools/list for your account.

#Where do I create the campaign first?

Use firstsales campaigns create, then read it with campaigns get. MCP can edit and watch the campaign afterwards.

#Do I need an API key to run these commands?

Yes. The CLI authenticates with a Developer API key, resolved from --api-key, then FIRSTSALES_API_KEY, then a saved profile.

#How should I store the API key?

In an environment variable or a secret manager. Never paste it into a prompt, a post or a committed file.

#What happens if I run a command against the wrong workspace?

The change lands in the wrong place. Run whoami first and print the workspace name back to a human before any mutation.

#Which spam rate should trigger a pause?

Google recommends keeping the rate below 0.10% and never letting it reach 0.30% or higher. Set your pause threshold well under 0.30%.

#Is the opt-out deadline different for B2B email?

No. The FTC guide says CAN-SPAM makes no exception for business-to-business email, and opt-outs must be honored within 10 business days.

#Can I schedule campaign starts with cron?

You can, but you should not. A scheduled start removes the human decision, so schedule reports and checks instead.

#Where can I see every command my installed version supports?

Run firstsales commands --json. The package skill calls that local registry the authority for what exists.


#Conclusion

Starting, pausing and resuming a campaign are three small commands wrapped in a lot of judgment. The CLI makes the commands boring on purpose, which leaves your attention free for the judgment.

Here are the takeaways to carry out of this post.

  • Run campaigns readiness and campaigns send-test-email before every campaigns start, and read the results yourself.
  • Build the start body from the evidence you just collected, preview it with --dry-run, and keep a human on the final call.
  • Let an agent run campaigns pause alone, and require a notification every time it does.
  • Treat resume as a new launch decision: name what changed, retest, and restart smaller.
  • Verify every change with a second read, because an exit code is not a state.
  • Keep API keys out of prompts, and give each agent a key with the least access it needs.
  • Delete commands cannot be undone, so keep --confirm for deliberate cleanup and never for launches.

If you want to try this on your own account, install the CLI with npm i -g @firstsales.io/cli, run firstsales whoami --json, and read one campaign before you change anything. Then read the next post in this series, the pre-launch checklist from the terminal, and turn the gate into a script.

Keep reading

Outreach that keeps going after you log off.Leads, emails, follow-ups and replies, handled.

Connect unlimited mailboxes and let AI agents find leads, write each email and follow up. Or send your own templates in manual mode. From $29/mo.

$1 for 3 days · Cancel anytime · No contract

Install with AI

Your coding agent sets up the FirstSales CLI and skills for you.

Prompt for Claude Code

I'm setting up FirstSales (https://firstsales.io), an AI outbound sales platform: campaigns, contacts, a reply inbox, deliverability and lead signals. Help me get my workspace sending real, good emails end to end, from Claude Code.

Read these first so you know how FirstSales works:
- Developer docs and API: https://developer.firstsales.io
- CLI: https://www.npmjs.com/package/@firstsales.io/cli
- Skills for coding agents: https://github.com/firstsalesio/firstsales-skills (read README.md and GUIDE.md)

Setup
1. Install the CLI: npm install -g @firstsales.io/cli
2. Install the FirstSales skills:
   claude plugin marketplace add firstsalesio/firstsales-skills
   claude plugin install firstsales@firstsales
3. Ask me if I already have a FirstSales account. If not, send me to https://app.firstsales.io to sign up.
4. Ask me to create an API key under Settings → API → API Keys, with only the scopes we need.
   Never ask me to paste the key into this chat. Tell me to run this in my own terminal, so the key stays hidden:
   read -rs FS_KEY && firstsales auth login --api-key "$FS_KEY"; unset FS_KEY
5. Run `firstsales whoami` and tell me which workspace and organisation we are connected to.

Then ask me about my business: what I sell, who I sell to (roles, company size, countries), my best customers, and what I use today for outbound (tools, lists, mailboxes). Walk me through:
1. Teaching FirstSales my business: website, offer, proof points and case studies in the knowledge base, so the AI writes like someone who knows the product.
2. Connecting my sending mailboxes and checking my sending domain: SPF, DKIM, DMARC and warm-up. If anything is unhealthy, tell me the exact DNS record to add.
3. Bringing in contacts: import my CSV or CRM export into a clean list, remove duplicates and bad emails, and block domains I must never email (customers, competitors, my own).
4. Building my first campaign: the right list, senders, steps and timing. Write the sequence with me, show me the drafts, and do not launch until I say yes.
5. Setting up Signals if it fits: find people engaging with LinkedIn posts about topics my buyers care about, and turn them into leads.
6. Inviting my team and giving each person the right access.

Once campaigns run, show me how to work the reply inbox from here: which replies need an answer, draft responses in my voice, and move good conversations into deals. If I use Cal.com, connect it so follow-ups can offer my booking link (not the first email).

Throughout, as you learn about my business, proactively suggest and help create:
- Reply templates for the questions and objections my prospects are likely to raise, based on my product, site and codebase if you can see them.
- Campaign ideas and segments worth testing, with the reason for each.

Safety rules:
- Show every change as a dry run (--dry-run) first and wait for my yes. Never send emails, launch campaigns or delete anything without my yes.
- Tell me before anything that spends credits, and how many.

Be opinionated. If the docs give a sensible default, just use it. Don't make me choose between five options.