Skip to main content
Version: 1.0

C#/.NET

VerifySpeed C#/.NET Client Library Documentation

The VerifySpeed .NET client library lets you manage verifications from your backend. It supports message-based methods (WhatsApp Message, Telegram Message), OTP methods (WhatsApp OTP, Telegram OTP, SMS OTP), OTP validation, local token decryption, and API token verification.

note

Please read Verification Flow and Verification Methods to understand the verification process.

Setup

Step 1: Install the Package

dotnet add package VerifySpeed.VSCSharp

Step 2: Service Registration

Register VerifySpeed in Program.cs or Startup.cs with your server key from the dashboard.

using VSCSharp;

WebApplicationBuilder builder = WebApplication.CreateBuilder(args);

builder.Services.AddVerifySpeed("YOUR_SERVER_KEY");

WebApplication app = builder.Build();
app.Run();

Inject IVerifySpeedClient where you need it.

Usage

1. Initializing the Client

Retrieve available verification methods before creating a verification.

using System;
using System.Threading.Tasks;
using VSCSharp.Clients;
using VSCSharp.Exceptions;
using VSCSharp.Models;

public class VerifySpeedExample
{
private readonly IVerifySpeedClient verifySpeedClient;

public VerifySpeedExample(IVerifySpeedClient verifySpeedClient)
{
this.verifySpeedClient = verifySpeedClient;
}

public async Task InitializeVerificationAsync()
{
try
{
InitializeResponse initialization = await verifySpeedClient.InitializeAsync();

Console.WriteLine("Available Verification Methods:");
foreach (Method availableMethod in initialization.AvailableMethods)
{
Console.WriteLine($"- {availableMethod.DisplayName} ({availableMethod.MethodName})");
}

// Return available methods to your client app
}
catch (FailedInitializationException exception)
{
Console.WriteLine($"Initialization failed: {exception.Message}");
}
}
}

2. Creating a Verification

CreateVerificationAsync parameters

ParameterRequiredDescription
methodNameYesVerification method identifier (e.g., telegram-message, whatsapp-otp).
phoneNumberFor OTP methodsUser's phone number in E.164 format. Required when methodName is an OTP method (whatsapp-otp, telegram-otp, sms-otp). Omit for message-based methods.
languageNoLanguage for the verification flow (e.g., en, ar, ckb). Defaults to en.
Task<CreatedVerificationResponse> CreateVerificationAsync(
string methodName,
string? phoneNumber = null,
string? language = null
);

Message-based method

For message-based methods (whatsapp-message, telegram-message), omit phoneNumber. The response includes a DeepLink for the client app.

CreatedVerificationResponse createdVerification = await verifySpeedClient.CreateVerificationAsync(
methodName: "telegram-message",
language: "ar"
);

Console.WriteLine($"Verification Key: {createdVerification.VerificationKey}");
Console.WriteLine($"Deep Link: {createdVerification.DeepLink}");

OTP-based method

For OTP methods, pass phoneNumber. VerifySpeed sends the OTP to that number. Your app collects the code and either validates via the client SDK or sends it to your backend for ValidateOtpAsync (see below). See Validating OTP for both approaches.

CreatedVerificationResponse createdVerification = await verifySpeedClient.CreateVerificationAsync(
methodName: "whatsapp-otp",
phoneNumber: "+14255552673",
language: "en"
);

Console.WriteLine($"Verification Key: {createdVerification.VerificationKey}");
// DeepLink is null for OTP methods — return VerificationKey to your client app

3. Validating OTP

Call ValidateOtpAsync from your backend when the user's app sends the OTP code to your server (instead of validating via the Android, iOS, Flutter, or Web client SDK). See Validating OTP.

ValidateOtpAsync parameters

ParameterRequiredDescription
codeYesOTP code entered by the user (max 5 characters).
verificationKeyYesVerificationKey from CreateVerificationAsync for the same OTP session.
/// <summary>
/// Validates an OTP code for a verification session created with an OTP method.
/// </summary>
/// <returns>Check <see cref="ValidateOtpResponse.Succeed"/>; business failures return HTTP 200 with succeed: false.</returns>
/// <exception cref="FailedValidateOtpException">Thrown when the validate-otp HTTP request fails.</exception>
Task<ValidateOtpResponse> ValidateOtpAsync(string code, string verificationKey);

Example

using VSCSharp.Clients;
using VSCSharp.Exceptions;
using VSCSharp.Models;

public async Task ValidateOtpAsync(string code, string verificationKey)
{
try
{
ValidateOtpResponse result = await verifySpeedClient.ValidateOtpAsync(code, verificationKey);

if (result.Succeed)
{
Console.WriteLine($"Phone: {result.PhoneNumber}");
Console.WriteLine($"Token: {result.Token}");

// Optional: decrypt token for methodName, dateOfVerification, verificationKey
DecryptTokenResult decrypted = verifySpeedClient.DecryptToken(result.Token!);
Console.WriteLine($"Method: {decrypted.MethodName}");
Console.WriteLine($"Verified at: {decrypted.DateOfVerification}");
}
else
{
Console.WriteLine($"Error: {result.ErrorMessage} ({result.ErrorCode})");
}
}
catch (FailedValidateOtpException exception)
{
Console.WriteLine($"Request failed: {exception.Message}");
}
}

When Succeed is false, check ErrorCode and ErrorMessage. Known API error codes:

ErrorCodeDescription
OTP_EXPIREDThe OTP session has expired.
OTP_INVALIDThe provided code does not match.
OTP_ALREADY_VERIFIEDThis verification was already completed.

4. Decrypting Verification Tokens (Local Method)

Recommended approach: Call DecryptToken on your server using the server key configured at registration. No extra VerifySpeed API call. Tokens expire after 5 minutes.

/// <summary>
/// Decrypts the verification token and returns the result.
/// </summary>
DecryptTokenResult DecryptToken(string token);
public void DecryptVerificationToken(string token)
{
try
{
DecryptTokenResult result = verifySpeedClient.DecryptToken(token);

Console.WriteLine($"Phone: {result.PhoneNumber}");
Console.WriteLine($"Method: {result.MethodName}");
Console.WriteLine($"Verification Key: {result.VerificationKey}");
Console.WriteLine($"Verified at: {result.DateOfVerification}");
}
catch (Exception exception)
{
Console.WriteLine($"Token invalid or expired: {exception.Message}");
}
}

Use tokens from ValidateOtpAsync, client SDKs, or message-based verification flows. On ValidateOtpAsync success, PhoneNumber is already in the response; use DecryptToken when you also need MethodName, DateOfVerification, or VerificationKey. See Verification Result Documentation.

5. Verifying a Token via API (Enhanced Security)

The API method returns firstTimeVerified to help detect token reuse—something local decryption alone cannot provide.

note

Security benefit: VerifyTokenAsync includes FirstTimeVerified, which indicates whether VerifySpeed has seen this token before.

/// <summary>
/// Verifies a verification token via the VerifySpeed API.
/// </summary>
Task<VerifyTokenResponse> VerifyTokenAsync(string token);
public async Task VerifyTokenViaApiAsync(string token)
{
try
{
VerifyTokenResponse result = await verifySpeedClient.VerifyTokenAsync(token);

Console.WriteLine($"Phone: {result.PhoneNumber}");
Console.WriteLine($"Method: {result.MethodName}");
Console.WriteLine($"First time verified: {result.FirstTimeVerified}");

if (result.FirstTimeVerified)
{
// Proceed with business logic
}
else
{
// Handle potential token reuse
}
}
catch (FailedVerifyingTokenException exception)
{
Console.WriteLine($"Token verification failed: {exception.Message}");
}
}

Models

InitializeResponse

Returned by InitializeAsync(). Contains available verification methods.

public record InitializeResponse
{
public List<Method> AvailableMethods { get; init; } = new();
}

public record Method
{
public string MethodName { get; init; } = null!;
public string DisplayName { get; init; } = null!;
}

CreatedVerificationResponse

Returned by CreateVerificationAsync().

public record CreatedVerificationResponse
{
public string MethodName { get; init; } = null!;
public string VerificationKey { get; init; } = null!;
public string? DeepLink { get; init; }
}
  • DeepLink is set for message-based methods; null for OTP methods.

ValidateOtpResponse

Returned by ValidateOtpAsync().

public record ValidateOtpResponse
{
public bool Succeed { get; init; }
public string? Token { get; init; }
public string? PhoneNumber { get; init; }
public string? ErrorMessage { get; init; }
public string? ErrorCode { get; init; }
}

DecryptTokenResult

Returned by DecryptToken().

public record DecryptTokenResult
{
public string PhoneNumber { get; init; } = null!;
public DateTime DateOfVerification { get; init; }
public string MethodName { get; init; } = null!;
public string VerificationKey { get; init; } = null!;
}

VerifyTokenResponse

Returned by VerifyTokenAsync().

public record VerifyTokenResponse
{
public string MethodName { get; init; } = null!;
public DateTime DateOfVerification { get; init; }
public string PhoneNumber { get; init; } = null!;
public string VerificationKey { get; init; } = null!;
public bool FirstTimeVerified { get; init; }
}

Exceptions

ExceptionWhen thrown
FailedInitializationExceptionInitialization request fails.
FailedCreateVerificationExceptionVerification creation fails, or phoneNumber is omitted for an OTP method.
FailedValidateOtpExceptionThe validate-otp HTTP request fails (network, auth, non-2xx). Business failures such as invalid OTP return HTTP 200 with Succeed: false instead.
FailedVerifyingTokenExceptionToken verification via API fails.

API Example (ASP.NET Core)

using Microsoft.AspNetCore.Mvc;
using System.ComponentModel.DataAnnotations;
using VSCSharp.Clients;
using VSCSharp.Exceptions;
using VSCSharp.Models;

[ApiController]
[Route("api/[controller]")]
public class VerificationController : ControllerBase
{
private readonly IVerifySpeedClient verifySpeedClient;

public VerificationController(IVerifySpeedClient verifySpeedClient)
{
this.verifySpeedClient = verifySpeedClient;
}

[HttpPost("initialize")]
public async Task<IActionResult> PostVerificationInitialization()
{
try
{
InitializeResponse initialization = await verifySpeedClient.InitializeAsync();

return Ok(initialization);
}
catch (Exception exception)
{
return BadRequest(exception.Message);
}
}

[HttpPost("create")]
public async Task<IActionResult> PostVerificationCreation([FromBody] PostVerificationCreationRequest request)
{
try
{
CreatedVerificationResponse verification = await verifySpeedClient.CreateVerificationAsync(
methodName: request.MethodType,
phoneNumber: request.PhoneNumber,
language: request.Language
);

return Ok(verification);
}
catch (Exception exception)
{
return BadRequest(exception.Message);
}
}

[HttpPost("validate-otp")]
public async Task<IActionResult> PostValidateOtp([FromBody] PostValidateOtpRequest request)
{
try
{
ValidateOtpResponse result = await verifySpeedClient.ValidateOtpAsync(
request.Code,
request.VerificationKey
);

if (!result.Succeed)
{
return UnprocessableEntity(result);
}

return Ok(result);
}
catch (FailedValidateOtpException exception)
{
return BadRequest(exception.Message);
}
}

[HttpGet("verify/{token}")]
public async Task<IActionResult> GetVerificationResult(string token)
{
try
{
// Option 1: Local decrypt (faster, no reuse detection)
DecryptTokenResult result = verifySpeedClient.DecryptToken(token);

// Option 2: API verification (includes FirstTimeVerified)
// VerifyTokenResponse result = await verifySpeedClient.VerifyTokenAsync(token);

return Ok(result);
}
catch (Exception exception)
{
return BadRequest(exception.Message);
}
}
}

public class PostVerificationCreationRequest
{
[Required]
public string MethodType { get; set; } = string.Empty;

/// <summary>
/// Required for OTP methods (whatsapp-otp, telegram-otp, sms-otp). E.164 format.
/// </summary>
public string? PhoneNumber { get; set; }

public string? Language { get; set; }
}

public class PostValidateOtpRequest
{
[Required]
[MaxLength(5)]
public string Code { get; set; } = string.Empty;

[Required]
public string VerificationKey { get; set; } = string.Empty;
}

License

This package is distributed under the MIT License.