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
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -303,3 +303,9 @@ $RECYCLE.BIN/

# Environment variables
.env

# Real API tests — local only
tests/CCAI.NET.IntegrationTests/
test_real_webhook.cs
test-real-webhook/
CCAI.NET.IntegrationTests
117 changes: 80 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ A C# client library for interacting with the [CloudContactAI](https://cloudconta
## Features

- Send SMS messages to single or multiple recipients
- Send MMS messages with images
- Send MMS messages with images (automatic S3 upload)
- Send Email campaigns to single or multiple recipients
- Upload images to S3 with signed URLs
- Variable substitution in messages
- Manage webhooks for event notifications
- Manage contact opt-out preferences (SetDoNotText)
- Webhook management: register, list, update, delete
- Webhook signature verification
- Template variable substitution (`${firstName}`, `${lastName}`)
- Async/await support
- Progress tracking
- Comprehensive error handling
Expand Down Expand Up @@ -253,6 +254,30 @@ var scheduledResponse = await ccai.Email.SendCampaignAsync(scheduledCampaign);
Console.WriteLine($"Email campaign scheduled with ID: {scheduledResponse.Id}");
```

### Contact Management

Manage opt-out preferences for contacts.

```csharp
using CCAI.NET;

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

// Opt a contact out of text messages (by phone number)
var result = await ccai.Contact.SetDoNotTextAsync(true, phone: "+15551234567");
Console.WriteLine($"Opted out: {result.Phone}, DoNotText={result.DoNotText}");

// Opt a contact back in
await ccai.Contact.SetDoNotTextAsync(false, phone: "+15551234567");

// Opt out by contactId
await ccai.Contact.SetDoNotTextAsync(true, contactId: "contact-abc-123");
```

### Webhook Management

#### CloudContact Webhook Events (New Format)
Expand Down Expand Up @@ -300,26 +325,42 @@ switch (cloudContactEvent.EventType)
- **`message.error.carrier`** - Carrier-level delivery failure
- **`message.error.cloudcontact`** - CloudContact system error

#### ASP.NET Core Webhook Endpoint
#### Webhook Endpoint Example

```csharp
[ApiController]
[Route("api/[controller]")]
public class WebhookController : ControllerBase
using CCAI.NET;
using CCAI.NET.Webhook;
using DotNetEnv;

// Load environment variables
Env.Load();

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

using var ccai = new CCAIClient(config);

// In your webhook handler (e.g., ASP.NET Core controller):
public void HandleWebhook(string body, string signature)
{
[HttpPost("cloudcontact")]
public async Task<IActionResult> HandleCloudContactWebhook()
// Parse the webhook event
var cloudContactEvent = ccai.Webhook.ParseCloudContactEvent(body);

switch (cloudContactEvent.EventType)
{
using var reader = new StreamReader(Request.Body, Encoding.UTF8);
var body = await reader.ReadToEndAsync();

var webhookService = new WebhookService(null!);
var cloudContactEvent = webhookService.ParseCloudContactEvent(body);

// Process the event
await ProcessWebhookEvent(cloudContactEvent);

return Ok(new { status = "success" });
case "message.sent":
Console.WriteLine($"✅ Message delivered to {cloudContactEvent.Data.To}");
break;
case "message.incoming":
Console.WriteLine($"📨 Reply from {cloudContactEvent.Data.From}");
break;
case "message.excluded":
Console.WriteLine($"⚠️ Message excluded");
break;
// Handle other event types...
}
}
```
Expand Down Expand Up @@ -384,29 +425,31 @@ var updatedWebhook = await ccai.Webhook.UpdateAsync(registration.Id, updatedConf
var deleteResponse = await ccai.Webhook.DeleteAsync(registration.Id);
Console.WriteLine($"Webhook deleted: {deleteResponse.Success}");

// Parse a webhook event (in your webhook handler)
public void ProcessWebhookEvent(string json, string signature, string secret)
// Verify webhook signature (in your webhook handler)
var signature = request.headers['x-ccai-signature'];
var json = request.body;
var payload = JsonDocument.Parse(json);
var clientId = config.ClientId;
var eventHash = payload.RootElement.GetProperty("eventHash").GetString() ?? "";

if (ccai.Webhook.VerifySignature(signature, clientId, eventHash, webhookSecret))
{
// Verify the signature
if (ccai.Webhook.VerifySignature(signature, json, secret))
// Signature is valid, process the webhook
var webhookEvent = ccai.Webhook.ParseEvent(json);

if (webhookEvent is MessageSentEvent sentEvent)
{
// Parse the event (supports both new and legacy formats)
var webhookEvent = ccai.Webhook.ParseEvent(json);

if (webhookEvent is MessageSentEvent sentEvent)
{
Console.WriteLine($"Message sent to: {sentEvent.To}");
}
else if (webhookEvent is MessageIncomingEvent incomingEvent)
{
Console.WriteLine($"Message received from: {incomingEvent.From}");
}
Console.WriteLine($"Message sent to: {sentEvent.To}");
}
else
else if (webhookEvent is MessageIncomingEvent incomingEvent)
{
Console.WriteLine("Invalid signature");
Console.WriteLine($"Message received from: {incomingEvent.From}");
}
}
else
{
Console.WriteLine("Invalid signature");
}
```

### Step-by-Step MMS Workflow
Expand Down
15 changes: 15 additions & 0 deletions RELEASE-NOTES-v1.4.5.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Release Notes - Version 1.4.4

## New Features
- Added sms-sender-phone example project demonstrating consistent sender phone usage
- Added Phone and PhoneService entities to enable the API to pull the existing phone numbers from their CCAI account.
- Added support for the SMSService to support passing in a senderPhone.
- Enhanced PhoneService integration for SMS campaigns

## Improvements
- Updated project templates to support .NET 8.0 compatibility
- Improved example project structure and organization

## Bug Fixes
- Fixed multiple entry point compilation errors in examples project
- Resolved .NET framework targeting issues
2 changes: 1 addition & 1 deletion examples/MMSExample.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public async Task SendMMSWithImageAsync()
new[] { account },
message,
title,
options);
options: options);

Console.WriteLine($"MMS sent! Campaign ID: {response.CampaignId}");
Console.WriteLine($"Messages sent: {response.MessagesSent}");
Expand Down
Loading