shipmail 0.2.1 → 0.2.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 CHANGED
@@ -20,6 +20,7 @@ Official TypeScript SDK for the [Shipmail](https://shipmail.to) API. Zero runtim
20
20
  - [Messages](#messages)
21
21
  - [Sandbox](#sandbox)
22
22
  - [Threads](#threads)
23
+ - [Reply scans](#reply-scans)
23
24
  - [Webhooks](#webhooks)
24
25
  - [Suppressions](#suppressions)
25
26
  - [Audiences](#audiences)
@@ -170,6 +171,26 @@ await shipmail.mailboxes.updateAutoReply("mbx_...", {
170
171
  from_date: "2026-06-01",
171
172
  to_date: "2026-06-07",
172
173
  });
174
+
175
+ const mailboxId = "550e8400-e29b-41d4-a716-446655440000";
176
+ const inbox = await shipmail.mailboxes.listInboxMessages(mailboxId, {
177
+ after: "2025-07-20T00:00:00.000Z",
178
+ before: "2026-07-20T00:00:00.000Z",
179
+ limit: 50,
180
+ });
181
+ const exact = await shipmail.mailboxes.getInboxMessage(mailboxId, inbox.data[0].id);
182
+
183
+ const queue = await shipmail.mailboxes.listInboxThreads(mailboxId, {
184
+ reply_state: "needs_reply",
185
+ after: "2025-07-20T00:00:00.000Z",
186
+ });
187
+ const candidate = queue.data[0];
188
+ const draft = await shipmail.mailboxes.createInboxReplyDraft(mailboxId, candidate.thread_id, {
189
+ text: "Thanks for the note.",
190
+ expected_reply_version: candidate.reply_version,
191
+ });
192
+ // Apply your approval policy before sending. Stale versions fail with 409 without delivery.
193
+ await shipmail.mailboxes.sendInboxReplyDraft(mailboxId, candidate.thread_id, draft.id);
173
194
  ```
174
195
 
175
196
  ## Messages
@@ -220,13 +241,30 @@ await testClient.mailboxes.injectSandboxInbound("mbx_...", {
220
241
  ```ts
221
242
  const threads = await shipmail.threads.list({ mailbox_id: "mbx_..." });
222
243
  const threadId = threads.data[0].id;
223
- const thread = await shipmail.threads.get(threadId);
244
+ const thread = await shipmail.threads.get(threadId, { mailbox_id: "mbx_..." });
224
245
 
225
246
  await shipmail.threads.reply(threadId, {
247
+ mailbox_id: "mbx_...",
248
+ to: ["customer@example.com"],
226
249
  text: "Thanks for your email.",
227
250
  });
228
251
  ```
229
252
 
253
+ ## Reply scans
254
+
255
+ Use a durable, atomically captured scan for a historical window. Creation returns a completed
256
+ snapshot; retry a `409` with bounded backoff while historical classification finishes, then follow
257
+ every opaque `next_cursor`; never parse or fabricate cursors. Scans are retained for 30 days.
258
+
259
+ ```ts
260
+ const scan = await shipmail.replyScans.create({
261
+ mailbox_ids: ["550e8400-e29b-41d4-a716-446655440000"],
262
+ after: new Date(Date.now() - 365 * 86_400_000).toISOString(),
263
+ });
264
+ const results = await shipmail.replyScans.listResults(scan.id, { limit: 100 });
265
+ console.log(results.data);
266
+ ```
267
+
230
268
  ## Webhooks
231
269
 
232
270
  ```ts
@@ -259,6 +297,7 @@ message.sent
259
297
  message.delivered
260
298
  message.bounced
261
299
  message.complained
300
+ thread.needs_reply
262
301
  domain.verified
263
302
  domain.verification_failed
264
303
  domain.degraded
@@ -356,6 +395,8 @@ Preflight responses include `url_breakdown` so you can see which links, image
356
395
  URLs, and video thumbnails contribute to deliverability checks.
357
396
  Block prose fields are plain text in API requests. Use newlines for paragraph
358
397
  breaks, and use `body_html` or `custom_html` only when you need raw HTML.
398
+ Concurrent newsletter updates can throw `ConflictError` (409). Fetch the latest
399
+ newsletter, merge your changes, and retry the update.
359
400
 
360
401
  ## Partner beta
361
402
 
@@ -424,6 +465,10 @@ if (page.pagination.has_more) {
424
465
  }
425
466
  ```
426
467
 
468
+ Cursors are opaque and operation-specific. Return them unchanged to the same operation. Inbox and
469
+ reply-queue cursors are bound to their mailbox, time window, sort, and filters; omit those filters on
470
+ later pages or repeat them exactly.
471
+
427
472
  Auto-paginate over all pages:
428
473
 
429
474
  ```ts
package/dist/index.cjs CHANGED
@@ -624,7 +624,9 @@ var Mailboxes = class extends ApiResource {
624
624
  folder_id: params?.folder_id,
625
625
  folder_role: params?.folder_role,
626
626
  search_text: params?.search_text,
627
- position: params?.position,
627
+ cursor: params?.cursor,
628
+ after: params?.after,
629
+ before: params?.before,
628
630
  limit: params?.limit,
629
631
  has_keyword: params?.has_keyword,
630
632
  not_keyword: params?.not_keyword
@@ -632,6 +634,29 @@ var Mailboxes = class extends ApiResource {
632
634
  methodOptions: options
633
635
  });
634
636
  }
637
+ getInboxMessage(id, messageId, options) {
638
+ return this.client.request({
639
+ method: "GET",
640
+ path: `/mailboxes/${encodeURIComponent(id)}/inbox/messages/${encodeURIComponent(messageId)}`,
641
+ methodOptions: options
642
+ });
643
+ }
644
+ listInboxThreads(id, params, options) {
645
+ return this.client.request({
646
+ method: "GET",
647
+ path: `/mailboxes/${encodeURIComponent(id)}/inbox/threads`,
648
+ query: {
649
+ reply_state: params?.reply_state,
650
+ sort_by: params?.sort_by,
651
+ order: params?.order,
652
+ after: params?.after,
653
+ before: params?.before,
654
+ cursor: params?.cursor,
655
+ limit: params?.limit
656
+ },
657
+ methodOptions: options
658
+ });
659
+ }
635
660
  getInboxThread(id, threadId, options) {
636
661
  return this.client.request({
637
662
  method: "GET",
@@ -639,6 +664,45 @@ var Mailboxes = class extends ApiResource {
639
664
  methodOptions: options
640
665
  });
641
666
  }
667
+ updateInboxThreadReplyState(id, threadId, params, options) {
668
+ return this.client.request({
669
+ method: "PATCH",
670
+ path: `/mailboxes/${encodeURIComponent(id)}/inbox/threads/${encodeURIComponent(threadId)}`,
671
+ body: params,
672
+ methodOptions: options
673
+ });
674
+ }
675
+ createInboxReplyDraft(id, threadId, params, options) {
676
+ return this.client.request({
677
+ method: "POST",
678
+ path: `/mailboxes/${encodeURIComponent(id)}/inbox/threads/${encodeURIComponent(threadId)}/drafts`,
679
+ body: params,
680
+ methodOptions: options
681
+ });
682
+ }
683
+ sendInboxReplyDraft(id, threadId, draftId, options) {
684
+ return this.client.request({
685
+ method: "POST",
686
+ path: `/mailboxes/${encodeURIComponent(id)}/inbox/threads/${encodeURIComponent(threadId)}/drafts/${encodeURIComponent(draftId)}/send`,
687
+ methodOptions: options
688
+ });
689
+ }
690
+ replyToInboxMessage(id, messageId, params, options) {
691
+ return this.client.request({
692
+ method: "POST",
693
+ path: `/mailboxes/${encodeURIComponent(id)}/inbox/messages/${encodeURIComponent(messageId)}/reply`,
694
+ body: params,
695
+ methodOptions: options
696
+ });
697
+ }
698
+ replyToInboxThread(id, threadId, params, options) {
699
+ return this.client.request({
700
+ method: "POST",
701
+ path: `/mailboxes/${encodeURIComponent(id)}/inbox/threads/${encodeURIComponent(threadId)}/reply`,
702
+ body: params,
703
+ methodOptions: options
704
+ });
705
+ }
642
706
  updateInboxMessage(id, messageId, params, options) {
643
707
  return this.client.request({
644
708
  method: "PATCH",
@@ -1081,6 +1145,33 @@ var Partner = class extends ApiResource {
1081
1145
  }
1082
1146
  };
1083
1147
 
1148
+ // src/resources/reply-scans.ts
1149
+ var ReplyScans = class extends ApiResource {
1150
+ create(params, options) {
1151
+ return this.client.request({
1152
+ method: "POST",
1153
+ path: "/reply-scans",
1154
+ body: params,
1155
+ methodOptions: options
1156
+ });
1157
+ }
1158
+ get(id, options) {
1159
+ return this.client.request({
1160
+ method: "GET",
1161
+ path: `/reply-scans/${encodeURIComponent(id)}`,
1162
+ methodOptions: options
1163
+ });
1164
+ }
1165
+ listResults(id, params, options) {
1166
+ return this.client.request({
1167
+ method: "GET",
1168
+ path: `/reply-scans/${encodeURIComponent(id)}/results`,
1169
+ query: { cursor: params?.cursor, limit: params?.limit },
1170
+ methodOptions: options
1171
+ });
1172
+ }
1173
+ };
1174
+
1084
1175
  // src/resources/status.ts
1085
1176
  var Status = class extends ApiResource {
1086
1177
  get(options) {
@@ -1144,8 +1235,9 @@ var Threads = class extends ApiResource {
1144
1235
  method: "GET",
1145
1236
  path: `/threads/${encodeURIComponent(id)}`,
1146
1237
  query: {
1147
- cursor: params?.cursor,
1148
- limit: params?.limit
1238
+ mailbox_id: params.mailbox_id,
1239
+ cursor: params.cursor,
1240
+ limit: params.limit
1149
1241
  },
1150
1242
  methodOptions: options
1151
1243
  });
@@ -1263,7 +1355,7 @@ var Webhooks = class extends ApiResource {
1263
1355
  };
1264
1356
 
1265
1357
  // src/version.ts
1266
- var VERSION = "0.2.1";
1358
+ var VERSION = "0.2.3";
1267
1359
 
1268
1360
  // src/client.ts
1269
1361
  var DEFAULT_BASE_URL = "https://shipmail.to/api/v1";
@@ -1324,6 +1416,7 @@ var ShipMailClient = class {
1324
1416
  this.messages = new Messages(this);
1325
1417
  this.newsletters = new Newsletters(this);
1326
1418
  this.partner = new Partner(this);
1419
+ this.replyScans = new ReplyScans(this);
1327
1420
  this.suppressions = new Suppressions(this);
1328
1421
  this.threads = new Threads(this);
1329
1422
  this.webhooks = new Webhooks(this);
@@ -1434,6 +1527,7 @@ var WEBHOOK_EVENT_TYPES = [
1434
1527
  "message.delivered",
1435
1528
  "message.bounced",
1436
1529
  "message.complained",
1530
+ "thread.needs_reply",
1437
1531
  "mailbox.rule_matched",
1438
1532
  "domain.verified",
1439
1533
  "domain.verification_failed",
@@ -1456,7 +1550,14 @@ var MESSAGE_STATUSES = [
1456
1550
  "suppressed",
1457
1551
  "cancelled"
1458
1552
  ];
1459
- var MESSAGE_SOURCES = ["api", "newsletter", "dashboard", "inbound", "smtp"];
1553
+ var MESSAGE_SOURCES = [
1554
+ "api",
1555
+ "newsletter",
1556
+ "dashboard",
1557
+ "inbound",
1558
+ "smtp",
1559
+ "agent"
1560
+ ];
1460
1561
  var WEBHOOK_DELIVERY_STATUSES = ["pending", "delivered", "failed"];
1461
1562
 
1462
1563
  // src/webhook-verification.ts