← Back to API Dashboard

🔷 C# NuGet Package

Enterprise-grade Voice, SMS, Email & AI API for .NET applications and Azure cloud services

🚧 Coming Soon - Use REST API for now

📦 Installation

The C# NuGet package will be available when released:

# Coming soon!
Install-Package TeamConnect.API

# Or via .NET CLI
dotnet add package TeamConnect.API

# For now, use HttpClient (built into .NET)
# No external dependencies needed!
🚀 .NET Ready: Built for .NET 6+, ASP.NET Core, Blazor, Azure Functions, and enterprise applications. Full async/await support, dependency injection, and production-ready patterns.

🚀 Quick Start

Here's how you'll use the C# package once it's available:

// Future NuGet package usage
using TeamConnect.API;
using TeamConnect.API.Models;

// Initialize with your API key
var client = new TeamConnectClient("tc_live_your_api_key_here");

// Make a voice call
var callRequest = new CallRequest
{
    To = "+447123456789",
    Message = "Hello from Team Connect!"
};

var callResult = await client.Voice.MakeCallAsync(callRequest);
Console.WriteLine($"Call initiated: {callResult.CallId}");

// Send an SMS
var smsRequest = new SmsRequest
{
    To = "+447123456789",
    Message = "Your verification code is 123456"
};

var smsResult = await client.Sms.SendAsync(smsRequest);
Console.WriteLine($"SMS sent: {smsResult.MessageId}");

// Send an email
var emailRequest = new EmailRequest
{
    To = "customer@example.com",
    Subject = "Welcome to our service",
    Html = "

Welcome!

Thanks for signing up.

" }; var emailResult = await client.Email.SendAsync(emailRequest); Console.WriteLine($"Email sent: {emailResult.MessageId}"); // AI Chat var chatRequest = new ChatRequest { Messages = new[] { new ChatMessage { Role = "system", Content = "You are a helpful assistant." }, new ChatMessage { Role = "user", Content = "Hello!" } } }; var aiResult = await client.AI.ChatAsync(chatRequest); Console.WriteLine($"AI Response: {aiResult.Response}");

🔐 Current Implementation (REST API)

Until the package is ready, use our REST API with HttpClient:

// Basic HttpClient implementation
using System.Text;
using System.Text.Json;

public class TeamConnectClient
{
    private readonly HttpClient _httpClient;
    private readonly string _apiKey;
    private const string BaseUrl = "https://us-central1-customerservice-2156c.cloudfunctions.net";

    public TeamConnectClient(string apiKey)
    {
        _apiKey = apiKey;
        _httpClient = new HttpClient
        {
            BaseAddress = new Uri(BaseUrl),
            Timeout = TimeSpan.FromSeconds(30)
        };
        _httpClient.DefaultRequestHeaders.Add("Accept", "application/json");
    }

    public async Task> MakeRequestAsync(string service, string action, object data, CancellationToken cancellationToken = default)
    {
        var request = new
        {
            service,
            action,
            data,
            api_key = _apiKey
        };

        var json = JsonSerializer.Serialize(request, new JsonSerializerOptions
        {
            PropertyNamingPolicy = JsonNamingPolicy.CamelCase
        });

        var content = new StringContent(json, Encoding.UTF8, "application/json");

        try
        {
            var response = await _httpClient.PostAsync("/executeAPI", content, cancellationToken);
            var responseContent = await response.Content.ReadAsStringAsync(cancellationToken);

            if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
            {
                throw new TeamConnectException("Invalid API key", "INVALID_API_KEY");
            }

            if (response.StatusCode == System.Net.HttpStatusCode.PaymentRequired)
            {
                var errorResponse = JsonSerializer.Deserialize(responseContent);
                throw new InsufficientCreditsException(errorResponse?.Error ?? "Insufficient credits");
            }

            if (response.StatusCode == System.Net.HttpStatusCode.TooManyRequests)
            {
                throw new RateLimitExceededException("Rate limit exceeded");
            }

            if (!response.IsSuccessStatusCode)
            {
                var errorResponse = JsonSerializer.Deserialize(responseContent);
                throw new TeamConnectException(errorResponse?.Error ?? $"HTTP {response.StatusCode}");
            }

            return JsonSerializer.Deserialize>(responseContent, new JsonSerializerOptions
            {
                PropertyNamingPolicy = JsonNamingPolicy.CamelCase
            });
        }
        catch (HttpRequestException ex)
        {
            throw new TeamConnectException($"Network error: {ex.Message}", ex);
        }
        catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException)
        {
            throw new TeamConnectException("Request timeout", ex);
        }
    }

    public void Dispose()
    {
        _httpClient?.Dispose();
    }
}

// Response models
public class ApiResponse
{
    public bool Success { get; set; }
    public T Result { get; set; }
    public string Error { get; set; }
    public BillingInfo Billing { get; set; }
}

public class ApiErrorResponse
{
    public string Error { get; set; }
    public string Code { get; set; }
    public string TopUpUrl { get; set; }
}

public class BillingInfo
{
    public decimal Cost { get; set; }
    public string CostFormatted { get; set; }
    public string Service { get; set; }
    public string Action { get; set; }
    public decimal CreditsRemaining { get; set; }
    public string CreditsRemainingFormatted { get; set; }
}
// Typed client with proper models
using System.ComponentModel.DataAnnotations;

// Request models
public class CallRequest
{
    [Required]
    public string To { get; set; }
    
    [Required]
    public string Message { get; set; }
    
    public string From { get; set; }
    public string Voice { get; set; }
    public string WebhookUrl { get; set; }
}

public class SmsRequest
{
    [Required]
    public string To { get; set; }
    
    [Required]
    public string Message { get; set; }
    
    public string From { get; set; }
}

public class EmailRequest
{
    [Required]
    public string To { get; set; }
    
    [Required]
    public string Subject { get; set; }
    
    [Required]
    public string Html { get; set; }
    
    public string FromName { get; set; }
}

public class ChatRequest
{
    [Required]
    public ChatMessage[] Messages { get; set; }
    
    public string Model { get; set; } = "gpt-4";
}

public class ChatMessage
{
    [Required]
    public string Role { get; set; }
    
    [Required]
    public string Content { get; set; }
}

// Response models
public class CallResponse
{
    public string CallId { get; set; }
    public string Status { get; set; }
    public string To { get; set; }
    public string From { get; set; }
    public int EstimatedDuration { get; set; }
    public DateTime CreatedAt { get; set; }
}

public class SmsResponse
{
    public string MessageId { get; set; }
    public string Status { get; set; }
    public string To { get; set; }
    public string From { get; set; }
    public DateTime CreatedAt { get; set; }
}

public class EmailResponse
{
    public string MessageId { get; set; }
    public string Status { get; set; }
    public string To { get; set; }
    public string Subject { get; set; }
    public DateTime CreatedAt { get; set; }
}

public class ChatResponse
{
    public string Response { get; set; }
    public string Model { get; set; }
    public int TokensUsed { get; set; }
}

// Typed service methods
public static class TeamConnectClientExtensions
{
    public static async Task<(CallResponse Result, BillingInfo Billing)> MakeCallAsync(
        this TeamConnectClient client,
        CallRequest request,
        CancellationToken cancellationToken = default)
    {
        var response = await client.MakeRequestAsync("voice", "make_call", request, cancellationToken);
        
        if (!response.Success)
        {
            throw new TeamConnectException(response.Error ?? "Call failed");
        }

        return (response.Result, response.Billing);
    }

    public static async Task<(SmsResponse Result, BillingInfo Billing)> SendSmsAsync(
        this TeamConnectClient client,
        SmsRequest request,
        CancellationToken cancellationToken = default)
    {
        var response = await client.MakeRequestAsync("sms", "send", request, cancellationToken);
        
        if (!response.Success)
        {
            throw new TeamConnectException(response.Error ?? "SMS failed");
        }

        return (response.Result, response.Billing);
    }

    public static async Task<(EmailResponse Result, BillingInfo Billing)> SendEmailAsync(
        this TeamConnectClient client,
        EmailRequest request,
        CancellationToken cancellationToken = default)
    {
        var response = await client.MakeRequestAsync("email", "send", request, cancellationToken);
        
        if (!response.Success)
        {
            throw new TeamConnectException(response.Error ?? "Email failed");
        }

        return (response.Result, response.Billing);
    }

    public static async Task<(ChatResponse Result, BillingInfo Billing)> ChatAsync(
        this TeamConnectClient client,
        ChatRequest request,
        CancellationToken cancellationToken = default)
    {
        var response = await client.MakeRequestAsync("ai", "chat", request, cancellationToken);
        
        if (!response.Success)
        {
            throw new TeamConnectException(response.Error ?? "AI chat failed");
        }

        return (response.Result, response.Billing);
    }
}

// Exception classes
public class TeamConnectException : Exception
{
    public string Code { get; }

    public TeamConnectException(string message, string code = null) : base(message)
    {
        Code = code;
    }

    public TeamConnectException(string message, Exception innerException) : base(message, innerException)
    {
    }
}

public class InsufficientCreditsException : TeamConnectException
{
    public InsufficientCreditsException(string message) : base(message, "INSUFFICIENT_CREDITS")
    {
    }
}

public class RateLimitExceededException : TeamConnectException
{
    public RateLimitExceededException(string message) : base(message, "RATE_LIMIT_EXCEEDED")
    {
    }
}

// Usage example
var client = new TeamConnectClient("tc_live_your_api_key_here");

try
{
    var callRequest = new CallRequest
    {
        To = "+447123456789",
        Message = "Hello from C# with types!",
        Voice = "Polly.Amy"
    };

    var (callResult, billing) = await client.MakeCallAsync(callRequest);

    Console.WriteLine("✅ Call initiated successfully!");
    Console.WriteLine($"Call ID: {callResult.CallId}");
    Console.WriteLine($"Status: {callResult.Status}");
    Console.WriteLine($"Cost: {billing.CostFormatted}");

    if (billing.CreditsRemaining < 500)
    {
        Console.WriteLine($"⚠️ Low credits remaining: {billing.CreditsRemainingFormatted}");
    }
}
catch (InsufficientCreditsException)
{
    Console.WriteLine("💰 Please top up your account");
}
catch (TeamConnectException ex)
{
    Console.WriteLine($"❌ Error: {ex.Message}");
}
// Dependency Injection setup for ASP.NET Core
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;

// Configuration model
public class TeamConnectOptions
{
    public const string SectionName = "TeamConnect";
    
    public string ApiKey { get; set; }
    public string BaseUrl { get; set; } = "https://us-central1-customerservice-2156c.cloudfunctions.net";
    public int TimeoutSeconds { get; set; } = 30;
    public int MaxRetries { get; set; } = 3;
}

// Service interface
public interface ITeamConnectService
{
    Task<(CallResponse Result, BillingInfo Billing)> MakeCallAsync(CallRequest request, CancellationToken cancellationToken = default);
    Task<(SmsResponse Result, BillingInfo Billing)> SendSmsAsync(SmsRequest request, CancellationToken cancellationToken = default);
    Task<(EmailResponse Result, BillingInfo Billing)> SendEmailAsync(EmailRequest request, CancellationToken cancellationToken = default);
    Task<(ChatResponse Result, BillingInfo Billing)> ChatAsync(ChatRequest request, CancellationToken cancellationToken = default);
}

// Service implementation
public class TeamConnectService : ITeamConnectService
{
    private readonly HttpClient _httpClient;
    private readonly TeamConnectOptions _options;
    private readonly ILogger _logger;

    public TeamConnectService(HttpClient httpClient, IOptions options, ILogger logger)
    {
        _httpClient = httpClient;
        _options = options.Value;
        _logger = logger;
    }

    public async Task<(CallResponse Result, BillingInfo Billing)> MakeCallAsync(CallRequest request, CancellationToken cancellationToken = default)
    {
        _logger.LogInformation("Making voice call to {PhoneNumber}", request.To);
        
        var response = await MakeRequestWithRetryAsync("voice", "make_call", request, cancellationToken);
        
        _logger.LogInformation("Voice call completed with ID {CallId}", response.Result.CallId);
        
        return (response.Result, response.Billing);
    }

    public async Task<(SmsResponse Result, BillingInfo Billing)> SendSmsAsync(SmsRequest request, CancellationToken cancellationToken = default)
    {
        _logger.LogInformation("Sending SMS to {PhoneNumber}", request.To);
        
        var response = await MakeRequestWithRetryAsync("sms", "send", request, cancellationToken);
        
        _logger.LogInformation("SMS sent with ID {MessageId}", response.Result.MessageId);
        
        return (response.Result, response.Billing);
    }

    public async Task<(EmailResponse Result, BillingInfo Billing)> SendEmailAsync(EmailRequest request, CancellationToken cancellationToken = default)
    {
        _logger.LogInformation("Sending email to {EmailAddress}", request.To);
        
        var response = await MakeRequestWithRetryAsync("email", "send", request, cancellationToken);
        
        _logger.LogInformation("Email sent with ID {MessageId}", response.Result.MessageId);
        
        return (response.Result, response.Billing);
    }

    public async Task<(ChatResponse Result, BillingInfo Billing)> ChatAsync(ChatRequest request, CancellationToken cancellationToken = default)
    {
        _logger.LogInformation("Making AI chat request with {MessageCount} messages", request.Messages.Length);
        
        var response = await MakeRequestWithRetryAsync("ai", "chat", request, cancellationToken);
        
        _logger.LogInformation("AI chat completed, used {TokenCount} tokens", response.Result.TokensUsed);
        
        return (response.Result, response.Billing);
    }

    private async Task> MakeRequestWithRetryAsync(string service, string action, object data, CancellationToken cancellationToken)
    {
        for (int attempt = 0; attempt <= _options.MaxRetries; attempt++)
        {
            try
            {
                return await MakeRequestAsync(service, action, data, cancellationToken);
            }
            catch (RateLimitExceededException) when (attempt < _options.MaxRetries)
            {
                var delay = TimeSpan.FromSeconds(Math.Pow(2, attempt));
                _logger.LogWarning("Rate limited, retrying in {Delay}s (attempt {Attempt})", delay.TotalSeconds, attempt + 1);
                await Task.Delay(delay, cancellationToken);
            }
            catch (TeamConnectException ex) when (ex.Code == "NETWORK_ERROR" && attempt < _options.MaxRetries)
            {
                var delay = TimeSpan.FromSeconds(Math.Pow(2, attempt));
                _logger.LogWarning("Network error, retrying in {Delay}s (attempt {Attempt}): {Error}", delay.TotalSeconds, attempt + 1, ex.Message);
                await Task.Delay(delay, cancellationToken);
            }
        }

        throw new TeamConnectException("Request failed after all retries");
    }

    private async Task> MakeRequestAsync(string service, string action, object data, CancellationToken cancellationToken)
    {
        var request = new
        {
            service,
            action,
            data,
            api_key = _options.ApiKey
        };

        var json = JsonSerializer.Serialize(request, new JsonSerializerOptions
        {
            PropertyNamingPolicy = JsonNamingPolicy.CamelCase
        });

        var content = new StringContent(json, Encoding.UTF8, "application/json");

        var response = await _httpClient.PostAsync("/executeAPI", content, cancellationToken);
        var responseContent = await response.Content.ReadAsStringAsync(cancellationToken);

        // Handle different status codes
        switch (response.StatusCode)
        {
            case System.Net.HttpStatusCode.Unauthorized:
                throw new TeamConnectException("Invalid API key", "INVALID_API_KEY");
            case System.Net.HttpStatusCode.PaymentRequired:
                var errorResponse = JsonSerializer.Deserialize(responseContent);
                throw new InsufficientCreditsException(errorResponse?.Error ?? "Insufficient credits");
            case System.Net.HttpStatusCode.TooManyRequests:
                throw new RateLimitExceededException("Rate limit exceeded");
            case System.Net.HttpStatusCode.OK:
                return JsonSerializer.Deserialize>(responseContent, new JsonSerializerOptions
                {
                    PropertyNamingPolicy = JsonNamingPolicy.CamelCase
                });
            default:
                var apiError = JsonSerializer.Deserialize(responseContent);
                throw new TeamConnectException(apiError?.Error ?? $"HTTP {response.StatusCode}");
        }
    }
}

// Service registration extension
public static class ServiceCollectionExtensions
{
    public static IServiceCollection AddTeamConnect(this IServiceCollection services, IConfiguration configuration)
    {
        services.Configure(configuration.GetSection(TeamConnectOptions.SectionName));
        
        services.AddHttpClient((serviceProvider, client) =>
        {
            var options = serviceProvider.GetRequiredService>().Value;
            client.BaseAddress = new Uri(options.BaseUrl);
            client.Timeout = TimeSpan.FromSeconds(options.TimeoutSeconds);
            client.DefaultRequestHeaders.Add("Accept", "application/json");
        });

        return services;
    }
}

// appsettings.json configuration
/*
{
  "TeamConnect": {
    "ApiKey": "tc_live_your_api_key_here",
    "TimeoutSeconds": 30,
    "MaxRetries": 3
  }
}
*/

// Program.cs (ASP.NET Core)
/*
var builder = WebApplication.CreateBuilder(args);

// Add services
builder.Services.AddTeamConnect(builder.Configuration);
builder.Services.AddControllers();

var app = builder.Build();

// Configure pipeline
app.UseRouting();
app.MapControllers();

app.Run();
*/

// Usage in controller
[ApiController]
[Route("api/[controller]")]
public class CommunicationController : ControllerBase
{
    private readonly ITeamConnectService _teamConnect;
    private readonly ILogger _logger;

    public CommunicationController(ITeamConnectService teamConnect, ILogger logger)
    {
        _teamConnect = teamConnect;
        _logger = logger;
    }

    [HttpPost("call")]
    public async Task MakeCall([FromBody] CallRequest request, CancellationToken cancellationToken)
    {
        try
        {
            var (result, billing) = await _teamConnect.MakeCallAsync(request, cancellationToken);
            
            return Ok(new
            {
                Success = true,
                Data = result,
                Billing = billing
            });
        }
        catch (InsufficientCreditsException ex)
        {
            return StatusCode(402, new { Success = false, Error = ex.Message });
        }
        catch (TeamConnectException ex)
        {
            _logger.LogError(ex, "Team Connect API error");
            return StatusCode(500, new { Success = false, Error = ex.Message });
        }
    }

    [HttpPost("sms")]
    public async Task SendSms([FromBody] SmsRequest request, CancellationToken cancellationToken)
    {
        try
        {
            var (result, billing) = await _teamConnect.SendSmsAsync(request, cancellationToken);
            
            return Ok(new
            {
                Success = true,
                Data = result,
                Billing = billing
            });
        }
        catch (TeamConnectException ex)
        {
            _logger.LogError(ex, "Team Connect API error");
            return StatusCode(500, new { Success = false, Error = ex.Message });
        }
    }
}

📞 Voice Calls

Make AI-powered voice calls with C#'s async/await patterns:

// Voice call service with comprehensive features
public class VoiceCallService
{
    private readonly ITeamConnectService _teamConnect;
    private readonly ILogger _logger;

    public VoiceCallService(ITeamConnectService teamConnect, ILogger logger)
    {
        _teamConnect = teamConnect;
        _logger = logger;
    }

    public async Task MakeAppointmentReminderAsync(Appointment appointment, CancellationToken cancellationToken = default)
    {
        var message = $"Hi {appointment.CustomerName}, this is confirming your {appointment.ServiceType} appointment tomorrow at {appointment.DateTime:h:mm tt}. Reply YES to confirm or NO to reschedule.";

        var request = new CallRequest
        {
            To = appointment.CustomerPhone,
            Message = message,
            Voice = "Polly.Amy",
            WebhookUrl = $"https://yourapp.com/webhooks/appointment/{appointment.Id}"
        };

        try
        {
            var (result, billing) = await _teamConnect.MakeCallAsync(request, cancellationToken);

            _logger.LogInformation("Appointment reminder call initiated for {AppointmentId}: {CallId}",
                appointment.Id, result.CallId);

            return new CallResult
            {
                Success = true,
                CallId = result.CallId,
                Status = result.Status,
                Cost = billing.Cost,
                CreditsRemaining = billing.CreditsRemaining
            };
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to make appointment reminder call for {AppointmentId}", appointment.Id);
            
            return new CallResult
            {
                Success = false,
                Error = ex.Message
            };
        }
    }

    public async Task MakePaymentReminderAsync(Invoice invoice, CancellationToken cancellationToken = default)
    {
        var message = $"Hello {invoice.CustomerName}, this is a friendly reminder that your payment of £{invoice.Amount:F2} is due. You can pay online at {invoice.PaymentUrl} or call us back.";

        var request = new CallRequest
        {
            To = invoice.CustomerPhone,
            Message = message,
            WebhookUrl = $"https://yourapp.com/webhooks/payment/{invoice.Id}"
        };

        try
        {
            var (result, billing) = await _teamConnect.MakeCallAsync(request, cancellationToken);

            _logger.LogInformation("Payment reminder call initiated for invoice {InvoiceId}: {CallId}",
                invoice.Id, result.CallId);

            return new CallResult
            {
                Success = true,
                CallId = result.CallId,
                Status = result.Status,
                Cost = billing.Cost,
                CreditsRemaining = billing.CreditsRemaining
            };
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to make payment reminder call for invoice {InvoiceId}", invoice.Id);
            
            return new CallResult
            {
                Success = false,
                Error = ex.Message
            };
        }
    }

    public async Task> MakeBatchCallsAsync(IEnumerable requests, int maxConcurrency = 5, CancellationToken cancellationToken = default)
    {
        var semaphore = new SemaphoreSlim(maxConcurrency, maxConcurrency);
        var tasks = requests.Select(async request =>
        {
            await semaphore.WaitAsync(cancellationToken);
            
            try
            {
                var (result, billing) = await _teamConnect.MakeCallAsync(request, cancellationToken);
                
                return new CallResult
                {
                    Success = true,
                    CallId = result.CallId,
                    Status = result.Status,
                    To = result.To,
                    Cost = billing.Cost,
                    CreditsRemaining = billing.CreditsRemaining
                };
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Batch call failed for {PhoneNumber}", request.To);
                
                return new CallResult
                {
                    Success = false,
                    To = request.To,
                    Error = ex.Message
                };
            }
            finally
            {
                semaphore.Release();
            }
        });

        return (await Task.WhenAll(tasks)).ToList();
    }
}

// Supporting models
public class Appointment
{
    public string Id { get; set; }
    public string CustomerName { get; set; }
    public string CustomerPhone { get; set; }
    public string ServiceType { get; set; }
    public DateTime DateTime { get; set; }
}

public class Invoice
{
    public string Id { get; set; }
    public string CustomerName { get; set; }
    public string CustomerPhone { get; set; }
    public decimal Amount { get; set; }
    public string PaymentUrl { get; set; }
}

public class CallResult
{
    public bool Success { get; set; }
    public string CallId { get; set; }
    public string Status { get; set; }
    public string To { get; set; }
    public decimal Cost { get; set; }
    public decimal CreditsRemaining { get; set; }
    public string Error { get; set; }
}

// Usage example
public async Task ProcessAppointmentReminders()
{
    var voiceService = serviceProvider.GetRequiredService();
    
    var appointment = new Appointment
    {
        Id = "apt_123",
        CustomerName = "John Doe",
        CustomerPhone = "+447123456789",
        ServiceType = "dental cleaning",
        DateTime = DateTime.Tomorrow.AddHours(14) // 2 PM tomorrow
    };

    var result = await voiceService.MakeAppointmentReminderAsync(appointment);

    if (result.Success)
    {
        Console.WriteLine($"✅ Appointment reminder sent: {result.CallId}");
        Console.WriteLine($"💰 Cost: £{result.Cost:F2}");
        
        if (result.CreditsRemaining < 5.00m)
        {
            Console.WriteLine("⚠️ Low credits remaining!");
        }
    }
    else
    {
        Console.WriteLine($"❌ Failed to send reminder: {result.Error}");
    }
}

💬 SMS Messages

Send SMS messages with C#'s robust async patterns:

// SMS service with 2FA and notifications
public class SmsService
{
    private readonly ITeamConnectService _teamConnect;
    private readonly IMemoryCache _cache;
    private readonly ILogger _logger;

    public SmsService(ITeamConnectService teamConnect, IMemoryCache cache, ILogger logger)
    {
        _teamConnect = teamConnect;
        _cache = cache;
        _logger = logger;
    }

    public async Task SendTwoFactorCodeAsync(string phoneNumber, string appName = "Dad-Link", CancellationToken cancellationToken = default)
    {
        // Generate 6-digit code
        var code = Random.Shared.Next(100000, 999999).ToString();
        
        // Store in cache with 5-minute expiry
        var cacheKey = $"2fa:{phoneNumber}";
        var cacheValue = new TwoFactorCache
        {
            Code = code,
            Attempts = 0,
            CreatedAt = DateTime.UtcNow
        };
        
        _cache.Set(cacheKey, cacheValue, TimeSpan.FromMinutes(5));

        var request = new SmsRequest
        {
            To = phoneNumber,
            Message = $"Your {appName} verification code: {code}"
        };

        try
        {
            var (result, billing) = await _teamConnect.SendSmsAsync(request, cancellationToken);

            _logger.LogInformation("2FA code sent to {PhoneNumber}: {MessageId}",
                phoneNumber, result.MessageId);

            return new TwoFactorResult
            {
                Success = true,
                MessageId = result.MessageId,
                Code = code, // Don't expose in production
                ExpiresAt = DateTime.UtcNow.AddMinutes(5)
            };
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to send 2FA code to {PhoneNumber}", phoneNumber);
            
            // Remove from cache on failure
            _cache.Remove(cacheKey);
            
            return new TwoFactorResult
            {
                Success = false,
                Error = ex.Message
            };
        }
    }

    public bool VerifyTwoFactorCode(string phoneNumber, string inputCode)
    {
        var cacheKey = $"2fa:{phoneNumber}";
        
        if (!_cache.TryGetValue(cacheKey, out TwoFactorCache cacheValue))
        {
            _logger.LogWarning("2FA verification failed - code not found for {PhoneNumber}", phoneNumber);
            return false;
        }

        // Check if expired
        if (DateTime.UtcNow > cacheValue.CreatedAt.AddMinutes(5))
        {
            _cache.Remove(cacheKey);
            _logger.LogWarning("2FA verification failed - code expired for {PhoneNumber}", phoneNumber);
            return false;
        }

        // Check attempts
        if (cacheValue.Attempts >= 3)
        {
            _cache.Remove(cacheKey);
            _logger.LogWarning("2FA verification failed - too many attempts for {PhoneNumber}", phoneNumber);
            return false;
        }

        // Increment attempts
        cacheValue.Attempts++;
        _cache.Set(cacheKey, cacheValue, TimeSpan.FromMinutes(5));

        // Verify code
        if (cacheValue.Code == inputCode)
        {
            _cache.Remove(cacheKey);
            _logger.LogInformation("2FA verification successful for {PhoneNumber}", phoneNumber);
            return true;
        }

        _logger.LogWarning("2FA verification failed - invalid code for {PhoneNumber}", phoneNumber);
        return false;
    }

    public async Task> SendOrderNotificationsAsync(IEnumerable orders, CancellationToken cancellationToken = default)
    {
        var semaphore = new SemaphoreSlim(10, 10); // Limit concurrent SMS
        var tasks = orders.Select(async order =>
        {
            await semaphore.WaitAsync(cancellationToken);
            
            try
            {
                var request = new SmsRequest
                {
                    To = order.CustomerPhone,
                    Message = $"Your order #{order.Number} is {order.Status}. Track: {order.TrackingUrl}"
                };

                var (result, billing) = await _teamConnect.SendSmsAsync(request, cancellationToken);

                _logger.LogInformation("Order notification sent for {OrderNumber}: {MessageId}",
                    order.Number, result.MessageId);

                return new SmsResult
                {
                    Success = true,
                    MessageId = result.MessageId,
                    To = result.To,
                    OrderNumber = order.Number,
                    Cost = billing.Cost
                };
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to send order notification for {OrderNumber}", order.Number);
                
                return new SmsResult
                {
                    Success = false,
                    To = order.CustomerPhone,
                    OrderNumber = order.Number,
                    Error = ex.Message
                };
            }
            finally
            {
                semaphore.Release();
            }
        });

        return (await Task.WhenAll(tasks)).ToList();
    }
}

// Supporting models
public class TwoFactorCache
{
    public string Code { get; set; }
    public int Attempts { get; set; }
    public DateTime CreatedAt { get; set; }
}

public class TwoFactorResult
{
    public bool Success { get; set; }
    public string MessageId { get; set; }
    public string Code { get; set; } // For testing only
    public DateTime ExpiresAt { get; set; }
    public string Error { get; set; }
}

public class Order
{
    public string Number { get; set; }
    public string CustomerPhone { get; set; }
    public string Status { get; set; }
    public string TrackingUrl { get; set; }
}

public class SmsResult
{
    public bool Success { get; set; }
    public string MessageId { get; set; }
    public string To { get; set; }
    public string OrderNumber { get; set; }
    public decimal Cost { get; set; }
    public string Error { get; set; }
}

// Usage example in controller
[ApiController]
[Route("api/[controller]")]
public class AuthController : ControllerBase
{
    private readonly SmsService _smsService;

    public AuthController(SmsService smsService)
    {
        _smsService = smsService;
    }

    [HttpPost("send-2fa")]
    public async Task SendTwoFactorCode([FromBody] Send2FARequest request)
    {
        var result = await _smsService.SendTwoFactorCodeAsync(request.PhoneNumber, "Dad-Link");

        if (result.Success)
        {
            return Ok(new
            {
                Success = true,
                MessageId = result.MessageId,
                ExpiresAt = result.ExpiresAt
            });
        }

        return BadRequest(new
        {
            Success = false,
            Error = result.Error
        });
    }

    [HttpPost("verify-2fa")]
    public IActionResult VerifyTwoFactorCode([FromBody] Verify2FARequest request)
    {
        var isValid = _smsService.VerifyTwoFactorCode(request.PhoneNumber, request.Code);

        return Ok(new
        {
            Success = isValid,
            Message = isValid ? "Code verified successfully" : "Invalid or expired code"
        });
    }
}

public class Send2FARequest
{
    [Required]
    public string PhoneNumber { get; set; }
}

public class Verify2FARequest
{
    [Required]
    public string PhoneNumber { get; set; }
    
    [Required]
    public string Code { get; set; }
}

📧 Email Sending

Send HTML emails with C#'s templating and async capabilities:

// Email service with templating system
public class EmailService
{
    private readonly ITeamConnectService _teamConnect;
    private readonly ILogger _logger;

    public EmailService(ITeamConnectService teamConnect, ILogger logger)
    {
        _teamConnect = teamConnect;
        _logger = logger;
    }

    public async Task SendWelcomeEmailAsync(WelcomeEmailData data, CancellationToken cancellationToken = default)
    {
        var template = GetWelcomeEmailTemplate();
        var html = RenderTemplate(template, data);

        var request = new EmailRequest
        {
            To = data.CustomerEmail,
            Subject = $"Welcome to {data.CompanyName} - Your Account is Ready!",
            Html = html,
            FromName = $"{data.CompanyName} Team"
        };

        return await SendEmailAsync(request, "welcome", cancellationToken);
    }

    public async Task SendInvoiceEmailAsync(InvoiceEmailData data, CancellationToken cancellationToken = default)
    {
        var template = GetInvoiceEmailTemplate();
        var html = RenderTemplate(template, data);

        var request = new EmailRequest
        {
            To = data.CustomerEmail,
            Subject = $"Invoice #{data.InvoiceNumber} - Payment Due",
            Html = html,
            FromName = "Dad-Link Billing"
        };

        return await SendEmailAsync(request, "invoice", cancellationToken);
    }

    public async Task> SendEmailCampaignAsync(string templateName, IEnumerable recipients, Func selector, int maxConcurrency = 5, CancellationToken cancellationToken = default)
    {
        var semaphore = new SemaphoreSlim(maxConcurrency, maxConcurrency);
        var tasks = recipients.Select(async recipient =>
        {
            await semaphore.WaitAsync(cancellationToken);
            
            try
            {
                var (email, subject, data) = selector(recipient);
                var template = GetEmailTemplate(templateName);
                var html = RenderTemplate(template, data);

                var request = new EmailRequest
                {
                    To = email,
                    Subject = subject,
                    Html = html,
                    FromName = "Dad-Link Team"
                };

                return await SendEmailAsync(request, templateName, cancellationToken);
            }
            finally
            {
                semaphore.Release();
            }
        });

        return (await Task.WhenAll(tasks)).ToList();
    }

    private async Task SendEmailAsync(EmailRequest request, string templateType, CancellationToken cancellationToken)
    {
        try
        {
            var (result, billing) = await _teamConnect.SendEmailAsync(request, cancellationToken);

            _logger.LogInformation("{TemplateType} email sent to {Email}: {MessageId}",
                templateType, request.To, result.MessageId);

            return new EmailResult
            {
                Success = true,
                MessageId = result.MessageId,
                To = result.To,
                Subject = result.Subject,
                Cost = billing.Cost,
                CreditsRemaining = billing.CreditsRemaining
            };
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to send {TemplateType} email to {Email}", templateType, request.To);
            
            return new EmailResult
            {
                Success = false,
                To = request.To,
                Subject = request.Subject,
                Error = ex.Message
            };
        }
    }

    private string GetWelcomeEmailTemplate()
    {
        return @"



    


    

Welcome to {{CompanyName}}!

Hi {{CustomerName}},

Thanks for signing up! Your account is now active and ready to use.

Get started by exploring our features:

  • 🎤 AI Voice Calls
  • 💬 SMS Messaging
  • 📧 Email Campaigns
  • 🤖 AI Chat Integration
Get Started

If you have any questions, just reply to this email!

© 2025 {{CompanyName}}. All rights reserved.

You received this email because you signed up for our service.

"; } private string GetInvoiceEmailTemplate() { return @"

Invoice #{{InvoiceNumber}}

Hi {{CustomerName}},

Your invoice is ready for payment.

Amount Due: £{{Amount:F2}}

Due Date: {{DueDate:MMMM d, yyyy}}

Pay Now
"; } private string GetEmailTemplate(string templateName) { // In production, load from database or file system return templateName switch { "welcome" => GetWelcomeEmailTemplate(), "invoice" => GetInvoiceEmailTemplate(), _ => throw new ArgumentException($"Unknown template: {templateName}") }; } private string RenderTemplate(string template, object data) { // Simple template rendering - in production, use a proper template engine var rendered = template; var properties = data.GetType().GetProperties(); foreach (var prop in properties) { var value = prop.GetValue(data); var placeholder = $"{{{{{prop.Name}}}}}"; if (value is DateTime dateTime) { rendered = rendered.Replace(placeholder, dateTime.ToString("MMMM d, yyyy")); } else if (value is decimal || value is double || value is float) { rendered = rendered.Replace(placeholder, $"{value:F2}"); } else { rendered = rendered.Replace(placeholder, value?.ToString() ?? ""); } } return rendered; } } // Supporting models public class WelcomeEmailData { public string CustomerName { get; set; } public string CustomerEmail { get; set; } public string CompanyName { get; set; } public string DashboardUrl { get; set; } } public class InvoiceEmailData { public string CustomerName { get; set; } public string CustomerEmail { get; set; } public string InvoiceNumber { get; set; } public decimal Amount { get; set; } public DateTime DueDate { get; set; } public string PaymentUrl { get; set; } } public class EmailResult { public bool Success { get; set; } public string MessageId { get; set; } public string To { get; set; } public string Subject { get; set; } public decimal Cost { get; set; } public decimal CreditsRemaining { get; set; } public string Error { get; set; } } // Usage example public async Task ProcessWelcomeEmails() { var emailService = serviceProvider.GetRequiredService(); var welcomeData = new WelcomeEmailData { CustomerName = "John Doe", CustomerEmail = "john@example.com", CompanyName = "Dad-Link", DashboardUrl = "https://team-connect.co.uk/dashboard.html" }; var result = await emailService.SendWelcomeEmailAsync(welcomeData); if (result.Success) { Console.WriteLine($"✅ Welcome email sent: {result.MessageId}"); Console.WriteLine($"💰 Cost: £{result.Cost:F4}"); } else { Console.WriteLine($"❌ Failed to send welcome email: {result.Error}"); } }

🤖 AI Chat

Use GPT-4 for intelligent conversations with C#'s powerful async patterns:

// AI Chat service with conversation management
    public class AIChatService
    {
        private readonly ITeamConnectService _teamConnect;
        private readonly IMemoryCache _cache;
        private readonly ILogger _logger;

        public AIChatService(ITeamConnectService teamConnect, IMemoryCache cache, ILogger logger)
        {
            _teamConnect = teamConnect;
            _cache = cache;
            _logger = logger;
        }

        public async Task SendMessageAsync(string userId, string message, CancellationToken cancellationToken = default)
        {
            // Get or create conversation
            var conversation = GetOrCreateConversation(userId);
            
            // Add user message
            conversation.Messages.Add(new ChatMessage
            {
                Role = "user",
                Content = message
            });

            var request = new ChatRequest
            {
                Messages = conversation.Messages.ToArray(),
                Model = "gpt-4"
            };

            try
            {
                var (result, billing) = await _teamConnect.ChatAsync(request, cancellationToken);

                // Add AI response to conversation
                conversation.Messages.Add(new ChatMessage
                {
                    Role = "assistant",
                    Content = result.Response
                });

                // Update conversation in cache
                conversation.LastUsed = DateTime.UtcNow;
                UpdateConversation(userId, conversation);

                _logger.LogInformation("AI chat response generated for user {UserId}, tokens used: {TokensUsed}",
                    userId, result.TokensUsed);

                return new ChatResult
                {
                    Success = true,
                    Response = result.Response,
                    Model = result.Model,
                    TokensUsed = result.TokensUsed,
                    Cost = billing.Cost,
                    CreditsRemaining = billing.CreditsRemaining
                };
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "AI chat failed for user {UserId}", userId);
                
                // Remove the user message on failure
                if (conversation.Messages.Count > 0 && conversation.Messages.Last().Role == "user")
                {
                    conversation.Messages.RemoveAt(conversation.Messages.Count - 1);
                    UpdateConversation(userId, conversation);
                }

                return new ChatResult
                {
                    Success = false,
                    Error = ex.Message
                };
            }
        }

        public async Task GenerateContentAsync(string prompt, string systemContext = null, CancellationToken cancellationToken = default)
        {
            var messages = new List();
            
            if (!string.IsNullOrEmpty(systemContext))
            {
                messages.Add(new ChatMessage
                {
                    Role = "system",
                    Content = systemContext
                });
            }

            messages.Add(new ChatMessage
            {
                Role = "user",
                Content = prompt
            });

            var request = new ChatRequest
            {
                Messages = messages.ToArray(),
                Model = "gpt-4"
            };

            try
            {
                var (result, billing) = await _teamConnect.ChatAsync(request, cancellationToken);
                
                _logger.LogInformation("Content generated, tokens used: {TokensUsed}, cost: {Cost}",
                    result.TokensUsed, billing.CostFormatted);

                return result.Response;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Content generation failed for prompt: {Prompt}", prompt);
                throw;
            }
        }

        public async Task> GenerateEmailSubjectsAsync(string product, string audience, int count = 5, CancellationToken cancellationToken = default)
        {
            var prompt = $"Generate {count} compelling email subject lines for {product} targeting {audience}. Make them engaging and click-worthy. Return only the subject lines, one per line.";
            var systemContext = "You are a marketing copywriter specializing in email subject lines.";

            var content = await GenerateContentAsync(prompt, systemContext, cancellationToken);
            
            return content
                .Split('\n', StringSplitOptions.RemoveEmptyEntries)
                .Select(line => line.Trim())
                .Where(line => !string.IsNullOrEmpty(line))
                .Take(count)
                .ToList();
        }

        public async Task GenerateProductDescriptionAsync(string productName, string features, CancellationToken cancellationToken = default)
        {
            var prompt = $"Write a compelling product description for {productName} with these features: {features}. Make it professional and persuasive.";
            var systemContext = "You are a product marketing specialist who writes compelling product descriptions.";

            return await GenerateContentAsync(prompt, systemContext, cancellationToken);
        }

        public void ClearConversation(string userId)
        {
            var cacheKey = $"conversation:{userId}";
            _cache.Remove(cacheKey);
            _logger.LogInformation("Conversation cleared for user {UserId}", userId);
        }

        public void ClearOldConversations()
        {
            // This is simplified - in production, you'd need a proper cache eviction strategy
            _logger.LogInformation("Old conversations cleanup triggered");
        }

        private Conversation GetOrCreateConversation(string userId)
        {
            var cacheKey = $"conversation:{userId}";
            
            if (_cache.TryGetValue(cacheKey, out Conversation conversation))
            {
                return conversation;
            }

            conversation = new Conversation
            {
                UserId = userId,
                Messages = new List
                {
                    new ChatMessage
                    {
                        Role = "system",
                        Content = "You are a helpful customer support assistant for Dad-Link, a communication platform. Be friendly and provide accurate information about our voice, SMS, email, and AI services."
                    }
                },
                CreatedAt = DateTime.UtcNow,
                LastUsed = DateTime.UtcNow
            };

            UpdateConversation(userId, conversation);
            return conversation;
        }

        private void UpdateConversation(string userId, Conversation conversation)
        {
            var cacheKey = $"conversation:{userId}";
            var options = new MemoryCacheEntryOptions
            {
                SlidingExpiration = TimeSpan.FromHours(2),
                AbsoluteExpirationRelativeToNow = TimeSpan.FromDays(1)
            };
            
            _cache.Set(cacheKey, conversation, options);
        }
    }

    // Supporting models
    public class Conversation
    {
        public string UserId { get; set; }
        public List Messages { get; set; } = new();
        public DateTime CreatedAt { get; set; }
        public DateTime LastUsed { get; set; }
    }

    public class ChatResult
    {
        public bool Success { get; set; }
        public string Response { get; set; }
        public string Model { get; set; }
        public int TokensUsed { get; set; }
        public decimal Cost { get; set; }
        public decimal CreditsRemaining { get; set; }
        public string Error { get; set; }
    }

    // Usage in SignalR Hub for real-time chat
    public class ChatHub : Hub
    {
        private readonly AIChatService _aiChatService;
        private readonly ILogger _logger;

        public ChatHub(AIChatService aiChatService, ILogger logger)
        {
            _aiChatService = aiChatService;
            _logger = logger;
        }

        public async Task SendMessage(string message)
        {
            var userId = Context.UserIdentifier;
            
            try
            {
                var result = await _aiChatService.SendMessageAsync(userId, message);

                if (result.Success)
                {
                    await Clients.Caller.SendAsync("ReceiveMessage", result.Response);
                    
                    // Optionally send billing info
                    await Clients.Caller.SendAsync("BillingUpdate", new
                    {
                        Cost = result.Cost,
                        CreditsRemaining = result.CreditsRemaining,
                        TokensUsed = result.TokensUsed
                    });
                }
                else
                {
                    await Clients.Caller.SendAsync("Error", result.Error);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Chat error for user {UserId}", userId);
                await Clients.Caller.SendAsync("Error", "An error occurred processing your message");
            }
        }

        public async Task ClearConversation()
        {
            var userId = Context.UserIdentifier;
            _aiChatService.ClearConversation(userId);
            await Clients.Caller.SendAsync("ConversationCleared");
        }
    }

    // Usage in controller
    [ApiController]
    [Route("api/[controller]")]
    public class ContentController : ControllerBase
    {
        private readonly AIChatService _aiChatService;

        public ContentController(AIChatService aiChatService)
        {
            _aiChatService = aiChatService;
        }

        [HttpPost("email-subjects")]
        public async Task GenerateEmailSubjects([FromBody] GenerateSubjectsRequest request)
        {
            try
            {
                var subjects = await _aiChatService.GenerateEmailSubjectsAsync(
                    request.Product,
                    request.Audience,
                    request.Count
                );

                return Ok(new
                {
                    Success = true,
                    Subjects = subjects
                });
            }
            catch (Exception ex)
            {
                return StatusCode(500, new
                {
                    Success = false,
                    Error = ex.Message
                });
            }
        }

        [HttpPost("product-description")]
        public async Task GenerateProductDescription([FromBody] GenerateDescriptionRequest request)
        {
            try
            {
                var description = await _aiChatService.GenerateProductDescriptionAsync(
                    request.ProductName,
                    request.Features
                );

                return Ok(new
                {
                    Success = true,
                    Description = description
                });
            }
            catch (Exception ex)
            {
                return StatusCode(500, new
                {
                    Success = false,
                    Error = ex.Message
                });
            }
        }
    }

    public class GenerateSubjectsRequest
    {
        [Required]
        public string Product { get; set; }
        
        [Required]
        public string Audience { get; set; }
        
        [Range(1, 20)]
        public int Count { get; set; } = 5;
    }

    public class GenerateDescriptionRequest
    {
        [Required]
        public string ProductName { get; set; }
        
        [Required]
        public string Features { get; set; }
    }

⚡ Framework Examples

Ready-to-use examples for popular .NET frameworks and platforms:

// Complete ASP.NET Core Web API Integration
    using Microsoft.AspNetCore.Authentication.JwtBearer;
    using Microsoft.AspNetCore.Authorization;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.SignalR;

    // Startup/Program.cs configuration
    var builder = WebApplication.CreateBuilder(args);

    // Add services
    builder.Services.AddTeamConnect(builder.Configuration);
    builder.Services.AddScoped();
    builder.Services.AddScoped();
    builder.Services.AddScoped();
    builder.Services.AddScoped();

    builder.Services.AddMemoryCache();
    builder.Services.AddSignalR();

    builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(options =>
        {
            // JWT configuration
        });

    builder.Services.AddControllers();
    builder.Services.AddEndpointsApiExplorer();
    builder.Services.AddSwaggerGen();

    var app = builder.Build();

    // Configure pipeline
    if (app.Environment.IsDevelopment())
    {
        app.UseSwagger();
        app.UseSwaggerUI();
    }

    app.UseHttpsRedirection();
    app.UseAuthentication();
    app.UseAuthorization();

    app.MapControllers();
    app.MapHub("/chathub");

    app.Run();

    // Communication Controller with full CRUD operations
    [ApiController]
    [Route("api/[controller]")]
    [Authorize]
    public class CommunicationController : ControllerBase
    {
        private readonly VoiceCallService _voiceService;
        private readonly SmsService _smsService;
        private readonly EmailService _emailService;
        private readonly ILogger _logger;

        public CommunicationController(
            VoiceCallService voiceService,
            SmsService smsService,
            EmailService emailService,
            ILogger logger)
        {
            _voiceService = voiceService;
            _smsService = smsService;
            _emailService = emailService;
            _logger = logger;
        }

        [HttpPost("voice/call")]
        public async Task MakeCall([FromBody] CallRequest request)
        {
            try
            {
                var userId = User.Identity?.Name;
                _logger.LogInformation("User {UserId} making call to {PhoneNumber}", userId, request.To);

                var result = await _voiceService.MakeAppointmentReminderAsync(new Appointment
                {
                    CustomerPhone = request.To,
                    CustomerName = "Customer",
                    ServiceType = "Service",
                    DateTime = DateTime.Now.AddDays(1)
                });

                if (result.Success)
                {
                    return Ok(new
                    {
                        Success = true,
                        Data = new
                        {
                            CallId = result.CallId,
                            Status = result.Status,
                            Cost = result.Cost,
                            CreditsRemaining = result.CreditsRemaining
                        }
                    });
                }

                return BadRequest(new { Success = false, Error = result.Error });
            }
            catch (InsufficientCreditsException ex)
            {
                return StatusCode(402, new { Success = false, Error = ex.Message });
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error making call");
                return StatusCode(500, new { Success = false, Error = "Internal server error" });
            }
        }

        [HttpPost("sms/send")]
        public async Task SendSms([FromBody] SmsRequest request)
        {
            try
            {
                var (result, billing) = await _smsService._teamConnect.SendSmsAsync(request);

                return Ok(new
                {
                    Success = true,
                    Data = new
                    {
                        MessageId = result.MessageId,
                        Status = result.Status,
                        Cost = billing.Cost
                    }
                });
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error sending SMS");
                return StatusCode(500, new { Success = false, Error = ex.Message });
            }
        }

        [HttpPost("email/send")]
        public async Task SendEmail([FromBody] EmailRequest request)
        {
            try
            {
                var result = await _emailService.SendWelcomeEmailAsync(new WelcomeEmailData
                {
                    CustomerEmail = request.To,
                    CustomerName = "Customer",
                    CompanyName = "Dad-Link",
                    DashboardUrl = "https://team-connect.co.uk/dashboard.html"
                });

                if (result.Success)
                {
                    return Ok(new
                    {
                        Success = true,
                        Data = new
                        {
                            MessageId = result.MessageId,
                            Cost = result.Cost
                        }
                    });
                }

                return BadRequest(new { Success = false, Error = result.Error });
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error sending email");
                return StatusCode(500, new { Success = false, Error = ex.Message });
            }
        }

        [HttpPost("webhooks/team-connect")]
        [AllowAnonymous]
        public async Task HandleWebhook([FromBody] WebhookPayload payload)
        {
            try
            {
                _logger.LogInformation("Webhook received: {Event}", payload.Event);

                // Process webhook based on event type
                switch (payload.Event)
                {
                    case "call.completed":
                        await ProcessCallCompleted(payload.Data);
                        break;
                    case "sms.delivered":
                        await ProcessSmsDelivered(payload.Data);
                        break;
                    case "email.delivered":
                        await ProcessEmailDelivered(payload.Data);
                        break;
                }

                return Ok(new { Received = true });
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error processing webhook");
                return StatusCode(500);
            }
        }

        private async Task ProcessCallCompleted(Dictionary data)
        {
            var callId = data["call_id"]?.ToString();
            var duration = Convert.ToInt32(data["duration"]);
            
            _logger.LogInformation("Call {CallId} completed, duration: {Duration}s", callId, duration);
            
            // Update database, send notifications, etc.
        }

        private async Task ProcessSmsDelivered(Dictionary data)
        {
            var messageId = data["message_id"]?.ToString();
            
            _logger.LogInformation("SMS {MessageId} delivered", messageId);
            
            // Update delivery status in database
        }

        private async Task ProcessEmailDelivered(Dictionary data)
        {
            var messageId = data["message_id"]?.ToString();
            
            _logger.LogInformation("Email {MessageId} delivered", messageId);
            
            // Update email campaign statistics
        }
    }

    public class WebhookPayload
    {
        public string Event { get; set; }
        public Dictionary Data { get; set; }
    }
// Blazor Server/WebAssembly Integration
    @page "/communication"
    @using Microsoft.AspNetCore.SignalR.Client
    @inject ITeamConnectService TeamConnect
    @inject IJSRuntime JSRuntime
    @implements IAsyncDisposable

    

Team Connect Communication

Voice Calls
SMS Messages
@(160 - (smsRequest.Message?.Length ?? 0)) characters remaining
AI Chat Assistant
@foreach (var message in chatMessages) {
@(message.IsUser ? "You" : "AI"):

@message.Content

@message.Timestamp.ToString("HH:mm")
}
@if (!string.IsNullOrEmpty(statusMessage)) {
@statusMessage
} @code { private CallRequest callRequest = new(); private SmsRequest smsRequest = new(); private string chatInput = ""; private bool isLoading = false; private string statusMessage = ""; private List chatMessages = new(); private HubConnection? hubConnection; protected override async Task OnInitializedAsync() { // Initialize SignalR connection for real-time chat hubConnection = new HubConnectionBuilder() .WithUrl("/chathub") .Build(); hubConnection.On("ReceiveMessage", (message) => { chatMessages.Add(new ChatMessage { Content = message, IsUser = false, Timestamp = DateTime.Now }); InvokeAsync(StateHasChanged); }); hubConnection.On("Error", (error) => { statusMessage = $"❌ {error}"; InvokeAsync(StateHasChanged); }); await hubConnection.StartAsync(); } private async Task MakeCall() { isLoading = true; statusMessage = ""; try { var (result, billing) = await TeamConnect.MakeCallAsync(callRequest); statusMessage = $"✅ Call initiated: {result.CallId} (Cost: {billing.CostFormatted})"; callRequest = new CallRequest(); // Reset form } catch (Exception ex) { statusMessage = $"❌ Call failed: {ex.Message}"; } finally { isLoading = false; } } private async Task SendSms() { isLoading = true; statusMessage = ""; try { var (result, billing) = await TeamConnect.SendSmsAsync(smsRequest); statusMessage = $"✅ SMS sent: {result.MessageId} (Cost: {billing.CostFormatted})"; smsRequest = new SmsRequest(); // Reset form } catch (Exception ex) { statusMessage = $"❌ SMS failed: {ex.Message}"; } finally { isLoading = false; } } private async Task SendChatMessage() { if (string.IsNullOrWhiteSpace(chatInput) || isLoading) return; isLoading = true; // Add user message to chat chatMessages.Add(new ChatMessage { Content = chatInput, IsUser = true, Timestamp = DateTime.Now }); var message = chatInput; chatInput = ""; StateHasChanged(); try { if (hubConnection?.State == HubConnectionState.Connected) { await hubConnection.SendAsync("SendMessage", message); } } catch (Exception ex) { statusMessage = $"❌ Chat error: {ex.Message}"; } finally { isLoading = false; } } public async ValueTask DisposeAsync() { if (hubConnection is not null) { await hubConnection.DisposeAsync(); } } public class ChatMessage { public string Content { get; set; } = ""; public bool IsUser { get; set; } public DateTime Timestamp { get; set; } } }
// Azure Functions Integration
    using Microsoft.Azure.Functions.Worker;
    using Microsoft.Azure.Functions.Worker.Http;
    using Microsoft.Extensions.Logging;
    using System.Net;
    using System.Text.Json;

    // Startup.cs or Program.cs for Azure Functions
    var host = new HostBuilder()
        .ConfigureFunctionsWorkerDefaults()
        .ConfigureServices(services =>
        {
            services.AddSingleton(provider =>
            {
                var apiKey = Environment.GetEnvironmentVariable("TEAM_CONNECT_API_KEY");
                var httpClient = new HttpClient();
                var logger = provider.GetRequiredService>();
                var options = Microsoft.Extensions.Options.Options.Create(new TeamConnectOptions
                {
                    ApiKey = apiKey
                });
                
                return new TeamConnectService(httpClient, options, logger);
            });
        })
        .Build();

    host.Run();

    // HTTP Trigger for Voice Calls
    public class VoiceCallFunction
    {
        private readonly ITeamConnectService _teamConnect;
        private readonly ILogger _logger;

        public VoiceCallFunction(ITeamConnectService teamConnect, ILogger logger)
        {
            _teamConnect = teamConnect;
            _logger = logger;
        }

        [Function("MakeCall")]
        public async Task MakeCall(
            [HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequestData req)
        {
            try
            {
                var requestBody = await new StreamReader(req.Body).ReadToEndAsync();
                var callRequest = JsonSerializer.Deserialize(requestBody);

                var (result, billing) = await _teamConnect.MakeCallAsync(callRequest);

                var response = req.CreateResponse(HttpStatusCode.OK);
                await response.WriteAsJsonAsync(new
                {
                    Success = true,
                    Data = new
                    {
                        CallId = result.CallId,
                        Status = result.Status,
                        Cost = billing.Cost
                    }
                });

                return response;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error making call");
                
                var errorResponse = req.CreateResponse(HttpStatusCode.InternalServerError);
                await errorResponse.WriteAsJsonAsync(new
                {
                    Success = false,
                    Error = ex.Message
                });

                return errorResponse;
            }
        }
    }

    // Timer Trigger for Scheduled Tasks
    public class ScheduledCommunicationFunction
    {
        private readonly ITeamConnectService _teamConnect;
        private readonly ILogger _logger;

        public ScheduledCommunicationFunction(ITeamConnectService teamConnect, ILogger logger)
        {
            _teamConnect = teamConnect;
            _logger = logger;
        }

        [Function("SendDailyReminders")]
        public async Task SendDailyReminders(
            [TimerTrigger("0 0 9 * * *")] TimerInfo timer) // Daily at 9 AM
        {
            _logger.LogInformation("Starting daily reminder job at {Time}", DateTime.UtcNow);

            try
            {
                // Get appointments from database (simulated)
                var appointments = await GetTodaysAppointments();

                var tasks = appointments.Select(async appointment =>
                {
                    var callRequest = new CallRequest
                    {
                        To = appointment.CustomerPhone,
                        Message = $"Hi {appointment.CustomerName}, this is a reminder for your {appointment.ServiceType} appointment today at {appointment.DateTime:h:mm tt}.",
                        Voice = "Polly.Amy"
                    };

                    try
                    {
                        var (result, billing) = await _teamConnect.MakeCallAsync(callRequest);
                        _logger.LogInformation("Reminder call sent for appointment {AppointmentId}: {CallId}",
                            appointment.Id, result.CallId);
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError(ex, "Failed to send reminder for appointment {AppointmentId}", appointment.Id);
                    }
                });

                await Task.WhenAll(tasks);
                
                _logger.LogInformation("Daily reminder job completed");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error in daily reminder job");
            }
        }

        private async Task> GetTodaysAppointments()
        {
            // Simulate database call
            await Task.Delay(100);
            
            return new List
            {
                new Appointment
                {
                    Id = "apt_1",
                    CustomerName = "John Doe",
                    CustomerPhone = "+447123456789",
                    ServiceType = "dental cleaning",
                    DateTime = DateTime.Today.AddHours(14)
                }
            };
        }
    }

    // Queue Trigger for Processing Messages
    public class QueueProcessorFunction
    {
        private readonly ITeamConnectService _teamConnect;
        private readonly ILogger _logger;

        public QueueProcessorFunction(ITeamConnectService teamConnect, ILogger logger)
        {
            _teamConnect = teamConnect;
            _logger = logger;
        }

        [Function("ProcessSmsQueue")]
        public async Task ProcessSmsQueue(
            [ServiceBusTrigger("sms-queue", Connection = "ServiceBusConnection")] string message)
        {
            try
            {
                var smsTask = JsonSerializer.Deserialize(message);
                
                var smsRequest = new SmsRequest
                {
                    To = smsTask.PhoneNumber,
                    Message = smsTask.Message
                };

                var (result, billing) = await _teamConnect.SendSmsAsync(smsRequest);
                
                _logger.LogInformation("SMS sent from queue: {MessageId}, Cost: {Cost}",
                    result.MessageId, billing.CostFormatted);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error processing SMS queue message: {Message}", message);
                throw; // Re-throw to trigger retry logic
            }
        }
    }

    public class SmsTask
    {
        public string PhoneNumber { get; set; }
        public string Message { get; set; }
        public string TaskId { get; set; }
    }
// Worker Service for Background Processing
    using Microsoft.Extensions.Hosting;
    using Microsoft.Extensions.DependencyInjection;

    // Program.cs
    var builder = Host.CreateApplicationBuilder(args);

    builder.Services.AddTeamConnect(builder.Configuration);
    builder.Services.AddHostedService();
    builder.Services.AddHostedService();

    var host = builder.Build();
    host.Run();

    // Background worker for processing communication queue
    public class CommunicationWorker : BackgroundService
    {
        private readonly IServiceProvider _serviceProvider;
        private readonly ILogger _logger;
        private readonly IConfiguration _configuration;

        public CommunicationWorker(
            IServiceProvider serviceProvider,
            ILogger logger,
            IConfiguration configuration)
        {
            _serviceProvider = serviceProvider;
            _logger = logger;
            _configuration = configuration;
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            _logger.LogInformation("Communication Worker started");

            while (!stoppingToken.IsCancellationRequested)
            {
                try
                {
                    using var scope = _serviceProvider.CreateScope();
                    var teamConnect = scope.ServiceProvider.GetRequiredService();

                    // Process communication queue (simulated)
                    var queueItems = await GetQueueItems();

                    if (queueItems.Any())
                    {
                        await ProcessQueueItems(teamConnect, queueItems, stoppingToken);
                    }

                    // Wait before next iteration
                    await Task.Delay(TimeSpan.FromSeconds(30), stoppingToken);
                }
                catch (OperationCanceledException)
                {
                    _logger.LogInformation("Communication Worker stopping");
                    break;
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "Error in Communication Worker");
                    await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken); // Wait before retry
                }
            }
        }

        private async Task> GetQueueItems()
        {
            // Simulate getting items from queue/database
            await Task.Delay(100);
            
            return new List
            {
                new CommunicationTask
                {
                    Id = Guid.NewGuid().ToString(),
                    Type = "sms",
                    PhoneNumber = "+447123456789",
                    Message = "Your order has been shipped!",
                    ScheduledFor = DateTime.UtcNow
                }
            };
        }

        private async Task ProcessQueueItems(ITeamConnectService teamConnect, List items, CancellationToken stoppingToken)
        {
            var semaphore = new SemaphoreSlim(5, 5); // Process 5 items concurrently
            
            var tasks = items.Select(async item =>
            {
                await semaphore.WaitAsync(stoppingToken);
                
                try
                {
                    await ProcessQueueItem(teamConnect, item, stoppingToken);
                }
                finally
                {
                    semaphore.Release();
                }
            });

            await Task.WhenAll(tasks);
        }

        private async Task ProcessQueueItem(ITeamConnectService teamConnect, CommunicationTask item, CancellationToken stoppingToken)
        {
            try
            {
                switch (item.Type.ToLower())
                {
                    case "sms":
                        var smsRequest = new SmsRequest
                        {
                            To = item.PhoneNumber,
                            Message = item.Message
                        };
                        
                        var (smsResult, smsBilling) = await teamConnect.SendSmsAsync(smsRequest, stoppingToken);
                        
                        _logger.LogInformation("SMS sent: {MessageId}, Cost: {Cost}",
                            smsResult.MessageId, smsBilling.CostFormatted);
                        break;

                    case "voice":
                        var callRequest = new CallRequest
                        {
                            To = item.PhoneNumber,
                            Message = item.Message
                        };
                        
                        var (callResult, callBilling) = await teamConnect.MakeCallAsync(callRequest, stoppingToken);
                        
                        _logger.LogInformation("Call initiated: {CallId}, Cost: {Cost}",
                            callResult.CallId, callBilling.CostFormatted);
                        break;

                    default:
                        _logger.LogWarning("Unknown communication type: {Type}", item.Type);
                        break;
                }

                // Mark item as processed (simulated)
                await MarkItemAsProcessed(item.Id);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error processing communication task {TaskId}", item.Id);
                
                // Mark item for retry or dead letter
                await MarkItemForRetry(item.Id);
            }
        }

        private async Task MarkItemAsProcessed(string itemId)
        {
            // Update database/queue to mark item as processed
            await Task.Delay(50);
            _logger.LogDebug("Marked item {ItemId} as processed", itemId);
        }

        private async Task MarkItemForRetry(string itemId)
        {
            // Update database/queue to mark item for retry
            await Task.Delay(50);
            _logger.LogDebug("Marked item {ItemId} for retry", itemId);
        }
    }

    // Appointment reminder worker
    public class AppointmentReminderWorker : BackgroundService
    {
        private readonly IServiceProvider _serviceProvider;
        private readonly ILogger _logger;

        public AppointmentReminderWorker(
            IServiceProvider serviceProvider,
            ILogger logger)
        {
            _serviceProvider = serviceProvider;
            _logger = logger;
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            _logger.LogInformation("Appointment Reminder Worker started");

            while (!stoppingToken.IsCancellationRequested)
            {
                try
                {
                    var now = DateTime.UtcNow;
                    var nextRun = now.Date.AddDays(1).AddHours(8); // Run daily at 8 AM

                    if (now.Hour == 8 && now.Minute < 5) // Run between 8:00-8:05 AM
                    {
                        await ProcessDailyReminders(stoppingToken);
                    }

                    // Wait until next check
                    await Task.Delay(TimeSpan.FromMinutes(5), stoppingToken);
                }
                catch (OperationCanceledException)
                {
                    _logger.LogInformation("Appointment Reminder Worker stopping");
                    break;
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "Error in Appointment Reminder Worker");
                    await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
                }
            }
        }

        private async Task ProcessDailyReminders(CancellationToken stoppingToken)
        {
            using var scope = _serviceProvider.CreateScope();
            var teamConnect = scope.ServiceProvider.GetRequiredService();

            _logger.LogInformation("Processing daily appointment reminders");

            // Get today's appointments (simulated)
            var appointments = await GetTodaysAppointments();

            foreach (var appointment in appointments)
            {
                if (stoppingToken.IsCancellationRequested)
                    break;

                try
                {
                    var reminderTime = appointment.DateTime.AddHours(-2); // Send 2 hours before
                    
                    if (DateTime.UtcNow >= reminderTime)
                    {
                        var callRequest = new CallRequest
                        {
                            To = appointment.CustomerPhone,
                            Message = $"Hi {appointment.CustomerName}, this is a reminder for your {appointment.ServiceType} appointment today at {appointment.DateTime:h:mm tt}. Please call if you need to reschedule.",
                            Voice = "Polly.Amy"
                        };

                        var (result, billing) = await teamConnect.MakeCallAsync(callRequest, stoppingToken);
                        
                        _logger.LogInformation("Reminder sent for appointment {AppointmentId}: {CallId}",
                            appointment.Id, result.CallId);

                        // Mark reminder as sent
                        await MarkReminderSent(appointment.Id);
                    }
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "Failed to send reminder for appointment {AppointmentId}", appointment.Id);
                }

                // Small delay between calls to avoid rate limiting
                await Task.Delay(TimeSpan.FromSeconds(2), stoppingToken);
            }

            _logger.LogInformation("Daily appointment reminders completed");
        }

        private async Task> GetTodaysAppointments()
        {
            // Simulate database call
            await Task.Delay(100);
            
            return new List();
        }

        private async Task MarkReminderSent(string appointmentId)
        {
            // Update database
            await Task.Delay(50);
        }
    }

    public class CommunicationTask
    {
        public string Id { get; set; }
        public string Type { get; set; } // "sms", "voice", "email"
        public string PhoneNumber { get; set; }
        public string Message { get; set; }
        public DateTime ScheduledFor { get; set; }
        public int RetryCount { get; set; }
    }

⚠️ Error Handling

Production-ready error handling with C#'s exception patterns:

// Comprehensive error handling with Polly
    using Polly;
    using Polly.Extensions.Http;

    // Enhanced client with circuit breaker and retry policies
    public class EnhancedTeamConnectService : ITeamConnectService
    {
        private readonly HttpClient _httpClient;
        private readonly TeamConnectOptions _options;
        private readonly ILogger _logger;
        private readonly IAsyncPolicy _retryPolicy;
        private readonly IAsyncPolicy _circuitBreakerPolicy;
        private readonly IAsyncPolicy _combinedPolicy;

        public EnhancedTeamConnectService(
            HttpClient httpClient,
            IOptions options,
            ILogger logger)
        {
            _httpClient = httpClient;
            _options = options.Value;
            _logger = logger;

            // Retry policy with exponential backoff
            _retryPolicy = Policy
                .HandleResult(r => !r.IsSuccessStatusCode && r.StatusCode != System.Net.HttpStatusCode.PaymentRequired)
                .Or()
                .WaitAndRetryAsync(
                    retryCount: _options.MaxRetries,
                    sleepDurationProvider: retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
                    onRetry: (outcome, timespan, retryCount, context) =>
                    {
                        _logger.LogWarning("Retry {RetryCount} for {Operation} after {Delay}ms",
                            retryCount, context.OperationKey, timespan.TotalMilliseconds);
                    });

            // Circuit breaker policy
            _circuitBreakerPolicy = Policy
                .HandleResult(r => !r.IsSuccessStatusCode)
                .Or()
                .CircuitBreakerAsync(
                    handledEventsAllowedBeforeBreaking: 5,
                    durationOfBreak: TimeSpan.FromSeconds(30),
                    onBreak: (exception, duration) =>
                    {
                        _logger.LogError("Circuit breaker opened for {Duration}s due to: {Exception}",
                            duration.TotalSeconds, exception.Exception?.Message ?? exception.Result?.StatusCode.ToString());
                    },
                    onReset: () =>
                    {
                        _logger.LogInformation("Circuit breaker reset");
                    });

            // Combine policies
            _combinedPolicy = Policy.WrapAsync(_retryPolicy, _circuitBreakerPolicy);
        }

        public async Task<(CallResponse Result, BillingInfo Billing)> MakeCallAsync(CallRequest request, CancellationToken cancellationToken = default)
        {
            try
            {
                var context = new Context($"make-call-{request.To}");
                var response = await _combinedPolicy.ExecuteAsync(async (ctx) =>
                {
                    return await MakeHttpRequestAsync("voice", "make_call", request, cancellationToken);
                }, context);

                var apiResponse = await ProcessHttpResponse(response, cancellationToken);
                
                if (!apiResponse.Success)
                {
                    throw new TeamConnectException(apiResponse.Error ?? "Call failed");
                }

                _logger.LogInformation("Call successful: {CallId}, Cost: {Cost}",
                    apiResponse.Result.CallId, apiResponse.Billing?.CostFormatted);

                return (apiResponse.Result, apiResponse.Billing);
            }
            catch (BrokenCircuitException)
            {
                _logger.LogError("Circuit breaker is open for voice calls");
                throw new ServiceUnavailableException("Voice call service is temporarily unavailable");
            }
            catch (HttpRequestException ex)
            {
                _logger.LogError(ex, "Network error making call to {PhoneNumber}", request.To);
                throw new NetworkException("Network error occurred", ex);
            }
            catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException)
            {
                _logger.LogError(ex, "Timeout making call to {PhoneNumber}", request.To);
                throw new TimeoutException("Request timed out", ex);
            }
            catch (TeamConnectException)
            {
                throw; // Re-throw known exceptions
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Unexpected error making call to {PhoneNumber}", request.To);
                throw new TeamConnectException("An unexpected error occurred", ex);
            }
        }

        public async Task<(SmsResponse Result, BillingInfo Billing)> SendSmsAsync(SmsRequest request, CancellationToken cancellationToken = default)
        {
            try
            {
                var context = new Context($"send-sms-{request.To}");
                var response = await _combinedPolicy.ExecuteAsync(async (ctx) =>
                {
                    return await MakeHttpRequestAsync("sms", "send", request, cancellationToken);
                }, context);

                var apiResponse = await ProcessHttpResponse(response, cancellationToken);
                
                if (!apiResponse.Success)
                {
                    throw new TeamConnectException(apiResponse.Error ?? "SMS failed");
                }

                return (apiResponse.Result, apiResponse.Billing);
            }
            catch (BrokenCircuitException)
            {
                throw new ServiceUnavailableException("SMS service is temporarily unavailable");
            }
            catch (Exception ex) when (!(ex is TeamConnectException))
            {
                _logger.LogError(ex, "Error sending SMS to {PhoneNumber}", request.To);
                throw new TeamConnectException("Failed to send SMS", ex);
            }
        }

        public async Task<(EmailResponse Result, BillingInfo Billing)> SendEmailAsync(EmailRequest request, CancellationToken cancellationToken = default)
        {
            try
            {
                var context = new Context($"send-email-{request.To}");
                var response = await _combinedPolicy.ExecuteAsync(async (ctx) =>
                {
                    return await MakeHttpRequestAsync("email", "send", request, cancellationToken);
                }, context);

                var apiResponse = await ProcessHttpResponse(response, cancellationToken);
                
                if (!apiResponse.Success)
                {
                    throw new TeamConnectException(apiResponse.Error ?? "Email failed");
                }

                return (apiResponse.Result, apiResponse.Billing);
            }
            catch (BrokenCircuitException)
            {
                throw new ServiceUnavailableException("Email service is temporarily unavailable");
            }
            catch (Exception ex) when (!(ex is TeamConnectException))
            {
                _logger.LogError(ex, "Error sending email to {EmailAddress}", request.To);
                throw new TeamConnectException("Failed to send email", ex);
            }
        }

        public async Task<(ChatResponse Result, BillingInfo Billing)> ChatAsync(ChatRequest request, CancellationToken cancellationToken = default)
        {
            try
            {
                var context = new Context("ai-chat");
                var response = await _combinedPolicy.ExecuteAsync(async (ctx) =>
                {
                    return await MakeHttpRequestAsync("ai", "chat", request, cancellationToken);
                }, context);

                var apiResponse = await ProcessHttpResponse(response, cancellationToken);
                
                if (!apiResponse.Success)
                {
                    throw new TeamConnectException(apiResponse.Error ?? "AI chat failed");
                }

                return (apiResponse.Result, apiResponse.Billing);
            }
            catch (BrokenCircuitException)
            {
                throw new ServiceUnavailableException("AI service is temporarily unavailable");
            }
            catch (Exception ex) when (!(ex is TeamConnectException))
            {
                _logger.LogError(ex, "Error in AI chat");
                throw new TeamConnectException("AI chat failed", ex);
            }
        }

        private async Task MakeHttpRequestAsync(string service, string action, object data, CancellationToken cancellationToken)
        {
            var request = new
            {
                service,
                action,
                data,
                api_key = _options.ApiKey
            };

            var json = JsonSerializer.Serialize(request, new JsonSerializerOptions
            {
                PropertyNamingPolicy = JsonNamingPolicy.CamelCase
            });

            var content = new StringContent(json, Encoding.UTF8, "application/json");

            return await _httpClient.PostAsync("/executeAPI", content, cancellationToken);
        }

        private async Task> ProcessHttpResponse(HttpResponseMessage response, CancellationToken cancellationToken)
        {
            var responseContent = await response.Content.ReadAsStringAsync(cancellationToken);

            switch (response.StatusCode)
            {
                case System.Net.HttpStatusCode.Unauthorized:
                    throw new InvalidApiKeyException("Invalid or revoked API key");
                
                case System.Net.HttpStatusCode.PaymentRequired:
                    var errorResponse = JsonSerializer.Deserialize(responseContent);
                    throw new InsufficientCreditsException(errorResponse?.Error ?? "Insufficient credits");
                
                case System.Net.HttpStatusCode.TooManyRequests:
                    throw new RateLimitExceededException("Rate limit exceeded");
                
                case System.Net.HttpStatusCode.ServiceUnavailable:
                    throw new ServiceUnavailableException("Service temporarily unavailable");
                
                case System.Net.HttpStatusCode.OK:
                    return JsonSerializer.Deserialize>(responseContent, new JsonSerializerOptions
                    {
                        PropertyNamingPolicy = JsonNamingPolicy.CamelCase
                    });
                
                default:
                    var apiError = JsonSerializer.Deserialize(responseContent);
                    throw new TeamConnectException(apiError?.Error ?? $"HTTP {response.StatusCode}");
            }
        }
    }

    // Custom exception hierarchy
    public class TeamConnectException : Exception
    {
        public string Code { get; }

        public TeamConnectException(string message, string code = null) : base(message)
        {
            Code = code;
        }

        public TeamConnectException(string message, Exception innerException) : base(message, innerException)
        {
            Code = "UNKNOWN_ERROR";
        }
    }

    public class InvalidApiKeyException : TeamConnectException
    {
        public InvalidApiKeyException(string message) : base(message, "INVALID_API_KEY") { }
    }

    public class InsufficientCreditsException : TeamConnectException
    {
        public InsufficientCreditsException(string message) : base(message, "INSUFFICIENT_CREDITS") { }
    }

    public class RateLimitExceededException : TeamConnectException
    {
        public RateLimitExceededException(string message) : base(message, "RATE_LIMIT_EXCEEDED") { }
    }

    public class ServiceUnavailableException : TeamConnectException
    {
        public ServiceUnavailableException(string message) : base(message, "SERVICE_UNAVAILABLE") { }
    }

    public class NetworkException : TeamConnectException
    {
        public NetworkException(string message, Exception innerException) : base(message, innerException)
        {
            Code = "NETWORK_ERROR";
        }
    }

    // Global exception handler for ASP.NET Core
    public class GlobalExceptionHandler : IExceptionHandler
    {
        private readonly ILogger _logger;

        public GlobalExceptionHandler(ILogger logger)
        {
            _logger = logger;
        }

        public async ValueTask TryHandleAsync(HttpContext httpContext, Exception exception, CancellationToken cancellationToken)
        {
            var (statusCode, response) = exception switch
            {
                InvalidApiKeyException ex => (401, new { Success = false, Error = ex.Message, Code = ex.Code }),
                InsufficientCreditsException ex => (402, new { Success = false, Error = ex.Message, Code = ex.Code, TopUpUrl = "https://team-connect.co.uk/api-dashboard.html#topup" }),
                RateLimitExceededException ex => (429, new { Success = false, Error = ex.Message, Code = ex.Code }),
                ServiceUnavailableException ex => (503, new { Success = false, Error = ex.Message, Code = ex.Code }),
                TeamConnectException ex => (500, new { Success = false, Error = ex.Message, Code = ex.Code }),
                _ => (500, new { Success = false, Error = "An unexpected error occurred", Code = "INTERNAL_ERROR" })
            };

            _logger.LogError(exception, "Handling exception: {ExceptionType}", exception.GetType().Name);

            httpContext.Response.StatusCode = statusCode;
            await httpContext.Response.WriteAsJsonAsync(response, cancellationToken);

            return true;
        }
    }

    // Usage example with comprehensive error handling
    public class CommunicationController : ControllerBase
    {
        private readonly ITeamConnectService _teamConnect;

        public CommunicationController(ITeamConnectService teamConnect)
        {
            _teamConnect = teamConnect;
        }

        [HttpPost("call")]
        public async Task MakeCall([FromBody] CallRequest request)
        {
            // No try-catch needed - global exception handler will handle it
            var (result, billing) = await _teamConnect.MakeCallAsync(request);
            
            return Ok(new
            {
                Success = true,
                Data = result,
                Billing = billing
            });
        }
    }

💰 Pricing

Enterprise-grade pricing perfect for .NET applications and Azure cloud services:

Service Price Unit C# Example
📞 Voice Calls 7p per minute await client.MakeCallAsync(request)
💬 SMS Messages 3p per message await client.SendSmsAsync(request)
📧 Email Sending 0.15p per email await client.SendEmailAsync(request)
🤖 AI Processing 15p per request await client.ChatAsync(request)

💡 Enterprise .NET Cost Example

ASP.NET Core application with 50,000 users:

  • 5,000 voice calls (2 min avg) = £700.00
  • 25,000 SMS messages = £750.00
  • 100,000 emails = £150.00
  • 2,500 AI requests = £375.00
  • Total: £1,975.00/month (vs £250k+ building in-house)
🔷 Perfect for .NET: Our API is designed for modern .NET applications with full async/await support, dependency injection, SignalR integration, and Azure cloud deployment. Built for enterprise scale with comprehensive error handling.

💳 Manage Credits: Visit your API Dashboard to top up credits and monitor usage across your .NET applications.

Need help? Contact us at support@team-connect.co.uk

← Back to API Dashboard | REST API Docs | Azure Examples

© 2025 Dad-Link. All rights reserved. | Last updated: 2025-08-05 15:36:45 UTC