Sendly 3.21.1
dotnet add package Sendly --version 3.21.1
NuGet\Install-Package Sendly -Version 3.21.1
<PackageReference Include="Sendly" Version="3.21.1" />
<PackageVersion Include="Sendly" Version="3.21.1" />
<PackageReference Include="Sendly" />
paket add Sendly --version 3.21.1
#r "nuget: Sendly, 3.21.1"
#:package Sendly@3.21.1
#addin nuget:?package=Sendly&version=3.21.1
#tool nuget:?package=Sendly&version=3.21.1
<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:
Business Verification - Complete verification in the Sendly dashboard
- International: Instant approval (just provide Sender ID)
- US/Canada: Requires carrier approval (3-7 business days)
Credits - Add credits to your account
- Test keys (
sk_test_*) work without credits (sandbox mode) - Live keys (
sk_live_*) require credits for each message
- Test keys (
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 | Versions 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. |
-
net8.0
- System.Text.Json (>= 8.0.5)
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 |