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
14 changes: 14 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.git
.github
__pycache__/
.pytest_cache/
.venv/
*.pyc
*.pyo
*.md
LICENSE
tests/
examples/
.env
.env.*
*.log
161 changes: 161 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,40 @@ 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.

```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)
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)
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 @@ -298,14 +332,141 @@ def handle_webhook():
return jsonify(result)
```

### Brands

Register and manage brands for TCR (The Campaign Registry) business verification.

```python
from ccai_python import CCAI

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

# Create a brand
brand = ccai.brands.create({
"legalCompanyName": "Collect.org Inc.",
"dba": "Collect",
"entityType": "NON_PROFIT",
"taxId": "123456789",
"taxIdCountry": "US",
"country": "US",
"verticalType": "NON_PROFIT",
"websiteUrl": "https://www.collect.org",
"street": "123 Main Street",
"city": "San Francisco",
"state": "CA",
"postalCode": "94105",
"contactFirstName": "Jane",
"contactLastName": "Doe",
"contactEmail": "jane@collect.org",
"contactPhone": "+14155551234",
})
print(f"Brand created with ID: {brand['id']}")

# Get a brand by ID
fetched = ccai.brands.get(brand["id"])
print(f"Website match score: {fetched.get('websiteMatchScore')}")

# List all brands for the account
brands = ccai.brands.list()
print(f"Found {len(brands)} brand(s)")

# Update a brand (partial update)
updated = ccai.brands.update(brand["id"], {
"street": "456 Oak Avenue",
"city": "Los Angeles",
})

# Delete a brand
ccai.brands.delete(brand["id"])
```

#### Entity Types

`PRIVATE_PROFIT`, `PUBLIC_PROFIT`, `NON_PROFIT`, `GOVERNMENT`, `SOLE_PROPRIETOR`

> Note: `PUBLIC_PROFIT` entities require `stockSymbol` and `stockExchange` fields.

#### Vertical Types

`AUTOMOTIVE`, `AGRICULTURE`, `BANKING`, `COMMUNICATION`, `CONSTRUCTION`, `EDUCATION`, `ENERGY`, `ENTERTAINMENT`, `GOVERNMENT`, `HEALTHCARE`, `HOSPITALITY`, `INSURANCE`, `LEGAL`, `MANUFACTURING`, `NON_PROFIT`, `PROFESSIONAL`, `REAL_ESTATE`, `RETAIL`, `TECHNOLOGY`, `TRANSPORTATION`

### Campaigns

Register and manage campaigns for TCR (The Campaign Registry) carrier vetting. Each campaign must be linked to a verified brand.

```python
from ccai_python import CCAI

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

# Create a campaign
campaign = ccai.campaigns.create({
"brandId": 1,
"useCase": "MIXED",
"subUseCases": ["CUSTOMER_CARE", "TWO_FACTOR_AUTHENTICATION", "ACCOUNT_NOTIFICATION"],
"description": "Security codes and support messaging.",
"messageFlow": "Users opt-in via signup form at https://example.com/signup",
"hasEmbeddedLinks": True,
"hasEmbeddedPhone": False,
"isAgeGated": False,
"isDirectLending": False,
"optInKeywords": ["START"],
"optInMessage": "Welcome! Reply STOP to cancel.",
"optInProofUrl": "https://example.com/opt-in-proof.png",
"helpKeywords": ["HELP"],
"helpMessage": "For HELP email support@example.com.",
"optOutKeywords": ["STOP"],
"optOutMessage": "STOP received. You are unsubscribed.",
"sampleMessages": [
"Your code is 554321. Reply STOP to cancel.",
"Your ticket has been updated. Reply HELP for info."
]
})
print(f"Campaign created with ID: {campaign['id']}")

# Get a campaign by ID
fetched = ccai.campaigns.get(campaign["id"])

# List all campaigns for the account
campaigns = ccai.campaigns.list()
print(f"Found {len(campaigns)} campaign(s)")

# Update a campaign (partial update)
updated = ccai.campaigns.update(campaign["id"], {
"description": "Updated description."
})

# Delete a campaign
ccai.campaigns.delete(campaign["id"])
```

#### Use Cases

`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`

> Note: `MIXED` and `LOW_VOLUME_MIXED` campaigns require 2–3 `subUseCases`.

#### Sub-Use Cases

`TWO_FACTOR_AUTHENTICATION`, `ACCOUNT_NOTIFICATION`, `CUSTOMER_CARE`, `DELIVERY_NOTIFICATION`, `FRAUD_ALERT`, `MARKETING`, `POLLING_VOTING`

## Features

- Send SMS messages to single or multiple recipients
- Send MMS messages with images
- Send Email campaigns with HTML content
- Schedule emails for future delivery
- Brand registration and management for TCR verification
- 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
14 changes: 14 additions & 0 deletions integration/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
FROM python:3.11-slim

WORKDIR /sdk
COPY pyproject.toml setup.py ./
COPY src/ ./src/
RUN pip install --no-cache-dir -e .

COPY integration/requirements.txt ./integration/
RUN pip install --no-cache-dir -r ./integration/requirements.txt

COPY integration/ ./integration/

WORKDIR /sdk/integration
CMD ["python", "test.py"]
2 changes: 2 additions & 0 deletions integration/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
requests>=2.31.0
pydantic>=2.5.0
Loading