Vonage Client Library for .NET
You can use this C# client library to integrate Vonage's APIs to your application. To use this, you'll need a Vonage API account. Sign up for free at vonage.com.
Installation
To use the client library you'll need to have created a Vonage account.
To install the C# client library using NuGet:
- Run the following command from your terminal in your projects directory:
dotnet add package Vonage
OR
- Run the following command in the Package Manager Console:
Install-Package Vonage
If you would prefer to run directly from source:
- Clone this repository
git clone https://github.com/vonage/vonage-dotnet-sdk
- Add the Vonage/Vonage.csproj file to your .sln file
- Add the Vonage/Vonage.csproj file as a project dependency of you project e.g.
<ItemGroup>
<ProjectReference Include="..\Vonage\Vonage.csproj" />
</ItemGroup>
Migration guides
Upgrading 5.x > 6.x
Changes in version 6.x
- Enum values are now caplitalised in alignment with accepted coding practices and are Pascal Case
- All classes that were marked as deprecated in 5.x are now removed
- Ncco now inherits from List, it no longer has the
Actions
property, to add an action usencco.Add(action);
- Strings with values "true" or "false" are now represented as
bool
in code
Targeted frameworks
The SDK targets towards netstandard2.0
.
It is compatible with every supported version.
Tested frameworks
We test the SDK against every supported version of the framework. Therefore, we ensure complete compatibility no matter the version you are using.
- .NET Framework 4.6.2
- .NET Framework 4.7.0
- .NET Framework 4.7.1
- .NET Framework 4.7.2
- .NET Framework 4.8.0
- .NET Framework 4.8.1
- .NET 6.0
- .NET 6.0-Windows
- .NET 7.0
- .NET 7.0-Windows
Configuration
Setup
There are various ways to initialize a VonageClient
with your custom values.
Overall, we encourage you to specify your configuration (Vonage URLs, credentials, etc.) in appsettings.json
, in an appsettings
section:
{
"appSettings": {
"Vonage.UserAgent": "myApp/1.0",
"Vonage.Url.Rest": "https://rest.nexmo.com",
"Vonage.Url.Api": "https://api.nexmo.com",
"Vonage.Url.Api.Europe": "https://api-eu.vonage.com",
"Vonage.Url.Api.Video": "https://video.api.vonage.com",
"Vonage_key": "VONAGE-API-KEY",
"Vonage_secret": "VONAGE-API-SECRET",
"Vonage.Application.Id": "ffffffff-ffff-ffff-ffff-ffffffffffff",
"Vonage.Application.Key": "VONAGE_APPLICATION_PRIVATE_KEY"
}
}
Note: While the section is currently names
appsettings
, we intend to use a more explicit name likevonageSettings
. Stay tuned for the next major release.
The configuration is automatically loaded in the Configuration
singleton.
Lazy registration (recommended for .NET Core and above)
Note: This implementation is not available for .NET Framework usages, given IConfiguration has been introduced in .NET Core.
You can register a client in your IServiceCollection
using the following extension methods:
AddVonageClientScoped
: registers using Scoped registration.AddVonageClientTransient
: registers using Transient registration.
// For 'Scoped' lifecycle
builder.Services.AddVonageClientScoped(builder.Configuration);
// Foor 'Transient' lifecycle
builder.Services.AddVonageClientTransient(builder.Configuration);
Note: Using
builder.Configuration
allow us to use settings you decided to load at runtime, including environment-specific settings.
var credentials = ...
// For 'Scoped' lifecycle
builder.Services.AddVonageClientScoped(credentials);
// Foor 'Transient' lifecycle
builder.Services.AddVonageClientTransient(credentials);
It will register the main VonageClient
, but also all sub client interfaces:
- IMessagesClient
- IVerifyV2Client
- IMeetingsClient
- IVoiceClient
- etc.
Finally, you can inject them in any of your components.
Manual initialization (recommended for .NET Framework)
Create a Vonage Client instance and pass in credentials in the constructor; this will only affect the security credentials (Api Key, Api Secret, Signing Secret, Signing Method Private Key, App Id).
var credentials = Credentials.FromApiKeyAndSecret(
VONAGE_API_KEY,
VONAGE_API_SECRET
);
var vonageClient = new VonageClient(credentials);
If required, you can override values directly in the Configuration
singleton:
Configuration.Instance.Settings["appSettings:Vonage.Url.Api"] = "https://www.example.com/api";
Configuration.Instance.Settings["appSettings:Vonage.Url.Rest"] = "https://www.example.com/rest";
Configuration.Instance.Settings["appSettings:Vonage.Url.Api.Europe"] = "https://www.meetings.example.com/api";
Configuration.Instance.Settings["appSettings:Vonage.Video.Url.Rest"] = "https://www.video.example.com/rest";
Note: Private Key is the literal key - not a path to the file containing the key
Note: Modifying the Configuration instance will be deprecated in the upcoming release, to keep the configuration immutable.
Configuration Reference
Key | Description |
---|---|
Vonage_key | Your API key from the dashboard |
Vonage_secret | Your API secret from the dashboard |
Vonage.Application.Id | Your application ID |
Vonage.Application.Key | Your application's private key |
Vonage.security_secret | Optional. This is the signing secret that's used for signing SMS |
Vonage.signing_method | Optional. This is the method used for signing SMS messages |
Vonage.Url.Rest | Optional. Vonage REST API base URL. Defaults to https://rest.nexmo.com |
Vonage.Url.Api | Optional. Vonage API base URL. Defaults to https://api.nexmo.com |
Vonage.Url.Api.Europe | Optional. Vonage API base URL for Meetings. Defaults to https://api-eu.vonage.com |
Vonage.Url.Api.Video | Optional. Vonage API base URL for Video. Defaults to https://video.api.vonage.com |
Vonage.RequestsPerSecond | Optional. Throttle to specified requests per second. |
Vonage.UserAgent | Optional. Your app-specific usage identifier in the format of name/version . Example: "myApp/1.0" |
Test configuration
Make sure to set Vonage.Test.RsaPrivateKey
(with a RSA Private Key) in your environment variables.
Some tests rely on that to verify a token can be created.
For security reasons, not RSA Private Key is hardcoded in the repository.
Logging
The Library uses Microsoft.Extensions.Logging to preform all of it's logging tasks. To configure logging for you app
simply create a new ILoggerFactory
and call the LogProvider.SetLogFactory()
method to tell the Vonage library how to
log. For example, to log to the console with serilog you can do the following:
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Configuration;
using Vonage.Logger;
using Serilog;
var log = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Console(outputTemplate: "{Timestamp:HH:mm} [{Level}]: {Message}\n")
.CreateLogger();
var factory = new LoggerFactory();
factory.AddSerilog(log);
LogProvider.SetLogFactory(factory);
Monads
Most recent features expose responses/results
using Monads.
While the name may be scary at first, this is just another data structure that offers various benefits.
We can picture them as a box
containing our value, and we manipulate the box instead of manipulating a value.
Their purpose is to provide a value that can have multiple states, and deal with each state using a similar workflow. This is a alternative to exception handling - see Railway Oriented Programming.
Result
The Result<T>
monad represents a value that can have one of the following states:
Success
- indicates the process was a successFailure
- indicates the process failed
The goal is to provide an exception-free
process.
- Transparency - the signature is fully transparent about the possible outputs (aka. a method can fail)
- Less intrusive - no
try-catch
required
// This method allows to Divide a number
private static Result<decimal> Divide(decimal value, decimal divider) =>
divider == 0
// If divider is 0, failure is returned
? Result<decimal>.FromFailure(ResultFailure.FromErrorMessage("Cannot divide by 0."))
// If divider is allowed, success is returned
: Result<decimal>.FromSuccess(value / divider);
Maybe
The Maybe<T>
monad represents a value that can have one of the following states:
Some
- indicates the presence of a valueNone
- indicates the absence of a value
The goal is to mitigate the 'billion-dollar mistake': null
.
// This method allows to retrieve phone number
private static Maybe<PhoneNumber> Find(string number) =>
this.numbers.FirstOrDefault(phoneNumber => phoneNumber.Number.Equals(number))
?? Maybe<PhoneNumber>.None;
How to extract a value from a Monad?
Given we cannot predict the state at build-time, we need to provide an operation for each scenario.
The following methods are all available for both Result<T>
and Maybe<T>
, but examples will focus on Result<T>
.
Match
.Match()
expects two operations, and will evaluate the one corresponding to the current state.
It supports transformation, and can be used with both Actions
and Functions
.
Result<int> monad = ...
// Supports transformation
string result = monad.Match(
// Will be executed if Result<int>.IsSuccess
some => $"The process is a success: {some}.",
// Will be executed if Result<int>.IsFailure
failure => $"The process is a failure: {failure.GetFailureMessage()}");
// Using Actions
monad.Match(
some => Console.WriteLine($"The process is a success: {some}."),
failure => Console.WriteLine($"The process is a failure: {failure.GetFailureMessage()}"));
IfFailure / IfNone
If you want to retrieve the value without transforming it, you can use .IfFailure()
which expects a fallback value in
case the state is Failure
.
Result<int> monad = ...
// Using the failure reason (recommended)
string result = monad.IfFailure(failure => $"The process is a failure: {failure.GetFailureMessage()}");
// Using a default value
string result = monad.IfFailure("Something failed.");
// Using an Action
monad.IfFailure(failure => Console.WriteLine($"The process is a failure: {failure.GetFailureMessage()}"));
Features
Both Result<T>
and Maybe<T>
also exposes more capabilities:
Map
: transforms the value (if success/some) that will wrap the result into a new Monad.Bind
: transforms the value (if success/some) that will return a new Monad.Merge
: merges (or flatten) two monads.IfSuccess
/IfSome
: provide an operation that will be executed if the Monad is in the expected state.IfFailure
/IfNone
: provide an operation that will be executed if the Monad is in the expected state.- Implicit operators
- Async support
- etc.
You can see how to benefit from these capabilities in our Tests.
What if you don't want to use Monads?
You can use GetUnsafe()
if you prefer having an exception thrown when a Monad is not in the desired state.
Result<int> result = ...
try
{
int output = result.GetSuccessUnsafe();
}
// The exception type cannot be defined at build-time
// It depends of the failure reason:
// - Authentication failure
// - Serialization/Deserialization failure
// - Http failure
// - Validation failure
// - etc.
catch (Exception exception)
{
...
}
Maybe<int> maybe = ...
try
{
int output = maybe.GetUnsafe();
}
catch (NoneStateException exception)
{
...
}
Examples
We are working on a separate repository for .NET examples. Check it out here!
The following examples show how to:
- Send a message
- Receive a message
- Receive a message delivery receipt
- Redact a message
- Initiate a call
- Receive a call
- Send 2FA code
- Check 2FA code
Sending a Message
Use Vonage's SMS API to send an SMS message.
var credentials = Credentials.FromApiKeyAndSecret(
VONAGE_API_KEY,
VONAGE_API_SECRET
);
var vonageClient = new VonageClient(credentials);
var response = await vonageClient.SmsClient.SendAnSmsAsync(new Vonage.Messaging.SendSmsRequest()
{
To = TO_NUMBER,
From = VONAGE_BRAND_NAME,
Text = "A text message sent using the Vonage SMS API"
});
Receiving a Message
Use Vonage's SMS API to receive an SMS message. Assumes your Vonage webhook endpoint is configured.
The best method for receiving an SMS will vary depending on whether you configure your webhooks to be GET or POST. Will Also Vary between ASP.NET MVC and ASP.NET MVC Core.
ASP.NET MVC Core
GET
[HttpGet("webhooks/inbound-sms")]
public async Task<IActionResult> InboundSmsGet()
{
var inbound = Vonage.Utility.WebhookParser.ParseQuery<InboundSms>(Request.Query);
return NoContent();
}
POST
[HttpPost("webhooks/inbound-sms")]
public async Task<IActionResult> InboundSms()
{
var inbound = await Vonage.Utility.WebhookParser.ParseWebhookAsync<InboundSms>(Request.Body, Request.ContentType);
return NoContent();
}
ASP.NET MVC
GET
[HttpGet]
[Route("webhooks/inbound-sms")]
public async Task<HttpResponseMessage> GetInbound()
{
var inboundSms = WebhookParser.ParseQueryNameValuePairs<InboundSms>(Request.GetQueryNameValuePairs());
return new HttpResponseMessage(HttpStatusCode.NoContent);
}
POST
[HttpPost]
[Route("webhooks/inbound-sms")]
public async Task<HttpResponseMessage> PostInbound()
{
var inboundSms = WebhookParser.ParseWebhook<InboundSms>(Request);
return new HttpResponseMessage(HttpStatusCode.NoContent);
}
Receiving a Message Delivery Receipt
Use Vonage's SMS API to receive an SMS delivery receipt. Assumes your Vonage webhook endpoint is configured.
The best method for receiving an SMS will vary depending on whether you configure your webhooks to be GET or POST. Will Also Vary between ASP.NET MVC and ASP.NET MVC Core.
ASP.NET MVC Core
GET
[HttpGet("webhooks/dlr")]
public async Task<IActionResult> InboundSmsGet()
{
var dlr = Vonage.Utility.WebhookParser.ParseQuery<DeliveryReceipt>(Request.Query);
return NoContent();
}
POST
[HttpPost("webhooks/dlr")]
public async Task<IActionResult> InboundSms()
{
var dlr = await Vonage.Utility.WebhookParser.ParseWebhookAsync<DeliveryReceipt>(Request.Body, Request.ContentType);
return NoContent();
}
ASP.NET MVC
GET
[HttpGet]
[Route("webhooks/dlr")]
public async Task<HttpResponseMessage> GetInbound()
{
var dlr = WebhookParser.ParseQueryNameValuePairs<DeliveryReceipt>(Request.GetQueryNameValuePairs());
return new HttpResponseMessage(HttpStatusCode.NoContent);
}
POST
[HttpPost]
[Route("webhooks/dlr")]
public async Task<HttpResponseMessage> PostInbound()
{
var dlr = WebhookParser.ParseWebhook<DeliveryReceipt>(Request);
return new HttpResponseMessage(HttpStatusCode.NoContent);
}
Redacting a message
Use Vonage's Redact API to redact a SMS message.
var credentials = Credentials.FromApiKeyAndSecret(VONAGE_API_KEY, VONAGE_API_SECRET);
var client = new VonageClient(credentials);
var request = new RedactRequest() { Id = VONAGE_REDACT_ID, Type = VONAGE_REDACT_TYPE, Product = VONAGE_REDACT_PRODUCT };
var response = await client.RedactClient.RedactAsync(request);
Initiating a Call
Use Vonage's Voice API to initiate a voice call.
NOTE: You must have a valid Application ID and private key in order to make voice calls. Use
either Vonage.Application
or Vonage's Node.js-based CLI tool to register.
See the Application API documentation for details.
var creds = Credentials.FromAppIdAndPrivateKeyPath(VONAGE_APPLICATION_ID, VONAGE_PRIVATE_KEY_PATH);
var client = new VonageClient(creds);
var command = new CallCommand() { To = new Endpoint[] { toEndpoint }, From = fromEndpoint, AnswerUrl=new[] { ANSWER_URL}};
var response = await client.VoiceClient.CreateCallAsync(command);
Receiving a Call
Use Vonage's Voice API to receive a voice call.
[HttpGet("webhooks/answer")]
public string Answer()
{
var talkAction = new TalkAction()
{
Text = $"Thank you for calling from " +
$"{string.Join(" ", Request.Query["from"].ToString().ToCharArray())}"
};
var ncco = new Ncco(talkAction);
return ncco.ToString();
}
Get Details About a Call
var credentials = Credentials.FromAppIdAndPrivateKeyPath(VONAGE_APPLICATION_ID, VONAGE_PRIVATE_KEY_PATH);
var client = new VonageClient(credentials);
var response = await client.VoiceClient.GetCallAsync(UUID);
Sending 2FA Code
Use Vonage's Verify API to send 2FA pin code.
var credentials = Credentials.FromApiKeyAndSecret(VONAGE_API_KEY, VONAGE_API_SECRET);
var client = new VonageClient(credentials);
var request = new VerifyRequest() { Brand = BRAND_NAME, Number = RECIPIENT_NUMBER };
var response = await client.VerifyClient.VerifyRequestAsync(request);
Checking 2FA Code
Use Vonage's Verify API to check 2FA pin code.
var credentials = Credentials.FromApiKeyAndSecret(VONAGE_API_KEY, VONAGE_API_SECRET);
var client = new VonageClient(credentials);
var request = new VerifyCheckRequest() { Code = CODE, RequestId = REQUEST_ID };
var response = await client.VerifyClient.VerifyCheckAsync(request);
Additional Examples
- Check out the sample MVC application and tests for more examples. Make sure to copy appsettings.json.example to appsettings.json and enter your key/secret.
Supported APIs
The following is a list of Vonage APIs and whether the Vonage .NET SDK provides support for them:
API | API Release Status | Supported? |
---|---|---|
Account API | General Availability | ✅ |
Alerts API | General Availability | ✅ |
Application API | General Availability | ✅ |
Audit API | Beta | ❌ |
Conversation API | Beta | ❌ |
Dispatch API | Beta | ❌ |
External Accounts API | Beta | ❌ |
Media API | Beta | ❌ |
Meetings API | General Availability | ✅ |
Messages API | General Availability | ✅ |
Number Insight API | General Availability | ✅ |
Number Management API | General Availability | ✅ |
Pricing API | General Availability | ✅ |
Proactive Connect API | General Availability | ✅ |
Redact API | Developer Preview | ✅ |
Reports API | Beta | ❌ |
SMS API | General Availability | ✅ |
Verify API | General Availability | ✅ |
Verify V2 API | General Availability | ✅ |
Video API | Beta | ✅ |
Voice API | General Availability | ✅ |
FAQ
Q: Does the .NET SDK Support the async pattern? A: Yes. All methods either support asynchronous behaviours by default or provide specific behaviours for each sync/async option.
Contributing
Pick your preferred IDE:
- Visual Studio (Community is fine)
- Visual Studio Code
- Jetbrains Rider
Keep in mind the SDK is built on netstandard2.0
and tested against several framework versions (v4.6.2 & upper, and
.Net6.0).
Therefore, they should be installed on your machine to guarantee compatibility with all supported framework versions.
- Get the latest code either by cloning the repository or downloading a snapshot of the source.
- Open "Vonage.sln"
- Build! NuGet dependencies should be brought down automatically; check your settings if they are not.
- Tests! Run all the tests to verify everything's fine.
Pull requests are welcome!
License
This library is released under the MIT License.