There are multiple versions of this document. Pick the options that suit you best.

SDK Language

Error Handling

The SDK uses a typed exception hierarchy so you can catch specific error categories and respond appropriately.

Exception hierarchy

OneSend2UException                    (base)
├── OneSend2UApiException             (HTTP errors from the server)
│   └── OneSend2URateLimitException   (HTTP 429 Too Many Requests)
└── OneSend2UValidationException      (client-side validation before HTTP call)

All exceptions inherit from OneSend2UException, which inherits from Exception.

OneSend2UApiException

Thrown when the API returns an HTTP error response (4xx or 5xx).

Property Type Description
StatusCode HttpStatusCode HTTP status code (e.g., 403, 404, 500)
ErrorCode string? Error code from the response body
Details string? Detailed error information
ValidationErrors IReadOnlyList<ValidationError>? Field-level validation errors (from 422 responses)
RateLimit RateLimitInfo? Rate limit headers parsed from the response
ResponseBody string? Raw response body

OneSend2URateLimitException

A subclass of OneSend2UApiException thrown specifically for HTTP 429 responses.

Property Type Description
RetryAfterSeconds int? Seconds to wait before retrying (from Retry-After header)

OneSend2UValidationException

Thrown by the SDK before making an HTTP call when required fields are missing or values exceed limits (e.g., TransactionId exceeds 100 characters).

HTTP status codes

Status Exception Common cause
400 Bad Request OneSend2UApiException Malformed request body
401 Unauthorized OneSend2UApiException Missing or invalid X-API-Key
403 Forbidden OneSend2UApiException Valid API key but insufficient permissions
404 Not Found OneSend2UApiException Entity does not exist or wrong tenant
409 Conflict OneSend2UApiException Concurrency conflict (stale ConcurrencyStamp)
422 Unprocessable Entity OneSend2UApiException Server-side validation failure; check ValidationErrors
429 Too Many Requests OneSend2URateLimitException Rate limit exceeded; check RetryAfterSeconds
500 Internal Server Error OneSend2UApiException Unexpected server error

Error handling example

using OneSend2U.Sdk.Exceptions;
using OneSend2U.Sdk.Models.Enums;
using OneSend2U.Sdk.Notifications.Models;

try
{
    var response = await client.Notifications.SendAsync(new SendNotificationRequest
    {
        TransactionId       = Guid.NewGuid().ToString(),
        Application         = "billing",
        Region              = "us",
        Language            = "en",
        NotificationType    = "trans",
        NotificationSubtype = "invoice",
        Recipients          = [new NotificationRecipient { Channel = Channel.Sms, Recipient = "+15550001234" }]
    });

    Console.WriteLine($"Sent: {response.Status}");
}
catch (OneSend2UValidationException ex)
{
    // Client-side validation failed — fix the request before retrying
    Console.Error.WriteLine($"Request validation failed: {ex.Message}");
}
catch (OneSend2URateLimitException ex)
{
    // Rate limit hit — wait and retry
    var waitSeconds = ex.RetryAfterSeconds ?? 60;
    Console.Error.WriteLine($"Rate limited. Retry after {waitSeconds}s.");
    await Task.Delay(TimeSpan.FromSeconds(waitSeconds));
    // retry logic here
}
catch (OneSend2UApiException ex) when (ex.StatusCode == System.Net.HttpStatusCode.Forbidden)
{
    // Permissions not assigned to the API key
    Console.Error.WriteLine($"Forbidden: {ex.ErrorCode} — {ex.Message}");
    Console.Error.WriteLine("Ensure the API key user has the required permissions.");
}
catch (OneSend2UApiException ex) when (ex.StatusCode == System.Net.HttpStatusCode.UnprocessableEntity)
{
    // Server-side validation errors
    Console.Error.WriteLine($"Validation errors:");
    foreach (var error in ex.ValidationErrors ?? [])
        Console.Error.WriteLine($"  [{string.Join(", ", error.Members ?? [])}] {error.Message}");
}
catch (OneSend2UApiException ex)
{
    // Other API errors
    Console.Error.WriteLine($"API error {(int)ex.StatusCode}: {ex.Message}");
    Console.Error.WriteLine($"Error code: {ex.ErrorCode}");
    Console.Error.WriteLine($"Details: {ex.Details}");
}
catch (Exception ex)
{
    // Network errors, timeouts, etc.
    Console.Error.WriteLine($"Unexpected error: {ex.Message}");
}

Rate limiting

The OneSend2U API enforces rate limits. When the limit is exceeded, the server returns HTTP 429 and the SDK throws OneSend2URateLimitException.

Rate limit response headers

Header Description
X-RateLimit-Limit Maximum requests allowed in the current window
X-RateLimit-Remaining Requests remaining in the current window
X-RateLimit-Reset UTC timestamp when the window resets
Retry-After Seconds to wait before retrying

The SDK parses these headers into RateLimitInfo:

catch (OneSend2URateLimitException ex)
{
    Console.Error.WriteLine($"Limit:     {ex.RateLimit?.Limit}");
    Console.Error.WriteLine($"Remaining: {ex.RateLimit?.Remaining}");
    Console.Error.WriteLine($"Reset:     {ex.RateLimit?.Reset:O}");
    Console.Error.WriteLine($"Retry in:  {ex.RetryAfterSeconds}s");
}

Built-in resilience (DI mode)

When you register the SDK with services.AddOneSend2U(...), the underlying HttpClient is configured with AddStandardResilienceHandler() from Microsoft.Extensions.Http.Resilience. This provides:

  • Retry: Automatically retries transient failures (5xx, network errors) with exponential backoff
  • Circuit breaker: Opens the circuit after repeated failures to prevent cascading load

These policies are transparent to application code. They apply before exceptions are thrown — the SDK will retry internally and only throw if all attempts are exhausted.

In non-DI mode (new OneSend2UClient(options)), no resilience policies are applied. Implement your own retry logic if needed.

Manual retry with Polly (non-DI mode)

using Polly;
using Polly.Retry;

var retryPipeline = new ResiliencePipelineBuilder()
    .AddRetry(new RetryStrategyOptions
    {
        MaxRetryAttempts = 3,
        Delay            = TimeSpan.FromSeconds(2),
        BackoffType      = DelayBackoffType.Exponential,
        ShouldHandle     = new PredicateBuilder()
            .Handle<OneSend2URateLimitException>()
            .Handle<HttpRequestException>()
    })
    .Build();

await retryPipeline.ExecuteAsync(async ct =>
{
    var result = await client.Notifications.SendAsync(request);
    Console.WriteLine($"Sent: {result.Status}");
});