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
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="RepletoryLib.Communication.WhatsApp" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="RepletoryLib.Communication.WhatsApp" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="RepletoryLib.Communication.WhatsApp" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add RepletoryLib.Communication.WhatsApp --version 1.0.0
                    
#r "nuget: RepletoryLib.Communication.WhatsApp, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package RepletoryLib.Communication.WhatsApp@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=RepletoryLib.Communication.WhatsApp&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=RepletoryLib.Communication.WhatsApp&version=1.0.0
                    
Install as a Cake Tool

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.

NuGet .NET 10 License: MIT


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-KEY header via IHttpClientFactory
  • Error extraction -- Parses 360Dialog error responses for actionable error messages
  • Unified routing -- Automatically registers ICommunicationService for 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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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