whatsapp-cloud 0.0.3 → 0.0.4

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.
Files changed (52) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +11 -0
  3. package/agent_docs/DESIGN.md +707 -0
  4. package/agent_docs/MESSAGES_NAMESPACE_ANALYSIS.md +368 -0
  5. package/agent_docs/NAMING_DECISION.md +78 -0
  6. package/agent_docs/STRUCTURE.md +711 -0
  7. package/agent_docs/messages-namespace-design.md +357 -0
  8. package/package.json +5 -2
  9. package/src/client/HttpClient.ts +122 -0
  10. package/src/client/WhatsAppClient.ts +55 -0
  11. package/src/client/index.ts +2 -0
  12. package/src/errors.ts +58 -0
  13. package/src/index.ts +16 -1
  14. package/src/schemas/accounts/index.ts +1 -0
  15. package/src/schemas/accounts/phone-number.ts +20 -0
  16. package/src/schemas/business/account.ts +43 -0
  17. package/src/schemas/business/index.ts +2 -0
  18. package/src/schemas/client.ts +50 -0
  19. package/src/schemas/debug.ts +25 -0
  20. package/src/schemas/index.ts +5 -0
  21. package/src/schemas/messages/index.ts +2 -0
  22. package/src/schemas/messages/request.ts +82 -0
  23. package/src/schemas/messages/response.ts +19 -0
  24. package/src/services/accounts/AccountsClient.ts +42 -0
  25. package/src/services/accounts/AccountsService.ts +47 -0
  26. package/src/services/accounts/index.ts +2 -0
  27. package/src/services/accounts/methods/list-phone-numbers.ts +16 -0
  28. package/src/services/business/BusinessClient.ts +42 -0
  29. package/src/services/business/BusinessService.ts +47 -0
  30. package/src/services/business/index.ts +3 -0
  31. package/src/services/business/methods/list-accounts.ts +18 -0
  32. package/src/services/index.ts +2 -0
  33. package/src/services/messages/MessagesClient.ts +38 -0
  34. package/src/services/messages/MessagesService.ts +77 -0
  35. package/src/services/messages/index.ts +8 -0
  36. package/src/services/messages/methods/send-image.ts +33 -0
  37. package/src/services/messages/methods/send-location.ts +32 -0
  38. package/src/services/messages/methods/send-reaction.ts +33 -0
  39. package/src/services/messages/methods/send-text.ts +32 -0
  40. package/src/services/messages/utils/build-message-payload.ts +32 -0
  41. package/src/types/accounts/index.ts +1 -0
  42. package/src/types/accounts/phone-number.ts +9 -0
  43. package/src/types/business/account.ts +10 -0
  44. package/src/types/business/index.ts +2 -0
  45. package/src/types/client.ts +8 -0
  46. package/src/types/debug.ts +8 -0
  47. package/src/types/index.ts +5 -0
  48. package/src/types/messages/index.ts +2 -0
  49. package/src/types/messages/request.ts +27 -0
  50. package/src/types/messages/response.ts +7 -0
  51. package/src/utils/zod-error.ts +28 -0
  52. package/tsconfig.json +4 -1
@@ -0,0 +1,357 @@
1
+ # Messages Namespace Design
2
+
3
+ This document describes the design patterns and architectural decisions for the Messages namespace. This serves as a blueprint for implementing other namespaces in the WhatsApp Cloud API SDK.
4
+
5
+ ## Core Design Principles
6
+
7
+ ### 1. **Namespace Client Pattern**
8
+
9
+ Each namespace that requires a base path identifier (like `phoneNumberId` for messages) gets its own client wrapper that handles the base path automatically.
10
+
11
+ **Pattern:**
12
+ - Create a `{Namespace}Client` class that wraps `HttpClient`
13
+ - The client automatically prepends the namespace identifier to all paths
14
+ - Methods use simple relative paths (e.g., `/messages`) instead of full paths (e.g., `/${phoneNumberId}/messages`)
15
+
16
+ **Example:**
17
+ ```typescript
18
+ // MessagesClient wraps HttpClient with phoneNumberId as base endpoint
19
+ export class MessagesClient {
20
+ constructor(
21
+ private readonly httpClient: HttpClient,
22
+ private readonly phoneNumberId: string
23
+ ) {}
24
+
25
+ async post<T>(path: string, body: unknown): Promise<T> {
26
+ return this.httpClient.post<T>(`/${this.phoneNumberId}${path}`, body);
27
+ }
28
+ }
29
+ ```
30
+
31
+ **Why:** Treats the namespace identifier (phoneNumberId) as a "client" for that namespace. Different identifiers represent different endpoints, making the abstraction clean and intuitive.
32
+
33
+ ### 2. **Request Structure Matches API**
34
+
35
+ Request schemas match the WhatsApp API structure exactly, minus fields that are handled internally.
36
+
37
+ **Pattern:**
38
+ - Request schemas mirror the API payload structure
39
+ - Exclude: `messaging_product`, `recipient_type`, `type` (added by `buildMessagePayload`)
40
+ - Exclude: `phoneNumberId` (handled at client level)
41
+ - Include: All user-provided fields matching API structure
42
+
43
+ **Example:**
44
+ ```typescript
45
+ // API expects: { to, image: { id?, link?, caption? } }
46
+ // Schema matches: { to, image: { id?, link?, caption? } }
47
+ export const sendImageRequestSchema = baseMessageRequestSchema.extend({
48
+ image: imageSchema, // Matches API structure
49
+ });
50
+ ```
51
+
52
+ **Why:**
53
+ - Reduces transformation logic
54
+ - Makes API documentation directly applicable
55
+ - Clear mapping between user input and API payload
56
+
57
+ ### 3. **Client-Level Configuration**
58
+
59
+ Namespace-specific identifiers (like `phoneNumberId`) are handled at the service construction level, not in individual requests.
60
+
61
+ **Pattern:**
62
+ - Service validates required identifier exists in `HttpClient` at construction
63
+ - Service creates namespace client once with the identifier
64
+ - Methods receive the namespace client, not the raw `HttpClient`
65
+ - No per-request identifier resolution needed
66
+
67
+ **Example:**
68
+ ```typescript
69
+ export class MessagesService {
70
+ private readonly messagesClient: MessagesClient;
71
+
72
+ constructor(httpClient: HttpClient) {
73
+ // Validate identifier at construction
74
+ if (!httpClient.phoneNumberId) {
75
+ throw new WhatsAppValidationError(...);
76
+ }
77
+
78
+ // Create namespace client once
79
+ this.messagesClient = new MessagesClient(httpClient, httpClient.phoneNumberId);
80
+ }
81
+
82
+ async sendImage(request: SendImageRequest) {
83
+ // Method receives namespace client, not HttpClient
84
+ return sendImage(this.messagesClient, request);
85
+ }
86
+ }
87
+ ```
88
+
89
+ **Why:**
90
+ - Identifier is required, so validate early (fail fast)
91
+ - Single source of truth for namespace identifier
92
+ - Methods don't need to handle optional overrides
93
+ - Cleaner method signatures
94
+
95
+ ### 4. **Clean Method Pattern**
96
+
97
+ All methods follow a consistent pattern: validate → extract → build → request.
98
+
99
+ **Pattern:**
100
+ ```typescript
101
+ export async function sendImage(
102
+ messagesClient: MessagesClient,
103
+ request: SendImageRequest
104
+ ): Promise<MessageResponse> {
105
+ // 1. Validate request with schema
106
+ const result = sendImageRequestSchema.safeParse(request);
107
+ if (!result.success) {
108
+ throw transformZodError(result.error);
109
+ }
110
+
111
+ // 2. Extract validated data
112
+ const data = result.data;
113
+
114
+ // 3. Build payload (request structure already matches API)
115
+ const payload = buildMessagePayload(data.to, "image", {
116
+ image: data.image,
117
+ });
118
+
119
+ // 4. Make API request (namespace client handles base path)
120
+ return messagesClient.post<MessageResponse>("/messages", payload);
121
+ }
122
+ ```
123
+
124
+ **Why:**
125
+ - Consistent, predictable flow
126
+ - Easy to understand and maintain
127
+ - Clear separation of concerns
128
+ - Request → result → data trajectory
129
+
130
+ ### 5. **Request → Result → Data Trajectory**
131
+
132
+ The validation flow follows a clear trajectory: request → validation result → validated data.
133
+
134
+ **Pattern:**
135
+ ```typescript
136
+ // Request comes in
137
+ const result = schema.safeParse(request);
138
+
139
+ // Extract validated data
140
+ const data = result.data;
141
+
142
+ // Use data directly (structure matches API)
143
+ const payload = buildMessagePayload(data.to, "image", {
144
+ image: data.image,
145
+ });
146
+ ```
147
+
148
+ **Why:**
149
+ - Clear path from user input to safe API request
150
+ - No intermediate transformations needed
151
+ - Type-safe throughout the flow
152
+
153
+ ### 6. **No Manual Undefined Filtering**
154
+
155
+ Let `JSON.stringify` handle undefined values automatically.
156
+
157
+ **Pattern:**
158
+ ```typescript
159
+ // Don't filter undefined manually
160
+ export function buildMessagePayload(to: string, type: string, content: T) {
161
+ return {
162
+ messaging_product: "whatsapp" as const,
163
+ recipient_type: "individual" as const,
164
+ to,
165
+ type,
166
+ ...content, // undefined values automatically omitted by JSON.stringify
167
+ };
168
+ }
169
+ ```
170
+
171
+ **Why:**
172
+ - `JSON.stringify` automatically omits undefined values
173
+ - Less code, simpler logic
174
+ - No need for `omitUndefined` utilities
175
+
176
+ ## Architecture Layers
177
+
178
+ ### Layer 1: Service (MessagesService)
179
+ - **Responsibility:** Validate namespace requirements, create namespace client
180
+ - **Input:** `HttpClient` (with namespace identifier)
181
+ - **Output:** Namespace client instance
182
+ - **Pattern:** Constructor validation + client creation
183
+
184
+ ### Layer 2: Namespace Client (MessagesClient)
185
+ - **Responsibility:** Handle namespace-specific base path
186
+ - **Input:** `HttpClient` + namespace identifier
187
+ - **Output:** Wrapped client with base path handling
188
+ - **Pattern:** Proxy pattern - wraps HttpClient with path prefix
189
+
190
+ ### Layer 3: Methods (sendImage, sendText, etc.)
191
+ - **Responsibility:** Validate request, build payload, make API call
192
+ - **Input:** Namespace client + request object
193
+ - **Output:** API response
194
+ - **Pattern:** Validate → extract → build → request
195
+
196
+ ### Layer 4: Utilities (buildMessagePayload)
197
+ - **Responsibility:** Add common API fields
198
+ - **Input:** User data
199
+ - **Output:** Complete API payload
200
+ - **Pattern:** Simple object construction
201
+
202
+ ## File Structure
203
+
204
+ ```
205
+ services/messages/
206
+ ├── MessagesService.ts # Service layer - validates & creates client
207
+ ├── MessagesClient.ts # Namespace client - handles base path
208
+ ├── methods/
209
+ │ ├── send-image.ts # Method implementations
210
+ │ ├── send-text.ts
211
+ │ ├── send-location.ts
212
+ │ └── send-reaction.ts
213
+ ├── utils/
214
+ │ └── build-message-payload.ts # Common payload builder
215
+ └── index.ts # Exports
216
+ ```
217
+
218
+ ## Schema Structure
219
+
220
+ ```
221
+ schemas/messages/
222
+ ├── request.ts # Request schemas matching API structure
223
+ └── response.ts # Response schemas
224
+ ```
225
+
226
+ **Request Schema Pattern:**
227
+ ```typescript
228
+ // Base schema (common fields)
229
+ const baseMessageRequestSchema = z.object({
230
+ to: z.string().regex(...),
231
+ });
232
+
233
+ // Type-specific schema (matches API structure)
234
+ const imageSchema = z.object({
235
+ id: z.string().optional(),
236
+ link: z.string().url().optional(),
237
+ caption: z.string().max(1024).optional(),
238
+ }).refine(...);
239
+
240
+ // Combined schema
241
+ export const sendImageRequestSchema = baseMessageRequestSchema.extend({
242
+ image: imageSchema,
243
+ });
244
+ ```
245
+
246
+ ## Type Structure
247
+
248
+ ```
249
+ types/messages/
250
+ ├── request.ts # Request types (inferred from schemas)
251
+ └── response.ts # Response types
252
+ ```
253
+
254
+ **Type Pattern:**
255
+ ```typescript
256
+ // Types are inferred from schemas
257
+ export type SendImageRequest = z.infer<typeof sendImageRequestSchema>;
258
+ ```
259
+
260
+ ## Key Design Decisions
261
+
262
+ ### ✅ Do's
263
+
264
+ 1. **Match API structure in requests** - Makes documentation directly applicable
265
+ 2. **Validate at service construction** - Fail fast, clear errors
266
+ 3. **Use namespace clients** - Clean abstraction for namespace-specific paths
267
+ 4. **Follow consistent method pattern** - Easy to understand and maintain
268
+ 5. **Let JSON.stringify handle undefined** - Simpler code
269
+
270
+ ### ❌ Don'ts
271
+
272
+ 1. **Don't include namespace identifiers in requests** - Handle at client level
273
+ 2. **Don't manually filter undefined** - Let JSON.stringify do it
274
+ 3. **Don't transform request structure unnecessarily** - Match API directly
275
+ 4. **Don't resolve identifiers in methods** - Do it at service level
276
+ 5. **Don't create namespace client per request** - Create once in constructor
277
+
278
+ ## Applying to Other Namespaces
279
+
280
+ When creating a new namespace (e.g., `BusinessAccounts`):
281
+
282
+ 1. **Create namespace client** (`BusinessAccountsClient`)
283
+ - Wrap `HttpClient` with namespace-specific base path
284
+ - Handle namespace identifier (e.g., `businessAccountId`)
285
+
286
+ 2. **Create service** (`BusinessAccountsService`)
287
+ - Validate namespace identifier exists in constructor
288
+ - Create namespace client once
289
+ - Pass client to methods
290
+
291
+ 3. **Create methods** (`getProfile`, `updateProfile`, etc.)
292
+ - Follow validate → extract → build → request pattern
293
+ - Use namespace client, not raw `HttpClient`
294
+ - Match API structure in requests
295
+
296
+ 4. **Create schemas** (`schemas/business-accounts/`)
297
+ - Match API structure exactly
298
+ - Exclude internal fields (handled by utilities)
299
+ - Exclude namespace identifier (handled at client level)
300
+
301
+ 5. **Create types** (`types/business-accounts/`)
302
+ - Infer from schemas using `z.infer`
303
+
304
+ ## Example: Applying Pattern to Business Accounts
305
+
306
+ ```typescript
307
+ // 1. Namespace Client
308
+ export class BusinessAccountsClient {
309
+ constructor(
310
+ private readonly httpClient: HttpClient,
311
+ private readonly businessAccountId: string
312
+ ) {}
313
+
314
+ async get<T>(path: string): Promise<T> {
315
+ return this.httpClient.get<T>(`/${this.businessAccountId}${path}`);
316
+ }
317
+ }
318
+
319
+ // 2. Service
320
+ export class BusinessAccountsService {
321
+ private readonly accountsClient: BusinessAccountsClient;
322
+
323
+ constructor(httpClient: HttpClient) {
324
+ if (!httpClient.businessAccountId) {
325
+ throw new WhatsAppValidationError(...);
326
+ }
327
+ this.accountsClient = new BusinessAccountsClient(
328
+ httpClient,
329
+ httpClient.businessAccountId
330
+ );
331
+ }
332
+
333
+ async getProfile() {
334
+ return getProfile(this.accountsClient);
335
+ }
336
+ }
337
+
338
+ // 3. Method
339
+ export async function getProfile(
340
+ accountsClient: BusinessAccountsClient
341
+ ): Promise<ProfileResponse> {
342
+ return accountsClient.get<ProfileResponse>("/profile");
343
+ }
344
+ ```
345
+
346
+ ## Summary
347
+
348
+ The Messages namespace design provides a clean, consistent pattern for implementing API namespaces:
349
+
350
+ - **Namespace clients** handle base paths automatically
351
+ - **Request structures** match API directly
352
+ - **Service layer** validates and creates clients
353
+ - **Methods** follow a consistent pattern
354
+ - **No manual filtering** - let JSON.stringify handle it
355
+
356
+ This pattern ensures consistency, maintainability, and clarity across all namespaces in the SDK.
357
+
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "whatsapp-cloud",
3
- "version": "0.0.3",
4
- "description": "",
3
+ "version": "0.0.4",
4
+ "description": "Work in progress. A WhatsApp client tailored for LLMs—built to actually work.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.mjs",
@@ -14,6 +14,9 @@
14
14
  "tsup": "^8.5.1",
15
15
  "typescript": "^5.9.3"
16
16
  },
17
+ "dependencies": {
18
+ "zod": "^4.2.1"
19
+ },
17
20
  "scripts": {
18
21
  "build": "tsup src/index.ts --format cjs,esm --dts",
19
22
  "lint": "tsc"
@@ -0,0 +1,122 @@
1
+ import type { ClientConfig } from "../types/client";
2
+
3
+ /**
4
+ * HTTP client for making requests to the WhatsApp Cloud API
5
+ */
6
+ export class HttpClient {
7
+ private readonly baseURL: string;
8
+ public readonly accessToken: string;
9
+ public readonly phoneNumberId?: string;
10
+ public readonly businessAccountId?: string;
11
+ public readonly businessId?: string;
12
+ public readonly apiVersion: string;
13
+
14
+ constructor(config: ClientConfig) {
15
+ this.accessToken = config.accessToken;
16
+ if (config.phoneNumberId !== undefined) {
17
+ this.phoneNumberId = config.phoneNumberId;
18
+ }
19
+ if (config.businessAccountId !== undefined) {
20
+ this.businessAccountId = config.businessAccountId;
21
+ }
22
+ if (config.businessId !== undefined) {
23
+ this.businessId = config.businessId;
24
+ }
25
+ this.apiVersion = config.apiVersion ?? "v18.0";
26
+ this.baseURL = config.baseURL ?? "https://graph.facebook.com";
27
+ }
28
+
29
+ /**
30
+ * Make a POST request
31
+ */
32
+ async post<T>(path: string, body: unknown): Promise<T> {
33
+ const url = `${this.baseURL}/${this.apiVersion}${path}`;
34
+
35
+ const response = await fetch(url, {
36
+ method: "POST",
37
+ headers: {
38
+ "Content-Type": "application/json",
39
+ Authorization: `Bearer ${this.accessToken}`,
40
+ },
41
+ body: JSON.stringify(body),
42
+ });
43
+
44
+ if (!response.ok) {
45
+ const error = await response.json().catch(() => ({
46
+ error: {
47
+ message: response.statusText,
48
+ code: response.status,
49
+ },
50
+ }));
51
+ throw new Error(
52
+ `API Error: ${error.error?.message || response.statusText} (${
53
+ error.error?.code || response.status
54
+ })`
55
+ );
56
+ }
57
+
58
+ return response.json() as Promise<T>;
59
+ }
60
+
61
+ /**
62
+ * Make a GET request
63
+ */
64
+ async get<T>(path: string): Promise<T> {
65
+ const url = `${this.baseURL}/${this.apiVersion}${path}`;
66
+
67
+ const response = await fetch(url, {
68
+ method: "GET",
69
+ headers: {
70
+ Authorization: `Bearer ${this.accessToken}`,
71
+ },
72
+ });
73
+
74
+ if (!response.ok) {
75
+ const error = await response.json().catch(() => ({
76
+ error: {
77
+ message: response.statusText,
78
+ code: response.status,
79
+ },
80
+ }));
81
+ throw new Error(
82
+ `API Error: ${error.error?.message || response.statusText} (${
83
+ error.error?.code || response.status
84
+ })`
85
+ );
86
+ }
87
+
88
+ return response.json() as Promise<T>;
89
+ }
90
+
91
+ /**
92
+ * Make a PATCH request
93
+ */
94
+ async patch<T>(path: string, body: unknown): Promise<T> {
95
+ const url = `${this.baseURL}/${this.apiVersion}${path}`;
96
+
97
+ const response = await fetch(url, {
98
+ method: "PATCH",
99
+ headers: {
100
+ "Content-Type": "application/json",
101
+ Authorization: `Bearer ${this.accessToken}`,
102
+ },
103
+ body: JSON.stringify(body),
104
+ });
105
+
106
+ if (!response.ok) {
107
+ const error = await response.json().catch(() => ({
108
+ error: {
109
+ message: response.statusText,
110
+ code: response.status,
111
+ },
112
+ }));
113
+ throw new Error(
114
+ `API Error: ${error.error?.message || response.statusText} (${
115
+ error.error?.code || response.status
116
+ })`
117
+ );
118
+ }
119
+
120
+ return response.json() as Promise<T>;
121
+ }
122
+ }
@@ -0,0 +1,55 @@
1
+ import { clientConfigSchema } from "../schemas/client";
2
+ import type { ClientConfig } from "../types/client";
3
+ import { HttpClient } from "./HttpClient";
4
+ import { MessagesService } from "../services/messages/index";
5
+ import { AccountsService } from "../services/accounts/index";
6
+ import { BusinessService } from "../services/business/index";
7
+ import { ZodError } from "zod";
8
+ import { transformZodError } from "../utils/zod-error";
9
+ import type { DebugTokenResponse } from "../types/debug";
10
+
11
+ /**
12
+ * WhatsApp Cloud API client
13
+ */
14
+ export class WhatsAppClient {
15
+ public readonly messages: MessagesService;
16
+ public readonly accounts: AccountsService;
17
+ public readonly business: BusinessService;
18
+
19
+ private readonly httpClient: HttpClient;
20
+
21
+ constructor(config: ClientConfig) {
22
+ // Validate config with schema - Zod provides detailed error messages
23
+ let validated: ClientConfig;
24
+ try {
25
+ validated = clientConfigSchema.parse(config);
26
+ } catch (error) {
27
+ if (error instanceof ZodError) {
28
+ throw transformZodError(error);
29
+ }
30
+ throw error;
31
+ }
32
+
33
+ // Initialize HTTP client
34
+ this.httpClient = new HttpClient(validated);
35
+
36
+ // Initialize services (namespaces)
37
+ this.messages = new MessagesService(this.httpClient);
38
+ this.accounts = new AccountsService(this.httpClient);
39
+ this.business = new BusinessService(this.httpClient);
40
+ }
41
+
42
+ /**
43
+ * Debug the current access token
44
+ *
45
+ * This method calls the Graph API debug_token endpoint to inspect the access token
46
+ * used by this client. Useful for understanding token permissions, expiration, and validity.
47
+ *
48
+ * @returns Debug information about the access token
49
+ */
50
+ async debugToken(): Promise<DebugTokenResponse> {
51
+ return this.httpClient.get<DebugTokenResponse>(
52
+ `/debug_token?input_token=${this.httpClient.accessToken}`
53
+ );
54
+ }
55
+ }
@@ -0,0 +1,2 @@
1
+ export { WhatsAppClient } from "./WhatsAppClient";
2
+ export { HttpClient } from "./HttpClient";
package/src/errors.ts ADDED
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Base error class for WhatsApp API errors
3
+ */
4
+ export class WhatsAppError extends Error {
5
+ constructor(message: string) {
6
+ super(message);
7
+ this.name = this.constructor.name;
8
+ // Maintains proper stack trace for where our error was thrown (only available on V8)
9
+ const captureStackTrace = (Error as any).captureStackTrace;
10
+ if (typeof captureStackTrace === "function") {
11
+ captureStackTrace(this, this.constructor);
12
+ }
13
+ }
14
+ }
15
+
16
+ /**
17
+ * Error thrown when validation fails (configuration, requests, etc.)
18
+ * Can be used for any Zod validation error
19
+ */
20
+ export class WhatsAppValidationError extends WhatsAppError {
21
+ constructor(
22
+ message: string,
23
+ public readonly field?: string,
24
+ public readonly issues?: Array<{
25
+ path: readonly (string | number)[];
26
+ message: string;
27
+ }>
28
+ ) {
29
+ super(message);
30
+ this.name = "WhatsAppValidationError";
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Error thrown when an API request fails
36
+ */
37
+ export class WhatsAppAPIError extends WhatsAppError {
38
+ constructor(
39
+ public readonly code: number,
40
+ public readonly type: string,
41
+ message: string,
42
+ public readonly statusCode?: number,
43
+ public readonly details?: unknown
44
+ ) {
45
+ super(message);
46
+ this.name = "WhatsAppAPIError";
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Error thrown when rate limit is exceeded
52
+ */
53
+ export class WhatsAppRateLimitError extends WhatsAppAPIError {
54
+ constructor(message: string, public readonly retryAfter?: number) {
55
+ super(131056, "rate_limit", message, 429, { retryAfter });
56
+ this.name = "WhatsAppRateLimitError";
57
+ }
58
+ }
package/src/index.ts CHANGED
@@ -1 +1,16 @@
1
- export const add = (a: number, b: number) => a + b;
1
+ // WhatsApp Cloud API SDK
2
+ export { WhatsAppClient } from "./client/index";
3
+
4
+ // Export schemas (AI-ready)
5
+ export * from "./schemas/index";
6
+
7
+ // Export types (primary export point)
8
+ export type * from "./types/index";
9
+
10
+ // Export errors for error handling
11
+ export {
12
+ WhatsAppError,
13
+ WhatsAppValidationError,
14
+ WhatsAppAPIError,
15
+ WhatsAppRateLimitError,
16
+ } from "./errors";
@@ -0,0 +1 @@
1
+ export * from "./phone-number";
@@ -0,0 +1,20 @@
1
+ import { z } from "zod";
2
+
3
+ /**
4
+ * Schema for phone number response
5
+ * Matches WhatsApp API structure for phone number objects
6
+ */
7
+ export const phoneNumberResponseSchema = z.object({
8
+ verified_name: z.string(),
9
+ display_phone_number: z.string(),
10
+ id: z.string(),
11
+ quality_rating: z.string(),
12
+ });
13
+
14
+ /**
15
+ * Schema for phone number list response
16
+ * Matches WhatsApp API structure for GET /phone_numbers endpoint
17
+ */
18
+ export const phoneNumberListResponseSchema = z.object({
19
+ data: z.array(phoneNumberResponseSchema),
20
+ });
@@ -0,0 +1,43 @@
1
+ import { z } from "zod";
2
+
3
+ /**
4
+ * Schema for WhatsApp Business Account (WABA) response
5
+ * Matches WhatsApp API structure for WABA objects
6
+ */
7
+ export const businessAccountResponseSchema = z.object({
8
+ id: z.string(),
9
+ name: z.string().optional(),
10
+ account_review_status: z.string().optional(),
11
+ currency: z.string().optional(),
12
+ country: z.string().optional(),
13
+ timezone_id: z.string().optional(),
14
+ business_verification_status: z.string().optional(),
15
+ is_enabled_for_insights: z.boolean().optional(),
16
+ message_template_namespace: z.string().optional(),
17
+ });
18
+
19
+ /**
20
+ * Schema for WhatsApp Business Accounts list response
21
+ * Matches WhatsApp API structure for GET /whatsapp_business_accounts endpoint
22
+ *
23
+ * Note: The API returns data as an object with numeric string keys (e.g., "0", "1")
24
+ * or as an array, plus optional paging information
25
+ */
26
+ export const businessAccountsListResponseSchema = z.object({
27
+ data: z.record(z.string(), businessAccountResponseSchema).or(
28
+ z.array(businessAccountResponseSchema)
29
+ ),
30
+ paging: z
31
+ .object({
32
+ cursors: z
33
+ .object({
34
+ before: z.string().optional(),
35
+ after: z.string().optional(),
36
+ })
37
+ .optional(),
38
+ next: z.string().url().optional(),
39
+ previous: z.string().url().optional(),
40
+ })
41
+ .optional(),
42
+ });
43
+