TL;DR: Treat a campaign workflow like a config file under code review. Read it, save it to git, change one thing, write it back, read it again and look at the diff. The MCP server gives Claude Code a workflow get and update path, and the CLI has the same pair, so you can chat your way to better copy without losing control.
#The angle: a workflow is a config file under review
Developers learned long ago that configuration changes cause outages. A one-line edit to a config file can take down a service, so careful teams review config changes the same way they review code.
A campaign workflow is configuration for your outbound program. It holds the steps, the gaps between them, the copy and the references to lists and senders.
A bad edit does not crash anything. It quietly sends worse email to real people.
This guide applies the config-review habit to workflow edits. You read the current state, store it, make one deliberate change, write it back, read it again and compare. Claude Code does the typing and the comparison, and you do the approving.
FirstSales is an AI SDR and cold email product. The commands in this post come from the @firstsales.io/cli README (version 0.1.11 as of October 2026), and the tool names come from the tools/list output of a connected MCP session. Nothing here was run against a live account while this was written.
One honest limit up front. This post has not verified how an edit applies to a campaign that is already sending, such as whether recipients mid-sequence see the new copy. Where that matters, the post says to pause first and test again.
The Model Context Protocol specification says tools should always keep a human able to deny an invocation, and this loop builds that denial into every write. If you have not connected the server, do that with the five-minute MCP setup. To create the campaign you are about to edit, follow building a cold email campaign from Claude Code.
#What you need first
Gather the following before you start.
- A campaign that exists. Campaigns are created with
firstsales campaigns create, not through MCP. - The
firstsales-productMCP server connected, with your tool list saved. - A tool list that includes campaign workflow get, and ideally workflow update.
- A git repository, even a tiny one, where the workflow files will live.
- The campaign identifier from
firstsales campaigns list.
Check the tool list first. The confirmed session had workflow get and workflow update, but the product skill describes the released catalog as read-only today, and the tool list is per account and per grant. Your own list is the authority.
If your list has no update tool, nothing is lost. The CLI has firstsales campaigns workflow update, and the loop below works the same way with the CLI doing the write.
Keep the safety rules in view. Never paste an API key into a prompt.
Run campaigns readiness and campaigns send-test-email before campaigns start. Treat deletes as permanent, since contacts delete and other delete commands cannot be undone.
#The loop: read, save, change, write, re-read, diff
The loop has six moves and one rule: only one change per pass.
| Move | Tool | Output you keep |
|---|---|---|
| Read | MCP workflow get, or CLI workflow get | Raw workflow JSON ✓ |
| Save | Shell and git | A committed file |
| Change | Claude Code, proposing | A stated before and after |
| Write | MCP workflow update, or CLI | A tool result, never proof alone ✗ |
| Re-read | Same tool as the first read | A second raw copy |
| Diff | git diff | The exact difference |
sequenceDiagram participant You participant CC as Claude Code participant FS as FirstSales participant Git as git You->>CC: read the workflow CC->>FS: workflow get FS-->>CC: current workflow CC->>Git: save workflow.json and commit You->>CC: change step two subject CC-->>You: proposes before and after You->>CC: approve CC->>FS: workflow update CC->>FS: workflow get FS-->>CC: new workflow CC->>Git: save and show diff Git-->>You: only step two changed
The rule matters more than it looks. When you change two things in one pass and the result is wrong, you cannot tell which change broke it. One change per pass keeps the diff readable and the cause obvious.
#Step 1: read the workflow
Start with a read-only request. Ask for raw output, and say plainly that you want no changes.
Use the firstsales-product campaign workflow get tool for campaign <campaign-id>. Print the full result exactly as returned. Then list the steps in order, with the delay before each one. Do not change anything.
Check that the campaign name and identifier in the result match what you intended. A common mistake is to edit the right workflow of the wrong campaign, and it starts with skipping this check.
The CLI offers the same read.
bashfirstsales campaigns workflow get --campaign <campaign-id> --org org_123 --workspace ws_123 --json
Replace org_123 and ws_123 with the identifiers you got from firstsales orgs list and firstsales workspaces list. The --json flag gives machine-readable output, which suits saving to a file.
The Claude Code MCP documentation explains approval prompts, and the OpenAPI document describes request bodies. The README does not publish the exact structure of the workflow object, so do not assume field names. Read the real output, and ask the model to list the field names before it describes anything.
List every top-level field name in the workflow result. Then list the field names inside one step.
You are building a map of the real schema from the real tool. The map makes every later edit more precise.
#Step 2: save it in git
Saving a copy sounds like a chore. It is the step that turns a chat into a reviewable change.
bashmkdir -p campaigns/camp_123 firstsales campaigns workflow get --campaign <campaign-id> \ --org org_123 --workspace ws_123 --json > campaigns/camp_123/workflow.json git add campaigns/camp_123/workflow.json git commit -m "chore: snapshot workflow before copy edit"
If you read the workflow through MCP, ask Claude Code to write the raw result to the same file, then check that the file is valid JSON.
bashjq . campaigns/camp_123/workflow.json > /dev/null && echo ok
Pretty-printing the file helps diffs a lot. A workflow stored on one line shows up in git diff as one giant changed line, which hides the edit.
bashjq -S . campaigns/camp_123/workflow.json > tmp.json && mv tmp.json campaigns/camp_123/workflow.json
The -S flag sorts keys, so the same data always produces the same text. Sorted keys keep diffs free of noise that has nothing to do with your edit.
Do not store secrets in the file. A workflow should hold copy and structure, and the file should never contain an API key. If you see one, stop and tell the team.
You now have a baseline. Any later difference is an edit you can name.
#Step 3: ask for one change
Now ask for the single change you want. Good requests are small and specific.
In step two, change the subject line to a lowercase question under six words. Show me the current subject, your proposed subject, and why. Do not call a write tool yet.
Notice the three parts: a precise target, a constraint and a stop. The stop is the instruction that keeps the model in the proposing role.
For wording help, point the model at real guidance. Our guides on writing cold emails and cold email subject lines are good references, and one-line cold emails shows how short a first message can be.
Ask for reasons, because reasons expose assumptions. If the model says a subject "will improve opens", question it. The post on open rates as a dead metric explains why open data is unreliable, so a promise about opens is a weak reason.
A strong proposal quotes the old text, shows the new text and names the risk. It might say that the shorter subject loses the company name, which could lower recognition for cold readers.
If the proposal is vague, send it back. "Make it punchier" is not a change you can review.
#Step 4: write the change
When the proposal is right, approve the write. The tool call is where Claude Code will ask for permission.
Read the permission prompt closely. It shows the tool name and the arguments. Compare the arguments with the proposal. The only difference between the saved workflow and the new one should be the change you approved.
This is the point where models can slip. A write tool for a workflow may expect the whole object instead of a partial patch, so a model that sends a partial object could drop fields it did not mention. The README's CLI example for the update sends only two fields, the included list ids and the sender connector ids, as one PATCH. The MCP tool's argument shape is not documented there, so read your tool's description in the tool list.
Tell the model to preserve everything it was not asked to change.
Apply only the change we agreed. Keep every other field, step, id and delay exactly as in workflow.json. After the write, tell me which tool you called and with what arguments.
If you prefer the CLI for the write, rehearse first.
bashfirstsales campaigns workflow update --campaign <campaign-id> \ --org org_123 --workspace ws_123 \ --include-list list_123 --sender sender_123 --dry-run
The README documents these two flags for the PATCH form. For edits to step copy or timing, there is also firstsales campaigns workflow-update, which the README describes as a PUT. The README does not document its body, so use --dry-run, read the OpenAPI document, and treat anything you cannot confirm as unverified.
A PUT usually replaces the whole object. That is a reason to send the full saved workflow with your one change applied, and a reason to read the dry run twice.
#Step 5: read it again and diff
Never trust a write because the tool said it succeeded. Read the workflow back and compare.
Call the campaign workflow get tool again. Write the result to workflow.after.json.
Then run the comparison yourself, or ask the model to show it.
bashdiff <(jq -S . campaigns/camp_123/workflow.json) \ <(jq -S . campaigns/camp_123/workflow.after.json)
If the only difference is the subject line you approved, you are done. Move the new file over the old one and commit it.
bashmv campaigns/camp_123/workflow.after.json campaigns/camp_123/workflow.json git commit -am "docs: step two subject is a short question"
If you find other differences, stop. Look first for server-side changes, such as a timestamp or a version field that the system updates on every save. Those are normal. Anything in the copy, the delays, the ids or the list and sender references is not, and you should investigate before you do anything else.
This diff is the most valuable minute of the whole process. It is the evidence that the system now holds what you meant.
The commit message above describes the change directly. Keep messages plain and specific, since they become your edit history.
#Edits worth making by chat
Some edits suit a conversation, and some do not. These are the good fits.
Tightening copy. Ask the model to cut a first email to five sentences or fewer, and to keep every fact. Then check the facts yourself. The post on AI email quality at scale shows why a human read matters.
Removing filler. Models and humans both write throat-clearing openers. Ask for a version without them, then compare. The warning signs are collected in AI slop in cold email.
Adjusting the gap between steps. Moving a follow-up from three days to five is a small, reversible change. The follow-up email strategy guide covers sensible spacing.
Fixing merge fields. If your test email showed a broken first-name field, a chat edit is fast. Re-run campaigns send-test-email afterward to confirm.
Aligning with the offer. If your offer changed, ask the model to list every sentence that mentions the old one. The guide to offer clarity helps you write the new one.
Adding a plain unsubscribe line. If a step lacks one, add it, and keep the wording simple and honest. The post on one-click unsubscribe explains the technical side.
Weaker fits include rewriting every step at once, changing the audience, and anything that needs new facts. If the model does not have a fact, it will invent one, so supply the facts or leave the change out.
#The CLI route for the same edit
If your MCP tool list has no update tool, or you want a script, use the CLI for both halves.
bash# read and save firstsales campaigns workflow get --campaign <campaign-id> --org org_123 --workspace ws_123 --json \ | jq -S . > campaigns/camp_123/workflow.json # rehearse the write firstsales campaigns workflow update --campaign <campaign-id> --org org_123 --workspace ws_123 \ --include-list list_123 --sender sender_123 --dry-run # after the real write, read back and compare firstsales campaigns workflow get --campaign <campaign-id> --org org_123 --workspace ws_123 --json \ | jq -S . > campaigns/camp_123/workflow.after.json diff campaigns/camp_123/workflow.json campaigns/camp_123/workflow.after.json
The CLI skill gives a rule worth following: prefer the narrowest command, and use firstsales commands --json as the authority on what exists. The firstsales api <METHOD> <path> escape hatch exists, but use it only for a documented public route.
The CLI also gives you exit codes, which scripts can check. Zero is success, 1 a runtime error, 2 a usage error, 3 an auth error, 4 not found and 5 rate limited.
Add --idempotency-key to a mutating call so a retry does not repeat the change. This is the same safety trick used in the terminal import guide.
#What not to change on a campaign that is running
Editing a draft is low risk. Editing a campaign that is sending is a different matter, because people may be partway through the sequence.
As noted at the start, this post has not verified how live edits apply. So take the conservative path.
- Pause the campaign with
firstsales campaigns pause. - Make the edit with the loop above.
- Run
campaigns readinessandcampaigns send-test-emailagain. - Resume with
firstsales campaigns resumeonly after you read the test.
The post on starting and pausing campaigns from the CLI covers those commands, and the pre-launch checklist covers the readiness step.
Some changes deserve extra caution even on a draft.
List and sender references. Changing which list or sender a campaign uses changes who receives mail and from where. The README's PATCH example edits exactly these, so rehearse it.
Delays. Shortening a delay can send a follow-up sooner than the recipient expects.
Step order. Reordering steps changes the story a recipient reads.
Removing a step. If someone has already received step one, removing step two changes what the sequence means for them.
When in doubt, copy the workflow into a new draft campaign and test the edit there. A scratch campaign costs a few commands and saves a lot of worry.
#Copy standards to give the model
A model edits to the standard you set. Give it one in writing, and keep it in CLAUDE.md so every session starts with it.
Cold email copy standard - One idea per email. One ask per email. - No claim without a source I can name. Never invent a statistic, a customer or a quote. - No fake urgency, no fake replies, no misleading subject lines. - Plain unsubscribe line in every step. - Short sentences. No filler openers. - Do not mention competitors by name.
The standard includes legal basics. The Federal Trade Commission's CAN-SPAM compliance guide lists the points: no false or misleading header information, no deceptive subject lines, a clear opt-out and your postal address. It also says the opt-out must work and be honored promptly.
The standard also protects deliverability. As of October 2026, Google's email sender guidelines ask senders to keep spam rates reported in Postmaster Tools below 0.10% and never reach 0.30%. Copy that annoys people is a direct route to those numbers.
Models drift in long sessions, so restate the standard when you start a copy edit. The post on AI SDR prompt drift explains why instructions fade over time.
For quality at volume, the idea of a fixed test set matters. The guide to building an outbound AI eval set describes how to check changes against known cases instead of relying on a feeling.
#Review habits that catch problems
Good reviewers share habits. Borrow them.
Read the diff before the prose. The diff tells you what changed. The prose tells you what the author hoped changed.
Check numbers against sources. If a step says "teams like yours save two hours a week", ask where the number comes from. If no source exists, remove the claim.
Read the email aloud. Awkward phrasing shows up quickly when you hear it.
Look for personalization that is not personal. A first line that could apply to anyone is filler. The guide to cold email personalization at scale explains the difference.
Send the test to yourself. campaigns send-test-email shows the real render. Run it after every copy edit.
Ask a second person. A teammate who did not write the copy sees what you stopped seeing.
The review loop also benefits from a human at the center. The post on human-in-the-loop cold email argues that automation earns more freedom only after a person watches it work. For the technical guardrails, see guardrails for email-sending agents.
#A worked edit with invented details
The scenario below is hypothetical. The company, the campaign and the copy are made up to show the loop, and no real account was involved.
A team writes to independent accounting firms about a tool that chases late client documents. Their campaign has three steps: an intro, a follow-up and a short close.
The reply rate on the first week is low, and a teammate suspects the intro is too long. The team reads the workflow, sees that step one has nine sentences, and saves the file to git.
They ask Claude Code for one change: cut step one to five sentences without losing the offer or the ask. The model proposes a version and lists what it removed, which includes a sentence about "years of experience" that has no source.
The team approves the cut, and they also approve dropping the unsourced sentence. The model applies the change, re-reads the workflow and shows the diff. Step one is shorter, and everything else matches.
They run campaigns send-test-email. The test arrives, and the merge field for the firm name renders correctly, but the sign-off lost a line break. The team goes back to the loop with a second one-line change.
Two small passes took less time than one big rewrite would have, and each pass left a commit. A week later, when someone asks why step one changed, the git log answers.
The story has no clever prompt in it. What made it work was the saved baseline, the single change and the read-back.
#Prompts to keep in a notes file
These prompts fit the loop. Paste them as needed.
Read. "Call the campaign workflow get tool for campaign X. Print the raw result. Do not change anything."
Map. "List the top-level field names and the field names inside one step."
Propose. "Propose one change to step N. Show the before and after text and the risk. Do not call a write tool."
Write. "Apply only that change. Keep every other field, id and delay as in workflow.json. Report the tool and arguments."
Verify. "Call workflow get again and save the result as workflow.after.json. Show the diff."
Challenge. "Which sentence in this email makes a claim I have not given you a source for?"
Simplify. "Rewrite step N in plain words at a grade-eight reading level. Keep every fact. Mark anything you changed."
Audit. "Check each step against the copy standard in CLAUDE.md and list violations with quotes."
Each prompt names a boundary. The best prompts leave little room for the model to do more than you asked.
#Working with a team
If more than one person edits workflows, treat the repository like any shared codebase.
Use branches for larger copy changes and open a pull request that shows the diff. A reviewer reads the change the same way they read code, and the discussion lives next to the diff.
Name one owner per campaign. Two people editing the same workflow at once will overwrite each other, because the system holds one current version. Agree on who edits and when.
Record the reason for every change in the commit message. A good message reads like "shorten step one to five sentences and remove an unsourced claim". A poor one reads like "updates".
Restrict who can write. The post on MCP tool permissions for sales explains how to give each role only the tools it needs, and the guide to API keys for outbound agents covers the CLI side of least access.
Finally, schedule a weekly look at the history. Patterns appear quickly: which steps change most, which edits were reverted and which claims keep getting removed. Those patterns tell you where the copy standard needs work.
#Failure stories to plan for
Four failure modes come up in any config-edit process. Each has a cheap defense.
The silent drop. A write replaces the object, and a field the model forgot disappears. The defense is the re-read and the diff. If a delay, a sender reference or a step vanished, the diff shows it.
The stale baseline. You saved the workflow on Monday, someone edited it on Tuesday, and you write on Wednesday from Monday's copy. The defense is to read again right before you edit, and to agree on one editor per campaign.
The wrong target. You edit campaign A while believing it was campaign B. The defense is to read the name and identifier at the top of every result and to quote them in the prompt.
The confident invention. The model adds a plausible statistic to make the copy stronger. The defense is the copy standard, a source rule, and a human who deletes any number without a name behind it.
Planning for these costs minutes. Cleaning up after one costs a day and, with email, some reputation.
#Measuring whether an edit helped
An edit is a hypothesis. To judge it, you need a comparison, and a comparison needs enough data to mean something.
Wait until the new version has gone to a reasonable number of recipients. Small samples swing wildly, so a jump from two replies to four says little. The next guide in this series, asking Claude how your campaign is doing, shows how to ask for denominators and how to treat tiny samples.
Change one thing at a time here too. If you change the subject, the body and the delay in one pass, you will not know which one moved the numbers.
Use replies as the main signal. The guide to cold email reply rate benchmarks gives reference ranges with sources, and bot clicks and fake engagement explains why click data needs suspicion.
Record each hypothesis in the commit message or a notes file: what you changed, what you expected and what you saw. After ten edits you will own a small, honest history of what works for your audience.
#Keeping the repository tidy
A few conventions keep the workflow folder useful over months.
Store one folder per campaign, named by its identifier, with a short README.md that holds the campaign name, the owner and the launch date. The workflow file sits beside it.
Keep before and after files out of the main branch. Use the after file for the diff, then replace the baseline and delete the temporary copy. A clean history shows one file changing over time.
Add a .gitignore entry for anything that might hold a secret, such as .env. The CLI reads FIRSTSALES_API_KEY from the environment, so no file in this folder needs a key.
Tag a commit when a campaign launches. If results change later, the tag tells you exactly which copy was live on launch day.
#Rolling back
Sometimes an edit is wrong. Git makes recovery quick, as long as you saved the baseline.
First, pause the campaign if it is running. Then look at the last good file.
bashgit log --oneline -- campaigns/camp_123/workflow.json git show <commit>:campaigns/camp_123/workflow.json > restore.json
Compare restore.json with the current server state. Then write the old values back using the same loop: a dry run, a write, a re-read and a diff. A rollback is just another edit, so it gets the same review.
If the write path only supports the include-list and sender fields, as in the README's PATCH example, a copy rollback may need the PUT route or the web app. Check the command reference before you plan on one path.
After a rollback, record the cause in a short note. A team that writes down why an edit failed makes fewer of them.
#FAQs
#Can Claude Code edit a FirstSales campaign workflow?
Yes, if your tool list includes campaign workflow get and update, or by using the CLI's workflow get and workflow update. Check your own list, since it varies by account and grant.
#Can Claude create the campaign too?
Not through MCP. Create the campaign with firstsales campaigns create, then edit and watch it with MCP.
#Why save the workflow to git?
It gives you a baseline and a diff. Any later change is visible and can be restored.
#What does workflow update send in the CLI?
The README example sends a single PATCH with only includeListIds and senderConnectorIds.
#What is workflow-update?
The README lists it as a PUT. Its body is not documented there, so check the OpenAPI document and use --dry-run.
#How do I avoid dropping fields on a write?
Tell the model to keep every field it was not asked to change, then re-read and diff. If the diff shows extra changes, investigate.
#Should I change several things at once?
No. One change per pass keeps the diff readable and the cause obvious.
#Can I edit a campaign that is running?
This post has not verified how live edits apply. The safe route is to pause, edit, test and resume.
#What should I run after a copy edit?
Run firstsales campaigns send-test-email to see the real render, and campaigns readiness before any start.
#Do I need a test email after every edit?
Yes for copy edits. Merge fields and rendering only show up in a real inbox.
#How do I see the exact tool call Claude Code made?
Read the permission prompt before you approve, and ask the model to report the tool and arguments after the call.
#What if the diff shows extra changes?
Check whether they are server-managed fields such as timestamps. Anything in copy, delays, ids or references needs an explanation before you continue.
#Can the model invent facts in the copy?
Yes. Give it the facts and require sources, and delete any claim you cannot trace.
#How do I handle the unsubscribe line?
Add a plain, honest opt-out to each step. The CAN-SPAM guide requires a working opt-out, and Google requires one-click unsubscribe for bulk senders.
#What if my tool list has no update tool?
Use the CLI. The loop is the same, and the CLI adds --dry-run and exit codes.
#How do I roll back an edit?
Pause the campaign, restore the file from git, and write it back with a dry run, a re-read and a diff.
#Should I let the model run campaigns start?
No. Prepare the command with the model, then run it yourself after you read the readiness result and the test email.
#Where do I find the workflow field names?
Read the real output. The README does not publish the workflow structure, so ask the model to list the field names it received.
#Does this work for follow-up steps?
Yes. Follow-ups are steps in the workflow, so they pass through the same loop.
#What should I read next?
Read asking Claude how your campaign is doing to measure the effect of your edits.
#Conclusion
Chat is a good interface for editing copy, and a poor one for knowing what changed. The loop in this guide fixes that gap by giving every edit a before, an after and a diff.
Keep three rules. Change one thing per pass, and save the baseline in git. Read the workflow back after every write. Run campaigns readiness and campaigns send-test-email before any campaigns start, and never let a key near a prompt.
A workflow you can diff is a workflow you can trust. Start with the next edit you were about to make, and run the six moves on it.
Ready to try it? Read your first workflow with firstsales campaigns workflow get, save it in git and ask Claude Code for one small change. Then check your results with the analytics guide.