@wabery/cli 0.11.5 → 0.13.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,10 +1,11 @@
1
1
  # Wabery CLI and MCP Server
2
2
 
3
3
  Wabery is a developer platform for building messaging workflows on WhatsApp,
4
- Instagram, and Messenger. It provides APIs for channels, contacts,
5
- conversations, messages, WhatsApp Flows, hosted functions, automations,
6
- dispatches, submissions, registration intents, and signed webhooks, so you can build your own product
7
- experience instead of wiring together a closed inbox tool.
4
+ Instagram, and Messenger. It provides APIs for channels, contacts, contact
5
+ imports, broadcasts, conversations, messages, WhatsApp Flows, hosted functions,
6
+ automations, dispatches, submissions, registration intents, and signed webhooks,
7
+ so you can build your own product experience instead of wiring together a
8
+ closed inbox tool.
8
9
 
9
10
  This package ships the `wabery` command and a local MCP server for AI coding
10
11
  agents such as Claude Code, Codex, and OpenCode.
@@ -163,11 +164,18 @@ wabery mcp
163
164
 
164
165
  MCP starts in read-only mode. To allow mutation tools for a trusted local
165
166
  session, set `WABERY_MCP_MODE=write` or generate config with `--write`.
166
- High-risk tools also require confirmation tokens:
167
+ In local stdio mode, high-risk tools also require confirmation tokens:
167
168
 
168
169
  - `wabery_apply_config`: `confirmation_token: "apply_config"`
169
170
  - `wabery_publish_flow`: `confirmation_token: "publish:<flow_id>"`
170
171
  - `wabery_unenroll_contact` with `erase=true`: `confirmation_token: "erase:<contact_id>"`
172
+ - `wabery_send_broadcast`: `confirmation_token: "send_broadcast:<broadcast_id>"`
173
+ - `wabery_schedule_broadcast`: `confirmation_token: "schedule_broadcast:<broadcast_id>"`
174
+ - `wabery_cancel_broadcast`: `confirmation_token: "cancel_broadcast:<broadcast_id>"`
175
+
176
+ The hosted OAuth connector does not trust model-supplied confirmation strings.
177
+ It asks the MCP client to show a person-controlled elicitation prompt and fails
178
+ closed if the client cannot do so.
171
179
 
172
180
  ## Claude Code
173
181
 
@@ -289,7 +297,18 @@ Installed CLI:
289
297
  - `wabery_enroll_contact`
290
298
  - `wabery_list_contacts`
291
299
  - `wabery_get_contact`
300
+ - `wabery_list_contact_imports`
301
+ - `wabery_get_contact_import`
292
302
  - `wabery_unenroll_contact`
303
+ - `wabery_list_broadcasts`
304
+ - `wabery_get_broadcast`
305
+ - `wabery_create_broadcast`
306
+ - `wabery_prepare_broadcast_audience`
307
+ - `wabery_list_broadcast_recipients`
308
+ - `wabery_send_broadcast`
309
+ - `wabery_schedule_broadcast`
310
+ - `wabery_cancel_broadcast`
311
+ - `wabery_duplicate_broadcast`
293
312
  - `wabery_list_conversations`
294
313
  - `wabery_get_conversation`
295
314
  - `wabery_list_conversation_messages`
@@ -312,6 +331,12 @@ keys. After `wabery_send_message`, use `wabery_get_message` to inspect provider
312
331
  status and Meta failure details. Use `wabery_get_template` with refresh or
313
332
  `wabery_wait_template` to track approval.
314
333
 
334
+ For broadcasts, create a draft, prepare exactly one audience source, wait for
335
+ `ready`, and show the user the frozen counts before requesting confirmation.
336
+ CSV files are uploaded with `wabery contacts import`; agents can inspect
337
+ completed imports through the contact-import MCP tools. Never infer permission
338
+ to send from a request to create or review a campaign.
339
+
315
340
  Hosted function MCP tools let agents create project-scoped TypeScript handlers,
316
341
  deploy source, test them, expose them as tool-callable logic, invoke them with
317
342
  structured arguments, and read invocation logs. Write-mode function tools require
@@ -345,6 +370,17 @@ wabery contacts update "contact_id" \
345
370
  --metadata '{"linked": true}'
346
371
  ```
347
372
 
373
+ For a larger audience, upload a CSV and wait for completion:
374
+
375
+ ```sh
376
+ wabery contacts import contacts.csv \
377
+ --phone-column phone --name-column name \
378
+ --language-column language --tags-column tags \
379
+ --consent '{"categories":["MARKETING"],"source":"signup_form","method":"web_form"}'
380
+ wabery contacts imports
381
+ wabery contacts import-status "import_id"
382
+ ```
383
+
348
384
  For self-serve customer onboarding, mint a single-use WhatsApp registration link
349
385
  and poll it with the publishable key used by your widget:
350
386
 
@@ -356,7 +392,22 @@ wabery registration-intents create \
356
392
  wabery registration-intents get "intent_id"
357
393
  ```
358
394
 
359
- 3. Generate and validate `wabery.config.json`:
395
+ 3. To send a broadcast, create and prepare a draft, poll until `ready`, and
396
+ inspect recipients before sending:
397
+
398
+ ```sh
399
+ wabery broadcasts create --body @broadcast.json
400
+ wabery broadcasts prepare "broadcast_id" --body @audience.json
401
+ wabery broadcasts get "broadcast_id"
402
+ wabery broadcasts recipients "broadcast_id"
403
+ wabery broadcasts send "broadcast_id"
404
+ ```
405
+
406
+ `audience.json` must contain exactly one of `contactIds`, `importId`, or
407
+ `filter`. Broadcasts require a dedicated WhatsApp channel, approved templates,
408
+ and category-specific consent. See https://wabery.com/docs/broadcasts.
409
+
410
+ 4. Generate and validate `wabery.config.json`:
360
411
 
361
412
  ```sh
362
413
  wabery config init wabery.config.json --project-id "proj_or_agent_id"
@@ -386,7 +437,7 @@ select the project in the dashboard, create the project-scoped API key with
386
437
  `wabery login`, run `config apply` to point the webhook, and run `wabery env` to
387
438
  emit the secret for your deploy.
388
439
 
389
- 4. Publish a flow explicitly after review. Without `--wait`, this queues the
440
+ 5. Publish a flow explicitly after review. Without `--wait`, this queues the
390
441
  publish job and returns immediately; `--wait` polls until Meta reports it
391
442
  published, or fails with the rejection reason:
392
443
 
@@ -409,7 +460,7 @@ wabery flows reconcile "flow_id" # re-sync status from Meta (re-adopts a drif
409
460
  wabery flows delete "flow_id" # draft → delete on Meta, published → deprecate
410
461
  ```
411
462
 
412
- 5. Send a published flow:
463
+ 6. Send a published flow:
413
464
 
414
465
  ```sh
415
466
  wabery channels list
@@ -455,7 +506,7 @@ wabery messages send --channel-id "channel_id" \
455
506
  --sticker-link "https://example.com/sticker.webp"
456
507
  ```
457
508
 
458
- 6. Deploy hosted logic for a workflow or Business Agent tool:
509
+ 7. Deploy hosted logic for a workflow or Business Agent tool:
459
510
 
460
511
  ```sh
461
512
  wabery functions create --slug quote-customer \
@@ -8,6 +8,8 @@ export interface WaberyClientOptions {
8
8
  projectIdProvider?: () => string | undefined;
9
9
  /** Working directory for repo-local `.wabery.json` discovery. */
10
10
  cwd?: string;
11
+ /** Server-only proof that an MCP OAuth token is used by the hosted MCP service. */
12
+ mcpServiceProofSecret?: string;
11
13
  }
12
14
  export declare class WaberyApiError extends Error {
13
15
  readonly status: number;
@@ -19,11 +21,13 @@ export declare class WaberyApiClient {
19
21
  private readonly baseUrl;
20
22
  private readonly apiVersion?;
21
23
  private readonly projectIdProvider?;
24
+ private readonly mcpServiceProofSecret?;
22
25
  /** The project this client's key is scoped to, when known. */
23
26
  readonly projectId?: string;
24
27
  constructor(options?: WaberyClientOptions);
25
28
  get(path: string, query?: Record<string, string | number | undefined>): Promise<unknown>;
26
- post(path: string, body?: unknown): Promise<unknown>;
29
+ post(path: string, body?: unknown, idempotencyKey?: string): Promise<unknown>;
30
+ postForm(path: string, body: FormData): Promise<unknown>;
27
31
  put(path: string, body?: unknown): Promise<unknown>;
28
32
  patch(path: string, body?: unknown): Promise<unknown>;
29
33
  delete(path: string): Promise<unknown>;
@@ -1 +1 @@
1
- {"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,mBAAmB;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+DAA+D;IAC/D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qEAAqE;IACrE,iBAAiB,CAAC,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;IAC7C,iEAAiE;IACjE,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED,qBAAa,cAAe,SAAQ,KAAK;aAGvB,MAAM,EAAE,MAAM;aACd,IAAI,EAAE,OAAO;gBAF7B,OAAO,EAAE,MAAM,EACC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,OAAO;CAI9B;AAED,qBAAa,eAAe;IAC3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAA2B;IAC9D,8DAA8D;IAC9D,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;gBAEhB,OAAO,GAAE,mBAAwB;IAiBvC,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAIrE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO;IAIjC,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO;IAIhC,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO;IAIlC,MAAM,CAAC,IAAI,EAAE,MAAM;YAIX,OAAO;CAqFrB"}
1
+ {"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,mBAAmB;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+DAA+D;IAC/D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qEAAqE;IACrE,iBAAiB,CAAC,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;IAC7C,iEAAiE;IACjE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,mFAAmF;IACnF,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,qBAAa,cAAe,SAAQ,KAAK;aAGvB,MAAM,EAAE,MAAM;aACd,IAAI,EAAE,OAAO;gBAF7B,OAAO,EAAE,MAAM,EACC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,OAAO;CAI9B;AAED,qBAAa,eAAe;IAC3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAA2B;IAC9D,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAC,CAAS;IAChD,8DAA8D;IAC9D,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;gBAEhB,OAAO,GAAE,mBAAwB;IAkBvC,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAIrE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,cAAc,CAAC,EAAE,MAAM;IAI1D,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ;IAIrC,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO;IAIhC,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO;IAIlC,MAAM,CAAC,IAAI,EAAE,MAAM;YAIX,OAAO;CAyGrB"}
@@ -1,3 +1,4 @@
1
+ import { createHmac } from "node:crypto";
1
2
  import { resolveCredential } from "./local-config.js";
2
3
  export class WaberyApiError extends Error {
3
4
  status;
@@ -13,6 +14,7 @@ export class WaberyApiClient {
13
14
  baseUrl;
14
15
  apiVersion;
15
16
  projectIdProvider;
17
+ mcpServiceProofSecret;
16
18
  /** The project this client's key is scoped to, when known. */
17
19
  projectId;
18
20
  constructor(options = {}) {
@@ -25,13 +27,17 @@ export class WaberyApiClient {
25
27
  this.apiKey = resolved.apiKey;
26
28
  this.projectId = resolved.projectId;
27
29
  this.projectIdProvider = options.projectIdProvider;
30
+ this.mcpServiceProofSecret = options.mcpServiceProofSecret;
28
31
  this.baseUrl = normalizeBaseUrl(resolved.baseUrl ?? "https://api.wabery.com/v1");
29
32
  this.apiVersion = options.apiVersion ?? process.env.WABERY_API_VERSION;
30
33
  }
31
34
  async get(path, query) {
32
35
  return this.request("GET", path, undefined, query);
33
36
  }
34
- async post(path, body) {
37
+ async post(path, body, idempotencyKey) {
38
+ return this.request("POST", path, body, undefined, idempotencyKey);
39
+ }
40
+ async postForm(path, body) {
35
41
  return this.request("POST", path, body);
36
42
  }
37
43
  async put(path, body) {
@@ -43,7 +49,7 @@ export class WaberyApiClient {
43
49
  async delete(path) {
44
50
  return this.request("DELETE", path);
45
51
  }
46
- async request(method, path, body, query) {
52
+ async request(method, path, body, query, idempotencyKey) {
47
53
  const url = new URL(`${this.baseUrl}${path.startsWith("/") ? path : `/${path}`}`);
48
54
  for (const [key, value] of Object.entries(query ?? {})) {
49
55
  if (value !== undefined)
@@ -61,17 +67,31 @@ export class WaberyApiClient {
61
67
  else {
62
68
  headers.authorization = `Bearer ${this.apiKey}`;
63
69
  }
70
+ if (this.mcpServiceProofSecret) {
71
+ headers["x-wabery-mcp-service-proof"] = createHmac("sha256", this.mcpServiceProofSecret)
72
+ .update(this.apiKey)
73
+ .digest("base64url");
74
+ }
64
75
  if (this.apiVersion)
65
76
  headers["wabery-version"] = this.apiVersion;
66
77
  const projectId = this.projectIdProvider?.() ?? this.projectId;
67
78
  if (projectId)
68
79
  headers["x-wabery-project-id"] = projectId;
69
- if (body !== undefined)
80
+ if (method === "POST" && path.startsWith("/broadcasts")) {
81
+ headers["idempotency-key"] =
82
+ idempotencyKey ?? globalThis.crypto.randomUUID();
83
+ }
84
+ if (body !== undefined && !(body instanceof FormData)) {
70
85
  headers["content-type"] = "application/json";
86
+ }
71
87
  const init = {
72
88
  method,
73
89
  headers,
74
- body: body === undefined ? undefined : JSON.stringify(body),
90
+ body: body === undefined
91
+ ? undefined
92
+ : body instanceof FormData
93
+ ? body
94
+ : JSON.stringify(body),
75
95
  };
76
96
  // Transient gateway errors (Cloudflare 502/503/504) and network blips are
77
97
  // retryable — the request usually never reached the origin. Back off and
@@ -1 +1 @@
1
- {"version":3,"file":"api-client.js","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"api-client.js","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"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"}
package/dist/cli.js CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { spawn } from "node:child_process";
3
3
  import { createHmac } from "node:crypto";
4
+ import { readFileSync } from "node:fs";
4
5
  import { readFile, writeFile } from "node:fs/promises";
5
6
  import { platform } from "node:os";
6
7
  import { stdin } from "node:process";
@@ -112,6 +113,10 @@ async function main() {
112
113
  await handleContacts(client, subcommand, rest, parsed.flags);
113
114
  return;
114
115
  }
116
+ if (command === "broadcasts") {
117
+ await handleBroadcasts(client, subcommand, rest, parsed.flags);
118
+ return;
119
+ }
115
120
  if (command === "conversations") {
116
121
  await handleConversations(client, subcommand, rest, parsed.flags);
117
122
  return;
@@ -817,6 +822,58 @@ async function handleContacts(client, subcommand, rest, flags) {
817
822
  }));
818
823
  return;
819
824
  }
825
+ if (subcommand === "import") {
826
+ const path = rest[0] ?? stringFlag(flags, "file");
827
+ const filename = requiredValue(path, "file");
828
+ const file = readFileSync(filename);
829
+ const form = new FormData();
830
+ form.set("file", new Blob([file], { type: "text/csv" }), filename);
831
+ form.set("mapping", JSON.stringify(optionalJsonFlag(flags, "mapping") ?? {
832
+ phone: requiredStringFlag(flags, "phone-column"),
833
+ name: stringFlag(flags, "name-column"),
834
+ preferredLanguage: stringFlag(flags, "language-column"),
835
+ tags: stringFlag(flags, "tags-column"),
836
+ metadata: {},
837
+ }));
838
+ form.set("defaults", JSON.stringify(optionalJsonFlag(flags, "defaults") ?? {}));
839
+ const consent = optionalJsonFlag(flags, "consent");
840
+ if (consent)
841
+ form.set("consent", JSON.stringify(consent));
842
+ const result = await client.postForm("/contact-imports", form);
843
+ await printJson(result);
844
+ return;
845
+ }
846
+ if (subcommand === "imports") {
847
+ await printJson(await client.get("/contact-imports", {
848
+ limit: numberFlag(flags, "limit"),
849
+ starting_after: stringFlag(flags, "starting-after"),
850
+ }));
851
+ return;
852
+ }
853
+ if (subcommand === "import-status") {
854
+ const importId = rest[0] ?? stringFlag(flags, "import-id");
855
+ await printJson(await client.get(`/contact-imports/${requiredValue(importId, "import-id")}`));
856
+ return;
857
+ }
858
+ if (subcommand === "consent") {
859
+ const contactId = rest[0] ?? stringFlag(flags, "contact-id");
860
+ await printJson(await client.post(`/contacts/${requiredValue(contactId, "contact-id")}/consents`, optionalJsonFlag(flags, "body") ?? {
861
+ categories: requiredStringFlag(flags, "categories")
862
+ .split(",")
863
+ .map((value) => value.trim().toUpperCase()),
864
+ source: requiredStringFlag(flags, "source"),
865
+ method: stringFlag(flags, "method"),
866
+ }));
867
+ return;
868
+ }
869
+ if (subcommand === "suppress") {
870
+ const contactId = rest[0] ?? stringFlag(flags, "contact-id");
871
+ await printJson(await client.post(`/contacts/${requiredValue(contactId, "contact-id")}/suppressions`, optionalJsonFlag(flags, "body") ?? {
872
+ category: stringFlag(flags, "category") ?? null,
873
+ reason: stringFlag(flags, "reason"),
874
+ }));
875
+ return;
876
+ }
820
877
  if (subcommand === "update") {
821
878
  const contactId = rest[0] ?? stringFlag(flags, "contact-id");
822
879
  await printJson(await client.patch(`/contacts/${requiredValue(contactId, "contact-id")}`, {
@@ -831,7 +888,51 @@ async function handleContacts(client, subcommand, rest, flags) {
831
888
  await printJson(await client.delete(`/contacts/${requiredValue(contactId, "contact-id")}${erase ? "?erase=true" : ""}`));
832
889
  return;
833
890
  }
834
- throw new Error("Usage: wabery contacts list|get|enroll|update|unenroll --phone <phone> --preferred-language <lang> --metadata <json> --project-id <id>");
891
+ throw new Error("Usage: wabery contacts list|get|enroll|update|unenroll|import|imports|import-status|consent|suppress");
892
+ }
893
+ async function handleBroadcasts(client, subcommand, rest, flags) {
894
+ const idempotencyKey = stringFlag(flags, "idempotency-key");
895
+ if (subcommand === "list") {
896
+ await printJson(await client.get("/broadcasts", {
897
+ limit: numberFlag(flags, "limit"),
898
+ starting_after: stringFlag(flags, "starting-after"),
899
+ status: stringFlag(flags, "status"),
900
+ }));
901
+ return;
902
+ }
903
+ const id = rest[0] ?? stringFlag(flags, "broadcast-id");
904
+ if (subcommand === "get") {
905
+ await printJson(await client.get(`/broadcasts/${requiredValue(id, "broadcast-id")}`));
906
+ return;
907
+ }
908
+ if (subcommand === "create") {
909
+ await printJson(await client.post("/broadcasts", requiredJsonFlag(flags, "body"), idempotencyKey));
910
+ return;
911
+ }
912
+ if (subcommand === "prepare") {
913
+ await printJson(await client.post(`/broadcasts/${requiredValue(id, "broadcast-id")}/audience`, requiredJsonFlag(flags, "body"), idempotencyKey));
914
+ return;
915
+ }
916
+ if (["send", "cancel", "duplicate"].includes(subcommand ?? "")) {
917
+ await printJson(await client.post(`/broadcasts/${requiredValue(id, "broadcast-id")}/${subcommand}`, undefined, idempotencyKey));
918
+ return;
919
+ }
920
+ if (subcommand === "schedule") {
921
+ await printJson(await client.post(`/broadcasts/${requiredValue(id, "broadcast-id")}/schedule`, optionalJsonFlag(flags, "body") ?? {
922
+ scheduledAt: requiredStringFlag(flags, "scheduled-at"),
923
+ timezone: requiredStringFlag(flags, "timezone"),
924
+ }, idempotencyKey));
925
+ return;
926
+ }
927
+ if (subcommand === "recipients") {
928
+ await printJson(await client.get(`/broadcasts/${requiredValue(id, "broadcast-id")}/recipients`, {
929
+ limit: numberFlag(flags, "limit"),
930
+ starting_after: stringFlag(flags, "starting-after"),
931
+ status: stringFlag(flags, "status"),
932
+ }));
933
+ return;
934
+ }
935
+ throw new Error("Usage: wabery broadcasts list|get|create|prepare|send|schedule|cancel|duplicate|recipients [--idempotency-key <key>]");
835
936
  }
836
937
  async function handleConversations(client, subcommand, rest, flags) {
837
938
  if (subcommand === "list") {
@@ -1504,7 +1605,18 @@ function booleanFlag(flags, key) {
1504
1605
  }
1505
1606
  function optionalJsonFlag(flags, key) {
1506
1607
  const value = stringFlag(flags, key);
1507
- return value ? JSON.parse(value) : undefined;
1608
+ if (!value)
1609
+ return undefined;
1610
+ const json = value.startsWith("@")
1611
+ ? readFileSync(value.slice(1), "utf8")
1612
+ : value;
1613
+ return JSON.parse(json);
1614
+ }
1615
+ function requiredJsonFlag(flags, key) {
1616
+ const value = optionalJsonFlag(flags, key);
1617
+ if (value === undefined)
1618
+ throw new Error(`Missing required --${key}`);
1619
+ return value;
1508
1620
  }
1509
1621
  function requiredValue(value, name) {
1510
1622
  if (!value)
@@ -1643,6 +1755,22 @@ Projects and contacts:
1643
1755
  wabery contacts unenroll <contact_id>
1644
1756
  wabery contacts delete <contact_id> --erase
1645
1757
  wabery contacts list
1758
+ wabery contacts import contacts.csv --phone-column phone [--name-column name] [--language-column language] [--tags-column tags] [--mapping @mapping.json] [--consent @consent.json]
1759
+ wabery contacts imports [--limit 25]
1760
+ wabery contacts import-status <import_id>
1761
+ wabery contacts consent <contact_id> --categories MARKETING,UTILITY --source checkout
1762
+ wabery contacts suppress <contact_id> [--category MARKETING] [--reason "Requested by customer"]
1763
+
1764
+ Broadcasts:
1765
+ wabery broadcasts list [--status ready]
1766
+ wabery broadcasts get <broadcast_id>
1767
+ wabery broadcasts create --body @broadcast.json
1768
+ wabery broadcasts prepare <broadcast_id> --body @audience.json
1769
+ wabery broadcasts recipients <broadcast_id> [--limit 100] [--starting-after <id>] [--status skipped]
1770
+ wabery broadcasts send <broadcast_id>
1771
+ wabery broadcasts schedule <broadcast_id> --scheduled-at 2026-08-01T10:00:00Z --timezone UTC
1772
+ wabery broadcasts cancel <broadcast_id>
1773
+ wabery broadcasts duplicate <broadcast_id>
1646
1774
 
1647
1775
  Templates, messages, and conversations:
1648
1776
  wabery templates list --channel-id <id>