Skip to main content

lib/increase.ex

defmodule Increase do
  @moduledoc """
  An idiomatic Elixir client for the [Increase API](https://increase.com),
  covering every resource: accounts, transfers (ACH, wire, check, RTP,
  FedNow, Swift), cards, entities, webhooks, and the sandbox simulation
  endpoints used for testing.

  ## Installation

  Add `:increase` to your `mix.exs` dependencies:

      def deps do
        [
          {:increase, "~> 1.0.0"}
        ]
      end

  ## Configuration

  Set your API key and environment in `config/config.exs` (or
  `config/runtime.exs` if you'd rather read it from the environment at
  boot):

      config :increase,
        api_key: System.fetch_env!("INCREASE_API_KEY"),
        environment: :sandbox # or :production

  Sign up at [dashboard.increase.com](https://dashboard.increase.com) to
  get sandbox and production API key pairs. In sandbox, no real money
  moves -- it's there so you can build and test your integration safely.

  If you'd rather not rely on application config (for example, if you're
  talking to multiple Increase accounts from one app), build a client
  explicitly and pass it as the first argument to any function instead:

      client = Increase.Client.new(api_key: "sandbox_...", environment: :sandbox)
      {:ok, account} = Increase.Accounts.retrieve(client, "account_in71c4amph0vgo2qllky")

  ## Usage

  Every resource lives in its own module, named to match the API docs --
  `Increase.Accounts`, `Increase.ACHTransfers`, `Increase.Cards`,
  `Increase.Events`, and so on. Each one exposes the operations available
  for that resource (`create/2`, `retrieve/2`, `update/3`, `list/2`, plus
  whatever resource-specific actions exist, like
  `Increase.CardDisputes.withdraw/2`).

      {:ok, account} =
        Increase.Accounts.create(client, %{name: "My First Account"})

      {:ok, account} =
        Increase.Accounts.retrieve(client, account["id"])

      {:ok, page} =
        Increase.Accounts.list(client, status: %{in: ["open"]})

  Every function returns `{:ok, result}` or `{:error, %Increase.Error{}}`
  -- there are no exceptions to rescue for ordinary API errors. See
  `Increase.Error` for the shape of API errors, and `Increase.Page` for
  how to page through list results (including lazy auto-pagination across
  every page with `Increase.Page.auto_paging_stream/1`).

  Mutating requests (`create/2`, `update/3`, and similar) accept an
  `idempotency_key:` option so you can safely retry them:

      Increase.ACHTransfers.create(
        client,
        %{account_id: "...", amount: 1000, ...},
        idempotency_key: "transfer-\#{order_id}"
      )

  For testing, see `Increase.Simulations` -- a whole family of sandbox-only
  endpoints that let you fast-forward events (settle a card authorization,
  receive an inbound wire, have the Federal Reserve acknowledge an ACH
  transfer) that would otherwise take hours or days to occur naturally.

  ## Webhooks

  If you've configured an Event Subscription, Increase will POST events to
  your endpoint as they happen. Verify these requests with
  `Increase.Webhook.verify/4` before trusting their contents:

      case Increase.Webhook.verify(raw_body, headers, signing_secret) do
        :ok -> handle_event(Jason.decode!(raw_body))
        {:error, reason} -> Logger.warning("Rejected webhook: \#{reason}")
      end

  See `Increase.Webhook` for the full guide, including how to extract the
  required headers from a `Plug.Conn`.

  ## Errors

  API errors are returned as `{:error, %Increase.Error{}}` and follow
  [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457.html): every error has
  a `type`, `title`, `status`, and optional `detail`. Transport-level
  failures (timeouts, DNS issues, etc) are normalized into the same
  struct with `type: "transport_error"`, so you only need to handle one
  shape.

      case Increase.Accounts.close(client, account_id) do
        {:ok, account} ->
          account

        {:error, %Increase.Error{type: "invalid_operation_error"} = error} ->
          Logger.warning("Could not close account: \#{error.detail}")

        {:error, error} ->
          raise error
      end
  """
end