Tink.Connector (Tink v0.1.1)

Copy Markdown View Source

Connector API for ingesting accounts and transactions directly.

The Connector API allows you to bypass bank connections and directly ingest account and transaction data into Tink. This is useful for:

  • Testing and development
  • Custom data sources
  • Legacy system integration
  • Mock data scenarios

Features

  • Create users programmatically
  • Ingest accounts with balance information
  • Ingest transactions in bulk
  • Real-time and batch transaction updates
  • Support for pending transactions

Prerequisites

To use the Connector API, ensure you have:

  • Client credentials with appropriate scopes
  • User creation permissions

Flow

# Step 1: Get access token with required scopes
client = Tink.client(
  scope: "user:create,user:read,transactions:write,transactions:read,accounts:write,accounts:read"
)

# Step 2: Create user
{:ok, user} = Tink.Connector.create_user(client, %{
  external_user_id: "test-user-1",
  market: "GB",
  locale: "en_US"
})

# Step 3: Ingest accounts
{:ok, accounts} = Tink.Connector.ingest_accounts(client, "test-user-1", %{
  accounts: [
    %{
      external_id: "checking-001",
      name: "Checking Account",
      type: "CHECKING",
      balance: 15000.50,
      number: "1234567890"
    }
  ]
})

# Step 4: Ingest transactions
{:ok, result} = Tink.Connector.ingest_transactions(client, "test-user-1", %{
  type: "REAL_TIME",
  transaction_accounts: [
    %{
      external_id: "checking-001",
      balance: 15000.50,
      transactions: [
        %{
          external_id: "txn-001",
          amount: -45.50,
          date: System.system_time(:millisecond),
          description: "Grocery Store",
          type: "DEFAULT",
          pending: false
        }
      ]
    }
  ]
})

Use Cases

Testing Financial Products

@spec setup_test_user_with_data() :: {:ok, map()} | {:error, Error.t()}

def setup_test_user_with_data do
  client = Tink.client(scope: connector_scopes())

  {:ok, user} = Tink.Connector.create_user(client, %{
    external_user_id: "test-user-#{:rand.uniform(10000)}",
    market: "GB",
    locale: "en_US"
  })

  {:ok, _} = Tink.Connector.ingest_accounts(client, user["external_user_id"], %{
    accounts: test_accounts()
  })

  {:ok, _} = Tink.Connector.ingest_transactions(
    client,
    user["external_user_id"],
    %{type: "REAL_TIME", transaction_accounts: test_transactions()}
  )

  user
end

Legacy System Integration

@spec sync_from_legacy_system(String.t()) :: {:ok, map()} | {:error, Error.t()}

def sync_from_legacy_system(external_user_id) do
  client = Tink.client(scope: connector_scopes())

  legacy_accounts = LegacyDB.get_accounts(external_user_id)
  legacy_transactions = LegacyDB.get_transactions(external_user_id)

  accounts = transform_accounts(legacy_accounts)
  {:ok, _} = Tink.Connector.ingest_accounts(client, external_user_id, accounts)

  transactions = transform_transactions(legacy_transactions)
  {:ok, _} = Tink.Connector.ingest_transactions(client, external_user_id, transactions)

  :ok
end

Mock Data for Demos

@spec create_demo_user_with_scenario(atom()) :: {:ok, map()} | {:error, Error.t()}

def create_demo_user_with_scenario(scenario_type) when is_atom(scenario_type) do
  client = Tink.client(scope: connector_scopes())

  scenario_str = Atom.to_string(scenario_type)

  {:ok, user} = Tink.Connector.create_user(client, %{
    external_user_id: "demo-#{scenario_str}",
    market: "GB",
    locale: "en_US"
  })

  case scenario_type do
    :high_saver   -> setup_high_saver_scenario(client, user["external_user_id"])
    :overspender  -> setup_overspender_scenario(client, user["external_user_id"])
    :stable_income -> setup_stable_income_scenario(client, user["external_user_id"])
  end

  user
end

Required Scopes

  • user:create - Create users
  • user:read - Read user data
  • accounts:write - Ingest accounts
  • accounts:read - Read accounts
  • transactions:write - Ingest transactions
  • transactions:read - Read transactions

Summary

Functions

Creates a new user via the Connector API.

Functions

create_user(client, params)

@spec create_user(Tink.Client.t(), map()) :: {:ok, map()} | {:error, Tink.Error.t()}

Creates a new user via the Connector API.

Parameters

  • client - Tink client with user:create scope
  • params - User parameters:
    • :external_user_id - Your unique user identifier (required)
    • :market - Market code (e.g., "GB", "SE") (required)
    • :locale - Locale code (e.g., "en_US", "sv_SE") (required)

Returns

  • {:ok, user} - Created user with user_id
  • {:error, error} - If the request fails

Examples

client = Tink.client(scope: "user:create")

{:ok, user} = Tink.Connector.create_user(client, %{
  external_user_id: "test-user-1",
  market: "GB",
  locale: "en_US"
})
#=> {:ok, %{
#     "user_id" => "tink_user_abc123",
#     "external_user_id" => "test-user-1",
#     "market" => "GB",
#     "locale" => "en_US"
#   }}

Required Scope

user:create

ingest_accounts(client, external_user_id, params)

@spec ingest_accounts(Tink.Client.t(), String.t(), map()) ::
  {:ok, map()} | {:error, Tink.Error.t()}

Ingests accounts for a user.

Creates or updates accounts with balance and metadata.

Parameters

  • client - Tink client with accounts:write scope
  • external_user_id - External user ID
  • params - Account data:
    • :accounts - List of accounts (required)

Each account should contain:

  • :external_id - Unique account identifier (required)
  • :name - Account name (required)
  • :type - Account type (required)
  • :balance - Current balance (required)
  • :number - Account number (optional)
  • :available_credit - Available credit (optional)
  • :reserved_amount - Reserved/pending amount (optional)
  • :closed - Whether account is closed (optional, default: false)
  • :flags - Account flags (optional)
  • :exclusion - Exclusion settings (optional)
  • :payload - Custom metadata (optional)

Returns

  • {:ok, result} - Ingestion result
  • {:error, error} - If the request fails

Examples

client = Tink.client(scope: "accounts:write")

{:ok, result} = Tink.Connector.ingest_accounts(client, "test-user-1", %{
  accounts: [
    %{
      external_id: "checking-001",
      name: "Main Checking",
      type: "CHECKING",
      balance: 15000.50,
      number: "1234567890",
      available_credit: 0.0,
      reserved_amount: 100.0,
      closed: false,
      flags: [],
      exclusion: "NONE",
      payload: %{}
    },
    %{
      external_id: "savings-001",
      name: "Savings Account",
      type: "SAVINGS",
      balance: 50000.0,
      number: "9876543210",
      closed: false
    },
    %{
      external_id: "credit-001",
      name: "Credit Card",
      type: "CREDIT_CARD",
      balance: -2500.0,
      available_credit: 20000.0,
      number: "4111111111111111"
    }
  ]
})

Account Types

  • CHECKING - Checking/current account
  • SAVINGS - Savings account
  • CREDIT_CARD - Credit card
  • LOAN - Loan account
  • PENSION - Pension/retirement
  • INVESTMENT - Investment account
  • MORTGAGE - Mortgage
  • OTHER - Other account type

Flags

  • MANDATE - Account has mandate
  • BUSINESS - Business account
  • EXTERNAL - External account

Exclusion

  • NONE - Include in all features
  • PFM - Exclude from PFM
  • SEARCH - Exclude from search
  • PFM_AND_SEARCH - Exclude from both

Required Scope

accounts:write

ingest_transactions(client, external_user_id, params)

@spec ingest_transactions(Tink.Client.t(), String.t(), map()) ::
  {:ok, map()} | {:error, Tink.Error.t()}

Ingests transactions for a user.

Creates or updates transactions with real-time or batch processing.

Parameters

  • client - Tink client with transactions:write scope
  • external_user_id - External user ID
  • params - Transaction data:
    • :type - Update type: "REAL_TIME" or "BATCH" (required)
    • :transaction_accounts - List of accounts with transactions (required)
    • :auto_book - Auto-book pending transactions (optional, default: false)
    • :override_pending - Override pending transactions (optional, default: false)

Each transaction_account should contain:

  • :external_id - Account external ID (required)
  • :balance - Updated account balance (required)
  • :reserved_amount - Reserved amount (optional)
  • :transactions - List of transactions (required)

Each transaction should contain:

  • :external_id - Unique transaction ID (required)
  • :amount - Transaction amount (negative for expenses) (required)
  • :date - Transaction timestamp in milliseconds (required)
  • :description - Transaction description (required)
  • :type - Transaction type (required)
  • :pending - Whether transaction is pending (optional, default: false)
  • :payload - Custom metadata (optional)

Returns

  • {:ok, result} - Ingestion result
  • {:error, error} - If the request fails

Examples

client = Tink.client(scope: "transactions:write")

# Real-time transaction ingestion
{:ok, result} = Tink.Connector.ingest_transactions(client, "test-user-1", %{
  type: "REAL_TIME",
  auto_book: false,
  override_pending: false,
  transaction_accounts: [
    %{
      external_id: "checking-001",
      balance: 14955.0,
      reserved_amount: 0.0,
      transactions: [
        %{
          external_id: "txn-#{System.unique_integer([:positive])}",
          amount: -45.50,
          date: System.system_time(:millisecond),
          description: "Coffee Shop",
          type: "DEFAULT",
          pending: false,
          payload: %{merchant: "Starbucks"}
        }
      ]
    }
  ]
})

# Batch transaction ingestion
{:ok, result} = Tink.Connector.ingest_transactions(client, "test-user-1", %{
  type: "BATCH",
  transaction_accounts: [
    %{
      external_id: "checking-001",
      balance: 15000.0,
      transactions: generate_month_of_transactions()
    }
  ]
})

Transaction Types

  • DEFAULT - Standard transaction
  • CREDIT_CARD - Credit card transaction
  • TRANSFER - Transfer between accounts
  • PAYMENT - Payment transaction
  • WITHDRAWAL - Cash withdrawal
  • DEPOSIT - Deposit

Update Types

REAL_TIME

  • Immediate processing
  • Updates balances in real-time
  • Use for live transaction feeds

BATCH

  • Bulk processing
  • Efficient for large datasets
  • Use for historical data import

Required Scope

transactions:write