postiee 0.1.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.
postiee-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Nomadic Influence
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,2 @@
1
+ include README.md
2
+ include LICENSE
postiee-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,209 @@
1
+ Metadata-Version: 2.4
2
+ Name: postiee
3
+ Version: 0.1.0
4
+ Summary: A minimal Python client for the Postmark API
5
+ Author-email: Nomadic Influence <support@nomadicinfluence.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/roddy-devs/postie
8
+ Project-URL: Repository, https://github.com/roddy-devs/postie
9
+ Project-URL: Issues, https://github.com/roddy-devs/postie/issues
10
+ Keywords: postmark,email,api,transactional
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.7
16
+ Classifier: Programming Language :: Python :: 3.8
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Topic :: Communications :: Email
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Requires-Python: >=3.7
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Requires-Dist: requests>=2.25.0
27
+ Dynamic: license-file
28
+
29
+ # Postie
30
+
31
+ A minimal Python client for the Postmark API.
32
+
33
+ ## Installation
34
+
35
+ ```bash
36
+ pip install postie
37
+ ```
38
+
39
+ ## Quick Start
40
+
41
+ ```python
42
+ from postie import PostieClient
43
+
44
+ # Initialize client
45
+ client = PostieClient(server_token="your-postmark-server-token")
46
+
47
+ # Send a simple email
48
+ response = client.send_email(
49
+ from_email="support@nomadicinfluence.com",
50
+ to="user@example.com",
51
+ subject="Hello from Postie",
52
+ html_body="<h1>Hello!</h1><p>This is a test email.</p>",
53
+ text_body="Hello! This is a test email."
54
+ )
55
+
56
+ print(response)
57
+ ```
58
+
59
+ ## Features
60
+
61
+ - ✅ Send single emails
62
+ - ✅ Send batch emails (up to 500)
63
+ - ✅ Send template-based emails
64
+ - ✅ HTML and plain text support
65
+ - ✅ CC, BCC, Reply-To
66
+ - ✅ Email tagging and metadata
67
+ - ✅ Open and link tracking
68
+ - ✅ Type hints
69
+ - ✅ Minimal dependencies (only `requests`)
70
+
71
+ ## Usage
72
+
73
+ ### Send Email
74
+
75
+ ```python
76
+ client.send_email(
77
+ from_email="support@nomadicinfluence.com",
78
+ to="user@example.com",
79
+ subject="Welcome!",
80
+ html_body="<h1>Welcome to our platform</h1>",
81
+ text_body="Welcome to our platform",
82
+ tag="onboarding",
83
+ track_opens=True
84
+ )
85
+ ```
86
+
87
+ ### Send Batch Emails
88
+
89
+ ```python
90
+ emails = [
91
+ {
92
+ "From": "support@nomadicinfluence.com",
93
+ "To": "user1@example.com",
94
+ "Subject": "Hello User 1",
95
+ "HtmlBody": "<p>Hello User 1</p>"
96
+ },
97
+ {
98
+ "From": "support@nomadicinfluence.com",
99
+ "To": "user2@example.com",
100
+ "Subject": "Hello User 2",
101
+ "HtmlBody": "<p>Hello User 2</p>"
102
+ }
103
+ ]
104
+
105
+ responses = client.send_batch(emails)
106
+ ```
107
+
108
+ ### Send Template Email
109
+
110
+ ```python
111
+ client.send_template_email(
112
+ from_email="support@nomadicinfluence.com",
113
+ to="user@example.com",
114
+ template_id=123456,
115
+ template_model={
116
+ "name": "John Doe",
117
+ "action_url": "https://example.com/verify"
118
+ }
119
+ )
120
+ ```
121
+
122
+ ## Django Integration
123
+
124
+ ### Option 1: Use Django Email Backend (Recommended)
125
+
126
+ Configure Postie as your Django email backend:
127
+
128
+ ```python
129
+ # settings.py
130
+ EMAIL_BACKEND = 'postie.django_backend.PostieEmailBackend'
131
+ POSTMARK_SERVER_TOKEN = os.getenv('POSTMARK_SERVER_TOKEN')
132
+ DEFAULT_FROM_EMAIL = 'support@nomadicinfluence.com'
133
+ ```
134
+
135
+ Then use Django's standard email functions:
136
+
137
+ ```python
138
+ from django.core.mail import send_mail, EmailMultiAlternatives
139
+
140
+ # Simple email
141
+ send_mail(
142
+ subject='Welcome!',
143
+ message='Welcome to our platform',
144
+ from_email='support@nomadicinfluence.com',
145
+ recipient_list=['user@example.com'],
146
+ )
147
+
148
+ # HTML email
149
+ email = EmailMultiAlternatives(
150
+ subject='Welcome!',
151
+ body='Welcome to our platform',
152
+ from_email='support@nomadicinfluence.com',
153
+ to=['user@example.com'],
154
+ )
155
+ email.attach_alternative('<h1>Welcome!</h1>', "text/html")
156
+ email.send()
157
+ ```
158
+
159
+ ### Option 2: Use PostieClient Directly
160
+
161
+ ```python
162
+ # utils/email.py
163
+ from postie import PostieClient
164
+ from django.conf import settings
165
+
166
+ postmark = PostieClient(server_token=settings.POSTMARK_SERVER_TOKEN)
167
+
168
+ def send_welcome_email(user_email, user_name):
169
+ postmark.send_email(
170
+ from_email="support@nomadicinfluence.com",
171
+ to=user_email,
172
+ subject=f"Welcome {user_name}!",
173
+ html_body=f"<h1>Welcome {user_name}!</h1>",
174
+ text_body=f"Welcome {user_name}!"
175
+ )
176
+ ```
177
+
178
+ ## Error Handling
179
+
180
+ ```python
181
+ from postie import PostieClient, PostieAPIError, PostieError
182
+
183
+ try:
184
+ client.send_email(...)
185
+ except PostieAPIError as e:
186
+ print(f"API Error: {e}")
187
+ print(f"Error Code: {e.error_code}")
188
+ print(f"Status Code: {e.status_code}")
189
+ except PostieError as e:
190
+ print(f"Client Error: {e}")
191
+ ```
192
+
193
+ ## Requirements
194
+
195
+ - Python 3.7+
196
+ - requests
197
+
198
+ ## License
199
+
200
+ MIT
201
+
202
+ ## Contributing
203
+
204
+ Contributions welcome! Please open an issue or PR.
205
+
206
+ ## Links
207
+
208
+ - [Postmark API Documentation](https://postmarkapp.com/developer)
209
+ - [GitHub Repository](https://github.com/roddy-devs/postie)
@@ -0,0 +1,181 @@
1
+ # Postie
2
+
3
+ A minimal Python client for the Postmark API.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install postie
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```python
14
+ from postie import PostieClient
15
+
16
+ # Initialize client
17
+ client = PostieClient(server_token="your-postmark-server-token")
18
+
19
+ # Send a simple email
20
+ response = client.send_email(
21
+ from_email="support@nomadicinfluence.com",
22
+ to="user@example.com",
23
+ subject="Hello from Postie",
24
+ html_body="<h1>Hello!</h1><p>This is a test email.</p>",
25
+ text_body="Hello! This is a test email."
26
+ )
27
+
28
+ print(response)
29
+ ```
30
+
31
+ ## Features
32
+
33
+ - ✅ Send single emails
34
+ - ✅ Send batch emails (up to 500)
35
+ - ✅ Send template-based emails
36
+ - ✅ HTML and plain text support
37
+ - ✅ CC, BCC, Reply-To
38
+ - ✅ Email tagging and metadata
39
+ - ✅ Open and link tracking
40
+ - ✅ Type hints
41
+ - ✅ Minimal dependencies (only `requests`)
42
+
43
+ ## Usage
44
+
45
+ ### Send Email
46
+
47
+ ```python
48
+ client.send_email(
49
+ from_email="support@nomadicinfluence.com",
50
+ to="user@example.com",
51
+ subject="Welcome!",
52
+ html_body="<h1>Welcome to our platform</h1>",
53
+ text_body="Welcome to our platform",
54
+ tag="onboarding",
55
+ track_opens=True
56
+ )
57
+ ```
58
+
59
+ ### Send Batch Emails
60
+
61
+ ```python
62
+ emails = [
63
+ {
64
+ "From": "support@nomadicinfluence.com",
65
+ "To": "user1@example.com",
66
+ "Subject": "Hello User 1",
67
+ "HtmlBody": "<p>Hello User 1</p>"
68
+ },
69
+ {
70
+ "From": "support@nomadicinfluence.com",
71
+ "To": "user2@example.com",
72
+ "Subject": "Hello User 2",
73
+ "HtmlBody": "<p>Hello User 2</p>"
74
+ }
75
+ ]
76
+
77
+ responses = client.send_batch(emails)
78
+ ```
79
+
80
+ ### Send Template Email
81
+
82
+ ```python
83
+ client.send_template_email(
84
+ from_email="support@nomadicinfluence.com",
85
+ to="user@example.com",
86
+ template_id=123456,
87
+ template_model={
88
+ "name": "John Doe",
89
+ "action_url": "https://example.com/verify"
90
+ }
91
+ )
92
+ ```
93
+
94
+ ## Django Integration
95
+
96
+ ### Option 1: Use Django Email Backend (Recommended)
97
+
98
+ Configure Postie as your Django email backend:
99
+
100
+ ```python
101
+ # settings.py
102
+ EMAIL_BACKEND = 'postie.django_backend.PostieEmailBackend'
103
+ POSTMARK_SERVER_TOKEN = os.getenv('POSTMARK_SERVER_TOKEN')
104
+ DEFAULT_FROM_EMAIL = 'support@nomadicinfluence.com'
105
+ ```
106
+
107
+ Then use Django's standard email functions:
108
+
109
+ ```python
110
+ from django.core.mail import send_mail, EmailMultiAlternatives
111
+
112
+ # Simple email
113
+ send_mail(
114
+ subject='Welcome!',
115
+ message='Welcome to our platform',
116
+ from_email='support@nomadicinfluence.com',
117
+ recipient_list=['user@example.com'],
118
+ )
119
+
120
+ # HTML email
121
+ email = EmailMultiAlternatives(
122
+ subject='Welcome!',
123
+ body='Welcome to our platform',
124
+ from_email='support@nomadicinfluence.com',
125
+ to=['user@example.com'],
126
+ )
127
+ email.attach_alternative('<h1>Welcome!</h1>', "text/html")
128
+ email.send()
129
+ ```
130
+
131
+ ### Option 2: Use PostieClient Directly
132
+
133
+ ```python
134
+ # utils/email.py
135
+ from postie import PostieClient
136
+ from django.conf import settings
137
+
138
+ postmark = PostieClient(server_token=settings.POSTMARK_SERVER_TOKEN)
139
+
140
+ def send_welcome_email(user_email, user_name):
141
+ postmark.send_email(
142
+ from_email="support@nomadicinfluence.com",
143
+ to=user_email,
144
+ subject=f"Welcome {user_name}!",
145
+ html_body=f"<h1>Welcome {user_name}!</h1>",
146
+ text_body=f"Welcome {user_name}!"
147
+ )
148
+ ```
149
+
150
+ ## Error Handling
151
+
152
+ ```python
153
+ from postie import PostieClient, PostieAPIError, PostieError
154
+
155
+ try:
156
+ client.send_email(...)
157
+ except PostieAPIError as e:
158
+ print(f"API Error: {e}")
159
+ print(f"Error Code: {e.error_code}")
160
+ print(f"Status Code: {e.status_code}")
161
+ except PostieError as e:
162
+ print(f"Client Error: {e}")
163
+ ```
164
+
165
+ ## Requirements
166
+
167
+ - Python 3.7+
168
+ - requests
169
+
170
+ ## License
171
+
172
+ MIT
173
+
174
+ ## Contributing
175
+
176
+ Contributions welcome! Please open an issue or PR.
177
+
178
+ ## Links
179
+
180
+ - [Postmark API Documentation](https://postmarkapp.com/developer)
181
+ - [GitHub Repository](https://github.com/roddy-devs/postie)
@@ -0,0 +1,10 @@
1
+ """Postie - A minimal Python client for Postmark API"""
2
+
3
+ from .client import PostieClient
4
+ from .exceptions import PostieError, PostieAPIError
5
+
6
+ __version__ = "0.1.0"
7
+ __all__ = ["PostieClient", "PostieError", "PostieAPIError"]
8
+
9
+ # Django backend is imported separately to avoid Django dependency
10
+ # from postie.django_backend import PostieEmailBackend
@@ -0,0 +1,196 @@
1
+ """Postie client for Postmark API"""
2
+
3
+ import requests
4
+ from typing import Optional, Dict, Any, List
5
+ from .exceptions import PostieAPIError, PostieError
6
+
7
+
8
+ class PostieClient:
9
+ """Minimal client for Postmark API"""
10
+
11
+ BASE_URL = "https://api.postmarkapp.com"
12
+
13
+ def __init__(self, server_token: str, timeout: int = 30):
14
+ """
15
+ Initialize Postie client
16
+
17
+ Args:
18
+ server_token: Postmark server API token
19
+ timeout: Request timeout in seconds (default: 30)
20
+ """
21
+ if not server_token:
22
+ raise PostieError("server_token is required")
23
+
24
+ self.server_token = server_token
25
+ self.timeout = timeout
26
+ self.session = requests.Session()
27
+ self.session.headers.update({
28
+ "Accept": "application/json",
29
+ "Content-Type": "application/json",
30
+ "X-Postmark-Server-Token": server_token
31
+ })
32
+
33
+ def _request(self, method: str, endpoint: str, data: Optional[Dict] = None) -> Dict[str, Any]:
34
+ """Make API request"""
35
+ url = f"{self.BASE_URL}{endpoint}"
36
+
37
+ try:
38
+ response = self.session.request(
39
+ method=method,
40
+ url=url,
41
+ json=data,
42
+ timeout=self.timeout
43
+ )
44
+
45
+ if response.status_code >= 400:
46
+ error_data = response.json() if response.content else {}
47
+ raise PostieAPIError(
48
+ message=error_data.get("Message", "Unknown error"),
49
+ error_code=error_data.get("ErrorCode"),
50
+ status_code=response.status_code
51
+ )
52
+
53
+ return response.json()
54
+
55
+ except requests.RequestException as e:
56
+ raise PostieError(f"Request failed: {str(e)}")
57
+
58
+ def send_email(
59
+ self,
60
+ from_email: str,
61
+ to: str,
62
+ subject: str,
63
+ html_body: Optional[str] = None,
64
+ text_body: Optional[str] = None,
65
+ cc: Optional[str] = None,
66
+ bcc: Optional[str] = None,
67
+ reply_to: Optional[str] = None,
68
+ tag: Optional[str] = None,
69
+ metadata: Optional[Dict[str, str]] = None,
70
+ track_opens: Optional[bool] = None,
71
+ track_links: Optional[str] = None,
72
+ ) -> Dict[str, Any]:
73
+ """
74
+ Send a single email
75
+
76
+ Args:
77
+ from_email: Sender email address
78
+ to: Recipient email address
79
+ subject: Email subject
80
+ html_body: HTML email body
81
+ text_body: Plain text email body
82
+ cc: CC recipients (comma-separated)
83
+ bcc: BCC recipients (comma-separated)
84
+ reply_to: Reply-To address
85
+ tag: Tag for categorizing emails
86
+ metadata: Custom metadata dictionary
87
+ track_opens: Enable open tracking
88
+ track_links: Link tracking ("None", "HtmlAndText", "HtmlOnly", "TextOnly")
89
+
90
+ Returns:
91
+ API response dictionary
92
+ """
93
+ if not html_body and not text_body:
94
+ raise PostieError("Either html_body or text_body is required")
95
+
96
+ payload = {
97
+ "From": from_email,
98
+ "To": to,
99
+ "Subject": subject,
100
+ }
101
+
102
+ if html_body:
103
+ payload["HtmlBody"] = html_body
104
+ if text_body:
105
+ payload["TextBody"] = text_body
106
+ if cc:
107
+ payload["Cc"] = cc
108
+ if bcc:
109
+ payload["Bcc"] = bcc
110
+ if reply_to:
111
+ payload["ReplyTo"] = reply_to
112
+ if tag:
113
+ payload["Tag"] = tag
114
+ if metadata:
115
+ payload["Metadata"] = metadata
116
+ if track_opens is not None:
117
+ payload["TrackOpens"] = track_opens
118
+ if track_links:
119
+ payload["TrackLinks"] = track_links
120
+
121
+ return self._request("POST", "/email", payload)
122
+
123
+ def send_batch(self, emails: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
124
+ """
125
+ Send multiple emails in a batch
126
+
127
+ Args:
128
+ emails: List of email dictionaries (same format as send_email args)
129
+
130
+ Returns:
131
+ List of API response dictionaries
132
+ """
133
+ if not emails:
134
+ raise PostieError("emails list cannot be empty")
135
+
136
+ if len(emails) > 500:
137
+ raise PostieError("Maximum 500 emails per batch")
138
+
139
+ return self._request("POST", "/email/batch", emails)
140
+
141
+ def send_template_email(
142
+ self,
143
+ from_email: str,
144
+ to: str,
145
+ template_id: int,
146
+ template_model: Dict[str, Any],
147
+ cc: Optional[str] = None,
148
+ bcc: Optional[str] = None,
149
+ reply_to: Optional[str] = None,
150
+ tag: Optional[str] = None,
151
+ metadata: Optional[Dict[str, str]] = None,
152
+ track_opens: Optional[bool] = None,
153
+ track_links: Optional[str] = None,
154
+ ) -> Dict[str, Any]:
155
+ """
156
+ Send email using a Postmark template
157
+
158
+ Args:
159
+ from_email: Sender email address
160
+ to: Recipient email address
161
+ template_id: Postmark template ID
162
+ template_model: Template variables dictionary
163
+ cc: CC recipients (comma-separated)
164
+ bcc: BCC recipients (comma-separated)
165
+ reply_to: Reply-To address
166
+ tag: Tag for categorizing emails
167
+ metadata: Custom metadata dictionary
168
+ track_opens: Enable open tracking
169
+ track_links: Link tracking ("None", "HtmlAndText", "HtmlOnly", "TextOnly")
170
+
171
+ Returns:
172
+ API response dictionary
173
+ """
174
+ payload = {
175
+ "From": from_email,
176
+ "To": to,
177
+ "TemplateId": template_id,
178
+ "TemplateModel": template_model,
179
+ }
180
+
181
+ if cc:
182
+ payload["Cc"] = cc
183
+ if bcc:
184
+ payload["Bcc"] = bcc
185
+ if reply_to:
186
+ payload["ReplyTo"] = reply_to
187
+ if tag:
188
+ payload["Tag"] = tag
189
+ if metadata:
190
+ payload["Metadata"] = metadata
191
+ if track_opens is not None:
192
+ payload["TrackOpens"] = track_opens
193
+ if track_links:
194
+ payload["TrackLinks"] = track_links
195
+
196
+ return self._request("POST", "/email/withTemplate", payload)
@@ -0,0 +1,64 @@
1
+ """Django email backend for Postie"""
2
+
3
+ from django.conf import settings
4
+ from django.core.mail.backends.base import BaseEmailBackend
5
+ from django.core.mail import EmailMultiAlternatives
6
+ from .client import PostieClient
7
+ from .exceptions import PostieError
8
+
9
+
10
+ class PostieEmailBackend(BaseEmailBackend):
11
+ """Django email backend using Postie/Postmark"""
12
+
13
+ def __init__(self, fail_silently=False, **kwargs):
14
+ super().__init__(fail_silently=fail_silently, **kwargs)
15
+
16
+ server_token = getattr(settings, 'POSTMARK_SERVER_TOKEN', None)
17
+ if not server_token:
18
+ raise PostieError("POSTMARK_SERVER_TOKEN setting is required")
19
+
20
+ self.client = PostieClient(server_token=server_token)
21
+
22
+ def send_messages(self, email_messages):
23
+ """Send one or more EmailMessage objects"""
24
+ if not email_messages:
25
+ return 0
26
+
27
+ sent_count = 0
28
+
29
+ for message in email_messages:
30
+ try:
31
+ self._send(message)
32
+ sent_count += 1
33
+ except Exception as e:
34
+ if not self.fail_silently:
35
+ raise
36
+
37
+ return sent_count
38
+
39
+ def _send(self, message):
40
+ """Send a single email message"""
41
+ # Get HTML body if available
42
+ html_body = None
43
+ if isinstance(message, EmailMultiAlternatives):
44
+ for content, mimetype in message.alternatives:
45
+ if mimetype == 'text/html':
46
+ html_body = content
47
+ break
48
+
49
+ # Prepare recipients
50
+ to = ', '.join(message.to) if message.to else None
51
+ cc = ', '.join(message.cc) if message.cc else None
52
+ bcc = ', '.join(message.bcc) if message.bcc else None
53
+
54
+ # Send via Postie
55
+ self.client.send_email(
56
+ from_email=message.from_email,
57
+ to=to,
58
+ subject=message.subject,
59
+ html_body=html_body,
60
+ text_body=message.body if message.body else None,
61
+ cc=cc,
62
+ bcc=bcc,
63
+ reply_to=message.reply_to[0] if message.reply_to else None,
64
+ )
@@ -0,0 +1,15 @@
1
+ """Postie exceptions"""
2
+
3
+
4
+ class PostieError(Exception):
5
+ """Base exception for Postie"""
6
+ pass
7
+
8
+
9
+ class PostieAPIError(PostieError):
10
+ """Raised when Postmark API returns an error"""
11
+
12
+ def __init__(self, message, error_code=None, status_code=None):
13
+ super().__init__(message)
14
+ self.error_code = error_code
15
+ self.status_code = status_code
@@ -0,0 +1,209 @@
1
+ Metadata-Version: 2.4
2
+ Name: postiee
3
+ Version: 0.1.0
4
+ Summary: A minimal Python client for the Postmark API
5
+ Author-email: Nomadic Influence <support@nomadicinfluence.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/roddy-devs/postie
8
+ Project-URL: Repository, https://github.com/roddy-devs/postie
9
+ Project-URL: Issues, https://github.com/roddy-devs/postie/issues
10
+ Keywords: postmark,email,api,transactional
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.7
16
+ Classifier: Programming Language :: Python :: 3.8
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Topic :: Communications :: Email
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Requires-Python: >=3.7
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Requires-Dist: requests>=2.25.0
27
+ Dynamic: license-file
28
+
29
+ # Postie
30
+
31
+ A minimal Python client for the Postmark API.
32
+
33
+ ## Installation
34
+
35
+ ```bash
36
+ pip install postie
37
+ ```
38
+
39
+ ## Quick Start
40
+
41
+ ```python
42
+ from postie import PostieClient
43
+
44
+ # Initialize client
45
+ client = PostieClient(server_token="your-postmark-server-token")
46
+
47
+ # Send a simple email
48
+ response = client.send_email(
49
+ from_email="support@nomadicinfluence.com",
50
+ to="user@example.com",
51
+ subject="Hello from Postie",
52
+ html_body="<h1>Hello!</h1><p>This is a test email.</p>",
53
+ text_body="Hello! This is a test email."
54
+ )
55
+
56
+ print(response)
57
+ ```
58
+
59
+ ## Features
60
+
61
+ - ✅ Send single emails
62
+ - ✅ Send batch emails (up to 500)
63
+ - ✅ Send template-based emails
64
+ - ✅ HTML and plain text support
65
+ - ✅ CC, BCC, Reply-To
66
+ - ✅ Email tagging and metadata
67
+ - ✅ Open and link tracking
68
+ - ✅ Type hints
69
+ - ✅ Minimal dependencies (only `requests`)
70
+
71
+ ## Usage
72
+
73
+ ### Send Email
74
+
75
+ ```python
76
+ client.send_email(
77
+ from_email="support@nomadicinfluence.com",
78
+ to="user@example.com",
79
+ subject="Welcome!",
80
+ html_body="<h1>Welcome to our platform</h1>",
81
+ text_body="Welcome to our platform",
82
+ tag="onboarding",
83
+ track_opens=True
84
+ )
85
+ ```
86
+
87
+ ### Send Batch Emails
88
+
89
+ ```python
90
+ emails = [
91
+ {
92
+ "From": "support@nomadicinfluence.com",
93
+ "To": "user1@example.com",
94
+ "Subject": "Hello User 1",
95
+ "HtmlBody": "<p>Hello User 1</p>"
96
+ },
97
+ {
98
+ "From": "support@nomadicinfluence.com",
99
+ "To": "user2@example.com",
100
+ "Subject": "Hello User 2",
101
+ "HtmlBody": "<p>Hello User 2</p>"
102
+ }
103
+ ]
104
+
105
+ responses = client.send_batch(emails)
106
+ ```
107
+
108
+ ### Send Template Email
109
+
110
+ ```python
111
+ client.send_template_email(
112
+ from_email="support@nomadicinfluence.com",
113
+ to="user@example.com",
114
+ template_id=123456,
115
+ template_model={
116
+ "name": "John Doe",
117
+ "action_url": "https://example.com/verify"
118
+ }
119
+ )
120
+ ```
121
+
122
+ ## Django Integration
123
+
124
+ ### Option 1: Use Django Email Backend (Recommended)
125
+
126
+ Configure Postie as your Django email backend:
127
+
128
+ ```python
129
+ # settings.py
130
+ EMAIL_BACKEND = 'postie.django_backend.PostieEmailBackend'
131
+ POSTMARK_SERVER_TOKEN = os.getenv('POSTMARK_SERVER_TOKEN')
132
+ DEFAULT_FROM_EMAIL = 'support@nomadicinfluence.com'
133
+ ```
134
+
135
+ Then use Django's standard email functions:
136
+
137
+ ```python
138
+ from django.core.mail import send_mail, EmailMultiAlternatives
139
+
140
+ # Simple email
141
+ send_mail(
142
+ subject='Welcome!',
143
+ message='Welcome to our platform',
144
+ from_email='support@nomadicinfluence.com',
145
+ recipient_list=['user@example.com'],
146
+ )
147
+
148
+ # HTML email
149
+ email = EmailMultiAlternatives(
150
+ subject='Welcome!',
151
+ body='Welcome to our platform',
152
+ from_email='support@nomadicinfluence.com',
153
+ to=['user@example.com'],
154
+ )
155
+ email.attach_alternative('<h1>Welcome!</h1>', "text/html")
156
+ email.send()
157
+ ```
158
+
159
+ ### Option 2: Use PostieClient Directly
160
+
161
+ ```python
162
+ # utils/email.py
163
+ from postie import PostieClient
164
+ from django.conf import settings
165
+
166
+ postmark = PostieClient(server_token=settings.POSTMARK_SERVER_TOKEN)
167
+
168
+ def send_welcome_email(user_email, user_name):
169
+ postmark.send_email(
170
+ from_email="support@nomadicinfluence.com",
171
+ to=user_email,
172
+ subject=f"Welcome {user_name}!",
173
+ html_body=f"<h1>Welcome {user_name}!</h1>",
174
+ text_body=f"Welcome {user_name}!"
175
+ )
176
+ ```
177
+
178
+ ## Error Handling
179
+
180
+ ```python
181
+ from postie import PostieClient, PostieAPIError, PostieError
182
+
183
+ try:
184
+ client.send_email(...)
185
+ except PostieAPIError as e:
186
+ print(f"API Error: {e}")
187
+ print(f"Error Code: {e.error_code}")
188
+ print(f"Status Code: {e.status_code}")
189
+ except PostieError as e:
190
+ print(f"Client Error: {e}")
191
+ ```
192
+
193
+ ## Requirements
194
+
195
+ - Python 3.7+
196
+ - requests
197
+
198
+ ## License
199
+
200
+ MIT
201
+
202
+ ## Contributing
203
+
204
+ Contributions welcome! Please open an issue or PR.
205
+
206
+ ## Links
207
+
208
+ - [Postmark API Documentation](https://postmarkapp.com/developer)
209
+ - [GitHub Repository](https://github.com/roddy-devs/postie)
@@ -0,0 +1,13 @@
1
+ LICENSE
2
+ MANIFEST.in
3
+ README.md
4
+ pyproject.toml
5
+ postie/__init__.py
6
+ postie/client.py
7
+ postie/django_backend.py
8
+ postie/exceptions.py
9
+ postiee.egg-info/PKG-INFO
10
+ postiee.egg-info/SOURCES.txt
11
+ postiee.egg-info/dependency_links.txt
12
+ postiee.egg-info/requires.txt
13
+ postiee.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ requests>=2.25.0
@@ -0,0 +1 @@
1
+ postie
@@ -0,0 +1,40 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "postiee"
7
+ version = "0.1.0"
8
+ description = "A minimal Python client for the Postmark API"
9
+ readme = "README.md"
10
+ authors = [
11
+ {name = "Nomadic Influence", email = "support@nomadicinfluence.com"}
12
+ ]
13
+ license = {text = "MIT"}
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.7",
20
+ "Programming Language :: Python :: 3.8",
21
+ "Programming Language :: Python :: 3.9",
22
+ "Programming Language :: Python :: 3.10",
23
+ "Programming Language :: Python :: 3.11",
24
+ "Programming Language :: Python :: 3.12",
25
+ "Topic :: Communications :: Email",
26
+ "Topic :: Software Development :: Libraries :: Python Modules",
27
+ ]
28
+ keywords = ["postmark", "email", "api", "transactional"]
29
+ requires-python = ">=3.7"
30
+ dependencies = [
31
+ "requests>=2.25.0",
32
+ ]
33
+
34
+ [project.urls]
35
+ Homepage = "https://github.com/roddy-devs/postie"
36
+ Repository = "https://github.com/roddy-devs/postie"
37
+ Issues = "https://github.com/roddy-devs/postie/issues"
38
+
39
+ [tool.setuptools.packages.find]
40
+ include = ["postie*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+