@flowapt/flowiq-cli 0.4.1 → 0.4.3

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
@@ -379,6 +379,12 @@ flowiq seg untag <org_id> repeat-60d --commit --confirm # ROLLBACK (its own ta
379
379
  tagged cohorts" one-liner — no id file, no database access.
380
380
  - Apply is **append-only** — it never touches a contact's other tags, names,
381
381
  or anything else, and never double-adds.
382
+ - **Large cohorts are chunked server-side** (fixed 3 Aug 2026): the tagging RPC
383
+ writes every batch inside one statement, so a ~90k apply used to hit the
384
+ database timeout and roll back with nothing written. Apply now splits into
385
+ calls of ≤8000 contacts. Safe to re-run at any size — if a chunk ever does
386
+ fail, the error names how many landed and a re-run finishes the job
387
+ (append-only ⇒ idempotent).
382
388
  - Point-in-time warning: the plan snapshots the cohort at plan time; re-run
383
389
  `plan` to refresh it (someone who ordered since won't auto-drop, criteria
384
390
  mode included).
@@ -405,6 +411,9 @@ flowiq tag segment <org_id> --min-orders 2 --min-spent 1000 --region ZA-GP --tag
405
411
 
406
412
  # Attributes — broadcast permission (all_contacts / allow_broadcast_true|false / no_broadcast_permission)
407
413
  flowiq tag attributes <org_id> --filter allow_broadcast_true --tag broadcastable --commit
414
+ # Big org (cohort > ~20k)? Chunk it: repeat this until it reports "matched 0" —
415
+ # each run tags the next 10k untagged contacts (see the bullet below on the 50k cap / DB timeout)
416
+ flowiq tag attributes <org_id> --filter allow_broadcast_true --limit 10000 --exclude broadcastable --tag broadcastable --commit --yes
408
417
 
409
418
  # Message activity — ≥ N messages of a sender type, optional date range
410
419
  flowiq tag messages <org_id> --min-count 3 --sender user-whatsapp --tag engaged --commit
@@ -422,6 +431,12 @@ flowiq tag remove <org_id> vip,old-promo --confirm # remove tag(s) from ALL con
422
431
  - **`cohort` is top-N, not a threshold** — `top_spenders --limit 500` tags the
423
432
  top 500 by spend, not "everyone above £X". Use `tag segment --min-spent` for
424
433
  a threshold.
434
+ - **Big orgs: chunk `attributes` commits with `--limit` + `--exclude <your-tag>`**
435
+ (flags added 3 Aug 2026, in-repo pending publish). The tagging RPC hard-caps at 50,000
436
+ per call AND a single large commit updates row-by-row, so a ~50k commit dies
437
+ on the DB statement timeout (full rollback, nothing written). `--limit 10000
438
+ --exclude <the-tag-you're-adding>` makes each run pick the next 10k untagged
439
+ matches — re-run until `matched 0`. Idempotent and safe to re-run.
425
440
  - **Undo** any tag with `flowiq tag remove <org> <tag> --confirm`.
426
441
 
427
442
  ### Keywords — `flowiq keywords pull|push|list` (alias `kw`)
package/TEAM-GUIDE.md CHANGED
@@ -92,6 +92,7 @@ When you see it, run `npm i -g @flowapt/flowiq-cli` — a stale version also mea
92
92
  | Split your whole broadcast list into N even cohorts (e.g. 3 for A/B/C or waves) | `flowiq seg plan <org_id> --tag-prefix bcast --from-attribute allow_broadcast_true --split 3` → `flowiq seg apply <org_id> bcast --commit` (makes `bcast-batch-01/02/03`, ~even, shuffled) |
93
93
  | Combine existing tags → a batched send list (include some tags, drop others, split into batches of N) | `flowiq seg plan <org_id> --tag-prefix clearance-bc --from-tag "loyalty-list" --exclude "recent-campaign" --batch-size 1000` → `flowiq seg apply <org_id> clearance-bc --commit` (makes `clearance-bc-batch-01/02/…`) |
94
94
  | Split a big id-list cohort into send-safe batch tags | `flowiq seg plan <org_id> --tag-prefix … --ids-file …` → `flowiq seg apply … --commit` |
95
+ | Split a whole tagged audience into batches of N (e.g. 90k → 7000s) | `flowiq seg plan <org_id> --tag-prefix 3-aug-bc --from-tag "3-aug-bc" --batch-size 7000` → `flowiq seg apply <org_id> 3-aug-bc --commit --yes` (makes `…-batch-01…13`; works at any size — big applies are chunked internally) |
95
96
  | Send to one batch tag | `flowiq bc send <org_id> --tag <batch-tag> --template … --body param1="Hi {{first_name}}" --commit` (per-contact tokens: the 6 contact fields + `{{attributes.<key>}}`) |
96
97
  | Send a broadcast whose template has an IMAGE/VIDEO/DOCUMENT header | Same as above — the media is automatic (the template's own stored header). Override with `--header-media <public-url>` if needed. The CLI verifies the resolved media's actual type against the header format — `header video (video ✓ video/mp4)` means verified; a mismatch (e.g. a video template whose stored default is secretly a png — templates made before 3 Aug 2026 can carry this) ABORTS and tells you to pass `--header-media` with the real file. |
97
98
  | Send via the SAME engine as the dashboard's "Python" toggle | add `--python` to a `bc send --tag …` (fire-and-forget; python resolves the tag + sends + tracks; no CLI resume for this engine). **Any tag send over 10 recipients uses python automatically.** |
@@ -179,6 +180,18 @@ real order line-items (accurate + windowable) rather than the order-history text
179
180
  And a commit over 2000 contacts asks you to type the tag name to confirm
180
181
  (add `--yes` to skip). Everything is appends-only and reversible with `tag remove`.
181
182
 
183
+ **Tagging a very large audience (20k+, e.g. "everyone opted in") with `attributes`?**
184
+ One giant commit will fail on a database timeout (nothing writes). Chunk it instead:
185
+
186
+ ```bash
187
+ # repeat this exact line until it prints "matched 0" — each run tags the NEXT 10k
188
+ flowiq tag attributes <org_id> --filter allow_broadcast_true \
189
+ --limit 10000 --exclude my-campaign-tag --tag my-campaign-tag --commit --yes
190
+ ```
191
+
192
+ `--exclude` names the same tag you're adding, so every run skips the people who
193
+ already have it. Safe to re-run as many times as you like.
194
+
182
195
  ## Keys, rotation, logging out
183
196
 
184
197
  - **`flowiq auth refresh`** — rotates this device's key in place (a fresh key
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowapt/flowiq-cli",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "description": "Command-line tool for FlowIQ staff: round-trip agent prompts, questionnaires, fine-tuning, pin-board tasks, webhooks, templates, agent-updates, chat exports, and live agent testing without ever touching service-role credentials.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -132,6 +132,7 @@ export async function attributes(orgId, opts = {}) {
132
132
  const payload = {
133
133
  mode: "attributes", organization_id: orgId, filter_type: opts.filter || "all_contacts",
134
134
  include_tags: splitList(opts.include), exclude_tags: splitList(opts.exclude),
135
+ limit: opts.limit ? Number(opts.limit) : undefined, offset: opts.offset ? Number(opts.offset) : 0,
135
136
  };
136
137
  await confirmLargeWrite(orgId, payload, opts, `Attributes ${payload.filter_type}`);
137
138
  await runMatch(`Attributes ${payload.filter_type}`, payload, opts);
package/src/index.js CHANGED
@@ -550,6 +550,8 @@ export function run(argv) {
550
550
  .option("--filter <f>", "attribute filter", "all_contacts")
551
551
  .option("--include <csv>", "only contacts carrying any of these tags")
552
552
  .option("--exclude <csv>", "exclude contacts carrying any of these tags")
553
+ .option("--limit <n>", "max contacts per run (default 50000 — the RPC cap; a 50k commit can hit the DB statement timeout, so chunk big orgs: --limit 10000 --exclude <tag>, re-run until 0 match)")
554
+ .option("--offset <n>", "skip the first N", "0")
553
555
  .option("--tag <name>", "tag to apply (required with --commit)")
554
556
  .option("--commit", "write the tag (omit = dry-run count + sample)")
555
557
  .option("--yes", "skip the type-the-tag confirm on large (>2000) writes")