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