cutmeshort 1.0.1__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,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 CutMeShort
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,307 @@
1
+ Metadata-Version: 2.4
2
+ Name: cutmeshort
3
+ Version: 1.0.1
4
+ Summary: CutMeShort CMS API
5
+ Author-email: CutMeShort <support@cutmeshort.com>
6
+ Project-URL: Repository, https://github.com/Cut-Me-Short/cms-python
7
+ Keywords: CutMeShort,CMS,Python SDK
8
+ Requires-Python: >=3.9
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ Requires-Dist: urllib3<3.0.0,>=2.1.0
12
+ Requires-Dist: python-dateutil>=2.8.2
13
+ Requires-Dist: pydantic>=2
14
+ Requires-Dist: typing-extensions>=4.7.1
15
+ Dynamic: license-file
16
+
17
+ # CutMeShort CMS Python SDK
18
+
19
+ The CutMeShort Python SDK enables you to track lead and sales events in your **Python applications** with ease.
20
+
21
+ It provides a clean and simple interface for:
22
+
23
+ * Lead tracking
24
+ * Deferred lead attribution (`mode="deferred"`)
25
+ * Sale / purchase tracking
26
+
27
+ ---
28
+
29
+ ## Installation
30
+
31
+ ### Install from PyPI (recommended)
32
+
33
+ ```bash
34
+ pip install cutmeshort
35
+ ```
36
+
37
+ ---
38
+
39
+ ## 📋 Requirements
40
+
41
+ * Python 3.9+
42
+
43
+ ---
44
+
45
+ ## Setup
46
+
47
+ Set your Bearer token:
48
+
49
+ ```bash
50
+ export CMS_BEARER_TOKEN="your_jwt_token"
51
+ ```
52
+
53
+ ---
54
+
55
+ ## âš¡ Quick Start
56
+
57
+ ```python
58
+ import os
59
+ from cutmeshort import CMSClient
60
+
61
+ client = CMSClient(token=os.environ["CMS_BEARER_TOKEN"])
62
+
63
+ response = client.track_lead(
64
+ click_id="id_123",
65
+ event_name="signup_started",
66
+ customer_external_id="user_42",
67
+ )
68
+
69
+ print(response)
70
+ ```
71
+
72
+ ---
73
+
74
+ ## What is Deferred Attribution?
75
+
76
+ Deferred attribution allows you to track a click **before knowing the customer identity**.
77
+
78
+ ### Flow:
79
+
80
+ 1. **First Call (store association)**
81
+
82
+ * Send `click_id` + `mode="deferred"`
83
+
84
+ 2. **Later Calls**
85
+
86
+ * Send normal events without `click_id`
87
+ * Backend automatically resolves mapping
88
+
89
+ ### Example Use Case
90
+
91
+ User clicks an ad → later signs up → events get linked automatically.
92
+
93
+ ---
94
+
95
+ ## Track a Lead Event (Standard Flow)
96
+
97
+ ```python
98
+ from cutmeshort import CMSClient
99
+
100
+ client = CMSClient(token="your_jwt_token")
101
+
102
+ response = client.track_lead(
103
+ click_id="id_123",
104
+ event_name="signup_started",
105
+ customer_external_id="user_42",
106
+ customer_name="Jane Doe", # optional
107
+ customer_email="jane@example.com", # optional
108
+ )
109
+
110
+ print(response)
111
+ ```
112
+
113
+ ---
114
+
115
+ ## Track a Lead Event (Deferred Flow)
116
+
117
+ ### Step 1: Store association
118
+
119
+ ```python
120
+ response = client.track_lead(
121
+ click_id="id_123",
122
+ event_name="lead_captured",
123
+ customer_external_id="user_42",
124
+ mode="deferred",
125
+ )
126
+ ```
127
+
128
+ ### Step 2: Send follow-up event
129
+
130
+ ```python
131
+ response = client.track_lead(
132
+ event_name="kyc_completed",
133
+ customer_external_id="user_42",
134
+ )
135
+ ```
136
+
137
+ ---
138
+
139
+ ## Track a Sale Event
140
+
141
+ ```python
142
+ from cutmeshort import CMSClient
143
+
144
+ client = CMSClient(token="your_jwt_token")
145
+
146
+ response = client.track_sale(
147
+ click_id="id_123",
148
+ event_name="purchase_completed",
149
+ customer_external_id="user_42",
150
+ invoice_id="inv_987",
151
+ amount=4999, # in cents
152
+ currency="USD", # 3-letter code
153
+ customer_email="jane@example.com", # optional
154
+ )
155
+
156
+ print(response)
157
+ ```
158
+
159
+ ---
160
+
161
+ ## CMSClient Reference
162
+
163
+ ### Initialization
164
+
165
+ ```python
166
+ from cutmeshort import CMSClient
167
+
168
+ client = CMSClient(token="your_jwt_token")
169
+
170
+ # Custom base URL
171
+ client = CMSClient(token="your_jwt_token", host="https://custom.com")
172
+ ```
173
+
174
+ ---
175
+
176
+ ## Methods
177
+
178
+ ### `track_lead()`
179
+
180
+ Track a lead event.
181
+
182
+ **Required:**
183
+
184
+ * `event_name: str`
185
+ * `customer_external_id: str`
186
+
187
+ **Optional:**
188
+
189
+ * `click_id: str`
190
+ * `customer_name: str`
191
+ * `customer_email: str`
192
+ * `customer_avatar: str`
193
+ * `timestamp: datetime`
194
+ * `mode: str` (`"deferred"` only)
195
+
196
+ ---
197
+
198
+ ### `track_sale()`
199
+
200
+ Track a sale event.
201
+
202
+ **Required:**
203
+
204
+ * `click_id: str`
205
+ * `event_name: str`
206
+ * `customer_external_id: str`
207
+ * `invoice_id: str`
208
+ * `amount: int` (in cents)
209
+ * `currency: str` (3-letter code)
210
+
211
+ **Optional:**
212
+
213
+ * `customer_name: str`
214
+ * `customer_email: str`
215
+ * `customer_avatar: str`
216
+ * `timestamp: datetime`
217
+
218
+ ---
219
+
220
+ ## Response Format
221
+
222
+ All methods return a dictionary:
223
+
224
+ ### Success Response
225
+
226
+ ```json
227
+ {
228
+ "success": true,
229
+ "message": "Event tracked successfully",
230
+ "data": {...}
231
+ }
232
+ ```
233
+
234
+ ### Error Response
235
+
236
+ ```json
237
+ {
238
+ "success": false,
239
+ "error": "Validation failed",
240
+ "status_code": 422
241
+ }
242
+ ```
243
+
244
+ ---
245
+
246
+ ## Error Handling
247
+
248
+ Errors are handled internally by the SDK.
249
+
250
+ ```python
251
+ response = client.track_lead(...)
252
+
253
+ if response.get("success"):
254
+ print("Success:", response["data"])
255
+ else:
256
+ print("Error:", response)
257
+ ```
258
+
259
+ ---
260
+
261
+ ## 📊 Payload Reference
262
+
263
+ ### Lead Payload
264
+
265
+ **Required:**
266
+
267
+ * `event_name`
268
+ * `customer_external_id`
269
+
270
+ **Conditionally Required:**
271
+
272
+ * `click_id` (for standard flow or deferred setup)
273
+
274
+ **Optional:**
275
+
276
+ * `timestamp`
277
+ * `customer_name`
278
+ * `customer_email`
279
+ * `customer_avatar`
280
+ * `mode` (`"deferred"` only)
281
+
282
+ ---
283
+
284
+ ### Sale Payload
285
+
286
+ **Required:**
287
+
288
+ * `click_id`
289
+ * `event_name`
290
+ * `customer_external_id`
291
+ * `invoice_id`
292
+ * `amount`
293
+ * `currency`
294
+
295
+ **Optional:**
296
+
297
+ * `timestamp`
298
+ * `customer_name`
299
+ * `customer_email`
300
+ * `customer_avatar`
301
+
302
+ ---
303
+
304
+ ## Example Scripts
305
+
306
+ - [examples/lead_example.py](examples/lead_example.py) – Lead tracking and deferred attribution
307
+ - [examples/sale_example.py](examples/sale_example.py) – Sale tracking
@@ -0,0 +1,291 @@
1
+ # CutMeShort CMS Python SDK
2
+
3
+ The CutMeShort Python SDK enables you to track lead and sales events in your **Python applications** with ease.
4
+
5
+ It provides a clean and simple interface for:
6
+
7
+ * Lead tracking
8
+ * Deferred lead attribution (`mode="deferred"`)
9
+ * Sale / purchase tracking
10
+
11
+ ---
12
+
13
+ ## Installation
14
+
15
+ ### Install from PyPI (recommended)
16
+
17
+ ```bash
18
+ pip install cutmeshort
19
+ ```
20
+
21
+ ---
22
+
23
+ ## 📋 Requirements
24
+
25
+ * Python 3.9+
26
+
27
+ ---
28
+
29
+ ## Setup
30
+
31
+ Set your Bearer token:
32
+
33
+ ```bash
34
+ export CMS_BEARER_TOKEN="your_jwt_token"
35
+ ```
36
+
37
+ ---
38
+
39
+ ## âš¡ Quick Start
40
+
41
+ ```python
42
+ import os
43
+ from cutmeshort import CMSClient
44
+
45
+ client = CMSClient(token=os.environ["CMS_BEARER_TOKEN"])
46
+
47
+ response = client.track_lead(
48
+ click_id="id_123",
49
+ event_name="signup_started",
50
+ customer_external_id="user_42",
51
+ )
52
+
53
+ print(response)
54
+ ```
55
+
56
+ ---
57
+
58
+ ## What is Deferred Attribution?
59
+
60
+ Deferred attribution allows you to track a click **before knowing the customer identity**.
61
+
62
+ ### Flow:
63
+
64
+ 1. **First Call (store association)**
65
+
66
+ * Send `click_id` + `mode="deferred"`
67
+
68
+ 2. **Later Calls**
69
+
70
+ * Send normal events without `click_id`
71
+ * Backend automatically resolves mapping
72
+
73
+ ### Example Use Case
74
+
75
+ User clicks an ad → later signs up → events get linked automatically.
76
+
77
+ ---
78
+
79
+ ## Track a Lead Event (Standard Flow)
80
+
81
+ ```python
82
+ from cutmeshort import CMSClient
83
+
84
+ client = CMSClient(token="your_jwt_token")
85
+
86
+ response = client.track_lead(
87
+ click_id="id_123",
88
+ event_name="signup_started",
89
+ customer_external_id="user_42",
90
+ customer_name="Jane Doe", # optional
91
+ customer_email="jane@example.com", # optional
92
+ )
93
+
94
+ print(response)
95
+ ```
96
+
97
+ ---
98
+
99
+ ## Track a Lead Event (Deferred Flow)
100
+
101
+ ### Step 1: Store association
102
+
103
+ ```python
104
+ response = client.track_lead(
105
+ click_id="id_123",
106
+ event_name="lead_captured",
107
+ customer_external_id="user_42",
108
+ mode="deferred",
109
+ )
110
+ ```
111
+
112
+ ### Step 2: Send follow-up event
113
+
114
+ ```python
115
+ response = client.track_lead(
116
+ event_name="kyc_completed",
117
+ customer_external_id="user_42",
118
+ )
119
+ ```
120
+
121
+ ---
122
+
123
+ ## Track a Sale Event
124
+
125
+ ```python
126
+ from cutmeshort import CMSClient
127
+
128
+ client = CMSClient(token="your_jwt_token")
129
+
130
+ response = client.track_sale(
131
+ click_id="id_123",
132
+ event_name="purchase_completed",
133
+ customer_external_id="user_42",
134
+ invoice_id="inv_987",
135
+ amount=4999, # in cents
136
+ currency="USD", # 3-letter code
137
+ customer_email="jane@example.com", # optional
138
+ )
139
+
140
+ print(response)
141
+ ```
142
+
143
+ ---
144
+
145
+ ## CMSClient Reference
146
+
147
+ ### Initialization
148
+
149
+ ```python
150
+ from cutmeshort import CMSClient
151
+
152
+ client = CMSClient(token="your_jwt_token")
153
+
154
+ # Custom base URL
155
+ client = CMSClient(token="your_jwt_token", host="https://custom.com")
156
+ ```
157
+
158
+ ---
159
+
160
+ ## Methods
161
+
162
+ ### `track_lead()`
163
+
164
+ Track a lead event.
165
+
166
+ **Required:**
167
+
168
+ * `event_name: str`
169
+ * `customer_external_id: str`
170
+
171
+ **Optional:**
172
+
173
+ * `click_id: str`
174
+ * `customer_name: str`
175
+ * `customer_email: str`
176
+ * `customer_avatar: str`
177
+ * `timestamp: datetime`
178
+ * `mode: str` (`"deferred"` only)
179
+
180
+ ---
181
+
182
+ ### `track_sale()`
183
+
184
+ Track a sale event.
185
+
186
+ **Required:**
187
+
188
+ * `click_id: str`
189
+ * `event_name: str`
190
+ * `customer_external_id: str`
191
+ * `invoice_id: str`
192
+ * `amount: int` (in cents)
193
+ * `currency: str` (3-letter code)
194
+
195
+ **Optional:**
196
+
197
+ * `customer_name: str`
198
+ * `customer_email: str`
199
+ * `customer_avatar: str`
200
+ * `timestamp: datetime`
201
+
202
+ ---
203
+
204
+ ## Response Format
205
+
206
+ All methods return a dictionary:
207
+
208
+ ### Success Response
209
+
210
+ ```json
211
+ {
212
+ "success": true,
213
+ "message": "Event tracked successfully",
214
+ "data": {...}
215
+ }
216
+ ```
217
+
218
+ ### Error Response
219
+
220
+ ```json
221
+ {
222
+ "success": false,
223
+ "error": "Validation failed",
224
+ "status_code": 422
225
+ }
226
+ ```
227
+
228
+ ---
229
+
230
+ ## Error Handling
231
+
232
+ Errors are handled internally by the SDK.
233
+
234
+ ```python
235
+ response = client.track_lead(...)
236
+
237
+ if response.get("success"):
238
+ print("Success:", response["data"])
239
+ else:
240
+ print("Error:", response)
241
+ ```
242
+
243
+ ---
244
+
245
+ ## 📊 Payload Reference
246
+
247
+ ### Lead Payload
248
+
249
+ **Required:**
250
+
251
+ * `event_name`
252
+ * `customer_external_id`
253
+
254
+ **Conditionally Required:**
255
+
256
+ * `click_id` (for standard flow or deferred setup)
257
+
258
+ **Optional:**
259
+
260
+ * `timestamp`
261
+ * `customer_name`
262
+ * `customer_email`
263
+ * `customer_avatar`
264
+ * `mode` (`"deferred"` only)
265
+
266
+ ---
267
+
268
+ ### Sale Payload
269
+
270
+ **Required:**
271
+
272
+ * `click_id`
273
+ * `event_name`
274
+ * `customer_external_id`
275
+ * `invoice_id`
276
+ * `amount`
277
+ * `currency`
278
+
279
+ **Optional:**
280
+
281
+ * `timestamp`
282
+ * `customer_name`
283
+ * `customer_email`
284
+ * `customer_avatar`
285
+
286
+ ---
287
+
288
+ ## Example Scripts
289
+
290
+ - [examples/lead_example.py](examples/lead_example.py) – Lead tracking and deferred attribution
291
+ - [examples/sale_example.py](examples/sale_example.py) – Sale tracking
@@ -0,0 +1,100 @@
1
+ [project]
2
+ name = "cutmeshort"
3
+ version = "1.0.1"
4
+ description = "CutMeShort CMS API"
5
+ authors = [
6
+ {name = "CutMeShort",email = "support@cutmeshort.com"},
7
+ ]
8
+ readme = "README.md"
9
+ keywords = ["CutMeShort", "CMS", "Python SDK"]
10
+ requires-python = ">=3.9"
11
+
12
+ dependencies = [
13
+ "urllib3>=2.1.0,<3.0.0",
14
+ "python-dateutil>=2.8.2",
15
+ "pydantic>=2",
16
+ "typing-extensions>=4.7.1",
17
+ ]
18
+
19
+ [project.urls]
20
+ Repository = "https://github.com/Cut-Me-Short/cms-python"
21
+
22
+ [tool.poetry]
23
+ requires-poetry = ">=2.0"
24
+
25
+ [tool.poetry.group.dev.dependencies]
26
+ pytest = ">= 7.2.1"
27
+ pytest-cov = ">= 2.8.1"
28
+ tox = ">= 3.9.0"
29
+ flake8 = ">= 4.0.0"
30
+ types-python-dateutil = ">= 2.8.19.14"
31
+ mypy = ">= 1.5"
32
+
33
+
34
+ [build-system]
35
+ requires = ["setuptools"]
36
+ build-backend = "setuptools.build_meta"
37
+
38
+ [tool.setuptools]
39
+ package-dir = {"" = "src"}
40
+
41
+ [tool.setuptools.packages.find]
42
+ where = ["src"]
43
+ include = ["cutmeshort*"]
44
+
45
+ [tool.pylint.'MESSAGES CONTROL']
46
+ extension-pkg-whitelist = "pydantic"
47
+
48
+ [tool.mypy]
49
+ files = [
50
+ "cutmeshort",
51
+ "src/test",
52
+ ]
53
+ # TODO: enable "strict" once all these individual checks are passing
54
+ # strict = true
55
+
56
+ # List from: https://mypy.readthedocs.io/en/stable/existing_code.html#introduce-stricter-options
57
+ warn_unused_configs = true
58
+ warn_redundant_casts = true
59
+ warn_unused_ignores = true
60
+
61
+ ## Getting these passing should be easy
62
+ strict_equality = true
63
+ extra_checks = true
64
+
65
+ ## Strongly recommend enabling this one as soon as you can
66
+ check_untyped_defs = true
67
+
68
+ ## These shouldn't be too much additional work, but may be tricky to
69
+ ## get passing if you use a lot of untyped libraries
70
+ disallow_subclassing_any = true
71
+ disallow_untyped_decorators = true
72
+ disallow_any_generics = true
73
+
74
+ ### These next few are various gradations of forcing use of type annotations
75
+ #disallow_untyped_calls = true
76
+ #disallow_incomplete_defs = true
77
+ #disallow_untyped_defs = true
78
+ #
79
+ ### This one isn't too hard to get passing, but return on investment is lower
80
+ #no_implicit_reexport = true
81
+ #
82
+ ### This one can be tricky to get passing if you use a lot of untyped libraries
83
+ #warn_return_any = true
84
+
85
+ [[tool.mypy.overrides]]
86
+ module = [
87
+ "cutmeshort.configuration",
88
+ ]
89
+ warn_unused_ignores = true
90
+ strict_equality = true
91
+ extra_checks = true
92
+ check_untyped_defs = true
93
+ disallow_subclassing_any = true
94
+ disallow_untyped_decorators = true
95
+ disallow_any_generics = true
96
+ disallow_untyped_calls = true
97
+ disallow_incomplete_defs = true
98
+ disallow_untyped_defs = true
99
+ no_implicit_reexport = true
100
+ warn_return_any = true
@@ -0,0 +1,7 @@
1
+ [flake8]
2
+ max-line-length = 99
3
+
4
+ [egg_info]
5
+ tag_build =
6
+ tag_date = 0
7
+