@mailhooks/sdk 1.0.4 → 1.1.3
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 +141 -3
- package/dist/client.d.ts +1 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +4 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/resources/emails.d.ts +11 -1
- package/dist/resources/emails.d.ts.map +1 -1
- package/dist/resources/emails.js +19 -2
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/webhooks.d.ts +123 -0
- package/dist/webhooks.d.ts.map +1 -0
- package/dist/webhooks.js +81 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -62,6 +62,7 @@ const emails = await mailhooks.emails.list({
|
|
|
62
62
|
subject: 'Important',
|
|
63
63
|
startDate: '2024-01-01',
|
|
64
64
|
endDate: '2024-12-31',
|
|
65
|
+
read: false, // Filter by read status
|
|
65
66
|
},
|
|
66
67
|
sort: {
|
|
67
68
|
field: 'createdAt',
|
|
@@ -73,7 +74,11 @@ const emails = await mailhooks.emails.list({
|
|
|
73
74
|
#### Get Email
|
|
74
75
|
|
|
75
76
|
```typescript
|
|
77
|
+
// Get email without marking as read
|
|
76
78
|
const email = await mailhooks.emails.getEmail('email-id');
|
|
79
|
+
|
|
80
|
+
// Get email and mark it as read in one call
|
|
81
|
+
const readEmail = await mailhooks.emails.getEmail('email-id', true);
|
|
77
82
|
```
|
|
78
83
|
|
|
79
84
|
#### Get Email Content
|
|
@@ -99,14 +104,29 @@ const attachment = await mailhooks.emails.downloadAttachment('email-id', 'attach
|
|
|
99
104
|
// attachment.contentType contains the MIME type
|
|
100
105
|
```
|
|
101
106
|
|
|
107
|
+
#### Mark Email as Read/Unread
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
// Mark email as read
|
|
111
|
+
const readEmail = await mailhooks.emails.markAsRead('email-id');
|
|
112
|
+
console.log(readEmail.read); // true
|
|
113
|
+
|
|
114
|
+
// Mark email as unread
|
|
115
|
+
const unreadEmail = await mailhooks.emails.markAsUnread('email-id');
|
|
116
|
+
console.log(unreadEmail.read); // false
|
|
117
|
+
```
|
|
118
|
+
|
|
102
119
|
#### Wait for Email
|
|
103
120
|
|
|
104
121
|
Wait for an email that matches specific filters. Useful for testing and automation.
|
|
105
122
|
|
|
106
123
|
```typescript
|
|
107
|
-
// Basic usage - wait for email from specific sender
|
|
124
|
+
// Basic usage - wait for unread email from specific sender
|
|
108
125
|
const email = await mailhooks.emails.waitFor({
|
|
109
|
-
filter: {
|
|
126
|
+
filter: {
|
|
127
|
+
from: 'noreply@example.com',
|
|
128
|
+
read: false // Only wait for unread emails
|
|
129
|
+
},
|
|
110
130
|
timeout: 30000, // 30 seconds
|
|
111
131
|
pollInterval: 2000, // Check every 2 seconds
|
|
112
132
|
});
|
|
@@ -117,6 +137,7 @@ const email = await mailhooks.emails.waitFor({
|
|
|
117
137
|
from: 'noreply@example.com',
|
|
118
138
|
to: 'test@yourdomain.com',
|
|
119
139
|
subject: 'Order Confirmation',
|
|
140
|
+
read: false, // Only unread emails
|
|
120
141
|
},
|
|
121
142
|
lookbackWindow: 10000, // Only consider emails from last 10 seconds
|
|
122
143
|
initialDelay: 5000, // Wait 5 seconds before first check
|
|
@@ -127,7 +148,7 @@ const email = await mailhooks.emails.waitFor({
|
|
|
127
148
|
```
|
|
128
149
|
|
|
129
150
|
**Options:**
|
|
130
|
-
- `filter`: Same filters as `list()` method (from, to, subject, startDate, endDate)
|
|
151
|
+
- `filter`: Same filters as `list()` method (from, to, subject, startDate, endDate, read)
|
|
131
152
|
- `lookbackWindow`: How far back to look for emails on first check (default: 10000ms)
|
|
132
153
|
- `initialDelay`: Delay before starting to poll (default: 0ms)
|
|
133
154
|
- `timeout`: Maximum time to wait before throwing error (default: 30000ms)
|
|
@@ -150,6 +171,7 @@ interface Email {
|
|
|
150
171
|
from: string;
|
|
151
172
|
to: string[];
|
|
152
173
|
subject: string;
|
|
174
|
+
read: boolean;
|
|
153
175
|
createdAt: Date;
|
|
154
176
|
attachments: Attachment[];
|
|
155
177
|
}
|
|
@@ -189,6 +211,122 @@ try {
|
|
|
189
211
|
- **404 Not Found**: Resource not found
|
|
190
212
|
- **429 Too Many Requests**: Rate limit exceeded
|
|
191
213
|
|
|
214
|
+
## Webhook Signature Verification
|
|
215
|
+
|
|
216
|
+
When you create a webhook in Mailhooks, you receive a secret that can be used to verify that incoming webhook requests are genuinely from Mailhooks. Each webhook request includes an `X-Webhook-Signature` header containing an HMAC-SHA256 signature of the request body.
|
|
217
|
+
|
|
218
|
+
### Verifying Signatures
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
import { verifyWebhookSignature, parseWebhookPayload } from '@mailhooks/sdk';
|
|
222
|
+
|
|
223
|
+
// Express.js example - IMPORTANT: Use raw body parser for signature verification
|
|
224
|
+
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
|
|
225
|
+
const signature = req.headers['x-webhook-signature'] as string;
|
|
226
|
+
const payload = req.body.toString();
|
|
227
|
+
|
|
228
|
+
// Verify the signature using your webhook secret
|
|
229
|
+
if (!verifyWebhookSignature(payload, signature, process.env.WEBHOOK_SECRET!)) {
|
|
230
|
+
console.error('Invalid webhook signature');
|
|
231
|
+
return res.status(401).send('Invalid signature');
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Parse the verified payload
|
|
235
|
+
const event = parseWebhookPayload(payload);
|
|
236
|
+
|
|
237
|
+
console.log(`Received email from ${event.from}: ${event.subject}`);
|
|
238
|
+
|
|
239
|
+
// Process the webhook...
|
|
240
|
+
res.status(200).send('OK');
|
|
241
|
+
});
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### Webhook Payload
|
|
245
|
+
|
|
246
|
+
The webhook payload contains the following fields:
|
|
247
|
+
|
|
248
|
+
```typescript
|
|
249
|
+
interface WebhookPayload {
|
|
250
|
+
id: string; // Unique email ID
|
|
251
|
+
from: string; // Sender email address
|
|
252
|
+
to: string[]; // Array of recipient addresses
|
|
253
|
+
subject: string; // Email subject
|
|
254
|
+
body: string; // Plain text body
|
|
255
|
+
html?: string; // HTML body (if available)
|
|
256
|
+
attachments: Array<{ // Attachment metadata
|
|
257
|
+
filename: string;
|
|
258
|
+
contentType: string;
|
|
259
|
+
size: number;
|
|
260
|
+
}>;
|
|
261
|
+
receivedAt: string; // ISO 8601 timestamp
|
|
262
|
+
|
|
263
|
+
// Email authentication results
|
|
264
|
+
spfResult?: 'pass' | 'fail' | 'softfail' | 'neutral' | 'none';
|
|
265
|
+
dkimResult?: 'pass' | 'fail' | 'none' | 'temperror' | 'permerror';
|
|
266
|
+
dmarcResult?: 'pass' | 'fail' | 'none' | 'temperror' | 'permerror';
|
|
267
|
+
authSummary?: 'pass' | 'fail' | 'partial';
|
|
268
|
+
|
|
269
|
+
// Spam detection
|
|
270
|
+
spamScore?: number; // 0.0 to 1.0
|
|
271
|
+
isSpam?: boolean;
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### Security Best Practices
|
|
276
|
+
|
|
277
|
+
1. **Always verify signatures** before processing webhook data
|
|
278
|
+
2. **Use the raw request body** for signature verification (not parsed JSON)
|
|
279
|
+
3. **Store secrets securely** in environment variables, not in code
|
|
280
|
+
4. **Use HTTPS** for your webhook endpoint
|
|
281
|
+
5. **Respond quickly** - return 200 OK before doing heavy processing
|
|
282
|
+
|
|
283
|
+
### Framework Examples
|
|
284
|
+
|
|
285
|
+
#### Next.js API Route
|
|
286
|
+
|
|
287
|
+
```typescript
|
|
288
|
+
import { verifyWebhookSignature, parseWebhookPayload } from '@mailhooks/sdk';
|
|
289
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
290
|
+
|
|
291
|
+
export async function POST(request: NextRequest) {
|
|
292
|
+
const payload = await request.text();
|
|
293
|
+
const signature = request.headers.get('x-webhook-signature') || '';
|
|
294
|
+
|
|
295
|
+
if (!verifyWebhookSignature(payload, signature, process.env.WEBHOOK_SECRET!)) {
|
|
296
|
+
return NextResponse.json({ error: 'Invalid signature' }, { status: 401 });
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const event = parseWebhookPayload(payload);
|
|
300
|
+
// Process event...
|
|
301
|
+
|
|
302
|
+
return NextResponse.json({ received: true });
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
#### Fastify
|
|
307
|
+
|
|
308
|
+
```typescript
|
|
309
|
+
import { verifyWebhookSignature, parseWebhookPayload } from '@mailhooks/sdk';
|
|
310
|
+
|
|
311
|
+
fastify.post('/webhook', {
|
|
312
|
+
config: {
|
|
313
|
+
rawBody: true, // Enable raw body
|
|
314
|
+
},
|
|
315
|
+
}, async (request, reply) => {
|
|
316
|
+
const signature = request.headers['x-webhook-signature'] as string;
|
|
317
|
+
const payload = request.rawBody?.toString() || '';
|
|
318
|
+
|
|
319
|
+
if (!verifyWebhookSignature(payload, signature, process.env.WEBHOOK_SECRET!)) {
|
|
320
|
+
return reply.status(401).send({ error: 'Invalid signature' });
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const event = parseWebhookPayload(payload);
|
|
324
|
+
// Process event...
|
|
325
|
+
|
|
326
|
+
return { received: true };
|
|
327
|
+
});
|
|
328
|
+
```
|
|
329
|
+
|
|
192
330
|
## License
|
|
193
331
|
|
|
194
332
|
MIT
|
package/dist/client.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export declare class MailhooksClient {
|
|
|
7
7
|
protected get<T>(path: string, params?: Record<string, any>): Promise<T>;
|
|
8
8
|
protected post<T>(path: string, data?: any): Promise<T>;
|
|
9
9
|
protected put<T>(path: string, data?: any): Promise<T>;
|
|
10
|
+
protected patch<T>(path: string, data?: any): Promise<T>;
|
|
10
11
|
protected delete<T>(path: string): Promise<T>;
|
|
11
12
|
protected downloadFile(path: string): Promise<ArrayBuffer>;
|
|
12
13
|
protected getAxiosInstance(): AxiosInstance;
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAc,EAAE,aAAa,EAAiB,MAAM,OAAO,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE1C,qBAAa,eAAe;IAC1B,OAAO,CAAC,IAAI,CAAgB;gBAEhB,MAAM,EAAE,eAAe;IAkBnC,OAAO,CAAC,UAAU;cAkBF,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;cAK9D,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,CAAC,CAAC;cAK7C,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,CAAC,CAAC;cAK5C,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;cAKnC,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAOhE,SAAS,CAAC,gBAAgB,IAAI,aAAa;CAG5C"}
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAc,EAAE,aAAa,EAAiB,MAAM,OAAO,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE1C,qBAAa,eAAe;IAC1B,OAAO,CAAC,IAAI,CAAgB;gBAEhB,MAAM,EAAE,eAAe;IAkBnC,OAAO,CAAC,UAAU;cAkBF,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;cAK9D,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,CAAC,CAAC;cAK7C,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,CAAC,CAAC;cAK5C,KAAK,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,CAAC,CAAC;cAK9C,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;cAKnC,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAOhE,SAAS,CAAC,gBAAgB,IAAI,aAAa;CAG5C"}
|
package/dist/client.js
CHANGED
|
@@ -53,6 +53,10 @@ class MailhooksClient {
|
|
|
53
53
|
const response = await this.http.put(path, data);
|
|
54
54
|
return response.data;
|
|
55
55
|
}
|
|
56
|
+
async patch(path, data) {
|
|
57
|
+
const response = await this.http.patch(path, data);
|
|
58
|
+
return response.data;
|
|
59
|
+
}
|
|
56
60
|
async delete(path) {
|
|
57
61
|
const response = await this.http.delete(path);
|
|
58
62
|
return response.data;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { Mailhooks } from './mailhooks';
|
|
2
2
|
export { EmailsResource } from './resources/emails';
|
|
3
3
|
export * from './types';
|
|
4
|
+
export { verifyWebhookSignature, parseWebhookPayload, constructSignature, type WebhookPayload, } from './webhooks';
|
|
4
5
|
import { Mailhooks } from './mailhooks';
|
|
5
6
|
export default Mailhooks;
|
|
6
7
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,cAAc,SAAS,CAAC;AAGxB,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,eAAe,SAAS,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,cAAc,SAAS,CAAC;AAGxB,OAAO,EACL,sBAAsB,EACtB,mBAAmB,EACnB,kBAAkB,EAClB,KAAK,cAAc,GACpB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,eAAe,SAAS,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -14,12 +14,17 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.EmailsResource = exports.Mailhooks = void 0;
|
|
17
|
+
exports.constructSignature = exports.parseWebhookPayload = exports.verifyWebhookSignature = exports.EmailsResource = exports.Mailhooks = void 0;
|
|
18
18
|
var mailhooks_1 = require("./mailhooks");
|
|
19
19
|
Object.defineProperty(exports, "Mailhooks", { enumerable: true, get: function () { return mailhooks_1.Mailhooks; } });
|
|
20
20
|
var emails_1 = require("./resources/emails");
|
|
21
21
|
Object.defineProperty(exports, "EmailsResource", { enumerable: true, get: function () { return emails_1.EmailsResource; } });
|
|
22
22
|
__exportStar(require("./types"), exports);
|
|
23
|
+
// Webhook verification utilities
|
|
24
|
+
var webhooks_1 = require("./webhooks");
|
|
25
|
+
Object.defineProperty(exports, "verifyWebhookSignature", { enumerable: true, get: function () { return webhooks_1.verifyWebhookSignature; } });
|
|
26
|
+
Object.defineProperty(exports, "parseWebhookPayload", { enumerable: true, get: function () { return webhooks_1.parseWebhookPayload; } });
|
|
27
|
+
Object.defineProperty(exports, "constructSignature", { enumerable: true, get: function () { return webhooks_1.constructSignature; } });
|
|
23
28
|
// Default export for convenience
|
|
24
29
|
const mailhooks_2 = require("./mailhooks");
|
|
25
30
|
exports.default = mailhooks_2.Mailhooks;
|
|
@@ -7,8 +7,10 @@ export declare class EmailsResource extends MailhooksClient {
|
|
|
7
7
|
list(params?: EmailListParams): Promise<EmailsResponse>;
|
|
8
8
|
/**
|
|
9
9
|
* Get a specific email by ID
|
|
10
|
+
* @param emailId - The ID of the email to retrieve
|
|
11
|
+
* @param markAsRead - Optional: Mark the email as read when fetching (default: false)
|
|
10
12
|
*/
|
|
11
|
-
getEmail(emailId: string): Promise<Email>;
|
|
13
|
+
getEmail(emailId: string, markAsRead?: boolean): Promise<Email>;
|
|
12
14
|
/**
|
|
13
15
|
* Get the HTML and text content of an email
|
|
14
16
|
*/
|
|
@@ -21,6 +23,14 @@ export declare class EmailsResource extends MailhooksClient {
|
|
|
21
23
|
* Download a specific attachment from an email
|
|
22
24
|
*/
|
|
23
25
|
downloadAttachment(emailId: string, attachmentId: string): Promise<DownloadResponse>;
|
|
26
|
+
/**
|
|
27
|
+
* Mark an email as read
|
|
28
|
+
*/
|
|
29
|
+
markAsRead(emailId: string): Promise<Email>;
|
|
30
|
+
/**
|
|
31
|
+
* Mark an email as unread
|
|
32
|
+
*/
|
|
33
|
+
markAsUnread(emailId: string): Promise<Email>;
|
|
24
34
|
/**
|
|
25
35
|
* Wait for an email that matches the given filters
|
|
26
36
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"emails.d.ts","sourceRoot":"","sources":["../../src/resources/emails.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EACL,KAAK,EACL,YAAY,EACZ,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,cAAc,EACf,MAAM,UAAU,CAAC;AAElB,qBAAa,cAAe,SAAQ,eAAe;IACjD;;OAEG;IACG,IAAI,CAAC,MAAM,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"emails.d.ts","sourceRoot":"","sources":["../../src/resources/emails.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EACL,KAAK,EACL,YAAY,EACZ,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,cAAc,EACf,MAAM,UAAU,CAAC;AAElB,qBAAa,cAAe,SAAQ,eAAe;IACjD;;OAEG;IACG,IAAI,CAAC,MAAM,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC;IA0B7D;;;;OAIG;IACG,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,GAAE,OAAe,GAAG,OAAO,CAAC,KAAK,CAAC;IAK5E;;OAEG;IACG,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAIxD;;OAEG;IACG,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAS7D;;OAEG;IACG,kBAAkB,CACtB,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,gBAAgB,CAAC;IAqB5B;;OAEG;IACG,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;IAIjD;;OAEG;IACG,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;IAInD;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACG,OAAO,CAAC,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,KAAK,CAAC;CA8F5D"}
|
package/dist/resources/emails.js
CHANGED
|
@@ -24,6 +24,8 @@ class EmailsResource extends client_1.MailhooksClient {
|
|
|
24
24
|
queryParams['filter.createdAfter'] = params.filter.startDate;
|
|
25
25
|
if (params.filter.endDate)
|
|
26
26
|
queryParams['filter.createdBefore'] = params.filter.endDate;
|
|
27
|
+
if (params.filter.read !== undefined)
|
|
28
|
+
queryParams['filter.read'] = String(params.filter.read);
|
|
27
29
|
}
|
|
28
30
|
// Handle sort params
|
|
29
31
|
if (params?.sort) {
|
|
@@ -36,9 +38,12 @@ class EmailsResource extends client_1.MailhooksClient {
|
|
|
36
38
|
}
|
|
37
39
|
/**
|
|
38
40
|
* Get a specific email by ID
|
|
41
|
+
* @param emailId - The ID of the email to retrieve
|
|
42
|
+
* @param markAsRead - Optional: Mark the email as read when fetching (default: false)
|
|
39
43
|
*/
|
|
40
|
-
async getEmail(emailId) {
|
|
41
|
-
|
|
44
|
+
async getEmail(emailId, markAsRead = false) {
|
|
45
|
+
const params = markAsRead ? { markAsRead: 'true' } : undefined;
|
|
46
|
+
return super.get(`/v1/emails/${emailId}`, params);
|
|
42
47
|
}
|
|
43
48
|
/**
|
|
44
49
|
* Get the HTML and text content of an email
|
|
@@ -75,6 +80,18 @@ class EmailsResource extends client_1.MailhooksClient {
|
|
|
75
80
|
contentType: response.headers['content-type'],
|
|
76
81
|
};
|
|
77
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* Mark an email as read
|
|
85
|
+
*/
|
|
86
|
+
async markAsRead(emailId) {
|
|
87
|
+
return super.patch(`/v1/emails/${emailId}/read`);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Mark an email as unread
|
|
91
|
+
*/
|
|
92
|
+
async markAsUnread(emailId) {
|
|
93
|
+
return super.patch(`/v1/emails/${emailId}/unread`);
|
|
94
|
+
}
|
|
78
95
|
/**
|
|
79
96
|
* Wait for an email that matches the given filters
|
|
80
97
|
*
|
package/dist/types.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export interface Email {
|
|
|
9
9
|
from: string;
|
|
10
10
|
to: string[];
|
|
11
11
|
subject: string;
|
|
12
|
+
read: boolean;
|
|
12
13
|
createdAt: Date;
|
|
13
14
|
attachments: Attachment[];
|
|
14
15
|
}
|
|
@@ -33,6 +34,7 @@ export interface EmailFilter {
|
|
|
33
34
|
subject?: string;
|
|
34
35
|
startDate?: string;
|
|
35
36
|
endDate?: string;
|
|
37
|
+
read?: boolean;
|
|
36
38
|
}
|
|
37
39
|
export interface EmailSort {
|
|
38
40
|
field?: 'createdAt' | 'from' | 'subject';
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,KAAK;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,EAAE,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;IAChB,WAAW,EAAE,UAAU,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,cAAe,SAAQ,kBAAkB;IACxD,IAAI,EAAE,KAAK,EAAE,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,KAAK;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,EAAE,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,OAAO,CAAC;IACd,SAAS,EAAE,IAAI,CAAC;IAChB,WAAW,EAAE,UAAU,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,cAAe,SAAQ,kBAAkB;IACxD,IAAI,EAAE,KAAK,EAAE,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,CAAC,EAAE,WAAW,GAAG,MAAM,GAAG,SAAS,CAAC;IACzC,KAAK,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,IAAI,CAAC,EAAE,SAAS,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,WAAW,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Webhook payload sent by Mailhooks when an email is received.
|
|
3
|
+
*/
|
|
4
|
+
export interface WebhookPayload {
|
|
5
|
+
/** Unique email ID */
|
|
6
|
+
id: string;
|
|
7
|
+
/** Sender email address */
|
|
8
|
+
from: string;
|
|
9
|
+
/** Array of recipient email addresses */
|
|
10
|
+
to: string[];
|
|
11
|
+
/** Email subject line */
|
|
12
|
+
subject: string;
|
|
13
|
+
/** Plain text body of the email */
|
|
14
|
+
body: string;
|
|
15
|
+
/** HTML body of the email (if available) */
|
|
16
|
+
html?: string;
|
|
17
|
+
/** Array of attachment metadata */
|
|
18
|
+
attachments: Array<{
|
|
19
|
+
filename: string;
|
|
20
|
+
contentType: string;
|
|
21
|
+
size: number;
|
|
22
|
+
}>;
|
|
23
|
+
/** ISO 8601 timestamp when the email was received */
|
|
24
|
+
receivedAt: string;
|
|
25
|
+
/** SPF authentication result */
|
|
26
|
+
spfResult?: 'pass' | 'fail' | 'softfail' | 'neutral' | 'none';
|
|
27
|
+
/** DKIM authentication result */
|
|
28
|
+
dkimResult?: 'pass' | 'fail' | 'none' | 'temperror' | 'permerror';
|
|
29
|
+
/** DMARC authentication result */
|
|
30
|
+
dmarcResult?: 'pass' | 'fail' | 'none' | 'temperror' | 'permerror';
|
|
31
|
+
/** Overall authentication summary */
|
|
32
|
+
authSummary?: 'pass' | 'fail' | 'partial';
|
|
33
|
+
/** Email headers as key-value pairs */
|
|
34
|
+
headers?: Record<string, string>;
|
|
35
|
+
/** Authentication diagnostic details */
|
|
36
|
+
authDiagnostics?: {
|
|
37
|
+
spf: {
|
|
38
|
+
clientIp: string;
|
|
39
|
+
domain: string;
|
|
40
|
+
record?: string;
|
|
41
|
+
helo?: string;
|
|
42
|
+
} | null;
|
|
43
|
+
dkim: Array<{
|
|
44
|
+
domain: string;
|
|
45
|
+
selector?: string;
|
|
46
|
+
algorithm?: string;
|
|
47
|
+
aligned?: boolean;
|
|
48
|
+
result: string;
|
|
49
|
+
}>;
|
|
50
|
+
dmarc: {
|
|
51
|
+
domain: string;
|
|
52
|
+
policy: string;
|
|
53
|
+
record?: string;
|
|
54
|
+
alignment: {
|
|
55
|
+
spf: {
|
|
56
|
+
result: string | false;
|
|
57
|
+
strict: boolean;
|
|
58
|
+
};
|
|
59
|
+
dkim: {
|
|
60
|
+
result: string | false;
|
|
61
|
+
strict: boolean;
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
} | null;
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Verifies a webhook signature using HMAC-SHA256.
|
|
69
|
+
*
|
|
70
|
+
* Each webhook request from Mailhooks includes an `X-Webhook-Signature` header
|
|
71
|
+
* containing a hex-encoded HMAC-SHA256 signature of the request body.
|
|
72
|
+
*
|
|
73
|
+
* @param payload - The raw request body as a string or Buffer
|
|
74
|
+
* @param signature - The signature from the `X-Webhook-Signature` header
|
|
75
|
+
* @param secret - Your webhook secret (starts with `whsec_`)
|
|
76
|
+
* @returns `true` if the signature is valid, `false` otherwise
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* ```typescript
|
|
80
|
+
* import { verifyWebhookSignature } from '@mailhooks/sdk';
|
|
81
|
+
*
|
|
82
|
+
* // Express.js with raw body parser
|
|
83
|
+
* app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
|
|
84
|
+
* const signature = req.headers['x-webhook-signature'] as string;
|
|
85
|
+
* const payload = req.body.toString();
|
|
86
|
+
*
|
|
87
|
+
* if (!verifyWebhookSignature(payload, signature, process.env.WEBHOOK_SECRET!)) {
|
|
88
|
+
* return res.status(401).send('Invalid signature');
|
|
89
|
+
* }
|
|
90
|
+
*
|
|
91
|
+
* const event = JSON.parse(payload);
|
|
92
|
+
* // Process the webhook...
|
|
93
|
+
* res.status(200).send('OK');
|
|
94
|
+
* });
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
export declare function verifyWebhookSignature(payload: string | Buffer, signature: string, secret: string): boolean;
|
|
98
|
+
/**
|
|
99
|
+
* Parses a webhook payload from a JSON string.
|
|
100
|
+
*
|
|
101
|
+
* @param body - The raw request body as a string
|
|
102
|
+
* @returns The parsed webhook payload
|
|
103
|
+
* @throws {SyntaxError} If the body is not valid JSON
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* ```typescript
|
|
107
|
+
* import { parseWebhookPayload } from '@mailhooks/sdk';
|
|
108
|
+
*
|
|
109
|
+
* const payload = parseWebhookPayload(req.body.toString());
|
|
110
|
+
* console.log(`Received email from ${payload.from}: ${payload.subject}`);
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
export declare function parseWebhookPayload(body: string): WebhookPayload;
|
|
114
|
+
/**
|
|
115
|
+
* Constructs the expected signature for a webhook payload.
|
|
116
|
+
* Useful for debugging or manual verification.
|
|
117
|
+
*
|
|
118
|
+
* @param payload - The raw request body as a string or Buffer
|
|
119
|
+
* @param secret - Your webhook secret
|
|
120
|
+
* @returns The expected HMAC-SHA256 signature as a hex string
|
|
121
|
+
*/
|
|
122
|
+
export declare function constructSignature(payload: string | Buffer, secret: string): string;
|
|
123
|
+
//# sourceMappingURL=webhooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../src/webhooks.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,sBAAsB;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,2BAA2B;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,yCAAyC;IACzC,EAAE,EAAE,MAAM,EAAE,CAAC;IACb,yBAAyB;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,4CAA4C;IAC5C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mCAAmC;IACnC,WAAW,EAAE,KAAK,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,WAAW,EAAE,MAAM,CAAC;QACpB,IAAI,EAAE,MAAM,CAAC;KACd,CAAC,CAAC;IACH,qDAAqD;IACrD,UAAU,EAAE,MAAM,CAAC;IACnB,gCAAgC;IAChC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,UAAU,GAAG,SAAS,GAAG,MAAM,CAAC;IAC9D,iCAAiC;IACjC,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,WAAW,GAAG,WAAW,CAAC;IAClE,kCAAkC;IAClC,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,WAAW,GAAG,WAAW,CAAC;IACnE,qCAAqC;IACrC,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAC1C,uCAAuC;IACvC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,wCAAwC;IACxC,eAAe,CAAC,EAAE;QAChB,GAAG,EAAE;YACH,QAAQ,EAAE,MAAM,CAAC;YACjB,MAAM,EAAE,MAAM,CAAC;YACf,MAAM,CAAC,EAAE,MAAM,CAAC;YAChB,IAAI,CAAC,EAAE,MAAM,CAAC;SACf,GAAG,IAAI,CAAC;QACT,IAAI,EAAE,KAAK,CAAC;YACV,MAAM,EAAE,MAAM,CAAC;YACf,QAAQ,CAAC,EAAE,MAAM,CAAC;YAClB,SAAS,CAAC,EAAE,MAAM,CAAC;YACnB,OAAO,CAAC,EAAE,OAAO,CAAC;YAClB,MAAM,EAAE,MAAM,CAAC;SAChB,CAAC,CAAC;QACH,KAAK,EAAE;YACL,MAAM,EAAE,MAAM,CAAC;YACf,MAAM,EAAE,MAAM,CAAC;YACf,MAAM,CAAC,EAAE,MAAM,CAAC;YAChB,SAAS,EAAE;gBACT,GAAG,EAAE;oBAAE,MAAM,EAAE,MAAM,GAAG,KAAK,CAAC;oBAAC,MAAM,EAAE,OAAO,CAAA;iBAAE,CAAC;gBACjD,IAAI,EAAE;oBAAE,MAAM,EAAE,MAAM,GAAG,KAAK,CAAC;oBAAC,MAAM,EAAE,OAAO,CAAA;iBAAE,CAAC;aACnD,CAAC;SACH,GAAG,IAAI,CAAC;KACV,CAAC;CACH;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,MAAM,GAAG,MAAM,EACxB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GACb,OAAO,CAqBT;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CAEhE;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,GAAG,MAAM,EACxB,MAAM,EAAE,MAAM,GACb,MAAM,CAER"}
|
package/dist/webhooks.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.verifyWebhookSignature = verifyWebhookSignature;
|
|
4
|
+
exports.parseWebhookPayload = parseWebhookPayload;
|
|
5
|
+
exports.constructSignature = constructSignature;
|
|
6
|
+
const crypto_1 = require("crypto");
|
|
7
|
+
/**
|
|
8
|
+
* Verifies a webhook signature using HMAC-SHA256.
|
|
9
|
+
*
|
|
10
|
+
* Each webhook request from Mailhooks includes an `X-Webhook-Signature` header
|
|
11
|
+
* containing a hex-encoded HMAC-SHA256 signature of the request body.
|
|
12
|
+
*
|
|
13
|
+
* @param payload - The raw request body as a string or Buffer
|
|
14
|
+
* @param signature - The signature from the `X-Webhook-Signature` header
|
|
15
|
+
* @param secret - Your webhook secret (starts with `whsec_`)
|
|
16
|
+
* @returns `true` if the signature is valid, `false` otherwise
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* import { verifyWebhookSignature } from '@mailhooks/sdk';
|
|
21
|
+
*
|
|
22
|
+
* // Express.js with raw body parser
|
|
23
|
+
* app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
|
|
24
|
+
* const signature = req.headers['x-webhook-signature'] as string;
|
|
25
|
+
* const payload = req.body.toString();
|
|
26
|
+
*
|
|
27
|
+
* if (!verifyWebhookSignature(payload, signature, process.env.WEBHOOK_SECRET!)) {
|
|
28
|
+
* return res.status(401).send('Invalid signature');
|
|
29
|
+
* }
|
|
30
|
+
*
|
|
31
|
+
* const event = JSON.parse(payload);
|
|
32
|
+
* // Process the webhook...
|
|
33
|
+
* res.status(200).send('OK');
|
|
34
|
+
* });
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
function verifyWebhookSignature(payload, signature, secret) {
|
|
38
|
+
if (!payload || !signature || !secret) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
// Normalize and validate signature format (should be 64 hex chars for SHA256)
|
|
42
|
+
const normalizedSignature = signature.trim().toLowerCase();
|
|
43
|
+
if (!/^[a-f0-9]{64}$/.test(normalizedSignature)) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
const expectedSignature = (0, crypto_1.createHmac)('sha256', secret)
|
|
47
|
+
.update(payload)
|
|
48
|
+
.digest('hex');
|
|
49
|
+
// Use timing-safe comparison to prevent timing attacks
|
|
50
|
+
// Both buffers are guaranteed to be 32 bytes (64 hex chars)
|
|
51
|
+
return (0, crypto_1.timingSafeEqual)(Buffer.from(normalizedSignature, 'hex'), Buffer.from(expectedSignature, 'hex'));
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Parses a webhook payload from a JSON string.
|
|
55
|
+
*
|
|
56
|
+
* @param body - The raw request body as a string
|
|
57
|
+
* @returns The parsed webhook payload
|
|
58
|
+
* @throws {SyntaxError} If the body is not valid JSON
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```typescript
|
|
62
|
+
* import { parseWebhookPayload } from '@mailhooks/sdk';
|
|
63
|
+
*
|
|
64
|
+
* const payload = parseWebhookPayload(req.body.toString());
|
|
65
|
+
* console.log(`Received email from ${payload.from}: ${payload.subject}`);
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
function parseWebhookPayload(body) {
|
|
69
|
+
return JSON.parse(body);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Constructs the expected signature for a webhook payload.
|
|
73
|
+
* Useful for debugging or manual verification.
|
|
74
|
+
*
|
|
75
|
+
* @param payload - The raw request body as a string or Buffer
|
|
76
|
+
* @param secret - Your webhook secret
|
|
77
|
+
* @returns The expected HMAC-SHA256 signature as a hex string
|
|
78
|
+
*/
|
|
79
|
+
function constructSignature(payload, secret) {
|
|
80
|
+
return (0, crypto_1.createHmac)('sha256', secret).update(payload).digest('hex');
|
|
81
|
+
}
|