diff --git a/README.md b/README.md
index 941cb44..5836023 100644
--- a/README.md
+++ b/README.md
@@ -10,6 +10,7 @@ A C# client library for interacting with the [CloudContactAI](https://cloudconta
- 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}`)
@@ -280,6 +281,40 @@ await ccai.Contact.SetDoNotTextAsync(false, phone: "+15551234567");
await ccai.Contact.SetDoNotTextAsync(true, contactId: "contact-abc-123");
```
+### Contact Validator
+
+Validate email addresses and phone numbers.
+
+> Bulk endpoints accept up to 50 contacts per request and are processed server-side in chunks.
+
+```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, processed server-side in chunks)
+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, processed server-side in chunks)
+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)
diff --git a/RELEASE-NOTES-v1.5.0.md b/RELEASE-NOTES-v1.5.0.md
new file mode 100644
index 0000000..5daddd0
--- /dev/null
+++ b/RELEASE-NOTES-v1.5.0.md
@@ -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
diff --git a/src/CCAI.NET/CCAI.NET.csproj b/src/CCAI.NET/CCAI.NET.csproj
index 479c99f..1dc26a5 100644
--- a/src/CCAI.NET/CCAI.NET.csproj
+++ b/src/CCAI.NET/CCAI.NET.csproj
@@ -6,7 +6,7 @@
enable
latest
CloudContactAI.CCAI.NET
- 1.4.5
+ 1.5.0
CloudContactAI LLC
CloudContactAI LLC
C# client for CloudContactAI API with SMS, MMS, Email, and Webhook support. Enhanced webhook support with new CloudContact event format, contact.unsubscribed events, environment variable configuration, and comprehensive examples.
@@ -15,7 +15,7 @@
true
https://github.com/CloudContactAI/CCAI.NET
true
- $([System.IO.File]::ReadAllText("$(MSBuildProjectDirectory)/../../RELEASE-NOTES-v1.4.5.md"))
+ $([System.IO.File]::ReadAllText("$(MSBuildProjectDirectory)/../../RELEASE-NOTES-v1.5.0.md"))
https://github.com/CloudContactAI/CCAI.NET
git
README.md
diff --git a/src/CCAI.NET/CCAIClient.cs b/src/CCAI.NET/CCAIClient.cs
index 986a7ad..aee1400 100644
--- a/src/CCAI.NET/CCAIClient.cs
+++ b/src/CCAI.NET/CCAIClient.cs
@@ -9,6 +9,7 @@
using CCAI.NET.Brands;
using CCAI.NET.Campaigns;
using CCAI.NET.Contact;
+using CCAI.NET.ContactValidator;
using CCAI.NET.Email;
using CCAI.NET.SMS;
using CCAI.NET.Webhook;
@@ -60,6 +61,11 @@ public interface ICCAIClient : IDisposable
///
CampaignService Campaigns { get; }
+ ///
+ /// Contact validator service for validating email and phone contacts
+ ///
+ IContactValidatorService ContactValidator { get; }
+
///
/// Get the client ID
///
@@ -284,6 +290,11 @@ public class CCAIClient : ICCAIClient
///
public CampaignService Campaigns { get; }
+ ///
+ /// Contact validator service for validating email and phone contacts
+ ///
+ public IContactValidatorService ContactValidator { get; }
+
///
/// Create a new CCAI client instance
///
@@ -333,6 +344,7 @@ public CCAIClient(CCAIConfig config, HttpClient? httpClient = null)
Contact = new ContactService(this);
Brands = new BrandService(this);
Campaigns = new CampaignService(this);
+ ContactValidator = new ContactValidatorService(this);
}
///
diff --git a/src/CCAI.NET/ContactValidator/ContactValidatorModels.cs b/src/CCAI.NET/ContactValidator/ContactValidatorModels.cs
new file mode 100644
index 0000000..a4be04c
--- /dev/null
+++ b/src/CCAI.NET/ContactValidator/ContactValidatorModels.cs
@@ -0,0 +1,139 @@
+// Copyright (c) 2025 CloudContactAI LLC
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace CCAI.NET.ContactValidator;
+
+///
+/// Validation result for a single email address
+///
+public class EmailValidationResult
+{
+ ///
+ /// The validated email address
+ ///
+ [JsonPropertyName("contactField")]
+ public string ContactField { get; set; } = string.Empty;
+
+ ///
+ /// Contact type — always "email"
+ ///
+ [JsonPropertyName("type")]
+ public string Type { get; set; } = string.Empty;
+
+ ///
+ /// Validation status: "valid", "invalid", or "risky"
+ ///
+ [JsonPropertyName("status")]
+ public string Status { get; set; } = string.Empty;
+
+ ///
+ /// Additional metadata (e.g. safe_to_send, ai_verdict)
+ ///
+ [JsonPropertyName("metadata")]
+ public Dictionary? Metadata { get; set; }
+}
+
+///
+/// Validation result for a single phone number
+///
+public class PhoneValidationResult
+{
+ ///
+ /// The validated phone number
+ ///
+ [JsonPropertyName("contactField")]
+ public string ContactField { get; set; } = string.Empty;
+
+ ///
+ /// Contact type — always "phone"
+ ///
+ [JsonPropertyName("type")]
+ public string Type { get; set; } = string.Empty;
+
+ ///
+ /// Validation status: "valid", "invalid", or "landline"
+ ///
+ [JsonPropertyName("status")]
+ public string Status { get; set; } = string.Empty;
+
+ ///
+ /// Additional metadata (e.g. country_code, national_number, carrier_type)
+ ///
+ [JsonPropertyName("metadata")]
+ public Dictionary? Metadata { get; set; }
+}
+
+///
+/// Aggregate counts for a bulk validation response
+///
+public class ValidationSummary
+{
+ /// Total contacts validated
+ [JsonPropertyName("total")]
+ public int Total { get; set; }
+
+ /// Number of valid contacts
+ [JsonPropertyName("valid")]
+ public int Valid { get; set; }
+
+ /// Number of invalid contacts
+ [JsonPropertyName("invalid")]
+ public int Invalid { get; set; }
+
+ /// Number of risky contacts (emails only)
+ [JsonPropertyName("risky")]
+ public int Risky { get; set; }
+
+ /// Number of landline numbers (phones only)
+ [JsonPropertyName("landline")]
+ public int Landline { get; set; }
+}
+
+///
+/// Response for a bulk email validation request
+///
+public class BulkEmailValidationResult
+{
+ /// Individual validation results
+ [JsonPropertyName("results")]
+ public List Results { get; set; } = new();
+
+ /// Aggregate summary
+ [JsonPropertyName("summary")]
+ public ValidationSummary Summary { get; set; } = new();
+}
+
+///
+/// Response for a bulk phone validation request
+///
+public class BulkPhoneValidationResult
+{
+ /// Individual validation results
+ [JsonPropertyName("results")]
+ public List Results { get; set; } = new();
+
+ /// Aggregate summary
+ [JsonPropertyName("summary")]
+ public ValidationSummary Summary { get; set; } = new();
+}
+
+///
+/// Phone number input for bulk validation
+///
+public class PhoneInput
+{
+ ///
+ /// Phone number in E.164 format (e.g. +15551234567)
+ ///
+ [JsonPropertyName("phone")]
+ public string Phone { get; set; } = string.Empty;
+
+ ///
+ /// Optional ISO 3166-1 alpha-2 country code (e.g. "US")
+ ///
+ [JsonPropertyName("countryCode")]
+ public string? CountryCode { get; set; }
+}
diff --git a/src/CCAI.NET/ContactValidator/ContactValidatorService.cs b/src/CCAI.NET/ContactValidator/ContactValidatorService.cs
new file mode 100644
index 0000000..793c016
--- /dev/null
+++ b/src/CCAI.NET/ContactValidator/ContactValidatorService.cs
@@ -0,0 +1,104 @@
+// Copyright (c) 2025 CloudContactAI LLC
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+namespace CCAI.NET.ContactValidator;
+
+///
+/// Interface for the contact validator service
+///
+public interface IContactValidatorService
+{
+ /// Validate a single email address (async)
+ Task ValidateEmailAsync(string email, CancellationToken cancellationToken = default);
+
+ /// Validate multiple email addresses up to the configured bulk limit (async)
+ Task ValidateEmailsAsync(IEnumerable emails, CancellationToken cancellationToken = default);
+
+ /// Validate a single phone number in E.164 format (async)
+ Task ValidatePhoneAsync(string phone, string? countryCode = null, CancellationToken cancellationToken = default);
+
+ /// Validate multiple phone numbers up to the configured bulk limit (async)
+ Task ValidatePhonesAsync(IEnumerable phones, CancellationToken cancellationToken = default);
+
+ /// Validate a single email address (synchronous)
+ EmailValidationResult ValidateEmail(string email);
+
+ /// Validate multiple email addresses up to the configured bulk limit (synchronous)
+ BulkEmailValidationResult ValidateEmails(IEnumerable emails);
+
+ /// Validate a single phone number in E.164 format (synchronous)
+ PhoneValidationResult ValidatePhone(string phone, string? countryCode = null);
+
+ /// Validate multiple phone numbers up to the configured bulk limit (synchronous)
+ BulkPhoneValidationResult ValidatePhones(IEnumerable phones);
+}
+
+///
+/// Service for validating email addresses and phone numbers through the CCAI API
+///
+public class ContactValidatorService : IContactValidatorService
+{
+ private readonly ICCAIClient _client;
+
+ ///
+ /// Create a new ContactValidatorService instance
+ ///
+ /// The parent CCAI client
+ public ContactValidatorService(ICCAIClient client)
+ {
+ _client = client;
+ }
+
+ ///
+ /// Validate a single email address
+ ///
+ /// Email address to validate
+ /// Cancellation token
+ /// Validation result with status and metadata
+ public Task ValidateEmailAsync(string email, CancellationToken cancellationToken = default) =>
+ _client.RequestAsync(HttpMethod.Post, "/v1/contact-validator/email", new { email }, cancellationToken);
+
+ ///
+ /// Validate multiple email addresses (up to the configured bulk limit)
+ ///
+ /// List of email addresses to validate
+ /// Cancellation token
+ /// Bulk validation results with summary
+ public Task ValidateEmailsAsync(IEnumerable emails, CancellationToken cancellationToken = default) =>
+ _client.RequestAsync(HttpMethod.Post, "/v1/contact-validator/emails", new { emails }, cancellationToken);
+
+ ///
+ /// Validate a single phone number
+ ///
+ /// Phone number in E.164 format (e.g. +15551234567)
+ /// Optional ISO 3166-1 alpha-2 country code (e.g. "US")
+ /// Cancellation token
+ /// Validation result with status and metadata
+ public Task ValidatePhoneAsync(string phone, string? countryCode = null, CancellationToken cancellationToken = default) =>
+ _client.RequestAsync(HttpMethod.Post, "/v1/contact-validator/phone", new { phone, countryCode }, cancellationToken);
+
+ ///
+ /// Validate multiple phone numbers (up to the configured bulk limit)
+ ///
+ /// List of phone inputs with optional country codes
+ /// Cancellation token
+ /// Bulk validation results with summary
+ public Task ValidatePhonesAsync(IEnumerable phones, CancellationToken cancellationToken = default) =>
+ _client.RequestAsync(HttpMethod.Post, "/v1/contact-validator/phones", new { phones }, cancellationToken);
+
+ ///
+ public EmailValidationResult ValidateEmail(string email) =>
+ ValidateEmailAsync(email).GetAwaiter().GetResult();
+
+ ///
+ public BulkEmailValidationResult ValidateEmails(IEnumerable emails) =>
+ ValidateEmailsAsync(emails).GetAwaiter().GetResult();
+
+ ///
+ public PhoneValidationResult ValidatePhone(string phone, string? countryCode = null) =>
+ ValidatePhoneAsync(phone, countryCode).GetAwaiter().GetResult();
+
+ ///
+ public BulkPhoneValidationResult ValidatePhones(IEnumerable phones) =>
+ ValidatePhonesAsync(phones).GetAwaiter().GetResult();
+}