CI Hex.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

  • From Node — 429 rate-limit retry with exponential backoff and full jitter
  • From Java — 5xx server-error retry, connection pooling (Finch/NimblePool, auto-sized)
  • From Python — Connection-error retry, auto-sized pool scaling
  • From Ruby — TwiML generation (functional pipeline builder), webhook validation, API surface parity
  • New in this library — Retry-After header parsing, idempotency tokens for safe POST retries, structured :telemetry events, typed Twilio.Error structs with retryable?/1
  • Full API coverage — 494 services and 467 typed resources across 37 products, auto-generated from 54 OpenAPI spec files with full-fidelity mapping
  • Dual pagination — Auto-detects v2010 flat and v1/v2/v3 meta wrapper formats
  • Async test stubs — Per-process HTTP stubs via NimbleOwnership

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

  • Getting Started - Installation, configuration, making API calls, pagination, retries
  • Webhooks - Signature verification, Phoenix integration, status callbacks
  • TwiML - Voice and messaging XML builders, IVR example
  • Testing - HTTP stubs, error simulation, async test patterns
  • Telemetry - Observability, logging, metrics integration

Telemetry Events

EventMeasurementsMetadata
[: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.