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.
- cutmeshort-1.0.1/LICENSE +21 -0
- cutmeshort-1.0.1/PKG-INFO +307 -0
- cutmeshort-1.0.1/README.md +291 -0
- cutmeshort-1.0.1/pyproject.toml +100 -0
- cutmeshort-1.0.1/setup.cfg +7 -0
- cutmeshort-1.0.1/src/cutmeshort/__init__.py +60 -0
- cutmeshort-1.0.1/src/cutmeshort/api/__init__.py +5 -0
- cutmeshort-1.0.1/src/cutmeshort/api/tracking_api.py +650 -0
- cutmeshort-1.0.1/src/cutmeshort/api_client.py +806 -0
- cutmeshort-1.0.1/src/cutmeshort/api_response.py +21 -0
- cutmeshort-1.0.1/src/cutmeshort/client.py +201 -0
- cutmeshort-1.0.1/src/cutmeshort/configuration.py +602 -0
- cutmeshort-1.0.1/src/cutmeshort/exceptions.py +247 -0
- cutmeshort-1.0.1/src/cutmeshort/models/__init__.py +20 -0
- cutmeshort-1.0.1/src/cutmeshort/models/error_response.py +95 -0
- cutmeshort-1.0.1/src/cutmeshort/models/lead_payload.py +112 -0
- cutmeshort-1.0.1/src/cutmeshort/models/sale_payload.py +107 -0
- cutmeshort-1.0.1/src/cutmeshort/models/track_response.py +89 -0
- cutmeshort-1.0.1/src/cutmeshort/py.typed +0 -0
- cutmeshort-1.0.1/src/cutmeshort/rest.py +268 -0
- cutmeshort-1.0.1/src/cutmeshort.egg-info/PKG-INFO +307 -0
- cutmeshort-1.0.1/src/cutmeshort.egg-info/SOURCES.txt +24 -0
- cutmeshort-1.0.1/src/cutmeshort.egg-info/dependency_links.txt +1 -0
- cutmeshort-1.0.1/src/cutmeshort.egg-info/requires.txt +4 -0
- cutmeshort-1.0.1/src/cutmeshort.egg-info/top_level.txt +1 -0
cutmeshort-1.0.1/LICENSE
ADDED
|
@@ -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
|