@easybits.cloud/mailmask 0.9.0 → 0.9.1

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
@@ -156,6 +156,15 @@ const job = await mm.batch("formmy-user-42", {
156
156
  const st = await mm.batchStatus("formmy-user-42", job.batchId);
157
157
  // st.sent / failed / skippedSuppressed, then (for up to 7 days) delivered / bounced / complained / unsubscribed
158
158
 
159
+ // Opens and clicks (opt-in per send/batch): `track: true` adds SES's open pixel and rewrites the html
160
+ // links. Without it nothing is added. Unique counts + rates + top links come back in batchStatus, and
161
+ // `email.opened` / `email.clicked` fire once per message (the first time). Opens are inflated by Apple
162
+ // Mail Privacy Protection: decide by clicks.
163
+ const tracked = await mm.batch("formmy-user-42", { from: "hola@laespiga.mx", subject: "Oct", html: "<a href=\"https://laespiga.mx/menu?utm_source=mail\">Menú</a>", recipients: ["ana@example.com"], track: true });
164
+ const ts = await mm.batchStatus("formmy-user-42", tracked.batchId);
165
+ // ts.opened / clicked (unique), ts.openRate / clickRate (0–1 over delivered, or sent while delivered is 0),
166
+ // ts.topLinks: [{ url: "https://laespiga.mx/menu", clicks, uniqueClicks }] (utm_*, fbclid, gclid stripped)
167
+
159
168
  // Test mode: validates everything (signature, domain, suppression, quota) without sending or billing
160
169
  await mm.send("formmy-user-42", { from: "hola@laespiga.mx", to: "x@example.com", subject: "s", text: "t" }, { test: true });
161
170
 
@@ -208,6 +217,9 @@ export async function action({ request }: { request: Request }) {
208
217
  case "tenant.paused":
209
218
  // notify the customer
210
219
  break;
220
+ case "email.clicked":
221
+ // event.data.url, event.data.recipient, event.data.batchId, event.data.tags (first click only)
222
+ break;
211
223
  }
212
224
  return new Response("ok");
213
225
  }
package/dist/index.cjs CHANGED
@@ -871,6 +871,8 @@ var PARTNER_WEBHOOK_EVENTS = [
871
871
  "email.delivered",
872
872
  "email.bounced",
873
873
  "email.complained",
874
+ "email.opened",
875
+ "email.clicked",
874
876
  "email.received",
875
877
  "contact.unsubscribed",
876
878
  "tenant.paused",
@@ -1306,7 +1308,10 @@ var MailMaskPartner = class {
1306
1308
  );
1307
1309
  return { ...data, idempotencyKey, replayed: meta.idempotentReplayed };
1308
1310
  }
1309
- /** Estado de un lote (`batchId` del resultado de `batch`): envíos y, después, entregas, rebotes, quejas y bajas. */
1311
+ /**
1312
+ * Estado de un lote (`batchId` del resultado de `batch`): envíos y, después, entregas, rebotes, quejas
1313
+ * y bajas; con `track: true`, también aperturas, clics, tasas y `topLinks`.
1314
+ */
1310
1315
  async batchStatus(externalId, batchId) {
1311
1316
  return (await this.request("GET", routePath(PARTNER_ROUTES.getBatch.path, { externalId, batchId }))).data;
1312
1317
  }
package/dist/index.js CHANGED
@@ -774,6 +774,8 @@ var PARTNER_WEBHOOK_EVENTS = [
774
774
  "email.delivered",
775
775
  "email.bounced",
776
776
  "email.complained",
777
+ "email.opened",
778
+ "email.clicked",
777
779
  "email.received",
778
780
  "contact.unsubscribed",
779
781
  "tenant.paused",
@@ -1209,7 +1211,10 @@ var MailMaskPartner = class {
1209
1211
  );
1210
1212
  return { ...data, idempotencyKey, replayed: meta.idempotentReplayed };
1211
1213
  }
1212
- /** Estado de un lote (`batchId` del resultado de `batch`): envíos y, después, entregas, rebotes, quejas y bajas. */
1214
+ /**
1215
+ * Estado de un lote (`batchId` del resultado de `batch`): envíos y, después, entregas, rebotes, quejas
1216
+ * y bajas; con `track: true`, también aperturas, clics, tasas y `topLinks`.
1217
+ */
1213
1218
  async batchStatus(externalId, batchId) {
1214
1219
  return (await this.request("GET", routePath(PARTNER_ROUTES.getBatch.path, { externalId, batchId }))).data;
1215
1220
  }
@@ -336,6 +336,8 @@ export interface SendRequest {
336
336
  vars?: TemplateVars;
337
337
  /** Reintentar con la misma llave en 24 h devuelve la respuesta guardada sin volver a enviar. */
338
338
  idempotencyKey?: string;
339
+ /** Mide aperturas y clics (ver `track` en `BatchRequest`). Default `false`. */
340
+ track?: boolean;
339
341
  }
340
342
  /** `POST /tenants/:externalId/send` — 200. Con `Idempotent-Replayed: true` si fue repetición. */
341
343
  export interface SendResponse {
@@ -368,6 +370,21 @@ export interface BatchRequest {
368
370
  /** Default `broadcast` (lleva baja de un clic por destinatario). */
369
371
  stream?: MessageStream;
370
372
  idempotencyKey?: string;
373
+ /**
374
+ * Mide aperturas y clics: SES agrega un pixel invisible y reescribe cada liga del `html` para
375
+ * pasar por su redirector (el `text` no cambia). Llegan `email.opened`/`email.clicked` (la primera
376
+ * vez por correo) y suben `opened`, `clicked` y `topLinks` del `BatchStatus`. Default `false`: sin
377
+ * él el correo sale sin pixel y con las ligas intactas.
378
+ */
379
+ track?: boolean;
380
+ }
381
+ /** Una liga del lote con sus clics. `url` va sin `utm_*`, `fbclid` ni `gclid` (agrupa variantes). */
382
+ export interface BatchTopLink {
383
+ url: string;
384
+ /** Clics totales (una persona que hace clic dos veces cuenta dos). */
385
+ clicks: number;
386
+ /** Correos distintos que hicieron clic en esta liga. */
387
+ uniqueClicks: number;
371
388
  }
372
389
  export type BatchState = "queued" | "processing" | "completed" | "failed";
373
390
  export interface BatchStatus {
@@ -387,6 +404,21 @@ export interface BatchStatus {
387
404
  bounced: number;
388
405
  complained: number;
389
406
  unsubscribed: number;
407
+ /** El lote se mandó con `track: true`. Sin él, los números de seguimiento se quedan en 0. */
408
+ track: boolean;
409
+ /**
410
+ * Correos del lote abiertos al menos una vez (únicos). Inflado: Apple Mail (Mail Privacy
411
+ * Protection) y algunos filtros abren el pixel solos. Para decidir, usa los clics.
412
+ */
413
+ opened: number;
414
+ /** Correos del lote con al menos un clic (únicos). Excluye la liga de baja. */
415
+ clicked: number;
416
+ /** `opened` / `delivered` (o / `sent` mientras `delivered` sea 0), de 0 a 1 con 4 decimales. */
417
+ openRate: number;
418
+ /** `clicked` sobre la misma base que `openRate`. */
419
+ clickRate: number;
420
+ /** Hasta 10 ligas con más clics, de más a menos. */
421
+ topLinks: BatchTopLink[];
390
422
  lastError: string | null;
391
423
  createdAt: string;
392
424
  completedAt: string | null;
@@ -559,6 +591,11 @@ export interface AutoTopup {
559
591
  */
560
592
  export interface SetAutoTopupRequest {
561
593
  amount: number | null;
594
+ /**
595
+ * Correo de la cuenta de MercadoPago con que se paga, si no es el del dueño en MailMask
596
+ * (MercadoPago rechaza el de su propia cuenta cobradora).
597
+ */
598
+ payerEmail?: string;
562
599
  returnUrl?: string;
563
600
  }
564
601
  /** Montos sugeridos para la recarga automática (unidades enteras). */
@@ -628,7 +665,7 @@ export interface RotateWebhookSecretResponse {
628
665
  export interface TestWebhookResponse {
629
666
  deliveryId: string;
630
667
  }
631
- export declare const PARTNER_WEBHOOK_EVENTS: readonly ["email.sent", "email.delivered", "email.bounced", "email.complained", "email.received", "contact.unsubscribed", "tenant.paused", "tenant.resumed", "domain.verified", "balance.low", "batch.throttled"];
668
+ export declare const PARTNER_WEBHOOK_EVENTS: readonly ["email.sent", "email.delivered", "email.bounced", "email.complained", "email.opened", "email.clicked", "email.received", "contact.unsubscribed", "tenant.paused", "tenant.resumed", "domain.verified", "balance.low", "batch.throttled"];
632
669
  export type PartnerWebhookEvent = typeof PARTNER_WEBHOOK_EVENTS[number];
633
670
  /** Headers de cada entrega al webhook del partner (los mismos que los webhooks de dominio). */
634
671
  export declare const WEBHOOK_HEADERS: {
@@ -675,6 +712,24 @@ export interface PartnerEventData {
675
712
  recipient: string;
676
713
  feedbackType: string | null;
677
714
  };
715
+ /**
716
+ * Primera apertura de un correo enviado con `track: true` (una sola vez por correo). Inflado por
717
+ * Apple Mail Privacy Protection: decide por `email.clicked`.
718
+ */
719
+ "email.opened": EmailEventBase & {
720
+ recipient: string;
721
+ batchId: string | null;
722
+ openedAt: string;
723
+ userAgent: string | null;
724
+ };
725
+ /** Primer clic de un correo enviado con `track: true` (una sola vez por correo). `url` es la liga original. */
726
+ "email.clicked": EmailEventBase & {
727
+ recipient: string;
728
+ batchId: string | null;
729
+ url: string;
730
+ clickedAt: string;
731
+ userAgent: string | null;
732
+ };
678
733
  "email.received": {
679
734
  domain: string;
680
735
  from: string;
package/dist/partner.d.ts CHANGED
@@ -237,7 +237,10 @@ export declare class MailMaskPartner {
237
237
  idempotencyKey: string;
238
238
  replayed: boolean;
239
239
  }>;
240
- /** Estado de un lote (`batchId` del resultado de `batch`): envíos y, después, entregas, rebotes, quejas y bajas. */
240
+ /**
241
+ * Estado de un lote (`batchId` del resultado de `batch`): envíos y, después, entregas, rebotes, quejas
242
+ * y bajas; con `track: true`, también aperturas, clics, tasas y `topLinks`.
243
+ */
241
244
  batchStatus(externalId: string, batchId: string): Promise<BatchStatus>;
242
245
  /** Saldo prepagado: saldo, precios, última recarga y consumo del mes (centavos MXN). */
243
246
  balance(): Promise<BalanceResponse>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@easybits.cloud/mailmask",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "description": "Official SDK for the MailMask email alias/forwarding API",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",