contackd-sync 0.2.0__tar.gz → 0.4.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,216 @@
1
+ Metadata-Version: 2.4
2
+ Name: contackd-sync
3
+ Version: 0.4.0
4
+ Summary: Sync CRM records and capture immutable person and business events with the Contackd API
5
+ Project-URL: Homepage, https://example.com/contackd-sync
6
+ Classifier: Programming Language :: Python :: 3
7
+ Classifier: Programming Language :: Python :: 3.9
8
+ Classifier: Programming Language :: Python :: 3.10
9
+ Classifier: Programming Language :: Python :: 3.11
10
+ Classifier: Programming Language :: Python :: 3.12
11
+ Classifier: Programming Language :: Python :: 3.13
12
+ Classifier: Programming Language :: Python :: 3.14
13
+ Requires-Python: >=3.9
14
+ Description-Content-Type: text/markdown
15
+ Requires-Dist: requests>=2.31.0
16
+ Requires-Dist: urllib3>=2.0.0
17
+
18
+ # contackd-sync (Python)
19
+
20
+ Python SDK for syncing CRM records (`contacts`, `leads`, `prospects`, and `businesses`), managing deals, and capturing immutable person and business events.
21
+
22
+ Requires Python 3.9 or later.
23
+
24
+ ## Install
25
+
26
+ ```bash
27
+ pip install contackd-sync
28
+ ```
29
+
30
+ ## 2-Minute Start (Push Customer Details)
31
+
32
+ ```python
33
+ from contackd_sync import ContackdClient, Contact
34
+
35
+ client = ContackdClient(
36
+ api_key="sk_live_...",
37
+ base_url="https://api.yourapp.com/api/v1", # no trailing slash required
38
+ )
39
+
40
+ # Create/update a customer contact by email
41
+ customer = client.contacts.upsert(
42
+ Contact(
43
+ email="jane@acme.com",
44
+ first_name="Jane",
45
+ last_name="Doe",
46
+ phone="+14155550199",
47
+ tags=["customer", "vip"],
48
+ custom={
49
+ "customer_id": "CUST-1001",
50
+ "plan": "pro",
51
+ "country": "US",
52
+ },
53
+ )
54
+ )
55
+
56
+ print(customer)
57
+ ```
58
+
59
+ ## Capture Custom Events
60
+
61
+ ```python
62
+ from contackd_sync import PersonEvent
63
+
64
+ event = client.person_events.track(
65
+ PersonEvent(
66
+ unified_id=customer.id,
67
+ event_name="financing",
68
+ status="applied",
69
+ correlation_id="application-10042",
70
+ amount="10000.00",
71
+ currency="NGN",
72
+ )
73
+ )
74
+
75
+ current_states = client.person_events.latest(customer.id)
76
+ ```
77
+
78
+ Use `BusinessEvent` with `client.business_events` for B2B actions. Both event resources expose `track`, `track_batch`, `list`, and `latest`; a retry-safe `event_id` is generated when omitted.
79
+
80
+ ## Manage Deals
81
+
82
+ ```python
83
+ from contackd_sync import Deal, DealStage, DealTransaction
84
+
85
+ deal = client.deals.create(
86
+ Deal(
87
+ contact_id=customer.id,
88
+ title="Annual facility management plan",
89
+ amount="150000.00",
90
+ currency="NGN",
91
+ )
92
+ )
93
+
94
+ client.deals.update(deal.id, assigned_to_user_id="sales-agent-id")
95
+ won = client.deals.update_stage(
96
+ deal.id,
97
+ DealStage.WON,
98
+ transaction=DealTransaction(external_transaction_id="payment-10042"),
99
+ )
100
+ ```
101
+
102
+ `client.deals` also provides `get` and `list`. List filters include
103
+ `contact_id`, `lead_id`, `conversation_id`, `stage`, `q`, and
104
+ `assigned_to_user_id`.
105
+
106
+ ## Common Customer Push Patterns
107
+
108
+ ### 1) Push one new customer
109
+
110
+ ```python
111
+ from contackd_sync import Contact
112
+
113
+ created = client.contacts.create(
114
+ Contact(email="new@acme.com", first_name="New", last_name="Customer")
115
+ )
116
+ ```
117
+
118
+ ### 2) Push many customers (bulk upsert)
119
+
120
+ ```python
121
+ from contackd_sync import Contact
122
+
123
+ result = client.contacts.bulk_upsert(
124
+ [
125
+ Contact(email="a@acme.com", first_name="A"),
126
+ Contact(email="b@acme.com", first_name="B", tags=["trial"]),
127
+ ]
128
+ )
129
+
130
+ print(len(result.created), len(result.updated), result.errors)
131
+ ```
132
+
133
+ ### 3) Read/filter customers
134
+
135
+ ```python
136
+ page = client.contacts.list(page=1, per_page=50, email="jane@acme.com")
137
+ ```
138
+
139
+ ## Sync Businesses Before Their Contacts
140
+
141
+ Create or update businesses first so contacts can reference the returned CRM
142
+ business IDs. Business synchronization uses `external_business_id` as its
143
+ primary natural key and falls back to the normalized business name.
144
+
145
+ ```python
146
+ from contackd_sync import Business, Contact, ContackdClient
147
+
148
+ client = ContackdClient(
149
+ api_key="sk_live_...",
150
+ base_url="https://api.yourapp.com/api/v1",
151
+ )
152
+
153
+ business_result = client.businesses.bulk_upsert(
154
+ [
155
+ Business(
156
+ name="Default Facility",
157
+ external_business_id="bb7d2b66-034a-4333-8573-dce07b9684ed",
158
+ stage="customer",
159
+ status="active",
160
+ tags=["FACILITY"],
161
+ custom={"org_code": "FACILITYBILLS"},
162
+ )
163
+ ]
164
+ )
165
+ business = [*business_result.created, *business_result.updated][0]
166
+
167
+ contact_result = client.contacts.bulk_upsert(
168
+ [
169
+ Contact(
170
+ email="resident@example.com",
171
+ first_name="Resident",
172
+ external_contact_id="resident-1",
173
+ business_id=business.id,
174
+ tags=["RESIDENT"],
175
+ custom={"role": "resident"},
176
+ )
177
+ ]
178
+ )
179
+ ```
180
+
181
+ Callers should merge duplicate source rows before sending them. For a person
182
+ belonging to multiple estates, keep the complete estate list in `custom` and
183
+ use `business_id` for the single primary CRM relationship.
184
+
185
+ ## API Key Setup
186
+
187
+ Contact support@dalmlabs.com for the API key and base domain.
188
+
189
+ ## Backend Routes Expected by SDK
190
+
191
+ Given `base_url = https://api.yourapp.com/api/v1`:
192
+
193
+ - `GET /health`
194
+ - `POST /tags`
195
+ - `GET /tags`
196
+ - `GET /tags/{id}`
197
+ - `PUT /tags/{id}`
198
+ - `DELETE /tags/{id}`
199
+ - `POST /contacts|leads|prospects|businesses`
200
+ - `GET /contacts|leads|prospects|businesses/{id}`
201
+ - `PUT /contacts|leads|prospects|businesses/{id}`
202
+ - `DELETE /contacts|leads|prospects|businesses/{id}`
203
+ - `GET /contacts|leads|prospects|businesses`
204
+ - `POST /contacts|leads|prospects|businesses/upsert`
205
+ - `POST /contacts|leads|prospects|businesses/bulk`
206
+ - `POST /contacts|leads|prospects|businesses/bulk/upsert`
207
+ - `POST /contacts|leads|prospects|businesses/bulk/delete`
208
+ - `POST /events/person|business`
209
+ - `POST /events/person|business/batch`
210
+ - `GET /events/person|business/{id}`
211
+ - `GET /events/person|business/{id}/latest`
212
+ - `POST /deals`
213
+ - `GET /deals`
214
+ - `GET /deals/{id}`
215
+ - `PATCH /deals/{id}`
216
+ - `PATCH /deals/{id}/stage`
@@ -0,0 +1,199 @@
1
+ # contackd-sync (Python)
2
+
3
+ Python SDK for syncing CRM records (`contacts`, `leads`, `prospects`, and `businesses`), managing deals, and capturing immutable person and business events.
4
+
5
+ Requires Python 3.9 or later.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ pip install contackd-sync
11
+ ```
12
+
13
+ ## 2-Minute Start (Push Customer Details)
14
+
15
+ ```python
16
+ from contackd_sync import ContackdClient, Contact
17
+
18
+ client = ContackdClient(
19
+ api_key="sk_live_...",
20
+ base_url="https://api.yourapp.com/api/v1", # no trailing slash required
21
+ )
22
+
23
+ # Create/update a customer contact by email
24
+ customer = client.contacts.upsert(
25
+ Contact(
26
+ email="jane@acme.com",
27
+ first_name="Jane",
28
+ last_name="Doe",
29
+ phone="+14155550199",
30
+ tags=["customer", "vip"],
31
+ custom={
32
+ "customer_id": "CUST-1001",
33
+ "plan": "pro",
34
+ "country": "US",
35
+ },
36
+ )
37
+ )
38
+
39
+ print(customer)
40
+ ```
41
+
42
+ ## Capture Custom Events
43
+
44
+ ```python
45
+ from contackd_sync import PersonEvent
46
+
47
+ event = client.person_events.track(
48
+ PersonEvent(
49
+ unified_id=customer.id,
50
+ event_name="financing",
51
+ status="applied",
52
+ correlation_id="application-10042",
53
+ amount="10000.00",
54
+ currency="NGN",
55
+ )
56
+ )
57
+
58
+ current_states = client.person_events.latest(customer.id)
59
+ ```
60
+
61
+ Use `BusinessEvent` with `client.business_events` for B2B actions. Both event resources expose `track`, `track_batch`, `list`, and `latest`; a retry-safe `event_id` is generated when omitted.
62
+
63
+ ## Manage Deals
64
+
65
+ ```python
66
+ from contackd_sync import Deal, DealStage, DealTransaction
67
+
68
+ deal = client.deals.create(
69
+ Deal(
70
+ contact_id=customer.id,
71
+ title="Annual facility management plan",
72
+ amount="150000.00",
73
+ currency="NGN",
74
+ )
75
+ )
76
+
77
+ client.deals.update(deal.id, assigned_to_user_id="sales-agent-id")
78
+ won = client.deals.update_stage(
79
+ deal.id,
80
+ DealStage.WON,
81
+ transaction=DealTransaction(external_transaction_id="payment-10042"),
82
+ )
83
+ ```
84
+
85
+ `client.deals` also provides `get` and `list`. List filters include
86
+ `contact_id`, `lead_id`, `conversation_id`, `stage`, `q`, and
87
+ `assigned_to_user_id`.
88
+
89
+ ## Common Customer Push Patterns
90
+
91
+ ### 1) Push one new customer
92
+
93
+ ```python
94
+ from contackd_sync import Contact
95
+
96
+ created = client.contacts.create(
97
+ Contact(email="new@acme.com", first_name="New", last_name="Customer")
98
+ )
99
+ ```
100
+
101
+ ### 2) Push many customers (bulk upsert)
102
+
103
+ ```python
104
+ from contackd_sync import Contact
105
+
106
+ result = client.contacts.bulk_upsert(
107
+ [
108
+ Contact(email="a@acme.com", first_name="A"),
109
+ Contact(email="b@acme.com", first_name="B", tags=["trial"]),
110
+ ]
111
+ )
112
+
113
+ print(len(result.created), len(result.updated), result.errors)
114
+ ```
115
+
116
+ ### 3) Read/filter customers
117
+
118
+ ```python
119
+ page = client.contacts.list(page=1, per_page=50, email="jane@acme.com")
120
+ ```
121
+
122
+ ## Sync Businesses Before Their Contacts
123
+
124
+ Create or update businesses first so contacts can reference the returned CRM
125
+ business IDs. Business synchronization uses `external_business_id` as its
126
+ primary natural key and falls back to the normalized business name.
127
+
128
+ ```python
129
+ from contackd_sync import Business, Contact, ContackdClient
130
+
131
+ client = ContackdClient(
132
+ api_key="sk_live_...",
133
+ base_url="https://api.yourapp.com/api/v1",
134
+ )
135
+
136
+ business_result = client.businesses.bulk_upsert(
137
+ [
138
+ Business(
139
+ name="Default Facility",
140
+ external_business_id="bb7d2b66-034a-4333-8573-dce07b9684ed",
141
+ stage="customer",
142
+ status="active",
143
+ tags=["FACILITY"],
144
+ custom={"org_code": "FACILITYBILLS"},
145
+ )
146
+ ]
147
+ )
148
+ business = [*business_result.created, *business_result.updated][0]
149
+
150
+ contact_result = client.contacts.bulk_upsert(
151
+ [
152
+ Contact(
153
+ email="resident@example.com",
154
+ first_name="Resident",
155
+ external_contact_id="resident-1",
156
+ business_id=business.id,
157
+ tags=["RESIDENT"],
158
+ custom={"role": "resident"},
159
+ )
160
+ ]
161
+ )
162
+ ```
163
+
164
+ Callers should merge duplicate source rows before sending them. For a person
165
+ belonging to multiple estates, keep the complete estate list in `custom` and
166
+ use `business_id` for the single primary CRM relationship.
167
+
168
+ ## API Key Setup
169
+
170
+ Contact support@dalmlabs.com for the API key and base domain.
171
+
172
+ ## Backend Routes Expected by SDK
173
+
174
+ Given `base_url = https://api.yourapp.com/api/v1`:
175
+
176
+ - `GET /health`
177
+ - `POST /tags`
178
+ - `GET /tags`
179
+ - `GET /tags/{id}`
180
+ - `PUT /tags/{id}`
181
+ - `DELETE /tags/{id}`
182
+ - `POST /contacts|leads|prospects|businesses`
183
+ - `GET /contacts|leads|prospects|businesses/{id}`
184
+ - `PUT /contacts|leads|prospects|businesses/{id}`
185
+ - `DELETE /contacts|leads|prospects|businesses/{id}`
186
+ - `GET /contacts|leads|prospects|businesses`
187
+ - `POST /contacts|leads|prospects|businesses/upsert`
188
+ - `POST /contacts|leads|prospects|businesses/bulk`
189
+ - `POST /contacts|leads|prospects|businesses/bulk/upsert`
190
+ - `POST /contacts|leads|prospects|businesses/bulk/delete`
191
+ - `POST /events/person|business`
192
+ - `POST /events/person|business/batch`
193
+ - `GET /events/person|business/{id}`
194
+ - `GET /events/person|business/{id}/latest`
195
+ - `POST /deals`
196
+ - `GET /deals`
197
+ - `GET /deals/{id}`
198
+ - `PATCH /deals/{id}`
199
+ - `PATCH /deals/{id}/stage`
@@ -3,17 +3,27 @@
3
3
  from ._version import __version__
4
4
  from .client import (
5
5
  AuthError,
6
+ Business,
7
+ BusinessesResource,
6
8
  BulkResult,
9
+ BusinessEvent,
10
+ BusinessEventsResource,
7
11
  ConflictStrategy,
8
12
  Contact,
9
13
  ContactsResource,
10
14
  ContackdClient,
11
15
  ContackdError,
16
+ Deal,
17
+ DealsResource,
18
+ DealStage,
19
+ DealTransaction,
12
20
  Lead,
13
21
  LeadStatus,
14
22
  LeadsResource,
15
23
  NotFoundError,
16
24
  PipelineStage,
25
+ PersonEvent,
26
+ PersonEventsResource,
17
27
  Prospect,
18
28
  ProspectsResource,
19
29
  RateLimitError,
@@ -21,22 +31,35 @@ from .client import (
21
31
  SyncConflictError,
22
32
  SyncEngine,
23
33
  SyncResult,
34
+ EventBatchError,
35
+ EventBatchResult,
36
+ EventList,
24
37
  )
25
38
 
26
39
  __all__ = [
27
40
  "__version__",
28
41
  "AuthError",
42
+ "Business",
43
+ "BusinessesResource",
29
44
  "BulkResult",
45
+ "BusinessEvent",
46
+ "BusinessEventsResource",
30
47
  "ConflictStrategy",
31
48
  "Contact",
32
49
  "ContactsResource",
33
50
  "ContackdClient",
34
51
  "ContackdError",
52
+ "Deal",
53
+ "DealsResource",
54
+ "DealStage",
55
+ "DealTransaction",
35
56
  "Lead",
36
57
  "LeadStatus",
37
58
  "LeadsResource",
38
59
  "NotFoundError",
39
60
  "PipelineStage",
61
+ "PersonEvent",
62
+ "PersonEventsResource",
40
63
  "Prospect",
41
64
  "ProspectsResource",
42
65
  "RateLimitError",
@@ -44,4 +67,7 @@ __all__ = [
44
67
  "SyncConflictError",
45
68
  "SyncEngine",
46
69
  "SyncResult",
70
+ "EventBatchError",
71
+ "EventBatchResult",
72
+ "EventList",
47
73
  ]