@wassist/sdk 0.1.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/CHANGELOG.md +12 -0
- package/LICENSE +21 -0
- package/README.md +282 -0
- package/dist/index.cjs +1134 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1626 -0
- package/dist/index.d.ts +1626 -0
- package/dist/index.js +1113 -0
- package/dist/index.js.map +1 -0
- package/package.json +61 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0 (Unreleased)
|
|
4
|
+
|
|
5
|
+
Initial release.
|
|
6
|
+
|
|
7
|
+
- Typed client for the public Wassist REST API: `agents`, `conversations` (+ `messages`), `phoneNumbers`, `whatsappAccounts`, `whatsappLinkSessions`, `whatsappTemplates`.
|
|
8
|
+
- Auto-paginating list endpoints with `for await` support and `.firstPage()`.
|
|
9
|
+
- Automatic retries on `429` and `5xx` with exponential backoff and `Retry-After` honoring.
|
|
10
|
+
- Per-call idempotency keys for `POST` mutations (`Idempotency-Key` header).
|
|
11
|
+
- Typed error hierarchy with `statusCode`, `code`, `requestId`, and raw response body.
|
|
12
|
+
- Stripe-style webhook signature verification via `wassist.webhooks.constructEvent()` (sync) and `constructEventAsync()` (Web Crypto, for edge runtimes).
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Wassist
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
# Wassist SDK
|
|
2
|
+
|
|
3
|
+
The TypeScript client for the [Wassist API](https://docs.wassist.app/api-reference). Build WhatsApp agents, send messages, manage templates, and verify webhooks from any modern JavaScript runtime.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@wassist/sdk)
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @wassist/sdk
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Requires Node.js >= 18, or any runtime with a global `fetch` (Deno, Bun, Cloudflare Workers, Vercel Edge, modern browsers).
|
|
14
|
+
|
|
15
|
+
## Quickstart
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { Wassist } from '@wassist/sdk';
|
|
19
|
+
|
|
20
|
+
const wassist = new Wassist({ apiKey: process.env.WASSIST_API_KEY! });
|
|
21
|
+
|
|
22
|
+
// Create an agent
|
|
23
|
+
const agent = await wassist.agents.create({ name: 'Sales Assistant' });
|
|
24
|
+
|
|
25
|
+
// Configure it
|
|
26
|
+
await wassist.agents.update(agent.id, {
|
|
27
|
+
systemPrompt: 'You help customers pick the right product.',
|
|
28
|
+
firstMessage: 'Hi! How can I help today?',
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// Start a conversation
|
|
32
|
+
const conversation = await wassist.conversations.create({
|
|
33
|
+
toNumber: '+447700900100',
|
|
34
|
+
agentId: agent.id,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Send a message
|
|
38
|
+
await wassist.conversations.messages.send(conversation.id, {
|
|
39
|
+
type: 'text',
|
|
40
|
+
text: { body: 'Hello!' },
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// Walk every conversation lazily
|
|
44
|
+
for await (const c of wassist.conversations.list()) {
|
|
45
|
+
console.log(c.id, c.lastMessage?.body);
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Get your API key at [wassist.app/settings](https://wassist.app/settings).
|
|
50
|
+
|
|
51
|
+
## Configuration
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
const wassist = new Wassist({
|
|
55
|
+
apiKey: process.env.WASSIST_API_KEY!, // required
|
|
56
|
+
baseUrl: 'https://backend.wassist.app', // default
|
|
57
|
+
timeout: 60_000, // ms; default 60s
|
|
58
|
+
maxRetries: 2, // default 2 (so 3 attempts total)
|
|
59
|
+
fetch: customFetch, // optional override
|
|
60
|
+
});
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Resources
|
|
64
|
+
|
|
65
|
+
| Namespace | Methods |
|
|
66
|
+
|-----------|---------|
|
|
67
|
+
| `wassist.agents` | `list`, `get`, `create`, `createBYOA`, `update`, `delete` |
|
|
68
|
+
| `wassist.conversations` | `list`, `get`, `create`, `read`, `typing`, `prompt`, `subscribe`, `unsubscribe` |
|
|
69
|
+
| `wassist.conversations.messages` | `list`, `send` |
|
|
70
|
+
| `wassist.phoneNumbers` | `list`, `getBusinessProfile`, `subscribe`, `connectAgent`, `unsubscribe` |
|
|
71
|
+
| `wassist.whatsappAccounts` | `list`, `get`, `addNumber`, `availableNumbers` |
|
|
72
|
+
| `wassist.whatsappLinkSessions` | `list`, `get`, `create`, `expire` |
|
|
73
|
+
| `wassist.whatsappTemplates` | `list`, `get`, `create`, `update`, `delete`, `publish`, `unpublish` |
|
|
74
|
+
| `wassist.webhooks` | `constructEvent`, `constructEventAsync` |
|
|
75
|
+
|
|
76
|
+
Every method is fully typed against the [public API reference](https://docs.wassist.app/api-reference) — your editor knows every field on every input and response.
|
|
77
|
+
|
|
78
|
+
## Pagination
|
|
79
|
+
|
|
80
|
+
List methods return a lazy `AutoPaginatedList<T>`. Use `for await` to walk all pages, or `.firstPage()` for a single page.
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
// Iterate every conversation (pages are fetched on demand)
|
|
84
|
+
for await (const conv of wassist.conversations.list({ limit: 100 })) {
|
|
85
|
+
// ...
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// One page at a time
|
|
89
|
+
const { data, hasMore, nextOffset } = await wassist.conversations
|
|
90
|
+
.list({ limit: 20 })
|
|
91
|
+
.firstPage();
|
|
92
|
+
|
|
93
|
+
// Eagerly collect everything (small lists only)
|
|
94
|
+
const allAgents = await wassist.agents.list().all();
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Errors
|
|
98
|
+
|
|
99
|
+
Every API failure throws a typed subclass of `WassistError`. Branch on `instanceof` to handle each case.
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
import {
|
|
103
|
+
WassistError,
|
|
104
|
+
WassistAuthenticationError,
|
|
105
|
+
WassistInvalidRequestError,
|
|
106
|
+
WassistNotFoundError,
|
|
107
|
+
WassistRateLimitError,
|
|
108
|
+
WassistTimeoutError,
|
|
109
|
+
} from '@wassist/sdk';
|
|
110
|
+
|
|
111
|
+
try {
|
|
112
|
+
await wassist.agents.get('not-a-real-id');
|
|
113
|
+
} catch (err) {
|
|
114
|
+
if (err instanceof WassistAuthenticationError) {
|
|
115
|
+
// 401 — bad/missing API key
|
|
116
|
+
} else if (err instanceof WassistNotFoundError) {
|
|
117
|
+
// 404
|
|
118
|
+
} else if (err instanceof WassistRateLimitError) {
|
|
119
|
+
console.log('retry after', err.retryAfter, 'seconds');
|
|
120
|
+
} else if (err instanceof WassistInvalidRequestError) {
|
|
121
|
+
// 400 / 422
|
|
122
|
+
} else if (err instanceof WassistTimeoutError) {
|
|
123
|
+
// request didn't complete within `timeout` ms
|
|
124
|
+
} else if (err instanceof WassistError) {
|
|
125
|
+
console.log(err.statusCode, err.code, err.requestId, err.raw);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Every error carries `statusCode`, `code`, and the `requestId` from the `X-Request-Id` response header — quote it when filing support tickets.
|
|
131
|
+
|
|
132
|
+
## Retries and idempotency
|
|
133
|
+
|
|
134
|
+
The SDK automatically retries `429`, `408`, `425`, `5xx`, and network failures with exponential backoff + jitter. `Retry-After` headers on `429` are honored.
|
|
135
|
+
|
|
136
|
+
For mutations that need to be safe against retries, pass an `idempotencyKey`:
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
import { randomUUID } from 'node:crypto';
|
|
140
|
+
|
|
141
|
+
const conv = await wassist.conversations.create(
|
|
142
|
+
{ toNumber: '+447700900100', agentId },
|
|
143
|
+
{ idempotencyKey: randomUUID() }
|
|
144
|
+
);
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
The key is forwarded as the `Idempotency-Key` header; the server deduplicates within a 24-hour window.
|
|
148
|
+
|
|
149
|
+
You can also tune retries and timeouts per call:
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
await wassist.agents.list({}, { timeout: 5_000, maxRetries: 0 });
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
And cancel any request:
|
|
156
|
+
|
|
157
|
+
```ts
|
|
158
|
+
const controller = new AbortController();
|
|
159
|
+
setTimeout(() => controller.abort(), 1000);
|
|
160
|
+
await wassist.agents.list({}, { signal: controller.signal });
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Webhook signature verification
|
|
164
|
+
|
|
165
|
+
Wassist signs every webhook delivery with HMAC-SHA256 using the Stripe-style `t=<unix>,v1=<hex>` header format. The SDK gives you a one-liner to verify it.
|
|
166
|
+
|
|
167
|
+
### Node.js / Express
|
|
168
|
+
|
|
169
|
+
```ts
|
|
170
|
+
import express from 'express';
|
|
171
|
+
import { Wassist, WassistSignatureVerificationError, type WassistEvent } from '@wassist/sdk';
|
|
172
|
+
|
|
173
|
+
const app = express();
|
|
174
|
+
|
|
175
|
+
app.post(
|
|
176
|
+
'/webhooks/wassist',
|
|
177
|
+
express.raw({ type: 'application/json' }),
|
|
178
|
+
(req, res) => {
|
|
179
|
+
let event: WassistEvent;
|
|
180
|
+
try {
|
|
181
|
+
event = Wassist.webhooks.constructEvent(
|
|
182
|
+
req.body, // raw Buffer — DON'T parse first
|
|
183
|
+
req.header('x-wassist-signature') ?? '',
|
|
184
|
+
process.env.WASSIST_WEBHOOK_SECRET!
|
|
185
|
+
);
|
|
186
|
+
} catch (err) {
|
|
187
|
+
if (err instanceof WassistSignatureVerificationError) {
|
|
188
|
+
return res.status(400).send(err.message);
|
|
189
|
+
}
|
|
190
|
+
throw err;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
switch (event.event) {
|
|
194
|
+
case 'message.received':
|
|
195
|
+
// event.message, event.from, event.conversationId, ...
|
|
196
|
+
break;
|
|
197
|
+
case 'subscription.activated':
|
|
198
|
+
case 'subscription.revoked':
|
|
199
|
+
// event.webhookId, event.conversationId
|
|
200
|
+
break;
|
|
201
|
+
case 'test.ping':
|
|
202
|
+
// dashboard "Send test event" button
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
res.status(200).send('ok');
|
|
206
|
+
}
|
|
207
|
+
);
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Cloudflare Workers / Deno / Edge runtimes
|
|
211
|
+
|
|
212
|
+
Use the async variant — it runs entirely on Web Crypto:
|
|
213
|
+
|
|
214
|
+
```ts
|
|
215
|
+
export default {
|
|
216
|
+
async fetch(request: Request, env: { WASSIST_WEBHOOK_SECRET: string }) {
|
|
217
|
+
const body = await request.text();
|
|
218
|
+
try {
|
|
219
|
+
const event = await Wassist.webhooks.constructEventAsync(
|
|
220
|
+
body,
|
|
221
|
+
request.headers.get('x-wassist-signature'),
|
|
222
|
+
env.WASSIST_WEBHOOK_SECRET
|
|
223
|
+
);
|
|
224
|
+
// ... handle event
|
|
225
|
+
return new Response('ok');
|
|
226
|
+
} catch {
|
|
227
|
+
return new Response('bad signature', { status: 400 });
|
|
228
|
+
}
|
|
229
|
+
},
|
|
230
|
+
};
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Replay protection
|
|
234
|
+
|
|
235
|
+
By default the SDK rejects events whose timestamp is more than **300 seconds** off from now. Tune or disable:
|
|
236
|
+
|
|
237
|
+
```ts
|
|
238
|
+
Wassist.webhooks.constructEvent(body, header, secret, { tolerance: 600 });
|
|
239
|
+
Wassist.webhooks.constructEvent(body, header, secret, { tolerance: 0 }); // off (not recommended)
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## Custom fetch / runtimes
|
|
243
|
+
|
|
244
|
+
The SDK uses `globalThis.fetch` by default. Inject your own — for tests, retries, tracing, or older Node:
|
|
245
|
+
|
|
246
|
+
```ts
|
|
247
|
+
const wassist = new Wassist({
|
|
248
|
+
apiKey: '...',
|
|
249
|
+
fetch: async (url, init) => {
|
|
250
|
+
console.log('->', init?.method ?? 'GET', url);
|
|
251
|
+
return fetch(url, init);
|
|
252
|
+
},
|
|
253
|
+
});
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## TypeScript
|
|
257
|
+
|
|
258
|
+
The package is published with full `.d.ts` types. Every response, input, and event is typed against the public Wassist API:
|
|
259
|
+
|
|
260
|
+
```ts
|
|
261
|
+
import type {
|
|
262
|
+
Agent,
|
|
263
|
+
Conversation,
|
|
264
|
+
Message,
|
|
265
|
+
PhoneNumber,
|
|
266
|
+
WhatsAppAccount,
|
|
267
|
+
WhatsAppTemplate,
|
|
268
|
+
WhatsAppLinkSession,
|
|
269
|
+
WassistEvent,
|
|
270
|
+
MessageReceivedEvent,
|
|
271
|
+
} from '@wassist/sdk';
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
## Support
|
|
275
|
+
|
|
276
|
+
- Documentation: [docs.wassist.app/api-reference](https://docs.wassist.app/api-reference)
|
|
277
|
+
- Email: [contact@wassist.app](mailto:contact@wassist.app)
|
|
278
|
+
- Issues: [github.com/wassist/sdk/issues](https://github.com/wassist/sdk/issues)
|
|
279
|
+
|
|
280
|
+
## License
|
|
281
|
+
|
|
282
|
+
MIT
|