All notable changes to this project will be documented in this file. The format follows Keep a Changelog.

[1.0.0] - 2026-06-17

Fixed (static analysis hardening — Dialyzer/Credo, included in this 1.0.0 cut)

Dialyzer — contract/spec accuracy

  • Tink.AuthToken.summary/1: tightened the return spec from map() to the exact fixed-key shape actually returned (expired, expiry, scope_count, scopes, auto_refresh, cache).
  • Tink.Cache.stats/0: tightened {:error, any()} to {:error, map()}, matching Cachex.stats/2's own spec (both tuple positions are map()).
  • Tink.HTTP.MutualTLS.finch_pools/0: tightened map() to the precise %{String.t() => keyword()} shape returned.
  • Tink.HTTP.MutualTLS.child_spec/0: fixed an invalid_contract — the declared Supervisor.child_spec() return type is the formal %{id:, start: {mod, fun, args}, ...} map shape, but this function actually returns a {Finch, opts} 2-tuple meant to be dropped directly into a children list (Finch expands it into the real child spec at start time). Spec corrected to {module(), keyword()} with a doc note explaining the distinction.

Credo — readability and design

  • Fixed alphabetical alias ordering in test/tink/utils_test.exs (alias Tink.{Utils, Client} → alias Tink.{Client, Utils}).
  • Tink.Utils.do_poll/5 and Tink.Paginator.stream/2 both nested 3 levels deep (if/case/cond and fn/case/if respectively), exceeding the configured max of 2. Refactored by extracting the innermost branch into separate named helper functions (handle_poll_result/6 + wait_and_retry/5 for the former; fetch_page/4 + next_token_or_done/1 for the latter) — behavior is unchanged, verified by the full test suite.
  • Tink.Client's core request pipeline used apply(:request, [...]) on a module value with a statically-known arity; replaced with the idiomatic module.request(...) dot-call syntax, which is both clearer and friendlier to Dialyzer.
  • Addressed "nested modules could be aliased at the top of the invoking module" (Credo.Check.Design.AliasUsage) across lib/: added missing aliases for Tink.Config (application.ex, cache.ex, client.ex, http.ex — both adapter modules, webhooks.ex), Tink.RateLimiter (application.ex), Tink.Statistics (finance.ex's Tink.CashFlow), and Plug.Conn (webhooks.ex's Tink.WebhookVerifier); also fixed one redundant fully-qualified Tink.Error.from_response/2 call in enrichment.ex where Error was already aliased but not used. lib/ is now clean of this warning. Note: test/ files still call most public API functions by their fully-qualified name (e.g. Tink.Accounts.list/1) by design — these files each exercise many unrelated modules, where the fully-qualified name makes it immediately clear which module's behavior is under test; this was left as-is rather than mass-aliased.

Incidental fixes found while addressing the above

  • Tink.Client's hardcoded User-Agent header ("tink-elixir/0.2.0") was stale after the version bump to 1.0.0. Replaced with a tink_version/0 helper that reads Application.spec(:tink, :vsn) at runtime, so it can never go stale again.

Fixed (post-review hardening, included in this 1.0.0 cut)

Auth (breaking change for these two functions)

  • Tink.Auth.create_authorization/2 and Tink.Auth.delegate_authorization/2 now send application/x-www-form-urlencoded bodies with snake_case keys (user_id, external_user_id, scope, id_hint), matching the real Tink API. Previously these sent JSON with camelCase keys, which Tink's authorization-grant endpoints reject.
  • delegate_authorization/2 now supports the required :actor_client_id parameter (previously missing entirely, making delegated grants impossible) and an optional :client_id override.

Application supervision tree (startup-crashing bugs)

  • Fixed Tink.Application's Cachex child spec, which used the :limit start option and %Cachex.Limit{} struct removed in Cachex v4. Cache size limiting is now configured via the :hooks option with Cachex.Limit.Scheduled, per Cachex's v4 migration guide. This previously crashed the supervisor on startup whenever cache: [enabled: true].
  • Fixed a typo'd Hammer ETS backend option (cleanup_rate_ms → cleanup_interval_ms).
  • Added the required config :hammer, backend: {Hammer.Backend.ETS, [...]} application config — without it, Hammer.check_rate/3 (used by Tink.RateLimiter) has no backend to talk to. The Hammer child is now only started when rate limiting is actually enabled.
  • Note: these bugs were not caught by the test suite because config/test.exs disables both caching and rate limiting, so the corresponding supervisor children were never started during mix test.

Webhook signature verification (breaking dependency fix)

  • Tink.WebhookVerifier.verify/3 (and therefore verify_with_config/2) called Plug.Crypto.secure_compare/2 directly, but :plug_crypto was never declared as a dependency anywhere in mix.exs. This made the core, non-Plug-specific signature verification function completely broken (UndefinedFunctionError) for any consumer that doesn't separately happen to have Phoenix/Plug in their dependency tree — e.g. an Oban worker or a plain CLI tool processing webhooks. Replaced with a dependency-free constant-time comparison (:crypto.hash_equals/2 on OTP 25+, with a manual XOR-accumulator fallback for OTP 23/24, which Elixir 1.14 still supports). verify_plug/2 still optionally uses Plug.Conn, which is now declared as an optional: true dependency.
  • This bug was caught by actually running the test suite (mix test) against real dependency sources, not just static review — mix.exs was also missing elixirc_paths: ["lib", "test/support"], so mix test couldn't even compile beforehand. Both are fixed.

Mutual TLS (Tink.HTTP.MutualTLS)

  • Fixed cert_pem/key_pem/ca_pem handling: :ssl transport options require raw DER-encoded binaries for :cert/:cacerts, and {KeyType, der} for :key — not decoded public_key records. The previous implementation called :public_key.pem_entry_decode/1, which fully decodes the PEM entry into an Erlang record (incompatible with :ssl), and hardcoded the key type to :RSAPrivateKey even for EC or PKCS#8 ("PrivateKeyInfo") keys.

Documentation accuracy

  • Removed an unverified "(max 1000)" page size claim for Tink.Transactions.list/2 (/data/v2/transactions); documented the confirmed default (10) and max (100) for the related /enrichment/v1/transactions endpoint instead.
  • Clarified that requests_per_min: 600 in Tink.RateLimiter's example config is a conservative client-side default, not a published Tink limit — Tink enforces per-app-ID server-side limits without publishing an exact number.

Known caveats (not changed — unverified)

  • Tink.WebhookVerifier implements HMAC-SHA256 hex-digest verification against an X-Tink-Signature header. This matches the most common industry pattern (and could not be contradicted), but the exact header name and encoding could not be independently confirmed against Tink's current docs in this review. Verify against your Tink dashboard/docs before relying on it in production.

Added

Auth

  • Tink.Auth.revoke_all/1 — authorization:revoke scope, POST /api/v1/oauth/revoke-all
  • Tink.Auth.inspect_token/1 — inspect current token validity and scopes

Users

  • Tink.Users.get_user/1 — GET /api/v1/user
  • Tink.Users.get_profile/1 — GET /api/v1/user/profile
  • Tink.Users.update_user/2 — PUT /api/v1/user
  • Tink.Users.update_profile/2 — PUT /api/v1/user/profile

Credentials (new dedicated module)

  • Tink.Credentials.get_qr/2 — GET /api/v1/credentials/{id}/qr
  • Tink.Credentials.create/2 — POST /api/v1/credentials
  • Tink.Credentials.authenticate/2 — POST /api/v1/credentials/{id}/authenticate
  • Tink.Credentials.submit_supplemental_info/3 — POST /api/v1/credentials/{id}/supplemental-information (MFA/BankID flows)

Accounts

  • Tink.Accounts.get_parties/2 — accounts.parties:readonly
  • Tink.Accounts.update/3 — PATCH /api/v1/accounts/{id}
  • Tink.Accounts.stream/2 — lazy Stream over all pages

Transactions

  • Tink.Transactions.get/2 — GET /api/v1/transactions/{id}
  • Tink.Transactions.get_similar/2 — GET /api/v1/transactions/{id}/similar
  • Tink.Transactions.search/2 — POST /api/v1/search
  • Tink.Transactions.suggest/2 — GET /api/v1/transactions/suggest
  • Tink.Transactions.update/3 — PUT /api/v1/transactions/{id}
  • Tink.Transactions.categorize_multiple/2 — transactions:categorize
  • Tink.Transactions.stream/2 — lazy Stream over all pages
  • Tink.Transactions.list_all/2 — eager collect all pages

Providers

  • Tink.Providers.list_markets/1
  • Tink.Providers.list_identifiers/1
  • Tink.Providers.get_auth_options/2
  • Tink.Providers.get_auth_options_for_market/2

New: Identities

  • Tink.Identities module — identity:read + identities:readonly

New: Enrichment sub-modules (replacing unnamed functions)

  • Tink.Enrichment.Categories
  • Tink.Enrichment.Transactions — with submit_feedback/2
  • Tink.Enrichment.Recurring
  • Tink.Enrichment.Merchants — get_brand/2, get_merchant/2
  • Tink.Enrichment.OnDemand — enrich/2
  • Tink.Enrichment.Sustainability — all 8 endpoints

New: Payments (entirely new domain)

  • Tink.Payments — create, get, get_transfers, cancel, get_conditions, create_settlement_payment, poll_until_terminal
  • Tink.MandatePayments — v1 + v2, poll_until_terminal
  • Tink.Mandates — get, revoke
  • Tink.BulkPayments — create, get
  • Tink.SettlementAccounts — accounts, refunds, withdrawals, transactions (full CRUD)

Budgets (completed)

  • Tink.Budgets.create_one_off/2
  • Tink.Budgets.create_recurring/2
  • Tink.Budgets.get_details/2
  • Tink.Budgets.get_transactions/2
  • Tink.Budgets.list_summaries/1
  • Tink.Budgets.list_recommended/1
  • Tink.Budgets.archive/2

New: Finance management modules

  • Tink.SavingsGoals — full CRUD + archive, complete, allocations, deposit, withdraw, reallocate
  • Tink.Subscriptions — list, get_transactions, update
  • Tink.CostOfLiving — list, get_transactions, update
  • Tink.Insights — list, list_archived, archive, take_action

RiskInsights (completed)

  • Tink.RiskInsights.create/2
  • Tink.RiskInsights.delete/2

AccountCheck

  • Tink.AccountCheck.get_pdf/2

New: BalanceCheck / BalanceRefresh

  • Tink.BalanceCheck.trigger_refresh/2
  • Tink.BalanceCheck.get_refresh_status/2
  • Tink.BalanceCheck.poll_until_complete/3

New: Connectivity

  • Tink.Consents — full v2 consent lifecycle (create, list, get, revoke, authorizations, relay, templates)
  • Tink.ProviderConsents — list, extend

Connector (completed)

  • Tink.Connector.upsert_account/4 — single account upsert
  • Tink.Connector.delete_accounts/2 — v2 batch delete
  • Tink.Connector.delete_transactions/3
  • Tink.Connector.batch_delete_transactions/2
  • Tink.Connector.batch_update_transactions/2
  • Tink.Connector.upsert_transactions_v2/2
  • Tink.Connector.get_operation/2
  • Tink.Connector.poll_operation/3

Webhooks (completed)

  • Tink.Webhooks — create, list, get, update, delete webhook endpoints
  • Tink.WebhookHandler — event registry with GenServer dispatcher, known_events/0
  • Tink.WebhookVerifier — verify/3, verify_with_config/2, verify_plug/2

New: Infrastructure modules

  • Tink.ReportJobs — get, poll_until_complete
  • Tink.TransactionReports — get
  • Tink.Merchants — create, list, get
  • Tink.Paginator — stream/2, collect_all/2

Infrastructure improvements

  • Full-jitter exponential backoff retry on 429/503/network errors
  • Telemetry events: [:tink, :request, :start/stop], [:tink, :cache, :hit/miss]
  • Tink.Client.expired?/1 for token lifetime checks
  • Tink.Client.add_query/2 utility for building query strings

Fixed

  • License: unified to Apache-2.0 throughout (README, mix.exs, LICENSE)
  • Tink.Users.create_authorization/2 removed (use Tink.Auth.create_authorization/2)
  • Transactions module split collapsed — single Tink.Transactions with pagination options

[0.1.1] - 2024-11-01

Initial published release.