defmodule ApiBrasil do @moduledoc """ 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. 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) Credenciais não informadas são lidas das variáveis de ambiente `APIBRASIL_BEARER_TOKEN`, `APIBRASIL_DEVICE_TOKEN`, `APIBRASIL_SECRET_KEY` e `APIBRASIL_BASE_URL` — basta `from_env/0`. ## Como a plataforma funciona | Família | Autenticação | Exemplos | | ---------------- | ---------------------------------------------- | ------------------------------------------------------------ | | **Device-based** | `Authorization: Bearer` + header `DeviceToken` | WhatsApp, SMS, veículos, CEP, correios, DDD, clima, OCR | | **Por créditos** | apenas `Authorization: Bearer` (debita saldo) | `ApiBrasil.Data.Consulta`: CPF, CNPJ, veículos, Serasa, CNH | ## Convenções - toda função devolve `{:ok, resultado}` ou `{:error, %ApiBrasil.Core.Error{}}`; - toda função tem uma variante `!` que devolve o resultado direto e levanta em caso de falha; - todas aceitam uma keyword list final com as opções da requisição (`:query`, `:headers`, `:bearer_token`, `:device_token`, `:secret_key`, `:timeout`, `:response_type`) — veja `ApiBrasil.Core.HTTP`; - o cliente é um valor imutável: `put_bearer_token/2` e `with_device/2` devolvem um **novo** cliente. ## Serviços | Módulo | Descrição | | ------------------------------------- | ----------------------------------------------------------------- | | `ApiBrasil.Messaging.WhatsApp` | WhatsApp: `start`, `qrcode`, `send_text`, `send_file`, fila... | | `ApiBrasil.Messaging.Evolution` | Evolution API (`/evolution/{controller}/{action}`) | | `ApiBrasil.Messaging.WhatsMeow` | WhatsMeow (`/whatsmeow/{action}`) | | `ApiBrasil.Messaging.SMS` | SMS device-based e por créditos | | `ApiBrasil.Data.Dados` | Dados cadastrais device-based (CPF, CNPJ, sócios...) | | `ApiBrasil.Data.Vehicles` | Veículos por placa | | `ApiBrasil.Data.Fipe` | Tabela FIPE | | `ApiBrasil.Data.Correios` | Correios | | `ApiBrasil.Data.Cep` | CEP + geolocalização | | `ApiBrasil.Data.Geolocation` | Geocoding | | `ApiBrasil.Data.Geomatrix` | Matriz de distâncias | | `ApiBrasil.Data.Recognize` | OCR / Google Vision | | `ApiBrasil.Data.Ddd` | DDD | | `ApiBrasil.Data.Holidays` | Feriados | | `ApiBrasil.Data.Translate` | Tradução | | `ApiBrasil.Data.Weather` | Clima | | `ApiBrasil.Data.Loterias` | Loterias | | `ApiBrasil.Data.DatabaseIp` | GeoIP | | `ApiBrasil.Data.Consulta` | Consultas por crédito (CPF, CNPJ, CNH, veículos, Serasa...) | | `ApiBrasil.Data.Ura` | URA reversa / ligações | | `ApiBrasil.Data.ChipVirtual` | Chip virtual | | `ApiBrasil.Data.Bulk` | Execução em lote | | `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 | | `ApiBrasil.Platform.IpWhitelist` | Whitelist de IPs da conta | | `ApiBrasil.Platform.BearerRateLimit` | Rate limit por Bearer Token | | `ApiBrasil.Platform.Reports` | Relatórios e dashboard de consumo | """ alias ApiBrasil.Client alias ApiBrasil.Core.{Config, Error, HTTP, Service, Transport} alias ApiBrasil.Platform.Auth @doc """ Cria o cliente. Campos não informados vêm do ambiente e da configuração da aplicação. ApiBrasil.new(bearer_token: "jwt", device_token: "device") ApiBrasil.new( base_url: "https://gateway.apibrasil.io/api/v2", timeout: 60_000, headers: %{"X-Correlation-Id" => "abc-123"}, retry: %ApiBrasil.Core.Retry{retries: 3}, hooks: %{response: &IO.inspect/1} ) Veja `ApiBrasil.Core.Config` para a lista completa de opções. """ @spec new(keyword() | map() | Config.t()) :: Client.t() def new(config \\ []), do: Config.resolve(config) @doc "Cria o cliente apenas com as credenciais do ambiente." @spec from_env() :: Client.t() def from_env, do: new([]) @doc "Configuração atual do cliente." @spec config(Client.t()) :: Config.t() def config(%Client{} = client), do: Config.from_client(client) @doc """ Devolve um cliente que aplica `opts` em todas as chamadas — mesma base, mesmas credenciais. client |> ApiBrasil.with_options(secret_key: "SUA_SECRET_KEY") |> ApiBrasil.Platform.Devices.store(%{"device_name" => "meu-bot"}) """ @spec with_options(Client.t(), keyword()) :: Client.t() def with_options(%Client{} = client, opts) do %{client | options: HTTP.merge_options(client.options, opts)} end @doc "Define/atualiza o Bearer Token, devolvendo um novo cliente." @spec put_bearer_token(Client.t(), String.t() | nil) :: Client.t() def put_bearer_token(%Client{} = client, token), do: %{client | bearer_token: presence(token)} @doc "Define/atualiza o DeviceToken, devolvendo um novo cliente." @spec put_device_token(Client.t(), String.t() | nil) :: Client.t() def put_device_token(%Client{} = client, token), do: %{client | device_token: presence(token)} @doc "Define/atualiza a SecretKey, devolvendo um novo cliente." @spec put_secret_key(Client.t(), String.t() | nil) :: Client.t() def put_secret_key(%Client{} = client, key), do: %{client | secret_key: presence(key)} @doc """ Devolve um novo cliente com as mesmas credenciais, mas apontando para outro device — útil para gerenciar vários números/instâncias. bot1 = ApiBrasil.with_device(client, "device_token_1") bot2 = ApiBrasil.with_device(client, "device_token_2") ApiBrasil.Messaging.WhatsApp.send_text(bot1, %{"number" => n, "text" => "do bot 1"}) ApiBrasil.Messaging.WhatsApp.send_text(bot2, %{"number" => n, "text" => "do bot 2"}) """ @spec with_device(Client.t(), String.t()) :: Client.t() def with_device(%Client{} = client, device_token), do: put_device_token(client, device_token) @doc "Monta a URL completa de um caminho do gateway." @spec url(Client.t(), String.t()) :: String.t() def url(%Client{} = client, path), do: HTTP.url(client, path) @doc """ Porta de saída genérica: chama qualquer endpoint do gateway com os headers de autenticação já configurados. Use para rotas que ainda não têm função dedicada na SDK. ApiBrasil.request(client, :post, "/consulta/cpf/credits", %{"cpf" => "00000000000"}) ApiBrasil.request(client, :get, "/reports/quick-stats") """ @spec request(Client.t(), Transport.method(), String.t(), term(), keyword()) :: {:ok, map()} | {:error, Error.t()} def request(%Client{} = client, method, path, body \\ nil, opts \\ []), do: HTTP.request_json(client, method, path, body, opts) @doc "Como `request/5`, mas levanta `ApiBrasil.Core.Error` em caso de falha." @spec request!(Client.t(), Transport.method(), String.t(), term(), keyword()) :: map() def request!(%Client{} = client, method, path, body \\ nil, opts \\ []), do: Service.unwrap!(request(client, method, path, body, opts)) @doc """ Como `request/5`, mas devolve o corpo decodificado sem normalizar em objeto JSON (útil quando a rota responde uma lista, texto ou bytes). """ @spec execute(Client.t(), Transport.method(), String.t(), term(), keyword()) :: {:ok, term()} | {:error, Error.t()} def execute(%Client{} = client, method, path, body \\ nil, opts \\ []), do: HTTP.execute(client, method, path, body, opts) @doc "Baixa os bytes crus de uma rota (PDF de boleto, imagens...)." @spec download(Client.t(), String.t(), keyword()) :: {:ok, binary()} | {:error, Error.t()} def download(%Client{} = client, path, opts \\ []), do: HTTP.bytes(client, :get, path, nil, opts) @doc """ Autentica por email/senha e devolve um cliente já autenticado, junto da sessão retornada pela plataforma. {:ok, client, sessao} = ApiBrasil.login(%{"email" => "voce@empresa.com.br", "password" => "******"}) Devolve erro quando a conta exige 2FA — nesse caso use `ApiBrasil.Platform.Auth.login/3` + `ApiBrasil.Platform.Auth.send_2fa/3` + `ApiBrasil.Platform.Auth.verify_2fa/3`. """ @spec login(map() | keyword(), keyword() | map() | Config.t()) :: {:ok, Client.t(), map()} | {:error, Error.t()} def login(credentials, config \\ []) do client = new(config) with {:ok, session} <- Auth.login(client, credentials) do if Auth.requires_2fa?(session) do {:error, Error.authentication( "Esta conta exige autenticação em dois fatores. " <> "Use ApiBrasil.Platform.Auth.login/3 + send_2fa/3 + verify_2fa/3.", response: session )} else {:ok, Auth.authenticate(client, session), session} end end end @doc "Como `login/2`, mas levanta `ApiBrasil.Core.Error` em caso de falha." @spec login!(map() | keyword(), keyword() | map() | Config.t()) :: {Client.t(), map()} def login!(credentials, config \\ []) do case login(credentials, config) do {:ok, client, session} -> {client, session} {:error, error} -> raise error end end defp presence(nil), do: nil defp presence(""), do: nil defp presence(token), do: token end