Composable Elixir SDK for the WhatsApp Business API.
Anu is an open core platform for building on WhatsApp. This repo contains the Elixir SDK — the first official client. TypeScript, Python, and Go SDKs are coming.
Docs · Landing page · Cloud
Anu.Message.new("5511999999999")
|> Anu.Message.text("Your order has shipped!")
|> Anu.Message.buttons([
{"Track", :track_order},
{"Cancel", :cancel}
])
|> Anu.deliver()Requires Elixir 1.19+ and OTP 27+.
Add anu to your dependencies in mix.exs:
def deps do
[
{:anu, "~> 0.1"}
]
endAnu talks directly to Meta's Cloud API. Bring your own token:
# config/config.exs
config :anu,
access_token: System.get_env("WHATSAPP_ACCESS_TOKEN"),
phone_number_id: System.get_env("WHATSAPP_PHONE_NUMBER_ID"),
verify_token: System.get_env("WHATSAPP_VERIFY_TOKEN")Anu uses Finch for HTTP. You can configure the connection pool to tune concurrency for your workload:
# config/config.exs
config :anu, :finch_pool,
size: 50,
count: 4Or start a named Finch instance and pass it in:
# In your application supervisor
children = [
{Finch, name: MyApp.Finch, pools: %{
"https://graph.facebook.com" => [size: 100, count: 8]
}}
]
# config/config.exs
config :anu, finch: MyApp.FinchMessages are built by piping through composable functions — no macros, no DSLs:
# Simple text
Anu.Message.new(to)
|> Anu.Message.text("Hello!")
|> Anu.deliver()
# Rich interactive message
Anu.Message.new(to)
|> Anu.Message.header_image("https://example.com/menu.jpg")
|> Anu.Message.body("Check out our new menu")
|> Anu.Message.footer("Open daily 8am–10pm")
|> Anu.Message.buttons([
{"Order now", :order},
{"View hours", :hours}
])
|> Anu.deliver()
# List message with sections
Anu.Message.new(to)
|> Anu.Message.body("What can I help you with?")
|> Anu.Message.button_text("Choose an option")
|> Anu.Message.sections([
Anu.Section.new("Orders", [
Anu.Row.new("order_status", "Order status"),
Anu.Row.new("order_cancel", "Cancel order")
]),
Anu.Section.new("Account", [
Anu.Row.new("account_info", "Account info"),
Anu.Row.new("account_help", "Get help")
])
])
|> Anu.deliver()
# Location
Anu.Message.new(to)
|> Anu.Message.location(-23.5505, -46.6333, name: "São Paulo", address: "SP, Brazil")
|> Anu.deliver()
# React to a message
Anu.Message.new(to)
|> Anu.Message.react("👍", message_id: original_msg_id)
|> Anu.deliver()Anu.Message.new(to)
|> Anu.Message.template("order_confirmation", "pt_BR", [
Anu.Template.body_param("João"),
Anu.Template.body_param("#12345")
])
|> Anu.deliver()Drop the plug into your Phoenix router:
# lib/my_app_web/router.ex
forward "/webhooks/whatsapp", Anu.Webhook.Plug,
handler: MyApp.WhatsAppHandlerImplement the handler behaviour:
defmodule MyApp.WhatsAppHandler do
@behaviour Anu.Webhook.Handler
@impl true
def handle_event(:message_received, %Anu.Event.Message{} = msg) do
msg.from
|> Anu.Message.new()
|> Anu.Message.react("👍", message_id: msg.id)
|> Anu.deliver()
end
@impl true
def handle_event(:message_status, %Anu.Event.Status{} = status) do
# status.id, status.status (:sent, :delivered, :read, :failed)
:ok
end
@impl true
def handle_event(_event, _payload), do: :ok
endLike Swoosh, Anu supports multiple adapters:
# config/config.exs
# Production — Meta Cloud API (default)
config :anu, adapter: Anu.Adapters.Meta
# Development — logs messages to console
config :anu, adapter: Anu.Adapters.Local
# Test — stores messages in-process
config :anu, adapter: Anu.Adapters.TestIn tests:
import Anu.TestAssertions
test "sends order confirmation" do
MyApp.send_confirmation(order)
assert_message_sent(to: order.customer_phone, body: "Your order has shipped!")
endThe open source SDK handles messaging and webhooks. Anu Cloud adds AI primitives and a workflow engine as a hosted API.
# Add the cloud client
{:anu_cloud, "~> 0.1"}# Classify intent
{:ok, %{intent: :order_status, confidence: 0.95}} =
Anu.AI.classify(msg, intents: [:order_status, :complaint, :general])
# Generate contextual reply
Anu.Message.new(msg.from)
|> Anu.AI.reply(context: order_data, tone: :friendly)
|> Anu.deliver()
# Extract structured data
{:ok, %{name: "João", order_id: "12345"}} =
Anu.AI.extract(msg, schema: %{name: :string, order_id: :string})
# Summarize conversation
{:ok, summary} = Anu.AI.summarize(conversation_id)Anu.Workflow.new("support")
|> Anu.Workflow.on_message(match: :any)
|> Anu.Workflow.step(:classify, &Anu.AI.classify(&1, intents: [:order, :billing, :other]))
|> Anu.Workflow.branch(%{
order: &Anu.AI.reply(&1, context: :orders_db),
billing: &Anu.Workflow.handoff(&1, to: :human_agent),
other: &Anu.AI.reply(&1, fallback: true)
})
|> Anu.Workflow.deploy()Anu does not mark up Meta's messaging fees. You pay Meta's per-message cost directly. The cloud service charges only for AI usage and the platform subscription.
This is the Elixir SDK. Other official SDKs are in development:
| SDK | Status | Repo |
|---|---|---|
| Elixir | Available | zoedsoupe/anu |
| TypeScript | Coming soon | — |
| Python | Coming soon | — |
| Go | Coming soon | — |
You can also use the REST API directly from any language.
Contributions are welcome! Please read CONTRIBUTING.md before submitting a PR.
git clone https://github.com/zeeetech/anu_ex.git
cd anu
mix deps.get
mix testBuilt with 💜 by @zoedsoupe