Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,42 @@ response = ccai.contact.set_do_not_text(
print(f"Contact {response.contact_id} do not text removed: {response.do_not_text}")
```

### Contact Validator

Validate email addresses and phone numbers.

> Bulk endpoints accept up to 50 contacts per request and are processed server-side in chunks.

```python
from ccai_python import CCAI

ccai = CCAI(
client_id="YOUR-CLIENT-ID",
api_key="YOUR-API-KEY"
)

# Validate a single email
email_result = ccai.contact_validator.validate_email("user@example.com")
print(email_result.status) # "valid" | "invalid" | "risky"
print(email_result.metadata.get("safe_to_send")) # True | False

# Validate multiple emails (up to 50, processed server-side in chunks)
bulk_emails = ccai.contact_validator.validate_emails(["user@example.com", "bad@invalid.xyz"])
print(bulk_emails.summary.model_dump()) # {"total": 2, "valid": 1, "invalid": 1, "risky": 0, "landline": 0}

# Validate a single phone number
phone_result = ccai.contact_validator.validate_phone("+15551234567", country_code="US")
print(phone_result.status) # "valid" | "invalid" | "landline"
print(phone_result.metadata.get("carrier_type")) # "mobile" | "landline" | "voip"

# Validate multiple phone numbers (up to 50, processed server-side in chunks)
bulk_phones = ccai.contact_validator.validate_phones([
{"phone": "+15551234567"},
{"phone": "+15559876543", "countryCode": "US"}
])
print(bulk_phones.summary.model_dump()) # {"total": 2, "valid": 1, "invalid": 0, "risky": 0, "landline": 1}
```

### Webhooks

```python
Expand Down Expand Up @@ -432,6 +468,7 @@ ccai.campaigns.delete(campaign["id"])
- Campaign registration and management for TCR carrier vetting
- Webhook management (register, update, list, delete)
- Webhook event handling for web frameworks
- Validate email addresses (valid/invalid/risky) and phone numbers (valid/invalid/landline)
- Upload images to S3 with signed URLs
- Variable substitution in messages
- Progress tracking callbacks
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "ccai-python"
version = "1.0.1"
version = "1.1.0"
description = "Python client for CloudContactAI API"
readme = "README.md"
requires-python = ">=3.10"
Expand Down
16 changes: 16 additions & 0 deletions src/ccai_python/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@
from .contact_service import Contact, ContactDoNotTextRequest, ContactDoNotTextResponse
from .brand_service import Brand
from .campaign_service import Campaign as CampaignService
from .contact_validator_service import (
ContactValidator,
EmailValidationResult,
PhoneValidationResult,
ValidationSummary,
BulkEmailValidationResult,
BulkPhoneValidationResult,
PhoneInput,
)

__all__ = [
'CCAI',
Expand All @@ -37,6 +46,13 @@
'ContactDoNotTextResponse',
'Brand',
'CampaignService',
'ContactValidator',
'EmailValidationResult',
'PhoneValidationResult',
'ValidationSummary',
'BulkEmailValidationResult',
'BulkPhoneValidationResult',
'PhoneInput',
]

__version__ = '1.0.1'
2 changes: 2 additions & 0 deletions src/ccai_python/ccai.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from .contact_service import Contact
from .brand_service import Brand
from .campaign_service import Campaign as CampaignService
from .contact_validator_service import ContactValidator


class CCAIConfig(BaseModel):
Expand Down Expand Up @@ -110,6 +111,7 @@ def __init__(
self.contact = Contact(self)
self.brands = Brand(self)
self.campaigns = CampaignService(self)
self.contact_validator = ContactValidator(self)

def _resolve_url(
self,
Expand Down
76 changes: 76 additions & 0 deletions src/ccai_python/contact_validator_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
"""
contact_validator_service.py - Email and phone contact validation via CloudContactAI

:license: MIT
:copyright: 2025 CloudContactAI LLC
"""

from typing import Any, Dict, List, Optional
from pydantic import BaseModel


class EmailValidationResult(BaseModel):
contactField: str
type: str
status: str
metadata: Dict[str, Any] = {}


class PhoneValidationResult(BaseModel):
contactField: str
type: str
status: str
metadata: Dict[str, Any] = {}


class ValidationSummary(BaseModel):
total: int
valid: int
invalid: int
risky: int
landline: int = 0


class BulkEmailValidationResult(BaseModel):
results: List[EmailValidationResult]
summary: ValidationSummary


class BulkPhoneValidationResult(BaseModel):
results: List[PhoneValidationResult]
summary: ValidationSummary


class PhoneInput(BaseModel):
phone: str
countryCode: Optional[str] = None


class ContactValidator:
"""Service for validating email addresses and phone numbers"""

def __init__(self, ccai) -> None:
self.ccai = ccai

def validate_email(self, email: str) -> EmailValidationResult:
"""Validate a single email address"""
response = self.ccai.request('POST', '/v1/contact-validator/email', {'email': email})
return EmailValidationResult(**response)

def validate_emails(self, emails: List[str]) -> BulkEmailValidationResult:
"""Validate multiple email addresses (up to 50)"""
response = self.ccai.request('POST', '/v1/contact-validator/emails', {'emails': emails})
return BulkEmailValidationResult(**response)

def validate_phone(self, phone: str, country_code: Optional[str] = None) -> PhoneValidationResult:
"""Validate a single phone number in E.164 format"""
response = self.ccai.request(
'POST', '/v1/contact-validator/phone',
{'phone': phone, 'countryCode': country_code}
)
return PhoneValidationResult(**response)

def validate_phones(self, phones: List[Dict[str, Any]]) -> BulkPhoneValidationResult:
"""Validate multiple phone numbers (up to 50)"""
response = self.ccai.request('POST', '/v1/contact-validator/phones', {'phones': phones})
return BulkPhoneValidationResult(**response)