---
title: "Elixir SDK"
lang: en
canonical: https://doc.apibrasil.io/sdks/elixir
markdown: https://doc.apibrasil.io/sdks/elixir.md
source: apibrasil-documentation
---

# Elixir SDK

- **Status:** published
- **Install:** `mix deps.get  # com {:apibrasil, "~> 0.0.1"} no mix.exs`
- **Registry:** Hex — https://hex.pm/packages/apibrasil
- **Repository:** https://github.com/APIBrasil/apigratis-sdk-elixir

## Do not make up the signature

DO NOT INVENT THE Elixir SDK's API. Every library in this catalogue has its own constructor, method names and return shape, and none of them can be deduced from the package name. Open the README of the repository above and use exactly what is written there. If you cannot open the repository, DO NOT GUESS: write the client in plain HTTP, following the section below, and say in your answer that this is what you did and why.

## How to authenticate

The Bearer goes in the Authorization header, on every call. The DeviceToken only on plan-billed APIs — credit-billed ones don't ask for it. It's the same pair the SDK sets for you.

With homolog set to true in the body, the gateway answers from a fixed dataset. The document number 00000000000 belongs to that dataset and to no one.

## The repository on GitHub

- **Stars:** 0
- **Forks:** 0
- **Open issues:** 0
- **Language:** Elixir
- **Licence:** MIT License
- **Last push:** July 28, 2026
- **git clone:** `https://github.com/APIBrasil/apigratis-sdk-elixir.git`

## Repository README

Reproduced from the repository, on the default branch. It is the documentation written by whoever maintains the library: the SDK's signature is what is written here, not what is deduced from the package name.

# SDK ELIXIR - APIGratis by API BRASIL 💧

SDK oficial Elixir da plataforma [APIBrasil](https://apibrasil.com.br) — WhatsApp, SMS, consultas de CPF/CNPJ, veículos, CEP, correios, pagamentos PIX/boleto e muito mais.

[![Hex.pm](https://img.shields.io/hexpm/v/apibrasil.svg)](https://hex.pm/packages/apibrasil)
[![HexDocs](https://img.shields.io/badge/hex-docs-8e7cc3.svg)](https://hexdocs.pm/apibrasil)
[![CI](https://github.com/APIBrasil/apigratis-sdk-elixir/actions/workflows/ci.yml/badge.svg)](https://github.com/APIBrasil/apigratis-sdk-elixir/actions/workflows/ci.yml)
<a href="https://github.com/APIBrasil/apigratis-sdk-elixir/issues" target="_blank"><img alt="GitHub issues" src="https://img.shields.io/github/issues/APIBrasil/apigratis-sdk-elixir"></a>
<a href="https://github.com/APIBrasil/apigratis-sdk-elixir/network" target="_blank"><img alt="GitHub forks" src="https://img.shields.io/github/forks/APIBrasil/apigratis-sdk-elixir"></a>
<a href="https://github.com/APIBrasil/apigratis-sdk-elixir/stargazers" target="_blank"><img alt="GitHub stars" src="https://img.shields.io/github/stars/APIBrasil/apigratis-sdk-elixir"></a>

## Canais de suporte (Comunidade)

[![WhatsApp Group](https://img.shields.io/badge/WhatsApp-Channel-25D366?logo=whatsapp)](https://whatsapp.com/channel/0029VaMiaT6B4hdX3hrUcz3X)
[![Telegram Group](https://img.shields.io/badge/Telegram-Group-32AFED?logo=telegram)](https://t.me/apibrasil1)

## Instalação

Adicione a dependência ao `mix.exs`:

```elixir
def deps do
  [
    {:apibrasil, "~> 0.0.1"}
  ]
end
```

```bash
mix deps.get
```

Requer **Elixir >= 1.14** e **OTP >= 25**. Não há dependência obrigatória: o HTTP usa o `:httpc` do Erlang/OTP (com `verify_peer` e checagem de hostname) e o JSON usa o `JSON` nativo do Elixir 1.18+ / o `:json` do OTP 27+.

Em versões anteriores, adicione um codec JSON:

```elixir
{:jason, "~> 1.4"}
```

Obtenha suas credenciais em https://apibrasil.com.br

## Começando

```elixir
client =
  ApiBrasil.new(
    bearer_token: "SEU_BEARER_TOKEN",
    device_token: "SEU_DEVICE_TOKEN"
  )

# WhatsApp
{:ok, _envelope} =
  ApiBrasil.Messaging.WhatsApp.send_text(client, %{
    "number" => "5511999999999",
    "text" => "Olá! 👋"
  })

# Consulta CNPJ (por créditos)
{:ok, empresa} = ApiBrasil.Data.Consulta.cnpj(client, %{"cnpj" => "00000000000000"})
ApiBrasil.Core.CreditResponse.data(empresa)
```

O `bearer_token` é o JWT do login; o `device_token` é o device dos serviços device-based.

As credenciais também podem vir só do ambiente — `ApiBrasil.from_env/0` lê automaticamente `APIBRASIL_BEARER_TOKEN`, `APIBRASIL_DEVICE_TOKEN`, `APIBRASIL_SECRET_KEY` e `APIBRASIL_BASE_URL`.

Também é possível autenticar por email/senha — `ApiBrasil.login/2` devolve o cliente já autenticado, junto da sessão:

```elixir
{:ok, client, _sessao} =
  ApiBrasil.login(%{"email" => "voce@empresa.com.br", "password" => "******"})
```

Contas com 2FA concluem o login em três passos, aplicando o token com `ApiBrasil.Platform.Auth.authenticate/2`:

```elixir
alias ApiBrasil.Platform.Auth

client = ApiBrasil.from_env()
{:ok, sessao} = Auth.login(client, %{"email" => email, "password" => senha})

client =
  if Auth.requires_2fa?(sessao) do
    desafio = sessao["challenge"]

    {:ok, _} = Auth.send_2fa(client, %{"challenge" => desafio, "method" => "email"})
    {:ok, sessao} = Auth.verify_2fa(client, %{"challenge" => desafio, "code" => "000000"})

    Auth.authenticate(client, sessao)
  else
    Auth.authenticate(client, sessao)
  end
```

## Como a plataforma funciona

A API Brasil tem duas famílias de serviços:

| Família          | Autenticação                                   | Exemplos                                                                              |
| ---------------- | ---------------------------------------------- | ------------------------------------------------------------------------------------- |
| **Device-based** | `Authorization: Bearer` + header `DeviceToken` | WhatsApp, SMS, veículos, CEP, correios, DDD, feriados, tradução, clima, OCR           |
| **Por créditos** | apenas `Authorization: Bearer` (debita saldo)  | `ApiBrasil.Data.Consulta`: `cpf/3`, `cnpj/3`, `veiculos/3`, Serasa, CNH               |

Para os serviços device-based, crie um device com a `SecretKey` da API desejada (painel APIBrasil) e use o `device_token` retornado:

```elixir
{:ok, device} =
  client
  |> ApiBrasil.with_options(secret_key: "SUA_SECRET_KEY")
  |> ApiBrasil.Platform.Devices.store(%{"device_name" => "meu-bot", "type" => "server"})

client = ApiBrasil.put_device_token(client, device["device_token"])
```

O cliente é um valor imutável: `ApiBrasil.put_device_token/2`, `ApiBrasil.put_bearer_token/2` e `ApiBrasil.with_options/2` devolvem sempre um **novo** cliente.

## Serviços disponíveis

| Módulo                                                                                                            | Descrição                                                                                            |
| ----------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| `ApiBrasil.Messaging.WhatsApp`                                                                                    | WhatsApp: `start/3`, `qrcode/3`, `send_text/3`, `send_file/3`, `send_audio/3`, fila (`queue/4`)...    |
| `ApiBrasil.Messaging.Evolution`                                                                                   | Evolution API: `request/5` (controller + action), `call/4`, `queue/5`                                 |
| `ApiBrasil.Messaging.WhatsMeow`                                                                                   | WhatsMeow: `send_text/3`, `instance_create/3`, `instance_qr/3`, `request/4`                           |
| `ApiBrasil.Messaging.SMS`                                                                                         | SMS device-based (`send/3`) e por créditos (`send_with_credits/3`)                                    |
| `ApiBrasil.Data.Dados`                                                                                            | Dados cadastrais device-based (`cpf/3`, `cnpj/3`, `lista_socios/3`...)                                |
| `ApiBrasil.Data.Vehicles`                                                                                         | Veículos por placa (`dados/3`, `fipe/3`, `consulta_fipe/3`, `base_dados/3`)                           |
| `ApiBrasil.Data.Fipe`                                                                                             | Tabela FIPE (`consultar_marcas/3`, `consultar_modelos/3`...)                                          |
| `ApiBrasil.Data.Correios`                                                                                         | Correios (`rastreio/3`, `request/4`)                                                                  |
| `ApiBrasil.Data.Cep`                                                                                              | CEP + geolocalização (`cep/3`, `cidades/3`, `estados/3`, `calcular_distancia/3`)                      |
| `ApiBrasil.Data.Geolocation` / `ApiBrasil.Data.Geomatrix`                                                         | Geocoding e matriz de distâncias                                                                      |
| `ApiBrasil.Data.Recognize`                                                                                        | OCR / Google Vision (`base64/3`, `uri/3`)                                                             |
| `ApiBrasil.Data.Ddd` / `ApiBrasil.Data.Holidays` / `ApiBrasil.Data.Translate` / `ApiBrasil.Data.Weather`          | DDD, feriados, tradução, clima                                                                        |
| `ApiBrasil.Data.Loterias`                                                                                         | Loterias (`latest/4`, `resultado/5`)                                                                  |
| `ApiBrasil.Data.DatabaseIp`                                                                                       | GeoIP (`ip/3`)                                                                                        |
| `ApiBrasil.Data.Consulta`                                                                                         | Consultas por créditos: `cpf/3`, `cnpj/3`, `cnh/3`, `cep/3`, `veiculos/3`, `telefone/3`, `generic/4`  |
| `ApiBrasil.Data.Ura` / `ApiBrasil.Data.ChipVirtual`                                                               | URA reversa e chip virtual                                                                            |
| `ApiBrasil.Data.Bulk`                                                                                             | Execução em lote (`direct/4`, `queue/4`)                                                              |
| `ApiBrasil.Platform.Auth`                                                                                         | Login, 2FA, cadastro, recuperação de senha, perfil                                                    |
| `ApiBrasil.Platform.Devices`                                                                                      | CRUD de devices                                                                                       |
| `ApiBrasil.Platform.Catalog`                                                                                      | Catálogo de APIs, planos, documentações, servidores                                                   |
| `ApiBrasil.Platform.Account`                                                                                      | Saldo, faturas, notificações, tickets                                                                 |
| `ApiBrasil.Platform.Payments`                                                                                     | Recargas e pagamentos PIX/boleto/cartão (Santander, Inter, Mercado Pago, Sicoob)                      |
| `ApiBrasil.Platform.IpWhitelist` / `ApiBrasil.Platform.BearerRateLimit`                                           | Segurança da conta                                                                                    |
| `ApiBrasil.Platform.Reports`                                                                                      | Relatórios e dashboard de consumo                                                                     |

Toda função recebe o cliente no primeiro argumento, aceita o body como mapa (ou `nil`) e termina com uma keyword list de opções.

### WhatsApp

```elixir
alias ApiBrasil.Core.DeviceResponse
alias ApiBrasil.Messaging.WhatsApp

# iniciar sessão e obter QR Code
{:ok, _} = WhatsApp.start(client, %{"webhook_wh_message" => "https://seu-webhook.com/mensagens"})

{:ok, qr} = WhatsApp.qrcode(client)
DeviceResponse.response(qr)["qrcode"]
# => imagem do QR Code em base64

# envios
{:ok, _} = WhatsApp.send_text(client, %{"number" => "5511999999999", "text" => "Olá!"})

{:ok, _} =
  WhatsApp.send_file(client, %{
    "number" => "5511999999999",
    "path" => "https://exemplo.com/boleto.pdf"
  })

{:ok, _} =
  WhatsApp.send_location(client, %{
    "number" => "5511999999999",
    "lat" => -23.5,
    "lng" => -46.6
  })

# qualquer action do catálogo
{:ok, _} = WhatsApp.request(client, "getAllChats")

# fila assíncrona
{:ok, _} = WhatsApp.queue(client, "sendText", %{"number" => "5511999999999", "text" => "por fila"})
```

O envelope device-based tem acessores nomeados — e continua sendo um mapa JSON, porque implementa `Access`:

```elixir
{:ok, envelope} = WhatsApp.send_text(client, %{"number" => numero, "text" => "Olá!"})

DeviceResponse.error?(envelope)     # false
DeviceResponse.message(envelope)    # mensagem do gateway
DeviceResponse.response(envelope)   # payload do provedor
DeviceResponse.api_limit(envelope)  # limite do plano

envelope["response"]                # acesso direto por chave
envelope.json                       # o envelope completo, como mapa
DeviceResponse.to_map(envelope)     # o mesmo
```

### Consultas por créditos

```elixir
alias ApiBrasil.{Consulta, Data}
alias ApiBrasil.Core.CreditResponse

{:ok, cpf} = Data.Consulta.cpf(client, %{"cpf" => "00000000000"})
{CreditResponse.balance(cpf), CreditResponse.data(cpf)}

# o campo `tipo` define o produto consultado — use o builder ApiBrasil.Consulta
"lista-socios"
|> Consulta.new()
|> Consulta.field("cnpj", "00000000000000")
|> then(&Data.Consulta.cnpj(client, &1))

# modo homologação (sandbox, sem cobrança)
"serasa-score-pj"
|> Consulta.new()
|> Consulta.homolog(true)
|> Consulta.field("cnpj", "00000000000000")
|> then(&Data.Consulta.cnpj(client, &1))

# qualquer serviço do catálogo, e os créditos disponíveis
{:ok, _} = Data.Consulta.generic(client, "cnh", %{"cpf" => "00000000000"})
{:ok, _} = Data.Consulta.credits(client, "cpf")
```

O builder também aceita `Consulta.lite/2`, `Consulta.agrupados/2`, `Consulta.extra/2` e `Consulta.fields/2`; qualquer função de serviço recebe a struct diretamente no lugar do mapa.

### Veículos e FIPE (device-based)

```elixir
{:ok, _} = ApiBrasil.Data.Vehicles.dados(client, %{"placa" => "ABC1234"})
{:ok, _} = ApiBrasil.Data.Vehicles.fipe(client, %{"placa" => "ABC1234"})
{:ok, _} = ApiBrasil.Data.Fipe.consultar_marcas(client, %{"codigoTabelaReferencia" => 300})
```

### SMS

```elixir
alias ApiBrasil.Messaging.SMS

{:ok, _} = SMS.send(client, %{"number" => "5511999999999", "message" => "Olá!"})
{:ok, _} = SMS.send_with_credits(client, %{"number" => "5511999999999", "message" => "Olá!"})
```

### Pagamentos e recargas

```elixir
alias ApiBrasil.Platform.Payments

{:ok, _} = Payments.recharge(client, %{"amount" => 50, "type" => "pix"})
{:ok, _} = Payments.pix_generate(client, Payments.provider_santander(), %{"amount" => 50})
{:ok, _} = Payments.pix_status(client, "santander", "TX_ID")

# bytes crus do PDF
{:ok, pdf} = Payments.boleto_pdf(client, Payments.provider_inter(), "ID")
File.write!("boleto.pdf", pdf)
```

### Múltiplos devices

```elixir
bot1 = ApiBrasil.with_device(client, "device_token_1")
bot2 = ApiBrasil.with_device(client, "device_token_2")

{:ok, _} = WhatsApp.send_text(bot1, %{"number" => numero, "text" => "do bot 1"})
{:ok, _} = WhatsApp.send_text(bot2, %{"number" => numero, "text" => "do bot 2"})
```

## Tratamento de erros

Toda chamada devolve `{:ok, resultado}` ou `{:error, %ApiBrasil.Core.Error{}}`; a falha carrega a categoria em `:kind`:

| `:kind`                 | Quando                                          |
| ----------------------- | ----------------------------------------------- |
| `:validation`           | 400/422 — payload inválido                      |
| `:authentication`       | 401 — token ausente/expirado                    |
| `:insufficient_balance` | 402 — sem saldo/créditos                        |
| `:permission`           | 403 — sem permissão (ex: exige PJ)              |
| `:not_found`            | 404/410 — sem dados / rota desativada           |
| `:rate_limit`           | 429 — limite atingido (`:retry_after`, em ms)   |
| `:server`               | 5xx — erro do gateway/provedor                  |
| `:network` / `:timeout` | falha antes da resposta                         |
| `:api`                  | qualquer outra falha da API                     |

```elixir
alias ApiBrasil.Core.{CreditResponse, Error}

case ApiBrasil.Data.Consulta.cpf(client, %{"cpf" => "00000000000"}) do
  {:ok, consulta} ->
    CreditResponse.data(consulta)

  {:error, %Error{kind: :insufficient_balance}} ->
    IO.puts("Recarregue seus créditos")

  {:error, %Error{kind: :rate_limit} = error} ->
    IO.puts("Aguarde #{error.retry_after}ms")

  # detalhes completos da falha
  {:error, %Error{} = error} ->
    IO.puts("#{Exception.message(error)} #{error.status} #{error.code}")
end
```

Cada categoria também tem o seu predicado: `Error.insufficient_balance?/1`, `Error.rate_limit?/1`, `Error.network?/1`... O struct é uma exceção, então `Exception.message/1` formata a mensagem com o status e o código.

## Variantes `!`

Toda função tem um par com `!` que devolve o resultado direto e levanta o `ApiBrasil.Core.Error` em caso de falha — útil em scripts e pipelines:

```elixir
alias ApiBrasil.Messaging.WhatsApp

envelope = WhatsApp.send_text!(client, %{"number" => numero, "text" => "Olá!"})
ApiBrasil.Core.DeviceResponse.response(envelope)

consulta = ApiBrasil.Data.Consulta.cpf!(client, %{"cpf" => "00000000000"})
ApiBrasil.Core.CreditResponse.data(consulta)

try do
  ApiBrasil.Platform.Account.balance!(client)
rescue
  error in ApiBrasil.Core.Error -> IO.puts(Exception.message(error))
end
```

`ApiBrasil.login!/2` segue a mesma ideia e devolve a tupla `{client, sessao}`.

## Retry e observabilidade

Por padrão a SDK refaz a chamada em **HTTP 429** e em **falhas de conexão** (2 tentativas extras, backoff exponencial com jitter, respeitando `Retry-After`). Timeouts e erros de negócio nunca são refeitos — evita duplicar cobranças e envios.

```elixir
client =
  ApiBrasil.new(
    retry: %ApiBrasil.Core.Retry{
      retries: 3,
      min_delay: 500,
      max_delay: 5_000,
      retry_on_statuses: [429, 503]
    },
    hooks: %{
      request: fn info -> IO.puts("→ #{info.method} #{info.url} (##{info.attempt})") end,
      response: fn info -> IO.puts("← #{info.status} em #{info.duration}ms") end,
      retry: fn info -> IO.puts("retry em #{info.delay}ms: #{info.reason}") end
    }
  )

# ou desativando o retry
ApiBrasil.new(retry: ApiBrasil.Core.Retry.none())
```

Os hooks também podem ser um módulo com o behaviour `ApiBrasil.Core.Hooks` (`on_request/1`, `on_response/1`, `on_retry/1`) — o caminho natural para `:telemetry` e `Logger`. Falhas dentro de um hook nunca derrubam a requisição.

Para limitar uma chamada, use `:timeout` (em ms) — no cliente ou só naquela requisição:

```elixir
ApiBrasil.Messaging.WhatsApp.send_text(client, body, timeout: 10_000)
```

## Opções por requisição

`ApiBrasil.with_options/2` devolve um cliente que aplica as opções em todas as chamadas, mantendo base, credenciais e transporte:

```elixir
client
|> ApiBrasil.with_options(
  secret_key: "SUA_SECRET_KEY",
  headers: %{"X-Correlation-Id" => "abc-123"},
  timeout: 5_000
)
|> ApiBrasil.Platform.Devices.store(%{"device_name" => "meu-bot"})

# ou só nesta chamada — a keyword list é sempre o último argumento
ApiBrasil.Messaging.WhatsApp.send_text(client, body, device_token: "outro-device")
```

Opções aceitas: `:query`, `:headers`, `:bearer_token`, `:device_token`, `:secret_key`, `:timeout` e `:response_type` (`:json` ou `:binary`). Veja `ApiBrasil.Core.HTTP`.

## Transporte plugável

O HTTP padrão é o `:httpc` (`ApiBrasil.Core.Transport.Httpc`, sem dependências), mas o behaviour `ApiBrasil.Core.Transport` permite trocar a camada inteira — proxy corporativo, instrumentação, mocks de teste:

```elixir
defmodule MeuTransporte do
  @behaviour ApiBrasil.Core.Transport

  alias ApiBrasil.Core.Transport.{Request, Response}

  @impl true
  def request(%Request{} = request, _opts) do
    # use o cliente HTTP que quiser e devolva status, headers e data
    {:ok, Response.json(200, %{"ok" => true, "url" => request.url})}
  end
end

ApiBrasil.new(transport: MeuTransporte)
```

Para pool de conexões, HTTP/2 e telemetria, use o [Finch](https://hex.pm/packages/finch):

```elixir
# mix.exs
{:finch, "~> 0.16"}

# na sua árvore de supervisão
children = [{Finch, name: MinhaApp.Finch}]

# cliente
ApiBrasil.new(transport: {ApiBrasil.Core.Transport.Finch, name: MinhaApp.Finch})
```

Em testes, o transporte pode ser uma **função de aridade 1** — sem rede, sem mock library:

```elixir
alias ApiBrasil.Core.Transport.Response

client =
  ApiBrasil.new(
    bearer_token: "token-de-teste",
    device_token: "device-de-teste",
    transport: fn request ->
      send(self(), {:requisicao, request.method, request.url})
      {:ok, Response.json(200, %{"error" => false, "response" => %{"id" => "ABC"}})}
    end
  )

{:ok, envelope} =
  ApiBrasil.Messaging.WhatsApp.send_text(client, %{"number" => "5511999999999", "text" => "oi"})

assert ApiBrasil.Core.DeviceResponse.response(envelope) == %{"id" => "ABC"}
assert_received {:requisicao, :post, _url}
```

O transporte padrão também aceita opções: `{ApiBrasil.Core.Transport.Httpc, profile: :default, ssl: [...], connect_timeout: 10_000}`.

## Catálogo gerado

As actions de WhatsApp/Evolution/WhatsMeow e os `tipo` das consultas são gerados do catálogo real da plataforma (`GET /documentations`):

```bash
mix apibrasil.codegen
```

```elixir
alias ApiBrasil.Generated.Catalog

Catalog.service_actions("whatsapp")   # todas as actions do WhatsApp
Catalog.service_actions("cep")        # ["bairros", "cep", "cidades", ...]
Catalog.has_action?("cep", "estados") # true
Catalog.evolution_paths()             # ["call/offer", "chat/deleteMessageForEveryone", ...]
Catalog.consulta_servicos()           # serviços de /consulta/{servico}/credits
Catalog.consulta_tipos()              # os `tipo` conhecidos das consultas
Catalog.consulta_tipo("acerta-essencial")
# => %{service: "cpf", fields: ["cpf"]}
```

## Endpoint sem função dedicada?

Todo o gateway fica acessível pela porta de saída genérica, já com seus headers de autenticação:

```elixir
ApiBrasil.request(client, :post, "/consulta/cpf/credits", %{"cpf" => "00000000000"})
ApiBrasil.request(client, :get, "/reports/quick-stats")

# levanta em caso de falha
ApiBrasil.request!(client, :get, "/reports/quick-stats")

# corpo decodificado sem normalizar em objeto JSON (listas, texto)
{:ok, dados} = ApiBrasil.execute(client, :get, "/plans")

# bytes crus (PDF de boleto, imagens)
{:ok, pdf} = ApiBrasil.download(client, "/inter/boleto/ID/pdf")
```

Documentação completa dos endpoints: https://doc.apibrasil.io

## Configuração avançada

```elixir
client =
  ApiBrasil.new(
    # ou APIBRASIL_BEARER_TOKEN
    bearer_token: "...",
    # ou APIBRASIL_DEVICE_TOKEN
    device_token: "...",
    # usada em Platform.Devices.store/3 (ou APIBRASIL_SECRET_KEY)
    secret_key: "...",
    # padrão (ou APIBRASIL_BASE_URL)
    base_url: "https://gateway.apibrasil.io/api/v2",
    timeout: 30_000,
    headers: %{"X-Correlation-Id" => "abc-123"},
    transport: ApiBrasil.Core.Transport.Httpc,
    retry: %ApiBrasil.Core.Retry{retries: 3},
    hooks: MinhaApp.ApiHooks,
    options: [timeout: 15_000]
  )
```

A mesma configuração pode viver na configuração da aplicação, inclusive com `{:system, "VAR"}`:

```elixir
# config/runtime.exs
config :apibrasil,
  bearer_token: {:system, "APIBRASIL_BEARER_TOKEN"},
  device_token: {:system, "APIBRASIL_DEVICE_TOKEN"},
  base_url: "https://gateway.apibrasil.io/api/v2",
  timeout: 60_000
```

As variáveis de ambiente têm prioridade sobre o `config :apibrasil`, e o que você passa em `ApiBrasil.new/1` tem prioridade sobre as duas. Credenciais vazias contam como ausentes: informar `""` é a forma de desligar o que veio do ambiente. Veja `ApiBrasil.Core.Config`.

## Interface legada

`ApiBrasil.Legacy` mantém o contrato das primeiras SDKs da plataforma — credenciais, body e action em uma única **string JSON** (`credentials` / `body` / `action`), com os erros da API devolvidos decodificados em `{:ok, mapa}` em vez de `{:error, ...}`.

```elixir
legacy = ApiBrasil.Legacy.new()

dados = ~s({
  "action": "sendText",
  "credentials": {
    "DeviceToken": "SEU_DEVICE_TOKEN",
    "BearerToken": "SEU_BEARER_TOKEN"
  },
  "body": {"number": "5511999999999", "text": "Hello World for Elixir"}
})

{:ok, resposta} = ApiBrasil.Legacy.whatsapp(legacy, dados)
```

Além de `whatsapp/3`, há `sms/3`, `cpf/3`, `cnpj/3` e `request/4` (qualquer serviço), todos com a variante `!`.

Ele existe só para quem está migrando das SDKs PHP/Node com o formato antigo. Em código novo, prefira o cliente `ApiBrasil`, que cobre toda a plataforma com funções dedicadas, erros com categoria, retry e hooks.

## Licença

MIT — veja [LICENSE](https://github.com/APIBrasil/apigratis-sdk-elixir/blob/main/LICENSE).

## The same API over HTTP

Base URL: https://gateway.apibrasil.io/api/v2
Each endpoint's path and method are on that API's page in this documentation. Do not deduce a route from the service name.

### Authentication, as HTTP headers
- `Authorization: Bearer YOUR_TOKEN` — on every call.
- `DeviceToken: YOUR_DEVICE_TOKEN` — only on plan-billed APIs; credit-billed ones don't ask for it. The exact header name is whatever the endpoint's page declares.

```bash
curl -X POST https://gateway.apibrasil.io/api/v2/dados/cpf \
  -H "Authorization: Bearer $APIBRASIL_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"cpf": "00000000000", "homolog": true}'
```

With `homolog` set to true in the body, the gateway answers from a fixed dataset and spends no credit. The document number 00000000000 belongs to that dataset and to no one — use it in tests, never in production.

## The other languages

- [C++](https://doc.apibrasil.io/sdks/cpp.md): `git clone https://github.com/APIBrasil/apigratis-sdk-cpp`
- [Python](https://doc.apibrasil.io/sdks/python.md): `pip install api-brasil`
- [Lua](https://doc.apibrasil.io/sdks/lua.md): `luarocks install apibrasil`
- [PHP](https://doc.apibrasil.io/sdks/php.md): `composer require jhowbhz/apigratis-sdk-php`
- [Java](https://doc.apibrasil.io/sdks/java.md): `mvn dependency:get -Dartifact=br.com.apibrasil:apigratis-sdk-java:0.0.1`
- [Ruby](https://doc.apibrasil.io/sdks/ruby.md): `gem install apigratis-sdk-ruby`
- [C# / .NET](https://doc.apibrasil.io/sdks/csharp.md): `dotnet add package ApiBrasil`
- [Node.js / TypeScript](https://doc.apibrasil.io/sdks/javascript.md): `npm install apigratis-sdk-nodejs`
- [Go](https://doc.apibrasil.io/sdks/go.md): `go get github.com/APIBrasil/apigratis-sdk-go`
- [Rust](https://doc.apibrasil.io/sdks/rust.md): `cargo add apibrasil`
- [Dart / Flutter](https://doc.apibrasil.io/sdks/flutter.md): `flutter pub add apigratis_sdk_flutter`

## See also

- Official SDKs: [Official SDKs](https://doc.apibrasil.io/sdks.md)
- Documentation index: https://doc.apibrasil.io/llms.txt
- HTML version of this page: https://doc.apibrasil.io/sdks/elixir
