ccai-python 1.0.1__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 (38) hide show
  1. {ccai_python-1.0.1 → ccai_python-1.1.0}/PKG-INFO +206 -11
  2. ccai_python-1.1.0/README.md +486 -0
  3. {ccai_python-1.0.1 → ccai_python-1.1.0}/pyproject.toml +1 -1
  4. {ccai_python-1.0.1 → ccai_python-1.1.0}/setup.py +2 -2
  5. {ccai_python-1.0.1 → ccai_python-1.1.0}/src/ccai_python/__init__.py +24 -5
  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.0.1 → ccai_python-1.1.0}/src/ccai_python/ccai.py +92 -25
  9. {ccai_python-1.0.1 → ccai_python-1.1.0}/src/ccai_python/contact_test.py +2 -2
  10. ccai_python-1.1.0/src/ccai_python/contact_validator_service.py +76 -0
  11. {ccai_python-1.0.1 → ccai_python-1.1.0}/src/ccai_python/email_send.py +5 -5
  12. {ccai_python-1.0.1 → ccai_python-1.1.0}/src/ccai_python/email_service.py +127 -41
  13. {ccai_python-1.0.1 → ccai_python-1.1.0}/src/ccai_python/mms_send.py +7 -6
  14. {ccai_python-1.0.1 → ccai_python-1.1.0}/src/ccai_python/sms/mms.py +28 -12
  15. {ccai_python-1.0.1 → ccai_python-1.1.0}/src/ccai_python/sms/sms.py +49 -19
  16. {ccai_python-1.0.1 → ccai_python-1.1.0}/src/ccai_python/sms_send.py +7 -6
  17. ccai_python-1.1.0/src/ccai_python/webhook.py +171 -0
  18. ccai_python-1.1.0/src/ccai_python/webhook_handler_example.py +91 -0
  19. {ccai_python-1.0.1 → ccai_python-1.1.0}/src/ccai_python/webhook_test.py +7 -6
  20. {ccai_python-1.0.1 → ccai_python-1.1.0}/src/ccai_python.egg-info/PKG-INFO +206 -11
  21. {ccai_python-1.0.1 → ccai_python-1.1.0}/src/ccai_python.egg-info/SOURCES.txt +6 -3
  22. {ccai_python-1.0.1 → ccai_python-1.1.0}/tests/test_ccai.py +33 -10
  23. ccai_python-1.1.0/tests/test_email.py +135 -0
  24. {ccai_python-1.0.1 → ccai_python-1.1.0}/tests/test_mms.py +25 -0
  25. {ccai_python-1.0.1 → ccai_python-1.1.0}/tests/test_sms.py +58 -3
  26. ccai_python-1.1.0/tests/test_webhook.py +178 -0
  27. ccai_python-1.0.1/README.md +0 -291
  28. ccai_python-1.0.1/src/ccai_python/examples/async_example.py +0 -248
  29. ccai_python-1.0.1/src/ccai_python/examples/basic_example.py +0 -79
  30. ccai_python-1.0.1/src/ccai_python/webhook.py +0 -110
  31. ccai_python-1.0.1/src/ccai_python/webhook_handler_example.py +0 -46
  32. {ccai_python-1.0.1 → ccai_python-1.1.0}/setup.cfg +0 -0
  33. {ccai_python-1.0.1 → ccai_python-1.1.0}/src/ccai_python/contact_service.py +0 -0
  34. {ccai_python-1.0.1 → ccai_python-1.1.0}/src/ccai_python/sms/__init__.py +0 -0
  35. {ccai_python-1.0.1 → ccai_python-1.1.0}/src/ccai_python.egg-info/dependency_links.txt +0 -0
  36. {ccai_python-1.0.1 → ccai_python-1.1.0}/src/ccai_python.egg-info/requires.txt +0 -0
  37. {ccai_python-1.0.1 → ccai_python-1.1.0}/src/ccai_python.egg-info/top_level.txt +0 -0
  38. {ccai_python-1.0.1 → ccai_python-1.1.0}/tests/test_contact.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ccai-python
3
- Version: 1.0.1
3
+ Version: 1.1.0
4
4
  Summary: Python client for CloudContactAI API
5
5
  Author: CloudContactAI LLC
6
6
  License: MIT
@@ -21,7 +21,7 @@ Dynamic: requires-python
21
21
 
22
22
  # CCAI Python Client
23
23
 
24
- Version: 1.0.1
24
+ Version: 1.1.0
25
25
 
26
26
  A Python client for interacting with the CloudContactAI API.
27
27
 
@@ -225,6 +225,42 @@ response = ccai.contact.set_do_not_text(
225
225
  print(f"Contact {response.contact_id} do not text removed: {response.do_not_text}")
226
226
  ```
227
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
+
228
264
  ### Webhooks
229
265
 
230
266
  ```python
@@ -236,31 +272,49 @@ ccai = CCAI(
236
272
  api_key="YOUR-API-KEY"
237
273
  )
238
274
 
239
- # Register a webhook
275
+ # Example 1: Register a webhook with auto-generated secret
276
+ # If secret is not provided, the server will auto-generate one
240
277
  config = WebhookConfig(
241
278
  url="https://your-domain.com/api/ccai-webhook",
242
- events=[WebhookEventType.MESSAGE_SENT, WebhookEventType.MESSAGE_RECEIVED],
243
- secret="your-webhook-secret"
279
+ events=[WebhookEventType.MESSAGE_SENT, WebhookEventType.MESSAGE_RECEIVED]
280
+ # secret not provided - server will auto-generate and return it
244
281
  )
245
282
 
246
283
  webhook = ccai.webhook.register(config)
247
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}")
248
296
 
249
297
  # List all webhooks
250
298
  webhooks = ccai.webhook.list()
251
299
  print(f"Found {len(webhooks)} webhooks")
252
300
 
253
301
  # Update a webhook
254
- update_data = {
255
- "events": [WebhookEventType.MESSAGE_RECEIVED]
256
- }
257
- updated_webhook = ccai.webhook.update(webhook.id, update_data)
258
- print(f"Webhook updated: {updated_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}")
259
306
 
260
307
  # Delete a webhook
261
308
  result = ccai.webhook.delete(webhook.id)
262
309
  print(f"Webhook deleted: {result}")
263
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
+
264
318
  # Create a webhook handler for web frameworks
265
319
  def handle_message_sent(event):
266
320
  print(f"Message sent: {event.message} to {event.to}")
@@ -277,24 +331,165 @@ webhook_handler = ccai.webhook.create_handler(handlers)
277
331
 
278
332
  # Use with Flask
279
333
  from flask import Flask, request, jsonify
334
+ import json
280
335
 
281
336
  app = Flask(__name__)
282
337
 
283
338
  @app.route('/api/ccai-webhook', methods=['POST'])
284
339
  def handle_webhook():
285
- payload = request.get_json()
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
286
354
  result = webhook_handler(payload)
287
355
  return jsonify(result)
288
356
  ```
289
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
+
290
482
  ## Features
291
483
 
292
484
  - Send SMS messages to single or multiple recipients
293
485
  - Send MMS messages with images
294
486
  - Send Email campaigns with HTML content
295
487
  - Schedule emails for future delivery
488
+ - Brand registration and management for TCR verification
489
+ - Campaign registration and management for TCR carrier vetting
296
490
  - Webhook management (register, update, list, delete)
297
491
  - Webhook event handling for web frameworks
492
+ - Validate email addresses (valid/invalid/risky) and phone numbers (valid/invalid/landline)
298
493
  - Upload images to S3 with signed URLs
299
494
  - Variable substitution in messages
300
495
  - Progress tracking callbacks