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,707 @@
1
+ # WhatsApp Cloud API SDK - Design Document
2
+
3
+ ## 🎯 Vision
4
+
5
+ Build the most well-designed, type-safe, and developer-friendly WhatsApp Cloud API SDK for TypeScript/JavaScript. The SDK should feel intuitive, provide excellent autocomplete, and handle all the complexity of the WhatsApp Business Platform APIs.
6
+
7
+ ## 📚 API Overview
8
+
9
+ Based on the WhatsApp Business Platform documentation, we have three main API categories:
10
+
11
+ ### 1. **WhatsApp Cloud API** (Messaging)
12
+
13
+ - Send messages (text, media, interactive, location, contacts, etc.)
14
+ - Make and receive calls
15
+ - Manage group conversations
16
+ - Base URL: `https://graph.facebook.com/v{version}/{phone-number-id}/messages`
17
+
18
+ ### 2. **Business Management API** (Account Management)
19
+
20
+ - Manage WhatsApp Business Accounts (WABA)
21
+ - Manage business phone numbers
22
+ - Create and manage message templates
23
+ - Access analytics (messaging, pricing, templates)
24
+ - Base URL: `https://graph.facebook.com/v{version}/{waba-id}/...`
25
+
26
+ ### 3. **Marketing Messages Lite API** (Optimized Marketing)
27
+
28
+ - Send optimized marketing messages
29
+ - Quality-based delivery
30
+ - Performance metrics and recommendations
31
+ - Conversion tracking
32
+
33
+ ### 4. **Webhooks** (Event Handling)
34
+
35
+ - Receive incoming messages
36
+ - Message status updates
37
+ - Account updates
38
+ - Template status changes
39
+
40
+ ## 🏗️ Architecture Design
41
+
42
+ ### Core Principles
43
+
44
+ 1. **Schemas First** - Zod schemas are the single source of truth, types are inferred
45
+ 2. **AI-Ready** - Zod schemas enable LLM function calling and validation
46
+ 3. **Type Safety** - TypeScript types inferred from schemas for full type safety
47
+ 4. **Discriminated Unions** - Type-safe message/response variants using Zod
48
+ 5. **Namespace-based organization** - Clear separation of concerns
49
+ 6. **Best-Practice Structure** - Inspired by Vercel AI SDK & Stripe
50
+ 7. **Error handling** - Comprehensive error types and messages
51
+ 8. **Extensibility** - Easy to add new features without breaking changes
52
+ 9. **Developer experience** - Excellent autocomplete and documentation
53
+
54
+ ### Package Structure
55
+
56
+ **See [STRUCTURE.md](./STRUCTURE.md) for the complete, detailed package structure.**
57
+
58
+ The structure follows a **types-first, schema-driven** approach:
59
+
60
+ ```
61
+ src/
62
+ ├── types/ # TypeScript type definitions (lead with types)
63
+ ├── schemas/ # Zod schemas (AI-ready, runtime validation)
64
+ ├── services/ # Service implementations
65
+ ├── client/ # Client classes
66
+ └── utils/ # Utilities
67
+ ```
68
+
69
+ **Key Pattern**: Types → Schemas → Services
70
+
71
+ 1. Define TypeScript types first
72
+ 2. Create corresponding Zod schemas
73
+ 3. Implement services using both types and schemas
74
+
75
+ ## 🎨 API Design
76
+
77
+ ### Client Initialization
78
+
79
+ ```typescript
80
+ // Simple initialization
81
+ const client = new WhatsAppClient({
82
+ accessToken: "your-access-token",
83
+ phoneNumberId: "123456789", // Optional, can be set per request
84
+ apiVersion: "v18.0", // Optional, defaults to latest
85
+ });
86
+
87
+ // With full configuration
88
+ const client = new WhatsAppClient({
89
+ accessToken: "your-access-token",
90
+ phoneNumberId: "123456789",
91
+ businessAccountId: "987654321", // WABA ID
92
+ apiVersion: "v18.0",
93
+ baseURL: "https://graph.facebook.com", // Optional override
94
+ timeout: 30000, // Request timeout in ms
95
+ retry: {
96
+ maxRetries: 3,
97
+ retryDelay: 1000,
98
+ },
99
+ });
100
+ ```
101
+
102
+ ### Namespace Structure
103
+
104
+ **Initial Release** (Phase 1):
105
+
106
+ - ✅ Messages Service
107
+ - ✅ Templates Service
108
+ - ✅ Accounts Service (including Phone Numbers)
109
+
110
+ **Future Releases**:
111
+
112
+ - ⏳ Groups Service (deferred)
113
+ - ⏳ Analytics Service (deferred)
114
+ - ⏳ Webhooks Service (deferred)
115
+ - ⏳ Calls Service (if needed)
116
+
117
+ ```typescript
118
+ // Messages namespace
119
+ client.messages.sendText({ to: '+1234567890', body: 'Hello!' });
120
+ client.messages.sendImage({ to: '+1234567890', imageUrl: '...' });
121
+ client.messages.sendTemplate({ to: '+1234567890', templateName: 'welcome', language: 'en' });
122
+
123
+ // Templates namespace
124
+ client.templates.create({ name: 'welcome', category: 'MARKETING', components: [...] });
125
+ client.templates.list();
126
+ client.templates.get('template-id');
127
+ client.templates.delete('template-id');
128
+
129
+ // Accounts namespace
130
+ client.accounts.getProfile();
131
+ client.accounts.updateProfile({ about: 'New about text' });
132
+
133
+ // Phone numbers (sub-namespace of accounts)
134
+ client.accounts.phoneNumbers.list();
135
+ client.accounts.phoneNumbers.get('phone-number-id');
136
+ client.accounts.phoneNumbers.update('phone-number-id', { displayName: 'New Name' });
137
+ ```
138
+
139
+ ## 🔧 Detailed Design Decisions
140
+
141
+ ### 1. Phone Number ID Handling
142
+
143
+ **Problem**: Most Cloud API endpoints require a `phone-number-id` in the URL path, but it's not always available at client initialization.
144
+
145
+ **Solution**:
146
+
147
+ - Accept `phoneNumberId` in constructor (for single-number use cases)
148
+ - Allow per-request override via method parameters
149
+ - Support multi-number scenarios with explicit phone number ID in each call
150
+
151
+ ```typescript
152
+ // Option 1: Set at client level
153
+ const client = new WhatsAppClient({
154
+ accessToken: "...",
155
+ phoneNumberId: "123456789",
156
+ });
157
+ client.messages.sendText({ to: "+1234567890", body: "Hello!" });
158
+
159
+ // Option 2: Override per request
160
+ const client = new WhatsAppClient({ accessToken: "..." });
161
+ client.messages.sendText({
162
+ to: "+1234567890",
163
+ body: "Hello!",
164
+ phoneNumberId: "123456789", // Override
165
+ });
166
+ ```
167
+
168
+ ### 2. Message Builders
169
+
170
+ For complex messages (interactive, template with parameters, media with captions), provide fluent builders:
171
+
172
+ ```typescript
173
+ // Simple messages - direct API
174
+ client.messages.sendText({ to: "+1234567890", body: "Hello!" });
175
+
176
+ // Complex messages - builder pattern
177
+ const message = client.messages
178
+ .builder()
179
+ .to("+1234567890")
180
+ .template("welcome")
181
+ .language("en")
182
+ .addParameter("1", "John")
183
+ .addParameter("2", "Doe")
184
+ .build();
185
+
186
+ await client.messages.send(message);
187
+ ```
188
+
189
+ ### 3. Schema-First Approach (AI-Ready)
190
+
191
+ **Schema-First Pattern**: Define Zod schemas → Infer TypeScript types → Implement services.
192
+
193
+ **Why Schema-First?**
194
+
195
+ - ✅ Single source of truth (schema)
196
+ - ✅ AI-ready (LLMs work with Zod schemas directly)
197
+ - ✅ Types automatically stay in sync
198
+ - ✅ Less duplication
199
+ - ✅ Modern best practice (tRPC, Next.js, etc.)
200
+
201
+ ```typescript
202
+ // 1. Define Zod schema first (src/schemas/messages/request.ts)
203
+ import { z } from "zod";
204
+
205
+ export const sendTextRequestSchema = z.object({
206
+ to: z.string().regex(/^\+[1-9]\d{1,14}$/, "Invalid phone number format"),
207
+ body: z.string().min(1).max(4096),
208
+ previewUrl: z.boolean().optional(),
209
+ phoneNumberId: z.string().optional(),
210
+ });
211
+
212
+ // 2. Infer type from schema
213
+ export type SendTextRequest = z.infer<typeof sendTextRequestSchema>;
214
+
215
+ // 3. Optional: Re-export in types/ for convenience (src/types/messages/request.ts)
216
+ export type { SendTextRequest } from "../../schemas/messages/request";
217
+
218
+ // 4. Use in service (src/services/messages/methods/send-text.ts)
219
+ import { sendTextRequestSchema } from "../../../schemas/messages/request";
220
+ import type { SendTextRequest } from "../../../schemas/messages/request";
221
+
222
+ export async function sendText(
223
+ client: HttpClient,
224
+ request: SendTextRequest
225
+ ): Promise<MessageResponse> {
226
+ // Runtime validation with Zod (AI-ready)
227
+ const validated = sendTextRequestSchema.parse(request);
228
+ // Implementation...
229
+ }
230
+ ```
231
+
232
+ **Discriminated Unions for Message Types** (Schema-First):
233
+
234
+ ```typescript
235
+ // 1. Define Zod schemas for each variant (src/schemas/messages/message.ts)
236
+ import { z } from "zod";
237
+
238
+ export const textMessageSchema = z.object({
239
+ type: z.literal("text"),
240
+ text: z.object({
241
+ body: z.string().min(1).max(4096),
242
+ previewUrl: z.boolean().optional(),
243
+ }),
244
+ });
245
+
246
+ export const imageMessageSchema = z.object({
247
+ type: z.literal("image"),
248
+ image: z
249
+ .object({
250
+ link: z.string().url().optional(),
251
+ id: z.string().optional(),
252
+ caption: z.string().max(1024).optional(),
253
+ })
254
+ .refine((data) => data.link || data.id, "Either link or id required"),
255
+ });
256
+
257
+ // ... other message schemas
258
+
259
+ // 2. Discriminated union schema
260
+ export const messageContentSchema = z.discriminatedUnion("type", [
261
+ textMessageSchema,
262
+ imageMessageSchema,
263
+ // ... etc
264
+ ]);
265
+
266
+ // 3. Infer types from schemas
267
+ export type MessageContent = z.infer<typeof messageContentSchema>;
268
+ export type TextMessage = z.infer<typeof textMessageSchema>;
269
+ export type ImageMessage = z.infer<typeof imageMessageSchema>;
270
+ ```
271
+
272
+ This enables:
273
+
274
+ - ✅ Full type safety with TypeScript
275
+ - ✅ Runtime validation with Zod
276
+ - ✅ AI/LLM tool calling (Zod schemas are perfect for function calling)
277
+ - ✅ Type narrowing with discriminated unions
278
+
279
+ ### 4. Error Handling
280
+
281
+ ```typescript
282
+ try {
283
+ await client.messages.sendText({ to: "+1234567890", body: "Hello!" });
284
+ } catch (error) {
285
+ if (error instanceof WhatsAppAPIError) {
286
+ switch (error.code) {
287
+ case 131056: // Rate limit
288
+ // Handle rate limiting
289
+ break;
290
+ case 100: // Invalid parameter
291
+ // Handle validation error
292
+ break;
293
+ default:
294
+ // Handle other errors
295
+ }
296
+ }
297
+ }
298
+ ```
299
+
300
+ ### 5. Business Account vs Phone Number Context
301
+
302
+ **Problem**: Business Management API uses WABA ID, Cloud API uses phone number ID.
303
+
304
+ **Solution**:
305
+
306
+ - Store both IDs in client config
307
+ - Automatically use correct ID based on namespace
308
+ - Allow explicit override when needed
309
+
310
+ ```typescript
311
+ const client = new WhatsAppClient({
312
+ accessToken: "...",
313
+ phoneNumberId: "123456789", // For Cloud API
314
+ businessAccountId: "987654321", // For Business Management API
315
+ });
316
+
317
+ // Cloud API uses phoneNumberId automatically
318
+ client.messages.sendText({ to: "+1234567890", body: "Hello!" });
319
+
320
+ // Business Management API uses businessAccountId automatically
321
+ client.templates.list(); // Uses businessAccountId
322
+ ```
323
+
324
+ ### 6. API Versioning
325
+
326
+ ```typescript
327
+ // Default to latest stable
328
+ const client = new WhatsAppClient({ accessToken: "..." });
329
+
330
+ // Explicit version
331
+ const client = new WhatsAppClient({
332
+ accessToken: "...",
333
+ apiVersion: "v18.0",
334
+ });
335
+
336
+ // Per-request override (if needed for migration)
337
+ client.messages.sendText({
338
+ to: "+1234567890",
339
+ body: "Hello!",
340
+ apiVersion: "v17.0", // Override
341
+ });
342
+ ```
343
+
344
+ ## 📦 Service Details
345
+
346
+ ### Messages Service
347
+
348
+ **Endpoints covered:**
349
+
350
+ - `POST /{phone-number-id}/messages` - Send messages
351
+ - `GET /{phone-number-id}/messages/{message-id}` - Get message status
352
+
353
+ **Methods:**
354
+
355
+ ```typescript
356
+ class MessagesService {
357
+ // Text messages
358
+ sendText(request: SendTextRequest): Promise<MessageResponse>;
359
+
360
+ // Media messages
361
+ sendImage(request: SendImageRequest): Promise<MessageResponse>;
362
+ sendVideo(request: SendVideoRequest): Promise<MessageResponse>;
363
+ sendAudio(request: SendAudioRequest): Promise<MessageResponse>;
364
+ sendDocument(request: SendDocumentRequest): Promise<MessageResponse>;
365
+ sendSticker(request: SendStickerRequest): Promise<MessageResponse>;
366
+
367
+ // Interactive messages
368
+ sendInteractive(request: SendInteractiveRequest): Promise<MessageResponse>;
369
+ sendButton(request: SendButtonRequest): Promise<MessageResponse>;
370
+ sendList(request: SendListRequest): Promise<MessageResponse>;
371
+
372
+ // Location & contacts
373
+ sendLocation(request: SendLocationRequest): Promise<MessageResponse>;
374
+ sendContacts(request: SendContactsRequest): Promise<MessageResponse>;
375
+
376
+ // Template messages
377
+ sendTemplate(request: SendTemplateRequest): Promise<MessageResponse>;
378
+
379
+ // Reactions
380
+ sendReaction(request: SendReactionRequest): Promise<MessageResponse>;
381
+
382
+ // Read receipts
383
+ markAsRead(messageId: string): Promise<void>;
384
+
385
+ // Message status
386
+ getStatus(messageId: string): Promise<MessageStatus>;
387
+
388
+ // Builder for complex messages
389
+ builder(): MessageBuilder;
390
+ }
391
+ ```
392
+
393
+ ### Templates Service
394
+
395
+ **Endpoints covered:**
396
+
397
+ - `POST /{waba-id}/message_templates` - Create template
398
+ - `GET /{waba-id}/message_templates` - List templates
399
+ - `GET /{template-id}` - Get template
400
+ - `DELETE /{template-id}` - Delete template
401
+
402
+ **Methods:**
403
+
404
+ ```typescript
405
+ class TemplatesService {
406
+ create(request: CreateTemplateRequest): Promise<TemplateResponse>;
407
+ list(filters?: TemplateFilters): Promise<TemplateListResponse>;
408
+ get(templateId: string): Promise<TemplateResponse>;
409
+ update(
410
+ templateId: string,
411
+ request: UpdateTemplateRequest
412
+ ): Promise<TemplateResponse>;
413
+ delete(templateId: string): Promise<void>;
414
+ getStatus(templateId: string): Promise<TemplateStatus>;
415
+ }
416
+ ```
417
+
418
+ ### Accounts Service
419
+
420
+ **Endpoints covered:**
421
+
422
+ - `GET /{phone-number-id}` - Get phone number info
423
+ - `PATCH /{phone-number-id}` - Update phone number
424
+ - `GET /{waba-id}` - Get WABA info
425
+ - `GET /{waba-id}/phone_numbers` - List phone numbers
426
+
427
+ **Methods:**
428
+
429
+ ```typescript
430
+ class AccountsService {
431
+ // WABA operations
432
+ getBusinessAccount(wabaId?: string): Promise<BusinessAccountResponse>;
433
+
434
+ // Phone number operations (sub-namespace)
435
+ phoneNumbers: {
436
+ list(wabaId?: string): Promise<PhoneNumberListResponse>;
437
+ get(phoneNumberId: string): Promise<PhoneNumberResponse>;
438
+ update(
439
+ phoneNumberId: string,
440
+ request: UpdatePhoneNumberRequest
441
+ ): Promise<PhoneNumberResponse>;
442
+ getMetrics(
443
+ phoneNumberId: string,
444
+ period: MetricsPeriod
445
+ ): Promise<PhoneNumberMetrics>;
446
+ };
447
+
448
+ // Profile operations
449
+ getProfile(phoneNumberId?: string): Promise<ProfileResponse>;
450
+ updateProfile(
451
+ phoneNumberId: string,
452
+ request: UpdateProfileRequest
453
+ ): Promise<ProfileResponse>;
454
+ }
455
+ ```
456
+
457
+ ### Services Not in Initial Release
458
+
459
+ The following services are planned for future releases:
460
+
461
+ - **Analytics Service** - Deferred to Phase 2
462
+ - **Groups Service** - Deferred to Phase 2
463
+ - **Webhooks Service** - Deferred to Phase 2
464
+ - **Calls Service** - Deferred (if needed)
465
+
466
+ ## 🎯 Design Questions & Decisions
467
+
468
+ ### Q1: Should we support both callback and promise-based APIs?
469
+
470
+ **Decision**: Promise-based only (async/await). Modern JavaScript/TypeScript standard.
471
+
472
+ ### Q2: How to handle pagination?
473
+
474
+ **Decision**: Provide both manual and automatic pagination:
475
+
476
+ ```typescript
477
+ // Manual pagination
478
+ const response = await client.templates.list({ limit: 50 });
479
+ if (response.paging?.next) {
480
+ const nextPage = await client.templates.list({
481
+ limit: 50,
482
+ after: response.paging.cursors.after,
483
+ });
484
+ }
485
+
486
+ // Automatic pagination (helper method)
487
+ const allTemplates = await client.templates.listAll(); // Fetches all pages
488
+ ```
489
+
490
+ ### Q3: Should we include request/response interceptors?
491
+
492
+ **Decision**: Yes, for logging, retry logic, and custom headers:
493
+
494
+ ```typescript
495
+ const client = new WhatsAppClient({
496
+ accessToken: "...",
497
+ interceptors: {
498
+ request: [
499
+ (config) => {
500
+ console.log("Request:", config);
501
+ return config;
502
+ },
503
+ ],
504
+ response: [
505
+ (response) => {
506
+ console.log("Response:", response);
507
+ return response;
508
+ },
509
+ ],
510
+ error: [
511
+ (error) => {
512
+ console.error("Error:", error);
513
+ return Promise.reject(error);
514
+ },
515
+ ],
516
+ },
517
+ });
518
+ ```
519
+
520
+ ### Q4: How to handle rate limiting?
521
+
522
+ **Decision**: Built-in retry with exponential backoff:
523
+
524
+ ```typescript
525
+ const client = new WhatsAppClient({
526
+ accessToken: "...",
527
+ retry: {
528
+ maxRetries: 3,
529
+ retryDelay: 1000,
530
+ retryableStatusCodes: [429, 503, 504],
531
+ onRetry: (attempt, error) => {
532
+ console.log(`Retry attempt ${attempt}:`, error);
533
+ },
534
+ },
535
+ });
536
+ ```
537
+
538
+ ### Q5: Should we validate inputs?
539
+
540
+ **Decision**: Yes, with helpful error messages:
541
+
542
+ ```typescript
543
+ // Validate phone number format
544
+ client.messages.sendText({
545
+ to: "invalid", // Throws ValidationError with helpful message
546
+ body: "Hello!",
547
+ });
548
+ ```
549
+
550
+ ### Q6: How to handle webhook verification and parsing?
551
+
552
+ **Decision**: Deferred to Phase 2. Will provide utilities when webhooks service is implemented.
553
+
554
+ ## 🧪 Testing Strategy
555
+
556
+ 1. **Unit tests** - Test each service method with mocked HTTP client
557
+ 2. **Integration tests** - Test against WhatsApp API (with test credentials)
558
+ 3. **Type tests** - Ensure TypeScript types are correct
559
+ 4. **Error handling tests** - Test all error scenarios
560
+
561
+ ## 📝 Documentation Plan
562
+
563
+ 1. **README.md** - Quick start, installation, basic examples
564
+ 2. **API Reference** - Generated from TypeScript types
565
+ 3. **Guides** - Common use cases, best practices
566
+ 4. **Examples** - Real-world examples for each feature
567
+ 5. **Migration guides** - When breaking changes occur
568
+
569
+ ## 🚀 Implementation Phases
570
+
571
+ ### Phase 1: Foundation (Schemas First)
572
+
573
+ - [ ] Core schemas: `common.ts`, `errors.ts` with Zod
574
+ - [ ] Core types: Infer from schemas (or explicit for complex cases like `client.ts`)
575
+ - [ ] `HttpClient` class with error handling
576
+ - [ ] `WhatsAppClient` base class (skeleton)
577
+ - [ ] Error schemas with discriminated unions
578
+
579
+ ### Phase 2: Messages Service
580
+
581
+ - [ ] Schemas: `messages/request.ts`, `messages/response.ts`, `messages/message.ts` with discriminated unions
582
+ - [ ] Types: Infer from schemas (re-export in `types/` for convenience)
583
+ - [ ] Service: `MessagesService` class
584
+ - [ ] Methods: `sendText`, `sendImage`, `sendVideo`, `sendAudio`, `sendDocument`
585
+ - [ ] Methods: `sendLocation`, `sendContacts`, `sendTemplate`, `sendInteractive`, `sendReaction`
586
+ - [ ] Methods: `markAsRead`, `getStatus`
587
+
588
+ ### Phase 3: Templates Service
589
+
590
+ - [ ] Schemas: `templates/request.ts`, `templates/response.ts`, `templates/template.ts` with discriminated unions
591
+ - [ ] Types: Infer from schemas (re-export in `types/` for convenience)
592
+ - [ ] Service: `TemplatesService` class
593
+ - [ ] Methods: `create`, `list`, `get`, `update`, `delete`, `getStatus`
594
+
595
+ ### Phase 4: Accounts Service
596
+
597
+ - [ ] Schemas: `accounts/*` (WABA, phone numbers, profile) with Zod validation
598
+ - [ ] Types: Infer from schemas (re-export in `types/` for convenience)
599
+ - [ ] Service: `AccountsService` class
600
+ - [ ] Methods: `getProfile`, `updateProfile`
601
+ - [ ] Service: `PhoneNumbersService` class
602
+ - [ ] Methods: `list`, `get`, `update`
603
+
604
+ ### Phase 5: Polish & Documentation
605
+
606
+ - [ ] Comprehensive error handling
607
+ - [ ] Input validation with helpful messages
608
+ - [ ] JSDoc documentation for all public APIs
609
+ - [ ] Usage examples
610
+ - [ ] Unit tests for schemas
611
+ - [ ] Integration tests for services
612
+
613
+ ### Future Phases (Phase 2+)
614
+
615
+ - [ ] Analytics Service
616
+ - [ ] Groups Service
617
+ - [ ] Webhooks Service
618
+ - [ ] Calls Service (if needed)
619
+ - [ ] Marketing Messages Lite API
620
+
621
+ ## 💭 Open Questions for Discussion
622
+
623
+ 1. **Should we support both ESM and CJS?**
624
+
625
+ - Current setup supports both via tsup ✅
626
+
627
+ 2. **How to handle different API versions?**
628
+
629
+ - Default to latest, allow override per client/request
630
+
631
+ 3. **Should we provide a CLI tool?**
632
+
633
+ - Maybe in future, focus on SDK first
634
+
635
+ 4. **How to handle file uploads for media?**
636
+
637
+ - Support both URL and file path/stream
638
+
639
+ 5. **Should we cache template lists?**
640
+
641
+ - Optional caching layer, configurable TTL
642
+
643
+ 6. **How to handle webhook signature verification?**
644
+ - Provide utility methods, but don't enforce
645
+
646
+ ## 🎨 Code Style & Conventions
647
+
648
+ - **Naming**: camelCase for methods, PascalCase for types/classes
649
+ - **Async**: Always use async/await, never callbacks
650
+ - **Types**: Explicit types everywhere, no `any` unless absolutely necessary
651
+ - **Schemas**: Every type has a corresponding Zod schema
652
+ - **Discriminated Unions**: Use for all variant types (messages, errors, etc.)
653
+ - **Errors**: Custom error classes with discriminated union types
654
+ - **Documentation**: JSDoc comments for all public methods
655
+
656
+ ## 🔑 Key Design Patterns
657
+
658
+ ### Schemas → Types → Services
659
+
660
+ 1. **Define Zod schemas first** (`src/schemas/`) - Single source of truth, AI-ready
661
+ 2. **Infer TypeScript types** (`src/types/`) - Automatic type safety from schemas
662
+ 3. **Implement services** (`src/services/`) - Use schemas for validation, types for safety
663
+
664
+ ### Discriminated Unions
665
+
666
+ Use discriminated unions for:
667
+
668
+ - Message content types (`text`, `image`, `video`, etc.)
669
+ - Template component types (`header`, `body`, `button`)
670
+ - API error types (rate limit, invalid parameter, etc.)
671
+ - Response variants
672
+
673
+ ### Schema Validation
674
+
675
+ Every service method:
676
+
677
+ 1. Accepts typed request (inferred from schema)
678
+ 2. Validates with Zod schema (runtime validation)
679
+ 3. Returns typed response (inferred from schema)
680
+ 4. Handles errors with typed error classes (inferred from schema)
681
+
682
+ **Key Benefit**: Types and schemas are always in sync because types are inferred from schemas.
683
+
684
+ ---
685
+
686
+ ## 📚 Related Documents
687
+
688
+ - **[STRUCTURE.md](./STRUCTURE.md)** - Complete package structure with file organization
689
+ - Detailed type definitions
690
+ - Schema patterns
691
+ - Service implementation patterns
692
+
693
+ ---
694
+
695
+ ## ✅ Design Decisions Made
696
+
697
+ 1. **Schemas First** - Zod schemas are the single source of truth, types are inferred
698
+ 2. **AI-Ready** - Zod schemas enable LLM function calling and validation
699
+ 3. **Type Safety** - TypeScript types inferred from schemas (always in sync)
700
+ 4. **Discriminated Unions** - Type-safe variants using Zod discriminated unions
701
+ 5. **Initial Scope** - Messages, Templates, Accounts only
702
+ 6. **Best-Practice Structure** - Inspired by Vercel AI SDK & Stripe
703
+ 7. **Namespace Organization** - Clear separation of concerns
704
+
705
+ **See [SCHEMA_VS_TYPE_ANALYSIS.md](./SCHEMA_VS_TYPE_ANALYSIS.md) for detailed reasoning on schema-first approach.**
706
+
707
+ Ready to start implementation! 🚀