OneSend2U.Sdk 2.0.0

dotnet add package OneSend2U.Sdk --version 2.0.0
                    
NuGet\Install-Package OneSend2U.Sdk -Version 2.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="OneSend2U.Sdk" Version="2.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="OneSend2U.Sdk" Version="2.0.0" />
                    
Directory.Packages.props
<PackageReference Include="OneSend2U.Sdk" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add OneSend2U.Sdk --version 2.0.0
                    
#r "nuget: OneSend2U.Sdk, 2.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package OneSend2U.Sdk@2.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=OneSend2U.Sdk&version=2.0.0
                    
Install as a Cake Addin
#tool nuget:?package=OneSend2U.Sdk&version=2.0.0
                    
Install as a Cake Tool

OneSend2U.Sdk

Lightweight .NET SDK for the OneSend2U CPaaS platform. Send notifications (SMS, Email, WhatsApp), query template configurations, track messages, manage webhooks, query API logs, manage contacts and contacts groups, and manage consents — all with API Key authentication, zero ABP dependencies.

Installation

dotnet add package OneSend2U.Sdk

Quick Start

Includes resilience policies (retry + circuit breaker) via Microsoft.Extensions.Http.Resilience.

services.AddOneSend2U(options =>
{
    options.BaseUrl = "https://api.onesend2u.com";
    options.ApiKey = "sk_prefix.your-api-key";
    options.TenantId = "your-tenant-id";
});

Then inject OneSend2UClient:

public class MyService(OneSend2UClient client)
{
    public async Task SendInvoiceAsync()
    {
        var result = await client.Notifications.SendAsync(new SendNotificationRequest
        {
            TransactionId = Guid.NewGuid().ToString(),
            Application = "myapp",
            Region = "es",
            Language = "es-ES",
            NotificationType = "ORD",
            NotificationSubtype = "CNF",
            Recipients = [new NotificationRecipient { Channel = "sms", Recipient = "+34699999999" }],
            TemplateVariables = [new Dictionary<string, string>
            {
                { "customer_name", "John Doe" },
                { "invoice_number", "INV-001" }
            }]
        });

        Console.WriteLine($"Status: {result.Status}");
    }
}

Non-DI Usage

Does not include resilience policies.

using var client = new OneSend2UClient(new OneSend2UClientOptions
{
    BaseUrl = "https://api.onesend2u.com",
    ApiKey = "sk_prefix.your-api-key",
    TenantId = "your-tenant-id"
});

var result = await client.Notifications.SendAsync(request);

Template Discovery Workflow

  1. Find template configurations for your app/region/environment:

    var configs = await client.Templates.GetListAsync(new GetTemplateConfigurationsRequest
    {
        IsActive = true,
        ApplicationId = myAppId
    });
    
  2. Get full details including the linked template:

    var details = await client.Templates.GetWithDetailsAsync(configId);
    var templateId = details.TemplateConfiguration!.TemplateId!.Value;
    
  3. Discover required variables:

    var variables = await client.Templates.GetTemplateVariableDefinitionsAsync(templateId);
    // Returns: [{ Name: "customer_name", SampleValue: "John" }, ...]
    
  4. Send using discovered variable names:

    await client.Notifications.SendAsync(new SendNotificationRequest
    {
        // ... codes from the template configuration ...
        TemplateVariables = [variables.ToDictionary(v => v.Name!, v => "actual value")]
    });
    

Validate Before Sending

Check if templates exist for a combination (uses codes, not IDs):

var validation = await client.Templates.ValidateAsync(new TemplateConfigurationValidationRequest
{
    ApplicationCode = "myapp",
    RegionCode = "br",
    Language = "pt-BR",
    NotificationTypeCode = "trans",
    NotificationSubtypeCode = "invoice",
    DeploymentEnvironmentId = envId,
    TargetChannels = [new TargetChannel { Channel = "sms" }]
});

if (validation.Status == TemplateConfigurationValidationStatus.All)
    Console.WriteLine("All channels configured!");

Webhooks

// Create
var webhook = await client.Webhooks.CreateAsync(new CreateWebhookRequest
{
    Name = "Status Updates",
    TargetEndPoint = "https://my-api.com/webhook",
    EventTypes = [EventType.Status],
    Statuses = [MessageProcessState.Success, MessageProcessState.Error]
});

// Update (requires ConcurrencyStamp from last read)
var current = await client.Webhooks.GetAsync(webhook.Id);
await client.Webhooks.UpdateAsync(webhook.Id, new UpdateWebhookRequest
{
    Name = "Updated Name",
    TargetEndPoint = current.TargetEndPoint!,
    ConcurrencyStamp = current.ConcurrencyStamp!,
    // ... other fields ...
});

// Test
var testResult = await client.Webhooks.TestAsync(current);
Console.WriteLine($"Test status: {testResult.StatusCode}");

Notifications (Read)

// List notifications with filters
var notifications = await client.Notifications.GetListAsync(new GetNotificationsRequest
{
    Status = NotificationStatus.Success,
    MaxResultCount = 50
});

// Get notification with navigation properties
var details = await client.Notifications.GetWithDetailsAsync(notificationId);
Console.WriteLine($"App: {details.Application?.Label}");

Messages

// List messages with filters
var messages = await client.Messages.GetListAsync(new GetMessagesRequest
{
    MessageProcessState = MessageProcessState.Error,
    CreationTimeMin = DateTime.UtcNow.AddDays(-7),
    MaxResultCount = 50
});

// Get message with full navigation properties
var details = await client.Messages.GetWithDetailsAsync(messageId);
Console.WriteLine($"Channel: {details.ChannelType?.Label}");

API Logs

// List API logs
var logs = await client.ApiLogs.GetListAsync(new GetApiLogsRequest
{
    Status = NotificationApiLogStatus.Error,
    MaxResultCount = 25
});

// Get single log with status history
var log = await client.ApiLogs.GetAsync(logId);
Console.WriteLine($"Duration: {log.DurationMs}ms, Statuses: {log.StatusHistory.Count}");

Contacts

// Create a contact
var contact = await client.Contacts.CreateAsync(new CreateContactRequest
{
    Name = "John",
    Email = "john@example.com",
    Phone = "+34699999999"
});

// Update (requires ConcurrencyStamp)
var current = await client.Contacts.GetAsync(contact.Id);
await client.Contacts.UpdateAsync(contact.Id, new UpdateContactRequest
{
    Name = "John Updated",
    Email = current.Email!,
    ConcurrencyStamp = current.ConcurrencyStamp!
});

Contacts Groups

// Create a group with contacts
var group = await client.ContactsGroups.CreateAsync(new CreateContactsGroupRequest
{
    Name = "VIP Customers",
    ContactIds = [contactId1, contactId2]
});

// Get group with contact details
var details = await client.ContactsGroups.GetWithDetailsAsync(group.Id);
Console.WriteLine($"Contacts: {details.Contacts.Count}");

Consents

// Create a consent
var consent = await client.Consents.CreateAsync(new CreateConsentRequest
{
    Recipient = "+34699999999",
    IsConsented = true,
    ApplicationId = appId,
    ChannelTypeId = smsChannelId
});

// Revoke consent
var current = await client.Consents.GetAsync(consent.Id);
await client.Consents.UpdateAsync(consent.Id, new UpdateConsentRequest
{
    Recipient = current.Recipient!,
    IsConsented = false,
    ConcurrencyStamp = current.ConcurrencyStamp!
});

Error Handling

The SDK throws typed exceptions:

try
{
    await client.Notifications.SendAsync(request);
}
catch (OneSend2UValidationException ex)
{
    // Client-side validation (before HTTP call)
    Console.WriteLine($"Field: {ex.FieldName}, Error: {ex.Message}");
}
catch (OneSend2URateLimitException ex)
{
    // HTTP 429 - retry after delay
    Console.WriteLine($"Retry after {ex.RetryAfterSeconds}s");
}
catch (OneSend2UApiException ex)
{
    // HTTP error from API
    Console.WriteLine($"Status: {ex.StatusCode}, Message: {ex.Message}");
    if (ex.ValidationErrors is not null)
    {
        foreach (var error in ex.ValidationErrors)
            Console.WriteLine($"  - {error.Message} ({string.Join(", ", error.Members ?? [])})");
    }
}

Configuration Options

Property Default Description
BaseUrl (required) API base URL
ApiKey (required) API key for X-API-Key header
TenantId (required) Tenant ID for X-Tenant-Id header
Timeout 30s HTTP request timeout
MaxRetries 3 Max retries (DI mode only)

Requirements

  • .NET 10.0+
  • No ABP framework dependencies

License

MIT

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.0.0 136 5/6/2026
1.3.0 121 3/27/2026
1.2.0 113 3/6/2026
1.1.0 116 3/5/2026
1.0.2 115 3/5/2026