poststack 0.4.0__tar.gz → 0.9.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 (35) hide show
  1. {poststack-0.4.0 → poststack-0.9.0}/PKG-INFO +31 -17
  2. {poststack-0.4.0 → poststack-0.9.0}/README.md +28 -14
  3. {poststack-0.4.0 → poststack-0.9.0}/pyproject.toml +6 -2
  4. {poststack-0.4.0 → poststack-0.9.0}/src/poststack/__init__.py +22 -7
  5. {poststack-0.4.0 → poststack-0.9.0}/src/poststack/_client.py +500 -333
  6. {poststack-0.4.0 → poststack-0.9.0}/src/poststack/_errors.py +8 -1
  7. {poststack-0.4.0 → poststack-0.9.0}/src/poststack/resources/__init__.py +9 -0
  8. {poststack-0.4.0 → poststack-0.9.0}/src/poststack/resources/api_keys.py +26 -0
  9. {poststack-0.4.0 → poststack-0.9.0}/src/poststack/resources/broadcasts.py +134 -0
  10. {poststack-0.4.0 → poststack-0.9.0}/src/poststack/resources/contact_properties.py +16 -0
  11. {poststack-0.4.0 → poststack-0.9.0}/src/poststack/resources/contacts.py +62 -7
  12. {poststack-0.4.0 → poststack-0.9.0}/src/poststack/resources/domains.py +119 -2
  13. {poststack-0.4.0 → poststack-0.9.0}/src/poststack/resources/emails.py +46 -0
  14. poststack-0.9.0/src/poststack/resources/inbound_emails.py +147 -0
  15. poststack-0.9.0/src/poststack/resources/mailboxes.py +345 -0
  16. poststack-0.9.0/src/poststack/resources/notification_channels.py +179 -0
  17. {poststack-0.4.0 → poststack-0.9.0}/src/poststack/resources/segments.py +72 -0
  18. poststack-0.9.0/src/poststack/resources/subscription_topics.py +184 -0
  19. {poststack-0.4.0 → poststack-0.9.0}/src/poststack/resources/templates.py +38 -0
  20. poststack-0.9.0/src/poststack/resources/webhooks.py +303 -0
  21. {poststack-0.4.0 → poststack-0.9.0}/src/poststack/resources/workflows.py +84 -51
  22. {poststack-0.4.0 → poststack-0.9.0}/src/poststack/types.py +206 -21
  23. poststack-0.9.0/tests/__init__.py +0 -0
  24. poststack-0.9.0/tests/test_api_parity.py +491 -0
  25. {poststack-0.4.0 → poststack-0.9.0}/tests/test_client.py +119 -13
  26. poststack-0.9.0/tests/test_webhooks_verify.py +60 -0
  27. poststack-0.4.0/src/poststack/resources/mailboxes.py +0 -157
  28. poststack-0.4.0/src/poststack/resources/subscription_topics.py +0 -119
  29. poststack-0.4.0/src/poststack/resources/webhooks.py +0 -139
  30. {poststack-0.4.0 → poststack-0.9.0}/.gitignore +0 -0
  31. {poststack-0.4.0 → poststack-0.9.0}/LICENSE +0 -0
  32. /poststack-0.4.0/tests/__init__.py → /poststack-0.9.0/src/poststack/py.typed +0 -0
  33. {poststack-0.4.0 → poststack-0.9.0}/src/poststack/resources/email_validations.py +0 -0
  34. {poststack-0.4.0 → poststack-0.9.0}/src/poststack/resources/signup_forms.py +0 -0
  35. {poststack-0.4.0 → poststack-0.9.0}/src/poststack/resources/suppressions.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: poststack
3
- Version: 0.4.0
3
+ Version: 0.9.0
4
4
  Summary: Official Python SDK for the PostStack Email API
5
5
  Project-URL: Homepage, https://poststack.dev
6
6
  Project-URL: Documentation, https://poststack.dev/docs
@@ -22,7 +22,7 @@ Classifier: Topic :: Communications :: Email
22
22
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
23
  Requires-Python: >=3.10
24
24
  Requires-Dist: httpx>=0.25.0
25
- Requires-Dist: pydantic>=2.0.0
25
+ Requires-Dist: pydantic>=2.4.0
26
26
  Provides-Extra: dev
27
27
  Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
28
28
  Requires-Dist: pytest>=7.0.0; extra == 'dev'
@@ -105,16 +105,28 @@ client = PostStack(api_key="sk_live_...")
105
105
  try:
106
106
  client.emails.send({"from": "x@y.com", "to": ["z@w.com"], "subject": "hi"})
107
107
  except PostStackError as e:
108
- print(e.status_code, e.error, e.request_id)
108
+ print(e.status_code, e.error, e.code, e.request_id)
109
+ # e.headers holds the response headers; e.retry_after the Retry-After
110
+ # delay in seconds, when the API sent one.
109
111
  ```
110
112
 
113
+ ## Retries
114
+
115
+ Network errors, timeouts, 408, 429 and 5xx responses are retried up to
116
+ `max_retries` times (default 3) with full-jitter exponential backoff. A 429
117
+ that carries `Retry-After` waits exactly that long (up to 60 seconds; a longer
118
+ wait is raised instead). A 429 that is a quota rather than throttling — the
119
+ monthly plan limit or the daily send cap, which carry an error code and no
120
+ `Retry-After` — is raised immediately, since retrying cannot lift it.
121
+
111
122
  ## Configuration
112
123
 
113
124
  ```python
114
125
  client = PostStack(
115
126
  api_key="sk_live_...",
116
127
  base_url="https://api.poststack.dev", # default
117
- timeout=30.0, # default (seconds)
128
+ timeout=30.0, # default (seconds), per attempt
129
+ max_retries=3, # default; 0 disables retrying
118
130
  )
119
131
  ```
120
132
 
@@ -126,19 +138,21 @@ client.emails.send({...}, timeout=60.0)
126
138
 
127
139
  ## Resources
128
140
 
129
- - `client.emails` — send, get, list, cancel, reschedule, batch, events, insights
130
- - `client.domains` — create, list, get, verify, update, delete, DMARC
131
- - `client.contacts` — CRUD, import, export, unsubscribe
132
- - `client.contact_properties` — custom properties
133
- - `client.segments` — create, list, get, update, delete, contacts, preview
134
- - `client.templates` — CRUD, publish, duplicate, presets
135
- - `client.webhooks` — CRUD, test, deliveries, replay
136
- - `client.broadcasts` — CRUD, send, test, variants
141
+ - `client.emails` — send, get, list, cancel, reschedule, batch, events, insights, preview, spam_preview
142
+ - `client.domains` — create, list, get, verify, update, delete, DMARC, DKIM rotation, inbox placement
143
+ - `client.contacts` — CRUD, import, export_csv (text; optional `segment_id`), unsubscribe
144
+ - `client.contact_properties` — custom properties, option_usage
145
+ - `client.segments` — create, list, get, update, delete, contacts, preview, members, growth, broadcasts
146
+ - `client.templates` — CRUD, publish, duplicate, render, presets
147
+ - `client.webhooks` — CRUD, test, deliveries, replay, batch_replay, rotate_secret
148
+ - `client.broadcasts` — CRUD, send, test, variants, resend, performance, non_openers, non_clickers, end_ab_test
137
149
  - `client.suppressions` — list, add, remove
138
- - `client.api_keys` — CRUD
139
- - `client.mailboxes` — CRUD, aliases, password
140
- - `client.subscription_topics` — topics + contact subscriptions
141
- - `client.workflows` — CRUD, steps, activate, pause, trigger
150
+ - `client.api_keys` — CRUD, rotate
151
+ - `client.mailboxes` — CRUD, aliases, password, filters, signature, shares, unread_summary
152
+ - `client.inbound_emails` — list, get, attachments, download, draft_reply, reply, forward
153
+ - `client.subscription_topics` — topics (get, update, list_subscribers) + contact subscriptions
154
+ - `client.workflows` — CRUD, graph (get_graph/put_graph/validate_graph), activate, pause, trigger, post_event
155
+ - `client.notification_channels` — Slack/Discord/Telegram alert channels: CRUD, test, deliveries, replay
142
156
  - `client.signup_forms` — CRUD, submit
143
157
  - `client.email_validations` — validate, validate_batch
144
158
 
@@ -74,16 +74,28 @@ client = PostStack(api_key="sk_live_...")
74
74
  try:
75
75
  client.emails.send({"from": "x@y.com", "to": ["z@w.com"], "subject": "hi"})
76
76
  except PostStackError as e:
77
- print(e.status_code, e.error, e.request_id)
77
+ print(e.status_code, e.error, e.code, e.request_id)
78
+ # e.headers holds the response headers; e.retry_after the Retry-After
79
+ # delay in seconds, when the API sent one.
78
80
  ```
79
81
 
82
+ ## Retries
83
+
84
+ Network errors, timeouts, 408, 429 and 5xx responses are retried up to
85
+ `max_retries` times (default 3) with full-jitter exponential backoff. A 429
86
+ that carries `Retry-After` waits exactly that long (up to 60 seconds; a longer
87
+ wait is raised instead). A 429 that is a quota rather than throttling — the
88
+ monthly plan limit or the daily send cap, which carry an error code and no
89
+ `Retry-After` — is raised immediately, since retrying cannot lift it.
90
+
80
91
  ## Configuration
81
92
 
82
93
  ```python
83
94
  client = PostStack(
84
95
  api_key="sk_live_...",
85
96
  base_url="https://api.poststack.dev", # default
86
- timeout=30.0, # default (seconds)
97
+ timeout=30.0, # default (seconds), per attempt
98
+ max_retries=3, # default; 0 disables retrying
87
99
  )
88
100
  ```
89
101
 
@@ -95,19 +107,21 @@ client.emails.send({...}, timeout=60.0)
95
107
 
96
108
  ## Resources
97
109
 
98
- - `client.emails` — send, get, list, cancel, reschedule, batch, events, insights
99
- - `client.domains` — create, list, get, verify, update, delete, DMARC
100
- - `client.contacts` — CRUD, import, export, unsubscribe
101
- - `client.contact_properties` — custom properties
102
- - `client.segments` — create, list, get, update, delete, contacts, preview
103
- - `client.templates` — CRUD, publish, duplicate, presets
104
- - `client.webhooks` — CRUD, test, deliveries, replay
105
- - `client.broadcasts` — CRUD, send, test, variants
110
+ - `client.emails` — send, get, list, cancel, reschedule, batch, events, insights, preview, spam_preview
111
+ - `client.domains` — create, list, get, verify, update, delete, DMARC, DKIM rotation, inbox placement
112
+ - `client.contacts` — CRUD, import, export_csv (text; optional `segment_id`), unsubscribe
113
+ - `client.contact_properties` — custom properties, option_usage
114
+ - `client.segments` — create, list, get, update, delete, contacts, preview, members, growth, broadcasts
115
+ - `client.templates` — CRUD, publish, duplicate, render, presets
116
+ - `client.webhooks` — CRUD, test, deliveries, replay, batch_replay, rotate_secret
117
+ - `client.broadcasts` — CRUD, send, test, variants, resend, performance, non_openers, non_clickers, end_ab_test
106
118
  - `client.suppressions` — list, add, remove
107
- - `client.api_keys` — CRUD
108
- - `client.mailboxes` — CRUD, aliases, password
109
- - `client.subscription_topics` — topics + contact subscriptions
110
- - `client.workflows` — CRUD, steps, activate, pause, trigger
119
+ - `client.api_keys` — CRUD, rotate
120
+ - `client.mailboxes` — CRUD, aliases, password, filters, signature, shares, unread_summary
121
+ - `client.inbound_emails` — list, get, attachments, download, draft_reply, reply, forward
122
+ - `client.subscription_topics` — topics (get, update, list_subscribers) + contact subscriptions
123
+ - `client.workflows` — CRUD, graph (get_graph/put_graph/validate_graph), activate, pause, trigger, post_event
124
+ - `client.notification_channels` — Slack/Discord/Telegram alert channels: CRUD, test, deliveries, replay
111
125
  - `client.signup_forms` — CRUD, submit
112
126
  - `client.email_validations` — validate, validate_batch
113
127
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "poststack"
7
- version = "0.4.0"
7
+ version = "0.9.0"
8
8
  description = "Official Python SDK for the PostStack Email API"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -25,7 +25,8 @@ classifiers = [
25
25
  ]
26
26
  dependencies = [
27
27
  "httpx>=0.25.0",
28
- "pydantic>=2.0.0",
28
+ # 2.4.0 floor closes GHSA-mr82-8j83-vxmv (regex DoS in 2.0–2.3.x).
29
+ "pydantic>=2.4.0",
29
30
  ]
30
31
 
31
32
  [project.optional-dependencies]
@@ -43,6 +44,9 @@ Issues = "https://github.com/getpoststack/python-sdk/issues"
43
44
 
44
45
  [tool.hatch.build.targets.wheel]
45
46
  packages = ["src/poststack"]
47
+ # PEP 561: ship the `py.typed` marker so downstream type checkers (mypy,
48
+ # pyright) honor our annotations.
49
+ include = ["src/poststack/py.typed"]
46
50
 
47
51
  [tool.hatch.build.targets.sdist]
48
52
  include = [
@@ -10,10 +10,9 @@ Public API::
10
10
 
11
11
  from __future__ import annotations
12
12
 
13
- from typing import Optional
14
-
15
13
  from ._client import (
16
14
  DEFAULT_BASE_URL,
15
+ DEFAULT_MAX_RETRIES,
17
16
  AsyncPostStackClient,
18
17
  PostStackClient,
19
18
  )
@@ -27,7 +26,9 @@ from .resources import (
27
26
  AsyncDomainsResource,
28
27
  AsyncEmailsResource,
29
28
  AsyncEmailValidationsResource,
29
+ AsyncInboundEmailsResource,
30
30
  AsyncMailboxesResource,
31
+ AsyncNotificationChannelsResource,
31
32
  AsyncSegmentsResource,
32
33
  AsyncSignupFormsResource,
33
34
  AsyncSubscriptionTopicsResource,
@@ -41,7 +42,9 @@ from .resources import (
41
42
  DomainsResource,
42
43
  EmailsResource,
43
44
  EmailValidationsResource,
45
+ InboundEmailsResource,
44
46
  MailboxesResource,
47
+ NotificationChannelsResource,
45
48
  SegmentsResource,
46
49
  SignupFormsResource,
47
50
  SubscriptionTopicsResource,
@@ -51,7 +54,7 @@ from .resources import (
51
54
  WorkflowsResource,
52
55
  )
53
56
 
54
- __version__ = "0.4.0"
57
+ __version__ = "0.9.0"
55
58
 
56
59
  __all__ = [
57
60
  "PostStack",
@@ -81,9 +84,13 @@ class PostStack:
81
84
  api_key: str,
82
85
  base_url: str = DEFAULT_BASE_URL,
83
86
  timeout: float = 30.0,
87
+ max_retries: int = DEFAULT_MAX_RETRIES,
84
88
  ) -> None:
85
89
  self._client = PostStackClient(
86
- api_key=api_key, base_url=base_url, timeout=timeout
90
+ api_key=api_key,
91
+ base_url=base_url,
92
+ timeout=timeout,
93
+ max_retries=max_retries,
87
94
  )
88
95
  self.emails = EmailsResource(self._client)
89
96
  self.domains = DomainsResource(self._client)
@@ -96,10 +103,12 @@ class PostStack:
96
103
  self.suppressions = SuppressionsResource(self._client)
97
104
  self.api_keys = ApiKeysResource(self._client)
98
105
  self.mailboxes = MailboxesResource(self._client)
106
+ self.inbound_emails = InboundEmailsResource(self._client)
99
107
  self.subscription_topics = SubscriptionTopicsResource(self._client)
100
108
  self.workflows = WorkflowsResource(self._client)
101
109
  self.signup_forms = SignupFormsResource(self._client)
102
110
  self.email_validations = EmailValidationsResource(self._client)
111
+ self.notification_channels = NotificationChannelsResource(self._client)
103
112
 
104
113
  def close(self) -> None:
105
114
  self._client.close()
@@ -125,9 +134,13 @@ class AsyncPostStack:
125
134
  api_key: str,
126
135
  base_url: str = DEFAULT_BASE_URL,
127
136
  timeout: float = 30.0,
137
+ max_retries: int = DEFAULT_MAX_RETRIES,
128
138
  ) -> None:
129
139
  self._client = AsyncPostStackClient(
130
- api_key=api_key, base_url=base_url, timeout=timeout
140
+ api_key=api_key,
141
+ base_url=base_url,
142
+ timeout=timeout,
143
+ max_retries=max_retries,
131
144
  )
132
145
  self.emails = AsyncEmailsResource(self._client)
133
146
  self.domains = AsyncDomainsResource(self._client)
@@ -140,10 +153,14 @@ class AsyncPostStack:
140
153
  self.suppressions = AsyncSuppressionsResource(self._client)
141
154
  self.api_keys = AsyncApiKeysResource(self._client)
142
155
  self.mailboxes = AsyncMailboxesResource(self._client)
156
+ self.inbound_emails = AsyncInboundEmailsResource(self._client)
143
157
  self.subscription_topics = AsyncSubscriptionTopicsResource(self._client)
144
158
  self.workflows = AsyncWorkflowsResource(self._client)
145
159
  self.signup_forms = AsyncSignupFormsResource(self._client)
146
160
  self.email_validations = AsyncEmailValidationsResource(self._client)
161
+ self.notification_channels = AsyncNotificationChannelsResource(
162
+ self._client
163
+ )
147
164
 
148
165
  async def aclose(self) -> None:
149
166
  await self._client.aclose()
@@ -155,5 +172,3 @@ class AsyncPostStack:
155
172
  await self.aclose()
156
173
 
157
174
 
158
- # Re-export for advanced callers.
159
- _: Optional[type] = None # noqa: E305 - keeps ruff/mypy happy with Optional import