@wazapi/sdk 0.1.0 → 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 +46 -0
- package/dist/types.d.ts +78 -0
- package/package.json +16 -5
package/README.md
CHANGED
|
@@ -119,6 +119,52 @@ await wazapi.upsertContactByExternalId('customer-1847', {
|
|
|
119
119
|
})
|
|
120
120
|
```
|
|
121
121
|
|
|
122
|
+
## Webhooks
|
|
123
|
+
|
|
124
|
+
Wazapi POSTs events to the HTTPS endpoint you register in **Settings → Wazapi API**.
|
|
125
|
+
The SDK ships the payload types; the union is discriminated on `type`, so narrowing
|
|
126
|
+
`event.type` narrows `event.data` with it.
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
import type { WazapiWebhookEvent } from '@wazapi/sdk'
|
|
130
|
+
|
|
131
|
+
function handle(event: WazapiWebhookEvent) {
|
|
132
|
+
switch (event.type) {
|
|
133
|
+
case 'message.received':
|
|
134
|
+
return reply(event.data.contact_uuid, event.data.content)
|
|
135
|
+
case 'message.status.updated':
|
|
136
|
+
return markDelivered(event.data.message_uuid, event.data.status)
|
|
137
|
+
case 'flow.execution.updated':
|
|
138
|
+
return event.data.error ? alert(event.data.error.code) : done()
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Verify the signature over the **raw** body, before parsing:
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
import { createHmac, timingSafeEqual } from 'node:crypto'
|
|
147
|
+
|
|
148
|
+
function verify(rawBody: string, headers: Record<string, string>, secret: string) {
|
|
149
|
+
const expected = createHmac('sha256', secret)
|
|
150
|
+
.update(`${headers['wazapi-timestamp']}.${rawBody}`)
|
|
151
|
+
.digest('hex')
|
|
152
|
+
const received = (headers['wazapi-signature'] ?? '').replace(/^v1=/, '')
|
|
153
|
+
const a = Buffer.from(expected)
|
|
154
|
+
const b = Buffer.from(received)
|
|
155
|
+
return a.length === b.length && timingSafeEqual(a, b)
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Delivery is at-least-once — deduplicate on `event.id` (also sent as the
|
|
160
|
+
`Wazapi-Event-Id` header). Wazapi treats only `2xx` as success and retries after
|
|
161
|
+
1min, 5min, 30min, 2h, 12h and 24h.
|
|
162
|
+
|
|
163
|
+
Two fields are added at delivery time when they apply: `data.external_id`, your own
|
|
164
|
+
identifier echoed back when the contact was linked via `upsertContactByExternalId`,
|
|
165
|
+
and `data.tracking`, the allowlisted attribution subset (`utm_*`, click IDs, ad
|
|
166
|
+
referral fields). No other custom field key is ever forwarded.
|
|
167
|
+
|
|
122
168
|
## Configuration
|
|
123
169
|
|
|
124
170
|
```ts
|
package/dist/types.d.ts
CHANGED
|
@@ -143,3 +143,81 @@ export interface AcceptedResult {
|
|
|
143
143
|
/** True when the request replayed a previously accepted idempotent operation. */
|
|
144
144
|
replayed: boolean;
|
|
145
145
|
}
|
|
146
|
+
export type WebhookEventType = 'message.received' | 'message.status.updated' | 'conversation.created' | 'conversation.updated' | 'flow.execution.updated' | 'webhook.test';
|
|
147
|
+
/**
|
|
148
|
+
* Allowlisted attribution subset of the contact's and conversation's custom
|
|
149
|
+
* fields. Conversation values win over contact values (last touch over first
|
|
150
|
+
* touch); no other custom field key is ever forwarded.
|
|
151
|
+
*/
|
|
152
|
+
export type WebhookTracking = Record<string, unknown>;
|
|
153
|
+
/**
|
|
154
|
+
* Added at delivery time whenever the payload carries a `contact_uuid` and that
|
|
155
|
+
* contact was linked through `PUT /contacts/external/{external_id}`.
|
|
156
|
+
*/
|
|
157
|
+
interface WebhookContactRefs {
|
|
158
|
+
external_id?: string;
|
|
159
|
+
tracking?: WebhookTracking;
|
|
160
|
+
}
|
|
161
|
+
export interface MessageReceivedData extends WebhookContactRefs {
|
|
162
|
+
message_uuid: string;
|
|
163
|
+
conversation_uuid: string;
|
|
164
|
+
contact_uuid: string;
|
|
165
|
+
channel_uuid: string;
|
|
166
|
+
type: string;
|
|
167
|
+
content: Record<string, unknown>;
|
|
168
|
+
status: string;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* `provider_message_id` comes from the provider status callback; `operation_uuid`
|
|
172
|
+
* and `conversation_uuid` are present when the message was sent through the API.
|
|
173
|
+
*/
|
|
174
|
+
export interface MessageStatusUpdatedData {
|
|
175
|
+
message_uuid: string;
|
|
176
|
+
status: string;
|
|
177
|
+
provider_message_id?: string | null;
|
|
178
|
+
operation_uuid?: string;
|
|
179
|
+
conversation_uuid?: string;
|
|
180
|
+
tracking?: WebhookTracking;
|
|
181
|
+
}
|
|
182
|
+
export interface ConversationCreatedData extends WebhookContactRefs {
|
|
183
|
+
conversation_uuid: string;
|
|
184
|
+
contact_uuid: string;
|
|
185
|
+
channel_uuid: string;
|
|
186
|
+
status: string;
|
|
187
|
+
}
|
|
188
|
+
/** `unread_count` is only present when an inbound message triggered the update. */
|
|
189
|
+
export interface ConversationUpdatedData extends ConversationCreatedData {
|
|
190
|
+
unread_count?: number;
|
|
191
|
+
last_message_at?: string | null;
|
|
192
|
+
}
|
|
193
|
+
export interface FlowExecutionUpdatedData {
|
|
194
|
+
operation_uuid: string;
|
|
195
|
+
operation_type: 'flow.execute';
|
|
196
|
+
status: OperationStatus;
|
|
197
|
+
result: Record<string, unknown> | null;
|
|
198
|
+
error: {
|
|
199
|
+
code: string;
|
|
200
|
+
message: string | null;
|
|
201
|
+
} | null;
|
|
202
|
+
}
|
|
203
|
+
export interface WebhookTestData {
|
|
204
|
+
integration_uuid: string;
|
|
205
|
+
message: string;
|
|
206
|
+
}
|
|
207
|
+
interface WebhookEnvelope<TType extends WebhookEventType, TData> {
|
|
208
|
+
/** Unique event id. Delivery is at-least-once — deduplicate on this value. */
|
|
209
|
+
id: string;
|
|
210
|
+
type: TType;
|
|
211
|
+
api_version: 'v1';
|
|
212
|
+
occurred_at: string;
|
|
213
|
+
data: TData;
|
|
214
|
+
}
|
|
215
|
+
export type MessageReceivedEvent = WebhookEnvelope<'message.received', MessageReceivedData>;
|
|
216
|
+
export type MessageStatusUpdatedEvent = WebhookEnvelope<'message.status.updated', MessageStatusUpdatedData>;
|
|
217
|
+
export type ConversationCreatedEvent = WebhookEnvelope<'conversation.created', ConversationCreatedData>;
|
|
218
|
+
export type ConversationUpdatedEvent = WebhookEnvelope<'conversation.updated', ConversationUpdatedData>;
|
|
219
|
+
export type FlowExecutionUpdatedEvent = WebhookEnvelope<'flow.execution.updated', FlowExecutionUpdatedData>;
|
|
220
|
+
export type WebhookTestEvent = WebhookEnvelope<'webhook.test', WebhookTestData>;
|
|
221
|
+
/** Discriminated on `type` — narrow it and `data` narrows with it. */
|
|
222
|
+
export type WazapiWebhookEvent = MessageReceivedEvent | MessageStatusUpdatedEvent | ConversationCreatedEvent | ConversationUpdatedEvent | FlowExecutionUpdatedEvent | WebhookTestEvent;
|
|
223
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wazapi/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Official Node.js SDK for the Wazapi Public API v1",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -10,8 +10,7 @@
|
|
|
10
10
|
"homepage": "https://wazapi.io",
|
|
11
11
|
"repository": {
|
|
12
12
|
"type": "git",
|
|
13
|
-
"url": "git+https://github.com/wazap-ai/
|
|
14
|
-
"directory": "apps/app/sdk"
|
|
13
|
+
"url": "git+https://github.com/wazap-ai/wazapi-node.git"
|
|
15
14
|
},
|
|
16
15
|
"main": "./dist/index.js",
|
|
17
16
|
"module": "./dist/index.js",
|
|
@@ -22,7 +21,10 @@
|
|
|
22
21
|
"import": "./dist/index.js"
|
|
23
22
|
}
|
|
24
23
|
},
|
|
25
|
-
"files": [
|
|
24
|
+
"files": [
|
|
25
|
+
"dist",
|
|
26
|
+
"README.md"
|
|
27
|
+
],
|
|
26
28
|
"engines": {
|
|
27
29
|
"node": ">=18"
|
|
28
30
|
},
|
|
@@ -31,8 +33,17 @@
|
|
|
31
33
|
"test": "npm run build && node --test --experimental-strip-types test/*.test.ts",
|
|
32
34
|
"prepublishOnly": "npm run build"
|
|
33
35
|
},
|
|
34
|
-
"keywords": [
|
|
36
|
+
"keywords": [
|
|
37
|
+
"wazapi",
|
|
38
|
+
"whatsapp",
|
|
39
|
+
"api",
|
|
40
|
+
"sdk",
|
|
41
|
+
"messaging"
|
|
42
|
+
],
|
|
35
43
|
"devDependencies": {
|
|
36
44
|
"typescript": "^5.6.0"
|
|
45
|
+
},
|
|
46
|
+
"bugs": {
|
|
47
|
+
"url": "https://github.com/wazap-ai/wazapi-node/issues"
|
|
37
48
|
}
|
|
38
49
|
}
|