waba-toolkit 0.3.1 → 0.4.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/dist/index.cli.js CHANGED
@@ -25,7 +25,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
25
25
 
26
26
  // src/cli/index.ts
27
27
  var import_commander = require("commander");
28
- var import_node_fs3 = require("fs");
28
+ var import_node_fs5 = require("fs");
29
29
  var import_node_path2 = require("path");
30
30
 
31
31
  // src/cli/commands/configure.ts
@@ -373,6 +373,7 @@ Valid fields: ${validFields.join(", ")}`
373
373
  }
374
374
 
375
375
  // src/api/client.ts
376
+ var import_node_fs2 = require("fs");
376
377
  var DEFAULT_API_VERSION = "v22.0";
377
378
  var DEFAULT_BASE_URL = "https://graph.facebook.com";
378
379
  var WABAApiClient = class {
@@ -507,6 +508,263 @@ var WABAApiClient = class {
507
508
  const url = `${this.baseUrl}/${this.apiVersion}/${this.phoneNumberId}/messages`;
508
509
  return this.makeRequest(url, "POST", payload);
509
510
  }
511
+ /**
512
+ * Create a new WhatsApp Flow.
513
+ *
514
+ * @param wabaId - WhatsApp Business Account ID
515
+ * @param options - Flow creation options
516
+ * @throws {WABAAuthError} - Authentication failure (invalid token)
517
+ * @throws {WABASendError} - Flow creation failure
518
+ * @throws {WABANetworkError} - Network/connection failures
519
+ */
520
+ async createFlow(wabaId, options) {
521
+ const url = `${this.baseUrl}/${this.apiVersion}/${wabaId}/flows`;
522
+ const formData = new FormData();
523
+ formData.append("name", options.name);
524
+ const categories = options.categories ?? ["OTHER"];
525
+ formData.append("categories", JSON.stringify(categories));
526
+ if (options.endpointUri) {
527
+ formData.append("endpoint_uri", options.endpointUri);
528
+ }
529
+ if (options.cloneFlowId) {
530
+ formData.append("clone_flow_id", options.cloneFlowId);
531
+ }
532
+ return this.makeFormDataRequest(url, formData);
533
+ }
534
+ /**
535
+ * Upload or update Flow JSON for an existing flow.
536
+ *
537
+ * @param flowId - Flow ID to update
538
+ * @param filePath - Path to the Flow JSON file
539
+ * @throws {WABAAuthError} - Authentication failure (invalid token)
540
+ * @throws {WABASendError} - Flow update failure
541
+ * @throws {WABANetworkError} - Network/connection failures
542
+ */
543
+ async updateFlowJson(flowId, filePath) {
544
+ const url = `${this.baseUrl}/${this.apiVersion}/${flowId}/assets`;
545
+ const fileContent = (0, import_node_fs2.readFileSync)(filePath, "utf8");
546
+ const blob = new Blob([fileContent], { type: "application/json" });
547
+ const formData = new FormData();
548
+ formData.append("file", blob, "flow.json");
549
+ formData.append("name", "flow.json");
550
+ formData.append("asset_type", "FLOW_JSON");
551
+ return this.makeFormDataRequest(url, formData);
552
+ }
553
+ /**
554
+ * Publish a flow. This action is irreversible.
555
+ * Once published, the flow and its assets become immutable.
556
+ * To update a published flow, create a new flow with clone_flow_id.
557
+ *
558
+ * @param flowId - Flow ID to publish
559
+ * @throws {WABAAuthError} - Authentication failure (invalid token)
560
+ * @throws {WABASendError} - Flow publish failure
561
+ * @throws {WABANetworkError} - Network/connection failures
562
+ */
563
+ async publishFlow(flowId) {
564
+ const url = `${this.baseUrl}/${this.apiVersion}/${flowId}/publish`;
565
+ let response;
566
+ try {
567
+ response = await fetch(url, {
568
+ method: "POST",
569
+ headers: {
570
+ Authorization: `Bearer ${this.accessToken}`
571
+ }
572
+ });
573
+ } catch (error) {
574
+ throw new WABANetworkError(
575
+ `Failed to make request: ${error instanceof Error ? error.message : "Unknown error"}`,
576
+ error instanceof Error ? error : void 0
577
+ );
578
+ }
579
+ if (response.ok) {
580
+ return await response.json();
581
+ }
582
+ const errorBody = await response.json().catch(() => null);
583
+ if (response.status === 401 || response.status === 403) {
584
+ throw new WABAAuthError(
585
+ `Authentication failed: ${response.status} ${response.statusText}`,
586
+ response.status
587
+ );
588
+ }
589
+ throw new WABASendError(
590
+ `Request failed: ${response.status} ${response.statusText}`,
591
+ response.status,
592
+ errorBody
593
+ );
594
+ }
595
+ /**
596
+ * List all flows for a WhatsApp Business Account.
597
+ *
598
+ * @param wabaId - WhatsApp Business Account ID
599
+ * @throws {WABAAuthError} - Authentication failure (invalid token)
600
+ * @throws {WABASendError} - Request failure
601
+ * @throws {WABANetworkError} - Network/connection failures
602
+ */
603
+ async listFlows(wabaId) {
604
+ const url = `${this.baseUrl}/${this.apiVersion}/${wabaId}/flows`;
605
+ let response;
606
+ try {
607
+ response = await fetch(url, {
608
+ method: "GET",
609
+ headers: {
610
+ Authorization: `Bearer ${this.accessToken}`
611
+ }
612
+ });
613
+ } catch (error) {
614
+ throw new WABANetworkError(
615
+ `Failed to make request: ${error instanceof Error ? error.message : "Unknown error"}`,
616
+ error instanceof Error ? error : void 0
617
+ );
618
+ }
619
+ if (response.ok) {
620
+ return await response.json();
621
+ }
622
+ const errorBody = await response.json().catch(() => null);
623
+ if (response.status === 401 || response.status === 403) {
624
+ throw new WABAAuthError(
625
+ `Authentication failed: ${response.status} ${response.statusText}`,
626
+ response.status
627
+ );
628
+ }
629
+ throw new WABASendError(
630
+ `Request failed: ${response.status} ${response.statusText}`,
631
+ response.status,
632
+ errorBody
633
+ );
634
+ }
635
+ /**
636
+ * List all message templates for a WhatsApp Business Account.
637
+ *
638
+ * @param wabaId - WhatsApp Business Account ID
639
+ * @throws {WABAAuthError} - Authentication failure (invalid token)
640
+ * @throws {WABASendError} - Request failure
641
+ * @throws {WABANetworkError} - Network/connection failures
642
+ */
643
+ async listTemplates(wabaId) {
644
+ const url = `${this.baseUrl}/${this.apiVersion}/${wabaId}/message_templates`;
645
+ return this.makeGetRequest(url);
646
+ }
647
+ /**
648
+ * Create a new message template.
649
+ *
650
+ * @param wabaId - WhatsApp Business Account ID
651
+ * @param request - Template creation request
652
+ * @throws {WABAAuthError} - Authentication failure (invalid token)
653
+ * @throws {WABASendError} - Template creation failure
654
+ * @throws {WABANetworkError} - Network/connection failures
655
+ */
656
+ async createTemplate(wabaId, request) {
657
+ const url = `${this.baseUrl}/${this.apiVersion}/${wabaId}/message_templates`;
658
+ return this.makeRequest(url, "POST", request);
659
+ }
660
+ /**
661
+ * Delete a message template by name.
662
+ *
663
+ * @param wabaId - WhatsApp Business Account ID
664
+ * @param templateName - Name of the template to delete
665
+ * @throws {WABAAuthError} - Authentication failure (invalid token)
666
+ * @throws {WABASendError} - Template deletion failure
667
+ * @throws {WABANetworkError} - Network/connection failures
668
+ */
669
+ async deleteTemplate(wabaId, templateName) {
670
+ const url = `${this.baseUrl}/${this.apiVersion}/${wabaId}/message_templates?name=${encodeURIComponent(templateName)}`;
671
+ return this.makeDeleteRequest(url);
672
+ }
673
+ async makeGetRequest(url) {
674
+ let response;
675
+ try {
676
+ response = await fetch(url, {
677
+ method: "GET",
678
+ headers: {
679
+ Authorization: `Bearer ${this.accessToken}`
680
+ }
681
+ });
682
+ } catch (error) {
683
+ throw new WABANetworkError(
684
+ `Failed to make request: ${error instanceof Error ? error.message : "Unknown error"}`,
685
+ error instanceof Error ? error : void 0
686
+ );
687
+ }
688
+ if (response.ok) {
689
+ return await response.json();
690
+ }
691
+ const errorBody = await response.json().catch(() => null);
692
+ if (response.status === 401 || response.status === 403) {
693
+ throw new WABAAuthError(
694
+ `Authentication failed: ${response.status} ${response.statusText}`,
695
+ response.status
696
+ );
697
+ }
698
+ throw new WABASendError(
699
+ `Request failed: ${response.status} ${response.statusText}`,
700
+ response.status,
701
+ errorBody
702
+ );
703
+ }
704
+ async makeDeleteRequest(url) {
705
+ let response;
706
+ try {
707
+ response = await fetch(url, {
708
+ method: "DELETE",
709
+ headers: {
710
+ Authorization: `Bearer ${this.accessToken}`
711
+ }
712
+ });
713
+ } catch (error) {
714
+ throw new WABANetworkError(
715
+ `Failed to make request: ${error instanceof Error ? error.message : "Unknown error"}`,
716
+ error instanceof Error ? error : void 0
717
+ );
718
+ }
719
+ if (response.ok) {
720
+ return await response.json();
721
+ }
722
+ const errorBody = await response.json().catch(() => null);
723
+ if (response.status === 401 || response.status === 403) {
724
+ throw new WABAAuthError(
725
+ `Authentication failed: ${response.status} ${response.statusText}`,
726
+ response.status
727
+ );
728
+ }
729
+ throw new WABASendError(
730
+ `Request failed: ${response.status} ${response.statusText}`,
731
+ response.status,
732
+ errorBody
733
+ );
734
+ }
735
+ async makeFormDataRequest(url, formData) {
736
+ let response;
737
+ try {
738
+ response = await fetch(url, {
739
+ method: "POST",
740
+ headers: {
741
+ Authorization: `Bearer ${this.accessToken}`
742
+ // Note: Do NOT set Content-Type header - fetch will set it with boundary for FormData
743
+ },
744
+ body: formData
745
+ });
746
+ } catch (error) {
747
+ throw new WABANetworkError(
748
+ `Failed to make request: ${error instanceof Error ? error.message : "Unknown error"}`,
749
+ error instanceof Error ? error : void 0
750
+ );
751
+ }
752
+ if (response.ok) {
753
+ return await response.json();
754
+ }
755
+ const errorBody = await response.json().catch(() => null);
756
+ if (response.status === 401 || response.status === 403) {
757
+ throw new WABAAuthError(
758
+ `Authentication failed: ${response.status} ${response.statusText}`,
759
+ response.status
760
+ );
761
+ }
762
+ throw new WABASendError(
763
+ `Request failed: ${response.status} ${response.statusText}`,
764
+ response.status,
765
+ errorBody
766
+ );
767
+ }
510
768
  async makeRequest(url, method, body) {
511
769
  let response;
512
770
  try {
@@ -587,7 +845,7 @@ async function deregisterPhone(options) {
587
845
  }
588
846
 
589
847
  // src/cli/commands/send.ts
590
- var import_node_fs2 = require("fs");
848
+ var import_node_fs3 = require("fs");
591
849
  async function sendText(options) {
592
850
  try {
593
851
  const accessToken = resolveAccessToken();
@@ -620,7 +878,7 @@ async function sendTemplate(options) {
620
878
  const config = getConfig();
621
879
  let templateData;
622
880
  try {
623
- const fileContent = (0, import_node_fs2.readFileSync)(options.file, "utf8");
881
+ const fileContent = (0, import_node_fs3.readFileSync)(options.file, "utf8");
624
882
  templateData = JSON.parse(fileContent);
625
883
  } catch (error) {
626
884
  throw new Error(
@@ -650,7 +908,7 @@ async function sendFile(options) {
650
908
  const config = getConfig();
651
909
  let payload;
652
910
  try {
653
- const fileContent = (0, import_node_fs2.readFileSync)(options.payload, "utf8");
911
+ const fileContent = (0, import_node_fs3.readFileSync)(options.payload, "utf8");
654
912
  payload = JSON.parse(fileContent);
655
913
  } catch (error) {
656
914
  throw new Error(
@@ -698,9 +956,183 @@ async function listPhones(options) {
698
956
  }
699
957
  }
700
958
 
959
+ // src/cli/commands/flow.ts
960
+ async function listFlows(options) {
961
+ try {
962
+ const accessToken = resolveAccessToken();
963
+ const wabaId = resolveWabaId(options.wabaId);
964
+ const config = getConfig();
965
+ const client = new WABAApiClient({
966
+ accessToken,
967
+ phoneNumberId: "",
968
+ // Not needed for flow operations
969
+ apiVersion: config.apiVersion
970
+ });
971
+ const response = await client.listFlows(wabaId);
972
+ printJson(response);
973
+ } catch (error) {
974
+ if (error instanceof WABASendError) {
975
+ console.error(formatError("Failed to list flows", error.errorPayload));
976
+ } else {
977
+ handleError(error);
978
+ }
979
+ process.exit(1);
980
+ }
981
+ }
982
+ async function createFlow(options) {
983
+ try {
984
+ const accessToken = resolveAccessToken();
985
+ const wabaId = resolveWabaId(options.wabaId);
986
+ const config = getConfig();
987
+ const client = new WABAApiClient({
988
+ accessToken,
989
+ phoneNumberId: "",
990
+ // Not needed for flow operations
991
+ apiVersion: config.apiVersion
992
+ });
993
+ let categories;
994
+ if (options.categories) {
995
+ categories = options.categories.split(",").map((c) => c.trim());
996
+ }
997
+ const response = await client.createFlow(wabaId, {
998
+ name: options.name,
999
+ categories,
1000
+ endpointUri: options.endpointUri,
1001
+ cloneFlowId: options.cloneFlowId
1002
+ });
1003
+ printJson(response);
1004
+ } catch (error) {
1005
+ if (error instanceof WABASendError) {
1006
+ console.error(formatError("Failed to create flow", error.errorPayload));
1007
+ } else {
1008
+ handleError(error);
1009
+ }
1010
+ process.exit(1);
1011
+ }
1012
+ }
1013
+ async function updateFlow(options) {
1014
+ try {
1015
+ const accessToken = resolveAccessToken();
1016
+ const config = getConfig();
1017
+ const client = new WABAApiClient({
1018
+ accessToken,
1019
+ phoneNumberId: "",
1020
+ // Not needed for flow operations
1021
+ apiVersion: config.apiVersion
1022
+ });
1023
+ const response = await client.updateFlowJson(options.flowId, options.file);
1024
+ printJson(response);
1025
+ } catch (error) {
1026
+ if (error instanceof WABASendError) {
1027
+ console.error(formatError("Failed to update flow", error.errorPayload));
1028
+ } else {
1029
+ handleError(error);
1030
+ }
1031
+ process.exit(1);
1032
+ }
1033
+ }
1034
+ async function publishFlow(options) {
1035
+ try {
1036
+ const accessToken = resolveAccessToken();
1037
+ const config = getConfig();
1038
+ const client = new WABAApiClient({
1039
+ accessToken,
1040
+ phoneNumberId: "",
1041
+ // Not needed for flow operations
1042
+ apiVersion: config.apiVersion
1043
+ });
1044
+ const response = await client.publishFlow(options.flowId);
1045
+ printJson(response);
1046
+ } catch (error) {
1047
+ if (error instanceof WABASendError) {
1048
+ console.error(formatError("Failed to publish flow", error.errorPayload));
1049
+ } else {
1050
+ handleError(error);
1051
+ }
1052
+ process.exit(1);
1053
+ }
1054
+ }
1055
+
1056
+ // src/cli/commands/template.ts
1057
+ var import_node_fs4 = require("fs");
1058
+ async function listTemplates(options) {
1059
+ try {
1060
+ const accessToken = resolveAccessToken();
1061
+ const wabaId = resolveWabaId(options.wabaId);
1062
+ const config = getConfig();
1063
+ const client = new WABAApiClient({
1064
+ accessToken,
1065
+ phoneNumberId: "",
1066
+ // Not needed for template operations
1067
+ apiVersion: config.apiVersion
1068
+ });
1069
+ const response = await client.listTemplates(wabaId);
1070
+ printJson(response);
1071
+ } catch (error) {
1072
+ if (error instanceof WABASendError) {
1073
+ console.error(formatError("Failed to list templates", error.errorPayload));
1074
+ } else {
1075
+ handleError(error);
1076
+ }
1077
+ process.exit(1);
1078
+ }
1079
+ }
1080
+ async function createTemplate(options) {
1081
+ try {
1082
+ const accessToken = resolveAccessToken();
1083
+ const wabaId = resolveWabaId(options.wabaId);
1084
+ const config = getConfig();
1085
+ const client = new WABAApiClient({
1086
+ accessToken,
1087
+ phoneNumberId: "",
1088
+ // Not needed for template operations
1089
+ apiVersion: config.apiVersion
1090
+ });
1091
+ const fileContent = (0, import_node_fs4.readFileSync)(options.file, "utf8");
1092
+ const templateData = JSON.parse(fileContent);
1093
+ const request = {
1094
+ name: options.name,
1095
+ ...templateData
1096
+ };
1097
+ const response = await client.createTemplate(wabaId, request);
1098
+ printJson(response);
1099
+ } catch (error) {
1100
+ if (error instanceof SyntaxError) {
1101
+ console.error(formatError("Invalid JSON in template file", { message: error.message }));
1102
+ } else if (error instanceof WABASendError) {
1103
+ console.error(formatError("Failed to create template", error.errorPayload));
1104
+ } else {
1105
+ handleError(error);
1106
+ }
1107
+ process.exit(1);
1108
+ }
1109
+ }
1110
+ async function deleteTemplate(options) {
1111
+ try {
1112
+ const accessToken = resolveAccessToken();
1113
+ const wabaId = resolveWabaId(options.wabaId);
1114
+ const config = getConfig();
1115
+ const client = new WABAApiClient({
1116
+ accessToken,
1117
+ phoneNumberId: "",
1118
+ // Not needed for template operations
1119
+ apiVersion: config.apiVersion
1120
+ });
1121
+ const response = await client.deleteTemplate(wabaId, options.name);
1122
+ printJson(response);
1123
+ } catch (error) {
1124
+ if (error instanceof WABASendError) {
1125
+ console.error(formatError("Failed to delete template", error.errorPayload));
1126
+ } else {
1127
+ handleError(error);
1128
+ }
1129
+ process.exit(1);
1130
+ }
1131
+ }
1132
+
701
1133
  // src/cli/index.ts
702
1134
  var packageJson = JSON.parse(
703
- (0, import_node_fs3.readFileSync)((0, import_node_path2.join)(__dirname, "../package.json"), "utf8")
1135
+ (0, import_node_fs5.readFileSync)((0, import_node_path2.join)(__dirname, "../package.json"), "utf8")
704
1136
  );
705
1137
  var program = new import_commander.Command();
706
1138
  program.name("waba-toolkit").description("WhatsApp Business API Toolkit - CLI for webhooks, media, and messaging").version(packageJson.version).addHelpText("after", `
@@ -710,6 +1142,13 @@ Examples:
710
1142
  $ waba-toolkit list-phones --waba-id 123 # List phone numbers
711
1143
  $ waba-toolkit send text --to 1234567890 --message "Hello"
712
1144
  $ waba-toolkit register --pin 123456
1145
+ $ waba-toolkit list-flows --waba-id 123 # List all flows
1146
+ $ waba-toolkit create flow --name "My Flow" # Create a new WhatsApp Flow
1147
+ $ waba-toolkit update flow --flow-id 123 --file flow.json # Upload Flow JSON
1148
+ $ waba-toolkit publish flow --flow-id 123 # Publish flow (irreversible)
1149
+ $ waba-toolkit list-templates --waba-id 123 # List all templates
1150
+ $ waba-toolkit create template --name my_tpl --file template.json
1151
+ $ waba-toolkit delete template --name old_tpl
713
1152
 
714
1153
  Environment Variables:
715
1154
  WABA_TOOLKIT_ACCESS_TOKEN Access token for WhatsApp API
@@ -745,14 +1184,16 @@ configCommand.command("set <field> <value>").description("Update specific config
745
1184
  Examples:
746
1185
  $ waba-toolkit config set access-token EAABsbCS...
747
1186
  $ waba-toolkit config set waba-id 1234567890
1187
+ $ waba-toolkit config set default-phone-number-id 9876543210
748
1188
  $ waba-toolkit config set api-version v22.0
1189
+ $ waba-toolkit config set business-id 1122334455
749
1190
 
750
1191
  Valid fields:
751
- - access-token
752
- - default-phone-number-id
753
- - api-version
754
- - waba-id
755
- - business-id
1192
+ access-token Your Meta access token (required for API calls)
1193
+ default-phone-number-id Default phone number ID for sending messages
1194
+ waba-id WhatsApp Business Account ID (for flows/templates)
1195
+ api-version Graph API version (default: v22.0)
1196
+ business-id Meta Business Portfolio ID
756
1197
  `).action(setConfigField);
757
1198
  program.command("register").description("Register phone number with WhatsApp Business API").option("--bpid <phone-number-id>", "Phone number ID (overrides default)").option("--phone-number-id <id>", "Phone number ID (alias for --bpid)").requiredOption("--pin <pin>", "Six-digit PIN for registration").addHelpText("after", `
758
1199
  Examples:
@@ -860,4 +1301,114 @@ Payload JSON format (image message):
860
1301
  payload: options.payload
861
1302
  });
862
1303
  });
1304
+ program.command("list-flows").description("List all WhatsApp Flows for a WABA").option("--waba-id <id>", "WhatsApp Business Account ID (overrides default)").addHelpText("after", `
1305
+ Examples:
1306
+ $ waba-toolkit list-flows --waba-id 1234567890
1307
+ $ WABA_TOOLKIT_WABA_ID=1234567890 waba-toolkit list-flows
1308
+
1309
+ Output:
1310
+ Returns JSON with flow details including name, status, categories, and validation errors.
1311
+ `).action(async (options) => {
1312
+ await listFlows({
1313
+ wabaId: options.wabaId
1314
+ });
1315
+ });
1316
+ var createCommand = program.command("create").description("Create WhatsApp Business API resources");
1317
+ createCommand.command("flow").description("Create a new WhatsApp Flow").requiredOption("--name <name>", "Name of the flow").option("--categories <categories>", "Comma-separated list of categories (default: OTHER)").option("--endpoint-uri <uri>", "Endpoint URI for the flow").option("--clone-flow-id <id>", "Clone from existing flow ID").option("--waba-id <id>", "WhatsApp Business Account ID (overrides default)").addHelpText("after", `
1318
+ Examples:
1319
+ $ waba-toolkit create flow --name "My Flow"
1320
+ $ waba-toolkit create flow --name "Lead Gen Flow" --categories LEAD_GENERATION
1321
+ $ waba-toolkit create flow --name "Multi-Category" --categories LEAD_GENERATION,SURVEY
1322
+ $ waba-toolkit create flow --name "Clone" --clone-flow-id 123456789
1323
+
1324
+ Categories:
1325
+ SIGN_UP, SIGN_IN, APPOINTMENT_BOOKING, LEAD_GENERATION,
1326
+ CONTACT_US, CUSTOMER_SUPPORT, SURVEY, OTHER
1327
+ `).action(async (options) => {
1328
+ await createFlow({
1329
+ name: options.name,
1330
+ categories: options.categories,
1331
+ endpointUri: options.endpointUri,
1332
+ cloneFlowId: options.cloneFlowId,
1333
+ wabaId: options.wabaId
1334
+ });
1335
+ });
1336
+ var updateCommand = program.command("update").description("Update WhatsApp Business API resources");
1337
+ updateCommand.command("flow").description("Upload or update Flow JSON for an existing flow").requiredOption("--flow-id <id>", "Flow ID to update").requiredOption("--file <path>", "Path to the Flow JSON file").addHelpText("after", `
1338
+ Examples:
1339
+ $ waba-toolkit update flow --flow-id 123456789 --file ./my-flow.json
1340
+
1341
+ Output:
1342
+ Returns JSON with success status and any validation errors from the Flow JSON.
1343
+ Validation errors include line/column numbers to help locate issues.
1344
+ `).action(async (options) => {
1345
+ await updateFlow({
1346
+ flowId: options.flowId,
1347
+ file: options.file
1348
+ });
1349
+ });
1350
+ var publishCommand = program.command("publish").description("Publish WhatsApp Business API resources");
1351
+ publishCommand.command("flow").description("Publish a flow (irreversible)").requiredOption("--flow-id <id>", "Flow ID to publish").addHelpText("after", `
1352
+ Examples:
1353
+ $ waba-toolkit publish flow --flow-id 123456789
1354
+
1355
+ WARNING: Publishing a flow is IRREVERSIBLE.
1356
+ Once published, the flow and its assets become immutable.
1357
+ To update a published flow, create a new flow with --clone-flow-id.
1358
+ `).action(async (options) => {
1359
+ await publishFlow({
1360
+ flowId: options.flowId
1361
+ });
1362
+ });
1363
+ program.command("list-templates").description("List all message templates for a WABA").option("--waba-id <id>", "WhatsApp Business Account ID (overrides default)").addHelpText("after", `
1364
+ Examples:
1365
+ $ waba-toolkit list-templates --waba-id 1234567890
1366
+ $ WABA_TOOLKIT_WABA_ID=1234567890 waba-toolkit list-templates
1367
+
1368
+ Output:
1369
+ Returns JSON with template details including name, status, category, and components.
1370
+ `).action(async (options) => {
1371
+ await listTemplates({
1372
+ wabaId: options.wabaId
1373
+ });
1374
+ });
1375
+ createCommand.command("template").description("Create a new message template").requiredOption("--name <name>", "Template name (lowercase, underscores, no spaces)").requiredOption("--file <path>", "Path to template JSON file").option("--waba-id <id>", "WhatsApp Business Account ID (overrides default)").addHelpText("after", `
1376
+ Examples:
1377
+ $ waba-toolkit create template --name my_template --file ./template.json
1378
+ $ waba-toolkit create template --name order_update --file ./order-template.json --waba-id 123
1379
+
1380
+ Template JSON format:
1381
+ {
1382
+ "language": "en_US",
1383
+ "category": "MARKETING",
1384
+ "components": [
1385
+ {
1386
+ "type": "BODY",
1387
+ "text": "Hello {{1}}, your order {{2}} is ready!"
1388
+ }
1389
+ ]
1390
+ }
1391
+
1392
+ Categories:
1393
+ AUTHENTICATION, MARKETING, UTILITY
1394
+ `).action(async (options) => {
1395
+ await createTemplate({
1396
+ name: options.name,
1397
+ file: options.file,
1398
+ wabaId: options.wabaId
1399
+ });
1400
+ });
1401
+ var deleteCommand = program.command("delete").description("Delete WhatsApp Business API resources");
1402
+ deleteCommand.command("template").description("Delete a message template by name").requiredOption("--name <name>", "Template name to delete").option("--waba-id <id>", "WhatsApp Business Account ID (overrides default)").addHelpText("after", `
1403
+ Examples:
1404
+ $ waba-toolkit delete template --name my_template
1405
+ $ waba-toolkit delete template --name old_promo --waba-id 1234567890
1406
+
1407
+ Note: Deleting a template removes all language versions of that template.
1408
+ `).action(async (options) => {
1409
+ await deleteTemplate({
1410
+ name: options.name,
1411
+ wabaId: options.wabaId
1412
+ });
1413
+ });
863
1414
  program.parse();