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

txn-service.feeder

Detail-Type

new_account

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 (AlertSettingMax = 5000 cents), the account is not marked as main (IsMain = false), or both available and current balances are zero.

Cooldown

The user was already sent an alert within the last 7 days (checked against BalanceAlertState.UpdatedDate in DynamoDB).

User opt-out

The user’s LowBalanceAlert setting (from the user-balance-settings legacy DynamoDB table) is nil, meaning they have not configured a threshold and have opted out of alerts.

Above threshold

The account balance is above the user’s configured LowBalanceAlert threshold (stored in dollars, compared in cents).

Float ineligible

The Underwriting Service returns Approved = false for a zero-amount float eligibility check.

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

prod-pave DynamoDB (alert entity)

Read: check last alert date and sent status. Write: record alert sent with balance snapshot.

user-balance-settings DynamoDB (legacy, us-east-1)

Read-only: fetch user’s configured low-balance threshold.

locks DynamoDB (legacy, us-east-1)

Read/write: distributed per-user lock to prevent concurrent processing.

Underwriting Service

Read-only: float eligibility check (FloatRequest with amount=0).

Segment

Write: dispatch low_balance_alert push notification.