WhatsApp.Core.Testing 0.1.1

dotnet add package WhatsApp.Core.Testing --version 0.1.1
                    
NuGet\Install-Package WhatsApp.Core.Testing -Version 0.1.1
                    
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="WhatsApp.Core.Testing" Version="0.1.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="WhatsApp.Core.Testing" Version="0.1.1" />
                    
Directory.Packages.props
<PackageReference Include="WhatsApp.Core.Testing" />
                    
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 WhatsApp.Core.Testing --version 0.1.1
                    
#r "nuget: WhatsApp.Core.Testing, 0.1.1"
                    
#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 WhatsApp.Core.Testing@0.1.1
                    
#: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=WhatsApp.Core.Testing&version=0.1.1
                    
Install as a Cake Addin
#tool nuget:?package=WhatsApp.Core.Testing&version=0.1.1
                    
Install as a Cake Tool

WhatsApp.Core

CI NuGet NuGet Downloads License: MIT .NET

<img src="https://raw.githubusercontent.com/kearns2000/WhatsAppCore/main/assets/logo.png" alt="WhatsApp.Core logo" width="160" height="160" />

A modern .NET SDK for the Meta WhatsApp Cloud API. Send strongly typed messages, manage media, receive webhooks, and integrate with ASP.NET Core - without reimplementing HTTP plumbing, signature validation, or JSON serialization yourself.

Disclaimer: WhatsApp.Core is a community-maintained library. It is not affiliated with, endorsed by, or sponsored by Meta Platforms, Inc. or WhatsApp LLC. WhatsApp is a trademark of its respective owner. See NOTICE.md for the full disclaimer.

What it does

WhatsApp.Core wraps the official Meta Graph API endpoints for WhatsApp Business messaging:

  • Outbound messaging - Send text, templates, images, documents, audio, video, stickers, locations, contacts, interactive messages, and reactions through strongly typed request models and convenience extension methods.
  • Media operations - Upload, retrieve metadata, stream-download, and delete media assets.
  • Webhook receiving - Parse Meta webhook payloads into typed events, verify subscription handshakes, and validate HMAC signatures.
  • ASP.NET Core integration - Map a webhook endpoint with one line, register typed handlers, and configure dispatch behavior.
  • Dependency injection - Register from configuration or code, with support for multiple named WhatsApp accounts.
  • Observability - Built-in ActivitySource and Meter instrumentation compatible with OpenTelemetry collectors.
  • Testing support - A fake client, webhook payload builders, and signature helpers in the optional WhatsApp.Core.Testing package.

What it does not do

WhatsApp.Core intentionally does not:

  • Automate WhatsApp Web, browser sessions, QR-code pairing, or any unofficial gateway.
  • Provide guaranteed message delivery, queueing, or durable webhook processing (you bring your own infrastructure for that).
  • Automatically retry message sends (a failed POST may have reached Meta; retrying could duplicate messages).
  • Validate or enrich phone numbers via network lookups.
  • Ship as an official Meta or WhatsApp SDK.

Installation

Install the core package:

dotnet add package WhatsApp.Core

For ASP.NET Core webhook support:

dotnet add package WhatsApp.Core.AspNetCore

For test doubles and webhook builders:

dotnet add package WhatsApp.Core.Testing

Packages target net8.0 and net10.0. The sample application in this repository uses .NET 10.

Before the first public release, verify that all package IDs (WhatsApp.Core, WhatsApp.Core.AspNetCore, WhatsApp.Core.Testing) are available on NuGet.

Meta prerequisites

Before using this library you need:

  1. A Meta for Developers account.
  2. A Meta app with the WhatsApp product enabled.
  3. A WhatsApp Business Account (WABA) linked to the app.
  4. A phone number registered with the Cloud API, yielding a Phone Number ID.
  5. A permanent or long-lived Access Token with whatsapp_business_messaging and whatsapp_business_management permissions.
  6. An App Secret (for webhook signature validation) and a Verify Token (for the subscription handshake) if you receive webhooks.

Configure the webhook callback URL in the Meta developer console to point at your application's endpoint (for example https://your-host/webhooks/whatsapp).

Basic configuration

Register the default account from configuration:

using WhatsApp.Core.DependencyInjection;

builder.Services.AddWhatsAppCore(
    builder.Configuration.GetSection("WhatsApp"));

appsettings.json:

{
  "WhatsApp": {
    "PhoneNumberId": "YOUR_PHONE_NUMBER_ID",
    "AccessToken": "YOUR_ACCESS_TOKEN",
    "AppSecret": "YOUR_APP_SECRET",
    "VerifyToken": "YOUR_VERIFY_TOKEN",
    "GraphApiVersion": "v21.0"
  }
}

Store secrets in user secrets, environment variables, or a secret manager - never commit real credentials.

Resolve the client and send a message:

using WhatsApp.Core.Client;
using WhatsApp.Core.DependencyInjection;

builder.Services.AddWhatsAppCore(
    builder.Configuration.GetSection("WhatsApp"));

var app = builder.Build();

IWhatsAppClient client =
    app.Services.GetRequiredService<IWhatsAppClient>();

await client.SendTextAsync(
    to: "353...",
    body: "Hello from WhatsApp.Core",
    stopToken: CancellationToken.None);

Programmatic registration is also supported:

builder.Services.AddWhatsAppCore(options =>
{
    options.PhoneNumberId = "...";
    options.AccessToken = "...";
    options.AppSecret = "...";
    options.VerifyToken = "...";
    options.GraphApiVersion = "v21.0";
});

See docs/configuration.md for all options.

Sending a text message

var response = await client.SendTextAsync(
    to: "353871234567",
    body: "Hello!",
    previewUrl: true,
    stopToken: stopToken);

string messageId = response.Messages[0].Id;

Reply to an inbound message by passing a reply context:

await client.SendTextAsync(
    to: "353871234567",
    body: "Thanks for your message.",
    context: new WhatsAppReplyContext { MessageId = "wamid.HBg..." },
    stopToken: stopToken);

See docs/sending-messages.md for all message types.

Sending a template

Template messages require pre-approved templates in your WABA:

await client.SendTemplateAsync(
    to: "353871234567",
    templateName: "hello_world",
    languageCode: "en_US",
    components:
    [
        new WhatsAppTemplateComponent
        {
            ComponentType = "body",
            Parameters = [WhatsAppTemplateParameter.ForText("Patrick")],
        },
    ],
    stopToken: stopToken);

Uploading and sending media

Upload a file, then reference the returned media id in a message:

await using var file = File.OpenRead("photo.jpg");

var upload = await client.UploadMediaAsync(
    content: file,
    fileName: "photo.jpg",
    contentType: "image/jpeg",
    stopToken: stopToken);

await client.SendImageAsync(
    to: "353871234567",
    mediaId: upload.Id,
    caption: "Here is the photo",
    stopToken: stopToken);

Alternatively, send media by public URL without uploading:

await client.SendImageAsync(
    to: "353871234567",
    link: "https://example.com/image.jpg",
    caption: "Sample image",
    stopToken: stopToken);

See docs/media.md for download, metadata, and deletion.

Mapping the webhook endpoint

using WhatsApp.Core.AspNetCore.DependencyInjection;
using WhatsApp.Core.AspNetCore.Webhooks;

builder.Services.AddWhatsAppWebhooks();

var app = builder.Build();

app.MapWhatsAppWebhook("/webhooks/whatsapp");

This maps:

  • GET /webhooks/whatsapp - Meta subscription verification (hub.mode, hub.verify_token, hub.challenge).
  • POST /webhooks/whatsapp - Inbound message and status deliveries.

See docs/webhooks.md for handler registration, deduplication, and dispatch modes.

Writing a typed webhook handler

Implement IWhatsAppWebhookHandler<TEvent> and register it:

using WhatsApp.Core.AspNetCore.Dispatch;
using WhatsApp.Core.AspNetCore.DependencyInjection;
using WhatsApp.Core.AspNetCore.Webhooks;

public sealed class TextMessageHandler(ILogger<TextMessageHandler> logger)
    : IWhatsAppWebhookHandler<WhatsAppTextMessageEvent>
{
    public Task HandleAsync(WhatsAppTextMessageEvent notification, CancellationToken stopToken)
    {
        logger.LogInformation(
            "Inbound text. EventType={EventType}, MessageId={MessageId}",
            nameof(WhatsAppTextMessageEvent),
            notification.MessageId);

        return Task.CompletedTask;
    }
}

builder.Services
    .AddWhatsAppWebhooks()
    .AddWhatsAppWebhookHandler<TextMessageHandler, WhatsAppTextMessageEvent>();

Available typed events include WhatsAppTextMessageEvent, WhatsAppImageMessageEvent, WhatsAppInteractiveReplyMessageEvent, WhatsAppMessageDeliveredEvent, WhatsAppMessageFailedEvent, and UnknownWhatsAppMessageEvent for forward compatibility.

Validating signatures

By default, POST webhook deliveries must include a valid X-Hub-Signature-256 header computed with your app secret. Validation runs on the raw request body before JSON parsing.

Disable validation only for local development, with an explicit insecure opt-in:

builder.Services.AddWhatsAppWebhooks(options =>
{
    options.RequireSignatureValidation = false;
    options.AllowInsecureNoSignatureValidation = true;
});

Without AllowInsecureNoSignatureValidation, options validation fails at startup. Never disable signature validation in production.

Generate test signatures with WhatsApp.Core.Testing:

using WhatsApp.Core.Testing.Signatures;

string signature = WhatsAppWebhookTestSignatures.CreateSignature(payloadBytes, appSecret);

Named accounts

Register multiple WhatsApp Business numbers in one application:

builder.Services.AddWhatsAppCore("support", options =>
{
    options.PhoneNumberId = "...";
    options.AccessToken = "...";
    options.GraphApiVersion = "v21.0";
});

builder.Services.AddWhatsAppCore("marketing", options =>
{
    options.PhoneNumberId = "...";
    options.AccessToken = "...";
    options.GraphApiVersion = "v21.0";
});

Resolve clients by name:

IWhatsAppClientFactory factory = app.Services.GetRequiredService<IWhatsAppClientFactory>();

IWhatsAppClient support = factory.CreateClient("support");
IWhatsAppClient marketing = factory.CreateClient("marketing");

Each account uses an isolated HttpClient, credentials, and diagnostics tags. Map separate webhook endpoints per account with WhatsAppWebhookOptions.AccountName.

Error handling

The library throws two primary exception types:

  • WhatsAppValidationException - Client-side validation failed before any HTTP call (missing recipient, empty body, invalid media source combination, etc.).
  • WhatsAppApiException - The Graph API returned a non-success response. Carries StatusCode, ErrorCode, MetaTraceId, IsTransient, and RetryAfter for informed retry decisions.

Message sends are never automatically retried. Optional safe retries apply only to idempotent operations (media metadata GET, media DELETE) when enabled in WhatsAppResilienceOptions.

See docs/error-handling.md.

Diagnostics

WhatsApp.Core emits standard .NET diagnostics without requiring OpenTelemetry packages:

Source Name
ActivitySource WhatsApp.Core
Meter WhatsApp.Core
ActivitySource (webhooks) WhatsApp.Core.AspNetCore
Meter (webhooks) WhatsApp.Core.AspNetCore

Metrics include outbound request counts, failures, duration, media bytes, webhooks received, invalid signatures, and dispatch failures. Safe tags only - no phone numbers, message bodies, or tokens.

See docs/observability.md.

Testing with the fake client

using WhatsApp.Core.Testing.Fakes;

var fake = new FakeWhatsAppClient();
fake.QueueResponse(new SendMessageResponse { /* ... */ });

await fake.SendTextAsync("353871234567", "test", stopToken: default);

RecordedWhatsAppRequest request = fake.Requests[0];
Assert.Equal(RecordedWhatsAppOperation.SendMessage, request.Operation);

Build webhook payloads for integration tests:

using WhatsApp.Core.Testing.Builders;

string json = WhatsAppWebhookBuilder.Create()
    .WithPhoneNumberId("PHONE_NUMBER_ID")
    .AddTextMessage("wamid.abc", "353871234567", "Hello")
    .AddDeliveredStatus("wamid.outbound", "353871234567")
    .BuildJson();

See docs/testing.md.

Security guidance

  • Store AccessToken, AppSecret, and VerifyToken in secret managers - not source control.
  • Enable webhook signature validation in every deployed environment.
  • Do not log message bodies, complete phone numbers, access tokens, or temporary media URLs.
  • Use HTTPS for your webhook callback URL.
  • Pin an explicit Graph API version (v21.0); never use the latest alias.
  • Acknowledge webhooks quickly and process events asynchronously via durable queues when work is substantial.
  • Implement durable deduplication for handlers that are not naturally idempotent.

See SECURITY.md for vulnerability reporting.

Versioning policy

This project follows Semantic Versioning:

  • Major - Breaking public API changes.
  • Minor - Backward-compatible features.
  • Patch - Backward-compatible bug fixes.

Pre-release versions use suffixes such as alpha, beta, or rc. The current version is 0.1.0.

During the 0.x series, minor releases may include breaking changes as the public API stabilizes. Pin exact versions in production until 1.0.

Graph API versions are configured independently via WhatsAppOptions.GraphApiVersion and are not tied to package version numbers.

Trademark and affiliation disclaimer

WhatsApp.Core is an independent, community-maintained open-source project. It is not affiliated with, endorsed by, or sponsored by Meta Platforms, Inc., WhatsApp LLC, or any of their subsidiaries.

WhatsApp is a registered trademark of WhatsApp LLC. The project logo is an unofficial mark for this community library and does not imply endorsement by Meta or WhatsApp.

For the full notice, see NOTICE.md.

Documentation

Document Description
architecture.md Project boundaries, request flows, extension points
configuration.md All configuration options
sending-messages.md Outbound message types
webhooks.md Receiving and handling webhooks
media.md Upload, download, and delete media
error-handling.md Exceptions and retry guidance
observability.md Tracing and metrics
testing.md Fake client and test builders

Sample application

Run the included sample API:

dotnet run --project samples/WhatsApp.Core.Sample.Api

Configure credentials via user secrets or appsettings.Development.json, then explore the Swagger UI at http://localhost:5080/swagger or use WhatsApp.Core.Sample.Api.http. Outbound /messages/* routes require Development and Sample:EnableSendApi=true (set in appsettings.Development.json).

Contributing

See CONTRIBUTING.md.

License

MIT - see LICENSE.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  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
0.1.1 46 7/19/2026
0.1.0 50 7/19/2026
0.1.0-alpha.1 39 7/19/2026

Initial release of WhatsApp.Core.