trackrev 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,96 +1,887 @@
1
1
  # trackrev
2
2
 
3
- Your [TrackRev](https://trackrev.io) link analytics, in the terminal — performance per channel,
4
- per link, the raw click stream, and any single visitor's full journey.
3
+ [TrackRev](https://trackrev.io) in your terminal — create and manage tracking links, pull
4
+ performance per channel and per link, walk the raw click stream, and replay any visitor's
5
+ journey from first click to paid conversion.
5
6
 
6
7
  Zero dependencies. Node 20 or newer.
7
8
 
8
9
  ## Install
9
10
 
10
11
  ```bash
11
- npx trackrev channels
12
+ npm install -g trackrev
12
13
  ```
13
14
 
14
- Or keep it around:
15
+ Or run it once without installing:
15
16
 
16
17
  ```bash
17
- npm install -g trackrev
18
+ npx trackrev channels
18
19
  ```
19
20
 
20
21
  ## Authenticate
21
22
 
22
- Create a **secret** key under Settings → Developers, then:
23
+ Create a **secret** key under Settings → Developers, then either save it:
24
+
25
+ ```bash
26
+ trackrev login
27
+ ```
28
+
29
+ It is stored at `~/.config/trackrev/config.json` with mode `0600` and verified against the API
30
+ before it is written, so a typo fails immediately rather than on your next command.
31
+
32
+ Or set it in the environment, which always wins and is what CI should use:
23
33
 
24
34
  ```bash
25
35
  export TRACKREV_KEY=lk_live_...
26
36
  ```
27
37
 
28
- Set `TRACKREV_API_URL` to point at a different API base (self-hosted, staging).
38
+ `TRACKREV_API_URL` points the CLI at a different API base (self-hosted, staging). Use
39
+ `--profile NAME` on `login` to keep several environments side by side.
40
+
41
+ ## Plans
42
+
43
+ Link and account commands work on **every plan**, including free, and respect the same limits the
44
+ dashboard does (50 links on the free tier). Analytics commands — `channels`, `links perf`,
45
+ `clicks`, `journey` — need a **paid plan**; on a free workspace they exit `3` with an upgrade
46
+ message.
29
47
 
30
48
  ## Commands
31
49
 
32
- ### `channels` — performance per traffic source
50
+ <!-- cli:commands:start -->
51
+
52
+ _Generated from `apps/cli/src/registry.js` — edit there, then run `pnpm --filter trackrev sync-docs`._
53
+
54
+ ### Analytics
55
+
56
+ #### `trackrev channels`
57
+
58
+ performance per traffic source _(paid plan)_
59
+
60
+ ```bash
61
+ trackrev channels --days 7 --ltv
62
+ ```
63
+
64
+ | flag | meaning |
65
+ | --- | --- |
66
+ | `--days N` | last N days (default 30, max 365) |
67
+ | `--from ISO` | explicit start, e.g. 2026-01-01; wins over --days |
68
+ | `--to ISO` | explicit end (defaults to now) |
69
+ | `--ltv` | add all-time lifetime value per channel |
70
+
71
+ conversions is a decimal on purpose — attribution splits credit, so one sale touched by two channels counts 0.5 on each.
72
+
73
+ #### `trackrev links perf`
74
+
75
+ performance per short link (bare `trackrev links` still works) _(paid plan)_
76
+
77
+ ```bash
78
+ trackrev links perf --limit 20
79
+ ```
80
+
81
+ | flag | meaning |
82
+ | --- | --- |
83
+ | `--limit N` | rows to return (max 500) |
84
+ | `--days N` | last N days (default 30, max 365) |
85
+ | `--from ISO` | explicit start, e.g. 2026-01-01; wins over --days |
86
+ | `--to ISO` | explicit end (defaults to now) |
87
+ | `--settings` | attach each link's URL, expiry and password flag |
88
+
89
+ #### `trackrev clicks`
90
+
91
+ the raw click stream — newest first, bots excluded _(paid plan)_
92
+
93
+ ```bash
94
+ trackrev clicks --all --json
95
+ ```
96
+
97
+ | flag | meaning |
98
+ | --- | --- |
99
+ | `--limit N` | page size (default 100, max 500) |
100
+ | `--all` | follow the cursor to the end of the stream |
101
+ | `--link ID` | one link only |
102
+ | `--bots` | include bot traffic |
103
+
104
+ --all stops after 200 pages and says so on stderr, so a runaway cursor can never loop forever.
105
+
106
+ #### `trackrev visitors journey <visitor-id>`
107
+
108
+ one visitor's timeline — every click, identify event and order, in order _(paid plan)_
109
+
110
+ ```bash
111
+ trackrev journey <visitor-id>
112
+ ```
113
+
114
+ The visitor caption goes to stderr, so the rows stay pipe-clean.
115
+
116
+ ### Links
117
+
118
+ #### `trackrev links list`
119
+
120
+ the links themselves — newest first, no window
121
+
122
+ ```bash
123
+ trackrev links list --channel youtube
124
+ ```
125
+
126
+ | flag | meaning |
127
+ | --- | --- |
128
+ | `--limit N` | page size (default 100, max 500) |
129
+ | `--all` | follow the cursor to the end |
130
+ | `--channel KEY` | one channel only |
131
+ | `--q TEXT` | slug contains this text |
132
+
133
+ #### `trackrev links create`
134
+
135
+ create a campaign: one link per channel, or one Smart Link
33
136
 
34
137
  ```bash
35
- trackrev channels # last 30 days
36
- trackrev channels --days 7
37
- trackrev channels --ltv # add all-time lifetime value per channel
138
+ trackrev links create --url https://acme.com/launch --name Launch --channel youtube --channel newsletter
38
139
  ```
39
140
 
141
+ | flag | meaning |
142
+ | --- | --- |
143
+ | `--url URL` | destination (required) |
144
+ | `--name TEXT` | campaign name (required) |
145
+ | `--channel KEY` | a channel; repeat for several |
146
+ | `--smart` | one link that infers its channel per click |
147
+ | `--tag TEXT` | a tag; repeat for several |
148
+ | `--folder ID` | put the campaign in this folder |
149
+ | `--external` | destination is a site you can't put the pixel on |
150
+ | `--slug SLUG` | custom slug (2–64 chars; lowercase, digits, hyphens) |
151
+ | `--campaign TEXT` | utm_campaign (defaults to the name) |
152
+ | `--expires ISO` | expire at this time |
153
+ | `--max-clicks N` | expire after this many clicks |
154
+ | `--expired-url URL` | where to send visitors after expiry |
155
+ | `--password TEXT` | require this password before redirecting |
156
+ | `--mobile-url URL` | device targeting: send mobile here |
157
+ | `--desktop-url URL` | device targeting: send desktop here |
158
+ | `--retarget on\|off` | fire the workspace's ad pixels on click |
159
+
160
+ Prints one row per link created. Channels: facebook instagram youtube linkedin twitter tiktok newsletter website other.
161
+
162
+ #### `trackrev links get <id-or-slug>`
163
+
164
+ one link by id, slug or short code
165
+
166
+ ```bash
167
+ trackrev links get black-friday
40
168
  ```
41
- channel clicks visitors conversions revenue
42
- youtube 1,204 980 12.50 2,410.50
43
- newsletter 310 287 9.00 1,890.00
169
+
170
+ #### `trackrev links update <id>`
171
+
172
+ change a link's slug, UTMs, expiry, password or targeting
173
+
174
+ ```bash
175
+ trackrev links update <id> --expires 2026-12-31T23:59:59Z --max-clicks 1000
176
+ ```
177
+
178
+ | flag | meaning |
179
+ | --- | --- |
180
+ | `--term TEXT` | utm_term |
181
+ | `--content TEXT` | utm_content |
182
+ | `--clear-password` | remove the password |
183
+ | `--clear-expiry` | remove date and click-cap expiry |
184
+ | `--slug SLUG` | custom slug (2–64 chars; lowercase, digits, hyphens) |
185
+ | `--campaign TEXT` | utm_campaign (defaults to the name) |
186
+ | `--expires ISO` | expire at this time |
187
+ | `--max-clicks N` | expire after this many clicks |
188
+ | `--expired-url URL` | where to send visitors after expiry |
189
+ | `--password TEXT` | require this password before redirecting |
190
+ | `--mobile-url URL` | device targeting: send mobile here |
191
+ | `--desktop-url URL` | device targeting: send desktop here |
192
+ | `--retarget on\|off` | fire the workspace's ad pixels on click |
193
+
194
+ #### `trackrev links delete <id>`
195
+
196
+ delete one link (its campaign and other channels stay) _(asks to confirm; `--yes` in scripts)_
197
+
198
+ ```bash
199
+ trackrev links delete <id> --yes
44
200
  ```
45
201
 
46
- `conversions` is a decimal because attribution splits credit — one sale touched by two channels
47
- counts 0.5 on each.
202
+ #### `trackrev links bulk`
48
203
 
49
- ### `links` — performance per short link
204
+ create up to 500 links from a CSV
50
205
 
51
206
  ```bash
52
- trackrev links --limit 20
53
- trackrev links --from 2026-01-01 --to 2026-02-01
207
+ trackrev links bulk --file links.csv
54
208
  ```
55
209
 
56
- ### `clicks` — the raw click stream
210
+ | flag | meaning |
211
+ | --- | --- |
212
+ | `--file PATH` | CSV with url, name, channel columns (- for stdin) |
213
+
214
+ Optional columns: utm_campaign, utm_term, utm_content, tags, campaign_id. Rows that fail are listed with the reason; the rest are created.
215
+
216
+ #### `trackrev links qr <id-or-slug>`
57
217
 
58
- Newest first, bots excluded, cursor-paginated.
218
+ the link's QR code as SVG
59
219
 
60
220
  ```bash
61
- trackrev clicks # most recent 100
62
- trackrev clicks --limit 500 # one page, max size
63
- trackrev clicks --all # follow the cursor to the end
64
- trackrev clicks --link <link-id> # one link only
65
- trackrev clicks --bots # include bot traffic
221
+ trackrev links qr black-friday --out black-friday.svg
66
222
  ```
67
223
 
68
- `--all` stops after 200 pages and says so on stderr, so a runaway cursor can't loop forever.
224
+ | flag | meaning |
225
+ | --- | --- |
226
+ | `--out PATH` | write here instead of stdout |
227
+ | `--size N` | size in px (default 512, max 2048) |
228
+
229
+ ### Developers
230
+
231
+ #### `trackrev keys list`
69
232
 
70
- ### `journey` — one visitor's timeline
233
+ the workspace's API keys (prefixes only)
71
234
 
72
235
  ```bash
73
- trackrev journey 3f1b8c22-9d4e-4a71-b8c0-2e6f5a91d7e4
236
+ trackrev keys list --revoked
74
237
  ```
75
238
 
76
- Every click, identify event and order in order. The visitor id is the `visitor_id` column from
77
- `trackrev clicks`.
239
+ | flag | meaning |
240
+ | --- | --- |
241
+ | `--revoked` | include revoked keys |
78
242
 
79
- ## Window
243
+ #### `trackrev keys create`
80
244
 
81
- `channels` and `links` accept:
245
+ mint a key — the plaintext is shown once, never again
246
+
247
+ ```bash
248
+ trackrev keys create --label 'CI deploy'
249
+ ```
82
250
 
83
251
  | flag | meaning |
84
252
  | --- | --- |
253
+ | `--scope SCOPE` | secret (servers, CLI) or public (browser) |
254
+ | `--label TEXT` | what this key is for |
255
+
256
+ The key goes to stdout and everything else to stderr, so `trackrev keys create > key.txt` captures only the key.
257
+
258
+ #### `trackrev keys revoke <id>`
259
+
260
+ revoke a key immediately _(asks to confirm; `--yes` in scripts)_
261
+
262
+ ```bash
263
+ trackrev keys revoke <id> --yes
264
+ ```
265
+
266
+ #### `trackrev webhooks list`
267
+
268
+ outbound endpoints, with their last delivery status
269
+
270
+ ```bash
271
+ trackrev webhooks list
272
+ ```
273
+
274
+ #### `trackrev webhooks events`
275
+
276
+ every event an endpoint can subscribe to
277
+
278
+ ```bash
279
+ trackrev webhooks events
280
+ ```
281
+
282
+ #### `trackrev webhooks create`
283
+
284
+ add an endpoint — the signing secret is shown once
285
+
286
+ ```bash
287
+ trackrev webhooks create --url https://acme.com/hook --event sale.created
288
+ ```
289
+
290
+ | flag | meaning |
291
+ | --- | --- |
292
+ | `--url URL` | https endpoint (required) |
293
+ | `--event NAME` | an event; repeat for several |
294
+
295
+ https only. Run `trackrev webhooks events` for the valid names.
296
+
297
+ #### `trackrev webhooks update <id>`
298
+
299
+ change the URL or events, or pause and resume delivery
300
+
301
+ ```bash
302
+ trackrev webhooks update <id> --pause
303
+ ```
304
+
305
+ | flag | meaning |
306
+ | --- | --- |
307
+ | `--url URL` | new endpoint URL |
308
+ | `--event NAME` | replace the event list; repeat |
309
+ | `--pause` | stop delivering |
310
+ | `--resume` | start delivering again |
311
+
312
+ #### `trackrev webhooks delete <id>`
313
+
314
+ remove an endpoint _(asks to confirm; `--yes` in scripts)_
315
+
316
+ ```bash
317
+ trackrev webhooks delete <id> --yes
318
+ ```
319
+
320
+ ### Setup
321
+
322
+ #### `trackrev attribution get`
323
+
324
+ the model and lookback window this workspace uses
325
+
326
+ ```bash
327
+ trackrev attribution get --models
328
+ ```
329
+
330
+ | flag | meaning |
331
+ | --- | --- |
332
+ | `--models` | list the three models and what each credits |
333
+
334
+ #### `trackrev attribution set`
335
+
336
+ change the model or the window
337
+
338
+ ```bash
339
+ trackrev attribution set --model linear --window 60
340
+ ```
341
+
342
+ | flag | meaning |
343
+ | --- | --- |
344
+ | `--model NAME` | last_touch, first_touch or linear |
345
+ | `--window N` | lookback in days (1-365) |
346
+
347
+ Both settings apply retroactively — every past order is re-credited against them.
348
+
349
+ #### `trackrev folders list`
350
+
351
+ campaign folders, with how many campaigns each holds
352
+
353
+ ```bash
354
+ trackrev folders list
355
+ ```
356
+
357
+ #### `trackrev folders create`
358
+
359
+ create a folder to group campaigns under
360
+
361
+ ```bash
362
+ trackrev folders create --name 'Q4 launch' --start 2026-10-01
363
+ ```
364
+
365
+ | flag | meaning |
366
+ | --- | --- |
367
+ | `--name TEXT` | folder name (required) |
368
+ | `--description TEXT` | what it covers |
369
+ | `--start YYYY-MM-DD` | start date |
370
+ | `--end YYYY-MM-DD` | end date |
371
+
372
+ #### `trackrev folders update <id>`
373
+
374
+ rename a folder or change its dates
375
+
376
+ ```bash
377
+ trackrev folders update <id> --name 'Q1 launch'
378
+ ```
379
+
380
+ | flag | meaning |
381
+ | --- | --- |
382
+ | `--name TEXT` | new name |
383
+ | `--description TEXT` | new description |
384
+ | `--start YYYY-MM-DD` | start date |
385
+ | `--end YYYY-MM-DD` | end date |
386
+
387
+ #### `trackrev folders delete <id>`
388
+
389
+ delete a folder — its campaigns become ungrouped, not deleted _(asks to confirm; `--yes` in scripts)_
390
+
391
+ ```bash
392
+ trackrev folders delete <id> --yes
393
+ ```
394
+
395
+ #### `trackrev folders assign <destination-id>`
396
+
397
+ file a campaign under a folder, or un-file it
398
+
399
+ ```bash
400
+ trackrev folders assign <destination-id> --folder <folder-id>
401
+ ```
402
+
403
+ | flag | meaning |
404
+ | --- | --- |
405
+ | `--folder ID` | folder to file under; omit to un-file |
406
+
407
+ The id is a CAMPAIGN (the destination behind a set of links), not a single link.
408
+
409
+ ### Revenue
410
+
411
+ #### `trackrev revenue list`
412
+
413
+ connected payment providers and their last sync
414
+
415
+ ```bash
416
+ trackrev revenue list
417
+ ```
418
+
419
+ #### `trackrev revenue providers`
420
+
421
+ what can be connected, and the credentials each needs
422
+
423
+ ```bash
424
+ trackrev revenue providers
425
+ ```
426
+
427
+ Stripe is absent by design — its restricted key lives on the workspace, not here.
428
+
429
+ #### `trackrev revenue connect`
430
+
431
+ connect a provider — credentials are verified before saving
432
+
433
+ ```bash
434
+ trackrev revenue connect --provider polar --field api_key=polar_oat_…
435
+ ```
436
+
437
+ | flag | meaning |
438
+ | --- | --- |
439
+ | `--provider NAME` | polar, lemonsqueezy, paddle, creem or dodo |
440
+ | `--field K=V` | credential as key=value; repeat per field |
441
+ | `--sandbox` | use the provider's sandbox host, where it has one |
442
+
443
+ #### `trackrev revenue sync`
444
+
445
+ pull charges now and attribute them _(paid plan)_
446
+
447
+ ```bash
448
+ trackrev revenue sync
449
+ ```
450
+
451
+ | flag | meaning |
452
+ | --- | --- |
453
+ | `--connection ID` | one connection only; omit for all + Stripe |
454
+
455
+ Reports imported and attributed per provider. A provider that fails does not stop the others.
456
+
457
+ #### `trackrev revenue disconnect <id>`
458
+
459
+ disconnect a provider; imported orders are kept _(asks to confirm; `--yes` in scripts)_
460
+
461
+ ```bash
462
+ trackrev revenue disconnect <id> --yes
463
+ ```
464
+
465
+ ### Audience
466
+
467
+ #### `trackrev visitors list`
468
+
469
+ visitors, most recently seen first _(paid plan)_
470
+
471
+ ```bash
472
+ trackrev visitors list --email @acme.com
473
+ ```
474
+
475
+ | flag | meaning |
476
+ | --- | --- |
477
+ | `--limit N` | page size (default 100, max 500) |
478
+ | `--all` | follow the cursor to the end |
479
+ | `--email TEXT` | email contains this text |
480
+
481
+ #### `trackrev visitors get <id>`
482
+
483
+ one visitor _(paid plan)_
484
+
485
+ ```bash
486
+ trackrev visitors get <id>
487
+ ```
488
+
489
+ #### `trackrev orders list`
490
+
491
+ synced purchases, newest first
492
+
493
+ ```bash
494
+ trackrev orders list --status refunded
495
+ ```
496
+
497
+ | flag | meaning |
498
+ | --- | --- |
499
+ | `--limit N` | page size (default 100, max 500) |
500
+ | `--all` | follow the cursor to the end |
501
+ | `--status NAME` | paid or refunded |
502
+ | `--email TEXT` | email contains this text |
503
+
504
+ amount is blank on the free plan, where revenue figures are hidden.
505
+
506
+ #### `trackrev export`
507
+
508
+ any dataset as CSV _(paid plan)_
509
+
510
+ ```bash
511
+ trackrev export --kind orders --days 90 --out orders.csv
512
+ ```
513
+
514
+ | flag | meaning |
515
+ | --- | --- |
516
+ | `--kind NAME` | channels, links, orders or visitors |
517
+ | `--out PATH` | write here instead of stdout |
85
518
  | `--days N` | last N days (default 30, max 365) |
86
- | `--from ISO` | explicit start, e.g. `2026-01-01` |
519
+ | `--from ISO` | explicit start, e.g. 2026-01-01; wins over --days |
87
520
  | `--to ISO` | explicit end (defaults to now) |
88
521
 
89
- `--from`/`--to` win over `--days`.
522
+ ### Domains
523
+
524
+ #### `trackrev domains list`
525
+
526
+ branded short-link domains and their DNS status
527
+
528
+ ```bash
529
+ trackrev domains list
530
+ ```
531
+
532
+ #### `trackrev domains add <domain>`
533
+
534
+ attach a domain — prints the DNS records to add
535
+
536
+ ```bash
537
+ trackrev domains add go.acme.com
538
+ ```
539
+
540
+ #### `trackrev domains verify <domain-or-id>`
541
+
542
+ re-check DNS now; exits non-zero until it is active
543
+
544
+ ```bash
545
+ trackrev domains verify go.acme.com
546
+ ```
547
+
548
+ Exits 1 while still pending, so a deploy script can poll until it passes.
549
+
550
+ #### `trackrev domains remove <domain-or-id>`
551
+
552
+ detach a domain; links keep working on the default host _(asks to confirm; `--yes` in scripts)_
553
+
554
+ ```bash
555
+ trackrev domains remove go.acme.com --yes
556
+ ```
557
+
558
+ #### `trackrev retargeting list`
559
+
560
+ the ad pixels fired on opted-in link clicks
561
+
562
+ ```bash
563
+ trackrev retargeting list --providers
564
+ ```
565
+
566
+ | flag | meaning |
567
+ | --- | --- |
568
+ | `--providers` | show what can be configured instead |
569
+
570
+ #### `trackrev retargeting set <provider>`
571
+
572
+ set a provider's pixel id
573
+
574
+ ```bash
575
+ trackrev retargeting set meta --id 1234567890123456
576
+ ```
577
+
578
+ | flag | meaning |
579
+ | --- | --- |
580
+ | `--id ID` | the pixel/tag id (required) |
581
+
582
+ The id must match that provider's shape — only validated ids are ever put into a loader snippet.
583
+
584
+ #### `trackrev retargeting remove <provider>`
585
+
586
+ remove a provider's pixel _(asks to confirm; `--yes` in scripts)_
587
+
588
+ ```bash
589
+ trackrev retargeting remove meta --yes
590
+ ```
591
+
592
+ ### Affiliate
593
+
594
+ #### `trackrev programs list`
595
+
596
+ the workspace's affiliate programs and their terms
597
+
598
+ ```bash
599
+ trackrev programs list --archived
600
+ ```
601
+
602
+ | flag | meaning |
603
+ | --- | --- |
604
+ | `--archived` | include archived programs |
605
+
606
+ #### `trackrev programs get <id>`
607
+
608
+ one program in full
609
+
610
+ ```bash
611
+ trackrev programs get <id>
612
+ ```
613
+
614
+ #### `trackrev programs update <id>`
615
+
616
+ change commission terms, or pause and archive _(paid plan)_
617
+
618
+ ```bash
619
+ trackrev programs update <id> --rate 0.25 --status paused
620
+ ```
621
+
622
+ | flag | meaning |
623
+ | --- | --- |
624
+ | `--name TEXT` | program name |
625
+ | `--landing-url URL` | where partner links point |
626
+ | `--type TYPE` | percent or flat |
627
+ | `--rate N` | 0-1 fraction for percent (0.25 = 25%), dollars for flat |
628
+ | `--recurring N` | months a commission keeps paying |
629
+ | `--cookie N` | attribution window in days |
630
+ | `--min-payout N` | minimum balance before a payout |
631
+ | `--auto-approve on\|off` | approve signups instantly |
632
+ | `--status NAME` | active, paused or archived |
633
+
634
+ Changes apply to NEW conversions; commissions already earned are untouched.
635
+
636
+ #### `trackrev partners list`
637
+
638
+ affiliates with their clicks, sales and earnings _(paid plan)_
639
+
640
+ ```bash
641
+ trackrev partners list --status pending
642
+ ```
643
+
644
+ | flag | meaning |
645
+ | --- | --- |
646
+ | `--program ID` | one program only |
647
+ | `--status NAME` | pending, approved, rejected, banned or archived |
648
+
649
+ #### `trackrev partners approve <partner-id>`
650
+
651
+ approve a pending affiliate _(paid plan)_
652
+
653
+ ```bash
654
+ trackrev partners approve <partner-id> --program <program-id>
655
+ ```
656
+
657
+ | flag | meaning |
658
+ | --- | --- |
659
+ | `--program ID` | the program id (required) |
660
+
661
+ Does NOT send the approval email the dashboard sends — a re-run would mail them again.
662
+
663
+ #### `trackrev partners reject <partner-id>`
664
+
665
+ reject an application _(paid plan)_ _(asks to confirm; `--yes` in scripts)_
666
+
667
+ ```bash
668
+ trackrev partners reject <partner-id> --program <program-id> --yes
669
+ ```
670
+
671
+ | flag | meaning |
672
+ | --- | --- |
673
+ | `--program ID` | the program id (required) |
674
+
675
+ #### `trackrev partners ban <partner-id>`
676
+
677
+ ban an affiliate _(paid plan)_ _(asks to confirm; `--yes` in scripts)_
678
+
679
+ ```bash
680
+ trackrev partners ban <partner-id> --program <program-id> --yes
681
+ ```
682
+
683
+ | flag | meaning |
684
+ | --- | --- |
685
+ | `--program ID` | the program id (required) |
686
+
687
+ #### `trackrev partners group <partner-id>`
688
+
689
+ move an affiliate into a group, or back to program terms _(paid plan)_
690
+
691
+ ```bash
692
+ trackrev partners group <partner-id> --program <program-id> --group <group-id>
693
+ ```
694
+
695
+ | flag | meaning |
696
+ | --- | --- |
697
+ | `--program ID` | the program id (required) |
698
+ | `--group ID` | group to move them to; omit to clear |
699
+
700
+ #### `trackrev groups list <program-id>`
701
+
702
+ a program's tiers, showing the terms each one resolves to _(paid plan)_
703
+
704
+ ```bash
705
+ trackrev groups list <program-id>
706
+ ```
707
+
708
+ ### Money
709
+
710
+ #### `trackrev commissions list`
711
+
712
+ the commission ledger, newest first _(paid plan)_
713
+
714
+ ```bash
715
+ trackrev commissions list --status pending
716
+ ```
717
+
718
+ | flag | meaning |
719
+ | --- | --- |
720
+ | `--limit N` | rows to return (max 500) |
721
+ | `--status NAME` | pending, eligible, paid, refunded, void or fraud |
722
+ | `--partner ID` | one affiliate only |
723
+
724
+ level 1 is the affiliate who sold; 2+ is an upline earning from their network.
725
+
726
+ #### `trackrev commissions add`
727
+
728
+ record an off-platform deal by hand _(paid plan)_
729
+
730
+ ```bash
731
+ trackrev commissions add --program <id> --partner <id> --amount 500 --earnings 100
732
+ ```
733
+
734
+ | flag | meaning |
735
+ | --- | --- |
736
+ | `--program ID` | program id (required) |
737
+ | `--partner ID` | affiliate id (required) |
738
+ | `--amount N` | gross sale value (required) |
739
+ | `--earnings N` | the affiliate's cut (required) |
740
+ | `--currency CODE` | defaults to usd |
741
+ | `--notes TEXT` | why this was entered by hand |
742
+
743
+ Earnings is not derived — a manual commission exists because the normal rate did not apply. It counts toward your monthly commission cap.
744
+
745
+ #### `trackrev commissions void <id>`
746
+
747
+ void a commission entered in error _(paid plan)_ _(asks to confirm; `--yes` in scripts)_
748
+
749
+ ```bash
750
+ trackrev commissions void <id> --yes
751
+ ```
752
+
753
+ | flag | meaning |
754
+ | --- | --- |
755
+ | `--status NAME` | set another status instead of void |
756
+
757
+ Refused if it is already on a payout batch — cancel the payout first.
758
+
759
+ #### `trackrev payouts list`
760
+
761
+ payout batches, with open and all-time totals _(paid plan)_
762
+
763
+ ```bash
764
+ trackrev payouts list --status pending
765
+ ```
766
+
767
+ | flag | meaning |
768
+ | --- | --- |
769
+ | `--status NAME` | pending, processing, paid, failed or canceled |
770
+ | `--limit N` | rows to return (max 500) |
771
+
772
+ Creating a batch stays in the dashboard: it applies per-group payout floors and platform fees, and a second implementation would eventually pay someone wrong.
773
+
774
+ #### `trackrev payouts mark-paid <id>`
775
+
776
+ settle a payout sent off-platform _(paid plan)_ _(asks to confirm; `--yes` in scripts)_
777
+
778
+ ```bash
779
+ trackrev payouts mark-paid <id> --reference PAYPAL-BATCH-123 --yes
780
+ ```
781
+
782
+ | flag | meaning |
783
+ | --- | --- |
784
+ | `--reference TEXT` | the rail's own id (PayPal batch, Wise transfer) |
785
+
786
+ Does NOT email the affiliate — the dashboard sends that, and a re-run would send it twice.
787
+
788
+ ### Settings
789
+
790
+ #### `trackrev settings notifications`
791
+
792
+ every transactional email, and whether it is on
793
+
794
+ ```bash
795
+ trackrev settings notifications
796
+ ```
797
+
798
+ default=yes means no override is stored and the catalogue default applies.
799
+
800
+ #### `trackrev settings notify <key>`
801
+
802
+ turn one transactional email on or off
803
+
804
+ ```bash
805
+ trackrev settings notify affiliate.approved --off
806
+ ```
807
+
808
+ | flag | meaning |
809
+ | --- | --- |
810
+ | `--on` | enable it |
811
+ | `--off` | disable it |
812
+
813
+ #### `trackrev settings branding`
814
+
815
+ the white-label settings affiliates see
816
+
817
+ ```bash
818
+ trackrev settings branding
819
+ ```
820
+
821
+ #### `trackrev settings set-branding`
822
+
823
+ set the partner-facing logo and accent colour
824
+
825
+ ```bash
826
+ trackrev settings set-branding --color '#e63e2e'
827
+ ```
828
+
829
+ | flag | meaning |
830
+ | --- | --- |
831
+ | `--logo URL` | https logo URL |
832
+ | `--color HEX` | hex accent, e.g. #e63e2e |
833
+ | `--clear-logo` | back to the TrackRev logo |
834
+ | `--clear-color` | back to the TrackRev colour |
835
+
836
+ The affiliate subdomain is read-only here — claiming one is a namespace reservation and belongs in one place.
837
+
838
+ ### Account
839
+
840
+ #### `trackrev me`
841
+
842
+ which workspace, plan, limits and key you're using
843
+
844
+ ```bash
845
+ trackrev me
846
+ ```
847
+
848
+ #### `trackrev login`
849
+
850
+ save a secret key so you don't need TRACKREV_KEY
851
+
852
+ ```bash
853
+ trackrev login --profile staging --api-url https://staging.example.com/api/v1
854
+ ```
855
+
856
+ | flag | meaning |
857
+ | --- | --- |
858
+ | `--key lk_…` | the key (prompted, hidden, when omitted) |
859
+ | `--api-url URL` | API base for this profile |
860
+
861
+ Stored at ~/.config/trackrev/config.json with mode 0600. TRACKREV_KEY in the environment always wins, for CI.
862
+
863
+ #### `trackrev logout`
864
+
865
+ forget a saved key
866
+
867
+ ```bash
868
+ trackrev logout
869
+ ```
870
+
871
+ ### Global flags
872
+
873
+ | flag | meaning |
874
+ | --- | --- |
875
+ | `--json` | print the API's JSON instead of a table |
876
+ | `--profile NAME` | use a saved login other than the current one |
877
+ | `--yes` | skip the confirmation on destructive commands |
878
+ | `--version` | print the version |
879
+ | `--help` | show help (also: trackrev <command> --help) |
880
+ <!-- cli:commands:end -->
90
881
 
91
882
  ## Output
92
883
 
93
- The same data comes out three ways, so the command works both as something you read and as
884
+ The same data comes out three ways, so a command works both as something you read and as
94
885
  something you pipe:
95
886
 
96
887
  ```bash
@@ -99,22 +890,63 @@ trackrev channels > channels.tsv # tab-separated, raw values (a pipe or file
99
890
  trackrev channels --json | jq . # the API's own JSON body
100
891
  ```
101
892
 
102
- Piped output is deliberately unformatted — `2410.5`, not `2,410.50` — so `cut` and `awk` see real
103
- numbers:
893
+ Piped output is deliberately unformatted — `2410.5`, not `2,410.50`, and `true` rather than
894
+ `yes` — so `cut` and `awk` see real values:
104
895
 
105
896
  ```bash
106
897
  trackrev channels | cut -f1,5
107
- trackrev links --limit 10 | column -t
898
+ trackrev links list | column -t
108
899
  trackrev clicks --all --json | jq '.clicks[] | select(.country == "BD")'
109
900
  ```
110
901
 
111
- Warnings, errors and the `journey` caption always go to **stderr**, never into your pipe.
902
+ Warnings, errors, confirmations and the `journey` caption always go to **stderr**, never into
903
+ your pipe.
904
+
905
+ ## Recipes
906
+
907
+ ```bash
908
+ # One link per channel for a launch, then print just the shareable URLs
909
+ trackrev links create --url https://acme.com/launch --name Launch \
910
+ --channel youtube --channel newsletter --channel twitter | cut -f3
911
+
912
+ # A link that dies after 1,000 clicks and sends latecomers to the waitlist
913
+ trackrev links create --url https://acme.com/beta --name Beta --channel newsletter \
914
+ --max-clicks 1000 --expired-url https://acme.com/waitlist
915
+
916
+ # Import a quarter's worth of links from a spreadsheet export
917
+ trackrev links bulk --file q4-links.csv
918
+
919
+ # Your five best links by revenue this month
920
+ trackrev links perf --limit 100 | tail -n +2 | sort -t$'\t' -k6,6nr | head -5
921
+
922
+ # Follow the newest click through to that visitor's whole journey
923
+ trackrev clicks --limit 1 | tail -1 | cut -f6 | xargs trackrev journey
924
+
925
+ # Nightly export
926
+ trackrev clicks --all --json > "clicks-$(date +%F).json"
927
+ ```
112
928
 
113
929
  ## Exit codes
114
930
 
115
931
  | code | meaning |
116
932
  | --- | --- |
117
933
  | `0` | success |
118
- | `1` | no API key, auth failure, plan gate, network error, API error |
119
- | `2` | usage error — unknown flag or command, bad `--limit`, missing visitor id |
934
+ | `1` | no API key, auth failure, network error, not found, API error |
935
+ | `2` | usage error — unknown flag or command, bad value, missing argument, refused confirmation |
936
+ | `3` | plan required — the workspace needs a paid plan for this command |
937
+
938
+ Exit `3` is separate so CI can tell "you are on the wrong plan" apart from "the call broke".
939
+
940
+ ## Development
941
+
942
+ ```bash
943
+ node --test test/*.test.js # the full suite, against a mock API — no network, no credentials
944
+ pnpm sync-docs # regenerate the command tables in this file and the docs site
945
+ ```
946
+
947
+ The test glob is pinned: a bare `node --test` also runs every other file under `test/`,
948
+ and `mock-api.mjs` is a server that never exits.
120
949
 
950
+ Commands are declared once in [`src/registry.js`](src/registry.js). The help text, the argument
951
+ parser, the tables above, the `/cli` marketing page and the Settings → Developers panel are all
952
+ derived from it — add a command there and a handler in `src/commands/`, then run `pnpm sync-docs`.