TwilioElixir

CIHex.pm

The complete Twilio SDK for Elixir — auto-generated from Twilio's official OpenAPI specs with full-fidelity mapping across all 54 spec files, 37 products, 494 services, and 467 typed resources.

Twilio has a fragmented feature set across its official SDKs. This library aims to combine the best features from each the official Twilio SDKs (Python, Ruby, Node, and Java) and ships them all in one idiomatic Elixir package.

Features

Installation

Add twilio_elixir to your dependencies in mix.exs:

def deps do
  [
    {:twilio_elixir, "~> 0.1.1"}
  ]
end

Requires Elixir 1.19+ and OTP 27+.

Quick Start

Configuration

# config/runtime.exs
config :twilio_elixir,
  account_sid: System.fetch_env!("TWILIO_ACCOUNT_SID"),
  auth_token: System.fetch_env!("TWILIO_AUTH_TOKEN")

Send a Message

client = Twilio.client()

{:ok, message} = Twilio.Api.V2010.MessageService.create(client, %{
  "To" => "+15551234567",
  "From" => "+15559876543",
  "Body" => "Hello from Elixir!"
})

message.sid    #=> "SMxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
message.status #=> "queued"

Make a Call

{:ok, call} = Twilio.Api.V2010.CallService.create(client, %{
  "To" => "+15551234567",
  "From" => "+15559876543",
  "Url" => "http://demo.twilio.com/docs/voice.xml"
})

Error Handling

case Twilio.Api.V2010.MessageService.create(client, params) do
  {:ok, message} ->
    IO.puts("Sent: #{message.sid}")

  {:error, %Twilio.Error{code: 21211} = err} ->
    IO.puts("Invalid phone number: #{err.message}")

  {:error, %Twilio.Error{} = err} ->
    IO.puts("Error #{err.code}: #{err.message}")
end

Pagination

# Single page
{:ok, page} = Twilio.Api.V2010.MessageService.list(client)

# Lazy auto-paging stream
client
|> Twilio.Api.V2010.MessageService.stream()
|> Stream.take(100)
|> Enum.to_list()

TwiML

Generate TwiML XML for voice and messaging webhooks:

alias Twilio.TwiML.VoiceResponse

xml = VoiceResponse.new()
|> VoiceResponse.say("Hello! Press 1 for sales.", voice: "alice")
|> VoiceResponse.gather(num_digits: 1, action: "/handle-key", children: [
  {"Say", %{}, ["Press 1 for sales, 2 for support."]}
])
|> VoiceResponse.to_xml()
alias Twilio.TwiML.MessagingResponse

xml = MessagingResponse.new()
|> MessagingResponse.message("Thanks for your message!")
|> MessagingResponse.to_xml()

See the TwiML guide for all verbs and a complete IVR example.

Webhook Validation

Verify that incoming requests are from Twilio:

# Form-encoded webhooks
Twilio.Webhook.valid?(url, params, signature, auth_token)

# JSON body webhooks
Twilio.Webhook.valid_body?(url, body, signature, auth_token)

See the Webhooks guide for Phoenix integration and status callback handling.

Testing

Use Twilio.Test to stub HTTP requests in your tests with async: true:

defmodule MyApp.NotifierTest do
  use ExUnit.Case, async: true

  test "sends SMS" do
    Twilio.Test.stub(fn _method, _url, _headers, _body ->
      {201, [], ~s({"sid": "SMxxx", "status": "queued"})}
    end)

    client = Twilio.client("ACtest", "token")

    assert {:ok, msg} = Twilio.Api.V2010.MessageService.create(client, %{
      "To" => "+15551234567",
      "From" => "+15559876543",
      "Body" => "Test"
    })

    assert msg.sid == "SMxxx"
  end
end

See the Testing guide for error simulation, pagination stubs, and process isolation patterns.

Guides

Telemetry Events

Event Measurements Metadata
[:twilio, :request, :start]system_timemethod, path, product
[:twilio, :request, :stop]durationmethod, path, product, status, retries, request_id, error
[:twilio, :request, :exception]durationmethod, path, product, kind, reason
[:twilio, :request, :retry]attempt, wait_msmethod, path, product, status, reason

See the Telemetry guide for logging setup and StatsD/Prometheus integration.

Code Generation

The SDK is auto-generated from Twilio's OpenAPI specs. To regenerate:

# Download latest specs
bash scripts/sync_openapi.sh

# Generate code
mix twilio.generate --clean --stats

Weekly CI automatically pulls the latest specs, regenerates, and opens a PR.

License

MIT License. See LICENSE for details.