A complete, production-grade Go client for the Tink Open Banking API, built with Domain-Driven Design and zero external dependencies (stdlib only).
The SDK is organised into three explicit, dependency-ordered layers:
Layer rules (enforced by import direction):
domain/imports nothing from this module.infrastructure/imports onlydomain/.application/importsdomain/andinfrastructure/.tink.goimports onlyapplication/andinfrastructure/http.
go get github.com/iamkanishka/tink-goRequires Go 1.25+. No external dependencies — stdlib only.
import (
tink "github.com/iamkanishka/tink-go"
domauth "github.com/iamkanishka/tink-go/domain/auth"
domconn "github.com/iamkanishka/tink-go/domain/connectivity"
)
// 1. Create the client (one per application, safe for concurrent use).
client := tink.New(tink.Config{
ClientID: os.Getenv("TINK_CLIENT_ID"),
ClientSecret: os.Getenv("TINK_CLIENT_SECRET"),
WebhookSecret: os.Getenv("TINK_WEBHOOK_SECRET"),
CacheEnabled: true,
MaxRetries: 3,
})
// 2. Acquire an app-level token.
token, err := client.Auth.ClientCredentials(ctx, "accounts:read,transactions:read")
// 3. Create an authorization grant for an end user.
grant, err := client.Auth.CreateAuthorizationGrant(ctx, token.AccessToken,
domauth.CreateAuthorizationGrantParams{
ExternalUserID: "your-user-id",
Scope: "accounts:read,transactions:read",
})
// 4. Redirect the user to Tink Link.
linkURL := client.Connectivity.BuildTransactionsLink(grant.Code,
domconn.LinkURLOptions{
Market: "GB",
Locale: "en_US",
RedirectURI: "https://yourapp.com/callback",
})
// 5. After callback, exchange the code for a user token.
userToken, err := client.Auth.ExchangeCode(ctx,
domauth.AuthorizationCodeParams{Code: callbackCode})
// 6. Fetch all accounts.
accounts, err := client.Accounts.ListAll(ctx, userToken.AccessToken)
// 7. Fetch all transactions with automatic pagination.
txns, err := client.Transactions.ListAll(ctx, userToken.AccessToken,
domtxn.ListOptions{BookedDateGte: "2026-01-01"})Every service method returns error. Use errors.As to inspect the structured type:
import (
goerrors "errors"
domainerrors "github.com/iamkanishka/tink-go/domain/errors"
)
_, err := client.Accounts.ListAll(ctx, token)
var te *domainerrors.TinkError
if goerrors.As(err, &te) {
fmt.Printf("status=%d type=%s retryable=%v\n",
te.StatusCode, te.Type, te.Retryable())
// te.ErrorCode — application error code (e.g. "TOKEN_INVALID")
// te.RequestID — Tink request ID for support escalation
// te.ErrorDetails — raw parsed JSON error body
}TinkError.Retryable() returns true for network errors, timeouts, and HTTP
408/429/500/502/503/504. The infrastructure layer retries these automatically
(up to Config.MaxRetries attempts with exponential backoff + jitter).
// Register typed handlers.
client.Webhooks.On("credentials.updated", func(e *domwebhook.Event) error {
log.Printf("credentials updated: %v", e.Data)
return nil
})
client.Webhooks.On("*", func(e *domwebhook.Event) error {
// Wildcard — receives every event type.
return nil
})
// In your HTTP handler:
func handleTinkWebhook(w http.ResponseWriter, r *http.Request) {
body, _ := io.ReadAll(r.Body)
sig := r.Header.Get("X-Tink-Signature")
if err := client.Webhooks.Dispatch(body, sig); err != nil {
http.Error(w, "webhook error", 500)
return
}
w.WriteHeader(204)
}| Field | Type | Default | Description |
|---|---|---|---|
ClientID |
string |
— | Tink application client_id (required) |
ClientSecret |
string |
— | Tink application client_secret (required) |
WebhookSecret |
string |
— | HMAC-SHA256 secret for webhook verification |
BaseURL |
string |
https://api.tink.com |
Override for testing/staging |
Timeout |
time.Duration |
30s |
Per-request HTTP deadline |
MaxRetries |
int |
3 |
Max retry attempts on transient errors |
CacheMaxSize |
int |
512 |
In-memory LRU cache capacity |
CacheEnabled |
bool |
false |
Enable GET-response caching |
export TINK_CLIENT_ID=your_client_id
export TINK_CLIENT_SECRET=your_client_secret
go run ./examples/quickstart/Apache-2.0. See LICENSE.