Insights

Insights gives you a structured financial profile of a user, derived from their bank transactions. Instead of working with raw transaction data, you receive pre-calculated statistics and high-level indicators that are ready to use directly in your business logic — credit decisions, affordability assessments, mortgage eligibility checks, or any other financial evaluation.

What you can build with this

Insights turns a user's raw bank transactions into a structured financial summary — aggregated amounts per category per time window, ready to use directly in your application.

A typical output looks like this:

Category July August September Total Count Average per transaction …
Salary €2,841 €2,921 €2,980 €8,742 3 €2,914
Mortgage repayment −€1,150 −€1,150 −€1,150 −€3,450 3 −€1,150
Groceries −€412 −€388 −€396 −€1,196 14 −€85
…

You choose the time window and the category scope. Insights handles the aggregation.

Requesting insights

GET {baseUrl}/enrichment/insights?type=user&categoryId=Gambling%20%26%20lotteries
Authorization: Bearer <access token for the user>

All query parameters are optional and can be combined:

Query parameter Values Meaning
type user | account user: all of the user's accounts together. account: one insight per account. Without it, both are returned.
accountId an account id Only the insight of this account.
categoryId a configured category id Only this category. * is the total over all categories.
periodId a period id (see Time windows) or YYYY-MM Only this period. An unknown period returns an empty list, not an error.

categoryId and periodId filter the categories and their statistics only. financialIndicators are always returned in full, regardless of these filters.

Request insights once insightsStatus on the account status endpoint is COMPLETED, and only use an insight whose processing.status is COMPLETED (see Status). The response contains a pagination object that is currently always empty: all matching insights are returned in a single response.

What the API returns

Here is a complete response for a user, filtered to all transactions (categoryId=*) in December 2025:

GET /enrichment/insights?type=user&categoryId=*&periodId=2025-12

{
  "data": [
    {
      "schemaVersion": 3,
      "insightId": "8b1f0c2e-4a7d-4e1b-9c55-2f6a1d0e7b31",
      "type": "user",
      "userId": "7d41c9e2-1b3a-4f60-8e2d-5a9c0b7f3e14",
      "tenantId": "tenant-a",
      "tenantAppId": "app-1",
      "tenantUserId": "customer-48213",
      "accountIds": [
        "31b0a6d4-9e2f-4c71-b8a3-0d5e6f7a8b9c",
        "8c2e4f1a-7b3d-4e9c-a6f0-1d2e3c4b5a69"
      ],
      "providers": ["ING", "RABOBANK"],
      "created": "2026-10-13T07:41:02.000Z",
      "lastUpdated": "2026-10-13T07:41:02.000Z",
      "processing": {
        "status": "COMPLETED",
        "created": "2026-10-13T07:40:11.000Z",
        "lastUpdated": "2026-10-13T07:41:02.000Z"
      },
      "financialIndicators": [
        {
          "name": "INCOME_SALARY_OR_PENSION",
          "periodId": "12-MONTHS",
          "period": {
            "id": "12-MONTHS",
            "type": "PERIOD",
            "unit": "months",
            "amount": 12,
            "fullPeriodsOnly": false
          },
          "result": 3650.0
        }
      ],
      "categories": [
        {
          "id": "*",
          "type": "external",
          "statistics": [
            {
              "categoryId": "*",
              "categoryType": "external",
              "periodId": "2025-12",
              "period": {
                "type": "INDIVIDUAL-MONTHS",
                "unit": "months",
                "amount": 1,
                "fullPeriodsOnly": false,
                "periodStart": "2025-12-01",
                "periodEnd": "2025-12-31",
                "firstTransactionDate": "2025-12-01T00:00:00.000Z",
                "lastTransactionDate": "2025-12-31T00:00:00.000Z"
              },
              "transactions": {
                "count": 74,
                "negativeCount": 61,
                "positiveCount": 13,
                "minAmount": -1250.0,
                "maxAmount": 2950.0,
                "totalAmount": 237.13,
                "averageAmount": 3.2,
                "totalPositiveAmount": 3650.0,
                "averagePositiveAmount": 280.77,
                "totalNegativeAmount": -3412.87,
                "averageNegativeAmount": -55.95
              },
              "recurringTransactions": {
                "count": 9,
                "negativeCount": 8,
                "positiveCount": 1,
                "minAmount": -1250.0,
                "maxAmount": 2950.0,
                "totalAmount": 962.5,
                "averageAmount": 106.94,
                "totalPositiveAmount": 2950.0,
                "averagePositiveAmount": 2950.0,
                "totalNegativeAmount": -1987.5,
                "averageNegativeAmount": -248.44
              }
            }
          ]
        }
      ]
    }
  ],
  "pagination": {}
}

data contains one insight per scope: one for the user and, without a type filter, one per account. Each entry in categoriesrepresents one transaction category. Within it, statistics contains one entry per time window. Each statistic gives you the aggregated figures for that category in that period.

In this example, the user received 13 incoming payments in December, totalling €3,650. Only one of them — €2,950 — was identified as recurring. If you are assessing structural income, recurringTransactions.totalPositiveAmount gives you €2,950, not €3,650. The remaining €700 arrived as incidental deposits.

How to read the response

User level vs. account level

Insights are available at two scopes:

  • User level (type: user) — statistics aggregated across all connected bank accounts. Use this for a complete financial picture. A user insight lists the included accounts in accountIds and the banks in providers.
  • Account level (type: account) — statistics scoped to a single account. Use this when you need to analyse one bank separately, for example to distinguish a salary account from a savings account. An account insight contains a single accountId and provider.

Filter using the type and accountId query parameters. Without type, both the user insight and the account insights are returned.

Categories

Which categories an insight contains is configured per tenant. A category is either:

  • internal — Ockto's own classification, or
  • external — your own category scheme.

The type of each category (and categoryType of each statistic) tells you which one it is. The category * is always the total over all transactions.

Example configuration:

Category id Meaning
* Total over all transactions
Gambling & lotteries Gambling and lottery spend
Debt collection, Bailiff & seizure Debt collection and bailiff payments
Recurring savings transfer, To savings account, From savings account Savings
To investment account, From investment account Investments
To crypto account, From crypto account Crypto

Category ids can contain spaces and special characters, so URL-encode them in the categoryId parameter — for example Gambling%20%26%20lotteries.

Time windows

Every statistic is linked to a periodId that defines the time window it covers. You can request all periods at once or filter to one with the periodId query parameter. Which periods are available is configured per tenant. The default set is:

periodId Window
YYYY-MM One statistic per calendar month, for up to the last 12 completed months. The current month is left out; months without transactions are filled with zeros.
90-DAYS Rolling 90 days
180-DAYS Rolling 180 days
12-MONTHS Rolling 12 months
FULL-HISTORY All collected data, from the first to the last booking date

The period object on each statistic describes the window:

Field Meaning
type INDIVIDUAL-MONTHS (monthly statistics), PERIOD (rolling windows) or FULL-HISTORY
unit / amount Length of the window, for example days / 90 or months / 1
fullPeriodsOnly Whether only complete periods are included
periodStart / periodEnd First and last day of the window, both inclusive. Use these to see exactly which dates are covered. Can be null for FULL-HISTORY on an account without transactions.
firstTransactionDate / lastTransactionDate Date and time of the first and last transaction within the window, or null if there are none

The difference between the two pairs matters: periodStart and periodEnd tell you which dates the aggregation covers, while firstTransactionDate and lastTransactionDate tell you how much actual data falls inside that window.

All transactions vs. recurring

Each statistic contains two parallel sets of figures:

  • transactions — all transactions in the category and period, and
  • recurringTransactions — only the transactions identified as recurring.

The difference matters when you want to distinguish structural patterns from incidental ones. A salary that arrives every month appears identically in both sets. A one-off payment of the same amount only appears in transactions. Use recurringTransactions when structural regularity is what you are testing for.

Available statistics per category

For each category and period, both transactions and recurringTransactions contain:

Field Meaning
count Number of transactions
positiveCount / negativeCount Number of incoming / outgoing transactions
minAmount / maxAmount Smallest and largest amount
totalAmount Net sum of all amounts (income + expenses)
averageAmount Mean amount per transaction
totalPositiveAmount / averagePositiveAmount Sum and mean of incoming amounts
totalNegativeAmount / averageNegativeAmount Sum and mean of outgoing amounts (negative)

Amounts are null when there are no transactions to aggregate — for example averagePositiveAmount in a category with only expenses, or all amounts in a month without transactions.

Financial indicators

Besides category statistics, each insight contains financialIndicators: high-level figures calculated over a given period. For example:

"financialIndicators": [
  {
    "name": "INCOME_SALARY_OR_PENSION",
    "periodId": "12-MONTHS",
    "period": {
      "id": "12-MONTHS",
      "type": "PERIOD",
      "unit": "months",
      "amount": 12,
      "fullPeriodsOnly": false
    },
    "result": 3650.0
  }
]

Each indicator has a name, the periodId and period it covers, and a result. Depending on the indicator, result is a number, a boolean, a string or null.

Financial indicators are not affected by the categoryId and periodId filters: they are always returned in full.

Example: combining statistics

Because every statistic has the same shape, you can combine them directly. Requesting categoryId=Gambling%20%26%20lotteries returns, among others, this statistic for December 2025:

{
  "categoryId": "Gambling & lotteries",
  "categoryType": "external",
  "periodId": "2025-12",
  "period": {
    "type": "INDIVIDUAL-MONTHS",
    "unit": "months",
    "amount": 1,
    "fullPeriodsOnly": false,
    "periodStart": "2025-12-01",
    "periodEnd": "2025-12-31",
    "firstTransactionDate": "2025-12-07T00:00:00.000Z",
    "lastTransactionDate": "2025-12-25T00:00:00.000Z"
  },
  "transactions": {
    "count": 2,
    "negativeCount": 2,
    "positiveCount": 0,
    "minAmount": -30.0,
    "maxAmount": -20.4,
    "totalAmount": -50.4,
    "averageAmount": -25.2,
    "totalPositiveAmount": 0,
    "averagePositiveAmount": null,
    "totalNegativeAmount": -50.4,
    "averageNegativeAmount": -25.2
  },
  "recurringTransactions": {
    "count": 0,
    "negativeCount": 0,
    "positiveCount": 0,
    "minAmount": null,
    "maxAmount": null,
    "totalAmount": null,
    "averageAmount": null,
    "totalPositiveAmount": null,
    "averagePositiveAmount": null,
    "totalNegativeAmount": null,
    "averageNegativeAmount": null
  }
}

Combined with the * statistic for the same month from the response above, gambling spend in December is €50.40 of €3,650.00 incoming (1.4%).

Status

Insights are calculated after the full enrichment pipeline — categorisation and recurring detection — has completed for all of a user's accounts. The calculation is triggered automatically — you do not need to initiate it.

Checking readiness

The cleanest way to know when insights are available is the account status endpoint (GET /processing-status). Its insightsStatus field tells you where the calculation stands:

insightsStatus Meaning
NOT_STARTED The calculation has not started yet
PROCESSING Calculation is in progress — do not use yet
COMPLETED Insights are available
FAILED Calculation could not complete

Insights are only available once insightsStatus is COMPLETED. There is no webhook for insight completion: poll the status endpoint until it reaches COMPLETED or FAILED.

Status of an insight

Each insight also carries its own processing.status. For a user insight, this is the lowest status across the user's accounts, so it only becomes COMPLETED once every account is done.

processing.status Meaning
RETRIEVING Transactions are still being retrieved
PROCESSING Transactions are being enriched or calculated
COMPLETED The insight is complete and ready to use
ERROR Processing failed

Always check that processing.status is COMPLETED before using the data.

We have a new documentation site with more integration guidance & a rich knowledge base Visit the new Open Banking documentation