Sendly 3.21.1

dotnet add package Sendly --version 3.21.1
                    
NuGet\Install-Package Sendly -Version 3.21.1
                    
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="Sendly" Version="3.21.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Sendly" Version="3.21.1" />
                    
Directory.Packages.props
<PackageReference Include="Sendly" />
                    
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 Sendly --version 3.21.1
                    
#r "nuget: Sendly, 3.21.1"
                    
#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 Sendly@3.21.1
                    
#: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=Sendly&version=3.21.1
                    
Install as a Cake Addin
#tool nuget:?package=Sendly&version=3.21.1
                    
Install as a Cake Tool

<p align="center"> <img src="https://raw.githubusercontent.com/SendlyHQ/sendly-dotnet/main/.github/header.svg" alt="Sendly .NET SDK" /> </p>

<p align="center"> <a href="https://www.nuget.org/packages/Sendly"><img src="https://img.shields.io/nuget/v/Sendly.svg?style=flat-square" alt="NuGet" /></a> <a href="https://github.com/SendlyHQ/sendly-dotnet/blob/main/LICENSE"><img src="https://img.shields.io/github/license/SendlyHQ/sendly-dotnet?style=flat-square" alt="license" /></a> </p>

Sendly .NET SDK

Official .NET SDK for the Sendly SMS API.

Requirements

  • .NET 8.0+

Installation

# .NET CLI
dotnet add package Sendly

# Package Manager Console
Install-Package Sendly

# PackageReference (add to .csproj)
<PackageReference Include="Sendly" Version="3.7.0" />

Quick Start

using Sendly;

using var client = new SendlyClient("sk_live_v1_your_api_key");

// Send an SMS
var message = await client.Messages.SendAsync(
    "+15551234567",
    "Hello from Sendly!"
);

Console.WriteLine(message.Id);     // "msg_abc123"
Console.WriteLine(message.Status); // "queued"

Prerequisites for Live Messaging

Before sending live SMS messages, you need:

  1. Business Verification - Complete verification in the Sendly dashboard

    • International: Instant approval (just provide Sender ID)
    • US/Canada: Requires carrier approval (3-7 business days)
  2. Credits - Add credits to your account

    • Test keys (sk_test_*) work without credits (sandbox mode)
    • Live keys (sk_live_*) require credits for each message
  3. Live API Key - Generate after verification + credits

    • Dashboard → API Keys → Create Live Key

Test vs Live Keys

Key Type Prefix Credits Required Verification Required Use Case
Test sk_test_v1_* No No Development, testing
Live sk_live_v1_* Yes Yes Production messaging

Note: You can start development immediately with a test key. Messages to sandbox test numbers are free and don't require verification.

Configuration

using var client = new SendlyClient("sk_live_v1_xxx", new SendlyClientOptions
{
    BaseUrl = "https://sendly.live/api/v1",
    Timeout = TimeSpan.FromSeconds(60),
    MaxRetries = 5
});

Messages

Send an SMS

// Marketing message (default)
var message = await client.Messages.SendAsync("+15551234567", "Check out our new features!");

// Transactional message (bypasses quiet hours)
var message = await client.Messages.SendAsync(new SendMessageRequest(
    "+15551234567",
    "Your verification code is: 123456"
) { MessageType = "transactional" });

// With custom metadata (max 4KB)
var message = await client.Messages.SendAsync(new SendMessageRequest(
    "+15551234567",
    "Your order #12345 has shipped!"
) { 
    Metadata = new Dictionary<string, object> 
    { 
        { "order_id", "12345" }, 
        { "customer_id", "cust_abc" } 
    } 
});

Console.WriteLine(message.Id);
Console.WriteLine(message.Status);
Console.WriteLine(message.CreditsUsed);

List Messages

// Basic listing
var messages = await client.Messages.ListAsync();

foreach (var msg in messages)
{
    Console.WriteLine(msg.To);
}

// With options
var messages = await client.Messages.ListAsync(new ListMessagesOptions
{
    Status = "delivered",
    To = "+15551234567",
    Limit = 50,
    Offset = 0
});

// Pagination info
Console.WriteLine(messages.Total);
Console.WriteLine(messages.HasMore);

Get a Message

var message = await client.Messages.GetAsync("msg_abc123");

Console.WriteLine(message.To);
Console.WriteLine(message.Text);
Console.WriteLine(message.Status);
Console.WriteLine(message.DeliveredAt);

Scheduling Messages

// Schedule a message for future delivery
var scheduled = await client.Messages.ScheduleAsync(new ScheduleMessageRequest(
    "+15551234567",
    "Your appointment is tomorrow!",
    "2025-01-15T10:00:00Z"
));

Console.WriteLine(scheduled.Id);
Console.WriteLine(scheduled.ScheduledAt);

// List scheduled messages
var result = await client.Messages.ListScheduledAsync();
foreach (var msg in result)
{
    Console.WriteLine($"{msg.Id}: {msg.ScheduledAt}");
}

// Get a specific scheduled message
var msg = await client.Messages.GetScheduledAsync("sched_xxx");

// Cancel a scheduled message (refunds credits)
var cancel = await client.Messages.CancelScheduledAsync("sched_xxx");
Console.WriteLine($"Refunded: {cancel.CreditsRefunded} credits");

Batch Messages

// Send multiple messages in one API call (up to 1000)
var batch = await client.Messages.SendBatchAsync(new SendBatchRequest()
    .AddMessage("+15551234567", "Hello User 1!")
    .AddMessage("+15559876543", "Hello User 2!")
    .AddMessage("+15551112222", "Hello User 3!")
);

Console.WriteLine(batch.BatchId);
Console.WriteLine($"Queued: {batch.Queued}");
Console.WriteLine($"Failed: {batch.Failed}");
Console.WriteLine($"Credits used: {batch.CreditsUsed}");

// Get batch status
var status = await client.Messages.GetBatchAsync("batch_xxx");

// List all batches
var batches = await client.Messages.ListBatchesAsync();

// Preview batch (dry run) - validates without sending
var preview = await client.Messages.PreviewBatchAsync(new SendBatchRequest()
    .AddMessage("+15551234567", "Hello User 1!")
    .AddMessage("+447700900123", "Hello UK!")
);
Console.WriteLine($"Total credits needed: {preview.TotalCredits}");
Console.WriteLine($"Valid: {preview.Valid}, Invalid: {preview.Invalid}");

Iterate All Messages

// Auto-pagination with IAsyncEnumerable
await foreach (var message in client.Messages.GetAllAsync())
{
    Console.WriteLine($"{message.Id}: {message.To}");
}

// With options
await foreach (var message in client.Messages.GetAllAsync(new ListMessagesOptions
{
    Status = "delivered"
}))
{
    Console.WriteLine($"Delivered: {message.Id}");
}

Webhooks

// Create a webhook endpoint
var webhook = await client.Webhooks.CreateAsync(new CreateWebhookRequest
{
    Url = "https://example.com/webhooks/sendly",
    Events = new[] { "message.delivered", "message.failed" }
});

Console.WriteLine(webhook.Id);
Console.WriteLine(webhook.Secret); // Store securely!

// List all webhooks
var webhooks = await client.Webhooks.ListAsync();

// Get a specific webhook
var wh = await client.Webhooks.GetAsync("whk_xxx");

// Update a webhook
await client.Webhooks.UpdateAsync("whk_xxx", new UpdateWebhookRequest
{
    Url = "https://new-endpoint.example.com/webhook",
    Events = new[] { "message.delivered", "message.failed", "message.sent" }
});

// Test a webhook
var result = await client.Webhooks.TestAsync("whk_xxx");

// Rotate webhook secret
var rotation = await client.Webhooks.RotateSecretAsync("whk_xxx");

// Delete a webhook
await client.Webhooks.DeleteAsync("whk_xxx");

// List available webhook event types
var eventTypes = await client.Webhooks.ListEventTypesAsync();
foreach (var eventType in eventTypes)
{
    Console.WriteLine($"Event: {eventType}");
}

Account & Credits

// Get account information
var account = await client.Account.GetAsync();
Console.WriteLine(account.Email);

// Check credit balance
var credits = await client.Account.GetCreditsAsync();
Console.WriteLine($"Available: {credits.AvailableBalance} credits");
Console.WriteLine($"Reserved: {credits.ReservedBalance} credits");
Console.WriteLine($"Total: {credits.Balance} credits");

// View credit transaction history
var transactions = await client.Account.GetCreditTransactionsAsync();
foreach (var tx in transactions)
{
    Console.WriteLine($"{tx.Type}: {tx.Amount} credits - {tx.Description}");
}

// List API keys
var keys = await client.Account.ListApiKeysAsync();
foreach (var key in keys)
{
    Console.WriteLine($"{key.Name}: {key.Prefix}*** ({key.Type})");
}

// Get a specific API key
var key = await client.Account.GetApiKeyAsync("key_xxx");

// Get API key usage stats
var usage = await client.Account.GetApiKeyUsageAsync("key_xxx");
Console.WriteLine($"Messages sent: {usage.MessagesSent}");

// Create a new API key
var newKey = await client.Account.CreateApiKeyAsync(new CreateApiKeyRequest
{
    Name = "Production Key",
    Type = "live",
    Scopes = new[] { "sms:send", "sms:read" }
});
Console.WriteLine($"New key: {newKey.Key}"); // Only shown once!

// Revoke an API key
await client.Account.RevokeApiKeyAsync("key_xxx");

Error Handling

using Sendly.Exceptions;

try
{
    var message = await client.Messages.SendAsync("+15551234567", "Hello!");
}
catch (AuthenticationException e)
{
    // Invalid API key
}
catch (RateLimitException e)
{
    // Rate limit exceeded
    Console.WriteLine($"Retry after: {e.RetryAfter?.TotalSeconds} seconds");
}
catch (InsufficientCreditsException e)
{
    // Add more credits
}
catch (ValidationException e)
{
    // Invalid request
}
catch (NotFoundException e)
{
    // Resource not found
}
catch (NetworkException e)
{
    // Network error
}
catch (SendlyException e)
{
    // Other error
    Console.WriteLine(e.Message);
    Console.WriteLine(e.ErrorCode);
    Console.WriteLine(e.StatusCode);
}

Message Object

message.Id           // Unique identifier
message.To           // Recipient phone number
message.Text         // Message content
message.Status       // queued, sending, sent, delivered, failed
message.CreditsUsed  // Credits consumed
message.CreatedAt    // DateTime
message.UpdatedAt    // DateTime
message.DeliveredAt  // DateTime? (nullable)
message.ErrorCode    // string? (nullable)
message.ErrorMessage // string? (nullable)

// Helper properties
message.IsDelivered  // bool
message.IsFailed     // bool
message.IsPending    // bool

Message Status

Status Description
queued Message is queued for delivery
sending Message is being sent
sent Message was sent to carrier
delivered Message was delivered
failed Message delivery failed

Pricing Tiers

Tier Countries Credits per SMS
Domestic US, CA 1
Tier 1 GB, PL, IN, etc. 8
Tier 2 FR, JP, AU, etc. 12
Tier 3 DE, IT, MX, etc. 16

Sandbox Testing

Use test API keys (sk_test_v1_xxx) with these test numbers:

Number Behavior
+15005550000 Success (instant)
+15005550001 Fails: invalid_number
+15005550002 Fails: unroutable_destination
+15005550003 Fails: queue_full
+15005550004 Fails: rate_limit_exceeded
+15005550006 Fails: carrier_violation

Enterprise

The Enterprise API lets you programmatically manage workspaces, verification, credits, and API keys for multi-tenant platforms. Requires an enterprise master key (sk_live_v1_master_*).

Quick Provision

Create a fully configured workspace in a single call:

var client = new SendlyClient("sk_live_v1_master_YOUR_KEY");

var result = await client.Enterprise.ProvisionAsync(new ProvisionWorkspaceOptions
{
    Name = "Acme Insurance - Austin",
    SourceWorkspaceId = "ws_verified",
    CreditAmount = 5000,
    CreditSourceWorkspaceId = "SOURCE_WORKSPACE_ID",
    KeyName = "Production",
    KeyType = "live",
    GenerateOptInPage = true
});

Console.WriteLine(result.Workspace.Id);
Console.WriteLine(result.Key?.Key);

Three provisioning modes:

Mode Params Description
Inherit SourceWorkspaceId Shares toll-free number from verified workspace
Inherit + New Number SourceWorkspaceId + InheritWithNewNumber = true Copies business info, purchases new number
Fresh Verification = new VerificationData{...} Full business details, new number + carrier approval

Workspace Management

var ws = await client.Enterprise.Workspaces.CreateAsync("Acme Insurance");
var list = await client.Enterprise.Workspaces.ListAsync();
var detail = await client.Enterprise.Workspaces.GetAsync("ws_xxx");
await client.Enterprise.Workspaces.DeleteAsync("ws_xxx");

Credits & API Keys

await client.Enterprise.Workspaces.TransferCreditsAsync("ws_dest", new TransferCreditsOptions
{
    SourceWorkspaceId = "ws_source",
    Amount = 5000
});

var key = await client.Enterprise.Workspaces.CreateKeyAsync("ws_xxx", new CreateKeyOptions
{
    Name = "Production",
    Type = "live"
});
Console.WriteLine(key.Key);

await client.Enterprise.Workspaces.RevokeKeyAsync("ws_xxx", "key_abc");

Webhooks & Analytics

await client.Enterprise.Webhooks.SetAsync("https://yourapp.com/webhooks");
var overview = await client.Enterprise.Analytics.OverviewAsync();
var messages = await client.Enterprise.Analytics.MessagesAsync("30d");
var delivery = await client.Enterprise.Analytics.DeliveryAsync();

Full enterprise docs: sendly.live/docs/enterprise


License

MIT

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  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
3.21.1 34 3/13/2026
3.21.0 31 3/11/2026
3.20.0 46 3/11/2026
3.19.1 78 3/9/2026
3.19.0 80 3/9/2026
3.18.2 79 3/6/2026
3.18.0 87 2/26/2026
3.17.0 89 2/23/2026
3.15.3 89 2/23/2026
3.15.2 93 2/8/2026
3.15.1 93 2/7/2026
3.13.1 100 2/2/2026
3.12.2 104 1/13/2026
3.12.1 104 1/10/2026
3.12.0 106 1/9/2026
3.11.0 100 1/6/2026
3.10.0 107 1/5/2026
3.9.0 103 1/5/2026
3.8.2 99 1/4/2026
3.8.1 105 1/4/2026
Loading failed