sendly-python 1.2.0__tar.gz → 1.3.0__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 (66) hide show
  1. {sendly_python-1.2.0 → sendly_python-1.3.0}/CHANGELOG.md +21 -0
  2. {sendly_python-1.2.0 → sendly_python-1.3.0}/PKG-INFO +21 -1
  3. {sendly_python-1.2.0 → sendly_python-1.3.0}/README.md +20 -0
  4. {sendly_python-1.2.0 → sendly_python-1.3.0}/pyproject.toml +1 -1
  5. {sendly_python-1.2.0 → sendly_python-1.3.0}/src/sendly/client.py +1 -1
  6. {sendly_python-1.2.0 → sendly_python-1.3.0}/src/sendly/types.py +5 -1
  7. {sendly_python-1.2.0 → sendly_python-1.3.0}/tests/fixtures/openapi.json +19835 -5361
  8. {sendly_python-1.2.0 → sendly_python-1.3.0}/tests/test_contract.py +137 -3
  9. {sendly_python-1.2.0 → sendly_python-1.3.0}/.github/workflows/ci.yml +0 -0
  10. {sendly_python-1.2.0 → sendly_python-1.3.0}/.github/workflows/release.yml +0 -0
  11. {sendly_python-1.2.0 → sendly_python-1.3.0}/.gitignore +0 -0
  12. {sendly_python-1.2.0 → sendly_python-1.3.0}/LICENSE +0 -0
  13. {sendly_python-1.2.0 → sendly_python-1.3.0}/scripts/sync_spec.py +0 -0
  14. {sendly_python-1.2.0 → sendly_python-1.3.0}/src/sendly/__init__.py +0 -0
  15. {sendly_python-1.2.0 → sendly_python-1.3.0}/src/sendly/errors.py +0 -0
  16. {sendly_python-1.2.0 → sendly_python-1.3.0}/src/sendly/py.typed +0 -0
  17. {sendly_python-1.2.0 → sendly_python-1.3.0}/src/sendly/resources/__init__.py +0 -0
  18. {sendly_python-1.2.0 → sendly_python-1.3.0}/src/sendly/resources/_helpers.py +0 -0
  19. {sendly_python-1.2.0 → sendly_python-1.3.0}/src/sendly/resources/_pagination.py +0 -0
  20. {sendly_python-1.2.0 → sendly_python-1.3.0}/src/sendly/resources/analytics.py +0 -0
  21. {sendly_python-1.2.0 → sendly_python-1.3.0}/src/sendly/resources/campaigns.py +0 -0
  22. {sendly_python-1.2.0 → sendly_python-1.3.0}/src/sendly/resources/contacts.py +0 -0
  23. {sendly_python-1.2.0 → sendly_python-1.3.0}/src/sendly/resources/deliverability.py +0 -0
  24. {sendly_python-1.2.0 → sendly_python-1.3.0}/src/sendly/resources/domains.py +0 -0
  25. {sendly_python-1.2.0 → sendly_python-1.3.0}/src/sendly/resources/emails.py +0 -0
  26. {sendly_python-1.2.0 → sendly_python-1.3.0}/src/sendly/resources/events.py +0 -0
  27. {sendly_python-1.2.0 → sendly_python-1.3.0}/src/sendly/resources/lists.py +0 -0
  28. {sendly_python-1.2.0 → sendly_python-1.3.0}/src/sendly/resources/mailboxes.py +0 -0
  29. {sendly_python-1.2.0 → sendly_python-1.3.0}/src/sendly/resources/projects.py +0 -0
  30. {sendly_python-1.2.0 → sendly_python-1.3.0}/src/sendly/resources/segments.py +0 -0
  31. {sendly_python-1.2.0 → sendly_python-1.3.0}/src/sendly/resources/snippets.py +0 -0
  32. {sendly_python-1.2.0 → sendly_python-1.3.0}/src/sendly/resources/suppression.py +0 -0
  33. {sendly_python-1.2.0 → sendly_python-1.3.0}/src/sendly/resources/templates.py +0 -0
  34. {sendly_python-1.2.0 → sendly_python-1.3.0}/src/sendly/resources/topics.py +0 -0
  35. {sendly_python-1.2.0 → sendly_python-1.3.0}/src/sendly/resources/usage.py +0 -0
  36. {sendly_python-1.2.0 → sendly_python-1.3.0}/src/sendly/resources/validation.py +0 -0
  37. {sendly_python-1.2.0 → sendly_python-1.3.0}/src/sendly/resources/verify.py +0 -0
  38. {sendly_python-1.2.0 → sendly_python-1.3.0}/src/sendly/resources/webhooks.py +0 -0
  39. {sendly_python-1.2.0 → sendly_python-1.3.0}/src/sendly/resources/workflows.py +0 -0
  40. {sendly_python-1.2.0 → sendly_python-1.3.0}/src/sendly/webhook_utils.py +0 -0
  41. {sendly_python-1.2.0 → sendly_python-1.3.0}/tests/support.py +0 -0
  42. {sendly_python-1.2.0 → sendly_python-1.3.0}/tests/test_analytics.py +0 -0
  43. {sendly_python-1.2.0 → sendly_python-1.3.0}/tests/test_campaigns.py +0 -0
  44. {sendly_python-1.2.0 → sendly_python-1.3.0}/tests/test_client.py +0 -0
  45. {sendly_python-1.2.0 → sendly_python-1.3.0}/tests/test_contacts.py +0 -0
  46. {sendly_python-1.2.0 → sendly_python-1.3.0}/tests/test_deliverability.py +0 -0
  47. {sendly_python-1.2.0 → sendly_python-1.3.0}/tests/test_domain_logo.py +0 -0
  48. {sendly_python-1.2.0 → sendly_python-1.3.0}/tests/test_domains.py +0 -0
  49. {sendly_python-1.2.0 → sendly_python-1.3.0}/tests/test_emails.py +0 -0
  50. {sendly_python-1.2.0 → sendly_python-1.3.0}/tests/test_errors_problem.py +0 -0
  51. {sendly_python-1.2.0 → sendly_python-1.3.0}/tests/test_events.py +0 -0
  52. {sendly_python-1.2.0 → sendly_python-1.3.0}/tests/test_lists.py +0 -0
  53. {sendly_python-1.2.0 → sendly_python-1.3.0}/tests/test_mailbox_conversations.py +0 -0
  54. {sendly_python-1.2.0 → sendly_python-1.3.0}/tests/test_mailboxes.py +0 -0
  55. {sendly_python-1.2.0 → sendly_python-1.3.0}/tests/test_projects.py +0 -0
  56. {sendly_python-1.2.0 → sendly_python-1.3.0}/tests/test_segments.py +0 -0
  57. {sendly_python-1.2.0 → sendly_python-1.3.0}/tests/test_snippets.py +0 -0
  58. {sendly_python-1.2.0 → sendly_python-1.3.0}/tests/test_suppression.py +0 -0
  59. {sendly_python-1.2.0 → sendly_python-1.3.0}/tests/test_templates.py +0 -0
  60. {sendly_python-1.2.0 → sendly_python-1.3.0}/tests/test_topics.py +0 -0
  61. {sendly_python-1.2.0 → sendly_python-1.3.0}/tests/test_usage.py +0 -0
  62. {sendly_python-1.2.0 → sendly_python-1.3.0}/tests/test_validation.py +0 -0
  63. {sendly_python-1.2.0 → sendly_python-1.3.0}/tests/test_verify.py +0 -0
  64. {sendly_python-1.2.0 → sendly_python-1.3.0}/tests/test_webhook_verify.py +0 -0
  65. {sendly_python-1.2.0 → sendly_python-1.3.0}/tests/test_webhooks.py +0 -0
  66. {sendly_python-1.2.0 → sendly_python-1.3.0}/tests/test_workflows.py +0 -0
@@ -5,6 +5,27 @@ All notable changes to `sendly-python` are documented here. This project adheres
5
5
 
6
6
  ## [Unreleased]
7
7
 
8
+ ## [1.3.0] - 2026-11-07
9
+
10
+ ### Added
11
+
12
+ - **Why a message bounced or was complained about.** An email (`emails.get`, `emails.list`)
13
+ gains the keys `bounceType`, `bounceSubType`, `bounceStatusCode`, `bounceDiagnosticCode` and
14
+ `complaintFeedbackType`; each entry of its delivery history gains the four `bounce*` keys. The
15
+ `email.bounced` webhook body gains `bounceSubType`, `bounceStatusCode` and
16
+ `bounceDiagnosticCode`, and `email.complained` gains `complaintFeedbackType`. All are `str | None`
17
+ and additive: responses are plain dicts, so existing code is unaffected.
18
+ - **Three other optional fields arrive with the re-sync:** `Email.hold`, `Problem.next` and
19
+ `CreateApiKeyBody.tiers`. All are optional and additive.
20
+
21
+ ### Changed
22
+
23
+ - **The vendored contract is re-synced** from the released one (`docs.sendly.now/openapi.json`),
24
+ which had grown from 106 to 198 paths since the last sync. The additions are account, dashboard
25
+ and member-management routes plus key-callable operations this SDK has no method for yet; the
26
+ contract suite lists them in `NOT_SDK_CALLABLE` / `NOT_YET_IMPLEMENTED` rather than shipping
27
+ them unreviewed. No existing method changed.
28
+
8
29
  ## [1.2.0] - 2026-11-03
9
30
 
10
31
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sendly-python
3
- Version: 1.2.0
3
+ Version: 1.3.0
4
4
  Summary: Official Python SDK for Sendly (sendly.now), the email API and platform for developers by Devino Solutions: transactional email, contacts, templates and webhooks.
5
5
  Project-URL: Homepage, https://sendly.now
6
6
  Project-URL: Documentation, https://docs.sendly.now
@@ -311,6 +311,26 @@ for event in email["events"]:
311
311
  somebody opened, read `openedAt` / `opens` on the rows — engagement is not a
312
312
  status.
313
313
 
314
+ A bounced or complained message says why. Each key below is `str | None` and is
315
+ `None` until the event happens (or when the receiving server did not say):
316
+
317
+ ```python
318
+ email = sendly.emails.get("em_123")["data"]
319
+ if email["status"] == "BOUNCED":
320
+ # `bounceType` is "Permanent" or "Undetermined" here (a "Transient" bounce is
321
+ # retried and never sets BOUNCED); the rest are the receiving server's own words.
322
+ # Show them to a person, do not parse them.
323
+ print(email["bounceType"], email["bounceSubType"],
324
+ email["bounceStatusCode"], email["bounceDiagnosticCode"])
325
+ if email["complainedAt"] is not None:
326
+ print(email["complaintFeedbackType"]) # e.g. "abuse"
327
+ ```
328
+
329
+ Each entry of `email["events"]` carries the same four `bounce*` keys for the
330
+ transition that recorded them. The `email.bounced` webhook body adds
331
+ `bounceSubType`, `bounceStatusCode` and `bounceDiagnosticCode`, and
332
+ `email.complained` adds `complaintFeedbackType`.
333
+
314
334
  ### Contacts
315
335
 
316
336
  ```python
@@ -279,6 +279,26 @@ for event in email["events"]:
279
279
  somebody opened, read `openedAt` / `opens` on the rows — engagement is not a
280
280
  status.
281
281
 
282
+ A bounced or complained message says why. Each key below is `str | None` and is
283
+ `None` until the event happens (or when the receiving server did not say):
284
+
285
+ ```python
286
+ email = sendly.emails.get("em_123")["data"]
287
+ if email["status"] == "BOUNCED":
288
+ # `bounceType` is "Permanent" or "Undetermined" here (a "Transient" bounce is
289
+ # retried and never sets BOUNCED); the rest are the receiving server's own words.
290
+ # Show them to a person, do not parse them.
291
+ print(email["bounceType"], email["bounceSubType"],
292
+ email["bounceStatusCode"], email["bounceDiagnosticCode"])
293
+ if email["complainedAt"] is not None:
294
+ print(email["complaintFeedbackType"]) # e.g. "abuse"
295
+ ```
296
+
297
+ Each entry of `email["events"]` carries the same four `bounce*` keys for the
298
+ transition that recorded them. The `email.bounced` webhook body adds
299
+ `bounceSubType`, `bounceStatusCode` and `bounceDiagnosticCode`, and
300
+ `email.complained` adds `complaintFeedbackType`.
301
+
282
302
  ### Contacts
283
303
 
284
304
  ```python
@@ -8,7 +8,7 @@ build-backend = "hatchling.build"
8
8
  # the PyPI trusted-publisher configuration). The import package is
9
9
  # unchanged (`import sendly`) — see [tool.hatch.build.targets.wheel] below.
10
10
  name = "sendly-python"
11
- version = "1.2.0"
11
+ version = "1.3.0"
12
12
  description = "Official Python SDK for Sendly (sendly.now), the email API and platform for developers by Devino Solutions: transactional email, contacts, templates and webhooks."
13
13
  readme = "README.md"
14
14
  requires-python = ">=3.10"
@@ -64,7 +64,7 @@ if TYPE_CHECKING:
64
64
  __all__ = ["DEFAULT_BASE_URL", "SDK_VERSION", "Sendly"]
65
65
 
66
66
  #: Package version. Kept in sync with ``pyproject.toml``.
67
- SDK_VERSION = "1.2.0"
67
+ SDK_VERSION = "1.3.0"
68
68
 
69
69
  #: Default production API base. Override via ``base_url`` for staging/self-hosted.
70
70
  DEFAULT_BASE_URL = "https://api.sendly.now"
@@ -39,12 +39,16 @@ Pagination = JSONDict
39
39
  SendEmailData = JSONDict
40
40
  SendEmailResponse = JSONDict
41
41
  BatchSendResponse = JSONDict
42
+ #: An email row. Beyond the delivery fields it carries, ``str | None`` and ``None``
43
+ #: until the event happens, ``bounceType``, ``bounceSubType``, ``bounceStatusCode``,
44
+ #: ``bounceDiagnosticCode`` and ``complaintFeedbackType``.
42
45
  EmailRecord = JSONDict
43
46
  EmailListResponse = JSONDict
44
47
 
45
48
  #: One transition in a message's delivery history -- the append-only record
46
49
  #: behind ``status``. ``status`` says where the message is now; these say how it
47
- #: got there.
50
+ #: got there. A bounce transition also carries ``bounceType``, ``bounceSubType``,
51
+ #: ``bounceStatusCode`` and ``bounceDiagnosticCode`` (each ``str | None``).
48
52
  EmailEvent = JSONDict
49
53
  #: An email together with its delivery history, oldest first.
50
54
  EmailWithEvents = JSONDict