Erlang

Thinwrap publishes three Erlang packages on Hex. The claims below hold for Erlang only — they're the developer-honest version of the umbrella "zero deps, BYO HTTP" pitch, narrowed to what's true on OTP alone. The same packages work from Elixir andGleam, which have their own pages because the setup and the code differ.

Erlang constraints

Runtime dependencies
Zero runtime dependencies
BYO HTTP client
transport behaviour: transport => {Module, Opts}, where Module exports request/2
Provenance
Hex registry signatures + lockfile checksums
Minimum runtime
Erlang/OTP 27+
  • Truly zero dependencies — OTP only: httpc (inets + ssl) is the only HTTP surface, the stdlib json module parses JSON, and AWS SigV4 (SES, SNS, Bedrock) / JWT signing (FCM RS256, APNs ES256) are hand-rolled on crypto and public_key.
  • Hex has no build-provenance attestation like npm provenance or PEP 740: the registry signs its index and your lockfile pins each tarball checksum, which proves integrity, not where the package was built.
  • Stateless: no supervision tree, ETS tables or application env to configure. The default httpc transport is HTTPS-only and never follows redirects — a 3xx surfaces as an error rather than silently re-sending auth headers to another host.

Setup

Add the packages you need to rebar.config. There is nothing to start or configure: the default transport startsinets and ssl itself if your release hasn't.

{deps, [
    {thinwrap_location, "~> 1.3"},
    {thinwrap_notifications, "~> 1.2"},
    {thinwrap_llm, "~> 1.1"}
]}.

One call

Every operation is a function of a config map and an input map, both with atom keys and binary values. The config's providerpicks the connector; switching providers changes the config, not the input or the result.

%% OSRM's public demo server needs no key, so this runs as-is.
Config = #{provider => osrm, base_url => <<"https://router.project-osrm.org">>},
Input = #{waypoints => [#{lat => 52.5200, lng => 13.4050},    % Berlin
                        #{lat => 52.3906, lng => 13.0645}]},  % Potsdam
case thinwrap_location:route(Config, Input) of
    {ok, #{total_distance_meters := M, total_duration_seconds := S}} ->
        io:format("~.1f km, ~B min~n", [M / 1000, round(S / 60)]);
    {error, #{code := Code, message := Msg}} ->
        io:format("~p: ~ts~n", [Code, Msg])
end.
%% => 35.1 km, 39 min

Erlang packages

Baseline-coverage discipline

The unified surface includes only features that ≥90% of providers natively support. Sub-baseline fields stay accessible via thepassthrough map (body deep-merged into the request body; your values win), and the vendor's answer rides inraw. Cross-language parity with the other packages is a design goal — not a hard release gate.

An operation failure returns {error, Error}, a plain map with a code atom (auth_failed,rate_limited, invalid_request, …) and amessage. Misusing the API — an unknown key, a wrong type, a provider that doesn't serve the operation — raiseserror({badarg, Description}) before any request is sent. The wrapper holds no state and performs no automatic retry: the raw Retry-After header and its parsed seconds ride incause, and there is no top-level retry key — by design. Retries, token caching and connection pooling are yours; pass any module implementing request/2 astransport => {Module, Opts}.