Balance Alerts
Lambda: prod-insight-balance-alert
Trigger: SQS (prod-insight-balance-alert-event-tap, via balance_alert_rule EventBridge rule)
The balance-alert Lambda sends a low-balance push notification to users who have just linked a new bank account, if their balance is below a user-configured threshold and they meet float eligibility requirements. Its goal is to surface the FloatMe advance offer at the moment a user’s account balance is freshest.
Trigger and Event Source
The Lambda is triggered by the balance_alert_rule EventBridge rule, which matches events from the Transactions Service:
| EventBridge Field | Value |
|---|---|
Source |
|
Detail-Type |
|
These events are routed from the default EventBridge bus to the prod-insight-balance-alert-event-tap SQS queue. Each SQS message body is a wrapped CloudWatch/EventBridge event whose detail contains a Transaction-compatible account object including user_id, IsMain, and Balances.
Processing Flow
Messages are processed concurrently using goroutines across three pipeline stages (parse → handle → collect failures):
balance_alert_event_tap SQS message
└─▶ ParseEvents (deserialize EventBridge wrapper → Account)
└─▶ Handler (per-account goroutine)
├─ balance.Disregard()? → skip
│ (balance > $50.00, not main account, or available+current == 0)
├─▶ DynamoDB lock.LockProcess(user_id)
├─▶ DynamoDB BalanceAlertRepo.Get(user_id)
│ └─ alertedPastWeek()? → skip (alerted within last 7 days)
├─▶ DynamoDB user-balance-settings.GetSetting(user_id)
│ └─ LowBalanceAlert == nil → opted out → skip
├─ balance.AboveThreshold()? → skip
│ (balance > user's configured threshold)
├─▶ Underwriting Service FloatRequest(user_id, amount=0)
│ └─ not approved → skip
├─▶ DynamoDB BalanceAlertRepo.Save(user_id, alert_sent=true, ...)
└─▶ Segment Dispatch("low_balance_alert", user_id)
(gated by notifierFlag feature flag)
Guard Conditions
The Lambda applies several guards in sequence before sending a notification. Any failing guard results in a silent skip for that account — no error, no retry.
| Guard | Condition to skip |
|---|---|
Balance disregard |
Account balance exceeds $50.00 ( |
Cooldown |
The user was already sent an alert within the last 7 days (checked against |
User opt-out |
The user’s |
Above threshold |
The account balance is above the user’s configured |
Float ineligible |
The Underwriting Service returns |
Concurrency and Locking
The Lambda uses a DynamoDB distributed lock (locks legacy table, us-east-1) to prevent concurrent alert sends for the same user. lock.LockProcess(user_id) acquires the lock before processing; lock.ReleaseProcess is deferred to release it after. This guards against duplicate alerts in cases where multiple new_account events arrive for the same user in the same SQS batch.
Notification
Notifications are dispatched via Segment.Dispatch using the low_balance_alert event type, with the account’s available and current balance included in the message payload. The dispatch is gated by a runtime boolean flag (notifierFlag) set from an environment variable — this allows alerting to be disabled without a code deployment.
The BalanceAlertState entity is written to DynamoDB before the notification is dispatched, so the 7-day cooldown is applied regardless of whether the Segment call succeeds.
Data Dependencies
| Store | Access |
|---|---|
|
Read: check last alert date and sent status. Write: record alert sent with balance snapshot. |
|
Read-only: fetch user’s configured low-balance threshold. |
|
Read/write: distributed per-user lock to prevent concurrent processing. |
Underwriting Service |
Read-only: float eligibility check ( |
Segment |
Write: dispatch |
Related Pages
-
Architecture — System context and Lambda inventory
-
Event Flows —
new_accountevent source and routing rule -
DynamoDB Tables —
alertentity schema and legacy table references