FluentWhatsApp 2.0.0
dotnet add package FluentWhatsApp --version 2.0.0
NuGet\Install-Package FluentWhatsApp -Version 2.0.0
<PackageReference Include="FluentWhatsApp" Version="2.0.0" />
<PackageVersion Include="FluentWhatsApp" Version="2.0.0" />
<PackageReference Include="FluentWhatsApp" />
paket add FluentWhatsApp --version 2.0.0
#r "nuget: FluentWhatsApp, 2.0.0"
#:package FluentWhatsApp@2.0.0
#addin nuget:?package=FluentWhatsApp&version=2.0.0
#tool nuget:?package=FluentWhatsApp&version=2.0.0
FluentWhatsApp
A modern, fluent dotnet SDK for sending WhatsApp messages through
the WhatsApp Cloud API.
Built as a lightweight class library using System.Text.Json for serialization β with no external JSON dependencies.
Installation
dotnet add package FluentWhatsApp
Sending messages
Every message is built using the fluent WhatsAppMessage API and sent via IWhatsAppClient.
// Build a request
var request = WhatsAppMessage.To("+1234567890").Text("Hello!").Build();
// Send it
SendMessageResponse response = await client.SendAsync(request);
Console.WriteLine(response.FirstMessageId); // wamid.xxx
Using the interface helpers
await client.SendToIndividualAsync("+1234567890", b =>
b.Text("Hello!").Build());
await client.SendToGroupAsync("GROUP_ID", b =>
b.Text("Hello group!").Build());
Setup
Register the client in your DI container with your phone number ID and access token:
builder.Services.AddWhatsAppClient(options =>
{
options.PhoneNumberId = "YOUR_PHONE_NUMBER_ID";
options.AccessToken = "YOUR_ACCESS_TOKEN";
// options.ApiVersion = "v22.0"; // default
});
Then inject IWhatsAppClient wherever you need it:
public class NotificationService(IWhatsAppClient whatsApp)
{
public Task NotifyAsync(string phone) =>
whatsApp.SendToIndividualAsync(phone, b => b.Text("Hello from FluentWhatsApp!").Build());
}
Resilience
The library registers a standard resilience pipeline (retry, circuit-breaker, timeouts) via
Microsoft.Extensions.Http.Resilience. Customise it through the optional second parameter:
builder.Services.AddWhatsAppClient(
options =>
{
options.PhoneNumberId = "...";
options.AccessToken = "...";
},
resilience =>
{
resilience.Retry.MaxRetryAttempts = 5;
});
Custom client
Bring your own IWhatsAppClient implementation (e.g., for testing):
builder.Services.AddWhatsAppClient<MyCustomClient>();
Message types
Text
var request = WhatsAppMessage
.To("+1234567890")
.Text("Hello, World!")
.Build();
With URL preview:
var request = WhatsAppMessage
.To("+1234567890")
.Text("Check this out: https://example.com", previewUrl: true)
.Build();
Image
// By URL
var request = WhatsAppMessage
.To("+1234567890")
.Image(link: "https://example.com/image.jpg", caption: "Look at this!")
.Build();
// By media ID (uploaded via Media API)
var request = WhatsAppMessage
.To("+1234567890")
.Image(mediaId: "MEDIA_ID")
.Build();
Audio
var request = WhatsAppMessage
.To("+1234567890")
.Audio(link: "https://example.com/audio.mp3")
.Build();
Video
var request = WhatsAppMessage
.To("+1234567890")
.Video(link: "https://example.com/video.mp4", caption: "Watch this")
.Build();
Document
var request = WhatsAppMessage
.To("+1234567890")
.Document(link: "https://example.com/file.pdf", caption: "Your invoice")
.WithFilename("invoice.pdf")
.Build();
Sticker
var request = WhatsAppMessage
.To("+1234567890")
.Sticker(link: "https://example.com/sticker.webp")
.Build();
Location
var request = WhatsAppMessage
.To("+1234567890")
.Location(37.7749, -122.4194)
.WithName("San Francisco")
.WithAddress("San Francisco, CA, USA")
.Build();
Reaction
// Returns a MessageRequest directly β no .Build() needed
var request = WhatsAppMessage
.To("+1234567890")
.Reaction("wamid.MESSAGE_ID", "π");
await client.SendAsync(request);
Contacts
var request = WhatsAppMessage
.To("+1234567890")
.Contacts()
.Add(c => c
.Name("John Doe", firstName: "John", lastName: "Doe")
.Phone("+19998887777")
.Email("john@example.com"))
.Add(c => c
.Name("Jane Doe", firstName: "Jane")
.Phone("+19997776666", type: "WORK"))
.Build();
Interactive β Reply buttons
Up to 3 buttons.
var request = WhatsAppMessage
.To("+1234567890")
.Interactive("Would you like to proceed?")
.WithFooter("Tap a button to respond")
.ReplyButtons()
.Add("yes", "Yes, continue")
.Add("no", "No, cancel")
.Build();
Interactive β List
var request = WhatsAppMessage
.To("+1234567890")
.Interactive("Choose a subscription plan")
.WithHeader("Our Plans")
.WithFooter("Prices are monthly")
.List("View Plans")
.Section("Monthly")
.Row("m_basic", "Basic", "$9/mo")
.Row("m_pro", "Pro", "$19/mo")
.Row("m_ultra", "Ultra", "$49/mo")
.Section("Annual")
.Row("a_basic", "Basic Annual", "$90/yr")
.Row("a_pro", "Pro Annual", "$190/yr")
.Build();
Interactive β CTA URL button
var request = WhatsAppMessage
.To("+1234567890")
.Interactive("Visit our website")
.CtaUrl("Open site", "https://example.com");
Interactive β Flow
var request = WhatsAppMessage
.To("+1234567890")
.Interactive("Fill in your details")
.Flow("FLOW_TOKEN", "FLOW_ID", "Start");
Interactive β Catalog
var request = WhatsAppMessage
.To("+1234567890")
.Interactive("Browse our catalog")
.Catalog();
Template
var request = WhatsAppMessage
.To("+1234567890")
.Template("order_confirmation", "en_US")
.AddBodyParameters(p => p
.Text("John")
.Text("Order #42")
.Text("$59.99"))
.AddButton(0, "url", p => p.Text("/orders/42"))
.Build();
Typing indicator
Show a "typingβ¦" indicator before sending a message:
var request = WhatsAppMessage
.To("+1234567890")
.TypingIndicator("wamid.INCOMING_MESSAGE_ID")
.Build();
await client.SendAsync(request);
Reply context and callback data
Chain .ReplyTo() and/or .WithCallbackData() before the message type method to attach a reply context or a business
opaque callback string:
var request = WhatsAppMessage
.To("+1234567890")
.ReplyTo("wamid.ORIGINAL_MESSAGE_ID")
.WithCallbackData("order_ref_42")
.Text("Got your message!")
.Build();
Group messages
var request = WhatsAppMessage
.ToGroup("GROUP_ID")
.Text("Hello everyone!")
.Build();
await client.SendAsync(request);
Or use the helper:
await client.SendToGroupAsync("GROUP_ID", b =>
b.Text("Hello everyone!").Build());
Multi-tenant / factory usage
When you need to send from multiple phone numbers at runtime, inject IWhatsAppClientFactory:
public class MultiTenantSender(IWhatsAppClientFactory factory)
{
public Task SendAsync(string phoneNumberId, string token, string to, string text)
{
var client = factory.Create(o =>
{
o.PhoneNumberId = phoneNumberId;
o.AccessToken = token;
});
return client.SendAsync(WhatsAppMessage.To(to).Text(text).Build());
}
}
Register the factory-only overload (no static options required):
builder.Services.AddWhatsAppClient();
Error handling
Failed API calls throw WhatsAppApiException:
try
{
await client.SendAsync(request);
}
catch (WhatsAppApiException ex)
{
Console.WriteLine($"Code: {ex.ErrorCode}");
Console.WriteLine($"Type: {ex.ErrorType}");
Console.WriteLine($"Trace: {ex.FbTraceId}");
Console.WriteLine($"Message: {ex.Message}");
}
Response
SendMessageResponse exposes the sent message ID and contact info returned by the API:
var response = await client.SendAsync(request);
if (response.IsSuccessful)
Console.WriteLine($"Sent: {response.FirstMessageId}");
License
MIT
| 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
- FluentWhatsApp.Message (>= 2.0.0)
- Microsoft.Extensions.Http (>= 10.0.8)
- Microsoft.Extensions.Http.Resilience (>= 10.6.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.