subscribeflow 1.0.4__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,36 @@
1
+ # Dependencies
2
+ uv.lock
3
+
4
+ # Python
5
+ __pycache__/
6
+ *.py[cod]
7
+ *$py.class
8
+ *.so
9
+ .Python
10
+ build/
11
+ develop-eggs/
12
+ dist/
13
+ downloads/
14
+ eggs/
15
+ .eggs/
16
+ lib/
17
+ lib64/
18
+ parts/
19
+ sdist/
20
+ var/
21
+ wheels/
22
+ *.egg-info/
23
+ .installed.cfg
24
+ *.egg
25
+
26
+ # Testing
27
+ .pytest_cache/
28
+ .coverage
29
+ htmlcov/
30
+ .mypy_cache/
31
+
32
+ # IDE
33
+ .idea/
34
+ .vscode/
35
+ *.swp
36
+ *.swo
@@ -0,0 +1,457 @@
1
+ Metadata-Version: 2.4
2
+ Name: subscribeflow
3
+ Version: 1.0.4
4
+ Summary: Python SDK for SubscribeFlow API - Email subscription management
5
+ Project-URL: Homepage, https://subscribeflow.net
6
+ Project-URL: Documentation, https://docs.subscribeflow.net/guides/sdk-setup
7
+ Project-URL: Repository, https://github.com/talent-factory/subscribe-flow
8
+ Project-URL: Issues, https://github.com/talent-factory/subscribe-flow/issues
9
+ Author-email: SubscribeFlow Team <support@subscribeflow.net>
10
+ License: MIT
11
+ Keywords: api,email,newsletter,sdk,subscribeflow,subscription
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Communications :: Email
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Classifier: Typing :: Typed
23
+ Requires-Python: >=3.11
24
+ Requires-Dist: httpx>=0.27.0
25
+ Requires-Dist: pydantic[email]>=2.0.0
26
+ Provides-Extra: dev
27
+ Requires-Dist: mypy>=1.10.0; extra == 'dev'
28
+ Requires-Dist: pytest-asyncio>=0.24.0; extra == 'dev'
29
+ Requires-Dist: pytest-httpx>=0.30.0; extra == 'dev'
30
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
31
+ Requires-Dist: ruff>=0.6.0; extra == 'dev'
32
+ Provides-Extra: mcp
33
+ Requires-Dist: mcp>=1.0.0; extra == 'mcp'
34
+ Description-Content-Type: text/markdown
35
+
36
+ # subscribeflow
37
+
38
+ Official Python SDK for the SubscribeFlow API.
39
+
40
+ ## Installation
41
+
42
+ ### Von GitHub (Empfohlen)
43
+
44
+ ```bash
45
+ # Basis-SDK
46
+ pip install "subscribeflow @ git+https://github.com/talent-factory/subscribe-flow.git#subdirectory=sdk/python"
47
+
48
+ # Mit MCP-Server (für Claude Desktop/Code Integration)
49
+ pip install "subscribeflow[mcp] @ git+https://github.com/talent-factory/subscribe-flow.git#subdirectory=sdk/python"
50
+ ```
51
+
52
+ In `requirements.txt`:
53
+ ```
54
+ subscribeflow @ git+https://github.com/talent-factory/subscribe-flow.git#subdirectory=sdk/python
55
+ ```
56
+
57
+ Mit MCP-Support:
58
+ ```
59
+ subscribeflow[mcp] @ git+https://github.com/talent-factory/subscribe-flow.git#subdirectory=sdk/python
60
+ ```
61
+
62
+ In `pyproject.toml` (uv/Poetry/PDM):
63
+ ```toml
64
+ [project.dependencies]
65
+ subscribeflow = { git = "https://github.com/talent-factory/subscribe-flow.git", subdirectory = "sdk/python" }
66
+
67
+ # Oder mit MCP-Support
68
+ subscribeflow = { git = "https://github.com/talent-factory/subscribe-flow.git", subdirectory = "sdk/python", extras = ["mcp"] }
69
+ ```
70
+
71
+ ### Lokale Entwicklung
72
+
73
+ Für parallele Entwicklung von SDK und Anwendung:
74
+
75
+ ```bash
76
+ # Editable Install (Änderungen am SDK wirken sofort)
77
+ pip install -e /pfad/zu/subscribeflow/sdk/python
78
+
79
+ # Mit uv (schneller)
80
+ uv pip install -e /pfad/zu/subscribeflow/sdk/python
81
+
82
+ # Oder relativ vom Projekt
83
+ pip install -e ../subscribeflow/sdk/python
84
+ ```
85
+
86
+ In `pyproject.toml` für lokale Entwicklung:
87
+ ```toml
88
+ [tool.poetry.dependencies]
89
+ subscribeflow = { path = "../subscribeflow/sdk/python", develop = true }
90
+ ```
91
+
92
+ ## Quick Start
93
+
94
+ ```python
95
+ import asyncio
96
+ from subscribeflow import SubscribeFlowClient
97
+
98
+ async def main():
99
+ async with SubscribeFlowClient(api_key="sf_live_xxx") as client:
100
+ # Create a subscriber
101
+ subscriber = await client.subscribers.create(
102
+ email="user@example.com",
103
+ tags=["newsletter", "product-updates"],
104
+ metadata={"source": "website"},
105
+ )
106
+ print(f"Created subscriber: {subscriber.id}")
107
+
108
+ asyncio.run(main())
109
+ ```
110
+
111
+ ## Usage
112
+
113
+ ### Subscribers
114
+
115
+ ```python
116
+ # List subscribers
117
+ result = await client.subscribers.list(
118
+ limit=50,
119
+ status="active",
120
+ )
121
+ for subscriber in result:
122
+ print(subscriber.email)
123
+
124
+ # Pagination
125
+ while result.next_cursor:
126
+ result = await client.subscribers.list(cursor=result.next_cursor)
127
+ for subscriber in result:
128
+ print(subscriber.email)
129
+
130
+ # Get a subscriber
131
+ subscriber = await client.subscribers.get("subscriber-id")
132
+
133
+ # Update a subscriber
134
+ updated = await client.subscribers.update(
135
+ "subscriber-id",
136
+ metadata={"plan": "premium"},
137
+ )
138
+
139
+ # Delete a subscriber
140
+ await client.subscribers.delete("subscriber-id")
141
+ ```
142
+
143
+ ### Tags
144
+
145
+ ```python
146
+ # Create a tag
147
+ tag = await client.tags.create(
148
+ name="Product Updates",
149
+ description="Get notified about new features",
150
+ )
151
+
152
+ # List tags
153
+ tags = await client.tags.list()
154
+ for tag in tags:
155
+ print(f"{tag.name}: {tag.subscriber_count} subscribers")
156
+
157
+ # Update a tag
158
+ await client.tags.update(
159
+ "tag-id",
160
+ description="Updated description",
161
+ )
162
+
163
+ # Delete a tag
164
+ await client.tags.delete("tag-id")
165
+ ```
166
+
167
+ ### Templates
168
+
169
+ ```python
170
+ # Create an email template
171
+ template = await client.templates.create(
172
+ name="Welcome Email",
173
+ subject="Welcome to {{company}}!",
174
+ mjml_content="<mjml><mj-body>...</mj-body></mjml>",
175
+ category="transactional",
176
+ )
177
+
178
+ # List templates
179
+ templates = await client.templates.list(category="transactional")
180
+
181
+ # Get a template by slug
182
+ template = await client.templates.get("welcome-email")
183
+
184
+ # Preview a template with variables
185
+ preview = await client.templates.preview(
186
+ "template-id",
187
+ variables={"company": "Acme Inc"},
188
+ )
189
+ print(preview.html)
190
+
191
+ # Update a template
192
+ await client.templates.update("template-id", subject="New Subject")
193
+
194
+ # Delete a template
195
+ await client.templates.delete("template-id")
196
+ ```
197
+
198
+ ### Email Send
199
+
200
+ ```python
201
+ # Send a transactional email
202
+ result = await client.emails.send(
203
+ template_slug="welcome-email",
204
+ to="user@example.com",
205
+ variables={"company": "Acme Inc"},
206
+ idempotency_key="unique-key-123",
207
+ )
208
+ print(f"Email queued: {result.id} (status: {result.status})")
209
+ ```
210
+
211
+ ### Campaigns
212
+
213
+ ```python
214
+ from subscribeflow.models import TagFilter
215
+
216
+ # Create a campaign
217
+ campaign = await client.campaigns.create(
218
+ name="February Newsletter",
219
+ template_id="template-uuid",
220
+ tag_filter={"include_tags": ["newsletter"], "match": "any"},
221
+ )
222
+
223
+ # List campaigns
224
+ campaigns = await client.campaigns.list(status="draft")
225
+
226
+ # Preview recipient count
227
+ count = await client.campaigns.count_recipients(include_tags=["newsletter"])
228
+ print(f"Will be sent to {count} subscribers")
229
+
230
+ # Send the campaign
231
+ result = await client.campaigns.send("campaign-id")
232
+ print(f"Sending to {result.total_recipients} recipients")
233
+
234
+ # Cancel a running campaign
235
+ await client.campaigns.cancel("campaign-id")
236
+ ```
237
+
238
+ ### Email Triggers
239
+
240
+ ```python
241
+ # Create an event-based trigger
242
+ trigger = await client.triggers.create(
243
+ event_type="subscriber.created",
244
+ template_id="welcome-template-uuid",
245
+ description="Send welcome email on signup",
246
+ )
247
+
248
+ # List triggers
249
+ triggers = await client.triggers.list()
250
+
251
+ # Update a trigger
252
+ await client.triggers.update("trigger-id", is_active=False)
253
+
254
+ # Delete a trigger
255
+ await client.triggers.delete("trigger-id")
256
+ ```
257
+
258
+ ### Webhooks
259
+
260
+ ```python
261
+ # Create a webhook endpoint
262
+ webhook = await client.webhooks.create(
263
+ url="https://your-app.com/webhooks/subscribeflow",
264
+ events=["subscriber.created", "tag.subscribed"],
265
+ description="Main webhook endpoint",
266
+ )
267
+
268
+ # Store the signing secret securely!
269
+ print(f"Signing secret: {webhook.secret}")
270
+
271
+ # List webhook endpoints
272
+ webhooks = await client.webhooks.list()
273
+
274
+ # Update a webhook
275
+ await client.webhooks.update(
276
+ "webhook-id",
277
+ events=["subscriber.created", "subscriber.deleted"],
278
+ )
279
+
280
+ # Test a webhook
281
+ result = await client.webhooks.test("webhook-id")
282
+ if result.success:
283
+ print(f"Webhook working! Response time: {result.response_time_ms}ms")
284
+ else:
285
+ print(f"Webhook failed: {result.error}")
286
+
287
+ # Rotate signing secret
288
+ new_webhook = await client.webhooks.regenerate_secret("webhook-id")
289
+ print(f"New secret: {new_webhook.secret}")
290
+
291
+ # View delivery history
292
+ deliveries = await client.webhooks.list_deliveries("webhook-id")
293
+ for d in deliveries:
294
+ print(f"{d.event_type}: {d.status}")
295
+
296
+ # Get delivery statistics
297
+ stats = await client.webhooks.get_stats("webhook-id")
298
+ print(f"Success rate: {stats.success_rate}%")
299
+
300
+ # Retry a failed delivery
301
+ await client.webhooks.retry_delivery("webhook-id", "delivery-id")
302
+
303
+ # Delete a webhook
304
+ await client.webhooks.delete("webhook-id")
305
+ ```
306
+
307
+ ### Preference Center
308
+
309
+ ```python
310
+ # Generate a preference center token for a subscriber
311
+ token_response = await client.subscribers.generate_token("subscriber-id")
312
+
313
+ # Access the preference center with the token
314
+ pref_center = client.preference_center(token_response.token)
315
+
316
+ # Get subscriber preferences and available tags
317
+ info = await pref_center.get_preferences()
318
+ print(f"Subscribed to: {len(info.subscribed_tags)} tags")
319
+ print(f"Available: {len(info.available_tags)} tags")
320
+
321
+ # Subscribe to a tag
322
+ result = await pref_center.subscribe_tag("tag-id")
323
+
324
+ # Unsubscribe from a tag
325
+ result = await pref_center.unsubscribe_tag("tag-id")
326
+
327
+ # Export all data (DSGVO)
328
+ export = await pref_center.export_data()
329
+
330
+ # Delete account (DSGVO)
331
+ await pref_center.delete_account()
332
+ ```
333
+
334
+ ## Error Handling
335
+
336
+ ```python
337
+ from subscribeflow import (
338
+ SubscribeFlowClient,
339
+ SubscribeFlowError,
340
+ AuthenticationError,
341
+ AuthorizationError,
342
+ NotFoundError,
343
+ ValidationError,
344
+ RateLimitError,
345
+ )
346
+
347
+ try:
348
+ subscriber = await client.subscribers.get("non-existent-id")
349
+ except NotFoundError as e:
350
+ print(f"Subscriber not found: {e.detail}")
351
+ except ValidationError as e:
352
+ print(f"Validation failed: {e.detail}")
353
+ for error in e.errors:
354
+ print(f" - {error['loc']}: {error['msg']}")
355
+ except RateLimitError as e:
356
+ print("Rate limit exceeded, please retry later")
357
+ except AuthenticationError as e:
358
+ print("Invalid API key")
359
+ except AuthorizationError as e:
360
+ print("Insufficient permissions")
361
+ except SubscribeFlowError as e:
362
+ print(f"API error ({e.status}): {e.detail}")
363
+ ```
364
+
365
+ ## Configuration
366
+
367
+ ```python
368
+ client = SubscribeFlowClient(
369
+ # Required: Your API key
370
+ api_key="sf_live_xxx",
371
+
372
+ # Optional: API base URL (default: https://api.subscribeflow.net)
373
+ # Note: SDK appends /api/v1/... to the base URL automatically
374
+ base_url="https://api.subscribeflow.net",
375
+
376
+ # Optional: Request timeout in seconds (default: 30)
377
+ timeout=30.0,
378
+ )
379
+ ```
380
+
381
+ ### Lokale API-Instanz
382
+
383
+ Für Entwicklung gegen eine lokale SubscribeFlow-Instanz:
384
+
385
+ ```python
386
+ client = SubscribeFlowClient(
387
+ api_key="sf_dev_xxx",
388
+ base_url="http://localhost:8000", # SDK fügt /api/v1/... automatisch hinzu
389
+ )
390
+ ```
391
+
392
+ ## MCP Server (Claude Integration)
393
+
394
+ Das SDK enthält einen MCP-Server für die Integration mit Claude Desktop und Claude Code.
395
+
396
+ ### Quick Start
397
+
398
+ **Claude Desktop/Code Konfiguration** (`claude_desktop_config.json`):
399
+
400
+ ```json
401
+ {
402
+ "mcpServers": {
403
+ "subscribeflow": {
404
+ "command": "uv",
405
+ "args": [
406
+ "--directory", "/pfad/zu/subscribe-flow/sdk/python",
407
+ "run", "subscribeflow-mcp"
408
+ ],
409
+ "env": {
410
+ "SUBSCRIBEFLOW_API_KEY": "sf_live_xxx"
411
+ }
412
+ }
413
+ }
414
+ }
415
+ ```
416
+
417
+ Nach der Konfiguration kannst du Claude bitten:
418
+
419
+ > "Füge user@example.com zum Newsletter hinzu"
420
+
421
+ Claude ruft automatisch die passenden Tools auf.
422
+
423
+ **Vollständige Dokumentation:** [MCP Integration Guide](https://docs.subscribeflow.net/guides/mcp-integration)
424
+
425
+ ## Without Context Manager
426
+
427
+ If you prefer not to use the async context manager, remember to close the client:
428
+
429
+ ```python
430
+ client = SubscribeFlowClient(api_key="sf_live_xxx")
431
+ try:
432
+ subscriber = await client.subscribers.get("id")
433
+ finally:
434
+ await client.close()
435
+ ```
436
+
437
+ ## Type Hints
438
+
439
+ This SDK is fully typed and works great with mypy and other type checkers:
440
+
441
+ ```python
442
+ from subscribeflow import SubscribeFlowClient, Subscriber, Tag
443
+
444
+ async def get_active_subscribers(client: SubscribeFlowClient) -> list[Subscriber]:
445
+ result = await client.subscribers.list(status="active")
446
+ return result.items
447
+ ```
448
+
449
+ ## Links
450
+
451
+ - [API Documentation](https://docs.subscribeflow.net/reference/api)
452
+ - [Getting Started Guide](https://docs.subscribeflow.net/getting-started)
453
+ - [Webhook Integration](https://docs.subscribeflow.net/guides/webhooks)
454
+
455
+ ## License
456
+
457
+ MIT