@flowapt/flowiq-cli 0.4.1 → 0.4.2

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
@@ -405,6 +405,9 @@ flowiq tag segment <org_id> --min-orders 2 --min-spent 1000 --region ZA-GP --tag
405
405
 
406
406
  # Attributes — broadcast permission (all_contacts / allow_broadcast_true|false / no_broadcast_permission)
407
407
  flowiq tag attributes <org_id> --filter allow_broadcast_true --tag broadcastable --commit
408
+ # Big org (cohort > ~20k)? Chunk it: repeat this until it reports "matched 0" —
409
+ # each run tags the next 10k untagged contacts (see the bullet below on the 50k cap / DB timeout)
410
+ flowiq tag attributes <org_id> --filter allow_broadcast_true --limit 10000 --exclude broadcastable --tag broadcastable --commit --yes
408
411
 
409
412
  # Message activity — ≥ N messages of a sender type, optional date range
410
413
  flowiq tag messages <org_id> --min-count 3 --sender user-whatsapp --tag engaged --commit
@@ -422,6 +425,12 @@ flowiq tag remove <org_id> vip,old-promo --confirm # remove tag(s) from ALL con
422
425
  - **`cohort` is top-N, not a threshold** — `top_spenders --limit 500` tags the
423
426
  top 500 by spend, not "everyone above £X". Use `tag segment --min-spent` for
424
427
  a threshold.
428
+ - **Big orgs: chunk `attributes` commits with `--limit` + `--exclude <your-tag>`**
429
+ (flags added 3 Aug 2026, in-repo pending publish). The tagging RPC hard-caps at 50,000
430
+ per call AND a single large commit updates row-by-row, so a ~50k commit dies
431
+ on the DB statement timeout (full rollback, nothing written). `--limit 10000
432
+ --exclude <the-tag-you're-adding>` makes each run pick the next 10k untagged
433
+ matches — re-run until `matched 0`. Idempotent and safe to re-run.
425
434
  - **Undo** any tag with `flowiq tag remove <org> <tag> --confirm`.
426
435
 
427
436
  ### Keywords — `flowiq keywords pull|push|list` (alias `kw`)
package/TEAM-GUIDE.md CHANGED
@@ -179,6 +179,18 @@ real order line-items (accurate + windowable) rather than the order-history text
179
179
  And a commit over 2000 contacts asks you to type the tag name to confirm
180
180
  (add `--yes` to skip). Everything is appends-only and reversible with `tag remove`.
181
181
 
182
+ **Tagging a very large audience (20k+, e.g. "everyone opted in") with `attributes`?**
183
+ One giant commit will fail on a database timeout (nothing writes). Chunk it instead:
184
+
185
+ ```bash
186
+ # repeat this exact line until it prints "matched 0" — each run tags the NEXT 10k
187
+ flowiq tag attributes <org_id> --filter allow_broadcast_true \
188
+ --limit 10000 --exclude my-campaign-tag --tag my-campaign-tag --commit --yes
189
+ ```
190
+
191
+ `--exclude` names the same tag you're adding, so every run skips the people who
192
+ already have it. Safe to re-run as many times as you like.
193
+
182
194
  ## Keys, rotation, logging out
183
195
 
184
196
  - **`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.2",
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")