poststack 0.4.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.
@@ -0,0 +1,9 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *$py.class
4
+ .pytest_cache/
5
+ *.egg-info/
6
+ dist/
7
+ build/
8
+ .venv/
9
+ .env
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 MICCI (PostStack)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,155 @@
1
+ Metadata-Version: 2.4
2
+ Name: poststack
3
+ Version: 0.4.0
4
+ Summary: Official Python SDK for the PostStack Email API
5
+ Project-URL: Homepage, https://poststack.dev
6
+ Project-URL: Documentation, https://poststack.dev/docs
7
+ Project-URL: Repository, https://github.com/getpoststack/python-sdk
8
+ Project-URL: Issues, https://github.com/getpoststack/python-sdk/issues
9
+ Author: PostStack
10
+ License: MIT
11
+ License-File: LICENSE
12
+ Keywords: api,email,poststack,smtp,transactional
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Communications :: Email
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Requires-Python: >=3.10
24
+ Requires-Dist: httpx>=0.25.0
25
+ Requires-Dist: pydantic>=2.0.0
26
+ Provides-Extra: dev
27
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
28
+ Requires-Dist: pytest>=7.0.0; extra == 'dev'
29
+ Requires-Dist: respx>=0.20.0; extra == 'dev'
30
+ Description-Content-Type: text/markdown
31
+
32
+ # PostStack Python SDK
33
+
34
+ Official Python SDK for the [PostStack](https://poststack.dev) Email API —
35
+ an [EU-hosted, GDPR-compliant](https://poststack.dev/security)
36
+ [email API for Python](https://poststack.dev) with sync and async clients,
37
+ full type hints, and drop-in compatibility with Django, FastAPI, Flask,
38
+ and Starlette. Up to 75% cheaper than Resend, SendGrid, and Postmark.
39
+
40
+ Docs: [poststack.dev/docs/sdk](https://poststack.dev/docs/sdk).
41
+
42
+ ## Installation
43
+
44
+ ```bash
45
+ pip install poststack
46
+ ```
47
+
48
+ Requires Python 3.10+.
49
+
50
+ ## Quick start
51
+
52
+ ```python
53
+ from poststack import PostStack
54
+
55
+ client = PostStack(api_key="sk_live_...")
56
+
57
+ result = client.emails.send({
58
+ "from": "hello@example.com",
59
+ "to": ["user@example.com"],
60
+ "subject": "Hello from PostStack",
61
+ "html": "<p>Welcome!</p>",
62
+ })
63
+
64
+ print(result["id"])
65
+ ```
66
+
67
+ ## Async usage
68
+
69
+ ```python
70
+ import asyncio
71
+ from poststack import AsyncPostStack
72
+
73
+ async def main():
74
+ client = AsyncPostStack(api_key="sk_live_...")
75
+ try:
76
+ result = await client.emails.send({
77
+ "from": "hello@example.com",
78
+ "to": ["user@example.com"],
79
+ "subject": "Hello from PostStack",
80
+ "html": "<p>Welcome!</p>",
81
+ })
82
+ print(result["id"])
83
+ finally:
84
+ await client.aclose()
85
+
86
+ asyncio.run(main())
87
+ ```
88
+
89
+ Or use it as an async context manager:
90
+
91
+ ```python
92
+ async with AsyncPostStack(api_key="sk_live_...") as client:
93
+ result = await client.emails.send({...})
94
+ ```
95
+
96
+ ## Error handling
97
+
98
+ Any non-2xx response raises `PostStackError`:
99
+
100
+ ```python
101
+ from poststack import PostStack, PostStackError
102
+
103
+ client = PostStack(api_key="sk_live_...")
104
+
105
+ try:
106
+ client.emails.send({"from": "x@y.com", "to": ["z@w.com"], "subject": "hi"})
107
+ except PostStackError as e:
108
+ print(e.status_code, e.error, e.request_id)
109
+ ```
110
+
111
+ ## Configuration
112
+
113
+ ```python
114
+ client = PostStack(
115
+ api_key="sk_live_...",
116
+ base_url="https://api.poststack.dev", # default
117
+ timeout=30.0, # default (seconds)
118
+ )
119
+ ```
120
+
121
+ Override timeout per call:
122
+
123
+ ```python
124
+ client.emails.send({...}, timeout=60.0)
125
+ ```
126
+
127
+ ## Resources
128
+
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
137
+ - `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
142
+ - `client.signup_forms` — CRUD, submit
143
+ - `client.email_validations` — validate, validate_batch
144
+
145
+ ## Links
146
+
147
+ - [PostStack — EU-hosted email API](https://poststack.dev)
148
+ - [API documentation](https://poststack.dev/docs)
149
+ - [Pricing](https://poststack.dev/pricing) · [Compare providers](https://poststack.dev/pricing/compare)
150
+ - [Migrate from Resend](https://poststack.dev/migrate/resend) · [from SendGrid](https://poststack.dev/migrate/sendgrid) · [from Postmark](https://poststack.dev/migrate/postmark)
151
+ - [Status](https://poststack.dev/status) · [Security](https://poststack.dev/security) · [DPA](https://poststack.dev/dpa)
152
+
153
+ ## License
154
+
155
+ MIT — see [LICENSE](./LICENSE).
@@ -0,0 +1,124 @@
1
+ # PostStack Python SDK
2
+
3
+ Official Python SDK for the [PostStack](https://poststack.dev) Email API —
4
+ an [EU-hosted, GDPR-compliant](https://poststack.dev/security)
5
+ [email API for Python](https://poststack.dev) with sync and async clients,
6
+ full type hints, and drop-in compatibility with Django, FastAPI, Flask,
7
+ and Starlette. Up to 75% cheaper than Resend, SendGrid, and Postmark.
8
+
9
+ Docs: [poststack.dev/docs/sdk](https://poststack.dev/docs/sdk).
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ pip install poststack
15
+ ```
16
+
17
+ Requires Python 3.10+.
18
+
19
+ ## Quick start
20
+
21
+ ```python
22
+ from poststack import PostStack
23
+
24
+ client = PostStack(api_key="sk_live_...")
25
+
26
+ result = client.emails.send({
27
+ "from": "hello@example.com",
28
+ "to": ["user@example.com"],
29
+ "subject": "Hello from PostStack",
30
+ "html": "<p>Welcome!</p>",
31
+ })
32
+
33
+ print(result["id"])
34
+ ```
35
+
36
+ ## Async usage
37
+
38
+ ```python
39
+ import asyncio
40
+ from poststack import AsyncPostStack
41
+
42
+ async def main():
43
+ client = AsyncPostStack(api_key="sk_live_...")
44
+ try:
45
+ result = await client.emails.send({
46
+ "from": "hello@example.com",
47
+ "to": ["user@example.com"],
48
+ "subject": "Hello from PostStack",
49
+ "html": "<p>Welcome!</p>",
50
+ })
51
+ print(result["id"])
52
+ finally:
53
+ await client.aclose()
54
+
55
+ asyncio.run(main())
56
+ ```
57
+
58
+ Or use it as an async context manager:
59
+
60
+ ```python
61
+ async with AsyncPostStack(api_key="sk_live_...") as client:
62
+ result = await client.emails.send({...})
63
+ ```
64
+
65
+ ## Error handling
66
+
67
+ Any non-2xx response raises `PostStackError`:
68
+
69
+ ```python
70
+ from poststack import PostStack, PostStackError
71
+
72
+ client = PostStack(api_key="sk_live_...")
73
+
74
+ try:
75
+ client.emails.send({"from": "x@y.com", "to": ["z@w.com"], "subject": "hi"})
76
+ except PostStackError as e:
77
+ print(e.status_code, e.error, e.request_id)
78
+ ```
79
+
80
+ ## Configuration
81
+
82
+ ```python
83
+ client = PostStack(
84
+ api_key="sk_live_...",
85
+ base_url="https://api.poststack.dev", # default
86
+ timeout=30.0, # default (seconds)
87
+ )
88
+ ```
89
+
90
+ Override timeout per call:
91
+
92
+ ```python
93
+ client.emails.send({...}, timeout=60.0)
94
+ ```
95
+
96
+ ## Resources
97
+
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
106
+ - `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
111
+ - `client.signup_forms` — CRUD, submit
112
+ - `client.email_validations` — validate, validate_batch
113
+
114
+ ## Links
115
+
116
+ - [PostStack — EU-hosted email API](https://poststack.dev)
117
+ - [API documentation](https://poststack.dev/docs)
118
+ - [Pricing](https://poststack.dev/pricing) · [Compare providers](https://poststack.dev/pricing/compare)
119
+ - [Migrate from Resend](https://poststack.dev/migrate/resend) · [from SendGrid](https://poststack.dev/migrate/sendgrid) · [from Postmark](https://poststack.dev/migrate/postmark)
120
+ - [Status](https://poststack.dev/status) · [Security](https://poststack.dev/security) · [DPA](https://poststack.dev/dpa)
121
+
122
+ ## License
123
+
124
+ MIT — see [LICENSE](./LICENSE).
@@ -0,0 +1,58 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "poststack"
7
+ version = "0.4.0"
8
+ description = "Official Python SDK for the PostStack Email API"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "PostStack" }]
13
+ keywords = ["email", "api", "poststack", "smtp", "transactional"]
14
+ classifiers = [
15
+ "Development Status :: 4 - Beta",
16
+ "Intended Audience :: Developers",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3.10",
20
+ "Programming Language :: Python :: 3.11",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Programming Language :: Python :: 3.13",
23
+ "Topic :: Communications :: Email",
24
+ "Topic :: Software Development :: Libraries :: Python Modules",
25
+ ]
26
+ dependencies = [
27
+ "httpx>=0.25.0",
28
+ "pydantic>=2.0.0",
29
+ ]
30
+
31
+ [project.optional-dependencies]
32
+ dev = [
33
+ "pytest>=7.0.0",
34
+ "pytest-asyncio>=0.21.0",
35
+ "respx>=0.20.0",
36
+ ]
37
+
38
+ [project.urls]
39
+ Homepage = "https://poststack.dev"
40
+ Documentation = "https://poststack.dev/docs"
41
+ Repository = "https://github.com/getpoststack/python-sdk"
42
+ Issues = "https://github.com/getpoststack/python-sdk/issues"
43
+
44
+ [tool.hatch.build.targets.wheel]
45
+ packages = ["src/poststack"]
46
+
47
+ [tool.hatch.build.targets.sdist]
48
+ include = [
49
+ "/src",
50
+ "/tests",
51
+ "/README.md",
52
+ "/LICENSE",
53
+ "/pyproject.toml",
54
+ ]
55
+
56
+ [tool.pytest.ini_options]
57
+ testpaths = ["tests"]
58
+ asyncio_mode = "auto"
@@ -0,0 +1,159 @@
1
+ """PostStack Python SDK.
2
+
3
+ Public API::
4
+
5
+ from poststack import PostStack, AsyncPostStack, PostStackError
6
+
7
+ client = PostStack(api_key="sk_live_...")
8
+ client.emails.send({"from": "a@b.com", "to": ["c@d.com"], "subject": "hi"})
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from typing import Optional
14
+
15
+ from ._client import (
16
+ DEFAULT_BASE_URL,
17
+ AsyncPostStackClient,
18
+ PostStackClient,
19
+ )
20
+ from ._errors import APIError, PostStackError
21
+ from .resources import (
22
+ ApiKeysResource,
23
+ AsyncApiKeysResource,
24
+ AsyncBroadcastsResource,
25
+ AsyncContactPropertiesResource,
26
+ AsyncContactsResource,
27
+ AsyncDomainsResource,
28
+ AsyncEmailsResource,
29
+ AsyncEmailValidationsResource,
30
+ AsyncMailboxesResource,
31
+ AsyncSegmentsResource,
32
+ AsyncSignupFormsResource,
33
+ AsyncSubscriptionTopicsResource,
34
+ AsyncSuppressionsResource,
35
+ AsyncTemplatesResource,
36
+ AsyncWebhooksResource,
37
+ AsyncWorkflowsResource,
38
+ BroadcastsResource,
39
+ ContactPropertiesResource,
40
+ ContactsResource,
41
+ DomainsResource,
42
+ EmailsResource,
43
+ EmailValidationsResource,
44
+ MailboxesResource,
45
+ SegmentsResource,
46
+ SignupFormsResource,
47
+ SubscriptionTopicsResource,
48
+ SuppressionsResource,
49
+ TemplatesResource,
50
+ WebhooksResource,
51
+ WorkflowsResource,
52
+ )
53
+
54
+ __version__ = "0.4.0"
55
+
56
+ __all__ = [
57
+ "PostStack",
58
+ "AsyncPostStack",
59
+ "PostStackError",
60
+ "APIError",
61
+ "__version__",
62
+ ]
63
+
64
+
65
+ class PostStack:
66
+ """Synchronous PostStack client.
67
+
68
+ Example::
69
+
70
+ client = PostStack(api_key="sk_live_...")
71
+ client.emails.send({
72
+ "from": "hello@example.com",
73
+ "to": ["user@example.com"],
74
+ "subject": "Hello",
75
+ "html": "<p>Hi</p>",
76
+ })
77
+ """
78
+
79
+ def __init__(
80
+ self,
81
+ api_key: str,
82
+ base_url: str = DEFAULT_BASE_URL,
83
+ timeout: float = 30.0,
84
+ ) -> None:
85
+ self._client = PostStackClient(
86
+ api_key=api_key, base_url=base_url, timeout=timeout
87
+ )
88
+ self.emails = EmailsResource(self._client)
89
+ self.domains = DomainsResource(self._client)
90
+ self.contacts = ContactsResource(self._client)
91
+ self.contact_properties = ContactPropertiesResource(self._client)
92
+ self.segments = SegmentsResource(self._client)
93
+ self.templates = TemplatesResource(self._client)
94
+ self.webhooks = WebhooksResource(self._client)
95
+ self.broadcasts = BroadcastsResource(self._client)
96
+ self.suppressions = SuppressionsResource(self._client)
97
+ self.api_keys = ApiKeysResource(self._client)
98
+ self.mailboxes = MailboxesResource(self._client)
99
+ self.subscription_topics = SubscriptionTopicsResource(self._client)
100
+ self.workflows = WorkflowsResource(self._client)
101
+ self.signup_forms = SignupFormsResource(self._client)
102
+ self.email_validations = EmailValidationsResource(self._client)
103
+
104
+ def close(self) -> None:
105
+ self._client.close()
106
+
107
+ def __enter__(self) -> "PostStack":
108
+ return self
109
+
110
+ def __exit__(self, *args: object) -> None:
111
+ self.close()
112
+
113
+
114
+ class AsyncPostStack:
115
+ """Asynchronous PostStack client.
116
+
117
+ Example::
118
+
119
+ async with AsyncPostStack(api_key="sk_live_...") as client:
120
+ await client.emails.send({...})
121
+ """
122
+
123
+ def __init__(
124
+ self,
125
+ api_key: str,
126
+ base_url: str = DEFAULT_BASE_URL,
127
+ timeout: float = 30.0,
128
+ ) -> None:
129
+ self._client = AsyncPostStackClient(
130
+ api_key=api_key, base_url=base_url, timeout=timeout
131
+ )
132
+ self.emails = AsyncEmailsResource(self._client)
133
+ self.domains = AsyncDomainsResource(self._client)
134
+ self.contacts = AsyncContactsResource(self._client)
135
+ self.contact_properties = AsyncContactPropertiesResource(self._client)
136
+ self.segments = AsyncSegmentsResource(self._client)
137
+ self.templates = AsyncTemplatesResource(self._client)
138
+ self.webhooks = AsyncWebhooksResource(self._client)
139
+ self.broadcasts = AsyncBroadcastsResource(self._client)
140
+ self.suppressions = AsyncSuppressionsResource(self._client)
141
+ self.api_keys = AsyncApiKeysResource(self._client)
142
+ self.mailboxes = AsyncMailboxesResource(self._client)
143
+ self.subscription_topics = AsyncSubscriptionTopicsResource(self._client)
144
+ self.workflows = AsyncWorkflowsResource(self._client)
145
+ self.signup_forms = AsyncSignupFormsResource(self._client)
146
+ self.email_validations = AsyncEmailValidationsResource(self._client)
147
+
148
+ async def aclose(self) -> None:
149
+ await self._client.aclose()
150
+
151
+ async def __aenter__(self) -> "AsyncPostStack":
152
+ return self
153
+
154
+ async def __aexit__(self, *args: object) -> None:
155
+ await self.aclose()
156
+
157
+
158
+ # Re-export for advanced callers.
159
+ _: Optional[type] = None # noqa: E305 - keeps ruff/mypy happy with Optional import