RepletoryLib.Communication.WhatsApp
1.0.0
dotnet add package RepletoryLib.Communication.WhatsApp --version 1.0.0
NuGet\Install-Package RepletoryLib.Communication.WhatsApp -Version 1.0.0
<PackageReference Include="RepletoryLib.Communication.WhatsApp" Version="1.0.0" />
<PackageVersion Include="RepletoryLib.Communication.WhatsApp" Version="1.0.0" />
<PackageReference Include="RepletoryLib.Communication.WhatsApp" />
paket add RepletoryLib.Communication.WhatsApp --version 1.0.0
#r "nuget: RepletoryLib.Communication.WhatsApp, 1.0.0"
#:package RepletoryLib.Communication.WhatsApp@1.0.0
#addin nuget:?package=RepletoryLib.Communication.WhatsApp&version=1.0.0
#tool nuget:?package=RepletoryLib.Communication.WhatsApp&version=1.0.0
RepletoryLib.Communication.WhatsApp
360Dialog WhatsApp Business API implementation with support for text, template, and media messages.
Part of the RepletoryLib ecosystem -- standalone, reusable .NET 10 libraries with zero business logic.
Overview
RepletoryLib.Communication.WhatsApp provides a 360Dialog WhatsApp Business API implementation of the IWhatsAppService interface from RepletoryLib.Communication.Abstractions. It sends messages via the 360Dialog REST API using a named HttpClient configured with the D-SECRET-KEY authentication header.
The service supports three message types: free-form text messages, pre-approved template messages (with body parameter substitution), and media messages (image, video, audio, document) with optional captions. It uses snake_case JSON serialization for the 360Dialog API and extracts message IDs from the messages[0].id response path.
Key Features
- Text messages -- Send free-form text messages to WhatsApp users
- Template messages -- Send pre-approved WhatsApp Business templates with body parameter substitution
- Media messages -- Send images, videos, audio files, and documents with optional captions
- Bulk send -- Send multiple text messages with per-message success/failure tracking
- Named HttpClient -- Pre-configured with base URL and
D-SECRET-KEYheader viaIHttpClientFactory - Error extraction -- Parses 360Dialog error responses for actionable error messages
- Unified routing -- Automatically registers
ICommunicationServicefor cross-channel messaging
Installation
dotnet add package RepletoryLib.Communication.WhatsApp
Or add to your .csproj:
<PackageReference Include="RepletoryLib.Communication.WhatsApp" Version="1.0.0" />
Note: RepletoryLib packages are published to a local BaGet feed. See the main repository README for feed configuration.
Dependencies
| Package | Type |
|---|---|
RepletoryLib.Communication.Abstractions |
RepletoryLib |
Microsoft.Extensions.Http |
NuGet (10.0.0) |
Microsoft.Extensions.Configuration.Binder |
NuGet (10.0.0) |
Microsoft.Extensions.DependencyInjection.Abstractions |
NuGet (10.0.0) |
Microsoft.Extensions.Logging.Abstractions |
NuGet (10.0.0) |
Microsoft.Extensions.Options.ConfigurationExtensions |
NuGet (10.0.0) |
Prerequisites
- 360Dialog WhatsApp Business API account
- API key from the 360Dialog dashboard
- WhatsApp Business-approved templates (for template messages)
- Phone numbers must be in E.164 format (e.g.,
+27821234567)
Quick Start
using RepletoryLib.Communication.WhatsApp;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRepletoryWhatsApp(builder.Configuration);
{
"Communication": {
"WhatsApp": {
"ApiKey": "your-360dialog-api-key",
"ApiBaseUrl": "https://waba.360dialog.io/v1",
"PhoneNumberId": "optional-phone-number-id"
}
}
}
Configuration
WhatsAppOptions
| Property | Type | Default | Description |
|---|---|---|---|
ApiKey |
string |
"" |
360Dialog API key sent via the D-SECRET-KEY header |
ApiBaseUrl |
string |
"https://waba.360dialog.io/v1" |
Base URL for the 360Dialog WhatsApp Business API |
PhoneNumberId |
string? |
null |
Optional phone number ID associated with the WhatsApp Business account |
Section name: "Communication:WhatsApp"
Usage Examples
Register Services
using RepletoryLib.Communication.WhatsApp;
var builder = WebApplication.CreateBuilder(args);
// Register the 360Dialog WhatsApp service
builder.Services.AddRepletoryWhatsApp(builder.Configuration);
var app = builder.Build();
Send a Text Message
using RepletoryLib.Communication.Abstractions.Interfaces;
using RepletoryLib.Communication.Abstractions.Models.WhatsApp;
public class CustomerSupportService
{
private readonly IWhatsAppService _whatsAppService;
public CustomerSupportService(IWhatsAppService whatsAppService) => _whatsAppService = whatsAppService;
public async Task SendSupportReplyAsync(string phoneNumber, string replyText)
{
var result = await _whatsAppService.SendTextAsync(new WhatsAppTextMessage
{
To = phoneNumber, // E.164 format: "+27821234567"
Body = replyText
});
if (!result.Success)
throw new InvalidOperationException($"WhatsApp message failed: {result.Error}");
Console.WriteLine($"Message sent. MessageId: {result.MessageId}");
}
}
Send a Template Message
using RepletoryLib.Communication.Abstractions.Models.WhatsApp;
// Template without parameters
var result = await _whatsAppService.SendTemplateAsync(new WhatsAppTemplateMessage
{
To = "+27821234567",
TemplateName = "hello_world",
LanguageCode = "en"
});
// Template with body parameters
var result2 = await _whatsAppService.SendTemplateAsync(new WhatsAppTemplateMessage
{
To = "+27821234567",
TemplateName = "order_confirmation",
LanguageCode = "en",
Parameters = ["John Doe", "ORD-2024-001", "R 299.99"]
});
Send a Media Message
using RepletoryLib.Communication.Abstractions.Enums;
using RepletoryLib.Communication.Abstractions.Models.WhatsApp;
// Send an image
var imageResult = await _whatsAppService.SendMediaAsync(new WhatsAppMediaMessage
{
To = "+27821234567",
MediaType = WhatsAppMediaType.Image,
MediaUrl = "https://cdn.example.com/products/widget.jpg",
Caption = "Check out our new Widget!"
});
// Send a document
var docResult = await _whatsAppService.SendMediaAsync(new WhatsAppMediaMessage
{
To = "+27821234567",
MediaType = WhatsAppMediaType.Document,
MediaUrl = "https://cdn.example.com/invoices/inv-001.pdf",
Caption = "Invoice #001"
});
// Send a video
var videoResult = await _whatsAppService.SendMediaAsync(new WhatsAppMediaMessage
{
To = "+27821234567",
MediaType = WhatsAppMediaType.Video,
MediaUrl = "https://cdn.example.com/tutorials/setup.mp4",
Caption = "Setup tutorial"
});
// Send audio (no caption support)
var audioResult = await _whatsAppService.SendMediaAsync(new WhatsAppMediaMessage
{
To = "+27821234567",
MediaType = WhatsAppMediaType.Audio,
MediaUrl = "https://cdn.example.com/audio/welcome.mp3"
});
Bulk Send
var messages = phoneNumbers.Select(phone => new WhatsAppTextMessage
{
To = phone,
Body = "Reminder: Your appointment is tomorrow at 10:00 AM."
});
var bulkResult = await _whatsAppService.SendBulkAsync(messages);
Console.WriteLine($"Sent: {bulkResult.SuccessCount}, Failed: {bulkResult.FailureCount}");
foreach (var failure in bulkResult.Results.Where(r => !r.Success))
{
Console.WriteLine($"Failed: {failure.Error}");
}
Using the Unified Communication Facade
using RepletoryLib.Communication.Abstractions.Enums;
using RepletoryLib.Communication.Abstractions.Interfaces;
using RepletoryLib.Communication.Abstractions.Models.Common;
public class NotificationService
{
private readonly ICommunicationService _communication;
public NotificationService(ICommunicationService communication) => _communication = communication;
public async Task SendWhatsAppNotificationAsync(string phoneNumber, string message)
{
var result = await _communication.SendAsync(new CommunicationMessage
{
Channel = CommunicationChannel.WhatsApp,
Recipient = phoneNumber,
Body = message
});
}
}
Multi-Channel Registration
using RepletoryLib.Communication.Email;
using RepletoryLib.Communication.Sms;
using RepletoryLib.Communication.WhatsApp;
var builder = WebApplication.CreateBuilder(args);
// Register email, SMS, and WhatsApp -- ICommunicationService routes to all three
builder.Services.AddRepletoryEmail(builder.Configuration);
builder.Services.AddRepletrySms(builder.Configuration);
builder.Services.AddRepletoryWhatsApp(builder.Configuration);
API Reference
ServiceCollectionExtensions
| Method | Returns | Description |
|---|---|---|
AddRepletoryWhatsApp(configuration) |
IServiceCollection |
Registers a named HttpClient, IWhatsAppService (scoped), and ICommunicationService (scoped) |
WhatsAppService (implements IWhatsAppService)
| Method | Returns | Description |
|---|---|---|
SendTextAsync(WhatsAppTextMessage, ct) |
Task<SendResult> |
Sends a text message via the 360Dialog API |
SendTemplateAsync(WhatsAppTemplateMessage, ct) |
Task<SendResult> |
Sends a template message with optional body parameters |
SendMediaAsync(WhatsAppMediaMessage, ct) |
Task<SendResult> |
Sends a media message (image, video, audio, document) |
SendBulkAsync(IEnumerable<WhatsAppTextMessage>, ct) |
Task<BulkSendResult> |
Sends multiple text messages with per-message results |
WhatsAppOptions
| Property | Type | Default | Description |
|---|---|---|---|
ApiKey |
string |
"" |
360Dialog API key |
ApiBaseUrl |
string |
"https://waba.360dialog.io/v1" |
API base URL |
PhoneNumberId |
string? |
null |
Optional phone number ID |
HTTP Client Configuration
The named HttpClient ("RepletoryWhatsApp") is pre-configured with:
| Setting | Value |
|---|---|
| Base address | Value of WhatsAppOptions.ApiBaseUrl |
D-SECRET-KEY header |
Value of WhatsAppOptions.ApiKey |
| JSON serialization | snake_case naming, null values omitted |
Integration with Other RepletoryLib Packages
| Package | Relationship |
|---|---|
RepletoryLib.Communication.Abstractions |
Direct dependency -- provides IWhatsAppService, message models, SendResult |
RepletoryLib.Communication.Email |
Combine with email for multi-channel notifications |
RepletoryLib.Communication.Sms |
Combine with SMS for multi-channel notifications |
RepletoryLib.Communication.Push |
Combine with push for multi-channel notifications |
RepletoryLib.Common |
Transitive dependency via Abstractions |
Testing
using Moq;
using Moq.Protected;
using System.Net;
using System.Net.Http.Json;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using RepletoryLib.Communication.Abstractions.Enums;
using RepletoryLib.Communication.Abstractions.Models.WhatsApp;
using RepletoryLib.Communication.WhatsApp.Options;
using RepletoryLib.Communication.WhatsApp.Services;
[Fact]
public async Task SendTextAsync_returns_success_with_message_id()
{
var responseJson = """{"messages": [{"id": "wamid.abc123"}]}""";
var handler = new Mock<HttpMessageHandler>();
handler
.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(responseJson, System.Text.Encoding.UTF8, "application/json")
});
var httpClient = new HttpClient(handler.Object) { BaseAddress = new Uri("https://waba.360dialog.io/v1") };
var mockFactory = new Mock<IHttpClientFactory>();
mockFactory.Setup(f => f.CreateClient("RepletoryWhatsApp")).Returns(httpClient);
var options = Options.Create(new WhatsAppOptions
{
ApiKey = "test-key",
ApiBaseUrl = "https://waba.360dialog.io/v1"
});
var service = new WhatsAppService(mockFactory.Object, options, NullLogger<WhatsAppService>.Instance);
var result = await service.SendTextAsync(new WhatsAppTextMessage
{
To = "+27821234567",
Body = "Hello from WhatsApp!"
});
result.Success.Should().BeTrue();
result.MessageId.Should().Be("wamid.abc123");
result.Channel.Should().Be(CommunicationChannel.WhatsApp);
}
Troubleshooting
| Issue | Solution |
|---|---|
HttpRequestException: 360Dialog API returned 401 |
Verify the ApiKey in appsettings.json matches your 360Dialog dashboard API key |
HttpRequestException: 360Dialog API returned 400 |
Check the phone number is in E.164 format and the message body is not empty |
| Template message rejected | Ensure the template name matches exactly as registered in WhatsApp Business Manager and the LanguageCode is correct |
| Media message failed | Ensure the MediaUrl is publicly accessible and the file format matches the MediaType |
SendResult.Error says "WhatsApp service is not registered" |
Ensure AddRepletoryWhatsApp(configuration) is called in Program.cs |
Message ID is null |
The 360Dialog API response may not contain messages[0].id -- check the raw response in debug logs |
License
This project is licensed under the MIT License.
Copyright (c) 2024-2026 Repletory.
For complete documentation, infrastructure setup, and configuration reference, see the RepletoryLib main repository.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. 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. |
-
net10.0
- Microsoft.Extensions.Configuration.Binder (>= 10.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Http (>= 10.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 10.0.0)
- RepletoryLib.Communication.Abstractions (>= 1.0.0)
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 |
|---|---|---|
| 1.0.0 | 75 | 3/2/2026 |