whatsapp-cloud 0.0.4 → 0.0.5
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 +6 -0
- package/agent_docs/INCOMING_MESSAGES_BRAINSTORM.md +500 -0
- package/cloud-api-docs/webhooks/endpoint.md +112 -0
- package/cloud-api-docs/webhooks/overview.md +154 -0
- package/package.json +6 -2
- package/src/client/HttpClient.ts +43 -6
- package/src/client/WhatsAppClient.ts +3 -0
- package/src/examples/main.ts +9 -0
- package/src/examples/template.ts +134 -0
- package/src/schemas/client.ts +2 -2
- package/src/schemas/index.ts +1 -0
- package/src/schemas/templates/component.ts +145 -0
- package/src/schemas/templates/index.ts +4 -0
- package/src/schemas/templates/request.ts +78 -0
- package/src/schemas/templates/response.ts +64 -0
- package/src/services/accounts/AccountsClient.ts +6 -14
- package/src/services/accounts/AccountsService.ts +19 -21
- package/src/services/accounts/methods/list-phone-numbers.ts +1 -2
- package/src/services/business/BusinessClient.ts +1 -9
- package/src/services/business/BusinessService.ts +19 -21
- package/src/services/business/methods/list-accounts.ts +1 -2
- package/src/services/messages/MessagesClient.ts +2 -6
- package/src/services/messages/MessagesService.ts +42 -22
- package/src/services/templates/TemplatesClient.ts +35 -0
- package/src/services/templates/TemplatesService.ts +117 -0
- package/src/services/templates/index.ts +3 -0
- package/src/services/templates/methods/create.ts +27 -0
- package/src/services/templates/methods/delete.ts +38 -0
- package/src/services/templates/methods/get.ts +23 -0
- package/src/services/templates/methods/list.ts +36 -0
- package/src/services/templates/methods/update.ts +35 -0
- package/src/types/index.ts +1 -0
- package/src/types/templates/component.ts +33 -0
- package/src/types/templates/index.ts +4 -0
- package/src/types/templates/request.ts +28 -0
- package/src/types/templates/response.ts +34 -0
- package/tsconfig.json +2 -3
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { componentSchema } from "./component";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Schema for template response (single template)
|
|
6
|
+
*/
|
|
7
|
+
export const templateResponseSchema = z.object({
|
|
8
|
+
id: z.string(),
|
|
9
|
+
name: z.string(),
|
|
10
|
+
language: z.string(),
|
|
11
|
+
status: z.string(),
|
|
12
|
+
category: z.string(),
|
|
13
|
+
components: z.array(componentSchema),
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Schema for create template response
|
|
18
|
+
*/
|
|
19
|
+
export const createTemplateResponseSchema = z.object({
|
|
20
|
+
id: z.string(),
|
|
21
|
+
status: z.string(),
|
|
22
|
+
category: z.string(),
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Schema for list templates response
|
|
27
|
+
*/
|
|
28
|
+
export const templateListItemSchema = z.object({
|
|
29
|
+
id: z.string(),
|
|
30
|
+
name: z.string(),
|
|
31
|
+
language: z.string(),
|
|
32
|
+
status: z.string(),
|
|
33
|
+
category: z.string(),
|
|
34
|
+
components: z.array(componentSchema),
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
export const listTemplatesResponseSchema = z.object({
|
|
38
|
+
data: z.array(templateListItemSchema),
|
|
39
|
+
paging: z
|
|
40
|
+
.object({
|
|
41
|
+
cursors: z
|
|
42
|
+
.object({
|
|
43
|
+
before: z.string().optional(),
|
|
44
|
+
after: z.string().optional(),
|
|
45
|
+
})
|
|
46
|
+
.optional(),
|
|
47
|
+
})
|
|
48
|
+
.optional(),
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Schema for update template response
|
|
53
|
+
*/
|
|
54
|
+
export const updateTemplateResponseSchema = z.object({
|
|
55
|
+
success: z.boolean(),
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Schema for delete template response
|
|
60
|
+
*/
|
|
61
|
+
export const deleteTemplateResponseSchema = z.object({
|
|
62
|
+
success: z.boolean(),
|
|
63
|
+
});
|
|
64
|
+
|
|
@@ -1,42 +1,34 @@
|
|
|
1
1
|
import type { HttpClient } from "../../client/HttpClient";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Accounts client - wraps HttpClient with WABA ID
|
|
4
|
+
* Accounts client - wraps HttpClient with WABA ID as base endpoint
|
|
5
5
|
*
|
|
6
|
-
* This client automatically prepends `/${
|
|
7
|
-
* so methods can use relative paths like `/phone_numbers` instead of `/${wabaId}/phone_numbers`.
|
|
8
|
-
*
|
|
9
|
-
* Note: The wabaId is the WhatsApp Business Account ID (not the Business Portfolio ID).
|
|
10
|
-
* This is used in endpoints like GET /<WABA_ID>/phone_numbers.
|
|
11
|
-
*
|
|
12
|
-
* This treats wabaId as a "client" for the accounts namespace - different
|
|
13
|
-
* wabaIds represent different WhatsApp Business Account endpoints.
|
|
6
|
+
* This client automatically prepends `/${businessAccountId}` to all request paths.
|
|
14
7
|
*/
|
|
15
8
|
export class AccountsClient {
|
|
16
9
|
constructor(
|
|
17
10
|
private readonly httpClient: HttpClient,
|
|
18
|
-
private readonly
|
|
11
|
+
private readonly businessAccountId: string
|
|
19
12
|
) {}
|
|
20
13
|
|
|
21
14
|
/**
|
|
22
15
|
* Make a GET request with WABA ID prefix
|
|
23
16
|
*/
|
|
24
17
|
async get<T>(path: string): Promise<T> {
|
|
25
|
-
return this.httpClient.get<T>(`/${this.
|
|
18
|
+
return this.httpClient.get<T>(`/${this.businessAccountId}${path}`);
|
|
26
19
|
}
|
|
27
20
|
|
|
28
21
|
/**
|
|
29
22
|
* Make a POST request with WABA ID prefix
|
|
30
23
|
*/
|
|
31
24
|
async post<T>(path: string, body: unknown): Promise<T> {
|
|
32
|
-
return this.httpClient.post<T>(`/${this.
|
|
25
|
+
return this.httpClient.post<T>(`/${this.businessAccountId}${path}`, body);
|
|
33
26
|
}
|
|
34
27
|
|
|
35
28
|
/**
|
|
36
29
|
* Make a PATCH request with WABA ID prefix
|
|
37
30
|
*/
|
|
38
31
|
async patch<T>(path: string, body: unknown): Promise<T> {
|
|
39
|
-
return this.httpClient.patch<T>(`/${this.
|
|
32
|
+
return this.httpClient.patch<T>(`/${this.businessAccountId}${path}`, body);
|
|
40
33
|
}
|
|
41
34
|
}
|
|
42
|
-
|
|
@@ -7,41 +7,39 @@ import type { PhoneNumberListResponse } from "../../types/accounts/phone-number"
|
|
|
7
7
|
/**
|
|
8
8
|
* Accounts service for managing WhatsApp Business Accounts
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* Note: businessAccountId in the client config represents the WABA ID, not the Business Portfolio ID.
|
|
14
|
-
* The WABA ID is used in endpoints like GET /<WABA_ID>/phone_numbers.
|
|
15
|
-
*
|
|
16
|
-
* AccountsClient treats wabaId as a "client" for the accounts namespace - different
|
|
17
|
-
* wabaIds represent different WhatsApp Business Account endpoints.
|
|
10
|
+
* This service handles WABA operations like listing phone numbers.
|
|
11
|
+
* It supports both a globally configured businessAccountId (in WhatsAppClient)
|
|
12
|
+
* and per-request businessAccountId overrides.
|
|
18
13
|
*/
|
|
19
14
|
export class AccountsService {
|
|
20
|
-
private readonly
|
|
15
|
+
constructor(private readonly httpClient: HttpClient) {}
|
|
21
16
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Helper to create a Scoped Client (prefer override, fallback to config)
|
|
19
|
+
*/
|
|
20
|
+
private getClient(overrideId?: string): AccountsClient {
|
|
21
|
+
const id = overrideId || this.httpClient.businessAccountId;
|
|
22
|
+
if (!id) {
|
|
26
23
|
throw new WhatsAppValidationError(
|
|
27
|
-
"businessAccountId (WABA ID
|
|
24
|
+
"businessAccountId (WABA ID) is required. Provide it in WhatsAppClient config or as a parameter.",
|
|
28
25
|
"businessAccountId"
|
|
29
26
|
);
|
|
30
27
|
}
|
|
31
28
|
|
|
32
|
-
//
|
|
33
|
-
|
|
34
|
-
httpClient,
|
|
35
|
-
httpClient.businessAccountId
|
|
36
|
-
);
|
|
29
|
+
// Just wrap the existing httpClient
|
|
30
|
+
return new AccountsClient(this.httpClient, id);
|
|
37
31
|
}
|
|
38
32
|
|
|
39
33
|
/**
|
|
40
34
|
* List phone numbers for a WhatsApp Business Account
|
|
41
35
|
*
|
|
36
|
+
* @param businessAccountId - Optional WABA ID (overrides client config)
|
|
42
37
|
* @returns List of phone numbers associated with the WABA
|
|
43
38
|
*/
|
|
44
|
-
async listPhoneNumbers(
|
|
45
|
-
|
|
39
|
+
async listPhoneNumbers(
|
|
40
|
+
businessAccountId?: string
|
|
41
|
+
): Promise<PhoneNumberListResponse> {
|
|
42
|
+
const client = this.getClient(businessAccountId);
|
|
43
|
+
return listPhoneNumbers(client);
|
|
46
44
|
}
|
|
47
45
|
}
|
|
@@ -4,7 +4,7 @@ import type { PhoneNumberListResponse } from "../../../types/accounts/phone-numb
|
|
|
4
4
|
/**
|
|
5
5
|
* List phone numbers for a WhatsApp Business Account
|
|
6
6
|
*
|
|
7
|
-
* @param accountsClient -
|
|
7
|
+
* @param accountsClient - Scoped accounts client
|
|
8
8
|
* @returns List of phone numbers associated with the WABA
|
|
9
9
|
*/
|
|
10
10
|
export async function listPhoneNumbers(
|
|
@@ -13,4 +13,3 @@ export async function listPhoneNumbers(
|
|
|
13
13
|
// Make API request - accountsClient handles the WABA ID prefix automatically
|
|
14
14
|
return accountsClient.get<PhoneNumberListResponse>("/phone_numbers");
|
|
15
15
|
}
|
|
16
|
-
|
|
@@ -3,14 +3,7 @@ import type { HttpClient } from "../../client/HttpClient";
|
|
|
3
3
|
/**
|
|
4
4
|
* Business client - wraps HttpClient with Business Portfolio ID as base endpoint
|
|
5
5
|
*
|
|
6
|
-
* This client automatically prepends `/${businessId}` to all request paths
|
|
7
|
-
* so methods can use relative paths like `/whatsapp_business_accounts` instead of `/${businessId}/whatsapp_business_accounts`.
|
|
8
|
-
*
|
|
9
|
-
* Note: The businessId is the Business Portfolio ID (not the WABA ID).
|
|
10
|
-
* This is used in endpoints like GET /<Business-ID>/whatsapp_business_accounts.
|
|
11
|
-
*
|
|
12
|
-
* This treats businessId as a "client" for the business namespace - different
|
|
13
|
-
* businessIds represent different Business Portfolio endpoints.
|
|
6
|
+
* This client automatically prepends `/${businessId}` to all request paths.
|
|
14
7
|
*/
|
|
15
8
|
export class BusinessClient {
|
|
16
9
|
constructor(
|
|
@@ -39,4 +32,3 @@ export class BusinessClient {
|
|
|
39
32
|
return this.httpClient.patch<T>(`/${this.businessId}${path}`, body);
|
|
40
33
|
}
|
|
41
34
|
}
|
|
42
|
-
|
|
@@ -7,41 +7,39 @@ import type { BusinessAccountsListResponse } from "../../types/business/account"
|
|
|
7
7
|
/**
|
|
8
8
|
* Business service for managing Business Portfolios
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* Note: businessId in the client config represents the Business Portfolio ID.
|
|
14
|
-
* The Business Portfolio ID is used in endpoints like GET /<Business-ID>/whatsapp_business_accounts.
|
|
15
|
-
*
|
|
16
|
-
* BusinessClient treats businessId as a "client" for the business namespace - different
|
|
17
|
-
* businessIds represent different Business Portfolio endpoints.
|
|
10
|
+
* This service handles Business Portfolio operations like listing WABAs.
|
|
11
|
+
* It supports both a globally configured businessId (in WhatsAppClient)
|
|
12
|
+
* and per-request businessId overrides.
|
|
18
13
|
*/
|
|
19
14
|
export class BusinessService {
|
|
20
|
-
private readonly
|
|
15
|
+
constructor(private readonly httpClient: HttpClient) {}
|
|
21
16
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Helper to create a Scoped Client (prefer override, fallback to config)
|
|
19
|
+
*/
|
|
20
|
+
private getClient(overrideId?: string): BusinessClient {
|
|
21
|
+
const id = overrideId || this.httpClient.businessId;
|
|
22
|
+
if (!id) {
|
|
25
23
|
throw new WhatsAppValidationError(
|
|
26
|
-
"businessId (Business Portfolio ID) is required
|
|
24
|
+
"businessId (Business Portfolio ID) is required. Provide it in WhatsAppClient config or as a parameter.",
|
|
27
25
|
"businessId"
|
|
28
26
|
);
|
|
29
27
|
}
|
|
30
28
|
|
|
31
|
-
//
|
|
32
|
-
|
|
33
|
-
httpClient,
|
|
34
|
-
httpClient.businessId
|
|
35
|
-
);
|
|
29
|
+
// Just wrap the existing httpClient
|
|
30
|
+
return new BusinessClient(this.httpClient, id);
|
|
36
31
|
}
|
|
37
32
|
|
|
38
33
|
/**
|
|
39
34
|
* List WhatsApp Business Accounts (WABAs) for a Business Portfolio
|
|
40
35
|
*
|
|
36
|
+
* @param businessId - Optional Business Portfolio ID (overrides client config)
|
|
41
37
|
* @returns List of WABAs associated with the Business Portfolio
|
|
42
38
|
*/
|
|
43
|
-
async listAccounts(
|
|
44
|
-
|
|
39
|
+
async listAccounts(
|
|
40
|
+
businessId?: string
|
|
41
|
+
): Promise<BusinessAccountsListResponse> {
|
|
42
|
+
const client = this.getClient(businessId);
|
|
43
|
+
return listAccounts(client);
|
|
45
44
|
}
|
|
46
45
|
}
|
|
47
|
-
|
|
@@ -4,7 +4,7 @@ import type { BusinessAccountsListResponse } from "../../../types/business/accou
|
|
|
4
4
|
/**
|
|
5
5
|
* List WhatsApp Business Accounts (WABAs) for a Business Portfolio
|
|
6
6
|
*
|
|
7
|
-
* @param businessClient -
|
|
7
|
+
* @param businessClient - Scoped business client
|
|
8
8
|
* @returns List of WABAs associated with the Business Portfolio
|
|
9
9
|
*/
|
|
10
10
|
export async function listAccounts(
|
|
@@ -15,4 +15,3 @@ export async function listAccounts(
|
|
|
15
15
|
"/whatsapp_business_accounts"
|
|
16
16
|
);
|
|
17
17
|
}
|
|
18
|
-
|
|
@@ -2,12 +2,8 @@ import type { HttpClient } from "../../client/HttpClient";
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Messages client - wraps HttpClient with phone number ID as base endpoint
|
|
5
|
-
*
|
|
6
|
-
* This client automatically prepends `/${phoneNumberId}` to all request paths
|
|
7
|
-
* so methods can use relative paths like `/messages` instead of `/${phoneNumberId}/messages`.
|
|
8
|
-
*
|
|
9
|
-
* This treats phoneNumberId as a "client" for the messaging namespace - different
|
|
10
|
-
* phoneNumberIds represent different messaging endpoints.
|
|
5
|
+
*
|
|
6
|
+
* This client automatically prepends `/${phoneNumberId}` to all request paths.
|
|
11
7
|
*/
|
|
12
8
|
export class MessagesClient {
|
|
13
9
|
constructor(
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { HttpClient } from "../../client/HttpClient";
|
|
2
2
|
import { sendText } from "./methods/send-text";
|
|
3
3
|
import { sendImage } from "./methods/send-image";
|
|
4
4
|
import { sendLocation } from "./methods/send-location";
|
|
@@ -16,62 +16,82 @@ import type { MessageResponse } from "../../types/messages/response";
|
|
|
16
16
|
/**
|
|
17
17
|
* Messages service for sending WhatsApp messages
|
|
18
18
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
19
|
+
* This service handles message operations.
|
|
20
|
+
* It supports both a globally configured phoneNumberId (in WhatsAppClient)
|
|
21
|
+
* and per-request phoneNumberId overrides.
|
|
22
22
|
*/
|
|
23
23
|
export class MessagesService {
|
|
24
|
-
private readonly
|
|
24
|
+
constructor(private readonly httpClient: HttpClient) {}
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Helper to create a Scoped Client (prefer override, fallback to config)
|
|
28
|
+
*/
|
|
29
|
+
private getClient(overrideId?: string): MessagesClient {
|
|
30
|
+
const id = overrideId || this.httpClient.phoneNumberId;
|
|
31
|
+
if (!id) {
|
|
29
32
|
throw new WhatsAppValidationError(
|
|
30
|
-
"phoneNumberId is required
|
|
33
|
+
"phoneNumberId is required. Provide it in WhatsAppClient config or as a parameter.",
|
|
31
34
|
"phoneNumberId"
|
|
32
35
|
);
|
|
33
36
|
}
|
|
34
37
|
|
|
35
|
-
//
|
|
36
|
-
|
|
37
|
-
httpClient,
|
|
38
|
-
httpClient.phoneNumberId
|
|
39
|
-
);
|
|
38
|
+
// Just wrap the existing httpClient
|
|
39
|
+
return new MessagesClient(this.httpClient, id);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
/**
|
|
43
43
|
* Send a text message
|
|
44
44
|
*
|
|
45
45
|
* @param request - Text message request (to, text)
|
|
46
|
+
* @param phoneNumberId - Optional phone number ID (overrides client config)
|
|
46
47
|
*/
|
|
47
|
-
async sendText(
|
|
48
|
-
|
|
48
|
+
async sendText(
|
|
49
|
+
request: SendTextRequest,
|
|
50
|
+
phoneNumberId?: string
|
|
51
|
+
): Promise<MessageResponse> {
|
|
52
|
+
const client = this.getClient(phoneNumberId);
|
|
53
|
+
return sendText(client, request);
|
|
49
54
|
}
|
|
50
55
|
|
|
51
56
|
/**
|
|
52
57
|
* Send an image message
|
|
53
58
|
*
|
|
54
59
|
* @param request - Image message request (to, image)
|
|
60
|
+
* @param phoneNumberId - Optional phone number ID (overrides client config)
|
|
55
61
|
*/
|
|
56
|
-
async sendImage(
|
|
57
|
-
|
|
62
|
+
async sendImage(
|
|
63
|
+
request: SendImageRequest,
|
|
64
|
+
phoneNumberId?: string
|
|
65
|
+
): Promise<MessageResponse> {
|
|
66
|
+
const client = this.getClient(phoneNumberId);
|
|
67
|
+
return sendImage(client, request);
|
|
58
68
|
}
|
|
59
69
|
|
|
60
70
|
/**
|
|
61
71
|
* Send a location message
|
|
62
72
|
*
|
|
63
73
|
* @param request - Location message request (to, location)
|
|
74
|
+
* @param phoneNumberId - Optional phone number ID (overrides client config)
|
|
64
75
|
*/
|
|
65
|
-
async sendLocation(
|
|
66
|
-
|
|
76
|
+
async sendLocation(
|
|
77
|
+
request: SendLocationRequest,
|
|
78
|
+
phoneNumberId?: string
|
|
79
|
+
): Promise<MessageResponse> {
|
|
80
|
+
const client = this.getClient(phoneNumberId);
|
|
81
|
+
return sendLocation(client, request);
|
|
67
82
|
}
|
|
68
83
|
|
|
69
84
|
/**
|
|
70
85
|
* Send a reaction message
|
|
71
86
|
*
|
|
72
87
|
* @param request - Reaction message request (to, reaction)
|
|
88
|
+
* @param phoneNumberId - Optional phone number ID (overrides client config)
|
|
73
89
|
*/
|
|
74
|
-
async sendReaction(
|
|
75
|
-
|
|
90
|
+
async sendReaction(
|
|
91
|
+
request: SendReactionRequest,
|
|
92
|
+
phoneNumberId?: string
|
|
93
|
+
): Promise<MessageResponse> {
|
|
94
|
+
const client = this.getClient(phoneNumberId);
|
|
95
|
+
return sendReaction(client, request);
|
|
76
96
|
}
|
|
77
97
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { HttpClient } from "../../client/HttpClient";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Templates client - wraps HttpClient with WABA ID as base endpoint
|
|
5
|
+
*
|
|
6
|
+
* This client automatically prepends `/${businessAccountId}` to all request paths.
|
|
7
|
+
* Similar to AccountsClient, since templates are scoped to WABA.
|
|
8
|
+
*/
|
|
9
|
+
export class TemplatesClient {
|
|
10
|
+
constructor(
|
|
11
|
+
private readonly httpClient: HttpClient,
|
|
12
|
+
private readonly businessAccountId: string
|
|
13
|
+
) {}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Make a GET request with WABA ID prefix
|
|
17
|
+
*/
|
|
18
|
+
async get<T>(path: string): Promise<T> {
|
|
19
|
+
return this.httpClient.get<T>(`/${this.businessAccountId}${path}`);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Make a POST request with WABA ID prefix
|
|
24
|
+
*/
|
|
25
|
+
async post<T>(path: string, body: unknown): Promise<T> {
|
|
26
|
+
return this.httpClient.post<T>(`/${this.businessAccountId}${path}`, body);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Make a DELETE request with WABA ID prefix
|
|
31
|
+
*/
|
|
32
|
+
async delete<T>(path: string): Promise<T> {
|
|
33
|
+
return this.httpClient.delete<T>(`/${this.businessAccountId}${path}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import type { HttpClient } from "../../client/HttpClient";
|
|
2
|
+
import { TemplatesClient } from "./TemplatesClient";
|
|
3
|
+
import { createTemplate } from "./methods/create";
|
|
4
|
+
import { listTemplates } from "./methods/list";
|
|
5
|
+
import { getTemplate } from "./methods/get";
|
|
6
|
+
import { updateTemplate } from "./methods/update";
|
|
7
|
+
import { deleteTemplate } from "./methods/delete";
|
|
8
|
+
import { WhatsAppValidationError } from "../../errors";
|
|
9
|
+
import type {
|
|
10
|
+
CreateTemplateRequest,
|
|
11
|
+
UpdateTemplateRequest,
|
|
12
|
+
ListTemplatesRequest,
|
|
13
|
+
DeleteTemplateRequest,
|
|
14
|
+
} from "../../types/templates/request";
|
|
15
|
+
import type {
|
|
16
|
+
CreateTemplateResponse,
|
|
17
|
+
ListTemplatesResponse,
|
|
18
|
+
TemplateResponse,
|
|
19
|
+
UpdateTemplateResponse,
|
|
20
|
+
DeleteTemplateResponse,
|
|
21
|
+
} from "../../types/templates/response";
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Templates service for managing message templates
|
|
25
|
+
*
|
|
26
|
+
* This service handles template operations like creating, listing, and deleting templates.
|
|
27
|
+
* It supports both a globally configured businessAccountId (in WhatsAppClient)
|
|
28
|
+
* and per-request businessAccountId overrides.
|
|
29
|
+
*
|
|
30
|
+
* Note: Get and Update operations use template ID directly (no WABA prefix needed).
|
|
31
|
+
*/
|
|
32
|
+
export class TemplatesService {
|
|
33
|
+
constructor(private readonly httpClient: HttpClient) {}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Helper to create a Scoped Client (prefer override, fallback to config)
|
|
37
|
+
*/
|
|
38
|
+
private getClient(overrideId?: string): TemplatesClient {
|
|
39
|
+
const id = overrideId || this.httpClient.businessAccountId;
|
|
40
|
+
if (!id) {
|
|
41
|
+
throw new WhatsAppValidationError(
|
|
42
|
+
"businessAccountId (WABA ID) is required for templates. Provide it in WhatsAppClient config or as a parameter.",
|
|
43
|
+
"businessAccountId"
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return new TemplatesClient(this.httpClient, id);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Create a message template
|
|
52
|
+
*
|
|
53
|
+
* @param request - Template creation request
|
|
54
|
+
* @param businessAccountId - Optional WABA ID (overrides client config)
|
|
55
|
+
*/
|
|
56
|
+
async create(
|
|
57
|
+
request: CreateTemplateRequest,
|
|
58
|
+
businessAccountId?: string
|
|
59
|
+
): Promise<CreateTemplateResponse> {
|
|
60
|
+
const client = this.getClient(businessAccountId);
|
|
61
|
+
return createTemplate(client, request);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* List message templates
|
|
66
|
+
*
|
|
67
|
+
* @param options - Optional filter options (name)
|
|
68
|
+
* @param businessAccountId - Optional WABA ID (overrides client config)
|
|
69
|
+
*/
|
|
70
|
+
async list(
|
|
71
|
+
options?: ListTemplatesRequest,
|
|
72
|
+
businessAccountId?: string
|
|
73
|
+
): Promise<ListTemplatesResponse> {
|
|
74
|
+
const client = this.getClient(businessAccountId);
|
|
75
|
+
return listTemplates(client, options);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Get a template by ID
|
|
80
|
+
*
|
|
81
|
+
* Note: This uses the template ID directly (no WABA prefix needed)
|
|
82
|
+
*
|
|
83
|
+
* @param templateId - Template ID
|
|
84
|
+
*/
|
|
85
|
+
async get(templateId: string): Promise<TemplateResponse> {
|
|
86
|
+
return getTemplate(this.httpClient, templateId);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Update a template
|
|
91
|
+
*
|
|
92
|
+
* Note: This uses the template ID directly (no WABA prefix needed)
|
|
93
|
+
*
|
|
94
|
+
* @param templateId - Template ID
|
|
95
|
+
* @param request - Template update request
|
|
96
|
+
*/
|
|
97
|
+
async update(
|
|
98
|
+
templateId: string,
|
|
99
|
+
request: UpdateTemplateRequest
|
|
100
|
+
): Promise<UpdateTemplateResponse> {
|
|
101
|
+
return updateTemplate(this.httpClient, templateId, request);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Delete a template
|
|
106
|
+
*
|
|
107
|
+
* @param options - Delete options (name or hsm_id)
|
|
108
|
+
* @param businessAccountId - Optional WABA ID (overrides client config)
|
|
109
|
+
*/
|
|
110
|
+
async delete(
|
|
111
|
+
options: DeleteTemplateRequest,
|
|
112
|
+
businessAccountId?: string
|
|
113
|
+
): Promise<DeleteTemplateResponse> {
|
|
114
|
+
const client = this.getClient(businessAccountId);
|
|
115
|
+
return deleteTemplate(client, options);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { TemplatesClient } from "../TemplatesClient";
|
|
2
|
+
import { createTemplateRequestSchema } from "../../../schemas/templates/request";
|
|
3
|
+
import type { CreateTemplateRequest } from "../../../types/templates/request";
|
|
4
|
+
import type { CreateTemplateResponse } from "../../../types/templates/response";
|
|
5
|
+
import { transformZodError } from "../../../utils/zod-error";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Create a message template
|
|
9
|
+
*
|
|
10
|
+
* @param templatesClient - Templates client with WABA ID baked in
|
|
11
|
+
* @param request - Template creation request
|
|
12
|
+
*/
|
|
13
|
+
export async function createTemplate(
|
|
14
|
+
templatesClient: TemplatesClient,
|
|
15
|
+
request: CreateTemplateRequest
|
|
16
|
+
): Promise<CreateTemplateResponse> {
|
|
17
|
+
// Validate request with schema - throws WhatsAppValidationError if invalid
|
|
18
|
+
const result = createTemplateRequestSchema.safeParse(request);
|
|
19
|
+
if (!result.success) {
|
|
20
|
+
throw transformZodError(result.error);
|
|
21
|
+
}
|
|
22
|
+
const data = result.data;
|
|
23
|
+
|
|
24
|
+
// Make API request - templatesClient handles the WABA ID prefix automatically
|
|
25
|
+
return templatesClient.post<CreateTemplateResponse>("/message_templates", data);
|
|
26
|
+
}
|
|
27
|
+
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { TemplatesClient } from "../TemplatesClient";
|
|
2
|
+
import { deleteTemplateRequestSchema } from "../../../schemas/templates/request";
|
|
3
|
+
import type { DeleteTemplateRequest } from "../../../types/templates/request";
|
|
4
|
+
import type { DeleteTemplateResponse } from "../../../types/templates/response";
|
|
5
|
+
import { transformZodError } from "../../../utils/zod-error";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Delete a template
|
|
9
|
+
*
|
|
10
|
+
* @param templatesClient - Templates client with WABA ID baked in
|
|
11
|
+
* @param options - Delete options (name or hsm_id)
|
|
12
|
+
*/
|
|
13
|
+
export async function deleteTemplate(
|
|
14
|
+
templatesClient: TemplatesClient,
|
|
15
|
+
options: DeleteTemplateRequest
|
|
16
|
+
): Promise<DeleteTemplateResponse> {
|
|
17
|
+
// Validate request with schema - throws WhatsAppValidationError if invalid
|
|
18
|
+
const result = deleteTemplateRequestSchema.safeParse(options);
|
|
19
|
+
if (!result.success) {
|
|
20
|
+
throw transformZodError(result.error);
|
|
21
|
+
}
|
|
22
|
+
const data = result.data;
|
|
23
|
+
|
|
24
|
+
// Build query string
|
|
25
|
+
const params = new URLSearchParams();
|
|
26
|
+
if (data.name) {
|
|
27
|
+
params.append("name", data.name);
|
|
28
|
+
}
|
|
29
|
+
if (data.hsm_id) {
|
|
30
|
+
params.append("hsm_id", data.hsm_id);
|
|
31
|
+
}
|
|
32
|
+
const queryString = params.toString();
|
|
33
|
+
const path = `/message_templates?${queryString}`;
|
|
34
|
+
|
|
35
|
+
// Make API request - templatesClient handles the WABA ID prefix automatically
|
|
36
|
+
return templatesClient.delete<DeleteTemplateResponse>(path);
|
|
37
|
+
}
|
|
38
|
+
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { HttpClient } from "../../../client/HttpClient";
|
|
2
|
+
import type { TemplateResponse } from "../../../types/templates/response";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Get a template by ID
|
|
6
|
+
*
|
|
7
|
+
* Note: This uses the template ID directly (no WABA prefix needed)
|
|
8
|
+
*
|
|
9
|
+
* @param httpClient - HTTP client
|
|
10
|
+
* @param templateId - Template ID
|
|
11
|
+
*/
|
|
12
|
+
export async function getTemplate(
|
|
13
|
+
httpClient: HttpClient,
|
|
14
|
+
templateId: string
|
|
15
|
+
): Promise<TemplateResponse> {
|
|
16
|
+
if (!templateId || templateId.trim().length === 0) {
|
|
17
|
+
throw new Error("Template ID is required");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Make API request - template ID is used directly, no WABA prefix
|
|
21
|
+
return httpClient.get<TemplateResponse>(`/${templateId}`);
|
|
22
|
+
}
|
|
23
|
+
|