WhatsApp SDK for Elixir

Hex.pmCI

Comprehensive Elixir SDK for the WhatsApp Business Platform Cloud API, generated from Meta's official OpenAPI spec with full API coverage.

Note: This is not an official Meta SDK. Meta does not publish a first-party Elixir library. This project is generated from Meta's OpenAPI spec for the WhatsApp Business Platform, follows the same API surface, and is tested for parity against the spec. The goal is an idiomatic Elixir experience with complete API coverage.

What's Included

The SDK layer provides typed resource structs, dedicated service modules, and auto-paging pagination — all generated from the spec with full documentation. The client layer handles HTTP execution via Finch with connection pooling, automatic retries, request encoding, response deserialization, and telemetry.

Together, the full Cloud API surface is covered: 79 service modules, 352 typed resource structs, 113 API operations across 55 domains, webhook signature verification, interactive message builders, and per-process test stubs.

Installation

Add whatsapp_sdk to your dependencies in mix.exs:

def deps do
  [
    {:whatsapp_sdk, "~> 0.1.0"}
  ]
end

Requires Elixir 1.19+ and OTP 27+.

Configuration

# config/runtime.exs
config :whatsapp_sdk,
  access_token: System.fetch_env!("WHATSAPP_ACCESS_TOKEN"),
  phone_number_id: System.fetch_env!("WHATSAPP_PHONE_NUMBER_ID")

Optional global defaults (all have sensible defaults if omitted):

config :whatsapp_sdk,
  access_token: "your_token",
  phone_number_id: "12345",
  waba_id: "67890",              # WhatsApp Business Account ID
  api_version: "v23.0",          # pin API version
  max_retries: 3,                # default: 0
  base_url: "https://graph.facebook.com"

Quick Start

client = WhatsApp.client()

# Send a text message
{:ok, result} = WhatsApp.Messages.MessagesService.send_message(client, %{
  "messaging_product" => "whatsapp",
  "to" => "15551234567",
  "type" => "text",
  "text" => %{"body" => "Hello from Elixir!"}
})

# Retrieve media metadata
{:ok, media} = WhatsApp.Media.RootService.get_media_url(client, "media_id_123")

# Delete a message template
{:ok, _} = WhatsApp.Templates.MessageTemplatesService.delete_template_by_name(client,
  name: "my_template"
)

Responses are automatically deserialized into typed structs:

result.__struct__  #=> WhatsApp.Resources.SendMessage
media.__struct__   #=> WhatsApp.Resources.GetMediaUrl

Override config per-client for multi-account scenarios:

client = WhatsApp.client("other_token",
  phone_number_id: "99999",
  max_retries: 5
)

Handle errors

case WhatsApp.Messages.MessagesService.send_message(client, params) do
  {:ok, result} ->
    result

  {:error, %WhatsApp.Error{code: 190}} ->
    Logger.error("Invalid access token")

  {:error, %WhatsApp.Error{status: 429, retry_after: seconds}} ->
    Logger.warning("Rate limited, retry after #{seconds}s")

  {:error, %WhatsApp.Error{} = err} ->
    Logger.error("WhatsApp error #{err.code}: #{err.message}")
end

Receive webhooks (Phoenix)

# router.ex
forward "/webhook/whatsapp", WhatsApp.WebhookPlug,
  app_secret: "your_app_secret",
  verify_token: "your_verify_token",
  handler: MyApp.WhatsAppHandler

# handler.ex
defmodule MyApp.WhatsAppHandler do
  @behaviour WhatsApp.WebhookPlug.Handler

  @impl true
  def handle_event(%{"messages" => messages}) do
    Enum.each(messages, &process_message/1)
    :ok
  end

  def handle_event(_event), do: :ok
end

Build interactive messages

alias WhatsApp.Interactive

payload =
  Interactive.buttons("Would you like to proceed?")
  |> Interactive.button("yes", "Yes")
  |> Interactive.button("no", "No")
  |> Interactive.build()

Write tests

# test/test_helper.exs
WhatsApp.Test.start()
ExUnit.start()

# test/my_app/notifier_test.exs
defmodule MyApp.NotifierTest do
  use ExUnit.Case, async: true

  setup do
    WhatsApp.Test.stub(fn _request ->
      %{status: 200, body: ~s({"messages":[{"id":"wamid.123"}]}), headers: []}
    end)
    :ok
  end

  test "sends message" do
    assert {:ok, _} = MyApp.Notifier.send("Hello!")
  end
end

Features

SDK

Client

Guides

Telemetry Events

Event Measurements Metadata
[:whatsapp, :request, :start]system_timemethod, path
[:whatsapp, :request, :stop]durationmethod, path, status
[:whatsapp, :request, :exception]durationmethod, path, kind, reason
[:whatsapp, :request, :retry]system_timemethod, path, attempt, reason, wait_ms

Development

# Sync the latest spec
./scripts/sync_openapi.sh

# Regenerate modules
mix whatsapp.generate --clean

# Verify
mix compile --warnings-as-errors
mix test
mix credo --strict
mix dialyzer

Code Generation

The SDK is auto-generated from Meta's WhatsApp Business Platform OpenAPI spec via mix whatsapp.generate. The generator produces:

Organized across 55 API domains covering 113 operations.

Parity Testing

Spec parity is a hard invariant. The test suite includes dedicated parity assertions comparing the generated module set against the OpenAPI spec — domain count, operation count, service module count, and resource module count.

References

License

MIT — see LICENSE for details.