Float Lifecycle
A float is a short-term advance disbursed to a user. Every float progresses through a set of statuses from the moment it is created until it is either fully collected or written off. This page covers the end-to-end lifecycle: creation, collection, manual payback, and terminal states.
Float Statuses
Status Transitions
SCHEDULING ──[T-1 Day: no valid debit card, ACH submitted]─────► ACHSENT SCHEDULING ──[Due Date: pinless success]───────────────────────► COMPLETED SCHEDULING ──[Due Date: all attempts fail]─────────────────────► RETRY SCHEDULING ──[Manual payback]──────────────────────────────────► COMPLETED ACHSENT ──[ACH settles: FLOAT_DEBIT_COMPLETED event]────────► COMPLETED ACHSENT ──[ACH returned: FLOAT_DEBIT_RETURNED event]────────► RETRY ACHSENT ──[Chargeback: FLOAT_CREDIT_RETURNED event]─────────► DEFAULTED RETRY ──[Daily Retry or webhook: pinless/ACH success]─────► COMPLETED RETRY ──[Daily Retry or webhook: ACH submitted]───────────► ACHSENT RETRY ──[ACH attempt limit reached]───────────────────────► DEFAULTED RETRY ──[> 90 days past due]──────────────────────────────► DEFAULTED RETRY ──[No valid Plaid account and no valid debit card]──► UNCOLLECTABLE UNCOLLECTABLE ──[Daily Retry re-evaluation, payment method found]► RETRY COMPLETED (terminal) DEFAULTED (terminal)
Status Reference
| Status | Description |
|---|---|
|
Initial status set at float creation. The disbursement has been submitted and the float is awaiting its due date. |
|
An ACH debit has been submitted to the payment processor. The float remains in this state until the ACH Handler Lambda receives a settlement callback from the |
|
The float has been fully collected. Set by the ACH Handler on a successful ACH settlement, or directly by the collections engine on a successful pinless debit or manual payback. |
|
A collection attempt was made but failed. The float will be retried by the Daily Retry scheduler or by the webhook workers on the next qualifying income or balance event. |
|
The float has exceeded the maximum ACH attempt count, is more than 90 days past due, or was charged back. Terminal. |
|
No valid Plaid account and no valid primary debit card exist for the user. Re-evaluated on each subsequent Daily Retry run — not a permanent terminal state. |
Disbursement
When a float is created via POST /{user_id}/floats, the API Lambda runs a series of validation and eligibility checks before submitting the disbursement. All steps are synchronous; the float record is not written to RDS until disbursement succeeds.
POST /{user_id}/floats
│
▼
Get user profile (User Service)
│
▼
User state is not restricted? ──No──► 400
User status is ACTIVE? ──No──────────► 400
Email verified (if enforced)? ──No──► 400
│ Yes
▼
Get subscription status (Subscription Service)
PAST_DUE or STALE? ──Yes──► 400
│ No
▼
Acquire distributed lock (DynamoDB)
│
▼
Get linked bank account (Transactions Service)
│
▼
Check requirements bypass (DynamoDB)
│ (bypass skips underwriting eligibility check below)
▼
Run fraud check
Connectivity error? ──Yes──► 500
│ No
▼
Get float profile and fee (Underwriting Service)
Float not enabled for user? ──Yes──► 400
│ No
▼
Get next payday and payback date (Insight Service)
Custom payback date requested? ──validate within allowed window──►
│
▼
Check underwriting eligibility (Underwriting Service)
Not approved and no bypass? ──Yes──► 400
│ Approved or bypassed
▼
Submit disbursement (Payments Service)
Type routes to: PINLESS credit, RTP credit, or ACH credit
RTP error + fallback enabled? ──Yes──► retry as PINLESS
│ Success: returns confirmationID and resolved loan type
▼
Write float record to RDS
DebitStatus: SCHEDULING
CreditID: confirmationID from Payments Service
DebitDate: computed payback date
│
▼
Send confirmation email (Segment + Iterable with retry)
PINLESS/RTP ──► instant transfer template
NORMAL ──────► standard ACH template
│
▼
Publish user_float_created event (EventBridge)
│
▼
Return 201 Created
| If the disbursement succeeds but the RDS write fails, the float record is lost while funds have already moved. The fraud check result and confirmation email are sent only after the RDS write succeeds. |
Collection
After a float is created, the collections engine takes over. It operates through three scheduled stages and two webhook-triggered paths.
See Collections Engine for detailed flow diagrams covering T-1 Day, Due Date, Daily Retry, and webhook-triggered collection.
ACH settlement callbacks are processed by the ACH Handler Lambda. See ACH Processing for how settlement events update float status.
Manual Payback
A user can pay back an active float before the due date via POST /{user_id}/floats/{float_id}/payback. The request is routed directly to the Payments Service; the Float Service does not directly update the float status.
POST /{user_id}/floats/{float_id}/payback
│
▼
Submit payback (Payments Service)
│
├── ErrFloatNotPayable ──► 404
├── Other error ──────────► 500
│
▼
Check resulting debit status:
│
├── ACHSENT or COMPLETED ──► 202 Accepted
└── Other ─────────────────► 500
The float’s final COMPLETED status is set asynchronously when the ACH Handler processes the corresponding settlement event from the prod-payments Kinesis stream.
Payback for Account Reactivation
When a user reactivates a cancelled account, all past-due floats must be paid off before reactivation can proceed. This is handled by POST /{user_id}/floats/payback/reactivate.
POST /{user_id}/floats/payback/reactivate
│
▼
Get all active floats for user
Active statuses: RETRY, DEFAULTED, SCHEDULING, ACHSENT, UNCOLLECTABLE
│
▼
Filter to past-due floats (DebitDate <= now)
Calculate total responsibility = sum(amount + fee) across all past-due floats
│
▼
For each past-due float:
│
├── Submit reactivation payback (Payments Service)
│ │
│ ├── ErrFloatNotPayable ──► log warning, subtract from
│ │ total responsibility, continue
│ ├── Other error ──────────► 500
│ │
│ └── Success
│ │
│ ├── DebitStatus != COMPLETED ──► 402 Payment Required
│ │ {AmountCollected, AmountRemaining, MaskedCardNumber}
│ │
│ └── COMPLETED ──► add to collected, continue to next float
│
▼
Return 200 OK
{AmountCollected, AmountRemaining, AmountTotal, MaskedCardNumber}
| A 402 response indicates partial collection — some floats were paid but at least one could not be collected. The response body includes amounts for both collected and remaining, which the caller uses to determine whether reactivation can proceed. |
Default & Uncollectable
DEFAULTED is set under three conditions:
-
ACH attempt limit exceeded — The number of automated ACH attempts reaches the configured limit (checked by Daily Retry and webhook workers).
-
Age threshold — The float is more than 90 days past its due date (checked by Daily Retry).
-
Chargeback — The Payments Service reports a
FLOAT_CREDIT_RETURNEDevent on theprod-paymentsKinesis stream. The ACH Handler sets the float toDEFAULTEDand triggers a user ban via the User Service.
UNCOLLECTABLE is set by the Daily Retry scheduler when neither a valid Plaid account (to check balance) nor a valid primary debit card exists. Unlike DEFAULTED, it is not terminal: the Daily Retry scheduler re-queues UNCOLLECTABLE floats on each run, so if the user later adds a valid payment method, collection will be reattempted automatically.
Related Pages
-
Collections Engine — Detailed collection flow diagrams and decision logic
-
ACH Processing — ACH settlement callbacks and status updates
-
Bypass & Payback — Requirements bypass and manual payback details
-
PostgreSQL Schema — Float entity schema and full status reference