Packages

Twilio's REST API (messages, calls, phone numbers, recordings, ...) for Elixir: typed structs, retries, pagination. Generated from Twilio's OpenAPI spec.

Current section

Files

Jump to
twilio README.md
Raw

README.md

# Twilio

Twilio's REST API for Elixir: send messages, make calls, buy phone numbers,
read recordings and usage. Every call of Twilio's core API (`2010-04-01`,
197 operations) has a function with docs and typespecs, and every response
decodes into a struct.

```elixir
{:ok, message} =
  Twilio.Messages.create(to: "+15558675310", from: "+15017122661", body: "Hello from Elixir")

message.sid     #=> "SM..."
message.status  #=> "queued"
```

Generated from [Twilio's OpenAPI spec](https://github.com/twilio/twilio-oai)
by [Infusion](https://github.com/mandar1045/infusion), so it follows the API
as Twilio changes it.

## Installation

```elixir
def deps do
  [{:twilio, "~> 0.1"}]
end
```

## Configuration

The SDK reads `TWILIO_ACCOUNT_SID` and `TWILIO_AUTH_TOKEN` from the
environment. Or set them in your config:

```elixir
# config/runtime.exs
config :twilio,
  api_key: {System.fetch_env!("TWILIO_ACCOUNT_SID"), System.fetch_env!("TWILIO_AUTH_TOKEN")},
  account_sid: System.fetch_env!("TWILIO_ACCOUNT_SID")
```

With an [API key](https://www.twilio.com/docs/iam/api-keys) instead of the
auth token, `api_key` is `{key_sid, key_secret}` and `account_sid` stays your
account's SID.

## Usage

Every function returns `{:ok, result}` or `{:error, %Infusion.Runtime.Error{}}`,
and has a `!` variant that returns the result or raises.

```elixir
# Calls
{:ok, call} =
  Twilio.Calls.create(
    to: "+15558675310",
    from: "+15017122661",
    url: "http://demo.twilio.com/docs/voice.xml"
  )

Twilio.Calls.update(call.sid, status: "completed")

# Phone numbers
{:ok, page} = Twilio.AvailablePhoneNumbers.Local.list("US", area_code: 415, page_size: 5)
Twilio.IncomingPhoneNumbers.create(phone_number: hd(page.available_phone_numbers).phone_number)

# Your account
{:ok, balance} = Twilio.Balance.retrieve()
```

**Lists** come one page at a time, or as a lazy stream of every item with
the `_all` functions, which fetch the next page only when the stream gets
there:

```elixir
Twilio.Messages.list_all!(date_sent: "2026-11-15", page_size: 100)
|> Stream.filter(&(&1.status == "failed"))
|> Enum.take(10)
```

**Subaccounts**: the account comes from your config, and any call can use
another one:

```elixir
Twilio.Messages.list(%{page_size: 20}, account_sid: "AC...")
```

**Errors** carry Twilio's error code and message:

```elixir
case Twilio.Messages.create(to: "+1555", from: "+15017122661", body: "Hi") do
  {:ok, message} ->
    message

  {:error, %Infusion.Runtime.Error{type: :bad_request, body: %{"code" => 21211}}} ->
    # https://www.twilio.com/docs/errors/21211: not a valid phone number
    :invalid_number

  {:error, %Infusion.Runtime.Error{type: :rate_limit}} ->
    :try_later
end
```

**Retries**: connection errors, 429 and 5xx responses are retried twice with
backoff, as Twilio's official SDKs do. Set `max_retries` and `timeout` in
your config, or on a single call:

```elixir
Twilio.Messages.retrieve("SM...", timeout: 5_000, max_retries: 0)
```

**Anything else**: `Twilio.request/4` calls an endpoint this SDK has no
function for, and `raw: true` returns the whole `%Req.Response{}`, for
headers.

## Testing your code

Requests go through [Req](https://hexdocs.pm/req), so you can stub Twilio
with `Req.Test`:

```elixir
# config/test.exs
config :twilio, req_options: [plug: {Req.Test, Twilio}], api_key: {"AC1", "token"}, account_sid: "AC1"

# in a test
Req.Test.stub(Twilio, fn conn ->
  Req.Test.json(conn, %{"sid" => "SM1", "status" => "queued"})
end)

assert {:ok, %Twilio.Schemas.Message{sid: "SM1"}} = MyApp.Notify.send_sms("+15558675310")
```

## What's covered

Twilio's core API: Messages, Calls (with recordings, streams, payments,
transcriptions), Conferences, Queues, phone numbers (available, incoming,
caller IDs), Addresses, Applications, Recordings, Transcriptions, SIP,
Usage, Keys and Accounts. Twilio's newer product APIs (Verify, Conversations,
Messaging Services, ...) are separate specs and not in this package yet.

## License

Apache 2.0. Not affiliated with or endorsed by Twilio.