Affordability Result Schema

Field reference for user-scoped affordability report messages on the Kafka affordability-result topic.

Overview

Affordability reports are produced per user. A user may have multiple accounts (for example, a current account and a credit card); the report aggregates enriched transactions and regular-transaction predictions across all of that user's accounts.

To request a report, publish a message to the affordability calculate topic. The affordability worker reads the user's data from storage, computes the report, and publishes the result to the affordability result topic.

When ENABLE_REGULAR_TRANSACTIONS_REQUEST_AFFORDABILITY_REPORT_ON_COMPLETION is enabled for regular transactions (see Configuration in the regular transactions schema), the regular-transactions worker publishes calculate messages automatically after successful detection; manual publishing is not required in that case.

Request message (affordability-calculate)

Deployment topologyTopic nameDefault consumer group ID
Internal brokeraffordability-calculateaffordability-calculate
External brokerexternal-affordability-calculateexternal-affordability-calculate

The Kafka message value must be a JSON object:

FieldTypeOptionalDescription
userIdString (max 36)NoUser identifier for whom the report is requested.

The message key should be a unique identifier (UUID recommended). It is copied to the corresponding result message.

Example (request message)

{
  "userId": "550e8400-e29b-41d4-a716-446655440000"
}

Result topic (affordability-result)

The affordability worker publishes successful calculation results to the affordability result topic.

Deployment topologyTopic nameDefault consumer group ID
Internal brokeraffordability-resultaffordability-result
External brokerexternal-affordability-resultexternal-affordability-result

Topic names are configurable per environment. Failed messages are sent to the affordability dead-letter topic (affordability-dead-letter / external-affordability-dead-letter); see Errors for the general dead-letter envelope shape.

Result message envelope

The Kafka message value is a JSON object produced by the affordability worker after processing an affordability-calculate message:

FieldTypeOptionalDescription
userIdString (max 36)YesUser identifier from the calculate request (present for user-scoped runs).
accountIdString (max 36)YesAccount identifier from the calculate request (present for account-scoped runs).
fullAffordabilityReportObjectNoThe affordability report (schema below). Derived from @mft/affordability-calculator output.

At least one of userId or accountId is present, matching the calculate request. The message key is copied from the triggering affordability-calculate message when present; otherwise a new UUID is generated.

Report object (fullAffordabilityReport)

The report analyses up to 12 months of transaction history across accounts in scope for the request, ending on the calculation date. Amounts are plain numbers in major currency units (for example, pounds rather than pence).

Missing sections may be omitted when the calculator does not produce them.

FieldTypeOptionalDescription
incomeObjectYesIncome analysis, including regular income detection.
expenditureObjectYesExpenditure medians by affordability category.
transfersObjectYesNet and outgoing transfer medians.
gamblingObjectYesGambling outflow median.
loansObjectYesOpened loan details and repayment median.
returnedPaymentsObjectYesReturned or reversed direct-debit activity.
overdraftsObjectYesOverdraft usage per account.

income

FieldTypeOptionalDescription
medianNumberYesMedian monthly income (excluding transfers).
medianIncludingTransfersNumberYesMedian monthly income including transfers.
amountConsistencyNumberYesConsistency score for income amounts (0–1).
categoriesUsedArray<Object>YesIncome category IDs referenced in the analysis.
longevityObjectYesHow long income has been observed.
primaryIncomeAccountObjectYesAccount receiving the primary income stream.
regularIncomeObjectYesWhether regular income was detected, plus predicted monthly amount.

income.categoriesUsed[]

FieldTypeOptionalDescription
uidString (max 3)NoLevel 2 category ID.
keyStringNoAffordability category key.

income.longevity

FieldTypeOptionalDescription
consecutiveIncomeObjectYesLongest run of consecutive income months.
incomePeriodLengthObjectYesTotal span of observed income.

Each longevity sub-object has a months integer field.

income.primaryIncomeAccount

FieldTypeOptionalDescription
uidString (max 36)NoAccount identifier.

income.regularIncome

FieldTypeOptionalDescription
isDetectedBooleanYesWhether regular income streams were used in monthly predictions.
predictedObjectYesAggregated regular income prediction.

income.regularIncome.predicted.monthly

FieldTypeOptionalDescription
amountNumberNoPredicted total monthly regular income.

expenditure

Each category object contains a median amount (negative for outflows).

FieldTypeOptionalDescription
cashWithdrawalsObjectYesATM and cash-withdrawal spending.
discretionaryObjectYesDiscretionary spending.
fixedObjectYesFixed commitments such as rent and loan repayments.
flexibleObjectYesFlexible non-discretionary spending.
nonDiscretionaryObjectYesEssential non-discretionary spending.

Expenditure category object

FieldTypeOptionalDescription
medianNumberNoMedian monthly spend (negative for outflows).

transfers

FieldTypeOptionalDescription
medianNumberYesMedian net monthly transfer amount.
outObjectYesOutgoing transfers.

transfers.out

FieldTypeOptionalDescription
medianNumberNoMedian monthly outgoing transfers (negative).

gambling

FieldTypeOptionalDescription
outObjectYesGambling stakes.

gambling.out

FieldTypeOptionalDescription
medianNumberNoMedian monthly gambling spend (negative).

loans

FieldTypeOptionalDescription
openedObjectYesRecently opened loans by type and period.
repaymentsObjectYesLoan repayment median.

loans.opened.details[]

One entry per loan type and loan window (30, 90, 180, and 365 days).

FieldTypeOptionalDescription
typeStringNoLoan type: dca/dmp, bnpl, unsecured, hcstc, secured, or unknown.
countIntegerNoNumber of loans opened in the period.
periodObjectNoLoan window (unit: days; value: 30, 90, 180, or 365).
totalObjectNoAggregated amounts (borrowed, repaid) in major currency units for loans opened in the period.

loans.repayments

FieldTypeOptionalDescription
medianNumberNoMedian monthly loan repayments (negative).

returnedPayments

FieldTypeOptionalDescription
countIntegerYesTotal number of returned-payment transactions in the analysis period.
periodObjectYesLength of the returned-payments analysis (months).
detailsArray<Object>YesReturned-payment counts grouped by category and period.

returnedPayments.details[]

One entry per affordability category and period (30, 90, 180, and 365 days).

FieldTypeOptionalDescription
categoryStringNoAffordability category key for the returned payments.
countIntegerNoNumber of returned payments in the period.
periodObjectNoRolling period (unit: days; value: 30, 90, 180, or 365).
totalObjectNoAggregated returned amount (returned) in major currency units.

overdrafts.usage.details[]

FieldTypeOptionalDescription
accountUidString (max 36)NoAccount identifier.
daysInOverdraftIntegerNoTotal days spent in overdraft in the last 90 days.
daysInUnarrangedOverdraftIntegerYesDays in unarranged overdraft in the last 90 days.
earliestBalanceDateStringNoEarliest balance date available for overdraft analysis (ISO 8601).
periodsInOverdraftArray<Object>NoContiguous overdraft periods in the last 90 days.

overdrafts.usage.details[].periodsInOverdraft[]

FieldTypeOptionalDescription
startString (max 10)NoPeriod start date (yyyy-MM-dd).
endString (max 10)NoPeriod end date (yyyy-MM-dd).
daysIntegerNoNumber of days in the overdraft period.
minimumBalanceNumberNoLowest balance reached during the period.
unarrangedOverdraftObjectYesUnarranged overdraft sub-periods within this period.

unarrangedOverdraft

FieldTypeOptionalDescription
daysIntegerNoTotal unarranged overdraft days in the parent period.
periodsArray<Object>NoContiguous unarranged overdraft sub-periods.

Each periods[] element has start, end, days, and minimumBalance fields (plain-number balances).

Example (result message body)

{
  "userId": "550e8400-e29b-41d4-a716-446655440000",
  "fullAffordabilityReport": {
    "income": {
      "median": 2403.74,
      "medianIncludingTransfers": 2403.74,
      "amountConsistency": 0.99,
      "categoriesUsed": [
        {
          "uid": "27",
          "key": "deposits"
        }
      ],
      "longevity": {
        "consecutiveIncome": {
          "months": 12
        },
        "incomePeriodLength": {
          "months": 12
        }
      },
      "primaryIncomeAccount": {
        "uid": "b874c5fe-3dc1-4752-a93f-26b99320430c"
      },
      "regularIncome": {
        "isDetected": true,
        "predicted": {
          "monthly": {
            "amount": 2403.74
          }
        }
      }
    },
    "expenditure": {
      "cashWithdrawals": {
        "median": -161.32
      },
      "discretionary": {
        "median": -516.77
      },
      "fixed": {
        "median": -1455.11
      },
      "flexible": {
        "median": -808.26
      },
      "nonDiscretionary": {
        "median": -1748.96
      }
    },
    "transfers": {
      "median": -51.22,
      "out": {
        "median": -203.25
      }
    },
    "gambling": {
      "out": {
        "median": -100.26
      }
    },
    "loans": {
      "opened": {
        "details": []
      },
      "repayments": {
        "median": -283.35
      }
    },
    "returnedPayments": {
      "count": 0,
      "period": {
        "months": 11
      },
      "details": []
    },
    "overdrafts": {
      "usage": {
        "details": []
      }
    }
  }
}

Did this page help you?