shipmail 0.1.24 → 0.1.26

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
@@ -21,6 +21,8 @@ Official TypeScript SDK for the [Shipmail](https://shipmail.to) API. Zero runtim
21
21
  - [Threads](#threads)
22
22
  - [Webhooks](#webhooks)
23
23
  - [Suppressions](#suppressions)
24
+ - [Audiences](#audiences)
25
+ - [Newsletters](#newsletters)
24
26
  - [Status](#status)
25
27
  - [Pagination](#pagination)
26
28
  - [Webhook verification](#webhook-verification)
@@ -232,6 +234,72 @@ for await (const item of shipmail.suppressions.listAutoPaginating({ limit: 100 }
232
234
  }
233
235
  ```
234
236
 
237
+ ## Audiences
238
+
239
+ ```ts
240
+ const audience = await shipmail.audiences.create({
241
+ name: "Newsletter",
242
+ consent_source: "Website signup form",
243
+ });
244
+
245
+ await shipmail.audiences.subscribers.add(audience.id, {
246
+ email_address: "jane@example.com",
247
+ merge_fields: { plan: "pro" },
248
+ });
249
+ ```
250
+
251
+ ## Newsletters
252
+
253
+ ```ts
254
+ import { readFile } from "node:fs/promises";
255
+
256
+ const newsletterDomains = await shipmail.newsletters.domains.list({ limit: 25 });
257
+ const hero = await shipmail.newsletters.assets.upload({
258
+ filename: "hero.png",
259
+ content_type: "image/png",
260
+ data: await readFile("hero.png"),
261
+ });
262
+
263
+ const newsletter = await shipmail.newsletters.create({
264
+ audience_id: "aud_...",
265
+ newsletter_domain_id: newsletterDomains.data[0].id,
266
+ name: "July changelog",
267
+ subject: "What shipped in July",
268
+ preview_text: "A quick product update",
269
+ blocks: [
270
+ { type: "heading", level: 1, text: "July updates" },
271
+ { type: "callout", variant: "info", title: "Quick note", body: "A short intro." },
272
+ { type: "paragraph", body: "A quick product update." },
273
+ { type: "image", url: hero.url, alt: "Product screenshot" },
274
+ {
275
+ type: "columns",
276
+ ratio: "50-50",
277
+ left: { title: "For teams", body: "Shared inbox improvements." },
278
+ right: { title: "For agents", body: "API and MCP improvements." },
279
+ },
280
+ ],
281
+ });
282
+
283
+ await shipmail.newsletters.preview(newsletter.id);
284
+
285
+ await shipmail.newsletters.sendTest(newsletter.id, {
286
+ recipient_email: "owner@example.com",
287
+ });
288
+
289
+ await shipmail.newsletters.preflight(newsletter.id);
290
+
291
+ await shipmail.newsletters.schedule(newsletter.id, {
292
+ scheduled_at: "2026-08-01T09:00:00.000Z",
293
+ });
294
+ ```
295
+
296
+ Newsletter test sends and schedules must pass preflight. Guardrail failures throw
297
+ `ValidationError` with the failed preflight items in `error.details`.
298
+ Preflight responses include `url_breakdown` so you can see which links, image
299
+ URLs, and video thumbnails count against the 20 unique URL limit.
300
+ Block prose fields are plain text in API requests. Use newlines for paragraph
301
+ breaks, and use `body_html` or `custom_html` only when you need raw HTML.
302
+
235
303
  ## Status
236
304
 
237
305
  ```ts
@@ -263,7 +331,7 @@ for await (const domain of shipmail.domains.listAutoPaginating({ limit: 25 })) {
263
331
  }
264
332
  ```
265
333
 
266
- `listAutoPaginating` is available on `domains`, `mailboxes`, `messages`, `threads`, `webhooks`, `webhooks.listDeliveriesAutoPaginating`, and `suppressions`.
334
+ `listAutoPaginating` is available on `domains`, `mailboxes`, `messages`, `threads`, `webhooks`, `webhooks.listDeliveriesAutoPaginating`, `suppressions`, `newsletters.domains`, and `newsletters.assets`.
267
335
 
268
336
  ## Webhook verification
269
337
 
package/dist/index.cjs CHANGED
@@ -117,9 +117,9 @@ function mapErrorFromResponse(status, body, requestId) {
117
117
 
118
118
  // src/pagination.ts
119
119
  var Page = class {
120
- constructor(client, path, query) {
120
+ constructor(client, path2, query) {
121
121
  this.client = client;
122
- this.path = path;
122
+ this.path = path2;
123
123
  this.query = query;
124
124
  }
125
125
  async *[Symbol.asyncIterator]() {
@@ -619,6 +619,149 @@ var Messages = class extends ApiResource {
619
619
  }
620
620
  };
621
621
 
622
+ // src/resources/newsletters.ts
623
+ function path(id) {
624
+ return `/newsletters/${encodeURIComponent(id)}`;
625
+ }
626
+ var NewsletterDomains = class extends ApiResource {
627
+ list(params, options) {
628
+ return this.client.request({
629
+ method: "GET",
630
+ path: "/newsletter-domains",
631
+ query: { cursor: params?.cursor, limit: params?.limit },
632
+ methodOptions: options
633
+ });
634
+ }
635
+ listAutoPaginating(params) {
636
+ return new Page(this.client, "/newsletter-domains", { limit: params?.limit });
637
+ }
638
+ };
639
+ var NewsletterAssets = class extends ApiResource {
640
+ list(params, options) {
641
+ return this.client.request({
642
+ method: "GET",
643
+ path: "/newsletter-assets",
644
+ query: {
645
+ cursor: params?.cursor,
646
+ limit: params?.limit,
647
+ kind: params?.kind,
648
+ q: params?.q
649
+ },
650
+ methodOptions: options
651
+ });
652
+ }
653
+ listAutoPaginating(params) {
654
+ return new Page(this.client, "/newsletter-assets", {
655
+ limit: params?.limit,
656
+ kind: params?.kind,
657
+ q: params?.q
658
+ });
659
+ }
660
+ upload(params, options) {
661
+ return this.client.request({
662
+ method: "POST",
663
+ path: "/newsletter-assets",
664
+ query: { filename: params.filename },
665
+ rawBody: params.data,
666
+ contentType: params.content_type,
667
+ methodOptions: options
668
+ });
669
+ }
670
+ };
671
+ var Newsletters = class extends ApiResource {
672
+ constructor(client) {
673
+ super(client);
674
+ this.domains = new NewsletterDomains(client);
675
+ this.assets = new NewsletterAssets(client);
676
+ }
677
+ create(params, options) {
678
+ return this.client.request({
679
+ method: "POST",
680
+ path: "/newsletters",
681
+ body: params,
682
+ methodOptions: options
683
+ });
684
+ }
685
+ createFromChangelog(params, options) {
686
+ return this.client.request({
687
+ method: "POST",
688
+ path: "/newsletters/from-changelog",
689
+ body: params,
690
+ methodOptions: options
691
+ });
692
+ }
693
+ list(params, options) {
694
+ return this.client.request({
695
+ method: "GET",
696
+ path: "/newsletters",
697
+ query: { cursor: params?.cursor, limit: params?.limit },
698
+ methodOptions: options
699
+ });
700
+ }
701
+ listAutoPaginating(params) {
702
+ return new Page(this.client, "/newsletters", { limit: params?.limit });
703
+ }
704
+ get(id, options) {
705
+ return this.client.request({
706
+ method: "GET",
707
+ path: path(id),
708
+ methodOptions: options
709
+ });
710
+ }
711
+ update(id, params, options) {
712
+ return this.client.request({
713
+ method: "PATCH",
714
+ path: path(id),
715
+ body: params,
716
+ methodOptions: options
717
+ });
718
+ }
719
+ preflight(id, options) {
720
+ return this.client.request({
721
+ method: "POST",
722
+ path: `${path(id)}/preflight`,
723
+ methodOptions: options
724
+ });
725
+ }
726
+ preview(id, options) {
727
+ return this.client.request({
728
+ method: "POST",
729
+ path: `${path(id)}/preview`,
730
+ methodOptions: options
731
+ });
732
+ }
733
+ sendTest(id, params, options) {
734
+ return this.client.request({
735
+ method: "POST",
736
+ path: `${path(id)}/test-send`,
737
+ body: params,
738
+ methodOptions: options
739
+ });
740
+ }
741
+ schedule(id, params, options) {
742
+ return this.client.request({
743
+ method: "POST",
744
+ path: `${path(id)}/schedule`,
745
+ body: params,
746
+ methodOptions: options
747
+ });
748
+ }
749
+ cancel(id, options) {
750
+ return this.client.request({
751
+ method: "POST",
752
+ path: `${path(id)}/cancel`,
753
+ methodOptions: options
754
+ });
755
+ }
756
+ resume(id, options) {
757
+ return this.client.request({
758
+ method: "POST",
759
+ path: `${path(id)}/resume`,
760
+ methodOptions: options
761
+ });
762
+ }
763
+ };
764
+
622
765
  // src/resources/status.ts
623
766
  var Status = class extends ApiResource {
624
767
  get(options) {
@@ -787,7 +930,7 @@ var Webhooks = class extends ApiResource {
787
930
  };
788
931
 
789
932
  // src/version.ts
790
- var VERSION = "0.1.24";
933
+ var VERSION = "0.1.26";
791
934
 
792
935
  // src/client.ts
793
936
  var DEFAULT_BASE_URL = "https://shipmail.to/api/v1";
@@ -811,6 +954,12 @@ function isApiErrorBody(value) {
811
954
  const error = obj["error"];
812
955
  return typeof error["type"] === "string" && typeof error["message"] === "string";
813
956
  }
957
+ function rawBodyToBlobPart(rawBody) {
958
+ if (rawBody instanceof ArrayBuffer) return rawBody;
959
+ const buffer = new ArrayBuffer(rawBody.byteLength);
960
+ new Uint8Array(buffer).set(rawBody);
961
+ return buffer;
962
+ }
814
963
  var ShipMailClient = class {
815
964
  /**
816
965
  * Create a new ShipMail client.
@@ -836,6 +985,7 @@ var ShipMailClient = class {
836
985
  this.domains = new Domains(this);
837
986
  this.mailboxes = new Mailboxes(this);
838
987
  this.messages = new Messages(this);
988
+ this.newsletters = new Newsletters(this);
839
989
  this.suppressions = new Suppressions(this);
840
990
  this.threads = new Threads(this);
841
991
  this.webhooks = new Webhooks(this);
@@ -879,7 +1029,7 @@ var ShipMailClient = class {
879
1029
  let body = null;
880
1030
  if (options.rawBody !== void 0) {
881
1031
  headers["Content-Type"] = options.contentType ?? "application/octet-stream";
882
- body = new Blob([options.rawBody]);
1032
+ body = new Blob([rawBodyToBlobPart(options.rawBody)]);
883
1033
  } else if (options.body !== void 0) {
884
1034
  headers["Content-Type"] = "application/json";
885
1035
  body = JSON.stringify(options.body);
@@ -919,8 +1069,8 @@ var ShipMailClient = class {
919
1069
  }
920
1070
  throw lastError ?? new ShipMailError("Request failed after retries");
921
1071
  }
922
- buildUrl(path, query) {
923
- const url = new URL(`${this.baseUrl}${path}`);
1072
+ buildUrl(path2, query) {
1073
+ const url = new URL(`${this.baseUrl}${path2}`);
924
1074
  if (query) {
925
1075
  for (const [key, value] of Object.entries(query)) {
926
1076
  if (value !== void 0) {