whatsapp-cloud 0.0.3 → 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 +13 -0
- package/README.md +11 -0
- package/agent_docs/DESIGN.md +707 -0
- package/agent_docs/INCOMING_MESSAGES_BRAINSTORM.md +500 -0
- package/agent_docs/MESSAGES_NAMESPACE_ANALYSIS.md +368 -0
- package/agent_docs/NAMING_DECISION.md +78 -0
- package/agent_docs/STRUCTURE.md +711 -0
- package/agent_docs/messages-namespace-design.md +357 -0
- package/cloud-api-docs/webhooks/endpoint.md +112 -0
- package/cloud-api-docs/webhooks/overview.md +154 -0
- package/package.json +10 -3
- package/src/client/HttpClient.ts +159 -0
- package/src/client/WhatsAppClient.ts +58 -0
- package/src/client/index.ts +2 -0
- package/src/errors.ts +58 -0
- package/src/examples/main.ts +9 -0
- package/src/examples/template.ts +134 -0
- package/src/index.ts +16 -1
- package/src/schemas/accounts/index.ts +1 -0
- package/src/schemas/accounts/phone-number.ts +20 -0
- package/src/schemas/business/account.ts +43 -0
- package/src/schemas/business/index.ts +2 -0
- package/src/schemas/client.ts +50 -0
- package/src/schemas/debug.ts +25 -0
- package/src/schemas/index.ts +6 -0
- package/src/schemas/messages/index.ts +2 -0
- package/src/schemas/messages/request.ts +82 -0
- package/src/schemas/messages/response.ts +19 -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 +34 -0
- package/src/services/accounts/AccountsService.ts +45 -0
- package/src/services/accounts/index.ts +2 -0
- package/src/services/accounts/methods/list-phone-numbers.ts +15 -0
- package/src/services/business/BusinessClient.ts +34 -0
- package/src/services/business/BusinessService.ts +45 -0
- package/src/services/business/index.ts +3 -0
- package/src/services/business/methods/list-accounts.ts +17 -0
- package/src/services/index.ts +2 -0
- package/src/services/messages/MessagesClient.ts +34 -0
- package/src/services/messages/MessagesService.ts +97 -0
- package/src/services/messages/index.ts +8 -0
- package/src/services/messages/methods/send-image.ts +33 -0
- package/src/services/messages/methods/send-location.ts +32 -0
- package/src/services/messages/methods/send-reaction.ts +33 -0
- package/src/services/messages/methods/send-text.ts +32 -0
- package/src/services/messages/utils/build-message-payload.ts +32 -0
- 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/accounts/index.ts +1 -0
- package/src/types/accounts/phone-number.ts +9 -0
- package/src/types/business/account.ts +10 -0
- package/src/types/business/index.ts +2 -0
- package/src/types/client.ts +8 -0
- package/src/types/debug.ts +8 -0
- package/src/types/index.ts +6 -0
- package/src/types/messages/index.ts +2 -0
- package/src/types/messages/request.ts +27 -0
- package/src/types/messages/response.ts +7 -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/src/utils/zod-error.ts +28 -0
- package/tsconfig.json +6 -4
|
@@ -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! 🚀
|