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/ARCHITECTURE.md +26 -12
- package/README.md +65 -10
- package/dist/index.cli.js +561 -10
- package/dist/index.d.mts +169 -1
- package/dist/index.d.ts +169 -1
- package/dist/index.js +258 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +258 -0
- package/dist/index.mjs.map +1 -1
- package/docs/CLI.md +392 -0
- package/docs/TROUBLESHOOTING.md +2 -2
- package/package.json +1 -1
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
|
|
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
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
752
|
-
|
|
753
|
-
-
|
|
754
|
-
-
|
|
755
|
-
|
|
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();
|