mymx 0.3.5 → 0.3.6

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/dist/index.d.cts DELETED
@@ -1,764 +0,0 @@
1
- import { EmailAddress, EmailReceivedEvent, KnownWebhookEvent, ParsedData, ParsedDataComplete, ParsedDataFailed, ParsedError, ParsedStatus, RawContent, RawContentDownloadOnly, RawContentInline, UnknownEvent, WEBHOOK_VERSION, WebhookAttachment, WebhookEvent } from "./types-BRl0ogvI.cjs";
2
- import { MYMX_CONFIRMED_HEADER, MYMX_SIGNATURE_HEADER, VerifyOptions, verifyWebhookSignature } from "./signing-ChbxCBRQ.cjs";
3
- import { MyMXWebhookError, PAYLOAD_ERRORS, RAW_EMAIL_ERRORS, RawEmailDecodeError, RawEmailDecodeErrorCode, VERIFICATION_ERRORS, WebhookErrorCode, WebhookPayloadError, WebhookPayloadErrorCode, WebhookValidationError, WebhookValidationErrorCode, WebhookVerificationError, WebhookVerificationErrorCode } from "./errors-CwKIO2XZ.cjs";
4
-
5
- //#region src/schema.generated.d.ts
6
- /**
7
- * JSON Schema for EmailReceivedEvent.
8
- *
9
- * AUTO-GENERATED - DO NOT EDIT
10
- * Run `pnpm generate:schema` to regenerate.
11
- */
12
- /**
13
- * JSON Schema for EmailReceivedEvent.
14
- *
15
- * AUTO-GENERATED - DO NOT EDIT
16
- * Run `pnpm generate:schema` to regenerate.
17
- */
18
- declare const emailReceivedEventJsonSchema: {
19
- readonly $schema: "http://json-schema.org/draft-07/schema#";
20
- readonly $ref: "#/definitions/EmailReceivedEvent";
21
- readonly definitions: {
22
- readonly EmailReceivedEvent: {
23
- readonly type: "object";
24
- readonly properties: {
25
- readonly id: {
26
- readonly type: "string";
27
- readonly description: "Unique delivery event ID.\n\nThis ID is stable across retries to the same endpoint - use it as your idempotency/dedupe key. Note that the same email delivered to different endpoints will have different event IDs.\n\nFormat: `evt_` prefix followed by a SHA-256 hash (64 hex characters). Example: `evt_a1b2c3d4e5f6...` (68 characters total)";
28
- };
29
- readonly event: {
30
- readonly type: "string";
31
- readonly const: "email.received";
32
- readonly description: "Event type identifier. Always `\"email.received\"` for this event type.";
33
- };
34
- readonly version: {
35
- readonly $ref: "#/definitions/WebhookVersion";
36
- readonly description: "API version in date format (YYYY-MM-DD). Use this to detect version mismatches between webhook and SDK.";
37
- };
38
- readonly delivery: {
39
- readonly type: "object";
40
- readonly properties: {
41
- readonly endpoint_id: {
42
- readonly type: "string";
43
- readonly description: "ID of the webhook endpoint receiving this event. Matches the endpoint ID from your MyMX dashboard.";
44
- };
45
- readonly attempt: {
46
- readonly type: "number";
47
- readonly description: "Delivery attempt number, starting at 1. Increments with each retry if previous attempts failed.";
48
- };
49
- readonly attempted_at: {
50
- readonly type: "string";
51
- readonly description: "ISO 8601 timestamp (UTC) when this delivery was attempted.";
52
- readonly examples: ["2025-01-15T10:30:00.000Z"];
53
- };
54
- };
55
- readonly required: ["endpoint_id", "attempt", "attempted_at"];
56
- readonly additionalProperties: false;
57
- readonly description: "Metadata about this webhook delivery.";
58
- };
59
- readonly email: {
60
- readonly type: "object";
61
- readonly properties: {
62
- readonly id: {
63
- readonly type: "string";
64
- readonly description: "Unique email ID in MyMX. Use this ID when calling MyMX APIs to reference this email.";
65
- };
66
- readonly received_at: {
67
- readonly type: "string";
68
- readonly description: "ISO 8601 timestamp (UTC) when MyMX received the email.";
69
- readonly examples: ["2025-01-15T10:29:55.123Z"];
70
- };
71
- readonly smtp: {
72
- readonly type: "object";
73
- readonly properties: {
74
- readonly helo: {
75
- readonly type: ["string", "null"];
76
- readonly description: "HELO/EHLO hostname from the sending server. Null if not provided during SMTP transaction.";
77
- };
78
- readonly mail_from: {
79
- readonly type: "string";
80
- readonly description: "SMTP envelope sender (MAIL FROM command). This is the bounce address, which may differ from the From header.";
81
- };
82
- readonly rcpt_to: {
83
- readonly type: "array";
84
- readonly items: {
85
- readonly type: "string";
86
- };
87
- readonly description: "SMTP envelope recipients (RCPT TO commands). All addresses that received this email in a single delivery.";
88
- };
89
- };
90
- readonly required: ["helo", "mail_from", "rcpt_to"];
91
- readonly additionalProperties: false;
92
- readonly description: "SMTP envelope information. This is the \"real\" sender/recipient info from the SMTP transaction, which may differ from the headers (e.g., BCC recipients).";
93
- };
94
- readonly headers: {
95
- readonly type: "object";
96
- readonly properties: {
97
- readonly message_id: {
98
- readonly type: ["string", "null"];
99
- readonly description: "Message-ID header value. Null if the email had no Message-ID header.";
100
- };
101
- readonly subject: {
102
- readonly type: ["string", "null"];
103
- readonly description: "Subject header value. Null if the email had no Subject header.";
104
- };
105
- readonly from: {
106
- readonly type: "string";
107
- readonly description: "From header value. May include display name: `\"John Doe\" <john@example.com>`";
108
- };
109
- readonly to: {
110
- readonly type: "string";
111
- readonly description: "To header value. May include multiple addresses or display names.";
112
- };
113
- readonly date: {
114
- readonly type: ["string", "null"];
115
- readonly description: "Date header value as it appeared in the email. Null if the email had no Date header.";
116
- };
117
- };
118
- readonly required: ["message_id", "subject", "from", "to", "date"];
119
- readonly additionalProperties: false;
120
- readonly description: "Parsed email headers. These are extracted from the email content, not the SMTP envelope.";
121
- };
122
- readonly content: {
123
- readonly type: "object";
124
- readonly properties: {
125
- readonly raw: {
126
- readonly $ref: "#/definitions/RawContent";
127
- readonly description: "Raw email in RFC 5322 format. May be inline (base64) or download-only depending on size.";
128
- };
129
- readonly download: {
130
- readonly type: "object";
131
- readonly properties: {
132
- readonly url: {
133
- readonly type: "string";
134
- readonly description: "HTTPS URL to download the raw email. Returns the email as-is in RFC 5322 format.";
135
- };
136
- readonly expires_at: {
137
- readonly type: "string";
138
- readonly description: "ISO 8601 timestamp (UTC) when this URL expires. Download before this time or the URL will return 403.";
139
- };
140
- };
141
- readonly required: ["url", "expires_at"];
142
- readonly additionalProperties: false;
143
- readonly description: "Download information for the raw email. Always present, even if raw content is inline.";
144
- };
145
- };
146
- readonly required: ["raw", "download"];
147
- readonly additionalProperties: false;
148
- readonly description: "Raw email content and download information.";
149
- };
150
- readonly parsed: {
151
- readonly $ref: "#/definitions/ParsedData";
152
- readonly description: "Parsed email content (body text, HTML, attachments). Check `status` to determine if parsing succeeded.";
153
- };
154
- readonly analysis: {
155
- readonly type: "object";
156
- readonly properties: {
157
- readonly spamassassin: {
158
- readonly type: "object";
159
- readonly properties: {
160
- readonly score: {
161
- readonly type: "number";
162
- readonly description: "Overall spam score (sum of all rule scores). Higher scores indicate higher likelihood of spam. Unbounded - can be negative (ham) or very high (spam).";
163
- };
164
- };
165
- readonly required: ["score"];
166
- readonly additionalProperties: false;
167
- readonly description: "SpamAssassin analysis results.";
168
- };
169
- };
170
- readonly required: ["spamassassin"];
171
- readonly additionalProperties: false;
172
- readonly description: "Email analysis and classification results. May be absent if analysis was not performed.";
173
- };
174
- };
175
- readonly required: ["id", "received_at", "smtp", "headers", "content", "parsed"];
176
- readonly additionalProperties: false;
177
- readonly description: "The email that triggered this event.";
178
- };
179
- };
180
- readonly required: ["id", "event", "version", "delivery", "email"];
181
- readonly additionalProperties: false;
182
- readonly description: "Webhook payload for the `email.received` event.\n\nThis is delivered to your webhook endpoint when MyMX receives an email matching your domain configuration.";
183
- };
184
- readonly WebhookVersion: {
185
- readonly type: "string";
186
- readonly description: "Valid webhook version format (YYYY-MM-DD date string). The SDK accepts any valid date-formatted version, not just the current one, for forward and backward compatibility.";
187
- };
188
- readonly RawContent: {
189
- readonly anyOf: [{
190
- readonly $ref: "#/definitions/RawContentInline";
191
- }, {
192
- readonly $ref: "#/definitions/RawContentDownloadOnly";
193
- }];
194
- readonly description: "Raw email content - a discriminated union on `included`.";
195
- };
196
- readonly RawContentInline: {
197
- readonly type: "object";
198
- readonly properties: {
199
- readonly included: {
200
- readonly type: "boolean";
201
- readonly const: true;
202
- readonly description: "Discriminant indicating raw content is included inline.";
203
- };
204
- readonly encoding: {
205
- readonly type: "string";
206
- readonly const: "base64";
207
- readonly description: "Encoding used for the data field. Always \"base64\".";
208
- };
209
- readonly max_inline_bytes: {
210
- readonly type: "number";
211
- readonly description: "Maximum size in bytes for inline inclusion. Emails larger than this threshold require download.";
212
- };
213
- readonly size_bytes: {
214
- readonly type: "number";
215
- readonly description: "Actual size of the raw email in bytes.";
216
- };
217
- readonly sha256: {
218
- readonly type: "string";
219
- readonly description: "SHA-256 hash of the raw email content (hex-encoded). Use this to verify integrity after base64 decoding.";
220
- };
221
- readonly data: {
222
- readonly type: "string";
223
- readonly description: "Base64-encoded raw email (RFC 5322 format). Decode with `Buffer.from(data, 'base64')` in Node.js.";
224
- };
225
- };
226
- readonly required: ["included", "encoding", "max_inline_bytes", "size_bytes", "sha256", "data"];
227
- readonly additionalProperties: false;
228
- readonly description: "Raw email content included inline (base64 encoded).\n\nWhen the raw email is small enough (under {@link max_inline_bytes } ), it's included directly in the webhook payload for convenience.";
229
- };
230
- readonly RawContentDownloadOnly: {
231
- readonly type: "object";
232
- readonly properties: {
233
- readonly included: {
234
- readonly type: "boolean";
235
- readonly const: false;
236
- readonly description: "Discriminant indicating raw content must be downloaded.";
237
- };
238
- readonly reason_code: {
239
- readonly type: "string";
240
- readonly const: "size_exceeded";
241
- readonly description: "Reason the content wasn't included inline.";
242
- };
243
- readonly max_inline_bytes: {
244
- readonly type: "number";
245
- readonly description: "Maximum size in bytes for inline inclusion. The email exceeded this threshold.";
246
- };
247
- readonly size_bytes: {
248
- readonly type: "number";
249
- readonly description: "Actual size of the raw email in bytes.";
250
- };
251
- readonly sha256: {
252
- readonly type: "string";
253
- readonly description: "SHA-256 hash of the raw email content (hex-encoded). Use this to verify integrity after download.";
254
- };
255
- };
256
- readonly required: ["included", "reason_code", "max_inline_bytes", "size_bytes", "sha256"];
257
- readonly additionalProperties: false;
258
- readonly description: "Raw email content not included (must be downloaded).\n\nWhen the raw email exceeds {@link max_inline_bytes } , it's not included in the webhook payload. Use the download URL from {@link EmailReceivedEvent.email.content.download } to fetch it.";
259
- };
260
- readonly ParsedData: {
261
- readonly anyOf: [{
262
- readonly $ref: "#/definitions/ParsedDataComplete";
263
- }, {
264
- readonly $ref: "#/definitions/ParsedDataFailed";
265
- }];
266
- readonly description: "Parsed email content - a discriminated union on `status`.";
267
- };
268
- readonly ParsedDataComplete: {
269
- readonly type: "object";
270
- readonly properties: {
271
- readonly status: {
272
- readonly type: "string";
273
- readonly const: "complete";
274
- readonly description: "Discriminant indicating successful parsing.";
275
- };
276
- readonly error: {
277
- readonly type: "null";
278
- readonly description: "Always null when parsing succeeds.";
279
- };
280
- readonly body_text: {
281
- readonly type: ["string", "null"];
282
- readonly description: "Plain text body of the email. Null if the email had no text/plain part.";
283
- };
284
- readonly body_html: {
285
- readonly type: ["string", "null"];
286
- readonly description: "HTML body of the email. Null if the email had no text/html part.";
287
- };
288
- readonly reply_to: {
289
- readonly anyOf: [{
290
- readonly type: "array";
291
- readonly items: {
292
- readonly $ref: "#/definitions/EmailAddress";
293
- };
294
- }, {
295
- readonly type: "null";
296
- }];
297
- readonly description: "Parsed Reply-To header addresses. Null if the email had no Reply-To header.";
298
- };
299
- readonly cc: {
300
- readonly anyOf: [{
301
- readonly type: "array";
302
- readonly items: {
303
- readonly $ref: "#/definitions/EmailAddress";
304
- };
305
- }, {
306
- readonly type: "null";
307
- }];
308
- readonly description: "Parsed CC header addresses. Null if the email had no CC header.";
309
- };
310
- readonly bcc: {
311
- readonly anyOf: [{
312
- readonly type: "array";
313
- readonly items: {
314
- readonly $ref: "#/definitions/EmailAddress";
315
- };
316
- }, {
317
- readonly type: "null";
318
- }];
319
- readonly description: "Parsed BCC header addresses. Null if the email had no BCC header. Note: BCC is only available for outgoing emails or when explicitly provided.";
320
- };
321
- readonly in_reply_to: {
322
- readonly anyOf: [{
323
- readonly type: "array";
324
- readonly items: {
325
- readonly type: "string";
326
- };
327
- }, {
328
- readonly type: "null";
329
- }];
330
- readonly description: "In-Reply-To header values (Message-IDs of the email(s) being replied to). Null if the email had no In-Reply-To header. Per RFC 5322, this can contain multiple Message-IDs, though typically just one.";
331
- readonly examples: [["<original-message-id@example.com>"]];
332
- };
333
- readonly references: {
334
- readonly anyOf: [{
335
- readonly type: "array";
336
- readonly items: {
337
- readonly type: "string";
338
- };
339
- }, {
340
- readonly type: "null";
341
- }];
342
- readonly description: "References header values (Message-IDs of the email thread). Null if the email had no References header.";
343
- readonly examples: [["<msg1@example.com>", "<msg2@example.com>"]];
344
- };
345
- readonly attachments: {
346
- readonly type: "array";
347
- readonly items: {
348
- readonly $ref: "#/definitions/WebhookAttachment";
349
- };
350
- readonly description: "List of attachments with metadata. Use {@link attachments_download_url } to download the actual files.";
351
- };
352
- readonly attachments_download_url: {
353
- readonly type: ["string", "null"];
354
- readonly description: "HTTPS URL to download all attachments as a tar.gz archive. Null if the email had no attachments. URL expires - check the expiration before downloading.";
355
- };
356
- };
357
- readonly required: ["status", "error", "body_text", "body_html", "reply_to", "cc", "bcc", "in_reply_to", "references", "attachments", "attachments_download_url"];
358
- readonly additionalProperties: false;
359
- readonly description: "Parsed email content when parsing succeeded.\n\nUse the discriminant `status: \"complete\"` to narrow from {@link ParsedData } .";
360
- };
361
- readonly EmailAddress: {
362
- readonly type: "object";
363
- readonly properties: {
364
- readonly address: {
365
- readonly type: "string";
366
- readonly description: "The email address portion (e.g., \"john@example.com\").\n\nThis is the raw value from the email header with no validation applied. May contain unusual but valid formats like quoted local parts.";
367
- };
368
- readonly name: {
369
- readonly type: ["string", "null"];
370
- readonly description: "The display name portion, if present. Null if the address had no display name.\n\nMay contain any characters including unicode, emoji, or special characters as they appeared in the original email header.";
371
- };
372
- };
373
- readonly required: ["address", "name"];
374
- readonly additionalProperties: false;
375
- readonly description: "A parsed email address with optional display name.\n\nThis structure is used in the `parsed` section of the webhook payload (e.g., `reply_to`, `cc`, `bcc`). For unparsed header strings, see the `headers` section (e.g., `event.email.headers.from`).";
376
- };
377
- readonly WebhookAttachment: {
378
- readonly type: "object";
379
- readonly properties: {
380
- readonly filename: {
381
- readonly type: ["string", "null"];
382
- readonly description: "Original filename from the email. May be null if the attachment had no filename specified.";
383
- };
384
- readonly content_type: {
385
- readonly type: "string";
386
- readonly description: "MIME content type (e.g., \"application/pdf\", \"image/png\").";
387
- };
388
- readonly size_bytes: {
389
- readonly type: "number";
390
- readonly description: "Size of the attachment in bytes.";
391
- };
392
- readonly sha256: {
393
- readonly type: "string";
394
- readonly description: "SHA-256 hash of the attachment content (hex-encoded). Use this to verify attachment integrity after download.";
395
- };
396
- readonly part_index: {
397
- readonly type: "number";
398
- readonly description: "Zero-based index of this part in the MIME structure.";
399
- };
400
- readonly tar_path: {
401
- readonly type: "string";
402
- readonly description: "Path to this attachment within the downloaded tar.gz archive.";
403
- };
404
- };
405
- readonly required: ["filename", "content_type", "size_bytes", "sha256", "part_index", "tar_path"];
406
- readonly additionalProperties: false;
407
- readonly description: "Metadata for an email attachment.\n\nAttachment content is not included directly in the webhook payload. Use the `attachments_download_url` from {@link ParsedDataComplete } to download all attachments as a tar.gz archive.";
408
- };
409
- readonly ParsedDataFailed: {
410
- readonly type: "object";
411
- readonly properties: {
412
- readonly status: {
413
- readonly type: "string";
414
- readonly const: "failed";
415
- readonly description: "Discriminant indicating parsing failed.";
416
- };
417
- readonly error: {
418
- readonly $ref: "#/definitions/ParsedError";
419
- readonly description: "Details about why parsing failed.";
420
- };
421
- readonly body_text: {
422
- readonly type: "null";
423
- readonly description: "Always null when parsing fails.";
424
- };
425
- readonly body_html: {
426
- readonly type: "null";
427
- readonly description: "Always null when parsing fails.";
428
- };
429
- readonly reply_to: {
430
- readonly type: "null";
431
- readonly description: "Always null when parsing fails.";
432
- };
433
- readonly cc: {
434
- readonly type: "null";
435
- readonly description: "Always null when parsing fails.";
436
- };
437
- readonly bcc: {
438
- readonly type: "null";
439
- readonly description: "Always null when parsing fails.";
440
- };
441
- readonly in_reply_to: {
442
- readonly type: "null";
443
- readonly description: "Always null when parsing fails.";
444
- };
445
- readonly references: {
446
- readonly type: "null";
447
- readonly description: "Always null when parsing fails.";
448
- };
449
- readonly attachments: {
450
- readonly type: "array";
451
- readonly items: {
452
- readonly $ref: "#/definitions/WebhookAttachment";
453
- };
454
- readonly description: "May contain partial attachment metadata even when parsing failed. Useful for debugging or recovering partial data.";
455
- };
456
- readonly attachments_download_url: {
457
- readonly type: "null";
458
- readonly description: "Always null when parsing fails.";
459
- };
460
- };
461
- readonly required: ["status", "error", "body_text", "body_html", "reply_to", "cc", "bcc", "in_reply_to", "references", "attachments", "attachments_download_url"];
462
- readonly additionalProperties: false;
463
- readonly description: "Parsed email content when parsing failed.\n\nUse the discriminant `status: \"failed\"` to narrow from {@link ParsedData } .";
464
- };
465
- readonly ParsedError: {
466
- readonly type: "object";
467
- readonly properties: {
468
- readonly code: {
469
- readonly type: "string";
470
- readonly enum: ["PARSE_FAILED", "ATTACHMENT_EXTRACTION_FAILED"];
471
- readonly description: "Error code indicating the type of failure.\n- `PARSE_FAILED`: The email could not be parsed (e.g., malformed MIME)\n- `ATTACHMENT_EXTRACTION_FAILED`: Email parsed but attachments couldn't be extracted";
472
- };
473
- readonly message: {
474
- readonly type: "string";
475
- readonly description: "Human-readable error message describing what went wrong.";
476
- };
477
- readonly retryable: {
478
- readonly type: "boolean";
479
- readonly description: "Whether retrying might succeed. If true, the error was transient (e.g., timeout). If false, the email itself is problematic.";
480
- };
481
- };
482
- readonly required: ["code", "message", "retryable"];
483
- readonly additionalProperties: false;
484
- readonly description: "Error details when email parsing fails.";
485
- };
486
- };
487
- }; //#endregion
488
- //#region src/index.d.ts
489
-
490
- /**
491
- * Parse a webhook payload, returning typed events for known types
492
- * and UnknownEvent for future event types.
493
- *
494
- * This provides forward-compatibility: when MyMX adds new event types,
495
- * your code won't break - you'll receive an UnknownEvent that you can
496
- * handle or ignore.
497
- *
498
- * For most use cases, prefer `handleWebhook()` which also verifies
499
- * the signature and validates the payload schema.
500
- *
501
- * @param input - The parsed JSON payload
502
- * @returns Typed event for known types, UnknownEvent for unknown types
503
- * @throws WebhookPayloadError if the input is not a valid webhook structure
504
- *
505
- * @example
506
- * ```typescript
507
- * import { parseWebhookEvent } from 'mymx';
508
- *
509
- * const event = parseWebhookEvent(JSON.parse(rawBody));
510
- *
511
- * if (event.event === "email.received") {
512
- * // TypeScript knows this is EmailReceivedEvent
513
- * console.log(event.email.headers.subject);
514
- * } else {
515
- * // Handle or log unknown event types
516
- * console.log("Unknown event:", event.event);
517
- * }
518
- * ```
519
- */
520
- declare function parseWebhookEvent(input: unknown): WebhookEvent;
521
- /**
522
- * Type guard to check if a webhook event is an EmailReceivedEvent.
523
- *
524
- * @example
525
- * ```typescript
526
- * const event = parseWebhookEvent(payload);
527
- * if (isEmailReceivedEvent(event)) {
528
- * // TypeScript knows event is EmailReceivedEvent
529
- * console.log(event.email.headers.subject);
530
- * }
531
- * ```
532
- */
533
- declare function isEmailReceivedEvent(event: WebhookEvent | unknown): event is EmailReceivedEvent;
534
- /**
535
- * Request headers in any common format.
536
- *
537
- * Accepts:
538
- * - **Plain object** from Express/Node.js (`req.headers`)
539
- * - **Fetch API `Headers`** from Next.js App Router, Cloudflare Workers (`request.headers`)
540
- *
541
- * Header lookup is case-insensitive per RFC 7230.
542
- */
543
- type WebhookHeaders = Record<string, string | string[] | undefined> | Headers;
544
- /**
545
- * Options for the handleWebhook function.
546
- */
547
- interface HandleWebhookOptions {
548
- /**
549
- * The raw request body (before JSON parsing).
550
- * Must be the exact bytes received - do not re-serialize.
551
- */
552
- body: string | Buffer;
553
- /**
554
- * The request headers object.
555
- * Works with Express (req.headers), Fetch API (Request.headers), or any
556
- * object with string keys. The SDK will find the MyMX-Signature header.
557
- */
558
- headers: WebhookHeaders;
559
- /**
560
- * Your webhook secret from the MyMX dashboard.
561
- */
562
- secret: string;
563
- /**
564
- * Maximum age of the webhook in seconds.
565
- * Webhooks older than this will be rejected as potential replay attacks.
566
- * @default 300 (5 minutes)
567
- */
568
- toleranceSeconds?: number;
569
- }
570
- /**
571
- * Verify, parse, and validate a webhook in one call.
572
- *
573
- * This is the recommended way to handle MyMX webhooks. It:
574
- * 1. Verifies the signature to ensure the webhook is authentic
575
- * 2. Parses the JSON body
576
- * 3. Validates the payload against the schema with Zod
577
- * 4. Returns a fully typed EmailReceivedEvent
578
- *
579
- * @param options - The webhook data and secret
580
- * @returns A validated EmailReceivedEvent
581
- * @throws {WebhookVerificationError} If signature verification fails
582
- * @throws {WebhookPayloadError} If JSON parsing fails
583
- * @throws {WebhookValidationError} If schema validation fails
584
- *
585
- * @example
586
- * ```typescript
587
- * import { handleWebhook, MyMXWebhookError } from 'mymx';
588
- *
589
- * app.post('/webhooks/email', express.raw({ type: 'application/json' }), (req, res) => {
590
- * try {
591
- * const event = handleWebhook({
592
- * body: req.body,
593
- * headers: req.headers,
594
- * secret: process.env.MYMX_WEBHOOK_SECRET,
595
- * });
596
- *
597
- * console.log('Email from:', event.email.headers.from);
598
- * res.json({ received: true });
599
- * } catch (err) {
600
- * if (err instanceof MyMXWebhookError) {
601
- * console.error(`[${err.code}] ${err.message}`);
602
- * return res.status(400).json({ error: err.code });
603
- * }
604
- * throw err;
605
- * }
606
- * });
607
- * ```
608
- */
609
- declare function handleWebhook(options: HandleWebhookOptions): EmailReceivedEvent;
610
- /**
611
- * Returns headers for the optional "content discard" feature.
612
- *
613
- * If you have the "content discard" setting enabled in your MyMX dashboard,
614
- * returning this header tells MyMX to permanently delete the email content
615
- * after successful delivery. Requires BOTH the dashboard setting AND this header.
616
- *
617
- * **Warning:** Only use this if you can durably guarantee you've processed the email.
618
- * Once discarded, the email content is gone forever.
619
- *
620
- * @returns Headers object to spread into your response
621
- *
622
- * @example Express (only if using content discard)
623
- * ```typescript
624
- * app.post('/webhook', (req, res) => {
625
- * const event = handleWebhook({ ... });
626
- * // Durably save the email first!
627
- * await db.saveEmail(event);
628
- * res.set(confirmedHeaders()).json({ received: true });
629
- * });
630
- * ```
631
- *
632
- * @example Fetch API / Next.js (only if using content discard)
633
- * ```typescript
634
- * return new Response(JSON.stringify({ received: true }), {
635
- * status: 200,
636
- * headers: {
637
- * 'Content-Type': 'application/json',
638
- * ...confirmedHeaders(),
639
- * },
640
- * });
641
- * ```
642
- */
643
- declare function confirmedHeaders(): {
644
- "X-MyMX-Confirmed": "true";
645
- };
646
- /**
647
- * Check if the download URL for a webhook event has expired.
648
- *
649
- * @param event - The webhook event
650
- * @param now - Optional current time for testing (defaults to Date.now())
651
- * @returns true if the download URL has expired
652
- *
653
- * @example
654
- * ```typescript
655
- * if (isDownloadExpired(event)) {
656
- * console.log("Download URL has expired, cannot fetch raw email");
657
- * } else {
658
- * const response = await fetch(event.email.content.download.url);
659
- * }
660
- * ```
661
- */
662
- declare function isDownloadExpired(event: EmailReceivedEvent, now?: number): boolean;
663
- /**
664
- * Get the time remaining (in milliseconds) before the download URL expires.
665
- * Returns 0 if already expired.
666
- *
667
- * @param event - The webhook event
668
- * @param now - Optional current time for testing (defaults to Date.now())
669
- * @returns Milliseconds until expiration, or 0 if expired
670
- *
671
- * @example
672
- * ```typescript
673
- * const remaining = getDownloadTimeRemaining(event);
674
- * if (remaining > 60000) {
675
- * // More than 1 minute left, safe to download
676
- * }
677
- * ```
678
- */
679
- declare function getDownloadTimeRemaining(event: EmailReceivedEvent, now?: number): number;
680
- /**
681
- * Check if the raw email content is included inline in the event.
682
- *
683
- * Use this to check before calling `decodeRawEmail()` to avoid try/catch.
684
- *
685
- * @param event - The webhook event
686
- * @returns true if raw content is included inline, false if download required
687
- *
688
- * @example
689
- * ```typescript
690
- * if (isRawIncluded(event)) {
691
- * const rawEmail = decodeRawEmail(event);
692
- * } else {
693
- * const response = await fetch(event.email.content.download.url);
694
- * }
695
- * ```
696
- */
697
- declare function isRawIncluded(event: EmailReceivedEvent): boolean;
698
- /**
699
- * Options for decoding raw email content.
700
- */
701
- interface DecodeRawEmailOptions {
702
- /**
703
- * Whether to verify the SHA-256 hash after decoding.
704
- * @default true
705
- */
706
- verify?: boolean;
707
- }
708
- /**
709
- * Decode the raw email content from an EmailReceivedEvent.
710
- *
711
- * Throws if the raw content is not included inline (i.e., must be downloaded).
712
- * By default, verifies the SHA-256 hash matches after decoding.
713
- *
714
- * NOTE: This function assumes a well-formed event from `handleWebhook()`.
715
- * Passing a manually constructed event with missing fields (e.g., `raw.data`
716
- * undefined when `raw.included` is true) will result in undefined behavior.
717
- *
718
- * @param event - The webhook event containing the raw email
719
- * @param options - Decoding options
720
- * @returns The decoded raw email as a Buffer
721
- * @throws {RawEmailDecodeError} If content not included or hash mismatch
722
- *
723
- * @example
724
- * ```typescript
725
- * import { handleWebhook, decodeRawEmail, isRawIncluded } from 'mymx';
726
- *
727
- * const event = handleWebhook({ body, headers, secret });
728
- *
729
- * if (isRawIncluded(event)) {
730
- * const rawEmail = decodeRawEmail(event);
731
- * // rawEmail is a Buffer containing the RFC 5322 email
732
- * } else {
733
- * // Must download from event.email.content.download.url
734
- * }
735
- * ```
736
- */
737
- declare function decodeRawEmail(event: EmailReceivedEvent, options?: DecodeRawEmailOptions): Buffer;
738
- /**
739
- * Verify downloaded raw email content against the SHA-256 hash in the event.
740
- *
741
- * Use this after fetching from `event.email.content.download.url` to ensure
742
- * the downloaded content matches what MyMX received.
743
- *
744
- * @param downloaded - The downloaded raw email content (Buffer, ArrayBuffer, or Uint8Array)
745
- * @param event - The webhook event containing the expected hash
746
- * @returns The verified content as a Buffer
747
- * @throws {RawEmailDecodeError} If hash doesn't match
748
- *
749
- * @example
750
- * ```typescript
751
- * import { handleWebhook, verifyRawEmailDownload, isRawIncluded } from 'mymx';
752
- *
753
- * const event = handleWebhook({ body, headers, secret });
754
- *
755
- * if (!isRawIncluded(event)) {
756
- * const response = await fetch(event.email.content.download.url);
757
- * const arrayBuffer = await response.arrayBuffer();
758
- * const verified = verifyRawEmailDownload(arrayBuffer, event);
759
- * // verified is a Buffer containing the RFC 5322 email
760
- * }
761
- * ```
762
- */
763
- declare function verifyRawEmailDownload(downloaded: Buffer | ArrayBuffer | Uint8Array, event: EmailReceivedEvent): Buffer; //#endregion
764
- export { DecodeRawEmailOptions, EmailAddress, EmailReceivedEvent, HandleWebhookOptions, KnownWebhookEvent, MYMX_CONFIRMED_HEADER, MYMX_SIGNATURE_HEADER, MyMXWebhookError, PAYLOAD_ERRORS, ParsedData, ParsedDataComplete, ParsedDataFailed, ParsedError, ParsedStatus, RAW_EMAIL_ERRORS, RawContent, RawContentDownloadOnly, RawContentInline, RawEmailDecodeError, RawEmailDecodeErrorCode, UnknownEvent, VERIFICATION_ERRORS, VerifyOptions, WEBHOOK_VERSION, WebhookAttachment, WebhookErrorCode, WebhookEvent, WebhookHeaders, WebhookPayloadError, WebhookPayloadErrorCode, WebhookValidationError, WebhookValidationErrorCode, WebhookVerificationError, WebhookVerificationErrorCode, confirmedHeaders, decodeRawEmail, emailReceivedEventJsonSchema, getDownloadTimeRemaining, handleWebhook, isDownloadExpired, isEmailReceivedEvent, isRawIncluded, parseWebhookEvent, verifyRawEmailDownload, verifyWebhookSignature };