Forecasts & Payday
Overview
The prod-insight-api Lambda exposes two related capabilities via its REST API:
-
Forecasts (
GET /{user_id}/forecasts) — a combined view of a user’s recurring income, recurring expenses, and ritual expenses, assembled either from Pave-cached data or from real-time FloatMe-generated analysis depending on GrowthBook flags. -
Payday prediction (called internally during float creation and by
GET /{user_id}/insights/employment/payday) — predicts the user’s next payday date using up to three algorithms run in parallel and compared before returning a result.
Both capabilities depend on the same upstream data sources: Pave-cached insight entities in DynamoDB, employment records in RDS, and recent transactions from the Transactions Service.
Forecasts (GET /{user_id}/forecasts)
The forecasts endpoint assembles a Forecasts response from income sources, recurring expenses, and ritual expenses. Two data paths exist and are controlled by GrowthBook flags:
| Path | Flag(s) | Behaviour |
|---|---|---|
Pave path (default) |
|
Reads |
FloatMe path (FM-generated) |
|
Fetches the user’s last 90 days of transactions from the Transactions Service and runs the FloatMe recurring detection algorithm directly. Does not use Pave-cached data. |
When insights.fm_forcasts.api.send_fm_generated = true for the user and insight.pave.decom.enabled = false, both forecasts are computed: the FloatMe result is saved to DynamoDB as a CASHFLOW_ANALYTICS data-capture record and the FloatMe response is returned to the caller. When neither flag is set, only the Pave forecast is computed and returned.
FloatMe Recurring Detection
The FloatMe forecasts algorithm analyses raw transaction history directly, without Pave:
-
Fetches up to 90 days of transactions from the Transactions Service.
-
Groups transactions by account, then by transaction name.
-
Within each group, separates credits (income) from debits (expenses) before analysis — this prevents income transactions with the same merchant name as an expense from being mixed.
-
Applies a Jaro-Winkler similarity threshold (≥ 0.90) to group similarly-named transactions together.
-
For expense groups: confirms recurrence by checking that transactions in the group span multiple calendar dates; builds a
RecurringExpenseentity if recurring. -
For income groups: confirms recurrence similarly and validates against Plaid category data.
Data-Capture Writes
When the FloatMe path runs alongside the Pave path, the result is written to the fmdatacapture DynamoDB table with:
| Field | Value |
|---|---|
Event name |
|
Sort key |
|
TTL |
2 days (172,800 seconds) |
This is a shared cross-service data-capture table — the Insight Service is a producer, not the owner.
Payday Prediction
When It Runs
Payday prediction (EvaluateNextPayday) is called in two contexts:
-
During float creation — called by the Float Service with a non-empty
loan_id. In this case, all three prediction algorithms run and results are saved to DynamoDB + data-capture. -
Direct API query —
GET /{user_id}/insights/employment/paydaycalls it with an emptyloan_id. In this case, results are computed but not persisted (no DynamoDB write, no data-capture).
Three Prediction Algorithms
All three run when loan_id is provided and insight.payday_prediction.v2.enabled = true for the user; when that flag is false (or not enabled), only Pave and FM Legacy run even if loan_id is provided. Only Pave and FM Legacy run when loan_id is empty.
| Algorithm | Key | Description |
|---|---|---|
Pave |
(always runs) |
Fetches the user’s |
FloatMe Legacy (FM) |
(always runs) |
Uses the user’s RDS employment record ( |
FloatMe V2 |
|
Fetches up to 93 days of transactions from the Transactions Service, filters them to payroll-like candidates using four passes (non-integer amounts, recurring names, override keywords, blacklist exclusions), then runs a 5-phase DOW/DOM analysis pipeline to predict the cadence and next payday date. Does not use employment records or Pave. See FloatMe V2 Payday Analyzer for the full algorithm walkthrough. |
Result Selection
After all algorithms run, the returned prediction is chosen in this order:
insight.payday_prediction.v2.rollout = true for user AND v2 produced a result
└─▶ return V2 prediction
Pave produced a non-empty payday AND no Pave error
└─▶ return Pave prediction
(fallback)
└─▶ return FM Legacy prediction
All three results (and any errors) are logged for offline comparison via a Datadog metric (insight.payday.comparison, insight.payday.v2.comparison).
Persistence (when loan_id is provided)
| Store | What is written |
|---|---|
|
The winning prediction (Pave or FM Legacy) along with both predictions' instant, standard, and extended collection payday dates. Written via |
|
Three entries per evaluation:
|
Collection Date Calculation
The FM Legacy predictor calculates four collection dates beyond the raw payday:
| Field | Buffer from request date |
|---|---|
|
Next payday after |
|
Next payday after |
|
Next payday after |
|
Same as |
GrowthBook Flags
| Flag Key | Default | Effect |
|---|---|---|
|
|
Disables the Pave data path for forecasts entirely; always uses the FloatMe-generated path |
|
|
Per-user flag: when |
|
|
Per-user flag: enables the V2 (transaction-history-based) payday algorithm during float creation |
|
|
Per-user flag: when true, returns V2 results to the caller instead of Pave/FM Legacy results |
Related Pages
-
Architecture — System context and Lambda inventory
-
FloatMe V2 Payday Analyzer — Full walkthrough of the V2 algorithm (filter pipeline, DOW/DOM grids, 5-phase prediction)
-
Pave Mining — How recurring, ritual, and income entities are written to DynamoDB by the miner
-
DynamoDB Tables —
payday,recurring,ritual,incomeentity schemas -
PostgreSQL Schema — Employment table used by FM Legacy predictors
-
Feature Flags — Full GrowthBook flag reference