shipmail 0.2.2__tar.gz → 0.2.3__tar.gz

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.
Files changed (65) hide show
  1. {shipmail-0.2.2 → shipmail-0.2.3}/CHANGELOG.md +7 -0
  2. {shipmail-0.2.2 → shipmail-0.2.3}/PKG-INFO +35 -1
  3. {shipmail-0.2.2 → shipmail-0.2.3}/README.md +34 -0
  4. {shipmail-0.2.2 → shipmail-0.2.3}/shipmail/__init__.py +31 -1
  5. {shipmail-0.2.2 → shipmail-0.2.3}/shipmail/_client.py +5 -0
  6. {shipmail-0.2.2 → shipmail-0.2.3}/shipmail/_types/__init__.py +32 -0
  7. {shipmail-0.2.2 → shipmail-0.2.3}/shipmail/_types/common.py +5 -2
  8. {shipmail-0.2.2 → shipmail-0.2.3}/shipmail/_types/mailbox.py +102 -5
  9. shipmail-0.2.3/shipmail/_types/reply_scan.py +60 -0
  10. {shipmail-0.2.2 → shipmail-0.2.3}/shipmail/_types/webhook.py +1 -1
  11. shipmail-0.2.3/shipmail/_version.py +1 -0
  12. {shipmail-0.2.2 → shipmail-0.2.3}/shipmail/resources/__init__.py +3 -0
  13. {shipmail-0.2.2 → shipmail-0.2.3}/shipmail/resources/mailboxes.py +176 -8
  14. shipmail-0.2.3/shipmail/resources/reply_scans.py +82 -0
  15. {shipmail-0.2.2 → shipmail-0.2.3}/tests/resources/test_mailboxes.py +123 -3
  16. shipmail-0.2.3/tests/resources/test_reply_scans.py +83 -0
  17. shipmail-0.2.2/shipmail/_version.py +0 -1
  18. {shipmail-0.2.2 → shipmail-0.2.3}/.gitignore +0 -0
  19. {shipmail-0.2.2 → shipmail-0.2.3}/LICENSE +0 -0
  20. {shipmail-0.2.2 → shipmail-0.2.3}/pyproject.toml +0 -0
  21. {shipmail-0.2.2 → shipmail-0.2.3}/shipmail/_base_client.py +0 -0
  22. {shipmail-0.2.2 → shipmail-0.2.3}/shipmail/_errors.py +0 -0
  23. {shipmail-0.2.2 → shipmail-0.2.3}/shipmail/_pagination.py +0 -0
  24. {shipmail-0.2.2 → shipmail-0.2.3}/shipmail/_types/audience.py +0 -0
  25. {shipmail-0.2.2 → shipmail-0.2.3}/shipmail/_types/booking_page.py +0 -0
  26. {shipmail-0.2.2 → shipmail-0.2.3}/shipmail/_types/calendar.py +0 -0
  27. {shipmail-0.2.2 → shipmail-0.2.3}/shipmail/_types/domain.py +0 -0
  28. {shipmail-0.2.2 → shipmail-0.2.3}/shipmail/_types/message.py +0 -0
  29. {shipmail-0.2.2 → shipmail-0.2.3}/shipmail/_types/newsletter.py +0 -0
  30. {shipmail-0.2.2 → shipmail-0.2.3}/shipmail/_types/partner.py +0 -0
  31. {shipmail-0.2.2 → shipmail-0.2.3}/shipmail/_types/suppression.py +0 -0
  32. {shipmail-0.2.2 → shipmail-0.2.3}/shipmail/_types/thread.py +0 -0
  33. {shipmail-0.2.2 → shipmail-0.2.3}/shipmail/_webhook.py +0 -0
  34. {shipmail-0.2.2 → shipmail-0.2.3}/shipmail/py.typed +0 -0
  35. {shipmail-0.2.2 → shipmail-0.2.3}/shipmail/resources/audiences.py +0 -0
  36. {shipmail-0.2.2 → shipmail-0.2.3}/shipmail/resources/booking_page.py +0 -0
  37. {shipmail-0.2.2 → shipmail-0.2.3}/shipmail/resources/calendar.py +0 -0
  38. {shipmail-0.2.2 → shipmail-0.2.3}/shipmail/resources/domains.py +0 -0
  39. {shipmail-0.2.2 → shipmail-0.2.3}/shipmail/resources/messages.py +0 -0
  40. {shipmail-0.2.2 → shipmail-0.2.3}/shipmail/resources/newsletters.py +0 -0
  41. {shipmail-0.2.2 → shipmail-0.2.3}/shipmail/resources/partner.py +0 -0
  42. {shipmail-0.2.2 → shipmail-0.2.3}/shipmail/resources/status.py +0 -0
  43. {shipmail-0.2.2 → shipmail-0.2.3}/shipmail/resources/suppressions.py +0 -0
  44. {shipmail-0.2.2 → shipmail-0.2.3}/shipmail/resources/threads.py +0 -0
  45. {shipmail-0.2.2 → shipmail-0.2.3}/shipmail/resources/webhooks.py +0 -0
  46. {shipmail-0.2.2 → shipmail-0.2.3}/tests/__init__.py +0 -0
  47. {shipmail-0.2.2 → shipmail-0.2.3}/tests/conftest.py +0 -0
  48. {shipmail-0.2.2 → shipmail-0.2.3}/tests/resources/__init__.py +0 -0
  49. {shipmail-0.2.2 → shipmail-0.2.3}/tests/resources/test_audiences.py +0 -0
  50. {shipmail-0.2.2 → shipmail-0.2.3}/tests/resources/test_booking_page.py +0 -0
  51. {shipmail-0.2.2 → shipmail-0.2.3}/tests/resources/test_calendar.py +0 -0
  52. {shipmail-0.2.2 → shipmail-0.2.3}/tests/resources/test_domains.py +0 -0
  53. {shipmail-0.2.2 → shipmail-0.2.3}/tests/resources/test_messages.py +0 -0
  54. {shipmail-0.2.2 → shipmail-0.2.3}/tests/resources/test_newsletters.py +0 -0
  55. {shipmail-0.2.2 → shipmail-0.2.3}/tests/resources/test_partner.py +0 -0
  56. {shipmail-0.2.2 → shipmail-0.2.3}/tests/resources/test_status.py +0 -0
  57. {shipmail-0.2.2 → shipmail-0.2.3}/tests/resources/test_suppressions.py +0 -0
  58. {shipmail-0.2.2 → shipmail-0.2.3}/tests/resources/test_threads.py +0 -0
  59. {shipmail-0.2.2 → shipmail-0.2.3}/tests/resources/test_webhooks.py +0 -0
  60. {shipmail-0.2.2 → shipmail-0.2.3}/tests/test_client.py +0 -0
  61. {shipmail-0.2.2 → shipmail-0.2.3}/tests/test_client_async.py +0 -0
  62. {shipmail-0.2.2 → shipmail-0.2.3}/tests/test_errors.py +0 -0
  63. {shipmail-0.2.2 → shipmail-0.2.3}/tests/test_pagination.py +0 -0
  64. {shipmail-0.2.2 → shipmail-0.2.3}/tests/test_pagination_async.py +0 -0
  65. {shipmail-0.2.2 → shipmail-0.2.3}/tests/test_webhook.py +0 -0
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.2.3](https://github.com/jcoulaud/ShipMail/compare/shipmail-python-v0.2.2...shipmail-python-v0.2.3) (2026-07-20)
4
+
5
+
6
+ ### Features
7
+
8
+ * **api:** add agent-safe reply queue workflows ([#890](https://github.com/jcoulaud/ShipMail/issues/890)) ([62dd211](https://github.com/jcoulaud/ShipMail/commit/62dd2118cd68b27dfb6481cb20cb35b98913ad97))
9
+
3
10
  ## [0.2.2](https://github.com/jcoulaud/ShipMail/compare/shipmail-python-v0.2.1...shipmail-python-v0.2.2) (2026-07-19)
4
11
 
5
12
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: shipmail
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: Official Python SDK for the ShipMail API
5
5
  Project-URL: Homepage, https://shipmail.to
6
6
  Project-URL: Documentation, https://shipmail.to/docs/sdks/python
@@ -135,6 +135,20 @@ rules = client.mailboxes.get_rules("mbx_...")
135
135
  rules = client.mailboxes.update_rules("mbx_...", {"rules": rules["rules"]})
136
136
  updated = client.mailboxes.update_spam_filter("mbx_...", {"threshold": 8})
137
137
  client.mailboxes.delete("mbx_...")
138
+
139
+ mailbox_id = "550e8400-e29b-41d4-a716-446655440000"
140
+ queue = client.mailboxes.list_inbox_threads(mailbox_id, {
141
+ "reply_state": "needs_reply",
142
+ "after": "2025-07-20T00:00:00.000Z",
143
+ })
144
+ candidate = queue["data"][0]
145
+ draft = client.mailboxes.create_inbox_reply_draft(
146
+ mailbox_id,
147
+ candidate["thread_id"],
148
+ {"text": "Thanks for the note.", "expected_reply_version": candidate["reply_version"]},
149
+ )
150
+ # Apply your approval policy first. Stale versions fail with 409 without delivery.
151
+ client.mailboxes.send_inbox_reply_draft(mailbox_id, candidate["thread_id"], draft["id"])
138
152
  ```
139
153
 
140
154
  ### Messages
@@ -184,6 +198,23 @@ reply = client.threads.reply(threads["data"][0]["id"], {
184
198
  })
185
199
  ```
186
200
 
201
+ ### Reply scans
202
+
203
+ Use a durable, atomically captured scan for a historical window, then page every result using the
204
+ opaque cursor unchanged. Creation returns the completed snapshot; retry a `409` with bounded
205
+ backoff while historical header classification finishes. Scans are retained for 30 days.
206
+
207
+ ```python
208
+ from datetime import datetime, timedelta, timezone
209
+
210
+ scan = client.reply_scans.create({
211
+ "mailbox_ids": ["550e8400-e29b-41d4-a716-446655440000"],
212
+ "after": (datetime.now(timezone.utc) - timedelta(days=365)).isoformat(),
213
+ })
214
+ results = client.reply_scans.list_results(scan["id"], {"limit": 100})
215
+ print(results["data"])
216
+ ```
217
+
187
218
  ### Audiences
188
219
 
189
220
  ```python
@@ -349,6 +380,9 @@ if page["pagination"]["has_more"]:
349
380
  })
350
381
  ```
351
382
 
383
+ Cursors are opaque and operation-specific. Never parse, modify, or fabricate them. Inbox and reply
384
+ queue cursors are bound to their mailbox, time window, sort, and filters.
385
+
352
386
  Auto-pagination iterates through all pages automatically:
353
387
 
354
388
  ```python
@@ -108,6 +108,20 @@ rules = client.mailboxes.get_rules("mbx_...")
108
108
  rules = client.mailboxes.update_rules("mbx_...", {"rules": rules["rules"]})
109
109
  updated = client.mailboxes.update_spam_filter("mbx_...", {"threshold": 8})
110
110
  client.mailboxes.delete("mbx_...")
111
+
112
+ mailbox_id = "550e8400-e29b-41d4-a716-446655440000"
113
+ queue = client.mailboxes.list_inbox_threads(mailbox_id, {
114
+ "reply_state": "needs_reply",
115
+ "after": "2025-07-20T00:00:00.000Z",
116
+ })
117
+ candidate = queue["data"][0]
118
+ draft = client.mailboxes.create_inbox_reply_draft(
119
+ mailbox_id,
120
+ candidate["thread_id"],
121
+ {"text": "Thanks for the note.", "expected_reply_version": candidate["reply_version"]},
122
+ )
123
+ # Apply your approval policy first. Stale versions fail with 409 without delivery.
124
+ client.mailboxes.send_inbox_reply_draft(mailbox_id, candidate["thread_id"], draft["id"])
111
125
  ```
112
126
 
113
127
  ### Messages
@@ -157,6 +171,23 @@ reply = client.threads.reply(threads["data"][0]["id"], {
157
171
  })
158
172
  ```
159
173
 
174
+ ### Reply scans
175
+
176
+ Use a durable, atomically captured scan for a historical window, then page every result using the
177
+ opaque cursor unchanged. Creation returns the completed snapshot; retry a `409` with bounded
178
+ backoff while historical header classification finishes. Scans are retained for 30 days.
179
+
180
+ ```python
181
+ from datetime import datetime, timedelta, timezone
182
+
183
+ scan = client.reply_scans.create({
184
+ "mailbox_ids": ["550e8400-e29b-41d4-a716-446655440000"],
185
+ "after": (datetime.now(timezone.utc) - timedelta(days=365)).isoformat(),
186
+ })
187
+ results = client.reply_scans.list_results(scan["id"], {"limit": 100})
188
+ print(results["data"])
189
+ ```
190
+
160
191
  ### Audiences
161
192
 
162
193
  ```python
@@ -322,6 +353,9 @@ if page["pagination"]["has_more"]:
322
353
  })
323
354
  ```
324
355
 
356
+ Cursors are opaque and operation-specific. Never parse, modify, or fabricate them. Inbox and reply
357
+ queue cursors are bound to their mailbox, time window, sort, and filters.
358
+
325
359
  Auto-pagination iterates through all pages automatically:
326
360
 
327
361
  ```python
@@ -45,6 +45,7 @@ from ._types import (
45
45
  CreateDomainParams,
46
46
  CreatedPartnerOrganization,
47
47
  CreateImportUploadParams,
48
+ CreateInboxReplyDraftParams,
48
49
  CreateMailboxAppPasswordParams,
49
50
  CreateMailboxFolderParams,
50
51
  CreateMailboxImportParams,
@@ -52,6 +53,7 @@ from ._types import (
52
53
  CreateNewsletterFromChangelogParams,
53
54
  CreateNewsletterParams,
54
55
  CreatePartnerOrganizationParams,
56
+ CreateReplyScanParams,
55
57
  CreateWebhookParams,
56
58
  DataMode,
57
59
  DeleteCalendarEventParams,
@@ -81,7 +83,13 @@ from ._types import (
81
83
  InboxMessage,
82
84
  InboxMessageAction,
83
85
  InboxMessages,
86
+ InboxReplyDraft,
87
+ InboxReplyDraftSend,
84
88
  InboxThread,
89
+ InboxThreadReplyState,
90
+ InboxThreadReplyStateResult,
91
+ InboxThreads,
92
+ InboxThreadSummary,
85
93
  InjectSandboxInboundParams,
86
94
  ListAudiencesParams,
87
95
  ListBookingPagesParams,
@@ -89,11 +97,13 @@ from ._types import (
89
97
  ListDeliveriesParams,
90
98
  ListDomainsParams,
91
99
  ListInboxMessagesParams,
100
+ ListInboxThreadsParams,
92
101
  ListMailboxesParams,
93
102
  ListMessagesParams,
94
103
  ListNewsletterAssetsParams,
95
104
  ListNewsletterDomainsParams,
96
105
  ListNewslettersParams,
106
+ ListReplyScanResultsParams,
97
107
  ListSubscribersParams,
98
108
  ListSuppressionsParams,
99
109
  ListThreadsParams,
@@ -139,7 +149,6 @@ from ._types import (
139
149
  MessageSource,
140
150
  MessageStatus,
141
151
  MethodOptions,
142
- OneTimeSecretMethodOptions,
143
152
  MoveInboxMessageParams,
144
153
  Newsletter,
145
154
  NewsletterArchiveVisibility,
@@ -186,6 +195,7 @@ from ._types import (
186
195
  NewsletterUrlBreakdown,
187
196
  NewsletterUrlSource,
188
197
  NewsletterVideoBlock,
198
+ OneTimeSecretMethodOptions,
189
199
  OutboundHeader,
190
200
  PaginatedResponse,
191
201
  PaginationParams,
@@ -205,6 +215,10 @@ from ._types import (
205
215
  RegisterNewsletterAssetFromUrlParams,
206
216
  RegistrationContact,
207
217
  Reminder,
218
+ ReplyScan,
219
+ ReplyScanCandidate,
220
+ ReplyScanResults,
221
+ ReplyScanStatus,
208
222
  ReplyToMessageParams,
209
223
  ReplyToThreadParams,
210
224
  ResetMailboxPasswordParams,
@@ -229,6 +243,7 @@ from ._types import (
229
243
  UpdateCalendarEventParams,
230
244
  UpdateDomainParams,
231
245
  UpdateInboxMessageParams,
246
+ UpdateInboxThreadReplyStateParams,
232
247
  UpdateMailboxFolderParams,
233
248
  UpdateMailboxParams,
234
249
  UpdateMailboxRulesParams,
@@ -321,6 +336,8 @@ __all__ = [
321
336
  "UpdateSubscriberParams",
322
337
  "CreateDomainParams",
323
338
  "CreatedMailboxAppPassword",
339
+ "CreateInboxReplyDraftParams",
340
+ "CreateReplyScanParams",
324
341
  "CreateMailboxAppPasswordParams",
325
342
  "CreateImportUploadParams",
326
343
  "CreateMailboxFolderParams",
@@ -357,10 +374,18 @@ __all__ = [
357
374
  "InboxMessage",
358
375
  "InboxMessageAction",
359
376
  "InboxMessages",
377
+ "InboxReplyDraft",
378
+ "InboxReplyDraftSend",
360
379
  "InboxThread",
380
+ "InboxThreadReplyState",
381
+ "InboxThreadReplyStateResult",
382
+ "InboxThreadSummary",
383
+ "InboxThreads",
361
384
  "ListDeliveriesParams",
362
385
  "ListDomainsParams",
363
386
  "ListInboxMessagesParams",
387
+ "ListInboxThreadsParams",
388
+ "ListReplyScanResultsParams",
364
389
  "ListMailboxesParams",
365
390
  "ListMessagesParams",
366
391
  "ListNewsletterAssetsParams",
@@ -472,6 +497,10 @@ __all__ = [
472
497
  "RegistrationContact",
473
498
  "ResetMailboxPasswordParams",
474
499
  "ReplyToMessageParams",
500
+ "ReplyScan",
501
+ "ReplyScanCandidate",
502
+ "ReplyScanResults",
503
+ "ReplyScanStatus",
475
504
  "ReplyToThreadParams",
476
505
  "RotateSecretResponse",
477
506
  "ScheduleNewsletterParams",
@@ -485,6 +514,7 @@ __all__ = [
485
514
  "Thread",
486
515
  "RegisterNewsletterAssetFromUrlParams",
487
516
  "UpdateInboxMessageParams",
517
+ "UpdateInboxThreadReplyStateParams",
488
518
  "UpdateDomainParams",
489
519
  "UpdateMailboxFolderParams",
490
520
  "UpdateMailboxRulesParams",
@@ -23,6 +23,7 @@ from .resources.mailboxes import AsyncMailboxes, SyncMailboxes
23
23
  from .resources.messages import AsyncMessages, SyncMessages
24
24
  from .resources.newsletters import AsyncNewsletters, SyncNewsletters
25
25
  from .resources.partner import AsyncPartner, SyncPartner
26
+ from .resources.reply_scans import AsyncReplyScans, SyncReplyScans
26
27
  from .resources.status import AsyncStatus, SyncStatus
27
28
  from .resources.suppressions import AsyncSuppressions, SyncSuppressions
28
29
  from .resources.threads import AsyncThreads, SyncThreads
@@ -40,6 +41,7 @@ class ShipMail(BaseClient):
40
41
  messages: SyncMessages
41
42
  newsletters: SyncNewsletters
42
43
  partner: SyncPartner
44
+ reply_scans: SyncReplyScans
43
45
  suppressions: SyncSuppressions
44
46
  threads: SyncThreads
45
47
  webhooks: SyncWebhooks
@@ -75,6 +77,7 @@ class ShipMail(BaseClient):
75
77
  self.messages = SyncMessages(self)
76
78
  self.newsletters = SyncNewsletters(self)
77
79
  self.partner = SyncPartner(self)
80
+ self.reply_scans = SyncReplyScans(self)
78
81
  self.suppressions = SyncSuppressions(self)
79
82
  self.threads = SyncThreads(self)
80
83
  self.webhooks = SyncWebhooks(self)
@@ -225,6 +228,7 @@ class AsyncShipMail(BaseClient):
225
228
  messages: AsyncMessages
226
229
  newsletters: AsyncNewsletters
227
230
  partner: AsyncPartner
231
+ reply_scans: AsyncReplyScans
228
232
  suppressions: AsyncSuppressions
229
233
  threads: AsyncThreads
230
234
  webhooks: AsyncWebhooks
@@ -260,6 +264,7 @@ class AsyncShipMail(BaseClient):
260
264
  self.messages = AsyncMessages(self)
261
265
  self.newsletters = AsyncNewsletters(self)
262
266
  self.partner = AsyncPartner(self)
267
+ self.reply_scans = AsyncReplyScans(self)
263
268
  self.suppressions = AsyncSuppressions(self)
264
269
  self.threads = AsyncThreads(self)
265
270
  self.webhooks = AsyncWebhooks(self)
@@ -95,6 +95,7 @@ from .mailbox import (
95
95
  AutoReply,
96
96
  CreatedMailboxAppPassword,
97
97
  CreateImportUploadParams,
98
+ CreateInboxReplyDraftParams,
98
99
  CreateMailboxAppPasswordParams,
99
100
  CreateMailboxFolderParams,
100
101
  CreateMailboxForwardingParams,
@@ -111,8 +112,15 @@ from .mailbox import (
111
112
  InboxMessage,
112
113
  InboxMessageAction,
113
114
  InboxMessages,
115
+ InboxReplyDraft,
116
+ InboxReplyDraftSend,
114
117
  InboxThread,
118
+ InboxThreadReplyState,
119
+ InboxThreadReplyStateResult,
120
+ InboxThreads,
121
+ InboxThreadSummary,
115
122
  ListInboxMessagesParams,
123
+ ListInboxThreadsParams,
116
124
  ListMailboxesParams,
117
125
  Mailbox,
118
126
  MailboxAppPassword,
@@ -156,6 +164,7 @@ from .mailbox import (
156
164
  ResetMailboxPasswordParams,
157
165
  UpdateAutoReplyParams,
158
166
  UpdateInboxMessageParams,
167
+ UpdateInboxThreadReplyStateParams,
159
168
  UpdateMailboxFolderParams,
160
169
  UpdateMailboxParams,
161
170
  UpdateMailboxRulesParams,
@@ -243,6 +252,14 @@ from .partner import (
243
252
  PartnerUsage,
244
253
  UpdatePartnerOrganizationParams,
245
254
  )
255
+ from .reply_scan import (
256
+ CreateReplyScanParams,
257
+ ListReplyScanResultsParams,
258
+ ReplyScan,
259
+ ReplyScanCandidate,
260
+ ReplyScanResults,
261
+ ReplyScanStatus,
262
+ )
246
263
  from .suppression import (
247
264
  ListSuppressionsParams,
248
265
  Suppression,
@@ -325,6 +342,8 @@ __all__ = [
325
342
  "CreateDomainParams",
326
343
  "AutoReply",
327
344
  "CreatedMailboxAppPassword",
345
+ "CreateInboxReplyDraftParams",
346
+ "CreateReplyScanParams",
328
347
  "CreateMailboxAppPasswordParams",
329
348
  "CreateImportUploadParams",
330
349
  "CreateMailboxFolderParams",
@@ -360,10 +379,18 @@ __all__ = [
360
379
  "InboxMessage",
361
380
  "InboxMessageAction",
362
381
  "InboxMessages",
382
+ "InboxReplyDraft",
383
+ "InboxReplyDraftSend",
363
384
  "InboxThread",
385
+ "InboxThreadReplyState",
386
+ "InboxThreadReplyStateResult",
387
+ "InboxThreadSummary",
388
+ "InboxThreads",
364
389
  "ListDeliveriesParams",
365
390
  "ListDomainsParams",
366
391
  "ListInboxMessagesParams",
392
+ "ListInboxThreadsParams",
393
+ "ListReplyScanResultsParams",
367
394
  "ListMailboxesParams",
368
395
  "ListMessagesParams",
369
396
  "ListNewsletterAssetsParams",
@@ -482,6 +509,10 @@ __all__ = [
482
509
  "RegistrationContact",
483
510
  "ResetMailboxPasswordParams",
484
511
  "ReplyToMessageParams",
512
+ "ReplyScan",
513
+ "ReplyScanCandidate",
514
+ "ReplyScanResults",
515
+ "ReplyScanStatus",
485
516
  "ReplyToThreadParams",
486
517
  "RotateSecretResponse",
487
518
  "ScheduleNewsletterParams",
@@ -495,6 +526,7 @@ __all__ = [
495
526
  "Thread",
496
527
  "RegisterNewsletterAssetFromUrlParams",
497
528
  "UpdateInboxMessageParams",
529
+ "UpdateInboxThreadReplyStateParams",
498
530
  "UpdateDomainParams",
499
531
  "UpdateAutoReplyParams",
500
532
  "UpdateMailboxFolderParams",
@@ -43,8 +43,8 @@ MessageStatus = Literal[
43
43
  "cancelled",
44
44
  ]
45
45
 
46
- MESSAGE_SOURCES = ("api", "newsletter", "dashboard", "inbound", "smtp")
47
- MessageSource = Literal["api", "newsletter", "dashboard", "inbound", "smtp"]
46
+ MESSAGE_SOURCES = ("api", "newsletter", "dashboard", "inbound", "smtp", "agent")
47
+ MessageSource = Literal["api", "newsletter", "dashboard", "inbound", "smtp", "agent"]
48
48
 
49
49
  WEBHOOK_EVENT_TYPES = (
50
50
  "message.received",
@@ -52,6 +52,7 @@ WEBHOOK_EVENT_TYPES = (
52
52
  "message.delivered",
53
53
  "message.bounced",
54
54
  "message.complained",
55
+ "thread.needs_reply",
55
56
  "mailbox.rule_matched",
56
57
  "domain.verified",
57
58
  "domain.verification_failed",
@@ -67,6 +68,7 @@ WebhookEventType = Literal[
67
68
  "message.delivered",
68
69
  "message.bounced",
69
70
  "message.complained",
71
+ "thread.needs_reply",
70
72
  "mailbox.rule_matched",
71
73
  "domain.verified",
72
74
  "domain.verification_failed",
@@ -85,6 +87,7 @@ WebhookDeliveryEventType = Literal[
85
87
  "message.delivered",
86
88
  "message.bounced",
87
89
  "message.complained",
90
+ "thread.needs_reply",
88
91
  "mailbox.rule_matched",
89
92
  "domain.verified",
90
93
  "domain.verification_failed",
@@ -3,6 +3,7 @@ from __future__ import annotations
3
3
  from typing import Literal, TypedDict
4
4
 
5
5
  from .common import PaginationParams
6
+ from .message import Message
6
7
 
7
8
 
8
9
  class AutoReply(TypedDict):
@@ -198,12 +199,11 @@ InboxFullMessage = TypedDict(
198
199
  )
199
200
 
200
201
 
201
- class InboxPositionPagination(TypedDict):
202
- position: int
202
+ class InboxCursorPagination(TypedDict):
203
203
  limit: int
204
204
  total: int
205
205
  has_more: bool
206
- next_position: int | None
206
+ next_cursor: str | None
207
207
 
208
208
 
209
209
  class InboxMessages(TypedDict):
@@ -211,7 +211,77 @@ class InboxMessages(TypedDict):
211
211
  mailbox_id: str
212
212
  address: str
213
213
  data: list[InboxFullMessage]
214
- pagination: InboxPositionPagination
214
+ pagination: InboxCursorPagination
215
+
216
+
217
+ InboxThreadReplyState = Literal["needs_reply", "waiting_on_contact", "resolved", "no_reply_expected"]
218
+
219
+
220
+ class InboxThreadSummary(TypedDict):
221
+ object: Literal["inbox_thread_summary"]
222
+ id: str
223
+ thread_id: str
224
+ reply_state: InboxThreadReplyState
225
+ reply_version: int
226
+ needs_reply_since: str | None
227
+ subject: str | None
228
+ latest_from_address: str | None
229
+ message_count: int
230
+ first_message_at: str
231
+ last_message_at: str
232
+ latest_message_id: str | None
233
+ latest_email_id: str | None
234
+ latest_inbound_message_id: str | None
235
+ latest_inbound_email_id: str | None
236
+ latest_inbound_at: str | None
237
+ latest_outbound_message_id: str | None
238
+ latest_outbound_email_id: str | None
239
+ latest_outbound_at: str | None
240
+
241
+
242
+ class InboxThreadsPagination(TypedDict):
243
+ limit: int
244
+ has_more: bool
245
+ next_cursor: str | None
246
+ snapshot_at: str
247
+
248
+
249
+ class InboxThreads(TypedDict):
250
+ object: Literal["inbox_threads"]
251
+ mailbox_id: str
252
+ data: list[InboxThreadSummary]
253
+ summary: dict[InboxThreadReplyState, int]
254
+ pagination: InboxThreadsPagination
255
+
256
+
257
+ class InboxThreadReplyStateResult(TypedDict):
258
+ object: Literal["inbox_thread_reply_state"]
259
+ id: str
260
+ thread_id: str
261
+ reply_state: InboxThreadReplyState
262
+ reply_version: int
263
+ needs_reply_since: str | None
264
+
265
+
266
+ class InboxReplyDraft(TypedDict):
267
+ object: Literal["inbox_reply_draft"]
268
+ id: str
269
+ mailbox_id: str
270
+ thread_id: str
271
+ based_on_message_id: str | None
272
+ expected_reply_version: int
273
+ reply_mode: Literal["reply", "reply_all"]
274
+ status: Literal["draft", "sending", "sent", "invalidated", "failed"]
275
+ to: list[dict[str, str | None]]
276
+ cc: list[dict[str, str | None]]
277
+ created_at: str
278
+
279
+
280
+ class InboxReplyDraftSend(TypedDict):
281
+ object: Literal["inbox_reply_draft_send"]
282
+ draft_id: str
283
+ status: Literal["draft", "sending", "sent", "invalidated", "failed"]
284
+ message: Message
215
285
 
216
286
 
217
287
  class InboxThread(TypedDict):
@@ -373,12 +443,39 @@ class ListInboxMessagesParams(TypedDict, total=False):
373
443
  folder_id: str
374
444
  folder_role: Literal["inbox", "starred", "sent", "drafts", "archive", "junk", "trash"]
375
445
  search_text: str
376
- position: int
446
+ cursor: str
447
+ after: str
448
+ before: str
377
449
  limit: int
378
450
  has_keyword: InboxKeyword
379
451
  not_keyword: InboxKeyword
380
452
 
381
453
 
454
+ class ListInboxThreadsParams(TypedDict, total=False):
455
+ reply_state: InboxThreadReplyState
456
+ sort_by: Literal["needs_reply_since", "last_message_at"]
457
+ order: Literal["asc", "desc"]
458
+ after: str
459
+ before: str
460
+ cursor: str
461
+ limit: int
462
+
463
+
464
+ class _CreateInboxReplyDraftParamsRequired(TypedDict):
465
+ expected_reply_version: int
466
+
467
+
468
+ class CreateInboxReplyDraftParams(_CreateInboxReplyDraftParamsRequired, total=False):
469
+ text: str
470
+ html: str
471
+ reply_mode: Literal["reply", "reply_all"]
472
+
473
+
474
+ class UpdateInboxThreadReplyStateParams(TypedDict):
475
+ reply_state: Literal["needs_reply", "resolved", "no_reply_expected"]
476
+ expected_reply_version: int
477
+
478
+
382
479
  class _DownloadInboxAttachmentParamsRequired(TypedDict):
383
480
  blob_id: str
384
481
 
@@ -0,0 +1,60 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Literal, TypedDict
4
+
5
+ ReplyScanStatus = Literal["completed"]
6
+
7
+
8
+ class ReplyScan(TypedDict):
9
+ object: Literal["reply_scan"]
10
+ id: str
11
+ mailbox_ids: list[str]
12
+ after: str
13
+ before: str
14
+ snapshot_at: str
15
+ status: ReplyScanStatus
16
+ candidate_count: int
17
+ completed_at: str
18
+ created_at: str
19
+
20
+
21
+ class ReplyScanCandidate(TypedDict):
22
+ object: Literal["reply_scan_candidate"]
23
+ id: str
24
+ mailbox_id: str
25
+ thread_id: str
26
+ tracked_thread_id: str
27
+ latest_message_id: str | None
28
+ latest_email_id: str | None
29
+ latest_inbound_message_id: str | None
30
+ latest_inbound_email_id: str | None
31
+ reply_version: int
32
+ needs_reply_since: str
33
+ subject: str | None
34
+
35
+
36
+ class ReplyScanResultsPagination(TypedDict):
37
+ limit: int
38
+ has_more: bool
39
+ next_cursor: str | None
40
+
41
+
42
+ class ReplyScanResults(TypedDict):
43
+ object: Literal["reply_scan_results"]
44
+ scan_id: str
45
+ data: list[ReplyScanCandidate]
46
+ pagination: ReplyScanResultsPagination
47
+
48
+
49
+ class _CreateReplyScanParamsRequired(TypedDict):
50
+ mailbox_ids: list[str]
51
+ after: str
52
+
53
+
54
+ class CreateReplyScanParams(_CreateReplyScanParamsRequired, total=False):
55
+ before: str
56
+
57
+
58
+ class ListReplyScanResultsParams(TypedDict, total=False):
59
+ cursor: str
60
+ limit: int
@@ -80,7 +80,7 @@ class ListDeliveriesParams(PaginationParams, total=False):
80
80
 
81
81
 
82
82
  class WebhookEvent(TypedDict):
83
- version: Literal["2026-07-12"]
83
+ version: Literal["2026-07-20"]
84
84
  event_id: str
85
85
  event_type: WebhookEventType
86
86
  created_at: str
@@ -0,0 +1 @@
1
+ VERSION = "0.2.3" # x-release-please-version
@@ -1,6 +1,7 @@
1
1
  from .booking_page import AsyncBookingPages, SyncBookingPages
2
2
  from .calendar import AsyncCalendar, AsyncCalendarEvents, SyncCalendar, SyncCalendarEvents
3
3
  from .partner import AsyncPartner, SyncPartner
4
+ from .reply_scans import AsyncReplyScans, SyncReplyScans
4
5
 
5
6
  __all__ = [
6
7
  "AsyncBookingPages",
@@ -10,5 +11,7 @@ __all__ = [
10
11
  "SyncCalendar",
11
12
  "SyncCalendarEvents",
12
13
  "AsyncPartner",
14
+ "AsyncReplyScans",
13
15
  "SyncPartner",
16
+ "SyncReplyScans",
14
17
  ]