mailumi 1.0.0 → 1.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/README.md CHANGED
@@ -132,12 +132,18 @@ export async function POST(request: Request) {
132
132
  if (error) return new Response(error.message, { status: 401 });
133
133
 
134
134
  if (event.type === 'email.received') {
135
- // event.data has from, to, subject, text, html and attachments
135
+ const { from, reply_to, subject, text, html, message_id, spam, attachments } = event.data;
136
+ // spam is a boolean; attachments have filename, content_type, size and download_url
137
+ }
138
+ if (event.type === 'email.bounced') {
139
+ const { recipient, bounce } = event.data; // bounce.type is "Permanent" or "Transient"
136
140
  }
137
141
  return new Response('OK');
138
142
  }
139
143
  ```
140
144
 
145
+ `event.data` is typed by `event.type`: `EmailReceivedData` for `email.received`, and `EmailEventData` for events about email you sent, which is the email plus `recipient` and a detail such as `delivery`, `bounce` or `click`. Every field is described under [Receiving email](https://mailumi.com/docs#receiving) and [Webhooks](https://mailumi.com/docs#webhooks).
146
+
141
147
  `headers` can be a `Headers` object, Node's `req.headers`, or `{ id, timestamp, signature }`. Deliveries can repeat, so store `event.id` and skip ids you have seen. Events: `email.sent`, `email.delivered`, `email.delivery_delayed`, `email.bounced`, `email.complained`, `email.opened`, `email.clicked`, `email.failed`, `email.unsubscribed` and `email.received`.
142
148
 
143
149
  ## Audiences and contacts
@@ -215,7 +221,7 @@ Mailumi publishes [llms.txt](https://mailumi.com/llms.txt), [llms-full.txt](http
215
221
  ## Links
216
222
 
217
223
  - [Documentation](https://mailumi.com/docs)
218
- - [Pricing](https://mailumi.com/pricing): the free plan includes 3,000 emails a month
224
+ - [Pricing](https://mailumi.com/pricing): the free plan includes 1,500 emails a month
219
225
  - [Support](https://mailumi.com/contact)
220
226
 
221
227
  ## Versioning
@@ -2,7 +2,7 @@
2
2
  * The official Mailumi SDK. Works anywhere `fetch` exists: Node.js 18+, Bun, Deno and Cloudflare Workers.
3
3
  * Every method resolves to `{ data, error }` and never throws for API errors.
4
4
  */
5
- export declare const VERSION = "1.0.0";
5
+ export declare const VERSION = "1.1.0";
6
6
  export type ErrorCode = 'invalid_request' | 'unauthorized' | 'plan_limit_reached' | 'forbidden' | 'not_found' | 'method_not_allowed' | 'conflict' | 'payload_too_large' | 'rate_limited' | 'internal_error' | 'service_unavailable' | 'network_error' | 'missing_api_key' | 'invalid_signature' | 'missing_webhook_secret';
7
7
  export interface MailumiError {
8
8
  name: 'MailumiError';
@@ -189,6 +189,105 @@ export interface WebhookEvent<T = Record<string, any>> {
189
189
  created_at: string;
190
190
  data: T;
191
191
  }
192
+ interface EmailFields {
193
+ email_id: string;
194
+ domain_id: string;
195
+ from: string;
196
+ to: string[];
197
+ subject: string;
198
+ last_event: string;
199
+ tags: Record<string, string>;
200
+ created_at: string;
201
+ }
202
+ export interface ReceivedAttachment {
203
+ id: string;
204
+ filename: string;
205
+ content_type: string;
206
+ /** Set for inline images that the HTML shows with `cid:`. */
207
+ content_id: string | null;
208
+ /** Size in bytes. */
209
+ size: number;
210
+ /** Download it server-side with `mailumi.emails.attachment({ id: email_id, index })` or a key with email:read. */
211
+ download_url: string;
212
+ }
213
+ /** `data` of an `email.received` event. */
214
+ export interface EmailReceivedData extends EmailFields {
215
+ direction: 'inbound';
216
+ status: 'received';
217
+ scheduled_at: null;
218
+ error: null;
219
+ /** `Name <address>`, or only the address when the email has no name. */
220
+ from: string;
221
+ /** The recipients at this domain, in lowercase. */
222
+ to: string[];
223
+ cc: string[];
224
+ reply_to: string[];
225
+ /** The Message-ID header with its angle brackets, for In-Reply-To and References when you reply. */
226
+ message_id: string | null;
227
+ /** Empty when the email has no plain-text part. */
228
+ text: string;
229
+ /** Empty when the email has no HTML part. It is the sender's HTML: sanitize it before showing it. */
230
+ html: string;
231
+ /** `true` when the Amazon SES spam check flagged the email. */
232
+ spam: boolean;
233
+ attachments: ReceivedAttachment[];
234
+ }
235
+ /** `data` of an event about an email you sent: the email as it is now, plus the detail for that event. */
236
+ export interface EmailEventData extends EmailFields {
237
+ direction: 'outbound';
238
+ status: string;
239
+ scheduled_at: string | null;
240
+ error: string | null;
241
+ /** The recipient the event is about. Not set on email.sent and email.failed, nor on opens and clicks of an email with more than one recipient. */
242
+ recipient?: string;
243
+ delivery?: {
244
+ smtp_response: string | null;
245
+ };
246
+ delay?: {
247
+ type: string | null;
248
+ expires_at: string | null;
249
+ message: string | null;
250
+ };
251
+ bounce?: {
252
+ type: 'Permanent' | 'Transient' | (string & {}) | null;
253
+ sub_type: string | null;
254
+ message: string | null;
255
+ };
256
+ complaint?: {
257
+ type: string | null;
258
+ };
259
+ open?: {
260
+ ip_address: string | null;
261
+ user_agent: string | null;
262
+ };
263
+ click?: {
264
+ link: string | null;
265
+ ip_address: string | null;
266
+ user_agent: string | null;
267
+ };
268
+ failed?: {
269
+ reason: string;
270
+ };
271
+ }
272
+ /** The event `verifyWebhook` returns. Check `event.type` and `event.data` has the matching fields. */
273
+ export type MailumiWebhookEvent = {
274
+ id: string;
275
+ type: 'email.received';
276
+ created_at: string;
277
+ data: EmailReceivedData;
278
+ } | {
279
+ id: string;
280
+ type: Exclude<WebhookEventType, 'email.received' | 'webhook.test'>;
281
+ created_at: string;
282
+ data: EmailEventData;
283
+ } | {
284
+ id: string;
285
+ type: 'webhook.test';
286
+ created_at: string;
287
+ data: {
288
+ message: string;
289
+ };
290
+ };
192
291
  export interface VerifyWebhookOptions {
193
292
  /** The raw request body exactly as received, before any JSON parsing. */
194
293
  payload: string | Uint8Array;
@@ -203,7 +302,8 @@ export interface VerifyWebhookOptions {
203
302
  * Checks the Standard Webhooks signature of a Mailumi webhook and returns the parsed event.
204
303
  * Store `event.id` and skip ids you have seen: a delivery can arrive more than once.
205
304
  */
206
- export declare function verifyWebhook<T = Record<string, any>>({ payload, headers, secret, tolerance }: VerifyWebhookOptions): Promise<Result<WebhookEvent<T>>>;
305
+ export declare function verifyWebhook(options: VerifyWebhookOptions): Promise<Result<MailumiWebhookEvent>>;
306
+ export declare function verifyWebhook<T>(options: VerifyWebhookOptions): Promise<Result<WebhookEvent<T>>>;
207
307
  /** Converts the SDK's camelCase options to the API's request body. snake_case keys are accepted too. */
208
308
  export declare function toApiEmail(o: SendEmailOptions & Record<string, any>): Record<string, unknown>;
209
309
  export declare class Mailumi {
package/dist/cjs/index.js CHANGED
@@ -7,7 +7,7 @@ exports.toApiEmail = toApiEmail;
7
7
  * The official Mailumi SDK. Works anywhere `fetch` exists: Node.js 18+, Bun, Deno and Cloudflare Workers.
8
8
  * Every method resolves to `{ data, error }` and never throws for API errors.
9
9
  */
10
- exports.VERSION = '1.0.0';
10
+ exports.VERSION = '1.1.0';
11
11
  const DEFAULT_BASE_URL = 'https://mailumi.com/api/v1';
12
12
  function header(h, name, short) {
13
13
  if (typeof h.get === 'function')
@@ -29,10 +29,6 @@ function sameText(a, b) {
29
29
  diff |= (a.charCodeAt(i) || 0) ^ (b.charCodeAt(i) || 0);
30
30
  return diff === 0;
31
31
  }
32
- /**
33
- * Checks the Standard Webhooks signature of a Mailumi webhook and returns the parsed event.
34
- * Store `event.id` and skip ids you have seen: a delivery can arrive more than once.
35
- */
36
32
  async function verifyWebhook({ payload, headers, secret, tolerance = 300 }) {
37
33
  const key = secret ?? globalThis.process?.env?.MAILUMI_WEBHOOK_SECRET;
38
34
  if (!key)
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  * The official Mailumi SDK. Works anywhere `fetch` exists: Node.js 18+, Bun, Deno and Cloudflare Workers.
3
3
  * Every method resolves to `{ data, error }` and never throws for API errors.
4
4
  */
5
- export declare const VERSION = "1.0.0";
5
+ export declare const VERSION = "1.1.0";
6
6
  export type ErrorCode = 'invalid_request' | 'unauthorized' | 'plan_limit_reached' | 'forbidden' | 'not_found' | 'method_not_allowed' | 'conflict' | 'payload_too_large' | 'rate_limited' | 'internal_error' | 'service_unavailable' | 'network_error' | 'missing_api_key' | 'invalid_signature' | 'missing_webhook_secret';
7
7
  export interface MailumiError {
8
8
  name: 'MailumiError';
@@ -189,6 +189,105 @@ export interface WebhookEvent<T = Record<string, any>> {
189
189
  created_at: string;
190
190
  data: T;
191
191
  }
192
+ interface EmailFields {
193
+ email_id: string;
194
+ domain_id: string;
195
+ from: string;
196
+ to: string[];
197
+ subject: string;
198
+ last_event: string;
199
+ tags: Record<string, string>;
200
+ created_at: string;
201
+ }
202
+ export interface ReceivedAttachment {
203
+ id: string;
204
+ filename: string;
205
+ content_type: string;
206
+ /** Set for inline images that the HTML shows with `cid:`. */
207
+ content_id: string | null;
208
+ /** Size in bytes. */
209
+ size: number;
210
+ /** Download it server-side with `mailumi.emails.attachment({ id: email_id, index })` or a key with email:read. */
211
+ download_url: string;
212
+ }
213
+ /** `data` of an `email.received` event. */
214
+ export interface EmailReceivedData extends EmailFields {
215
+ direction: 'inbound';
216
+ status: 'received';
217
+ scheduled_at: null;
218
+ error: null;
219
+ /** `Name <address>`, or only the address when the email has no name. */
220
+ from: string;
221
+ /** The recipients at this domain, in lowercase. */
222
+ to: string[];
223
+ cc: string[];
224
+ reply_to: string[];
225
+ /** The Message-ID header with its angle brackets, for In-Reply-To and References when you reply. */
226
+ message_id: string | null;
227
+ /** Empty when the email has no plain-text part. */
228
+ text: string;
229
+ /** Empty when the email has no HTML part. It is the sender's HTML: sanitize it before showing it. */
230
+ html: string;
231
+ /** `true` when the Amazon SES spam check flagged the email. */
232
+ spam: boolean;
233
+ attachments: ReceivedAttachment[];
234
+ }
235
+ /** `data` of an event about an email you sent: the email as it is now, plus the detail for that event. */
236
+ export interface EmailEventData extends EmailFields {
237
+ direction: 'outbound';
238
+ status: string;
239
+ scheduled_at: string | null;
240
+ error: string | null;
241
+ /** The recipient the event is about. Not set on email.sent and email.failed, nor on opens and clicks of an email with more than one recipient. */
242
+ recipient?: string;
243
+ delivery?: {
244
+ smtp_response: string | null;
245
+ };
246
+ delay?: {
247
+ type: string | null;
248
+ expires_at: string | null;
249
+ message: string | null;
250
+ };
251
+ bounce?: {
252
+ type: 'Permanent' | 'Transient' | (string & {}) | null;
253
+ sub_type: string | null;
254
+ message: string | null;
255
+ };
256
+ complaint?: {
257
+ type: string | null;
258
+ };
259
+ open?: {
260
+ ip_address: string | null;
261
+ user_agent: string | null;
262
+ };
263
+ click?: {
264
+ link: string | null;
265
+ ip_address: string | null;
266
+ user_agent: string | null;
267
+ };
268
+ failed?: {
269
+ reason: string;
270
+ };
271
+ }
272
+ /** The event `verifyWebhook` returns. Check `event.type` and `event.data` has the matching fields. */
273
+ export type MailumiWebhookEvent = {
274
+ id: string;
275
+ type: 'email.received';
276
+ created_at: string;
277
+ data: EmailReceivedData;
278
+ } | {
279
+ id: string;
280
+ type: Exclude<WebhookEventType, 'email.received' | 'webhook.test'>;
281
+ created_at: string;
282
+ data: EmailEventData;
283
+ } | {
284
+ id: string;
285
+ type: 'webhook.test';
286
+ created_at: string;
287
+ data: {
288
+ message: string;
289
+ };
290
+ };
192
291
  export interface VerifyWebhookOptions {
193
292
  /** The raw request body exactly as received, before any JSON parsing. */
194
293
  payload: string | Uint8Array;
@@ -203,7 +302,8 @@ export interface VerifyWebhookOptions {
203
302
  * Checks the Standard Webhooks signature of a Mailumi webhook and returns the parsed event.
204
303
  * Store `event.id` and skip ids you have seen: a delivery can arrive more than once.
205
304
  */
206
- export declare function verifyWebhook<T = Record<string, any>>({ payload, headers, secret, tolerance }: VerifyWebhookOptions): Promise<Result<WebhookEvent<T>>>;
305
+ export declare function verifyWebhook(options: VerifyWebhookOptions): Promise<Result<MailumiWebhookEvent>>;
306
+ export declare function verifyWebhook<T>(options: VerifyWebhookOptions): Promise<Result<WebhookEvent<T>>>;
207
307
  /** Converts the SDK's camelCase options to the API's request body. snake_case keys are accepted too. */
208
308
  export declare function toApiEmail(o: SendEmailOptions & Record<string, any>): Record<string, unknown>;
209
309
  export declare class Mailumi {
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  * The official Mailumi SDK. Works anywhere `fetch` exists: Node.js 18+, Bun, Deno and Cloudflare Workers.
3
3
  * Every method resolves to `{ data, error }` and never throws for API errors.
4
4
  */
5
- export const VERSION = '1.0.0';
5
+ export const VERSION = '1.1.0';
6
6
  const DEFAULT_BASE_URL = 'https://mailumi.com/api/v1';
7
7
  function header(h, name, short) {
8
8
  if (typeof h.get === 'function')
@@ -24,10 +24,6 @@ function sameText(a, b) {
24
24
  diff |= (a.charCodeAt(i) || 0) ^ (b.charCodeAt(i) || 0);
25
25
  return diff === 0;
26
26
  }
27
- /**
28
- * Checks the Standard Webhooks signature of a Mailumi webhook and returns the parsed event.
29
- * Store `event.id` and skip ids you have seen: a delivery can arrive more than once.
30
- */
31
27
  export async function verifyWebhook({ payload, headers, secret, tolerance = 300 }) {
32
28
  const key = secret ?? globalThis.process?.env?.MAILUMI_WEBHOOK_SECRET;
33
29
  if (!key)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mailumi",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Official Mailumi SDK and CLI. Send transactional email and campaigns from your own domain, receive email by webhook, with your data stored in the EU.",
5
5
  "keywords": [
6
6
  "email",