Install
Add the package from NuGet. Targets net8.0 and netstandard2.0.
dotnet add package XpressNotification.SdkRegister with DI (recommended)
builder.Services.AddXpressNotification(options =>
{
options.ApiKey = builder.Configuration["XpressNotification:ApiKey"];
options.BaseUrl = "https://your-api.com";
});Then inject and use it anywhere:
public class WelcomeService(IXpressNotificationClient client)
{
public Task SendWelcomeAsync(string to) =>
client.Email.SendAsync(new EmailSendRequest
{
To = to,
Subject = "Welcome!",
Html = "<p>Hello there</p>"
});
}var client = new XpressNotificationClient("xpn_your_key");var result = await client.Email.SendAsync(new EmailSendRequest
{
To = "user@example.com",
Subject = "Welcome!",
Html = "<p>Hello</p>",
From = "no-reply@yourapp.com" // optional
});
// result.Id, result.StatusValidate email addresses
Check deliverability before you send — syntax, MX records, and disposable domains. Great for cleaning a contact import.
var check = await client.Email.ValidateAsync("user@example.com");
// check.IsValid, check.Reason ("valid" | "invalid_syntax" | "no_mx_record" | "disposable_domain")
var results = await client.Email.ValidateBulkAsync(new[]
{
"a@example.com", "typo@gmial.com", "spam@mailinator.com"
});
var deliverable = results.Where(r => r.IsValid);SMS
await client.Sms.SendAsync(new SmsSendRequest
{
To = "+233200000000",
Body = "Your OTP is 1234"
});Send with a saved template
Create reusable content on the Templates page using {{variable}} placeholders, then send it by ID instead of inlining the body. The server renders the template — and the subject — with the values you pass, so the delivered message matches the dashboard preview. Available for email and SMS.
// Email — template content becomes the body; {{variables}} also fill the subject
await client.Email.SendAsync(new EmailSendRequest
{
To = "user@example.com",
Subject = "Hi {{name}}, your order shipped",
TemplateId = Guid.Parse("2b1e…"), // Templates page → "Copy ID" on the row
Variables = new()
{
["name"] = "Ada",
["orderId"] = "A-1042"
}
});
// SMS — template content becomes the message text
await client.Sms.SendAsync(new SmsSendRequest
{
To = "+233200000000",
TemplateId = Guid.Parse("7c9d…"),
Variables = new() { ["code"] = "123456" }
});TemplateId or a literal Html/Body. An unknown or empty variable is left as its {{placeholder}}, and a template whose channel doesn't match the send returns a 400.Push
Send to a single device token, or fan out to all of a contact's registered devices. iOS tokens route to APNs, everything else to FCM.
// To one device token
await client.Push.SendAsync(new PushSendRequest
{
To = "device-token",
Title = "Hi",
Body = "You have a new message",
Data = new() { ["orderId"] = "42" } // optional
});
// Or fan out to a contact's devices
var result = await client.Push.SendAsync(new PushSendRequest
{
ContactId = contactId,
Title = "Hi",
Body = "Broadcast to all your devices"
});
// result.Ids (one per device)Send a free-text message inside the 24-hour window, or a pre-approved template message outside it.
// Text (session) message
await client.WhatsApp.SendAsync(new WhatsAppSendRequest
{
To = "+233200000000",
Body = "Thanks for your order!"
});
// Template message
await client.WhatsApp.SendAsync(new WhatsAppSendRequest
{
To = "+233200000000",
TemplateName = "order_confirmation",
LanguageCode = "en_US",
Variables = new() { "Ama", "#1042" }
});Providers & failover
Email sends through Amazon SES by default. Add your own SMTP servers under Settings → Providers → Email and pick a per-account routing mode (e.g. SES → SMTP with automatic failover). Most apps never set a provider — it just works. To force a single message onto one provider, set Provider (an explicit provider is used as-is, with no failover):
await client.Email.SendAsync(new EmailSendRequest
{
To = "user@example.com",
Subject = "Sent via our own mail server",
Html = "<p>Hello</p>",
From = "no-reply@yourdomain.com",
Provider = "smtp" // or "ses"; omit to use the account's routing/failover
});Error handling
Every call throws a single typed exception on a non-success response, with the status and error code attached.
try
{
await client.Sms.SendAsync(request);
}
catch (XpressNotificationApiException ex)
{
// ex.StatusCode, ex.ErrorCode, ex.Message
if (ex.ErrorCode == "usage_limit_exceeded") { /* upgrade plan */ }
}