@audienti/cli 0.1.33 → 0.1.34

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/CHANGELOG.md CHANGED
@@ -4,6 +4,18 @@ All notable changes to the Audienti CLI are documented here.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.1.34] - 2026-08-17
8
+
9
+ ### Added
10
+
11
+ - Add `audienti icps update <icp_id> --payload <file.json>` for rich ICP facet patches using the same payload shape as ICP creation.
12
+ - Surface executable motion configuration validity in `audienti motions status`, including missing outbound signals and missing LOPA tracked profile URLs.
13
+ - Add outbound motion signal configuration to `audienti motions create --payload` and `audienti motions update <motn_id> --payload <file.json>`, including scoped company/person/both signal rows.
14
+
15
+ ### Changed
16
+
17
+ - Default omitted inbound motion channels to LinkedIn-only when motions are created through the CLI/API contract; Reddit remains explicit opt-in.
18
+
7
19
  ## [0.1.33] - 2026-08-13
8
20
 
9
21
  ### Added
package/README.md CHANGED
@@ -142,6 +142,7 @@ audienti motions run-discovery <motn_id>
142
142
  audienti motions quick-start --url https://example.com --wait --confirm
143
143
  audienti motions update <motn_id> --status paused
144
144
  audienti motions update <motn_id> --own-post-engagement true
145
+ audienti motions update <motn_id> --payload motion-signals.json
145
146
  audienti motions activate <motn_id>
146
147
  audienti motions delete <motn_id> --confirm yes
147
148
  audienti content programs
@@ -249,12 +250,36 @@ used by the operator surface:
249
250
  audienti motions run-discovery <motn_id>
250
251
  ```
251
252
 
253
+ To check whether a motion has executable configuration before preparing,
254
+ activating, or running discovery:
255
+
256
+ ```bash
257
+ audienti motions status <motn_id>
258
+ audienti motions status <motn_id> --json
259
+ ```
260
+
261
+ The JSON response includes `executable_configuration.valid` plus a reason when
262
+ the config is incomplete, such as missing outbound signals or missing LOPA
263
+ tracked profile URLs.
264
+
252
265
  To create and launch a quick-start motion from a company URL:
253
266
 
254
267
  ```bash
255
268
  audienti motions quick-start --url https://example.com --wait --confirm
256
269
  ```
257
270
 
271
+ To create or update an ICP from a rich JSON payload:
272
+
273
+ ```bash
274
+ audienti icps create --payload icp.json
275
+ audienti icps update <icp_id> --payload icp-patch.json
276
+ ```
277
+
278
+ `icps update --payload` accepts the same human-readable facet keys as create.
279
+ Supplied facet collections, such as `company_sizes_attributes`, replace that
280
+ collection in place; omitted fields and collections remain unchanged. Invalid
281
+ lookup values fail server-side without partially applying the patch.
282
+
258
283
  To audit one account user's outbound actions, optionally narrowed to one motion
259
284
  and one AccountProspect.created_at cohort:
260
285
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@audienti/cli",
3
- "version": "0.1.33",
3
+ "version": "0.1.34",
4
4
  "description": "Agent-first command-line client for Audienti.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/cli.js CHANGED
@@ -72,7 +72,7 @@ const OFFERS_DELETE_USAGE = "Usage: audienti offers delete <offr_id> --confirm <
72
72
  const WRITER_TEST_RUN_USAGE = "Usage: audienti writer test-run <prsp_id> [--json] [--mode <plan|report|step>] [--branch <both|no-accept|accepted>] [--step <step_key|row_number>] [--report <rprt_id>] [--no-wait] [--timeout-seconds <n>] [--poll-interval-seconds <n>] [--account <acct_id>]";
73
73
  const WRITER_TEST_RUN_SHOW_USAGE = "Usage: audienti writer test-run show <prsp_id> <rprt_id> [--json] [--account <acct_id>]";
74
74
  const MOTIONS_ANALYTICS_USAGE = "Usage: audienti motions analytics <motn_id> [--window 30d] [--json] [--account <acct_id>]";
75
- const MOTIONS_UPDATE_USAGE = "Usage: audienti motions update <motn_id> [--status <draft|preparing|active|paused|archived>] [--tags <tag[,tag...]>] [--own-post-engagement <true|false>] [--json] [--account <acct_id>]";
75
+ const MOTIONS_UPDATE_USAGE = "Usage: audienti motions update <motn_id> ([--status <draft|preparing|active|paused|archived>] [--tags <tag[,tag...]>] [--own-post-engagement <true|false>] | --payload <file.json>) [--json] [--account <acct_id>]";
76
76
  const CONTENT_PROGRAMS_USAGE = "Usage: audienti content programs [--user <account_user_id|email|name|me>] [--json] [--account <acct_id>]";
77
77
  const CONTENT_PLAN_USAGE = "Usage: audienti content plan <cprg_id> [--week <n>] [--due] [--json] [--account <acct_id>]";
78
78
  const CONTENT_SHOW_USAGE = "Usage: audienti content show <cpwi_id> [--json] [--account <acct_id>]";
@@ -91,7 +91,7 @@ const MOTIONS_MOVE_PROSPECTS_USAGE = "Usage: audienti motions move-prospects <so
91
91
  const MOTIONS_RUN_DISCOVERY_USAGE = "Usage: audienti motions run-discovery <motn_id> [--target-count <n>] [--json] [--account <acct_id>]";
92
92
  const MOTIONS_QUICK_START_USAGE = "Usage: audienti motions quick-start --url <company_url> [--principal <account_user_id|me>] [--feedback <text>] [--offer-type <type>] [--force] [--confirm] [--wait] [--timeout-seconds <n>] [--poll-interval-seconds <n>] [--json] [--account <acct_id>]";
93
93
  const ICPS_SHOW_USAGE = "Usage: audienti icps show <icp_id> [--json] [--account <acct_id>]";
94
- const ICPS_UPDATE_USAGE = "Usage: audienti icps update <icp_id> [--name <text>] [--notes <text>] [--discovery-keyword <text>] [--tags <tag[,tag...]>] [--json] [--account <acct_id>]";
94
+ const ICPS_UPDATE_USAGE = "Usage: audienti icps update <icp_id> ([--name <text>] [--notes <text>] [--discovery-keyword <text>] [--tags <tag[,tag...]>] | --payload <file.json>) [--json] [--account <acct_id>]";
95
95
  const ICPS_ADD_TAG_USAGE = "Usage: audienti icps add-tag <icp_id> <tag> [--json] [--account <acct_id>]";
96
96
  const ICPS_REMOVE_TAG_USAGE = "Usage: audienti icps remove-tag <icp_id> <tag> [--json] [--account <acct_id>]";
97
97
  const LISTS_ADD_TAG_USAGE = "Usage: audienti lists add-tag <list_id> <tag> [--json] [--account <acct_id>]";
@@ -1030,25 +1030,19 @@ async function icpsCreate(args, context, { accountOverride } = {}) {
1030
1030
  async function icpsUpdate(args, context, { accountOverride } = {}) {
1031
1031
  const { values, positionals } = parseCommandArgs(args, {
1032
1032
  ...jsonOptions(),
1033
+ payload: { type: "string" },
1033
1034
  name: { type: "string" },
1034
1035
  notes: { type: "string" },
1035
1036
  tags: { type: "string" },
1036
1037
  "discovery-keyword": { type: "string" }
1037
1038
  });
1038
- const hasUpdateField = values.name || values.notes !== undefined || values.tags !== undefined || values["discovery-keyword"] !== undefined;
1039
+ const hasUpdateField = values.payload || icpSimpleFieldsPresent(values);
1039
1040
  if (positionals.length !== 1 || !hasUpdateField) {
1040
1041
  throw new CommandError(ICPS_UPDATE_USAGE);
1041
1042
  }
1042
1043
 
1043
1044
  const { client, accountId } = await requireAccountContext(context, { accountOverride });
1044
- const icp = await client.updateIcp(accountId, positionals[0], {
1045
- icp: compactObject({
1046
- name: values.name,
1047
- notes: values.notes,
1048
- discovery_keyword: values["discovery-keyword"],
1049
- tags: values.tags !== undefined ? tagList(values.tags) : undefined
1050
- })
1051
- });
1045
+ const icp = await client.updateIcp(accountId, positionals[0], { icp: await icpUpdatePayload(values) });
1052
1046
  if (values.json) return writeJson(context.stdout, icp);
1053
1047
 
1054
1048
  writeLine(context.stdout, `Updated ICP ${display(icp?.name)} (${display(icp?.prefix_id)}).`);
@@ -1683,7 +1677,7 @@ async function motionsCreate(args, context, { accountOverride } = {}) {
1683
1677
  }
1684
1678
 
1685
1679
  const { client, accountId } = await requireAccountContext(context, { accountOverride });
1686
- const payload = await readJsonPayload(values.payload);
1680
+ const payload = normalizeMotionCreatePayload(await readJsonPayload(values.payload));
1687
1681
  const created = await client.createMotion(accountId, { motion: payload });
1688
1682
  if (values.json) return writeJson(context.stdout, created);
1689
1683
 
@@ -1691,6 +1685,14 @@ async function motionsCreate(args, context, { accountOverride } = {}) {
1691
1685
  renderMotion(created, context);
1692
1686
  }
1693
1687
 
1688
+ function normalizeMotionCreatePayload(payload) {
1689
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) return payload;
1690
+ if (String(payload.kind || "").trim().toLowerCase() !== "inbound") return payload;
1691
+ if (Object.prototype.hasOwnProperty.call(payload, "inbound_channels")) return payload;
1692
+
1693
+ return { ...payload, inbound_channels: ["linkedin"] };
1694
+ }
1695
+
1694
1696
  async function motionsDelete(args, context, { accountOverride } = {}) {
1695
1697
  const { values, positionals } = parseCommandArgs(args, {
1696
1698
  ...jsonOptions(),
@@ -1711,30 +1713,44 @@ async function motionsDelete(args, context, { accountOverride } = {}) {
1711
1713
  async function motionsUpdate(args, context, { accountOverride } = {}) {
1712
1714
  const { values, positionals } = parseCommandArgs(args, {
1713
1715
  ...jsonOptions(),
1716
+ payload: { type: "string" },
1714
1717
  status: { type: "string" },
1715
1718
  tags: { type: "string" },
1716
1719
  "own-post-engagement": { type: "string" }
1717
1720
  });
1718
- const hasUpdateField = values.status || values.tags !== undefined || values["own-post-engagement"] !== undefined;
1721
+ const hasUpdateField = values.payload || motionSimpleFieldsPresent(values);
1719
1722
  if (positionals.length !== 1 || !hasUpdateField) {
1720
1723
  throw new CommandError(MOTIONS_UPDATE_USAGE);
1721
1724
  }
1722
1725
 
1723
- const normalizedStatus = normalizeMotionStatus(values.status);
1724
- const motionAttributes = compactObject({
1725
- status: normalizedStatus,
1726
- play_tags: values.tags !== undefined ? tagList(values.tags) : undefined,
1727
- own_post_engagement: values["own-post-engagement"] !== undefined ? parseBooleanString(values["own-post-engagement"], "--own-post-engagement") : undefined
1728
- });
1729
-
1730
1726
  const { client, accountId } = await requireAccountContext(context, { accountOverride });
1731
- const motion = await client.updateMotion(accountId, positionals[0], { motion: motionAttributes });
1727
+ const motion = await client.updateMotion(accountId, positionals[0], { motion: await motionUpdatePayload(values) });
1732
1728
  if (values.json) return writeJson(context.stdout, motion);
1733
1729
 
1734
1730
  writeLine(context.stdout, `Updated motion ${display(motion?.name)} (${display(motion?.prefix_id)}).`);
1735
1731
  renderMotion(motion, context);
1736
1732
  }
1737
1733
 
1734
+ async function motionUpdatePayload(values) {
1735
+ if (values.payload) {
1736
+ if (motionSimpleFieldsPresent(values)) {
1737
+ throw new CommandError("Choose one motion input mode: either --payload <file.json> or the simple --status/--tags/--own-post-engagement flags.");
1738
+ }
1739
+
1740
+ return readJsonPayload(values.payload);
1741
+ }
1742
+
1743
+ return compactObject({
1744
+ status: normalizeMotionStatus(values.status),
1745
+ play_tags: values.tags !== undefined ? tagList(values.tags) : undefined,
1746
+ own_post_engagement: values["own-post-engagement"] !== undefined ? parseBooleanString(values["own-post-engagement"], "--own-post-engagement") : undefined
1747
+ });
1748
+ }
1749
+
1750
+ function motionSimpleFieldsPresent(values) {
1751
+ return Boolean(values.status) || values.tags !== undefined || values["own-post-engagement"] !== undefined;
1752
+ }
1753
+
1738
1754
  async function motionsStatusShortcut(action, args, context, { accountOverride } = {}) {
1739
1755
  const usage = `Usage: audienti motions ${action} <motn_id> [--json] [--account <acct_id>]`;
1740
1756
  const { values, positionals } = parseCommandArgs(args, jsonOptions());
@@ -3575,7 +3591,7 @@ function filterRecordsByTag(records, value, field) {
3575
3591
 
3576
3592
  async function icpCreatePayload(values) {
3577
3593
  if (values.payload) {
3578
- if (values.name || values.notes || values.tags !== undefined || values["discovery-keyword"]) {
3594
+ if (icpSimpleFieldsPresent(values)) {
3579
3595
  throw new CommandError("Choose one ICP input mode: either --payload <file.json> or the simple --name/--notes/--discovery-keyword/tags flags.");
3580
3596
  }
3581
3597
 
@@ -3594,6 +3610,27 @@ async function icpCreatePayload(values) {
3594
3610
  });
3595
3611
  }
3596
3612
 
3613
+ async function icpUpdatePayload(values) {
3614
+ if (values.payload) {
3615
+ if (icpSimpleFieldsPresent(values)) {
3616
+ throw new CommandError("Choose one ICP input mode: either --payload <file.json> or the simple --name/--notes/--discovery-keyword/tags flags.");
3617
+ }
3618
+
3619
+ return readJsonPayload(values.payload);
3620
+ }
3621
+
3622
+ return compactObject({
3623
+ name: values.name,
3624
+ notes: values.notes,
3625
+ tags: values.tags !== undefined ? tagList(values.tags) : undefined,
3626
+ discovery_keyword: values["discovery-keyword"]
3627
+ });
3628
+ }
3629
+
3630
+ function icpSimpleFieldsPresent(values) {
3631
+ return Boolean(values.name) || values.notes !== undefined || values.tags !== undefined || values["discovery-keyword"] !== undefined;
3632
+ }
3633
+
3597
3634
  async function prospectNotePayload(values, { forcedType, usageText } = {}) {
3598
3635
  const engagementKey = values["engagement-type"] || values["engagement-key"];
3599
3636
 
@@ -4323,6 +4360,7 @@ function renderMotion(motion, context) {
4323
4360
  writeLine(context.stdout, `Motion: ${display(motion?.name)} (${display(motion?.prefix_id)})`);
4324
4361
  writeLine(context.stdout, `Status: ${display(motion?.status)}`);
4325
4362
  writeLine(context.stdout, `Kind: ${display(motion?.kind)}`);
4363
+ renderExecutableConfiguration(motion?.executable_configuration, context);
4326
4364
  if (motion?.offer?.name) writeLine(context.stdout, `Offer: ${motion.offer.name} (${display(motion.offer.prefix_id)})`);
4327
4365
  if (motion?.icp?.name) writeLine(context.stdout, `ICP: ${motion.icp.name} (${display(motion.icp.prefix_id)})`);
4328
4366
  if (motion?.list?.name) writeLine(context.stdout, `List: ${motion.list.name} (${display(motion.list.prefix_id)})`);
@@ -4393,10 +4431,23 @@ function renderMotionStatus(status, context) {
4393
4431
  writeLine(context.stdout, `Motion: ${display(status?.name)} (${display(status?.prefix_id)})`);
4394
4432
  writeLine(context.stdout, `State: ${display(status?.state)}`);
4395
4433
  writeLine(context.stdout, `Reason: ${display(status?.reason_label || status?.reason_key)}`);
4434
+ renderExecutableConfiguration(status?.executable_configuration, context);
4396
4435
  if (status?.description) writeLine(context.stdout, status.description);
4397
4436
  if (status?.action?.label) writeLine(context.stdout, `Action: ${status.action.label}`);
4398
4437
  }
4399
4438
 
4439
+ function renderExecutableConfiguration(config, context) {
4440
+ if (!config || typeof config !== "object") return;
4441
+
4442
+ writeLine(context.stdout, `Valid config: ${config.valid ? "yes" : "no"}`);
4443
+ if (!config.valid && (config.reason_label || config.reason_key)) {
4444
+ writeLine(context.stdout, `Config reason: ${display(config.reason_label || config.reason_key)}`);
4445
+ }
4446
+ if (!config.valid && config.description) {
4447
+ writeLine(context.stdout, `Config detail: ${config.description}`);
4448
+ }
4449
+ }
4450
+
4400
4451
  function renderProspects(payload, context, { wide = false, profiles = false } = {}) {
4401
4452
  const prospects = Array.isArray(payload?.prospects) ? payload.prospects : [];
4402
4453
  if (prospects.length === 0) return writeLine(context.stdout, "No prospects found.");
@@ -6239,6 +6290,7 @@ const HELP_TOPICS = new Map([
6239
6290
  " audienti motions prospects <motn_id>",
6240
6291
  " audienti motions create --payload <file.json>",
6241
6292
  " audienti motions update <motn_id> [--status <state>] [--tags <tag[,tag...]>] [--own-post-engagement <true|false>]",
6293
+ " audienti motions update <motn_id> --payload <file.json>",
6242
6294
  " audienti motions add-tag <motn_id> <tag>",
6243
6295
  " audienti motions remove-tag <motn_id> <tag>",
6244
6296
  " audienti motions activate <motn_id>",
@@ -6293,7 +6345,7 @@ const HELP_TOPICS = new Map([
6293
6345
  " audienti offers delete <offr_id> --confirm <yes|true|Y|y>",
6294
6346
  " audienti icps list [--tag <tag>]",
6295
6347
  " audienti icps show <icp_id>",
6296
- " audienti icps update <icp_id> [--tags <tag[,tag...]>]",
6348
+ " audienti icps update <icp_id> [--tags <tag[,tag...]> | --payload <file.json>]",
6297
6349
  " audienti icps add-tag <icp_id> <tag>",
6298
6350
  " audienti icps remove-tag <icp_id> <tag>",
6299
6351
  " audienti companies search --query <text>",
@@ -6770,7 +6822,7 @@ const HELP_TOPICS = new Map([
6770
6822
  " audienti icps list [--tag <tag>] [--json]",
6771
6823
  " audienti icps show <icp_id> [--json]",
6772
6824
  " audienti icps create (--name <text> | --payload <file.json>) [--json]",
6773
- " audienti icps update <icp_id> [--tags <tag[,tag...]>] [--json]",
6825
+ " audienti icps update <icp_id> ([--tags <tag[,tag...]>] | --payload <file.json>) [--json]",
6774
6826
  " audienti icps add-tag <icp_id> <tag> [--json]",
6775
6827
  " audienti icps remove-tag <icp_id> <tag> [--json]",
6776
6828
  "",
@@ -6863,12 +6915,12 @@ const HELP_TOPICS = new Map([
6863
6915
 
6864
6916
  ["icps update", [
6865
6917
  "Usage:",
6866
- " audienti icps update <icp_id> [--name <text>] [--notes <text>] [--discovery-keyword <text>] [--tags <tag[,tag...]>] [--json] [--account <acct_id>]",
6918
+ ` ${ICPS_UPDATE_USAGE.slice("Usage: ".length)}`,
6867
6919
  "",
6868
6920
  "Status: implemented",
6869
6921
  "",
6870
6922
  "Purpose:",
6871
- " Update simple ICP fields or replace the ICP's full tag set.",
6923
+ " Update simple ICP fields, replace the ICP's full tag set, or apply a rich ICP payload patch.",
6872
6924
  "",
6873
6925
  "Input shape:",
6874
6926
  " icp_id: icpp_ prefixed id or integer id",
@@ -6876,15 +6928,30 @@ const HELP_TOPICS = new Map([
6876
6928
  " notes: string | optional",
6877
6929
  " discovery_keyword: string | optional",
6878
6930
  " tags: comma-separated tag list | optional",
6931
+ " payload: file.json | optional full or partial ICP object using the account API create shape",
6932
+ "",
6933
+ "Behavior:",
6934
+ " Choose either --payload or simple flags. Supplied facet collections replace that collection in place; omitted fields and facet collections remain unchanged.",
6935
+ " Human-readable lookup-backed facet names are resolved by the account API. Invalid lookup values fail with 422 and no partial mutation.",
6879
6936
  "",
6880
6937
  "API:",
6881
6938
  " PATCH /api/v1/accounts/:account_id/icps/:id.json",
6882
6939
  "",
6883
- "JSON body:",
6940
+ "Simple JSON body:",
6884
6941
  " {",
6885
6942
  " \"icp\": {",
6886
6943
  " \"tags\": [\"enterprise\", \"renewal\"]",
6887
6944
  " }",
6945
+ " }",
6946
+ "",
6947
+ "Payload file example:",
6948
+ " {",
6949
+ " \"company_sizes_attributes\": [",
6950
+ " {\"name\": \"1001-5000 employees\"}",
6951
+ " ],",
6952
+ " \"seniorities_attributes\": [",
6953
+ " {\"name\": \"CXO\"}",
6954
+ " ]",
6888
6955
  " }"
6889
6956
  ].join("\n")],
6890
6957
 
@@ -7489,7 +7556,7 @@ const HELP_TOPICS = new Map([
7489
7556
  " audienti motions prospects <motn_id> [--json]",
7490
7557
  " audienti motions add-prospects <motn_id> <prsp_id> [prsp_id...] [--json]",
7491
7558
  " audienti motions create --payload <file.json> [--json]",
7492
- " audienti motions update <motn_id> [--status <draft|preparing|active|paused|archived>] [--tags <tag[,tag...]>] [--own-post-engagement <true|false>] [--json]",
7559
+ " audienti motions update <motn_id> ([--status <draft|preparing|active|paused|archived>] [--tags <tag[,tag...]>] [--own-post-engagement <true|false>] | --payload <file.json>) [--json]",
7493
7560
  " audienti motions add-tag <motn_id> <tag> [--json]",
7494
7561
  " audienti motions remove-tag <motn_id> <tag> [--json]",
7495
7562
  " audienti motions activate <motn_id> [--json]",
@@ -7587,6 +7654,7 @@ const HELP_TOPICS = new Map([
7587
7654
  " state: healthy_idle | broken",
7588
7655
  " reason_key: string",
7589
7656
  " reason_label: string",
7657
+ " executable_configuration: { valid: boolean, reason_key: string | null, reason_label: string | null, description: string | null }",
7590
7658
  " description: string",
7591
7659
  " action: { key: string, label: string } | null",
7592
7660
  " stats: { target_count, deficit, projected_connectable, capacity, daily_target }",
@@ -7738,7 +7806,9 @@ const HELP_TOPICS = new Map([
7738
7806
  " icp_id: icpp_ prefix id | optional",
7739
7807
  " list_id: list_ prefix id | optional",
7740
7808
  " play_tags: [string] | optional",
7741
- " inbound_channels: [linkedin | reddit] | optional",
7809
+ " signal_rows: [{ scope: company | person | both, question: string, company_signal_category: string | optional, role_terms: string | optional, posting_language: string | optional, topics: string | optional }] | optional for outbound motions",
7810
+ " signal_questions: newline-delimited company::, person::, or both:: legacy question text | optional for outbound motions",
7811
+ " inbound_channels: [linkedin | reddit] | optional; defaults to [linkedin] for inbound motions",
7742
7812
  " lopa_profiles: [{ url: string, source_type: creator | competitor | partner | customer | other }] | optional",
7743
7813
  "",
7744
7814
  "JSON example:",
@@ -7793,25 +7863,44 @@ const HELP_TOPICS = new Map([
7793
7863
  "Status: implemented",
7794
7864
  "",
7795
7865
  "Purpose:",
7796
- " Change one motion or play's lifecycle status or replace its tag set.",
7866
+ " Change one motion or play's lifecycle status, replace its tag set, or patch rich motion configuration.",
7797
7867
  "",
7798
7868
  "Input shape:",
7799
7869
  " motn_id: motn_ prefix id",
7800
7870
  " status: draft | preparing | active | paused | archived | optional",
7801
7871
  " tags: comma-separated tag list | optional",
7872
+ " payload: file.json | optional full or partial motion object using the account API shape",
7802
7873
  "",
7803
7874
  "Behavior:",
7804
- " Updates only the provided fields. Sending --tags replaces the motion's full tag set.",
7875
+ " Choose either --payload or simple flags. Updates only the provided fields. Sending --tags replaces the motion's full tag set.",
7876
+ " Payload mode supports outbound signal_rows or legacy signal_questions; supplied rows replace the motion's active ready signals.",
7805
7877
  "",
7806
7878
  "API:",
7807
7879
  " PATCH /api/v1/accounts/:account_id/motions/:id.json",
7808
7880
  "",
7809
- "JSON body:",
7881
+ "Simple JSON body:",
7810
7882
  " {",
7811
7883
  " \"motion\": {",
7812
7884
  " \"status\": \"paused\",",
7813
7885
  " \"play_tags\": [\"sarit\", \"pj\"]",
7814
7886
  " }",
7887
+ " }",
7888
+ "",
7889
+ "Payload file example:",
7890
+ " {",
7891
+ " \"signal_rows\": [",
7892
+ " {",
7893
+ " \"scope\": \"company\",",
7894
+ " \"question\": \"Is the company hiring revenue operations leaders?\",",
7895
+ " \"company_signal_category\": \"hiring\",",
7896
+ " \"role_terms\": \"Revenue Operations\"",
7897
+ " },",
7898
+ " {",
7899
+ " \"scope\": \"person\",",
7900
+ " \"question\": \"Is the person talking about pipeline quality?\",",
7901
+ " \"topics\": \"pipeline quality\"",
7902
+ " }",
7903
+ " ]",
7815
7904
  " }"
7816
7905
  ].join("\n")],
7817
7906