Handler for Tink API webhooks.
Tink sends webhooks for various events like:
- User credentials updated
- Transaction data refreshed
- Account data changed
- Credential errors
Setup
Configure webhook secret in your application:
config :tink, webhook_secret: System.get_env("TINK_WEBHOOK_SECRET")
Create a webhook endpoint in your Phoenix app:
defmodule MyAppWeb.TinkWebhookController do use MyAppWeb, :controller
def handle(conn, params) do
signature = get_req_header(conn, "x-tink-signature") |> List.first() body = conn.assigns.raw_body # You need to capture raw body case Tink.WebhookHandler.handle_webhook(body, signature) do {:ok, event} -> # Process event process_webhook_event(event) send_resp(conn, 200, "OK") {:error, :invalid_signature} -> send_resp(conn, 401, "Invalid signature") {:error, reason} -> send_resp(conn, 400, "Bad request: #{reason}") endend end
Register webhook handlers:
Tink.WebhookHandler.register_handler(:credentials_updated, &handle_credentials_update/1)
Webhook Events
Tink sends the following event types:
credentials.updated- Credentials were updatedcredentials.refresh.succeeded- Data refresh succeededcredentials.refresh.failed- Data refresh failedprovider_consents.created- Consent was createdprovider_consents.revoked- Consent was revoked
Examples
# Handle webhook in controller
def webhook(conn, _params) do
signature = get_req_header(conn, "x-tink-signature") |> List.first()
body = conn.assigns.raw_body
case Tink.WebhookHandler.handle_webhook(body, signature) do
{:ok, event} ->
MyApp.Webhooks.process(event)
send_resp(conn, 200, "OK")
{:error, :invalid_signature} ->
send_resp(conn, 401, "Unauthorized")
end
end
# Register event handlers
Tink.WebhookHandler.register_handler(:credentials_updated, fn event ->
# Update user's credential status
Users.update_credential_status(event["userId"], :updated)
end)Verification
Webhooks are verified using HMAC-SHA256 signature.
Summary
Functions
Gets all registered handlers grouped by event type.
Handles an incoming webhook request.
Registers a handler function for a specific event type.
Unregisters all handlers for an event type.
Types
@type event() :: %{ type: event_type(), data: map(), timestamp: DateTime.t(), raw: map() }
@type event_type() ::
:credentials_updated
| :credentials_refresh_succeeded
| :credentials_refresh_failed
| :provider_consents_created
| :provider_consents_revoked
| :unknown
Functions
@spec get_handlers() :: %{required(event_type()) => [handler_function()]}
Gets all registered handlers grouped by event type.
Examples
handlers = Tink.WebhookHandler.get_handlers()
#=> %{credentials_updated: [#Function<...>]}
Handles an incoming webhook request.
Verifies the signature, validates the payload structure, guards against test webhooks, and parses the event.
Parameters
body- Raw webhook request body (JSON string)signature- Webhook signature fromX-Tink-Signatureheader
Returns
{:ok, event}- Successfully parsed and verified webhook{:error, :invalid_signature}- Signature verification failed{:error, :invalid_payload}- Failed to parse webhook body{:error, :test_webhook}- Payload is a Tink test ping (acknowledged, not dispatched)
Examples
{:ok, event} = Tink.WebhookHandler.handle_webhook(body, signature)
#=> {:ok, %{type: :credentials_updated, data: %{...}}}
@spec register_handler(event_type(), handler_function()) :: :ok
Registers a handler function for a specific event type.
Uses an ETS :bag table for concurrent-safe registration.
Parameters
event_type- Type of event to handlehandler_fun- Function to call when event is received (arity 1)
Examples
Tink.WebhookHandler.register_handler(:credentials_updated, fn event ->
Logger.info("Credentials updated for user: #{event.data["userId"]}")
MyApp.Users.sync_credentials(event.data["userId"])
end)
@spec unregister_handlers(event_type()) :: :ok
Unregisters all handlers for an event type.
Examples
Tink.WebhookHandler.unregister_handlers(:credentials_updated)