keplars 1.0.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.
keplars-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,222 @@
1
+ Metadata-Version: 2.1
2
+ Name: keplars
3
+ Version: 1.0.0
4
+ Summary: Official Python SDK for Keplars Email API - modern transactional email service with priority-based delivery
5
+ Home-page: https://keplars.com
6
+ License: MIT
7
+ Keywords: keplars,email,transactional,api,sdk,priority,scheduling
8
+ Author: Keplars
9
+ Author-email: support@keplars.com
10
+ Requires-Python: >=3.8,<4.0
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.8
16
+ Classifier: Programming Language :: Python :: 3.9
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-Dist: httpx (>=0.26.0,<0.27.0)
24
+ Requires-Dist: pydantic[email] (>=2.5.0,<3.0.0)
25
+ Project-URL: Documentation, https://docs.keplars.com
26
+ Project-URL: Repository, https://github.com/Swing-Technologies/keplers-mail-sdk
27
+ Description-Content-Type: text/markdown
28
+
29
+ # Keplars Email SDK for Python
30
+
31
+ Official Python SDK for the Keplars Email API - modern transactional email service with priority-based delivery.
32
+
33
+ ## Installation
34
+
35
+ ```bash
36
+ pip install keplars
37
+ ```
38
+
39
+ or with Poetry:
40
+
41
+ ```bash
42
+ poetry add keplars
43
+ ```
44
+
45
+ ## Quick Start
46
+
47
+ ### Synchronous Client
48
+
49
+ ```python
50
+ from keplars import Keplars
51
+
52
+ client = Keplars(api_key='kms_<workspaceId>.live_<secret>')
53
+
54
+ result = client.emails.send_instant(
55
+ **{'from': 'noreply@yourdomain.com'},
56
+ to='user@example.com',
57
+ subject='Your verification code is 123456',
58
+ html='<p>Your verification code is <strong>123456</strong></p>'
59
+ )
60
+
61
+ print(result.data.job_id)
62
+ ```
63
+
64
+ ### Async Client
65
+
66
+ ```python
67
+ import asyncio
68
+ from keplars import AsyncKeplars
69
+
70
+ async def main():
71
+ async with AsyncKeplars(api_key='kms_<workspaceId>.live_<secret>') as client:
72
+ result = await client.emails.send_instant(
73
+ **{'from': 'noreply@yourdomain.com'},
74
+ to='user@example.com',
75
+ subject='Welcome!',
76
+ html='<h1>Welcome aboard</h1>'
77
+ )
78
+ print(result.data.job_id)
79
+
80
+ asyncio.run(main())
81
+ ```
82
+
83
+ ### API Key Types
84
+
85
+ | Type | Format | Used for |
86
+ |---|---|---|
87
+ | Regular | `kms_<id>.live_<secret>` | Email sending |
88
+ | Admin | `kms_<id>.adm_<secret>` | Contacts, audiences, automations, domains |
89
+
90
+ ## Email Sending
91
+
92
+ ### Priority Levels
93
+
94
+ | Method | Delivery | Use case |
95
+ |---|---|---|
96
+ | `send_instant` | 0–5 sec | OTPs, login codes, critical alerts |
97
+ | `send_high` | 0–30 sec | Transactional, notifications |
98
+ | `send_async` / `send` | 0–5 min | General transactional |
99
+ | `send_bulk` | Idle | Newsletters, marketing |
100
+
101
+ ### Response Shape
102
+
103
+ ```python
104
+ result.success # True
105
+ result.message # 'Email queued'
106
+ result.data.job_id # 'job_abc123'
107
+ result.data.priority # 'instant'
108
+ ```
109
+
110
+ ### Send with Recipients
111
+
112
+ ```python
113
+ result = client.emails.send_high(
114
+ **{'from': 'noreply@yourdomain.com'},
115
+ to=[{'email': 'user@example.com', 'name': 'John Doe'}],
116
+ cc=[{'email': 'manager@example.com'}],
117
+ subject='Order Confirmation',
118
+ html='<p>Your order has been confirmed</p>'
119
+ )
120
+ ```
121
+
122
+ ### Send with Template
123
+
124
+ ```python
125
+ result = client.emails.send(
126
+ **{'from': 'noreply@yourdomain.com'},
127
+ to='user@example.com',
128
+ subject='Password Reset',
129
+ template_id='tpl_reset_password',
130
+ template_data={'name': 'John', 'reset_link': 'https://example.com/reset/abc'}
131
+ )
132
+ ```
133
+
134
+ ### Schedule Email
135
+
136
+ ```python
137
+ result = client.emails.schedule(
138
+ **{'from': 'newsletter@yourdomain.com'},
139
+ to='user@example.com',
140
+ subject='Your weekly digest',
141
+ html='<p>Here is your weekly digest...</p>',
142
+ scheduled_for='2026-06-01T09:00:00Z',
143
+ priority='bulk'
144
+ )
145
+ ```
146
+
147
+ ## Contacts (Admin API Key Required)
148
+
149
+ ```python
150
+ admin_client = Keplars(api_key='kms_<workspaceId>.adm_<secret>')
151
+
152
+ admin_client.contacts.add(email='user@example.com', name='John Doe', audience_id='aud_abc123')
153
+
154
+ contact = admin_client.contacts.get('user@example.com')
155
+
156
+ contacts = admin_client.contacts.list(audience_id='aud_abc123', page=1, limit=20)
157
+
158
+ admin_client.contacts.update('user@example.com', name='Jane Doe')
159
+
160
+ admin_client.contacts.delete('user@example.com')
161
+ ```
162
+
163
+ ## Audiences (Admin API Key Required)
164
+
165
+ ```python
166
+ audience = admin_client.audiences.create('Newsletter Subscribers', description='Main list')
167
+
168
+ audiences = admin_client.audiences.list(page=1, limit=20)
169
+
170
+ audience = admin_client.audiences.get('aud_abc123')
171
+
172
+ admin_client.audiences.delete('aud_abc123')
173
+ ```
174
+
175
+ ## Automations (Admin API Key Required)
176
+
177
+ ```python
178
+ automations = admin_client.automations.list()
179
+
180
+ automation = admin_client.automations.get('auto_abc123')
181
+
182
+ admin_client.automations.enroll('auto_abc123', 'user@example.com')
183
+
184
+ admin_client.automations.unenroll('auto_abc123', 'user@example.com')
185
+ ```
186
+
187
+ ## Domains (Admin API Key Required)
188
+
189
+ ```python
190
+ domain = admin_client.domains.add('mail.yourcompany.com')
191
+
192
+ domains = admin_client.domains.list()
193
+
194
+ status = admin_client.domains.get_status('dom_abc123')
195
+
196
+ result = admin_client.domains.verify('dom_abc123')
197
+
198
+ api_key = admin_client.domains.create_api_key(domain_id='dom_abc123', name='Production Key')
199
+
200
+ admin_client.domains.delete('dom_abc123')
201
+ ```
202
+
203
+ ## Error Handling
204
+
205
+ ```python
206
+ from keplars import (
207
+ Keplars,
208
+ AuthenticationError,
209
+ RateLimitError,
210
+ ValidationError,
211
+ )
212
+
213
+ try:
214
+ result = client.emails.send_instant(...)
215
+ except AuthenticationError:
216
+ print('Invalid API key')
217
+ except RateLimitError as e:
218
+ print(f'Rate limited, retry after: {e.retry_after}s')
219
+ except ValidationError as e:
220
+ print(f'Validation error: {e}')
221
+ ```
222
+
@@ -0,0 +1,193 @@
1
+ # Keplars Email SDK for Python
2
+
3
+ Official Python SDK for the Keplars Email API - modern transactional email service with priority-based delivery.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install keplars
9
+ ```
10
+
11
+ or with Poetry:
12
+
13
+ ```bash
14
+ poetry add keplars
15
+ ```
16
+
17
+ ## Quick Start
18
+
19
+ ### Synchronous Client
20
+
21
+ ```python
22
+ from keplars import Keplars
23
+
24
+ client = Keplars(api_key='kms_<workspaceId>.live_<secret>')
25
+
26
+ result = client.emails.send_instant(
27
+ **{'from': 'noreply@yourdomain.com'},
28
+ to='user@example.com',
29
+ subject='Your verification code is 123456',
30
+ html='<p>Your verification code is <strong>123456</strong></p>'
31
+ )
32
+
33
+ print(result.data.job_id)
34
+ ```
35
+
36
+ ### Async Client
37
+
38
+ ```python
39
+ import asyncio
40
+ from keplars import AsyncKeplars
41
+
42
+ async def main():
43
+ async with AsyncKeplars(api_key='kms_<workspaceId>.live_<secret>') as client:
44
+ result = await client.emails.send_instant(
45
+ **{'from': 'noreply@yourdomain.com'},
46
+ to='user@example.com',
47
+ subject='Welcome!',
48
+ html='<h1>Welcome aboard</h1>'
49
+ )
50
+ print(result.data.job_id)
51
+
52
+ asyncio.run(main())
53
+ ```
54
+
55
+ ### API Key Types
56
+
57
+ | Type | Format | Used for |
58
+ |---|---|---|
59
+ | Regular | `kms_<id>.live_<secret>` | Email sending |
60
+ | Admin | `kms_<id>.adm_<secret>` | Contacts, audiences, automations, domains |
61
+
62
+ ## Email Sending
63
+
64
+ ### Priority Levels
65
+
66
+ | Method | Delivery | Use case |
67
+ |---|---|---|
68
+ | `send_instant` | 0–5 sec | OTPs, login codes, critical alerts |
69
+ | `send_high` | 0–30 sec | Transactional, notifications |
70
+ | `send_async` / `send` | 0–5 min | General transactional |
71
+ | `send_bulk` | Idle | Newsletters, marketing |
72
+
73
+ ### Response Shape
74
+
75
+ ```python
76
+ result.success # True
77
+ result.message # 'Email queued'
78
+ result.data.job_id # 'job_abc123'
79
+ result.data.priority # 'instant'
80
+ ```
81
+
82
+ ### Send with Recipients
83
+
84
+ ```python
85
+ result = client.emails.send_high(
86
+ **{'from': 'noreply@yourdomain.com'},
87
+ to=[{'email': 'user@example.com', 'name': 'John Doe'}],
88
+ cc=[{'email': 'manager@example.com'}],
89
+ subject='Order Confirmation',
90
+ html='<p>Your order has been confirmed</p>'
91
+ )
92
+ ```
93
+
94
+ ### Send with Template
95
+
96
+ ```python
97
+ result = client.emails.send(
98
+ **{'from': 'noreply@yourdomain.com'},
99
+ to='user@example.com',
100
+ subject='Password Reset',
101
+ template_id='tpl_reset_password',
102
+ template_data={'name': 'John', 'reset_link': 'https://example.com/reset/abc'}
103
+ )
104
+ ```
105
+
106
+ ### Schedule Email
107
+
108
+ ```python
109
+ result = client.emails.schedule(
110
+ **{'from': 'newsletter@yourdomain.com'},
111
+ to='user@example.com',
112
+ subject='Your weekly digest',
113
+ html='<p>Here is your weekly digest...</p>',
114
+ scheduled_for='2026-06-01T09:00:00Z',
115
+ priority='bulk'
116
+ )
117
+ ```
118
+
119
+ ## Contacts (Admin API Key Required)
120
+
121
+ ```python
122
+ admin_client = Keplars(api_key='kms_<workspaceId>.adm_<secret>')
123
+
124
+ admin_client.contacts.add(email='user@example.com', name='John Doe', audience_id='aud_abc123')
125
+
126
+ contact = admin_client.contacts.get('user@example.com')
127
+
128
+ contacts = admin_client.contacts.list(audience_id='aud_abc123', page=1, limit=20)
129
+
130
+ admin_client.contacts.update('user@example.com', name='Jane Doe')
131
+
132
+ admin_client.contacts.delete('user@example.com')
133
+ ```
134
+
135
+ ## Audiences (Admin API Key Required)
136
+
137
+ ```python
138
+ audience = admin_client.audiences.create('Newsletter Subscribers', description='Main list')
139
+
140
+ audiences = admin_client.audiences.list(page=1, limit=20)
141
+
142
+ audience = admin_client.audiences.get('aud_abc123')
143
+
144
+ admin_client.audiences.delete('aud_abc123')
145
+ ```
146
+
147
+ ## Automations (Admin API Key Required)
148
+
149
+ ```python
150
+ automations = admin_client.automations.list()
151
+
152
+ automation = admin_client.automations.get('auto_abc123')
153
+
154
+ admin_client.automations.enroll('auto_abc123', 'user@example.com')
155
+
156
+ admin_client.automations.unenroll('auto_abc123', 'user@example.com')
157
+ ```
158
+
159
+ ## Domains (Admin API Key Required)
160
+
161
+ ```python
162
+ domain = admin_client.domains.add('mail.yourcompany.com')
163
+
164
+ domains = admin_client.domains.list()
165
+
166
+ status = admin_client.domains.get_status('dom_abc123')
167
+
168
+ result = admin_client.domains.verify('dom_abc123')
169
+
170
+ api_key = admin_client.domains.create_api_key(domain_id='dom_abc123', name='Production Key')
171
+
172
+ admin_client.domains.delete('dom_abc123')
173
+ ```
174
+
175
+ ## Error Handling
176
+
177
+ ```python
178
+ from keplars import (
179
+ Keplars,
180
+ AuthenticationError,
181
+ RateLimitError,
182
+ ValidationError,
183
+ )
184
+
185
+ try:
186
+ result = client.emails.send_instant(...)
187
+ except AuthenticationError:
188
+ print('Invalid API key')
189
+ except RateLimitError as e:
190
+ print(f'Rate limited, retry after: {e.retry_after}s')
191
+ except ValidationError as e:
192
+ print(f'Validation error: {e}')
193
+ ```
@@ -0,0 +1,72 @@
1
+ [tool.poetry]
2
+ name = "keplars"
3
+ version = "1.0.0"
4
+ description = "Official Python SDK for Keplars Email API - modern transactional email service with priority-based delivery"
5
+ authors = ["Keplars <support@keplars.com>"]
6
+ license = "MIT"
7
+ readme = "README.md"
8
+ homepage = "https://keplars.com"
9
+ repository = "https://github.com/Swing-Technologies/keplers-mail-sdk"
10
+ documentation = "https://docs.keplars.com"
11
+ keywords = ["keplars", "email", "transactional", "api", "sdk", "priority", "scheduling"]
12
+ classifiers = [
13
+ "Development Status :: 4 - Beta",
14
+ "Intended Audience :: Developers",
15
+ "License :: OSI Approved :: MIT License",
16
+ "Programming Language :: Python :: 3",
17
+ "Programming Language :: Python :: 3.8",
18
+ "Programming Language :: Python :: 3.9",
19
+ "Programming Language :: Python :: 3.10",
20
+ "Programming Language :: Python :: 3.11",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Topic :: Communications :: Email",
23
+ "Topic :: Software Development :: Libraries :: Python Modules",
24
+ ]
25
+ packages = [{include = "keplars", from = "src"}]
26
+
27
+ [tool.poetry.dependencies]
28
+ python = "^3.8"
29
+ httpx = "^0.26.0"
30
+ pydantic = {version = "^2.5.0", extras = ["email"]}
31
+
32
+ [tool.poetry.group.dev.dependencies]
33
+ pytest = "^7.4.3"
34
+ pytest-asyncio = "^0.23.2"
35
+ pytest-cov = "^4.1.0"
36
+ pytest-mock = "^3.12.0"
37
+ black = "^23.12.1"
38
+ isort = "^5.13.2"
39
+ mypy = "^1.8.0"
40
+ ruff = "^0.1.9"
41
+
42
+ [build-system]
43
+ requires = ["poetry-core"]
44
+ build-backend = "poetry.core.masonry.api"
45
+
46
+ [tool.black]
47
+ line-length = 100
48
+ target-version = ['py38']
49
+ include = '\.pyi?$'
50
+
51
+ [tool.isort]
52
+ profile = "black"
53
+ line_length = 100
54
+
55
+ [tool.mypy]
56
+ python_version = "3.8"
57
+ strict = true
58
+ warn_return_any = true
59
+ warn_unused_configs = true
60
+ disallow_untyped_defs = true
61
+
62
+ [tool.ruff]
63
+ line-length = 100
64
+ target-version = "py38"
65
+
66
+ [tool.pytest.ini_options]
67
+ testpaths = ["tests"]
68
+ python_files = "test_*.py"
69
+ python_classes = "Test*"
70
+ python_functions = "test_*"
71
+ addopts = "-v --cov=keplars --cov-report=term-missing"
72
+ asyncio_mode = "auto"
@@ -0,0 +1,74 @@
1
+ from .client import Keplars, AsyncKeplars
2
+ from .errors import (
3
+ KeplarsError,
4
+ ValidationError,
5
+ AuthenticationError,
6
+ AuthorizationError,
7
+ ConflictError,
8
+ DomainNotVerifiedError,
9
+ InternalError,
10
+ NetworkError,
11
+ NotFoundError,
12
+ PlanLimitError,
13
+ QuotaExceededError,
14
+ RateLimitError,
15
+ )
16
+ from .models import (
17
+ EmailPriority,
18
+ ErrorCode,
19
+ KeplarsConfig,
20
+ EmailRecipient,
21
+ SendEmailRequest,
22
+ SendEmailResponse,
23
+ SendEmailData,
24
+ ScheduleEmailRequest,
25
+ ScheduledEmailResponse,
26
+ Contact,
27
+ Audience,
28
+ Automation,
29
+ DomainListItem,
30
+ PaginatedMeta,
31
+ ErrorDetail,
32
+ RateLimitInfo,
33
+ )
34
+ from .utils import (
35
+ verify_webhook_signature,
36
+ render_template,
37
+ )
38
+
39
+ __version__ = "1.0.0"
40
+
41
+ __all__ = [
42
+ "Keplars",
43
+ "AsyncKeplars",
44
+ "KeplarsError",
45
+ "ValidationError",
46
+ "AuthenticationError",
47
+ "AuthorizationError",
48
+ "ConflictError",
49
+ "DomainNotVerifiedError",
50
+ "InternalError",
51
+ "NetworkError",
52
+ "NotFoundError",
53
+ "PlanLimitError",
54
+ "QuotaExceededError",
55
+ "RateLimitError",
56
+ "EmailPriority",
57
+ "ErrorCode",
58
+ "KeplarsConfig",
59
+ "EmailRecipient",
60
+ "SendEmailRequest",
61
+ "SendEmailResponse",
62
+ "SendEmailData",
63
+ "ScheduleEmailRequest",
64
+ "ScheduledEmailResponse",
65
+ "Contact",
66
+ "Audience",
67
+ "Automation",
68
+ "DomainListItem",
69
+ "PaginatedMeta",
70
+ "ErrorDetail",
71
+ "RateLimitInfo",
72
+ "verify_webhook_signature",
73
+ "render_template",
74
+ ]
@@ -0,0 +1,90 @@
1
+ from typing import Any, Dict, Optional, TYPE_CHECKING
2
+
3
+ if TYPE_CHECKING:
4
+ from .client import Keplars, AsyncKeplars
5
+
6
+
7
+ class AudiencesResource:
8
+ def __init__(self, client: "Keplars") -> None:
9
+ self._client = client
10
+
11
+ def create(self, name: str, description: Optional[str] = None) -> Dict[str, Any]:
12
+ body: Dict[str, Any] = {"name": name}
13
+ if description is not None:
14
+ body["description"] = description
15
+ response_data, _ = self._client._request(
16
+ "POST", "/api/v1/public/audiences/add-audience", body
17
+ )
18
+ return response_data
19
+
20
+ def list(self, page: Optional[int] = None, limit: Optional[int] = None) -> Dict[str, Any]:
21
+ params: Dict[str, Any] = {}
22
+ if page is not None:
23
+ params["page"] = page
24
+ if limit is not None:
25
+ params["limit"] = limit
26
+
27
+ query = ""
28
+ if params:
29
+ query = "?" + "&".join(f"{k}={v}" for k, v in params.items())
30
+
31
+ response_data, _ = self._client._request(
32
+ "GET", f"/api/v1/public/audiences/get-audiences{query}"
33
+ )
34
+ return response_data
35
+
36
+ def get(self, id: str) -> Dict[str, Any]:
37
+ response_data, _ = self._client._request(
38
+ "GET", f"/api/v1/public/audiences/get-audience?id={id}"
39
+ )
40
+ return response_data
41
+
42
+ def delete(self, id: str) -> Dict[str, Any]:
43
+ response_data, _ = self._client._request(
44
+ "DELETE", f"/api/v1/public/audiences/delete-audience?id={id}"
45
+ )
46
+ return response_data
47
+
48
+
49
+ class AsyncAudiencesResource:
50
+ def __init__(self, client: "AsyncKeplars") -> None:
51
+ self._client = client
52
+
53
+ async def create(self, name: str, description: Optional[str] = None) -> Dict[str, Any]:
54
+ body: Dict[str, Any] = {"name": name}
55
+ if description is not None:
56
+ body["description"] = description
57
+ response_data, _ = await self._client._request(
58
+ "POST", "/api/v1/public/audiences/add-audience", body
59
+ )
60
+ return response_data
61
+
62
+ async def list(
63
+ self, page: Optional[int] = None, limit: Optional[int] = None
64
+ ) -> Dict[str, Any]:
65
+ params: Dict[str, Any] = {}
66
+ if page is not None:
67
+ params["page"] = page
68
+ if limit is not None:
69
+ params["limit"] = limit
70
+
71
+ query = ""
72
+ if params:
73
+ query = "?" + "&".join(f"{k}={v}" for k, v in params.items())
74
+
75
+ response_data, _ = await self._client._request(
76
+ "GET", f"/api/v1/public/audiences/get-audiences{query}"
77
+ )
78
+ return response_data
79
+
80
+ async def get(self, id: str) -> Dict[str, Any]:
81
+ response_data, _ = await self._client._request(
82
+ "GET", f"/api/v1/public/audiences/get-audience?id={id}"
83
+ )
84
+ return response_data
85
+
86
+ async def delete(self, id: str) -> Dict[str, Any]:
87
+ response_data, _ = await self._client._request(
88
+ "DELETE", f"/api/v1/public/audiences/delete-audience?id={id}"
89
+ )
90
+ return response_data