ccai-python 1.0.0__tar.gz → 1.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.
Files changed (42) hide show
  1. ccai_python-1.1.0/PKG-INFO +507 -0
  2. ccai_python-1.1.0/README.md +486 -0
  3. {ccai_python-1.0.0 → ccai_python-1.1.0}/pyproject.toml +1 -1
  4. ccai_python-1.1.0/setup.py +26 -0
  5. ccai_python-1.1.0/src/ccai_python/__init__.py +58 -0
  6. ccai_python-1.1.0/src/ccai_python/brand_service.py +91 -0
  7. ccai_python-1.1.0/src/ccai_python/campaign_service.py +125 -0
  8. ccai_python-1.1.0/src/ccai_python/ccai.py +272 -0
  9. ccai_python-1.1.0/src/ccai_python/contact_service.py +33 -0
  10. ccai_python-1.1.0/src/ccai_python/contact_test.py +30 -0
  11. ccai_python-1.1.0/src/ccai_python/contact_validator_service.py +76 -0
  12. ccai_python-1.1.0/src/ccai_python/email_send.py +30 -0
  13. ccai_python-1.1.0/src/ccai_python/email_service.py +288 -0
  14. ccai_python-1.1.0/src/ccai_python/mms_send.py +31 -0
  15. {ccai_python-1.0.0 → ccai_python-1.1.0}/src/ccai_python/sms/__init__.py +2 -1
  16. ccai_python-1.1.0/src/ccai_python/sms/mms.py +468 -0
  17. {ccai_python-1.0.0 → ccai_python-1.1.0}/src/ccai_python/sms/sms.py +80 -100
  18. ccai_python-1.1.0/src/ccai_python/sms_send.py +28 -0
  19. ccai_python-1.1.0/src/ccai_python/webhook.py +171 -0
  20. ccai_python-1.1.0/src/ccai_python/webhook_handler_example.py +91 -0
  21. ccai_python-1.1.0/src/ccai_python/webhook_test.py +38 -0
  22. ccai_python-1.1.0/src/ccai_python.egg-info/PKG-INFO +507 -0
  23. ccai_python-1.1.0/src/ccai_python.egg-info/SOURCES.txt +31 -0
  24. ccai_python-1.1.0/src/ccai_python.egg-info/top_level.txt +1 -0
  25. {ccai_python-1.0.0 → ccai_python-1.1.0}/tests/test_ccai.py +33 -10
  26. ccai_python-1.1.0/tests/test_contact.py +115 -0
  27. ccai_python-1.1.0/tests/test_email.py +135 -0
  28. ccai_python-1.1.0/tests/test_mms.py +552 -0
  29. {ccai_python-1.0.0 → ccai_python-1.1.0}/tests/test_sms.py +68 -12
  30. ccai_python-1.1.0/tests/test_webhook.py +178 -0
  31. ccai_python-1.0.0/PKG-INFO +0 -83
  32. ccai_python-1.0.0/README.md +0 -63
  33. ccai_python-1.0.0/src/ccai_python/__init__.py +0 -21
  34. ccai_python-1.0.0/src/ccai_python/ccai.py +0 -149
  35. ccai_python-1.0.0/src/ccai_python/examples/async_example.py +0 -248
  36. ccai_python-1.0.0/src/ccai_python/examples/basic_example.py +0 -79
  37. ccai_python-1.0.0/src/ccai_python.egg-info/PKG-INFO +0 -83
  38. ccai_python-1.0.0/src/ccai_python.egg-info/SOURCES.txt +0 -15
  39. ccai_python-1.0.0/src/ccai_python.egg-info/top_level.txt +0 -3
  40. {ccai_python-1.0.0 → ccai_python-1.1.0}/setup.cfg +0 -0
  41. {ccai_python-1.0.0 → ccai_python-1.1.0}/src/ccai_python.egg-info/dependency_links.txt +0 -0
  42. {ccai_python-1.0.0 → ccai_python-1.1.0}/src/ccai_python.egg-info/requires.txt +0 -0
@@ -0,0 +1,507 @@
1
+ Metadata-Version: 2.4
2
+ Name: ccai-python
3
+ Version: 1.1.0
4
+ Summary: Python client for CloudContactAI API
5
+ Author: CloudContactAI LLC
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/cloudcontactai/ccai-python
8
+ Project-URL: Bug Tracker, https://github.com/cloudcontactai/ccai-python/issues
9
+ Keywords: sms,api,client,cloud,contact,ai,cloudcontactai
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: OS Independent
16
+ Requires-Python: >=3.8
17
+ Description-Content-Type: text/markdown
18
+ Requires-Dist: requests>=2.31.0
19
+ Requires-Dist: pydantic>=2.5.0
20
+ Dynamic: requires-python
21
+
22
+ # CCAI Python Client
23
+
24
+ Version: 1.1.0
25
+
26
+ A Python client for interacting with the CloudContactAI API.
27
+
28
+ ## Installation
29
+
30
+ ```bash
31
+ pip install ccai-python
32
+ ```
33
+
34
+ ## Usage
35
+
36
+ ### SMS
37
+
38
+ ```python
39
+ from ccai_python import CCAI
40
+
41
+ # Initialize the client
42
+ ccai = CCAI(
43
+ client_id="YOUR-CLIENT-ID",
44
+ api_key="YOUR-API-KEY"
45
+ )
46
+
47
+ # Send a single SMS
48
+ response = ccai.sms.send_single(
49
+ first_name="John",
50
+ last_name="Doe",
51
+ phone="+15551234567",
52
+ message="Hello ${first_name}, this is a test message!",
53
+ title="Test Campaign"
54
+ )
55
+
56
+ print(f"Message sent with ID: {response.id}")
57
+
58
+ # Send to multiple recipients
59
+ accounts = [
60
+ {"first_name": "John", "last_name": "Doe", "phone": "+15551234567"},
61
+ {"first_name": "Jane", "last_name": "Smith", "phone": "+15559876543"}
62
+ ]
63
+
64
+ campaign_response = ccai.sms.send(
65
+ accounts=accounts,
66
+ message="Hello ${first_name} ${last_name}, this is a test message!",
67
+ title="Bulk Test Campaign"
68
+ )
69
+
70
+ print(f"Campaign sent with ID: {campaign_response.campaign_id}")
71
+ ```
72
+
73
+ ### MMS
74
+
75
+ ```python
76
+ from ccai_python import CCAI, Account, SMSOptions
77
+
78
+ # Initialize the client
79
+ ccai = CCAI(
80
+ client_id="YOUR-CLIENT-ID",
81
+ api_key="YOUR-API-KEY"
82
+ )
83
+
84
+ # Define a progress callback
85
+ def track_progress(status):
86
+ print(f"Progress: {status}")
87
+
88
+ # Create options with progress tracking
89
+ options = SMSOptions(
90
+ timeout=60,
91
+ retries=3,
92
+ on_progress=track_progress
93
+ )
94
+
95
+ # Complete MMS workflow (get URL, upload image, send MMS)
96
+ image_path = "path/to/your/image.jpg"
97
+ content_type = "image/jpeg"
98
+
99
+ # Define recipient
100
+ account = Account(
101
+ first_name="John",
102
+ last_name="Doe",
103
+ phone="+15551234567" # Use E.164 format
104
+ )
105
+
106
+ # Send MMS with image in one step
107
+ response = ccai.mms.send_with_image(
108
+ image_path=image_path,
109
+ content_type=content_type,
110
+ accounts=[account],
111
+ message="Hello ${first_name}, check out this image!",
112
+ title="MMS Campaign Example",
113
+ options=options
114
+ )
115
+
116
+ print(f"MMS sent! Campaign ID: {response.campaign_id}")
117
+ ```
118
+
119
+ ### Email
120
+
121
+ ```python
122
+ from ccai_python import CCAI, EmailAccount, EmailCampaign
123
+ from datetime import datetime, timedelta
124
+
125
+ # Initialize the client
126
+ ccai = CCAI(
127
+ client_id="YOUR-CLIENT-ID",
128
+ api_key="YOUR-API-KEY"
129
+ )
130
+
131
+ # Send a single email
132
+ response = ccai.email.send_single(
133
+ first_name="John",
134
+ last_name="Doe",
135
+ email="john@example.com",
136
+ subject="Welcome to Our Service",
137
+ message="<p>Hello John,</p><p>Thank you for signing up!</p>",
138
+ sender_email="noreply@yourcompany.com",
139
+ reply_email="support@yourcompany.com",
140
+ sender_name="Your Company",
141
+ title="Welcome Email"
142
+ )
143
+
144
+ print(f"Email sent with ID: {response.id}")
145
+
146
+ # Send email campaign to multiple recipients
147
+ accounts = [
148
+ EmailAccount(
149
+ first_name="John",
150
+ last_name="Doe",
151
+ email="john@example.com",
152
+ phone=""
153
+ ),
154
+ EmailAccount(
155
+ first_name="Jane",
156
+ last_name="Smith",
157
+ email="jane@example.com",
158
+ phone=""
159
+ )
160
+ ]
161
+
162
+ campaign = EmailCampaign(
163
+ subject="Monthly Newsletter",
164
+ title="July 2025 Newsletter",
165
+ message="<h1>Hello ${firstName}!</h1><p>Here's our monthly update...</p>",
166
+ sender_email="newsletter@yourcompany.com",
167
+ reply_email="support@yourcompany.com",
168
+ sender_name="Your Company Newsletter",
169
+ accounts=accounts
170
+ )
171
+
172
+ response = ccai.email.send_campaign(campaign)
173
+ print(f"Email campaign sent: {response}")
174
+
175
+ # Schedule an email for future delivery
176
+ tomorrow = datetime.now() + timedelta(days=1)
177
+ tomorrow = tomorrow.replace(hour=10, minute=0, second=0, microsecond=0)
178
+
179
+ scheduled_campaign = EmailCampaign(
180
+ subject="Scheduled Email",
181
+ title="Future Email",
182
+ message="<p>This email was scheduled in advance!</p>",
183
+ sender_email="scheduled@yourcompany.com",
184
+ reply_email="support@yourcompany.com",
185
+ sender_name="Your Company",
186
+ accounts=[accounts[0]],
187
+ scheduled_timestamp=tomorrow.isoformat(),
188
+ scheduled_timezone="America/New_York"
189
+ )
190
+
191
+ response = ccai.email.send_campaign(scheduled_campaign)
192
+ print(f"Email scheduled: {response}")
193
+ ```
194
+
195
+ ### Contacts
196
+
197
+ ```python
198
+ from ccai_python import CCAI
199
+
200
+ # Initialize the client
201
+ ccai = CCAI(
202
+ client_id="YOUR-CLIENT-ID",
203
+ api_key="YOUR-API-KEY"
204
+ )
205
+
206
+ # Set do not text status using contact ID
207
+ response = ccai.contact.set_do_not_text(
208
+ do_not_text=True,
209
+ contact_id="your-contact-id"
210
+ )
211
+ print(f"Contact {response.contact_id} do not text set to {response.do_not_text}")
212
+
213
+ # Set do not text status using phone number
214
+ response = ccai.contact.set_do_not_text(
215
+ do_not_text=True,
216
+ phone="+15551234567"
217
+ )
218
+ print(f"Contact {response.contact_id} ({response.phone}) do not text set to {response.do_not_text}")
219
+
220
+ # Remove do not text status from a contact
221
+ response = ccai.contact.set_do_not_text(
222
+ do_not_text=False,
223
+ contact_id="your-contact-id"
224
+ )
225
+ print(f"Contact {response.contact_id} do not text removed: {response.do_not_text}")
226
+ ```
227
+
228
+ ### Contact Validator
229
+
230
+ Validate email addresses and phone numbers.
231
+
232
+ > Bulk endpoints accept up to 50 contacts per request and are processed server-side in chunks.
233
+
234
+ ```python
235
+ from ccai_python import CCAI
236
+
237
+ ccai = CCAI(
238
+ client_id="YOUR-CLIENT-ID",
239
+ api_key="YOUR-API-KEY"
240
+ )
241
+
242
+ # Validate a single email
243
+ email_result = ccai.contact_validator.validate_email("user@example.com")
244
+ print(email_result.status) # "valid" | "invalid" | "risky"
245
+ print(email_result.metadata.get("safe_to_send")) # True | False
246
+
247
+ # Validate multiple emails (up to 50, processed server-side in chunks)
248
+ bulk_emails = ccai.contact_validator.validate_emails(["user@example.com", "bad@invalid.xyz"])
249
+ print(bulk_emails.summary.model_dump()) # {"total": 2, "valid": 1, "invalid": 1, "risky": 0, "landline": 0}
250
+
251
+ # Validate a single phone number
252
+ phone_result = ccai.contact_validator.validate_phone("+15551234567", country_code="US")
253
+ print(phone_result.status) # "valid" | "invalid" | "landline"
254
+ print(phone_result.metadata.get("carrier_type")) # "mobile" | "landline" | "voip"
255
+
256
+ # Validate multiple phone numbers (up to 50, processed server-side in chunks)
257
+ bulk_phones = ccai.contact_validator.validate_phones([
258
+ {"phone": "+15551234567"},
259
+ {"phone": "+15559876543", "countryCode": "US"}
260
+ ])
261
+ print(bulk_phones.summary.model_dump()) # {"total": 2, "valid": 1, "invalid": 0, "risky": 0, "landline": 1}
262
+ ```
263
+
264
+ ### Webhooks
265
+
266
+ ```python
267
+ from ccai_python import CCAI, WebhookConfig, WebhookEventType
268
+
269
+ # Initialize the client
270
+ ccai = CCAI(
271
+ client_id="YOUR-CLIENT-ID",
272
+ api_key="YOUR-API-KEY"
273
+ )
274
+
275
+ # Example 1: Register a webhook with auto-generated secret
276
+ # If secret is not provided, the server will auto-generate one
277
+ config = WebhookConfig(
278
+ url="https://your-domain.com/api/ccai-webhook",
279
+ events=[WebhookEventType.MESSAGE_SENT, WebhookEventType.MESSAGE_RECEIVED]
280
+ # secret not provided - server will auto-generate and return it
281
+ )
282
+
283
+ webhook = ccai.webhook.register(config)
284
+ print(f"Webhook registered with ID: {webhook.id}")
285
+ print(f"Auto-generated Secret: {webhook.secretKey}")
286
+
287
+ # Example 2: Register a webhook with a custom secret
288
+ config_custom = WebhookConfig(
289
+ url="https://your-domain.com/api/ccai-webhook-v2",
290
+ events=[WebhookEventType.MESSAGE_SENT, WebhookEventType.MESSAGE_RECEIVED],
291
+ secret="your-custom-secret-key"
292
+ )
293
+
294
+ webhook_custom = ccai.webhook.register(config_custom)
295
+ print(f"Webhook with custom secret registered: {webhook_custom.id}")
296
+
297
+ # List all webhooks
298
+ webhooks = ccai.webhook.list()
299
+ print(f"Found {len(webhooks)} webhooks")
300
+
301
+ # Update a webhook
302
+ updated_webhook = ccai.webhook.update(webhook.id, {
303
+ "url": "https://your-domain.com/api/ccai-webhook-v3"
304
+ })
305
+ print(f"Webhook updated: {updated_webhook.url}")
306
+
307
+ # Delete a webhook
308
+ result = ccai.webhook.delete(webhook.id)
309
+ print(f"Webhook deleted: {result}")
310
+
311
+ # Verify webhook signature in your handler
312
+ def verify_and_handle_webhook(signature, client_id, event_hash, secret):
313
+ if ccai.webhook.verify_signature(signature, client_id, event_hash, secret):
314
+ print(f"Valid webhook signature verified")
315
+ else:
316
+ print("Invalid signature")
317
+
318
+ # Create a webhook handler for web frameworks
319
+ def handle_message_sent(event):
320
+ print(f"Message sent: {event.message} to {event.to}")
321
+
322
+ def handle_message_received(event):
323
+ print(f"Message received: {event.message} from {event.from_}")
324
+
325
+ handlers = {
326
+ 'on_message_sent': handle_message_sent,
327
+ 'on_message_received': handle_message_received
328
+ }
329
+
330
+ webhook_handler = ccai.webhook.create_handler(handlers)
331
+
332
+ # Use with Flask
333
+ from flask import Flask, request, jsonify
334
+ import json
335
+
336
+ app = Flask(__name__)
337
+
338
+ @app.route('/api/ccai-webhook', methods=['POST'])
339
+ def handle_webhook():
340
+ signature = request.headers.get('X-CCAI-Signature', '')
341
+ body = request.get_data(as_text=True)
342
+ secret = 'your-webhook-secret-key' # Use the secret from webhook registration
343
+
344
+ # Parse payload to get client_id and event_hash
345
+ payload = json.loads(body)
346
+ client_id = os.getenv('CCAI_CLIENT_ID')
347
+ event_hash = payload.get('eventHash', '')
348
+
349
+ # Verify signature
350
+ if not ccai.webhook.verify_signature(signature, client_id, event_hash, secret):
351
+ return jsonify({"error": "Invalid signature"}), 401
352
+
353
+ # Process webhook
354
+ result = webhook_handler(payload)
355
+ return jsonify(result)
356
+ ```
357
+
358
+ ### Brands
359
+
360
+ Register and manage brands for TCR (The Campaign Registry) business verification.
361
+
362
+ ```python
363
+ from ccai_python import CCAI
364
+
365
+ ccai = CCAI(
366
+ client_id="YOUR-CLIENT-ID",
367
+ api_key="YOUR-API-KEY"
368
+ )
369
+
370
+ # Create a brand
371
+ brand = ccai.brands.create({
372
+ "legalCompanyName": "Collect.org Inc.",
373
+ "dba": "Collect",
374
+ "entityType": "NON_PROFIT",
375
+ "taxId": "123456789",
376
+ "taxIdCountry": "US",
377
+ "country": "US",
378
+ "verticalType": "NON_PROFIT",
379
+ "websiteUrl": "https://www.collect.org",
380
+ "street": "123 Main Street",
381
+ "city": "San Francisco",
382
+ "state": "CA",
383
+ "postalCode": "94105",
384
+ "contactFirstName": "Jane",
385
+ "contactLastName": "Doe",
386
+ "contactEmail": "jane@collect.org",
387
+ "contactPhone": "+14155551234",
388
+ })
389
+ print(f"Brand created with ID: {brand['id']}")
390
+
391
+ # Get a brand by ID
392
+ fetched = ccai.brands.get(brand["id"])
393
+ print(f"Website match score: {fetched.get('websiteMatchScore')}")
394
+
395
+ # List all brands for the account
396
+ brands = ccai.brands.list()
397
+ print(f"Found {len(brands)} brand(s)")
398
+
399
+ # Update a brand (partial update)
400
+ updated = ccai.brands.update(brand["id"], {
401
+ "street": "456 Oak Avenue",
402
+ "city": "Los Angeles",
403
+ })
404
+
405
+ # Delete a brand
406
+ ccai.brands.delete(brand["id"])
407
+ ```
408
+
409
+ #### Entity Types
410
+
411
+ `PRIVATE_PROFIT`, `PUBLIC_PROFIT`, `NON_PROFIT`, `GOVERNMENT`, `SOLE_PROPRIETOR`
412
+
413
+ > Note: `PUBLIC_PROFIT` entities require `stockSymbol` and `stockExchange` fields.
414
+
415
+ #### Vertical Types
416
+
417
+ `AUTOMOTIVE`, `AGRICULTURE`, `BANKING`, `COMMUNICATION`, `CONSTRUCTION`, `EDUCATION`, `ENERGY`, `ENTERTAINMENT`, `GOVERNMENT`, `HEALTHCARE`, `HOSPITALITY`, `INSURANCE`, `LEGAL`, `MANUFACTURING`, `NON_PROFIT`, `PROFESSIONAL`, `REAL_ESTATE`, `RETAIL`, `TECHNOLOGY`, `TRANSPORTATION`
418
+
419
+ ### Campaigns
420
+
421
+ Register and manage campaigns for TCR (The Campaign Registry) carrier vetting. Each campaign must be linked to a verified brand.
422
+
423
+ ```python
424
+ from ccai_python import CCAI
425
+
426
+ ccai = CCAI(
427
+ client_id="YOUR-CLIENT-ID",
428
+ api_key="YOUR-API-KEY"
429
+ )
430
+
431
+ # Create a campaign
432
+ campaign = ccai.campaigns.create({
433
+ "brandId": 1,
434
+ "useCase": "MIXED",
435
+ "subUseCases": ["CUSTOMER_CARE", "TWO_FACTOR_AUTHENTICATION", "ACCOUNT_NOTIFICATION"],
436
+ "description": "Security codes and support messaging.",
437
+ "messageFlow": "Users opt-in via signup form at https://example.com/signup",
438
+ "hasEmbeddedLinks": True,
439
+ "hasEmbeddedPhone": False,
440
+ "isAgeGated": False,
441
+ "isDirectLending": False,
442
+ "optInKeywords": ["START"],
443
+ "optInMessage": "Welcome! Reply STOP to cancel.",
444
+ "optInProofUrl": "https://example.com/opt-in-proof.png",
445
+ "helpKeywords": ["HELP"],
446
+ "helpMessage": "For HELP email support@example.com.",
447
+ "optOutKeywords": ["STOP"],
448
+ "optOutMessage": "STOP received. You are unsubscribed.",
449
+ "sampleMessages": [
450
+ "Your code is 554321. Reply STOP to cancel.",
451
+ "Your ticket has been updated. Reply HELP for info."
452
+ ]
453
+ })
454
+ print(f"Campaign created with ID: {campaign['id']}")
455
+
456
+ # Get a campaign by ID
457
+ fetched = ccai.campaigns.get(campaign["id"])
458
+
459
+ # List all campaigns for the account
460
+ campaigns = ccai.campaigns.list()
461
+ print(f"Found {len(campaigns)} campaign(s)")
462
+
463
+ # Update a campaign (partial update)
464
+ updated = ccai.campaigns.update(campaign["id"], {
465
+ "description": "Updated description."
466
+ })
467
+
468
+ # Delete a campaign
469
+ ccai.campaigns.delete(campaign["id"])
470
+ ```
471
+
472
+ #### Use Cases
473
+
474
+ `TWO_FACTOR_AUTHENTICATION`, `ACCOUNT_NOTIFICATION`, `CUSTOMER_CARE`, `DELIVERY_NOTIFICATION`, `FRAUD_ALERT`, `HIGHER_EDUCATION`, `LOW_VOLUME_MIXED`, `MARKETING`, `MIXED`, `POLLING_VOTING`, `PUBLIC_SERVICE_ANNOUNCEMENT`, `SECURITY_ALERT`
475
+
476
+ > Note: `MIXED` and `LOW_VOLUME_MIXED` campaigns require 2–3 `subUseCases`.
477
+
478
+ #### Sub-Use Cases
479
+
480
+ `TWO_FACTOR_AUTHENTICATION`, `ACCOUNT_NOTIFICATION`, `CUSTOMER_CARE`, `DELIVERY_NOTIFICATION`, `FRAUD_ALERT`, `MARKETING`, `POLLING_VOTING`
481
+
482
+ ## Features
483
+
484
+ - Send SMS messages to single or multiple recipients
485
+ - Send MMS messages with images
486
+ - Send Email campaigns with HTML content
487
+ - Schedule emails for future delivery
488
+ - Brand registration and management for TCR verification
489
+ - Campaign registration and management for TCR carrier vetting
490
+ - Webhook management (register, update, list, delete)
491
+ - Webhook event handling for web frameworks
492
+ - Validate email addresses (valid/invalid/risky) and phone numbers (valid/invalid/landline)
493
+ - Upload images to S3 with signed URLs
494
+ - Variable substitution in messages
495
+ - Progress tracking callbacks
496
+ - Type hints for better IDE integration
497
+ - Comprehensive error handling
498
+
499
+ ## Requirements
500
+
501
+ - Python 3.10 or higher
502
+ - `requests` library
503
+ - `pydantic` library
504
+
505
+ ## License
506
+
507
+ MIT