broadcast-python 0.4.0__tar.gz → 0.5.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 (34) hide show
  1. {broadcast_python-0.4.0 → broadcast_python-0.5.0}/CHANGELOG.md +26 -0
  2. {broadcast_python-0.4.0 → broadcast_python-0.5.0}/PKG-INFO +10 -2
  3. {broadcast_python-0.4.0 → broadcast_python-0.5.0}/README.md +9 -1
  4. {broadcast_python-0.4.0 → broadcast_python-0.5.0}/pyproject.toml +1 -1
  5. {broadcast_python-0.4.0 → broadcast_python-0.5.0}/src/broadcast_python/__init__.py +2 -0
  6. {broadcast_python-0.4.0 → broadcast_python-0.5.0}/src/broadcast_python/resources/autopilots.py +2 -1
  7. {broadcast_python-0.4.0 → broadcast_python-0.5.0}/src/broadcast_python/resources/opt_in_forms.py +9 -0
  8. {broadcast_python-0.4.0 → broadcast_python-0.5.0}/src/broadcast_python/resources/subscribers.py +5 -1
  9. {broadcast_python-0.4.0 → broadcast_python-0.5.0}/src/broadcast_python/version.py +1 -1
  10. {broadcast_python-0.4.0 → broadcast_python-0.5.0}/src/broadcast_python/webhook.py +2 -0
  11. {broadcast_python-0.4.0 → broadcast_python-0.5.0}/.gitignore +0 -0
  12. {broadcast_python-0.4.0 → broadcast_python-0.5.0}/LICENSE +0 -0
  13. {broadcast_python-0.4.0 → broadcast_python-0.5.0}/src/broadcast_python/client.py +0 -0
  14. {broadcast_python-0.4.0 → broadcast_python-0.5.0}/src/broadcast_python/configuration.py +0 -0
  15. {broadcast_python-0.4.0 → broadcast_python-0.5.0}/src/broadcast_python/connection.py +0 -0
  16. {broadcast_python-0.4.0 → broadcast_python-0.5.0}/src/broadcast_python/errors.py +0 -0
  17. {broadcast_python-0.4.0 → broadcast_python-0.5.0}/src/broadcast_python/py.typed +0 -0
  18. {broadcast_python-0.4.0 → broadcast_python-0.5.0}/src/broadcast_python/resources/__init__.py +0 -0
  19. {broadcast_python-0.4.0 → broadcast_python-0.5.0}/src/broadcast_python/resources/base.py +0 -0
  20. {broadcast_python-0.4.0 → broadcast_python-0.5.0}/src/broadcast_python/resources/broadcasts.py +0 -0
  21. {broadcast_python-0.4.0 → broadcast_python-0.5.0}/src/broadcast_python/resources/channel_design.py +0 -0
  22. {broadcast_python-0.4.0 → broadcast_python-0.5.0}/src/broadcast_python/resources/discovery.py +0 -0
  23. {broadcast_python-0.4.0 → broadcast_python-0.5.0}/src/broadcast_python/resources/email_servers.py +0 -0
  24. {broadcast_python-0.4.0 → broadcast_python-0.5.0}/src/broadcast_python/resources/global_suppressions.py +0 -0
  25. {broadcast_python-0.4.0 → broadcast_python-0.5.0}/src/broadcast_python/resources/migration.py +0 -0
  26. {broadcast_python-0.4.0 → broadcast_python-0.5.0}/src/broadcast_python/resources/segments.py +0 -0
  27. {broadcast_python-0.4.0 → broadcast_python-0.5.0}/src/broadcast_python/resources/sequences.py +0 -0
  28. {broadcast_python-0.4.0 → broadcast_python-0.5.0}/src/broadcast_python/resources/suppressions.py +0 -0
  29. {broadcast_python-0.4.0 → broadcast_python-0.5.0}/src/broadcast_python/resources/templates.py +0 -0
  30. {broadcast_python-0.4.0 → broadcast_python-0.5.0}/src/broadcast_python/resources/topics.py +0 -0
  31. {broadcast_python-0.4.0 → broadcast_python-0.5.0}/src/broadcast_python/resources/transactionals.py +0 -0
  32. {broadcast_python-0.4.0 → broadcast_python-0.5.0}/src/broadcast_python/resources/users.py +0 -0
  33. {broadcast_python-0.4.0 → broadcast_python-0.5.0}/src/broadcast_python/resources/webhook_endpoints.py +0 -0
  34. {broadcast_python-0.4.0 → broadcast_python-0.5.0}/src/broadcast_python/response.py +0 -0
@@ -4,6 +4,32 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.5.0] - 2026-10-10
8
+
9
+ ### Added
10
+
11
+ - `email.send_delayed` and `broadcast.batch_completed` in `EVENT_TYPES` (and in
12
+ `EMAIL_EVENTS` / `BROADCAST_EVENTS`), matching the server (Broadcast 2.43.0).
13
+ `email.send_delayed` fires when a transactional email waits for room under
14
+ its server's hourly limit.
15
+ - `excluded_segment_ids` on broadcasts, sequences and autopilots: segments a
16
+ send never reaches. The server refuses a segment that is both sent to and
17
+ excluded with 422.
18
+ - `subscribers.create(unsubscribed_at=...)`: creates a subscriber who has
19
+ already unsubscribed, in one request, with any token. Before, the server
20
+ ignored the field and a migration had to create, then unsubscribe.
21
+
22
+ - `TRIGGER_FREQUENCIES`: the words `trigger_settings["frequency"]` accepts
23
+ (`always`, `every_visit`, `once_per_session`, `once_per_day`,
24
+ `once_per_week`, `once`).
25
+
26
+ ### Changed
27
+
28
+ - Opt-in forms: the server now refuses an unknown `trigger_settings`
29
+ frequency word with 422 (`ValidationError`). Before, it saved any word, and
30
+ an unknown one showed the popup on every visit. The client still sends what
31
+ it is given.
32
+
7
33
  ## [0.4.0] - 2026-10-06
8
34
 
9
35
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: broadcast-python
3
- Version: 0.4.0
3
+ Version: 0.5.0
4
4
  Summary: Python client for the Broadcast email platform. Subscribers, sequences, broadcasts, segments, templates, autopilot, webhooks, and transactional email.
5
5
  Project-URL: Homepage, https://sendbroadcast.net
6
6
  Project-URL: Repository, https://github.com/send-broadcast/broadcast-python
@@ -240,6 +240,8 @@ client.sequences.move_step(id, step_id, under_id)
240
240
  client.segments.create(name="VIPs")
241
241
  client.templates.create(label="Welcome", subject="Hi", body="...")
242
242
  client.opt_in_forms.analytics(id, start_date=date(2026, 1, 1))
243
+ # How often the same visitor sees a popup: one of TRIGGER_FREQUENCIES, or ValidationError (422).
244
+ client.opt_in_forms.update(id, trigger_settings={"frequency": "once_per_day"})
243
245
  client.opt_in_forms.create_variant(id, name="B", weight=50)
244
246
  client.opt_in_forms.duplicate(id, label="Copy")
245
247
  ```
@@ -264,6 +266,12 @@ client.topics.delete(topic["id"]) # 422 while a broadcast or sequence uses it
264
266
  # Send to a topic: (segments) AND topic
265
267
  client.broadcasts.create(subject="Webinar", body="...", segment_ids=[2], topic_id=topic["id"])
266
268
 
269
+ # Send to everyone except a segment (also on sequences and autopilots)
270
+ client.broadcasts.create(subject="Offer", body="...", excluded_segment_ids=[competitors["id"]])
271
+
272
+ # Migrate someone who already unsubscribed, in one request
273
+ client.subscribers.create(email="opted-out@example.com", unsubscribed_at="2025-03-14T09:30:00Z")
274
+
267
275
  # Change only the keys you send (None deletes a key); the default replaces custom_data
268
276
  client.subscribers.update("jane@example.com", custom_data={"sub_webinars": False}, custom_data_mode="merge")
269
277
  ```
@@ -439,7 +447,7 @@ every rejection rather than distinguishing them.
439
447
  Pass the **raw** request body. Re-serialising a parsed dict changes the bytes
440
448
  and verification will fail.
441
449
 
442
- `broadcast_python.EVENT_TYPES` lists all 35 event names. `SUBSCRIBER_EVENTS`
450
+ `broadcast_python.EVENT_TYPES` lists all 37 event names. `SUBSCRIBER_EVENTS`
443
451
  includes `subscribers.purged` and `subscribers.purge_failed`: a purge of the
444
452
  whole list sends one of these instead of a `subscriber.deleted` per subscriber.
445
453
 
@@ -216,6 +216,8 @@ client.sequences.move_step(id, step_id, under_id)
216
216
  client.segments.create(name="VIPs")
217
217
  client.templates.create(label="Welcome", subject="Hi", body="...")
218
218
  client.opt_in_forms.analytics(id, start_date=date(2026, 1, 1))
219
+ # How often the same visitor sees a popup: one of TRIGGER_FREQUENCIES, or ValidationError (422).
220
+ client.opt_in_forms.update(id, trigger_settings={"frequency": "once_per_day"})
219
221
  client.opt_in_forms.create_variant(id, name="B", weight=50)
220
222
  client.opt_in_forms.duplicate(id, label="Copy")
221
223
  ```
@@ -240,6 +242,12 @@ client.topics.delete(topic["id"]) # 422 while a broadcast or sequence uses it
240
242
  # Send to a topic: (segments) AND topic
241
243
  client.broadcasts.create(subject="Webinar", body="...", segment_ids=[2], topic_id=topic["id"])
242
244
 
245
+ # Send to everyone except a segment (also on sequences and autopilots)
246
+ client.broadcasts.create(subject="Offer", body="...", excluded_segment_ids=[competitors["id"]])
247
+
248
+ # Migrate someone who already unsubscribed, in one request
249
+ client.subscribers.create(email="opted-out@example.com", unsubscribed_at="2025-03-14T09:30:00Z")
250
+
243
251
  # Change only the keys you send (None deletes a key); the default replaces custom_data
244
252
  client.subscribers.update("jane@example.com", custom_data={"sub_webinars": False}, custom_data_mode="merge")
245
253
  ```
@@ -415,7 +423,7 @@ every rejection rather than distinguishing them.
415
423
  Pass the **raw** request body. Re-serialising a parsed dict changes the bytes
416
424
  and verification will fail.
417
425
 
418
- `broadcast_python.EVENT_TYPES` lists all 35 event names. `SUBSCRIBER_EVENTS`
426
+ `broadcast_python.EVENT_TYPES` lists all 37 event names. `SUBSCRIBER_EVENTS`
419
427
  includes `subscribers.purged` and `subscribers.purge_failed`: a purge of the
420
428
  whole list sends one of these instead of a `subscriber.deleted` per subscriber.
421
429
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "broadcast-python"
7
- version = "0.4.0"
7
+ version = "0.5.0"
8
8
  description = "Python client for the Broadcast email platform. Subscribers, sequences, broadcasts, segments, templates, autopilot, webhooks, and transactional email."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -28,6 +28,7 @@ from .errors import (
28
28
  )
29
29
  from .resources.email_servers import REDACTED_FIELDS
30
30
  from .resources.migration import COLLECTIONS
31
+ from .resources.opt_in_forms import TRIGGER_FREQUENCIES
31
32
  from .resources.transactionals import MAX_IDEMPOTENCY_KEY_LENGTH
32
33
  from .response import RateLimit, Response, Warning_
33
34
  from .version import VERSION
@@ -42,6 +43,7 @@ __all__ = [
42
43
  "EVENT_TYPES",
43
44
  "MAX_IDEMPOTENCY_KEY_LENGTH",
44
45
  "REDACTED_FIELDS",
46
+ "TRIGGER_FREQUENCIES",
45
47
  "VERSION",
46
48
  "WARNINGS_MODES",
47
49
  "APIError",
@@ -36,7 +36,8 @@ class Autopilots(BaseResource):
36
36
  write-only. Scheduling takes ``schedule_frequency`` (daily, weekly,
37
37
  biweekly, monthly), ``schedule_day_of_week``, ``schedule_day_of_month``,
38
38
  ``schedule_time``, ``schedule_timezone``. ``segment_ids`` restricts the
39
- newsletter's audience.
39
+ newsletter's audience; ``excluded_segment_ids`` lists segments its
40
+ drafts never reach.
40
41
  """
41
42
  return self._post("/api/v1/autopilots", {"autopilot": attrs})
42
43
 
@@ -5,6 +5,12 @@ from .base import BaseResource, compact
5
5
 
6
6
  Id = Union[str, int]
7
7
 
8
+ #: The words ``trigger_settings["frequency"]`` accepts: how often the same
9
+ #: visitor sees a popup. ``always`` and ``every_visit`` mean the same. The server
10
+ #: refuses any other word with 422 (:class:`ValidationError`); the client does
11
+ #: not check, so an older server is never refused a word it would take.
12
+ TRIGGER_FREQUENCIES = ("always", "every_visit", "once_per_session", "once_per_day", "once_per_week", "once")
13
+
8
14
 
9
15
  class OptInForms(BaseResource):
10
16
  def list(self, **params: Any) -> Any:
@@ -23,6 +29,9 @@ class OptInForms(BaseResource):
23
29
  Nested settings dicts (``theme_settings``, ``automation_settings``,
24
30
  ``security_settings``, ``trigger_settings``, ``widget_settings``) and
25
31
  the block arrays are passed through verbatim.
32
+ ``trigger_settings["frequency"]`` must be one of
33
+ :data:`TRIGGER_FREQUENCIES`; any other word raises
34
+ :class:`ValidationError` (422).
26
35
  """
27
36
  return self._post("/api/v1/opt_in_forms", {"opt_in_form": attrs})
28
37
 
@@ -33,7 +33,11 @@ class Subscribers(BaseResource):
33
33
  timestamp when migrating an already-confirmed list off another provider.
34
34
  It is ignored (with a warning) on update.
35
35
 
36
- ``unsubscribed_at`` is never settable here; use :meth:`unsubscribe`.
36
+ ``unsubscribed_at`` (any token, create only, ISO 8601) stores the
37
+ subscriber as unsubscribed in the same request, for migrating a list
38
+ with its opt-outs intact: no confirmation email, 422 if it is in the
39
+ future or sent with ``is_active=True``. To unsubscribe an existing
40
+ subscriber, use :meth:`unsubscribe`.
37
41
  """
38
42
  double_opt_in = attrs.pop("double_opt_in", None)
39
43
  confirmation_template_id = attrs.pop("confirmation_template_id", None)
@@ -1,3 +1,3 @@
1
1
  # Kept in sync with pyproject.toml by tests/test_package.py — a User-Agent that
2
2
  # lies about its version misattributes server-side client analytics.
3
- VERSION = "0.4.0"
3
+ VERSION = "0.5.0"
@@ -15,6 +15,7 @@ EMAIL_EVENTS = (
15
15
  "email.sent",
16
16
  "email.delivered",
17
17
  "email.delivery_delayed",
18
+ "email.send_delayed",
18
19
  "email.complained",
19
20
  "email.bounced",
20
21
  "email.opened",
@@ -45,6 +46,7 @@ BROADCAST_EVENTS = (
45
46
  "broadcast.partial_failure",
46
47
  "broadcast.aborted",
47
48
  "broadcast.paused",
49
+ "broadcast.batch_completed",
48
50
  )
49
51
 
50
52
  SEQUENCE_EVENTS = (