Lib.WhatsAppMeta
1.0.89
dotnet add package Lib.WhatsAppMeta --version 1.0.89
NuGet\Install-Package Lib.WhatsAppMeta -Version 1.0.89
<PackageReference Include="Lib.WhatsAppMeta" Version="1.0.89" />
<PackageVersion Include="Lib.WhatsAppMeta" Version="1.0.89" />
<PackageReference Include="Lib.WhatsAppMeta" />
paket add Lib.WhatsAppMeta --version 1.0.89
#r "nuget: Lib.WhatsAppMeta, 1.0.89"
#:package Lib.WhatsAppMeta@1.0.89
#addin nuget:?package=Lib.WhatsAppMeta&version=1.0.89
#tool nuget:?package=Lib.WhatsAppMeta&version=1.0.89
Lib.WhatsAppMeta
A .NET library for integrating with the WhatsApp Business API (Meta). It provides a ready-to-use service for sending template messages, creating templates (including with media), uploading media, webhook verification and processing, and persisting WhatsApp-related data via Entity Framework Core. Designed to work both in the same solution and when installed as a package in another project.
Table of contents
- Features
- Installation
- Package changes (v1.0.58)
- Quick start
- Service registration
- Configuration
- Using the service
- API reference
- Webhook (Meta callback)
- Realtime / SignalR (optional)
- CRM template sending (SendTemplateMessageCrm)
- Creating message templates
- Entities and DbContext
- Data insert and webhook in any project
- Database setup and migrations
- Stored procedures (refresh SQL in another project)
- Requirements
- Troubleshooting
Features
- WhatsApp Meta API – Send template messages, create templates, upload media, manage business account
- Entity Framework Core – DbContext and entities for credentials, templates, message list, webhook message status
- Custom schema – Tables can use a SQL schema (e.g.
crm.MessageTemplates) - CRM bulk sending –
SendTemplateMessageCrmwith template lookup, image upload, and send-list persistence - Webhook – Verify Meta webhook (GET) and process incoming messages, status updates, and history (POST). Optional default processor persists to the same DbContext; you can replace it with your own. Register
AddWhatsAppMediaDownloader()(and optionallyIWhatsAppBaseUrlProvider) so incoming media gets a public URL instead of blank when using the package in another project. - Audit fields (optional) –
IWhatsAppMetaUserIdProviderfor CreatedBy/UpdatedBy/DeletedBy; works without it in another project (inserts still succeed) - Dependency injection – Designed for ASP.NET Core / any host with DI; works when used as a NuGet package in another solution
Installation
NuGet
dotnet add package Lib.WhatsAppMeta
Project reference (same solution)
<ProjectReference Include="path\to\Lib.WhatsAppMeta.csproj" />
Package version: 1.0.58 (multi-targets net7.0 and net10.0).
Package changes (v1.0.58)
| Area | Before | Now (1.0.58) |
|---|---|---|
| Target frameworks | .NET 7 only | .NET 7 and .NET 10 |
| EF Core | 7.0.11 | 7.0.11 (net7) / 10.0.8 (net10) |
| App dependencies | App.Infrastructure, Lib.Core, Lib.Config |
None — standalone NuGet |
| CRM provider classes | WhatsAppCrmClientIdProvider, etc. in the lib |
Removed — implement interfaces in your app |
| SignalR adapter | WhatsAppSignalRNotifierAdapter in the lib |
Removed — implement IWhatsAppRealtimeNotifier |
| Stored procedures | FromSqlRaw |
ADO.NET (works on EF 7 and EF 10) |
| Inbox list DTOs | App.Domain view models |
ChatListEntryDto, BroadcastListEntryDto, ContactListEntryDto |
Common errors after upgrade
| Error | Fix |
|---|---|
WhatsAppCrmClientIdProvider could not be found |
Implement IWhatsAppCrmClientIdProvider in your app — see Scenario B |
WhatsAppSignalRNotifierAdapter could not be found |
Implement IWhatsAppRealtimeNotifier in your app |
SetPropertyCalls type load error |
Use 1.0.58 + matching EF Core major version (10.0.x on .NET 10) |
.NET 7 host
<PackageReference Include="Lib.WhatsAppMeta" Version="1.0.58" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.11" />
.NET 10 host
<PackageReference Include="Lib.WhatsAppMeta" Version="1.0.58" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="10.0.8" />
Quick start
- Register services (e.g. in
Program.csor your DI configuration):
using Lib.WhatsAppMeta.Extensions;
// Uses connection string named "db" from IConfiguration
builder.Services.AddWhatsAppMeta(builder.Configuration);
// Optional: webhook verify token (required if you expose a webhook endpoint)
builder.Services.AddWhatsAppMetaWebhook("YourVerifyToken");
// Optional: install DB objects (tables, schema) on startup
builder.Services.UseWhatsAppMetaMigrations();
- Inject and use the service (always use the interface):
using Lib.WhatsAppMeta.Services;
public class MyHandler
{
private readonly IWhatsappMetaService _whatsappMetaService;
public MyHandler(IWhatsappMetaService whatsappMetaService)
{
_whatsappMetaService = whatsappMetaService;
}
public async Task DoSomething()
{
var response = await _whatsappMetaService.SendTemplateMessage(
jsonPayload, accessToken, CancellationToken.None);
}
}
Service registration
AddWhatsAppMeta (extension methods)
| Overload | Use when |
|---|---|
AddWhatsAppMeta(IConfiguration, connectionStringName?, schema?) |
You want to read the connection string from configuration (e.g. ConnectionStrings:db). |
AddWhatsAppMeta(string connectionString, schema?) |
You have the connection string in code or from another source. |
Parameters
- connectionStringName – Key in
IConfiguration(default:"db") →configuration.GetConnectionString("db"). - schema – SQL schema for tables (e.g.
"crm"→crm.MessageTemplates). Optional; if omitted, default schema is used. - connectionString – Full SQL Server connection string (second overload).
Examples
using Lib.WhatsAppMeta.Extensions;
// Default: connection string "db", default schema
builder.Services.AddWhatsAppMeta(builder.Configuration);
// Custom connection string key and schema "crm"
builder.Services.AddWhatsAppMeta(builder.Configuration, "db", "crm");
// Direct connection string and schema
builder.Services.AddWhatsAppMeta(
"Server=.;Database=MyDb;Integrated Security=true;TrustServerCertificate=true",
"crm");
What gets registered
IWhatsappMetaService→WhatsappMetaService(scoped)WhatsAppMetaDbContext(scoped), when a non-empty connection string is providedIWhatsAppWebhookService→WhatsAppWebhookService(scoped)IWhatsAppWebhookProcessor→DefaultWhatsAppWebhookProcessor(scoped), unless you register your own (your registration wins)IWhatsAppMetaUserIdProvider→HttpContextUserIdProviderAdapter(scoped, optional bridge for audit fields)
AddWhatsAppMetaWebhook (verify token)
Call after AddWhatsAppMeta when you expose a webhook URL for Meta to call:
| Parameter | Description |
|---|---|
verifyToken |
Token Meta sends as hub.verify_token on GET; set per project (e.g. "WAVerifytokenJMS"). |
builder.Services.AddWhatsAppMetaWebhook("YourVerifyToken");
Configuration
appsettings.json
Connection string (required for DB and migrations):
{
"ConnectionStrings": {
"db": "Server=localhost;Database=YourDb;User Id=sa;Password=YourPassword;TrustServerCertificate=True;"
}
}
Facebook / WhatsApp (needed for CRM and template creation):
{
"Facebook": {
"ClientId": "your-meta-app-client-id"
},
"WhatsAppMeta": {
"WebhookVerifyToken": "WAVerifytokenJMS"
},
"BaseUrl": "https://yoursite.com/"
}
Bind Facebook to your own settings class and use it in your IWhatsAppCrmClientIdProvider implementation (see Scenario B).
Using the service
- Always depend on the interface
IWhatsappMetaService. Do not inject the concrete typeWhatsappMetaServicein application code.
// ✅ Correct
public MyService(IWhatsappMetaService whatsappMetaService) { }
// ❌ Wrong – can break DI in some setups
public MyService(WhatsappMetaService whatsappMetaService) { }
API reference
All methods are on IWhatsappMetaService (implemented by WhatsappMetaService).
| Method | Description |
|---|---|
SendTemplateMessage(string json, string accessToken, CancellationToken) |
Sends a template message using raw JSON payload. |
SendTemplateMessageCrm(SendTemplateMessageCrmRequest, SendTemplateMessageCrmCredential, IWhatsAppCrmSendContext, CancellationToken) |
Sends template messages in a CRM flow: template lookup, optional image upload, body/header params, location, and send-list persistence. Requires CRM context and providers to be registered. |
ProcessMediaUploadAsync(string mediaInput, string clientId, string phoneNumberId, string accessToken) |
Uploads media (e.g. base64 or URL) and returns a media ID. |
CreateMessageTemplate(CreateMessageTemplateRequest, string wabaId, string accessToken, CancellationToken) |
Creates a message template (no media upload). |
CreateMessageTemplateWithMedia(object commandRequest, string wabaId, string accessToken, string clientId, string phoneNumberId, ICreateTemplateContext?, MessageTemplateSaveOptions?, CancellationToken) |
Validates base64 size, uploads media, creates template, and optionally persists it via ICreateTemplateContext. |
FetchWhatsappBusinessAccount(string wabaId, string accessToken, CancellationToken) |
Fetches WhatsApp Business Account details. |
GetTemplateById / GetTemplateByIdAsString |
Gets a template by ID. |
GetAllTemplates(string wabaId, string accessToken, string? fields, int? limit, CancellationToken) |
Lists templates. |
RegisterPhoneNumber(string phoneNumberId, string pin, string codeMethod, string accessToken) |
Registers a phone number. |
SubscribeAppToWebhook(string wabaId, string accessToken) |
Subscribes app to webhook. |
SendMessageFromInbox(object commandRequest, string phoneNumberId, string accessToken, CancellationToken) |
Sends a message from inbox. Use SendMessageFromInboxRequest (in Lib.WhatsAppMeta.Models) for a shared request shape across projects. |
Request/response types live in Lib.WhatsAppMeta.Models and related namespaces (e.g. SendTemplateMessageCrmRequest, SendTemplateMessageCrmCredential, CreateMessageTemplateRequest, WhatsAppResponseModel, CreateMessageTemplateResult).
Webhook (Meta callback)
Meta sends GET requests to verify your webhook URL and POST requests with incoming messages, status updates, and history. The Lib handles verification and payload parsing; you expose a thin controller and optionally replace the default processor.
1. Set the verify token
builder.Services.AddWhatsAppMetaWebhook("YourVerifyToken"); // e.g. "WAVerifytokenJMS"
Use the same value in the Meta App Dashboard under Webhook Verify token.
2. Add a webhook controller
Inject IWhatsAppWebhookService. For GET, call VerifyWebhook and return the challenge when valid. For POST, read the raw body and call ProcessWebhookPayloadAsync; the Lib parses the payload and invokes your processor (or the default). Return 200 so Meta does not retry.
using Lib.WhatsAppMeta.Webhook;
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/[controller]")]
public class WhatsappWebhookController : ControllerBase
{
private readonly IWhatsAppWebhookService _webhookService;
public WhatsappWebhookController(IWhatsAppWebhookService webhookService)
{
_webhookService = webhookService;
}
[HttpGet]
public IActionResult VerifyWebhook([FromQuery] string hub_mode, [FromQuery] string hub_verify_token, [FromQuery] string hub_challenge)
{
var result = _webhookService.VerifyWebhook(hub_mode, hub_verify_token, hub_challenge);
if (result.IsValid)
return Ok(result.Challenge);
return Forbid();
}
[HttpPost]
public async Task<IActionResult> ProcessWebhook(CancellationToken cancellationToken)
{
using var reader = new StreamReader(Request.Body);
var body = await reader.ReadToEndAsync(cancellationToken);
await _webhookService.ProcessWebhookPayloadAsync(body, cancellationToken);
return Ok();
}
}
3. Default processor vs custom processor
- Default processor – The Lib registers
DefaultWhatsAppWebhookProcessor, which persists incoming messages, status updates, and history to WhatsAppMetaDbContext (same DB asWhatsappMetaService). No extra code needed; insert/update works when using the Lib in another project. - Custom processor – To use your own DB, SignalR, or external APIs, implement
IWhatsAppWebhookProcessorand register it (your registration overrides the default):
public interface IWhatsAppWebhookProcessor
{
Task ProcessIncomingMessagesAsync(WebhookPayloadContext context, IReadOnlyList<IncomingMessageDto> messages, CancellationToken cancellationToken = default);
Task ProcessStatusUpdatesAsync(WebhookPayloadContext context, IReadOnlyList<StatusUpdateDto> statuses, CancellationToken cancellationToken = default);
Task ProcessHistoryAsync(WebhookPayloadContext context, IReadOnlyList<HistoryMessageDto> history, CancellationToken cancellationToken = default);
}
// In Startup/Program:
builder.Services.AddScoped<IWhatsAppWebhookProcessor, MyWebhookProcessor>();
DTOs and context are in Lib.WhatsAppMeta.Webhook (WebhookPayloadContext, IncomingMessageDto, StatusUpdateDto, HistoryMessageDto). The Lib does not rethrow from ProcessWebhookPayloadAsync so you can always return 200 and avoid Meta retries.
4. Realtime / SignalR (optional)
When using the default webhook processor, you can push incoming messages and status updates to real-time clients (e.g. SignalR) by implementing IWhatsAppRealtimeNotifier and registering it. The default processor then calls your notifier after persisting each incoming message and each status update (sent / delivered / read / failed). Status notifications set MessageStatus on WhatsAppRealtimeNotification.
- Implement
IWhatsAppRealtimeNotifier(e.g. an adapter that calls yourIHubContext<ChatHub>orIRealTimeService):
public class MySignalRNotifier : IWhatsAppRealtimeNotifier
{
private readonly IRealTimeService _realTimeService; // or IHubContext<ChatHub>
public MySignalRNotifier(IRealTimeService realTimeService) => _realTimeService = realTimeService;
public async Task NotifyAsync(WhatsAppRealtimeNotification n, CancellationToken ct)
{
await _realTimeService.SendMessageAsync(n.MessageId, n.ContextMessageId, n.EventName, n.ChatStatus,
n.FromPhoneNumber, n.ToRegisteredUserId, n.Message, n.ContactInfo ?? "", n.TotalRecords, n.ErrorMessage);
}
}
- Register your notifier before
AddWhatsAppMeta(so it is used instead of the default no-op):
builder.Services.AddWhatsAppRealtimeNotifier<MySignalRNotifier>();
builder.Services.AddWhatsAppMeta(builder.Configuration);
// Use the default processor (do not register a custom IWhatsAppWebhookProcessor) so the lib pushes via your notifier.
Event names and payload match the app’s ChatHub: ReceiveMessage for incoming messages, SendMessage for status updates. Use WhatsAppRealtimeStatus.Received (1) and WhatsAppRealtimeStatus.Send (2), or your app’s enum if values match.
CRM template sending (SendTemplateMessageCrm)
SendTemplateMessageCrm sends template messages using template metadata, optional media upload, and persist send-list entries. It needs:
- IWhatsAppCrmSendContext – Implemented by the Lib’s
WhatsAppCrmSendContext, which in turn needs the four providers below. - Four provider interfaces – Implement in your app (NuGet) or use JewelSteps
App.Infrastructureimplementations (monorepo only). See Scenario A / B below.
Provider interfaces (Lib.WhatsAppMeta.Models)
| Interface | Purpose |
|---|---|
IWhatsAppCrmClientIdProvider |
Returns WhatsApp/Meta client ID (e.g. from config). |
IWhatsAppImageUpload |
Uploads an image (e.g. base64) and returns UploadImageResultDto (e.g. path or URL). |
IBaseUrlProvider |
Returns your application’s base URL. |
IWhatsAppErrorParser |
Parses API error JSON and returns a user-facing message. |
WhatsAppCrmSendContext (Lib) uses these four plus WhatsAppMetaDbContext for template lookup and send-list persistence.
Scenario A: JewelStepsAdmin monorepo (project reference + App.Infrastructure)
In JewelStepsAdmin only, CRM provider classes live in App.Infrastructure/Services/FaceBook/WhatsAppCrmProviders.cs (not in the NuGet package). Register them from App.Application/ApplicationServices.cs:
using App.Infrastructure.Services.FaceBook;
using Lib.WhatsAppMeta.Extensions;
using Lib.WhatsAppMeta.Models;
using Lib.WhatsAppMeta.Services;
builder.Services.Configure<FacebookSettings>(builder.Configuration.GetSection("FacebookSettings"));
builder.Services.AddWhatsAppMeta(builder.Configuration, "db");
builder.Services.AddScoped<IWhatsAppCrmClientIdProvider, WhatsAppCrmClientIdProvider>();
builder.Services.AddScoped<IWhatsAppImageUpload, WhatsAppCrmImageUpload>();
builder.Services.AddScoped<IBaseUrlProvider, WhatsAppCrmBaseUrlProvider>();
builder.Services.AddScoped<IWhatsAppErrorParser, WhatsAppCrmErrorParser>();
builder.Services.AddScoped<IWhatsAppCrmSendContext, WhatsAppCrmSendContext>();
Scenario B: Using the Lib as a NuGet package (other solution)
The package does not ship WhatsAppCrmClientIdProvider, WhatsAppSignalRNotifierAdapter, or other app-specific classes. Implement the interfaces in your project and register them.
1. Provider implementations (folder e.g. WhatsApp/Providers/):
using Lib.WhatsAppMeta.Models;
using Microsoft.Extensions.Options;
public class FacebookSettings { public string? ClientId { get; set; } }
public class AppWhatsAppCrmClientIdProvider : IWhatsAppCrmClientIdProvider
{
private readonly FacebookSettings _settings;
public AppWhatsAppCrmClientIdProvider(IOptions<FacebookSettings> opts) =>
_settings = opts?.Value ?? throw new ArgumentNullException(nameof(opts));
public string ClientId => _settings.ClientId ?? "";
}
public class AppWhatsAppImageUpload : IWhatsAppImageUpload
{
public Task<UploadImageResultDto> UploadImageAsync(string base64Path, string folder, CancellationToken ct = default)
=> Task.FromResult(new UploadImageResultDto { ImagePath = null }); // use your upload service
}
public class AppBaseUrlProvider : IBaseUrlProvider
{
private readonly IHttpContextAccessor _http; private readonly IConfiguration _config;
public AppBaseUrlProvider(IHttpContextAccessor http, IConfiguration config) { _http = http; _config = config; }
public string GetBaseUrl() => _http.HttpContext?.Request != null
? $"https://{_http.HttpContext.Request.Host}/"
: (_config["BaseUrl"] ?? "https://yoursite.com/");
}
public class AppWhatsAppErrorParser : IWhatsAppErrorParser
{
public string ParseErrorMessage(string apiError) { /* parse Graph API error JSON */ return apiError; }
}
2. SignalR (optional) — implement IWhatsAppRealtimeNotifier in your app and register with AddWhatsAppRealtimeNotifier<YourAdapter>().
3. Central DI registration (recommended pattern):
using Lib.WhatsAppMeta.Extensions;
using Lib.WhatsAppMeta.Models;
using Lib.WhatsAppMeta.Services;
public static class WhatsAppMetaConfiguration
{
public static IServiceCollection AddWhatsAppMetaServices(this IServiceCollection services, IConfiguration configuration)
{
services.Configure<FacebookSettings>(configuration.GetSection("Facebook"));
services.AddWhatsAppMediaDownloader();
services.AddWhatsAppRealtimeNotifier<AppWhatsAppSignalRNotifierAdapter>(); // your adapter
services.AddWhatsAppMeta(configuration, "db", "crm");
var verifyToken = configuration["WhatsAppMeta:WebhookVerifyToken"] ?? "WAVerifytokenJMS";
services.AddWhatsAppMetaWebhook(verifyToken);
services.AddScoped<IWhatsAppCrmClientIdProvider, AppWhatsAppCrmClientIdProvider>();
services.AddScoped<IWhatsAppImageUpload, AppWhatsAppImageUpload>();
services.AddScoped<IBaseUrlProvider, AppBaseUrlProvider>();
services.AddScoped<IWhatsAppErrorParser, AppWhatsAppErrorParser>();
services.AddScoped<IWhatsAppCrmSendContext, WhatsAppCrmSendContext>(); // from package
services.AddScoped<ICreateTemplateContext, AppCreateTemplateContext>(); // your impl
if (configuration.GetConnectionString("db") is not null)
{
services.UseWhatsAppMetaMigrations();
services.UseWhatsAppMetaProgrammability(); // stored procedures + indexes
}
return services;
}
}
Program.cs
builder.Services.AddHttpContextAccessor();
builder.Services.AddWhatsAppMetaServices(builder.Configuration);
Migration checklist (from older package)
- Upgrade to 1.0.58
- Align EF Core version (7.x or 10.x) with target framework
- Replace
WhatsAppCrmClientIdProviderwith yourAppWhatsAppCrmClientIdProvider - Replace
WhatsAppSignalRNotifierAdapterwith yourIWhatsAppRealtimeNotifierimpl - Keep
services.AddScoped<IWhatsAppCrmSendContext, WhatsAppCrmSendContext>() - Deploy SPs:
UseWhatsAppMetaProgrammability()or extract SQL (see Stored procedures) - Test: template send, inbox, webhook, mark-as-read
Creating message templates
- Without media: use
CreateMessageTemplate(CreateMessageTemplateRequest, wabaId, accessToken, cancellationToken). - With media (image/video/document): use
CreateMessageTemplateWithMedia(object commandRequest, wabaId, accessToken, clientId, phoneNumberId, ICreateTemplateContext?, MessageTemplateSaveOptions?, cancellationToken). The Lib validates base64 size, uploads media, then creates the template. Optionally passICreateTemplateContextandMessageTemplateSaveOptionsto persist the template in your DB.
Optional persistence after create
Implement ICreateTemplateContext and pass it (and MessageTemplateSaveOptions) when calling CreateMessageTemplateWithMedia:
public interface ICreateTemplateContext
{
Task SaveMessageTemplateAsync(MessageTemplateSaveDto dto, CancellationToken cancellationToken = default);
}
Register your implementation and pass it from your app layer when invoking CreateMessageTemplateWithMedia.
Entities and DbContext
DbContext: WhatsAppMetaDbContext (from Lib.WhatsAppMeta.Data).
Entities (Lib.WhatsAppMeta.Entities):
WAUserCredential– WhatsApp user credentialsWAUserCredentialUsersMappingWAWebhookMsgStatus– Webhook message/status/history (default processor persists here)SendWAMessageList– Sent message list (used by CRM send and status updates)MetaChargeListMessageTemplate– Template metadata (used by CRM send)BroadcastCampaign– Broadcast campaignsBroadcastRecipient– Broadcast recipients
You can inject WhatsAppMetaDbContext for direct DB access when needed.
Data insert and webhook in any project
When you use the Lib as a package in another project, the same DbContext and patterns apply so inserts and updates work without app-specific code.
Audit fields (CreatedBy, UpdatedBy, DeletedBy)
WhatsAppMetaDbContext.SaveChangesAsync sets these when an IWhatsAppMetaUserIdProvider is registered. If you do not register one, the fields remain null and inserts still work. To set them:
builder.Services.AddScoped<IWhatsAppMetaUserIdProvider, MyUserIdProvider>();
Webhook persistence
See Webhook (Meta callback). The default processor persists to WhatsAppMetaDbContext; register your own IWhatsAppWebhookProcessor to override (e.g. your DB or SignalR).
Database setup and migrations
Automatic (recommended)
Call after AddWhatsAppMeta:
builder.Services.UseWhatsAppMetaMigrations();
This runs the Lib’s embedded SQL installation (tables, schema) at startup. Ensure the connection string is valid and the app has DB permissions.
Manual
If you use EF Core migrations in the Lib project:
dotnet ef migrations add InitialCreate --context WhatsAppMetaDbContext --project Lib.WhatsAppMeta
dotnet ef database update --context WhatsAppMetaDbContext --project Lib.WhatsAppMeta
Stored procedures and inbox indexes (refresh SQL / NuGet package)
The Lib ships six stored procedures plus InboxPerformanceIndexes.sql (see table in package under content/Lib.WhatsAppMeta.Sql/): sp_GetAllChatByNumber, sp_GetAllContactList, sp_GetAllBroadcastList, sp_GetConversationStats, sp_GetDistinctUnreadRecipientCount, sp_MarkRecipientMessagesAsRead, and inbox indexes.
Your host project’s “refresh SQL” (or programmability step) usually reads from its own output folder (e.g. Programmability/StoredProcedures/*.sql). When you use Lib.WhatsAppMeta as a package, those scripts are not in your project by default.
Option 1: Extract SQL files to your project (recommended if your refresh reads from disk)
Extract the SQL files to your Programmability/StoredProcedures/ folder so your existing refresh SQL step picks them up:
using Lib.WhatsAppMeta.Data;
// One-time setup (e.g. in a migration script or startup):
var targetDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Programmability", "StoredProcedures");
WhatsAppMetaStoredProcedures.ExtractStoredProceduresToDirectory(targetDir);
After extraction, your refresh SQL step will find them in Programmability/StoredProcedures/ and include them automatically.
Option 2: Execute programmatically (recommended for NuGet consumers)
Deploy all stored procedures and inbox indexes in one call:
using Lib.WhatsAppMeta.Data;
using Lib.WhatsAppMeta.Extensions;
// Startup (after AddWhatsAppMeta):
builder.Services.UseWhatsAppMetaProgrammability();
// Or from your own DbContext / refresh handler:
WhatsAppMetaStoredProcedures.ApplyProgrammability(yourDbContext, schema: "dbo");
Stored procedures only (no indexes):
WhatsAppMetaStoredProcedures.ExecuteStoredProcedures(yourDbContext);
WhatsAppMetaObjectsInstaller.InstallInboxPerformanceIndexes(yourDbContext.Database.GetDbConnection(), "dbo");
Option 3: Get scripts and handle yourself
Enumerate scripts and write/execute them as needed:
foreach (var (fileName, content) in WhatsAppMetaStoredProcedures.GetStoredProcedureScripts())
{
// Write to disk, execute, etc.
}
Note: SQL files are included as ContentFiles in the NuGet package (content/Lib.WhatsAppMeta.Sql/StoredProcedures/). Use Option 1 to extract them into your project, or Option 2 for programmatic deploy.
SQL quick reference
| Action | Code |
|---|---|
| Deploy all SPs + indexes | services.UseWhatsAppMetaProgrammability() |
| Deploy from code | WhatsAppMetaStoredProcedures.ApplyProgrammability(dbContext, "crm") |
| Extract to disk | WhatsAppMetaStoredProcedures.ExtractStoredProceduresToDirectory(targetDir) |
Requirements
- .NET 7.0 or .NET 10.0 (package multi-targets both)
- Entity Framework Core matching your target framework:
- .NET 7 host →
Microsoft.EntityFrameworkCore.SqlServer7.0.x - .NET 10 host →
Microsoft.EntityFrameworkCore.SqlServer10.0.x
- .NET 7 host →
- SQL Server (for DbContext and CRM persistence)
- WhatsApp Business API (Meta) credentials and configuration
Troubleshooting
| Error or symptom | What to do |
|---|---|
| Unable to resolve service for type 'WhatsappMetaService' | Inject IWhatsappMetaService, not the concrete WhatsappMetaService type. |
| WhatsAppCrmClientIdProvider could not be found | Not in the NuGet package (v1.0.58+). Implement IWhatsAppCrmClientIdProvider in your app — see Scenario B. |
| WhatsAppSignalRNotifierAdapter could not be found | Not in the package. Implement IWhatsAppRealtimeNotifier in your app. |
| Could not load type 'App.Infrastructure...' | Do not use JewelSteps-only classes. Use Scenario B. |
| 'WhatsAppImageUpload' could not be found | Implement IWhatsAppImageUpload in your app (e.g. AppWhatsAppImageUpload). |
| Package downgrade (Microsoft.Extensions.DependencyInjection.Abstractions 8.0.0 → 7.0.0) | The Lib references 8.0.0. If your app or another package forces 7.x, align versions (e.g. upgrade the app to 8.0.0 or use a single shared version). |
| Could not load type 'SetPropertyCalls`1' from Microsoft.EntityFrameworkCore.Relational | EF Core version mismatch between the host app and an older Lib.WhatsAppMeta package that used FromSqlRaw. Upgrade to the latest Lib.WhatsAppMeta (stored procedures now use ADO.NET). Ensure all Microsoft.EntityFrameworkCore.* packages in the host use the same major version (e.g. all 10.0.x on .NET 10). |
| DbContext not registered / migrations not running | Ensure you pass a non-null, non-empty connection string to AddWhatsAppMeta (via config key or direct string). Call UseWhatsAppMetaMigrations() only when the connection string is available. |
| Webhook GET returns 403 / verification fails | Ensure AddWhatsAppMetaWebhook("YourToken") uses the same token as in the Meta App Dashboard (Webhook → Verify token). |
| Webhook POST succeeds but no data in DB | Ensure WAUserCredentials has a row for the webhook's WABA ID and the credential is not soft-deleted. Default processor looks up by context.WabaId. If you use a custom processor, ensure it is registered (e.g. AddScoped<IWhatsAppWebhookProcessor, YourProcessor>()). |
| Method not found: GetCurrentUserId | The Lib uses IWhatsAppMetaUserIdProvider (optional). If you see this from another assembly, do not inject the Lib's HttpContextUserIdProviderAdapter in that project, or implement your own IWhatsAppMetaUserIdProvider without depending on the missing method. |
License
MIT. For issues or contributions, see the GitHub 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.EntityFrameworkCore.SqlServer (>= 10.0.8)
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.89 | 0 | 11/13/2026 |
| 1.0.88 | 0 | 11/13/2026 |
| 1.0.87 | 131 | 9/30/2026 |
| 1.0.86 | 486 | 8/27/2026 |
| 1.0.85 | 137 | 8/27/2026 |
| 1.0.84 | 125 | 8/27/2026 |
| 1.0.83 | 128 | 8/26/2026 |
| 1.0.82 | 142 | 8/26/2026 |
| 1.0.81 | 125 | 8/26/2026 |
| 1.0.80 | 129 | 8/26/2026 |
| 1.0.79 | 128 | 8/26/2026 |
| 1.0.78 | 147 | 8/24/2026 |
| 1.0.77 | 159 | 8/20/2026 |
| 1.0.76 | 129 | 8/20/2026 |
| 1.0.75 | 180 | 8/13/2026 |
| 1.0.74 | 131 | 8/11/2026 |
| 1.0.73 | 127 | 8/11/2026 |
| 1.0.72 | 124 | 8/11/2026 |
| 1.0.71 | 195 | 7/31/2026 |
| 1.0.70 | 135 | 7/31/2026 |