01Run it
Straight from the command's own help.
p202 campaign replace-url --match g2afse.com --set 'https://example.com/?utm_source={slug}' --dry-runp202 campaign replace-url --match http://promo.example.com --with https://promo.example.com --forcep202 campaign replace-url --undo ~/.p202/undo/replace-url-20260930T101500Z.json --dry-run02What you get
--json, --csv or --ndjson for scripts, or -q for ids only. An AI agent gets compact JSON without asking. All output formats03Build your command
Pick values and the command line writes itself, quoted and ready to paste.
p202 campaign replace-urlSet flags below; the command updates as you type.
04Flags
9 flags, plus the global flags every command takes.
| Flag | What it does |
|---|---|
| --aff-network-idstring | Only campaigns in this affiliate network (p202 aff-network list) |
| --dry-runbool | List the changes without writing anything |
| --force, -fbool | Skip the confirmation prompt |
| --idsstring | Only these campaign IDs (comma-separated) |
| --matchstring | Text the offer URLs to change contain (case-insensitive; required unless --undo) |
| --setstring | Replace the whole URL with this; {id} and {slug} are filled per campaign |
| --slotstring ยท default all | URL slots to consider: 1-5, comma-separated, or all |
| --undostring | Revert a run from its undo manifest file (printed after "Undo with:"): restores old URLs where the slot is unchanged since |
| --withstring | Replace the matched text with this |
05How it works
Finds every campaign whose offer URL slots (aff_campaign_url, _2 to _5) contain --match (case-insensitive), and rewrites the matching slots:
--with <text> replaces the matched text inside the URL --set <url> replaces the whole URL; {id} and {slug} (the campaign name, lowercased and hyphenated) are filled per campaign
Run with --dry-run first: it lists every campaign, slot, old and new URL and writes nothing. Without --dry-run the same list is shown and you are asked to confirm (--force skips the prompt; --staged records one proposal per campaign instead of writing). Matching runs in the CLI over every page of campaigns, so it works against any server version.
Re-running is safe with --set (updated URLs no longer contain --match), but with --with it rewrites again if the replacement still contains --match.
Undo: every run that applies a change saves the applied slots (old and new
URL, profile, base URL) to ~/.p202/undo/replace-url-<UTC time>.json and
prints Undo with: p202 campaign replace-url --undo <file> (under --json
also meta.undo_manifest). --undo puts old_url back only where the slot
still holds that run's new_url; slots changed since, and deleted campaigns,
are left alone and listed with status skipped. It refuses a manifest
written against another base URL, cannot be combined with --match, --with,
--set, --slot, --ids or --aff-network-id, and otherwise behaves like a
normal run (--dry-run, the prompt, --force, --staged, exit 5 on a failed
PUT). An undo that applies changes saves its own manifest, so it can be
undone too. If a manifest cannot be saved after the writes, the command
prints it on stderr and exits 5.
06For agents
Running this from an agent
- Read the same facts as JSON:
p202 commands campaign replace-url --json. - With
AI_AGENT,CLAUDECODEor another agent variable set, output is compact JSON and errors arrive on stderr as a JSON envelope with ahint. - Exit codes: 0 ok, 1 bad input, 2 auth, 3 network, 4 server error, 5 partial failure.
- This command writes. Add
--stagedto record it as a proposal a person applies withp202 change apply.
{
"path": "p202 campaign replace-url",
"use": "replace-url",
"short": "Find campaigns whose offer URLs contain a string and rewrite those URLs in bulk",
"long": "Finds every campaign whose offer URL slots (aff_campaign_url, _2 to _5)\ncontain --match (case-insensitive), and rewrites the matching slots:\n\n --with <text> replaces the matched text inside the URL\n --set <url> replaces the whole URL; {id} and {slug} (the campaign\n name, lowercased and hyphenated) are filled per campaign\n\nRun with --dry-run first: it lists every campaign, slot, old and new URL\nand writes nothing. Without --dry-run the same list is shown and you are\nasked to confirm (--force skips the prompt; --staged records one proposal\nper campaign instead of writing). Matching runs in the CLI over every page\nof campaigns, so it works against any server version.\n\nRe-running is safe with --set (updated URLs no longer contain --match),\nbut with --with it rewrites again if the replacement still contains --match.\n\nUndo: every run that applies a change saves the applied slots (old and new\nURL, profile, base URL) to ~/.p202/undo/replace-url-<UTC time>.json and\nprints `Undo with: p202 campaign replace-url --undo <file>` (under --json\nalso meta.undo_manifest). --undo puts old_url back only where the slot\nstill holds that run's new_url; slots changed since, and deleted campaigns,\nare left alone and listed with status skipped. It refuses a manifest\nwritten against another base URL, cannot be combined with --match, --with,\n--set, --slot, --ids or --aff-network-id, and otherwise behaves like a\nnormal run (--dry-run, the prompt, --force, --staged, exit 5 on a failed\nPUT). An undo that applies changes saves its own manifest, so it can be\nundone too. If a manifest cannot be saved after the writes, the command\nprints it on stderr and exits 5.",
"runnable": true,
"example": "p202 campaign replace-url --match g2afse.com --set 'https://example.com/?utm_source={slug}' --dry-run\n p202 campaign replace-url --match http://promo.example.com --with https://promo.example.com --force\n p202 campaign replace-url --undo ~/.p202/undo/replace-url-20260930T101500Z.json --dry-run",
"flags": [
{
"name": "aff-network-id",
"type": "string",
"default": "",
"usage": "Only campaigns in this affiliate network (`p202 aff-network list`)",
"required": false
},
{
"name": "dry-run",
"type": "bool",
"default": "false",
"usage": "List the changes without writing anything",
"required": false
},
{
"name": "force",
"shorthand": "f",
"type": "bool",
"default": "false",
"usage": "Skip the confirmation prompt",
"required": false
},
{
"name": "ids",
"type": "string",
"default": "",
"usage": "Only these campaign IDs (comma-separated)",
"required": false
},
{
"name": "match",
"type": "string",
"default": "",
"usage": "Text the offer URLs to change contain (case-insensitive; required unless --undo)",
"required": false
},
{
"name": "set",
"type": "string",
"default": "",
"usage": "Replace the whole URL with this; {id} and {slug} are filled per campaign",
"required": false
},
{
"name": "slot",
"type": "string",
"default": "all",
"usage": "URL slots to consider: 1-5, comma-separated, or all",
"required": false
},
{
"name": "undo",
"type": "string",
"default": "",
"usage": "Revert a run from its undo manifest `file` (printed after \"Undo with:\"): restores old URLs where the slot is unchanged since",
"required": false
},
{
"name": "with",
"type": "string",
"default": "",
"usage": "Replace the matched text with this",
"required": false
}
]
}