cdp-python-sdk 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.
@@ -0,0 +1,7 @@
1
+ recursive-include cdp_client *.py
2
+ include README.md pyproject.toml
3
+ global-exclude __pycache__ *.py[cod] *.so
4
+ prune tests
5
+ prune example
6
+ exclude main.py requirements.txt
7
+ exclude CONTRIBUTING.md DEPLOYMENT.md
@@ -0,0 +1,310 @@
1
+ Metadata-Version: 2.4
2
+ Name: cdp-python-sdk
3
+ Version: 0.1.0
4
+ Summary: A Python client library for Codematic's Customer Data Platform (CDP) with optional Customer.io integration.
5
+ Author-email: Codematic Engineering <engineering@codematic.io>
6
+ License-Expression: MIT
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Operating System :: OS Independent
9
+ Requires-Python: >=3.8
10
+ Description-Content-Type: text/markdown
11
+ Requires-Dist: httpx>=0.24.0
12
+ Requires-Dist: pydantic>=2.0.0
13
+ Requires-Dist: customerio>=1.1.0
14
+ Provides-Extra: test
15
+ Requires-Dist: pytest>=7.0.0; extra == "test"
16
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "test"
17
+ Requires-Dist: pytest-httpx>=0.21.0; extra == "test"
18
+
19
+ # CDP Python SDK
20
+
21
+ A Python client library for Codematic's Customer Data Platform (CDP) with optional Customer.io integration.
22
+
23
+ ## Installation
24
+
25
+ ```bash
26
+ pip install cdp-python-sdk
27
+ ```
28
+
29
+ Or via `requirements.txt`:
30
+
31
+ ```
32
+ cdp-python-sdk
33
+ ```
34
+
35
+ ## Features
36
+
37
+ - **Async Support**: Built on `httpx` and `asyncio` for high-performance non-blocking I/O.
38
+ - **Dual-Write**: Optional integration to send data to both CDP and Customer.io simultaneously.
39
+ - **Type Safety**: Uses Pydantic models for request validation.
40
+ - **Transactional Messaging**: Support for Email, Push, and SMS.
41
+ - **Device Registration**: Register devices for push notification targeting.
42
+
43
+ ## Examples
44
+
45
+ A complete runnable example is available in [`example/`](example/):
46
+
47
+ ```
48
+ example/
49
+ ├── main.py # Full flow: init → identify → track → register_device → email → push → sms
50
+ └── README.md # How to run
51
+ ```
52
+
53
+ See [`example/README.md`](example/README.md) for run instructions.
54
+
55
+ ---
56
+
57
+ ## Quick Start
58
+
59
+ ```python
60
+ import asyncio
61
+ from cdp_client import CDPClient, CDPConfig
62
+
63
+ async def main():
64
+ config = CDPConfig(cdp_api_key="your-cdp-api-key")
65
+ client = CDPClient(config)
66
+
67
+ await client.identify("user-123", {"email": "user@example.com", "name": "Jane"})
68
+ await client.track("user-123", "signed_up", {"plan": "pro"})
69
+
70
+ await client.close()
71
+
72
+ asyncio.run(main())
73
+ ```
74
+
75
+ ## Usage
76
+
77
+ ### Initialization
78
+
79
+ ```python
80
+ import asyncio
81
+ from cdp_client import CDPClient, CDPConfig, CustomerIoConfig
82
+
83
+ async def main():
84
+ config = CDPConfig(
85
+ cdp_api_key="your-cdp-api-key",
86
+ debug=True,
87
+ # Optional: enable Customer.io dual-write
88
+ send_to_customer_io=True,
89
+ customer_io=CustomerIoConfig(
90
+ site_id="your-cio-site-id",
91
+ api_key="your-cio-api-key",
92
+ region="us" # "us" or "eu"
93
+ )
94
+ )
95
+ client = CDPClient(config)
96
+
97
+ # ... use client ...
98
+
99
+ await client.close()
100
+
101
+ asyncio.run(main())
102
+ ```
103
+
104
+ ---
105
+
106
+ ### Identify a User
107
+
108
+ Associate a user ID with a set of traits (name, email, plan, etc.). Call this on sign-up, login, or whenever user attributes change.
109
+
110
+ ```python
111
+ await client.identify("user-123", {
112
+ "email": "user@example.com",
113
+ "name": "Jane Doe",
114
+ "plan": "premium"
115
+ })
116
+ ```
117
+
118
+ ---
119
+
120
+ ### Track an Event
121
+
122
+ Record a user action or behaviour.
123
+
124
+ ```python
125
+ await client.track("user-123", "purchase_completed", {
126
+ "amount": 99.99,
127
+ "currency": "USD",
128
+ "item_id": "prod-456"
129
+ })
130
+ ```
131
+
132
+ ---
133
+
134
+ ### Register a Device
135
+
136
+ Register a device token for push notification targeting. Call this after you receive a push token from your mobile platform.
137
+
138
+ ```python
139
+ await client.register_device(
140
+ user_id="user-123",
141
+ device_id="device-abc", # Unique device identifier
142
+ platform="ios", # "ios", "android", or "web"
143
+ token="apns-or-fcm-token",
144
+ attributes={ # Optional device metadata
145
+ "app_version": "2.1.0",
146
+ "os_version": "17.2"
147
+ }
148
+ )
149
+ ```
150
+
151
+ ---
152
+
153
+ ### Send Transactional Email
154
+
155
+ ```python
156
+ from cdp_client import EmailPayload, Identifiers
157
+
158
+ await client.send_email(EmailPayload(
159
+ to="user@example.com",
160
+ identifiers=Identifiers(id="user-123"),
161
+ transactional_message_id="WELCOME_EMAIL",
162
+ subject="Welcome!",
163
+ body="<h1>Thanks for joining!</h1>",
164
+ body_plain="Thanks for joining!"
165
+ ))
166
+ ```
167
+
168
+ ---
169
+
170
+ ### Send Push Notification
171
+
172
+ ```python
173
+ from cdp_client import PushPayload, Identifiers
174
+
175
+ await client.send_push(PushPayload(
176
+ identifiers=Identifiers(id="user-123"),
177
+ transactional_message_id="PROMO_PUSH",
178
+ title="Flash Sale 🔥",
179
+ body="50% off for the next hour."
180
+ ))
181
+ ```
182
+
183
+ ---
184
+
185
+ ### Send SMS
186
+
187
+ ```python
188
+ from cdp_client import SmsPayload, Identifiers
189
+
190
+ await client.send_sms(SmsPayload(
191
+ identifiers=Identifiers(id="user-123"),
192
+ to="+14155551234", # Optional: raw phone number
193
+ transactional_message_id="OTP_MSG",
194
+ body="Your one-time code is 881234."
195
+ ))
196
+ ```
197
+
198
+ ---
199
+
200
+ ### Clear Identity / Logout
201
+
202
+ To reset the client's user context (e.g., on logout), close the current client and re-initialize without a user session:
203
+
204
+ ```python
205
+ # On user logout: flush pending work and release the HTTP client
206
+ await client.close()
207
+
208
+ # Re-initialize for anonymous or new user session
209
+ client = CDPClient(config)
210
+ ```
211
+
212
+ ---
213
+
214
+ ## Error Handling
215
+
216
+ By default, the Python SDK raises exceptions on HTTP errors or network failures. Wrap calls in `try/except` to handle them gracefully:
217
+
218
+ ```python
219
+ import httpx
220
+
221
+ try:
222
+ await client.identify("user-123", {"email": "user@example.com"})
223
+ except httpx.HTTPStatusError as e:
224
+ # Server returned 4xx or 5xx
225
+ print(f"API error {e.response.status_code}: {e.response.text}")
226
+ except Exception as e:
227
+ # Network error, timeout, etc.
228
+ print(f"Unexpected error: {e}")
229
+ ```
230
+
231
+ > All methods (`identify`, `track`, `send_email`, `send_push`, `send_sms`, `register_device`) raise on failure. Dual-write Customer.io errors are non-fatal and only emit a warning log.
232
+
233
+ ---
234
+
235
+ ## Configuration Options
236
+
237
+ | Option | Type | Default | Description |
238
+ |--------|------|---------|-------------|
239
+ | `cdp_api_key` | `str` | **Required** | Your CDP API Key |
240
+ | `cdp_endpoint` | `str` | Production URL | Custom CDP Gateway URL |
241
+ | `debug` | `bool` | `False` | Enable verbose debug logging |
242
+ | `send_to_customer_io` | `bool` | `False` | Enable dual-write to Customer.io |
243
+ | `customer_io` | `CustomerIoConfig` | `None` | Customer.io integration config (see below) |
244
+
245
+ ### `CustomerIoConfig` Options
246
+
247
+ | Option | Type | Description |
248
+ |--------|------|-------------|
249
+ | `site_id` | `str` | Customer.io Site ID |
250
+ | `api_key` | `str` | Customer.io API Key |
251
+ | `region` | `str` | `"us"` (default) or `"eu"` |
252
+
253
+ ---
254
+
255
+ ## Dual-Write to Customer.io
256
+
257
+ When `send_to_customer_io=True`, all `identify`, `track`, and `register_device` calls are mirrored to Customer.io automatically. Customer.io failures are **non-blocking** — the CDP call succeeds even if the Customer.io call fails.
258
+
259
+ ```python
260
+ config = CDPConfig(
261
+ cdp_api_key="your-cdp-api-key",
262
+ send_to_customer_io=True,
263
+ customer_io=CustomerIoConfig(
264
+ site_id="cio-site-id",
265
+ api_key="cio-api-key",
266
+ region="eu"
267
+ )
268
+ )
269
+ ```
270
+
271
+ ---
272
+
273
+ ## Development
274
+
275
+ ### Setup
276
+
277
+ ```bash
278
+ python3 -m venv venv
279
+ source venv/bin/activate
280
+ python -m pip install --upgrade pip setuptools wheel build
281
+ python -m pip install -e ".[test]"
282
+ ```
283
+
284
+ ### Run Tests
285
+
286
+ ```bash
287
+ pytest -v
288
+ ```
289
+
290
+ ### Building the Package
291
+
292
+ ```bash
293
+ python -m build
294
+ ```
295
+
296
+ This creates files in the `dist/` directory:
297
+ - `cdp_python_sdk-{version}-py3-none-any.whl`
298
+ - `cdp_python_sdk-{version}.tar.gz`
299
+
300
+ ---
301
+
302
+ ## Versioning
303
+
304
+ Follow [Semantic Versioning](https://semver.org/):
305
+
306
+ | Bump | When |
307
+ |------|------|
308
+ | **PATCH** `1.0.0 → 1.0.1` | Bug fixes |
309
+ | **MINOR** `1.0.0 → 1.1.0` | New features, backward compatible |
310
+ | **MAJOR** `1.0.0 → 2.0.0` | Breaking API changes |
@@ -0,0 +1,292 @@
1
+ # CDP Python SDK
2
+
3
+ A Python client library for Codematic's Customer Data Platform (CDP) with optional Customer.io integration.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install cdp-python-sdk
9
+ ```
10
+
11
+ Or via `requirements.txt`:
12
+
13
+ ```
14
+ cdp-python-sdk
15
+ ```
16
+
17
+ ## Features
18
+
19
+ - **Async Support**: Built on `httpx` and `asyncio` for high-performance non-blocking I/O.
20
+ - **Dual-Write**: Optional integration to send data to both CDP and Customer.io simultaneously.
21
+ - **Type Safety**: Uses Pydantic models for request validation.
22
+ - **Transactional Messaging**: Support for Email, Push, and SMS.
23
+ - **Device Registration**: Register devices for push notification targeting.
24
+
25
+ ## Examples
26
+
27
+ A complete runnable example is available in [`example/`](example/):
28
+
29
+ ```
30
+ example/
31
+ ├── main.py # Full flow: init → identify → track → register_device → email → push → sms
32
+ └── README.md # How to run
33
+ ```
34
+
35
+ See [`example/README.md`](example/README.md) for run instructions.
36
+
37
+ ---
38
+
39
+ ## Quick Start
40
+
41
+ ```python
42
+ import asyncio
43
+ from cdp_client import CDPClient, CDPConfig
44
+
45
+ async def main():
46
+ config = CDPConfig(cdp_api_key="your-cdp-api-key")
47
+ client = CDPClient(config)
48
+
49
+ await client.identify("user-123", {"email": "user@example.com", "name": "Jane"})
50
+ await client.track("user-123", "signed_up", {"plan": "pro"})
51
+
52
+ await client.close()
53
+
54
+ asyncio.run(main())
55
+ ```
56
+
57
+ ## Usage
58
+
59
+ ### Initialization
60
+
61
+ ```python
62
+ import asyncio
63
+ from cdp_client import CDPClient, CDPConfig, CustomerIoConfig
64
+
65
+ async def main():
66
+ config = CDPConfig(
67
+ cdp_api_key="your-cdp-api-key",
68
+ debug=True,
69
+ # Optional: enable Customer.io dual-write
70
+ send_to_customer_io=True,
71
+ customer_io=CustomerIoConfig(
72
+ site_id="your-cio-site-id",
73
+ api_key="your-cio-api-key",
74
+ region="us" # "us" or "eu"
75
+ )
76
+ )
77
+ client = CDPClient(config)
78
+
79
+ # ... use client ...
80
+
81
+ await client.close()
82
+
83
+ asyncio.run(main())
84
+ ```
85
+
86
+ ---
87
+
88
+ ### Identify a User
89
+
90
+ Associate a user ID with a set of traits (name, email, plan, etc.). Call this on sign-up, login, or whenever user attributes change.
91
+
92
+ ```python
93
+ await client.identify("user-123", {
94
+ "email": "user@example.com",
95
+ "name": "Jane Doe",
96
+ "plan": "premium"
97
+ })
98
+ ```
99
+
100
+ ---
101
+
102
+ ### Track an Event
103
+
104
+ Record a user action or behaviour.
105
+
106
+ ```python
107
+ await client.track("user-123", "purchase_completed", {
108
+ "amount": 99.99,
109
+ "currency": "USD",
110
+ "item_id": "prod-456"
111
+ })
112
+ ```
113
+
114
+ ---
115
+
116
+ ### Register a Device
117
+
118
+ Register a device token for push notification targeting. Call this after you receive a push token from your mobile platform.
119
+
120
+ ```python
121
+ await client.register_device(
122
+ user_id="user-123",
123
+ device_id="device-abc", # Unique device identifier
124
+ platform="ios", # "ios", "android", or "web"
125
+ token="apns-or-fcm-token",
126
+ attributes={ # Optional device metadata
127
+ "app_version": "2.1.0",
128
+ "os_version": "17.2"
129
+ }
130
+ )
131
+ ```
132
+
133
+ ---
134
+
135
+ ### Send Transactional Email
136
+
137
+ ```python
138
+ from cdp_client import EmailPayload, Identifiers
139
+
140
+ await client.send_email(EmailPayload(
141
+ to="user@example.com",
142
+ identifiers=Identifiers(id="user-123"),
143
+ transactional_message_id="WELCOME_EMAIL",
144
+ subject="Welcome!",
145
+ body="<h1>Thanks for joining!</h1>",
146
+ body_plain="Thanks for joining!"
147
+ ))
148
+ ```
149
+
150
+ ---
151
+
152
+ ### Send Push Notification
153
+
154
+ ```python
155
+ from cdp_client import PushPayload, Identifiers
156
+
157
+ await client.send_push(PushPayload(
158
+ identifiers=Identifiers(id="user-123"),
159
+ transactional_message_id="PROMO_PUSH",
160
+ title="Flash Sale 🔥",
161
+ body="50% off for the next hour."
162
+ ))
163
+ ```
164
+
165
+ ---
166
+
167
+ ### Send SMS
168
+
169
+ ```python
170
+ from cdp_client import SmsPayload, Identifiers
171
+
172
+ await client.send_sms(SmsPayload(
173
+ identifiers=Identifiers(id="user-123"),
174
+ to="+14155551234", # Optional: raw phone number
175
+ transactional_message_id="OTP_MSG",
176
+ body="Your one-time code is 881234."
177
+ ))
178
+ ```
179
+
180
+ ---
181
+
182
+ ### Clear Identity / Logout
183
+
184
+ To reset the client's user context (e.g., on logout), close the current client and re-initialize without a user session:
185
+
186
+ ```python
187
+ # On user logout: flush pending work and release the HTTP client
188
+ await client.close()
189
+
190
+ # Re-initialize for anonymous or new user session
191
+ client = CDPClient(config)
192
+ ```
193
+
194
+ ---
195
+
196
+ ## Error Handling
197
+
198
+ By default, the Python SDK raises exceptions on HTTP errors or network failures. Wrap calls in `try/except` to handle them gracefully:
199
+
200
+ ```python
201
+ import httpx
202
+
203
+ try:
204
+ await client.identify("user-123", {"email": "user@example.com"})
205
+ except httpx.HTTPStatusError as e:
206
+ # Server returned 4xx or 5xx
207
+ print(f"API error {e.response.status_code}: {e.response.text}")
208
+ except Exception as e:
209
+ # Network error, timeout, etc.
210
+ print(f"Unexpected error: {e}")
211
+ ```
212
+
213
+ > All methods (`identify`, `track`, `send_email`, `send_push`, `send_sms`, `register_device`) raise on failure. Dual-write Customer.io errors are non-fatal and only emit a warning log.
214
+
215
+ ---
216
+
217
+ ## Configuration Options
218
+
219
+ | Option | Type | Default | Description |
220
+ |--------|------|---------|-------------|
221
+ | `cdp_api_key` | `str` | **Required** | Your CDP API Key |
222
+ | `cdp_endpoint` | `str` | Production URL | Custom CDP Gateway URL |
223
+ | `debug` | `bool` | `False` | Enable verbose debug logging |
224
+ | `send_to_customer_io` | `bool` | `False` | Enable dual-write to Customer.io |
225
+ | `customer_io` | `CustomerIoConfig` | `None` | Customer.io integration config (see below) |
226
+
227
+ ### `CustomerIoConfig` Options
228
+
229
+ | Option | Type | Description |
230
+ |--------|------|-------------|
231
+ | `site_id` | `str` | Customer.io Site ID |
232
+ | `api_key` | `str` | Customer.io API Key |
233
+ | `region` | `str` | `"us"` (default) or `"eu"` |
234
+
235
+ ---
236
+
237
+ ## Dual-Write to Customer.io
238
+
239
+ When `send_to_customer_io=True`, all `identify`, `track`, and `register_device` calls are mirrored to Customer.io automatically. Customer.io failures are **non-blocking** — the CDP call succeeds even if the Customer.io call fails.
240
+
241
+ ```python
242
+ config = CDPConfig(
243
+ cdp_api_key="your-cdp-api-key",
244
+ send_to_customer_io=True,
245
+ customer_io=CustomerIoConfig(
246
+ site_id="cio-site-id",
247
+ api_key="cio-api-key",
248
+ region="eu"
249
+ )
250
+ )
251
+ ```
252
+
253
+ ---
254
+
255
+ ## Development
256
+
257
+ ### Setup
258
+
259
+ ```bash
260
+ python3 -m venv venv
261
+ source venv/bin/activate
262
+ python -m pip install --upgrade pip setuptools wheel build
263
+ python -m pip install -e ".[test]"
264
+ ```
265
+
266
+ ### Run Tests
267
+
268
+ ```bash
269
+ pytest -v
270
+ ```
271
+
272
+ ### Building the Package
273
+
274
+ ```bash
275
+ python -m build
276
+ ```
277
+
278
+ This creates files in the `dist/` directory:
279
+ - `cdp_python_sdk-{version}-py3-none-any.whl`
280
+ - `cdp_python_sdk-{version}.tar.gz`
281
+
282
+ ---
283
+
284
+ ## Versioning
285
+
286
+ Follow [Semantic Versioning](https://semver.org/):
287
+
288
+ | Bump | When |
289
+ |------|------|
290
+ | **PATCH** `1.0.0 → 1.0.1` | Bug fixes |
291
+ | **MINOR** `1.0.0 → 1.1.0` | New features, backward compatible |
292
+ | **MAJOR** `1.0.0 → 2.0.0` | Breaking API changes |
@@ -0,0 +1,22 @@
1
+ from .client import CDPClient
2
+ from .errors import CDPError, CDPValidationError
3
+ from .models import (
4
+ CDPConfig,
5
+ CustomerIoConfig,
6
+ DeviceRegistrationParameters,
7
+ EmailPayload,
8
+ PushPayload,
9
+ SmsPayload,
10
+ )
11
+
12
+ __all__ = [
13
+ "CDPClient",
14
+ "CDPConfig",
15
+ "CDPError",
16
+ "CDPValidationError",
17
+ "CustomerIoConfig",
18
+ "DeviceRegistrationParameters",
19
+ "EmailPayload",
20
+ "PushPayload",
21
+ "SmsPayload",
22
+ ]