Get the FREE Ultimate OpenClaw Setup Guide →

enable-banking-api

npx machina-cli add skill peerjakobsen/smartspender/enable-banking-api --openclaw
Files (1)
SKILL.md
8.1 KB

Enable Banking API

Purpose

Provides domain knowledge about the Enable Banking Open Banking API — endpoints, authentication, session lifecycle, transaction structure, rate limits, and supported Danish banks. Reference this when orchestrating tools/eb-api.py for API-based transaction sync.

API Overview

Enable Banking provides PSD2-compliant access to bank account data across 2,500+ European banks. SmartSpender uses it as an alternative to browser automation for faster, more reliable transaction sync.

  • API Base URL: https://api.enablebanking.com
  • Authentication: JWT signed with RSA private key (PS256 algorithm)
  • Data Format: JSON, ISO 20022 inspired field names
  • Rate Limit: Max 4 account information requests per day per account (PSD2 regulation)

Authentication

JWT Structure

The eb-api.py helper handles JWT generation. Conceptually:

  • Issuer: enablebanking.com
  • Audience: api.enablebanking.com
  • Algorithm: PS256 (RSA-PSS with SHA-256)
  • Expiry: 1 hour from creation
  • Payload: includes app_id from config

Config File

Stored at ~/.config/smartspender/eb-config.json:

{
  "app_id": "your-application-id",
  "key_path": "~/.config/smartspender/private.pem"
}

Session Lifecycle

Consent Flow

  1. Initiate: eb-api.py auth --bank <name> sends POST to /auth with ASPSP details and redirect URL (https://smartspender.mentilead.com/callback.html)
  2. MitID: User authenticates in browser (auto-opened)
  3. HTTPS Relay: Bank redirects to callback.html which forwards code to http://localhost:19876/callback
  4. Capture: Localhost listener on port 19876 catches the authorization code
  5. Session: Code exchanged for session_id via POST to /sessions
  6. Active: Session provides access to account data for 90-180 days

Session States

StateMeaningAction
activeConsent valid, API accessibleProceed with sync
expiredConsent period endedRe-run auth for new consent
revokedBank-side revocationRe-run auth
no_sessionNo session file existsRun setup + auth

Session File

Stored at ~/.config/smartspender/eb-session.json:

{
  "session_id": "uuid-string",
  "accounts": [
    {
      "uid": "account-uid-string",
      "account_id": {"iban": "DK1234567890123456"},
      "name": "Lønkonto",
      "currency": "DKK",
      "product": "Checking Account"
    }
  ],
  "consent_expires": "2026-05-02T12:00:00.000Z",
  "created_at": "2026-02-02T12:00:00+00:00",
  "fetch_log": {}
}

API Endpoints

POST /auth

Initiate consent. Returns url for user to visit.

POST /sessions

Exchange authorization code for session. Returns session_id and accounts.

GET /sessions/{session_id}/accounts

List accounts linked to the session. Returns account UIDs, IBANs, names, currencies.

GET /accounts/{uid}/transactions

Fetch transactions. Parameters:

  • date_from (required): YYYY-MM-DD
  • date_to (optional): YYYY-MM-DD (defaults to today)
  • continuation_key (optional): for pagination

GET /accounts/{uid}/balances

Fetch current balances. Returns balance type, amount, currency, and date.

Response structure:

{
  "balances": [
    {
      "balance_amount": {"amount": "12543.25", "currency": "DKK"},
      "balance_type": "CLBD",
      "reference_date": "2026-02-03"
    },
    {
      "balance_amount": {"amount": "12100.00", "currency": "DKK"},
      "balance_type": "ITAV",
      "reference_date": "2026-02-03"
    }
  ]
}

Balance type codes:

TypeDescriptionUse Case
CLBDClosing booked balanceEnd of day settled — most accurate, prefer this
ITAVIntraday availableCurrent available funds — fallback if CLBD missing
XPCDExpected balanceProjected balance including pending

Parsing priority: Prefer CLBD over ITAV over XPCD. Extract balance_amount.amount as the balance value.

Transaction Structure

Enable Banking returns transactions in this JSON format:

{
  "bank_transaction_code": {"code": "12", "description": "Kortbetaling"},
  "booking_date": "2026-01-15",
  "booking_date_time": "2026-01-15T14:30:00Z",
  "credit_debit_indicator": "DBIT",
  "creditor": {"name": "FØTEX"},
  "debtor": {"name": "Peer Jakobsen"},
  "entry_reference": "5561990681",
  "remittance_information": ["Dankort-køb", "FØTEX 4123"],
  "status": "BOOK",
  "transaction_amount": {"amount": "847.50", "currency": "DKK"},
  "balance_after_transaction": {"amount": "12543.25", "currency": "DKK", "credit_debit_indicator": "CRDT"},
  "value_date": "2026-01-15"
}

Key Fields

FieldDescription
booking_dateTransaction date (already YYYY-MM-DD)
credit_debit_indicatorDBIT = debit (expense), CRDT = credit (income)
transaction_amount.amountPositive number as string
transaction_amount.currencyISO currency code
creditor.nameRecipient (present on debits)
debtor.nameSender (present on credits)
remittance_informationArray of description strings
balance_after_transactionRunning balance (for tx_hash)
statusBOOK (booked), PDNG (pending), INFO (informational)
entry_referenceBank's reference number
bank_transaction_codeTransaction type code and description

Transaction Status Values

  • BOOK: Booked, final — include in sync
  • PDNG: Pending, not yet final — skip (may change or disappear)
  • INFO: Informational, not a real transaction — skip

Only sync transactions with status: BOOK.

PSD2 Rate Limits

Regulation

PSD2 (Payment Services Directive 2) limits account information requests to 4 per day per account. This is a European banking regulation, not an Enable Banking or SmartSpender limitation.

Rate Limit Strategy

  • eb-api.py tracks fetches per account per day in the session file
  • Before each fetch, check the count. Warn at 3/4, block at 4/4
  • A single eb-api.py transactions call counts as 1 fetch regardless of pagination
  • Balances and account list queries are also rate-limited

Practical Impact

  • Sync once per day during normal use
  • If testing, be mindful of the 4/day limit
  • Rate resets at midnight UTC
  • Error when exceeded: rate_limit with message explaining the constraint

Supported Danish Banks

Enable Banking supports these Danish banks (non-exhaustive):

Major Banks

  • Nykredit
  • Danske Bank
  • Nordea
  • Jyske Bank
  • Sydbank
  • Spar Nord

Digital Banks

  • Lunar
  • Bank Norwegian

Cooperative Banks (via BEC/Bankdata)

  • Arbejdernes Landsbank
  • Vestjysk Bank
  • Ringkjøbing Landbobank
  • Sparekassen Danmark
  • Plus 50+ smaller banks

ASPSP Names

When running eb-api.py auth --bank <name>, use the bank's name as registered with Enable Banking. Common Danish bank names:

  • Nykredit (not "nykredit" — use proper case)
  • Danske Bank
  • Nordea
  • Jyske Bank
  • Sydbank
  • Lunar

Error Codes

ErrorMeaningResolution
401 UnauthorizedJWT invalid or expiredCheck RSA key and app_id
403 ForbiddenConsent missing or expiredRe-run auth for new consent
404 Not FoundInvalid session or account UIDVerify session is active
429 Too Many RequestsPSD2 rate limit exceededWait until tomorrow
503 Service UnavailableBank API temporarily downRetry later

Related Skills

  • skills/data-schemas/SKILL.md — Common transaction schema and CSV file structures including accounts.csv with EB fields

Known Limitations

  • PSD2 rate limit (4/day/account) means no real-time balance checking
  • Transaction descriptions from API may differ from browser export CSV (banks format them differently)
  • balance_after_transaction is optional — some banks don't provide it for all transaction types
  • Session can be revoked bank-side without notice (user changed consent settings)
  • ASPSP names must match Enable Banking's registry (case-sensitive)

Source

git clone https://github.com/peerjakobsen/smartspender/blob/main/skills/enable-banking-api/SKILL.mdView on GitHub

Overview

Enable Banking provides PSD2 compliant access to bank data across 2,500+ European banks. SmartSpender uses it as an API alternative to browser automation for faster, more reliable transaction sync. This skill covers endpoints, authentication, session lifecycle, and rate limits for API-based bank sync.

How This Skill Works

The eb-api.py helper handles JWT generation for authentication using PS256. After consent is granted, the flow exchanges an authorization code for a session_id, which then allows account data access via /sessions/{session_id}/accounts and subsequent endpoints to fetch transactions and balances. Data is returned in JSON with ISO style field names and a daily rate limit of four account info requests per account.

When to Use It

  • When you need fast, API-based bank synchronization instead of browser automation.
  • When you want PSD2 compliant access to accounts from 2,500+ European banks via Enable Banking.
  • When implementing the consent flow and MitID-based authentication via the eb-api.py helper.
  • When you must enforce the API rate limit of up to four account information requests per day per account.
  • When you need to manage and reuse a session for 90–180 days of access to account data.

Quick Start

  1. Step 1: Create eb-config.json at ~/.config/smartspender with your app_id and the path to private.pem.
  2. Step 2: Run eb-api.py auth --bank <name> to initiate consent and complete the MitID flow.
  3. Step 3: Exchange the authorization code for a session_id, then call GET /sessions/{session_id}/accounts and GET /accounts/{uid}/transactions as needed.

Best Practices

  • Use the eb-api.py helper to generate JWTs and keep a secure eb-config.json at ~/.config/smartspender/eb-config.json with app_id and key_path.
  • Protect the RSA private key (private.pem) and rotate keys when needed; never expose keys in logs or public repos.
  • Respect the rate limit by batching requests and implementing backoff for /accounts related calls.
  • When fetching transactions, use date_from and optional date_to, and leverage continuation_key for pagination.
  • Parse responses against the documented structure and normalize balance types, especially CLBD and ITAV, for downstream use.

Example Use Cases

  • Initiate consent with POST /auth, complete MitID in the browser, exchange code for a session, then list accounts with GET /sessions/{session_id}/accounts and fetch transactions with GET /accounts/{uid}/transactions.
  • Use GET /sessions/{session_id}/accounts to retrieve account UIDs and then call GET /accounts/{uid}/balances to obtain current balances including CLBD and ITAV.
  • Sync a Danish user's transactions for the last 30 days by passing date_from in YYYY-MM-DD format and handle pagination with continuation_key.
  • Display user friendly account names by reading the accounts array from the /sessions/{session_id}/accounts endpoint.
  • On session expiry, re-run the auth flow to obtain a new session_id and continue syncing without starting from scratch.

Frequently Asked Questions

Add this skill to your agents
Sponsor this space

Reach thousands of developers