Regular Transactions Schema
Field reference for user-scoped regular transaction series detect and result messages on Kafka.
Overview
Regular transaction series detection is user-scoped: every detect and result message includes a userId, and result egress is always shaped as { userId, accounts[] }. A user may have multiple accounts (for example, a current account and a credit card).
To request detection, publish a message to the regular-transactions detect topic. A user-scoped result is then published to the regular-transactions result topic.
Detect messages may contain one account or several accounts for the same user. Moneyhub configures how daily batch detection is grouped for your environment; see List of configurable features.
This mirrors the affordability interface: one userId in, one user-scoped message out per detect message processed.
Request message
The Kafka message value must be a JSON object:
| Field | Type | Optional | Description |
|---|---|---|---|
| userId | String (max 36) | No | User identifier whose accounts are being processed. |
| accounts | Array<Object> | Yes | Accounts to process. Required unless the message is a dated user request (below). At least one entry is required when present. |
| asOfDate | String (max 10) | Yes | Calendar date (yyyy-MM-dd) the detection should end on. See Dated detection. |
Each element of accounts[]:
| Field | Type | Optional | Description |
|---|---|---|---|
| accountId | String (max 36) | No | Account identifier to detect series for. |
| mode | String (max 11) | Yes | Detection mode: full (default) or incremental. |
| updatedAt | String | Yes | ISO 8601 timestamp. Required when mode is incremental; ignored for full. |
When asOfDate is omitted, full mode uses the configured retention window of transaction history. incremental mode uses transactions from updatedAt onwards and merges with the current series.
Dated detection
asOfDate asks for the series as they stood at the end of that calendar day, using the 12 months of stored transactions ending on that date. The whole of asOfDate is included. Transactions that have not already been submitted for those dates are not part of the run.
A dated message applies to every account in accounts[]. mode and updatedAt are not used. The run does not merge with the current series, and it does not replace them. A later detection or affordability request that omits asOfDate still uses the current series. Sending the same date again replaces the series stored for that date only.
You can omit accounts and send only userId and asOfDate. Detection then runs for every account already known for that user. If userId is missing, or the user has no accounts, the message is dead-lettered and nothing is stored.
When affordability after regular transactions is enabled, a successful dated detection also requests the affordability report for the same date. You can request that report yourself by publishing the same asOfDate to the affordability calculate topic. See Affordability schema.
If historical affordability functionality is disabled for your environment, any detect message that includes asOfDate is dead-lettered and nothing is read or stored. Messages that omit asOfDate are unchanged. A value that is not a real calendar date (yyyy-MM-dd) is rejected and is not treated as today. See Errors.
The message key should be a unique identifier (UUID recommended). It is copied to the corresponding result message.
Example (detect message — single account)
{
"userId": "550e8400-e29b-41d4-a716-446655440000",
"accounts": [
{
"accountId": "090ea264-eb48-494c-9d59-1b10b322c3e1",
"mode": "full"
}
]
}Example (detect message for a past date)
{
"userId": "550e8400-e29b-41d4-a716-446655440000",
"asOfDate": "2025-04-01",
"accounts": [
{
"accountId": "090ea264-eb48-494c-9d59-1b10b322c3e1",
"mode": "full"
}
]
}Example (dated detection for every account of a user)
{
"userId": "550e8400-e29b-41d4-a716-446655440000",
"asOfDate": "2025-04-01"
}Example (detect message — multiple accounts)
{
"userId": "550e8400-e29b-41d4-a716-446655440000",
"accounts": [
{
"accountId": "090ea264-eb48-494c-9d59-1b10b322c3e1",
"mode": "full"
},
{
"accountId": "de4be182-efb4-4aad-b221-fa29631e6653",
"mode": "incremental",
"updatedAt": "2026-06-01T12:00:00.000Z"
}
]
}Result topic
Detection results are published to the regular-transactions result topic. Failed messages are sent to the regular-transactions dead-letter topic; see Errors for the general dead-letter envelope shape.
Result message envelope
The Kafka message value is a JSON object:
| Field | Type | Optional | Description |
|---|---|---|---|
| userId | String (max 36) | No | User identifier that was requested for the detection run. |
| accounts | Array<Object> | No | Detection results per account (schema below). One entry per account processed. |
The message key is copied from the triggering detect message when present; otherwise a new UUID is generated.
Account result (accounts[])
accounts[])| Field | Type | Optional | Description |
|---|---|---|---|
| accountId | String (max 36) | No | Account identifier for which series were computed. |
| predictions | Array<Object> | No | Array of regular transaction series objects (schema below). May be empty. |
Series object (predictions[])
predictions[])| Field | Type | Optional | Description |
|---|---|---|---|
| seriesId | String (max 36) | No | The ID of the regular transaction series. UUID format. |
| accountId | String (max 36) | No | The ID of the account the transactions belong to. Matches the parent accounts[].accountId. |
| userId | String (max 36) | No | The ID of the user the transactions belong to. Matches the envelope userId. |
| type | String (max 7) | No | Whether the series contains income or payment transactions (income or payment). |
| subtype | String (max 21) | Yes | Subtype: directDebit, standingOrder, frequentVisit, committedCardPayment, or other. |
| frequency | String (max 11) | No | Frequency: yearly, quarterly, monthly, fortnightly, or weekly. |
| description | String (max 256) | No | The full description of the latest transaction in the series. Same limit as enriched transaction description. |
| cleanedDescription | String (max 256) | No | The cleaned description of the latest transaction in the series. |
| numTxMatchedInSeries | Integer | No | The number of transactions contained in the series. |
| gapLengthInFreqUnits | Integer | No | If the series contains a gap, the number of whole frequency units that are missing. |
| dateAnomaliesCount | Integer | No | The number of date anomalies detected in the series. |
| numReturnedTxs | Integer | No | The number of returned transactions (reversed or declined) contained in the series. |
| lastDate | String (max 10) | No | The date of the latest transaction in the series (yyyy-MM-dd). |
| predictedDate | String (max 10) | No | The predicted date of the next transaction in the series (yyyy-MM-dd). |
| predictedDateEarliest | String (max 10) | Yes | The earliest predicted date at which the next transaction could occur (null if not set). |
| predictedDateLatest | String (max 10) | Yes | The latest predicted date at which the next transaction could occur (null if not set). |
| predictedTxLateOrNotDetected | Boolean | Yes | Whether the next predicted transaction is overdue or not detected. May be omitted if not set. |
| predictedAmount | Number | No | The predicted amount of the next transaction (as emitted by detection; currency base units where applicable). |
| predictedAmountLower | Number | Yes | Lower bound of the predicted amount. |
| predictedAmountUpper | Number | Yes | Upper bound of the predicted amount. |
| currency | String (max 3) | Yes | ISO 4217 currency code. Same limit as enriched transaction currency. |
| counterpartyId | String (max 36) | Yes | L3 counterparty identifier from the first matched transaction that has counterparty enrichment. UUID format. |
| predictedCategoryId | String (max 3) | No | The ID of the predicted category. Same limit as enriched transaction l2CategoryId. |
| l3CounterpartyCategory | String (max 32) | Yes | The counterparty analysis category identifier. Same limit as counterparty l3CounterpartyCategory. May be null. |
| transactions | Array<Object> | No | Matched historical transactions in the series (schema below). |
Transaction object (transactions[])
transactions[])| Field | Type | Optional | Description |
|---|---|---|---|
| uid | String (max 36) | No | Transaction identifier. |
| date | String (max 10) | No | Transaction date (yyyy-MM-dd). |
| description | String (max 256) | No | Transaction description. |
| amount | Number | No | Transaction amount. |
| categoryId | String (max 3) | No | L2 category identifier. |
| cleanedDescription | String (max 256) | No | Cleaned description. |
| txCode | String (max 64) | Yes | Provider transaction type code. |
| cardPresent | Boolean | Yes | Whether the transaction was card-present. |
| l3CounterpartyCategory | String (max 32) | Yes | Counterparty analysis category identifier. May be null when not available. |
Example (result message body)
{
"userId": "550e8400-e29b-41d4-a716-446655440000",
"accounts": [
{
"accountId": "090ea264-eb48-494c-9d59-1b10b322c3e1",
"predictions": [
{
"seriesId": "45f004dc-254f-5d09-b07f-8d4de4487781",
"accountId": "090ea264-eb48-494c-9d59-1b10b322c3e1",
"userId": "550e8400-e29b-41d4-a716-446655440000",
"type": "payment",
"subtype": "frequentVisit",
"frequency": "quarterly",
"description": "Dentist Checkup",
"cleanedDescription": "Dentist Checkup",
"numTxMatchedInSeries": 4,
"gapLengthInFreqUnits": 0,
"dateAnomaliesCount": 0,
"numReturnedTxs": 0,
"lastDate": "2026-01-20",
"predictedDate": "2026-04-20",
"predictedDateEarliest": "2026-04-20",
"predictedDateLatest": "2026-04-21",
"predictedTxLateOrNotDetected": false,
"predictedAmount": -65,
"predictedAmountLower": -65,
"predictedAmountUpper": -65,
"currency": "GBP",
"counterpartyId": "203d5a9a-d4dd-5ce5-ab60-60f740336dea",
"predictedCategoryId": "11",
"l3CounterpartyCategory": null,
"transactions": [
{
"uid": "TXN-090ea264-775",
"date": "2026-01-20",
"description": "Dentist Checkup",
"amount": -65,
"categoryId": "11",
"cleanedDescription": "Dentist Checkup",
"txCode": null,
"cardPresent": false,
"l3CounterpartyCategory": null
},
{
"uid": "TXN-090ea264-774",
"date": "2025-10-20",
"description": "Dentist Checkup",
"amount": -65,
"categoryId": "11",
"cleanedDescription": "Dentist Checkup",
"txCode": null,
"cardPresent": false,
"l3CounterpartyCategory": null
}
]
}
]
},
{
"accountId": "de4be182-efb4-4aad-b221-fa29631e6653",
"predictions": []
}
]
}Updated about 1 month ago
