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
11 changes: 11 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.git
.github
*.md
LICENSE
examples/
tests/
test-email-sender/
**/bin/
**/obj/
.env
.env.*
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -308,4 +308,6 @@ $RECYCLE.BIN/
tests/CCAI.NET.IntegrationTests/
test_real_webhook.cs
test-real-webhook/
CCAI.NET.IntegrationTests
CCAI.NET.IntegrationTests

.claude/
168 changes: 168 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ A C# client library for interacting with the [CloudContactAI](https://cloudconta
- Send SMS messages to single or multiple recipients
- Send MMS messages with images (automatic S3 upload)
- Send Email campaigns to single or multiple recipients
- Brand registration and management for TCR verification
- Campaign registration and management for TCR carrier vetting
- Manage contact opt-out preferences (SetDoNotText)
- Validate email addresses (valid/invalid/risky) and phone numbers (valid/invalid/landline)
- Webhook management: register, list, update, delete
- Webhook signature verification
- Template variable substitution (`${firstName}`, `${lastName}`)
Expand Down Expand Up @@ -278,6 +281,38 @@ await ccai.Contact.SetDoNotTextAsync(false, phone: "+15551234567");
await ccai.Contact.SetDoNotTextAsync(true, contactId: "contact-abc-123");
```

### Contact Validator

Validate email addresses and phone numbers.

```csharp
using CCAI.NET;
using CCAI.NET.ContactValidator;

// Validate a single email
var emailResult = await ccai.ContactValidator.ValidateEmailAsync("user@example.com");
Console.WriteLine(emailResult.Status); // "valid" | "invalid" | "risky"

// Validate multiple emails (up to 50)
var bulkEmails = await ccai.ContactValidator.ValidateEmailsAsync(new[] {
"user@example.com",
"bad@invalid.xyz"
});
Console.WriteLine(bulkEmails.Summary.Total); // 2
Console.WriteLine(bulkEmails.Summary.Valid); // 1

// Validate a single phone number
var phoneResult = await ccai.ContactValidator.ValidatePhoneAsync("+15551234567", "US");
Console.WriteLine(phoneResult.Status); // "valid" | "invalid" | "landline"

// Validate multiple phone numbers (up to 50)
var bulkPhones = await ccai.ContactValidator.ValidatePhonesAsync(new[] {
new PhoneInput { Phone = "+15551234567" },
new PhoneInput { Phone = "+15559876543", CountryCode = "US" }
});
Console.WriteLine(bulkPhones.Summary.Landline); // 1
```

### Webhook Management

#### CloudContact Webhook Events (New Format)
Expand Down Expand Up @@ -452,6 +487,139 @@ else
}
```

### Brand Registration

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

```csharp
using CCAI.NET;
using CCAI.NET.Brands;

var ccai = new CCAIClient(new CCAIConfig
{
ClientId = "YOUR-CLIENT-ID",
ApiKey = "YOUR-API-KEY"
});

// Create a brand
var brand = await ccai.Brands.CreateAsync(new BrandRequest
{
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"
});
Console.WriteLine($"Brand created with ID: {brand.Id}");

// Get a brand by ID
var fetched = await ccai.Brands.GetAsync(brand.Id);
Console.WriteLine($"Website match score: {fetched.WebsiteMatchScore?.ToString() ?? "pending"}");

// List all brands for the account
var brands = await ccai.Brands.ListAsync();
Console.WriteLine($"Found {brands.Length} brand(s)");

// Update a brand (partial update)
var updated = await ccai.Brands.UpdateAsync(brand.Id, new BrandRequest
{
Street = "456 Oak Avenue",
City = "Los Angeles"
});

// Delete a brand
await ccai.Brands.DeleteAsync(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`

### Campaign Registration

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

```csharp
using CCAI.NET;
using CCAI.NET.Campaigns;

var ccai = new CCAIClient(new CCAIConfig
{
ClientId = "YOUR-CLIENT-ID",
ApiKey = "YOUR-API-KEY"
});

// Create a campaign
var campaign = await ccai.Campaigns.CreateAsync(new CampaignRequest
{
BrandId = 1,
UseCase = "MIXED",
SubUseCases = new List<string> { "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 = new List<string> { "START" },
OptInMessage = "Welcome! Reply STOP to cancel.",
OptInProofUrl = "https://example.com/opt-in-proof.png",
HelpKeywords = new List<string> { "HELP" },
HelpMessage = "For HELP email support@example.com.",
OptOutKeywords = new List<string> { "STOP" },
OptOutMessage = "STOP received. You are unsubscribed.",
SampleMessages = new List<string>
{
"Your code is 554321. Reply STOP to cancel.",
"Your ticket has been updated. Reply HELP for info."
}
});
Console.WriteLine($"Campaign created with ID: {campaign.Id}");

// Get a campaign by ID
var fetched = await ccai.Campaigns.GetAsync(campaign.Id);

// List all campaigns for the account
var campaigns = await ccai.Campaigns.ListAsync();
Console.WriteLine($"Found {campaigns.Length} campaign(s)");

// Update a campaign (partial update)
var updated = await ccai.Campaigns.UpdateAsync(campaign.Id, new CampaignRequest
{
Description = "Updated description."
});

// Delete a campaign
await ccai.Campaigns.DeleteAsync(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`

### Step-by-Step MMS Workflow

```csharp
Expand Down
10 changes: 10 additions & 0 deletions RELEASE-NOTES-v1.5.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Release Notes - Version 1.5.0

## New Features
- Added `ContactValidator` service for validating email addresses and phone numbers
- `ValidateEmailAsync` / `ValidateEmail` — validate a single email, returns `valid`, `invalid`, or `risky` status with `safe_to_send` and `ai_verdict` metadata
- `ValidateEmailsAsync` / `ValidateEmails` — bulk email validation (up to 50 addresses) with summary counts
- `ValidatePhoneAsync` / `ValidatePhone` — validate a single phone number, returns `valid`, `invalid`, or `landline` status with carrier metadata
- `ValidatePhonesAsync` / `ValidatePhones` — bulk phone validation (up to 50 numbers) with summary counts including landline count
- New models: `EmailValidationResult`, `PhoneValidationResult`, `ValidationSummary`, `BulkEmailValidationResult`, `BulkPhoneValidationResult`, `PhoneInput`
- `IContactValidatorService` interface for dependency injection and testing
88 changes: 88 additions & 0 deletions examples/BrandExample.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Copyright (c) 2025 CloudContactAI LLC
// Licensed under the MIT License. See LICENSE in the project root for license information.

using System;
using System.Threading.Tasks;
using CCAI.NET;
using CCAI.NET.Brands;
using DotNetEnv;

namespace CCAI.NET.Examples;

/// <summary>
/// Brand registration example using the CCAI.NET client
/// </summary>
public class BrandExample
{
public static async Task RunAsync()
{
Env.Load("./.env");

var config = new CCAIConfig
{
ClientId = Environment.GetEnvironmentVariable("CCAI_CLIENT_ID") ??
throw new InvalidOperationException("CCAI_CLIENT_ID not found"),
ApiKey = Environment.GetEnvironmentVariable("CCAI_API_KEY") ??
throw new InvalidOperationException("CCAI_API_KEY not found"),
UseTestEnvironment = true
};

using var ccai = new CCAIClient(config);

try
{
// Create a brand
Console.WriteLine("Creating a brand...");
var brand = await ccai.Brands.CreateAsync(new BrandRequest
{
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"
});
Console.WriteLine($"Brand created with ID: {brand.Id}");

// Get brand by ID
Console.WriteLine("\nFetching brand by ID...");
var fetched = await ccai.Brands.GetAsync(brand.Id);
Console.WriteLine($"Brand: {fetched.LegalCompanyName}, Score: {fetched.WebsiteMatchScore?.ToString() ?? "pending"}");

// List all brands
Console.WriteLine("\nListing all brands...");
var brands = await ccai.Brands.ListAsync();
Console.WriteLine($"Found {brands.Length} brand(s)");

// Update a brand
Console.WriteLine("\nUpdating brand...");
var updated = await ccai.Brands.UpdateAsync(brand.Id, new BrandRequest
{
Street = "456 Oak Avenue",
City = "Los Angeles",
ContactEmail = "admin@collect.org"
});
Console.WriteLine($"Brand updated: {updated.Street}, {updated.City}");

// Delete a brand
Console.WriteLine("\nDeleting brand...");
await ccai.Brands.DeleteAsync(brand.Id);
Console.WriteLine("Brand deleted successfully");
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
throw;
}
}
}
Loading