@wazzapi/wazzapi 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,232 @@
1
+ # WazzAPI Node SDK
2
+
3
+ Official Node.js and TypeScript SDK for WazzAPI.
4
+
5
+ This package is a modern Node.js and TypeScript SDK for WazzAPI, built with Bun-powered development, typed models, ESM/CJS output, and an API surface that feels natural in TypeScript.
6
+
7
+ Use it to send WhatsApp messages, manage contacts and groups, work with templates, verify webhooks, and download encrypted WhatsApp media.
8
+
9
+ ## Highlights
10
+
11
+ - typed `WazzapiClient` with resource-based API access
12
+ - Node.js 20, 22, and 24 support
13
+ - ESM and CommonJS package output
14
+ - bundled declaration files for TypeScript consumers
15
+ - camelCase APIs for idiomatic TS usage
16
+ - snake_case aliases for compatibility with existing naming styles
17
+ - built-in webhook verification helpers
18
+ - encrypted media download and decryption helpers
19
+ - standard and advanced runnable examples
20
+
21
+ ## Supported Node.js versions
22
+
23
+ This library supports the following Node.js implementations:
24
+
25
+ - Node.js 20
26
+ - Node.js 22
27
+ - Node.js 24 (LTS)
28
+
29
+ TypeScript consumers are supported as long as their compiler can consume the emitted declaration files.
30
+
31
+ ## Warning
32
+
33
+ Do not use this Node.js library in a front-end application. Doing so can expose your WazzAPI credentials to end-users as part of the bundled HTML and JavaScript sent to their browser.
34
+
35
+ ## Installation
36
+
37
+ ### Bun
38
+
39
+ ```bash
40
+ bun add @wazzapi/wazzapi
41
+ ```
42
+
43
+ ### npm
44
+
45
+ ```bash
46
+ npm install @wazzapi/wazzapi
47
+ ```
48
+
49
+ ## Configuration
50
+
51
+ The SDK uses `https://api.wazzapi.com` by default.
52
+
53
+ For most integrations, you only need:
54
+
55
+ - `WAZZAPI_API_KEY`
56
+
57
+ If you plan to receive webhooks, also configure:
58
+
59
+ - `WAZZAPI_WEBHOOK_SECRET`
60
+
61
+ Advanced media examples also use:
62
+
63
+ - `WAZZAPI_MEDIA_URL`
64
+ - `WAZZAPI_MEDIA_KEY`
65
+ - `WAZZAPI_MEDIA_MIMETYPE`
66
+ - `WAZZAPI_MEDIA_FILE_NAME`
67
+ - `WAZZAPI_MEDIA_SHA256`
68
+ - `WAZZAPI_MEDIA_ENC_SHA256`
69
+
70
+ ## Quick start
71
+
72
+ ```ts
73
+ import { WazzapiClient } from "@wazzapi/wazzapi";
74
+
75
+ const client = new WazzapiClient({
76
+ apiKey: process.env.WAZZAPI_API_KEY,
77
+ });
78
+
79
+ const response = await client.messages.send({
80
+ phone_number: "+6281234567890",
81
+ whatsapp_account_id: "your-whatsapp-account-id",
82
+ content: "Hello from WazzAPI!",
83
+ });
84
+
85
+ console.log(response.message_id, response.status);
86
+ ```
87
+
88
+ ## API shape
89
+
90
+ The main client exposes four primary resources:
91
+
92
+ - `client.contacts`
93
+ - `client.groups`
94
+ - `client.messages`
95
+ - `client.templates`
96
+
97
+ Preferred TypeScript method names use camelCase:
98
+
99
+ - `client.contacts.listGroups()`
100
+ - `client.groups.getParticipants()`
101
+ - `client.messages.sendImage()`
102
+ - `client.templates.builtinVariables()`
103
+
104
+ Snake_case aliases are also available:
105
+
106
+ - `client.contacts.list_groups()`
107
+ - `client.groups.get_participants()`
108
+ - `client.messages.send_image()`
109
+ - `client.templates.builtin_variables()`
110
+
111
+ ## Error handling
112
+
113
+ When the API returns a non-success response, the SDK throws `WazzapiAPIError`.
114
+
115
+ ```ts
116
+ import { WazzapiAPIError, WazzapiClient } from "@wazzapi/wazzapi";
117
+
118
+ try {
119
+ const client = new WazzapiClient({ apiKey: process.env.WAZZAPI_API_KEY });
120
+ await client.messages.get("missing-message-id");
121
+ } catch (error) {
122
+ if (error instanceof WazzapiAPIError) {
123
+ console.error(error.statusCode);
124
+ console.error(error.message);
125
+ console.error(error.details);
126
+ }
127
+ }
128
+ ```
129
+
130
+ ## Webhook verification
131
+
132
+ Use `WebhookHandler` to verify the raw request body against `X-Wazzapi-Signature` before parsing JSON.
133
+
134
+ ```ts
135
+ import { WebhookHandler } from "@wazzapi/wazzapi";
136
+
137
+ const handler = new WebhookHandler(process.env.WAZZAPI_WEBHOOK_SECRET || "");
138
+ const event = handler.verifyAndParse(rawBody, request.headers);
139
+
140
+ console.log(event.event_type);
141
+ console.log(event.data);
142
+ ```
143
+
144
+ WazzAPI webhook headers:
145
+
146
+ - `X-Wazzapi-Signature`
147
+ - `X-Wazzapi-Event`
148
+ - `X-Wazzapi-Event-ID`
149
+
150
+ Supported webhook event families:
151
+
152
+ - message events: `message.received`, `message.sent`, `message.delivered`, `message.read`, `message.failed`
153
+ - device events: `device.connected`, `device.disconnected`
154
+
155
+ ## Media downloads
156
+
157
+ Use `downloadMedia()` to retrieve and decrypt WhatsApp media payloads.
158
+
159
+ ```ts
160
+ import { downloadMedia } from "@wazzapi/wazzapi";
161
+
162
+ const file = await downloadMedia(mediaUrl, mediaKey, mimeType, {
163
+ file_name: "invoice.pdf",
164
+ file_sha256: expectedPlainSha256,
165
+ file_enc_sha256: expectedEncryptedSha256,
166
+ });
167
+
168
+ console.log(file.file_name, file.file_size);
169
+ ```
170
+
171
+ ## Examples
172
+
173
+ ### Standard examples
174
+
175
+ - `examples/list-contacts.ts`
176
+ - `examples/send-message.ts`
177
+ - `examples/create-template.ts`
178
+ - `examples/preview-template.ts`
179
+ - `examples/verify-webhook.ts`
180
+
181
+ ### Advanced examples
182
+
183
+ - `advanced-examples/custom-fetch.ts` — custom fetch injection for logging and telemetry
184
+ - `advanced-examples/download-media.ts` — encrypted media download and verification
185
+ - `advanced-examples/http-webhook-server.ts` — minimal Node HTTP webhook receiver
186
+
187
+ ## Documentation
188
+
189
+ Topic-based documentation is available in `docs/`:
190
+
191
+ - `docs/README.md`
192
+ - `docs/authentication.md`
193
+ - `docs/client.md`
194
+ - `docs/messages.md`
195
+ - `docs/groups.md`
196
+ - `docs/contacts.md`
197
+ - `docs/templates.md`
198
+ - `docs/webhooks.md`
199
+ - `docs/media.md`
200
+ - `docs/errors.md`
201
+
202
+ Repository: <https://github.com/wazzapihq/wazzapi-node>
203
+
204
+ ## Try it
205
+
206
+ ```bash
207
+ bun run examples/list-contacts.ts
208
+ bun run examples/send-message.ts
209
+ bun run examples/create-template.ts
210
+ bun run examples/preview-template.ts
211
+ bun run examples/verify-webhook.ts
212
+ bun run advanced-examples/custom-fetch.ts
213
+ bun run advanced-examples/download-media.ts
214
+ bun run advanced-examples/http-webhook-server.ts
215
+ ```
216
+
217
+ ## Development
218
+
219
+ ```bash
220
+ bun install
221
+ bun run typecheck
222
+ bun test
223
+ bun run build
224
+ ```
225
+
226
+ ## Package output
227
+
228
+ The build produces:
229
+
230
+ - `dist/index.js` — ESM bundle
231
+ - `dist/index.cjs` — CommonJS bundle
232
+ - `dist/index.d.ts` — TypeScript declarations
@@ -0,0 +1,40 @@
1
+ import { WazzapiClient } from "../src";
2
+
3
+ function requireEnv(name: string): string {
4
+ const value = process.env[name];
5
+ if (!value) {
6
+ throw new Error(`Missing required environment variable: ${name}`);
7
+ }
8
+
9
+ return value;
10
+ }
11
+
12
+ async function loggingFetch(
13
+ input: string | URL | Request,
14
+ init?: RequestInit,
15
+ ): Promise<Response> {
16
+ let url: string;
17
+
18
+ if (typeof input === "string") {
19
+ url = input;
20
+ } else if (input instanceof URL) {
21
+ url = input.toString();
22
+ } else {
23
+ url = input.url;
24
+ }
25
+
26
+ console.log(`[wazzapi] ${init?.method ?? "GET"} ${url}`);
27
+ return fetch(input, init);
28
+ }
29
+
30
+ const client = new WazzapiClient({
31
+ apiKey: requireEnv("WAZZAPI_API_KEY"),
32
+ fetch: loggingFetch,
33
+ timeout: 15000,
34
+ headers: {
35
+ "X-SDK-Example": "advanced-custom-fetch",
36
+ },
37
+ });
38
+
39
+ const stats = await client.messages.stats();
40
+ console.log(stats);
@@ -0,0 +1,27 @@
1
+ import { downloadMedia } from "../src";
2
+
3
+ function requireEnv(name: string): string {
4
+ const value = process.env[name];
5
+ if (!value) {
6
+ throw new Error(`Missing required environment variable: ${name}`);
7
+ }
8
+
9
+ return value;
10
+ }
11
+
12
+ const result = await downloadMedia(
13
+ requireEnv("WAZZAPI_MEDIA_URL"),
14
+ requireEnv("WAZZAPI_MEDIA_KEY"),
15
+ process.env.WAZZAPI_MEDIA_MIMETYPE || "application/octet-stream",
16
+ {
17
+ file_name: process.env.WAZZAPI_MEDIA_FILE_NAME || "media.bin",
18
+ file_sha256: process.env.WAZZAPI_MEDIA_SHA256 || undefined,
19
+ file_enc_sha256: process.env.WAZZAPI_MEDIA_ENC_SHA256 || undefined,
20
+ },
21
+ );
22
+
23
+ console.log({
24
+ file_name: result.file_name,
25
+ file_size: result.file_size,
26
+ mimetype: result.mimetype,
27
+ });
@@ -0,0 +1,80 @@
1
+ /// <reference types="node" />
2
+
3
+ import { Buffer } from "node:buffer";
4
+ import { createServer, type IncomingMessage } from "node:http";
5
+
6
+ import {
7
+ EVENT_HEADER,
8
+ EVENT_ID_HEADER,
9
+ SIGNATURE_HEADER,
10
+ WebhookHandler,
11
+ } from "../src";
12
+
13
+ function requireEnv(name: string): string {
14
+ const value = process.env[name];
15
+ if (!value) {
16
+ throw new Error(`Missing required environment variable: ${name}`);
17
+ }
18
+
19
+ return value;
20
+ }
21
+
22
+ function readRequestBody(request: IncomingMessage): Promise<Buffer> {
23
+ return new Promise((resolve, reject) => {
24
+ const chunks: Buffer[] = [];
25
+
26
+ request.on("data", (chunk: Buffer | string) => {
27
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
28
+ });
29
+ request.on("end", () => resolve(Buffer.concat(chunks)));
30
+ request.on("error", reject);
31
+ });
32
+ }
33
+
34
+ const handler = new WebhookHandler(requireEnv("WAZZAPI_WEBHOOK_SECRET"));
35
+ const port = Number(process.env.PORT || 3000);
36
+
37
+ const server = createServer(async (request, response) => {
38
+ if (request.method !== "POST" || request.url !== "/webhooks/wazzapi") {
39
+ response.statusCode = 404;
40
+ response.end("Not Found");
41
+ return;
42
+ }
43
+
44
+ try {
45
+ const rawBody = await readRequestBody(request);
46
+ const headers = {
47
+ [SIGNATURE_HEADER]: request.headers[
48
+ SIGNATURE_HEADER.toLowerCase()
49
+ ] as string,
50
+ [EVENT_HEADER]: request.headers[EVENT_HEADER.toLowerCase()] as string,
51
+ [EVENT_ID_HEADER]: request.headers[
52
+ EVENT_ID_HEADER.toLowerCase()
53
+ ] as string,
54
+ };
55
+
56
+ const event = handler.verifyAndParse(rawBody, headers);
57
+ console.log(`received ${event.event_type}`);
58
+ console.log(event.data);
59
+
60
+ response.statusCode = 200;
61
+ response.setHeader("content-type", "application/json");
62
+ response.end(JSON.stringify({ ok: true }));
63
+ } catch (error) {
64
+ console.error(error);
65
+ response.statusCode = 400;
66
+ response.setHeader("content-type", "application/json");
67
+ response.end(
68
+ JSON.stringify({
69
+ ok: false,
70
+ error: error instanceof Error ? error.message : "unknown error",
71
+ }),
72
+ );
73
+ }
74
+ });
75
+
76
+ server.listen(port, () => {
77
+ console.log(
78
+ `Webhook server listening on http://localhost:${port}/webhooks/wazzapi`,
79
+ );
80
+ });