User Lifecycle
Overview
A user progresses through a defined set of statuses from account creation through cancellation. The status is stored in both DynamoDB (user_status) and RDS (employment_validation) and controls what actions the user can take and whether they are billed.
User Status
| Status | Trigger | Description |
|---|---|---|
|
Account creation |
Default status set when the Auth0 account is first created. User cannot take out floats and is not billed. |
|
Successful activation |
User has linked a bank account, has an active primary debit card, and has an activated subscription. User is eligible for floats and billing. |
|
Cancellation or MX action |
Subscription has ended. User can log in but is shown a reactivation screen. Not billed. |
|
MX action |
User is flagged for review. Cannot take out floats. Not billed. MX can clear this status. |
|
MX action or chargeback |
User is blocked from logging in. Triggered automatically when a |
Signup Flow
New users go through a two-step process: account creation followed by activation. Activation may happen immediately (Plaid Layer flow) or later when the user connects their bank account.
Each successful user creation emits the Datadog distribution user.signup (value 1), tagged with:
-
platform:<app_platform>
This is the entry point of the signup funnel and is intentionally separate from the user.membership.* namespace, which tracks membership state transitions rather than initial account creation.
Standard Flow
POST /users
│
▼
Validate phone number (deduplicate)
Validate access token
│
▼
Save user record (status: PROCESSING)
│
├── Require MFA in Auth0
├── Add START_DATE tag
├── Accept SMS terms (if requested)
└── Save phone number
│
▼
Return 201 Created
(user activates later via POST /{user_id}/user/activate)
Plaid Layer Flow
When a layer_token is provided at signup, Plaid account linking happens inline and activation is attempted immediately after user creation.
POST /users (with layer_token)
│
▼
Create Plaid items from Layer stub if reactivating (Transactions service)
│
▼
Save user record (status: PROCESSING)
│
▼
Attempt activation (see below)
│
▼
Return 201 Created
Activation
Activation transitions a user from PROCESSING to ACTIVE. Two paths exist depending on which onboarding flow the user went through.
Each successful activation (an actual PROCESSING → ACTIVE transition) emits the Datadog distribution user.activated (value 1), tagged with:
-
flow:legacy(POST /{user_id}/user/activate, including the inline Plaid Layer path) orflow:prequalify(POST /{user_id}/user/activate/prequalify) -
platform:<app_platform>
The metric is only emitted on a real transition — no-op calls for users that are already ACTIVE (or not in PROCESSING) do not emit it. Together with user.signup, this forms the signup funnel: user.signup → user.activated.
Legacy Activation (POST /{user_id}/user/activate)
Triggered explicitly or inline during the Plaid Layer signup flow. Validates that the user has connected a bank account and debit card before proceeding.
User status must be PROCESSING
│
▼
Has active Plaid items? ──No──► Return (user stays PROCESSING)
│ Yes
▼
Has a main account? ──No──► Return (user stays PROCESSING)
│ Yes
▼
Has an active debit card? ──No──► Return (user stays PROCESSING)
│ Yes
▼
Has a primary debit card? ──No──► Return (user stays PROCESSING)
│ Yes
▼
Activate subscription (Subscription Service)
│
▼
Create membership record (base tier, monthly term)
│
▼
Update user status → ACTIVE
New Float Onboarding Activation (POST /{user_id}/user/activate/prequalify)
Used by the new prequalification onboarding flow. The user connects their bank account and debit card during prequalification (handled by the Transactions and Payments services) before explicitly calling this endpoint. Bank account and debit card presence are not re-validated here — they are already confirmed by prequalification.
User status must be PROCESSING
│
▼
Save billing consent agreement (consent_key + IP + app version + approval date)
│
▼
Emit first_debit_card_connected marketing attribution event
(appsflyer_id read from user record; X-Floatme-Appsflyer-Id header used as fallback)
│
▼
Activate subscription (Subscription Service)
│
▼
Create membership record (base tier, monthly term)
│
▼
Update user status → ACTIVE
Cancellation & Cleanup Flow
Account cancellation is handled by POST /{user_id}/user/close-account (or POST /{user_id}/user/cancel, which calls the same logic). The flow is split into a synchronous phase and an asynchronous cleanup phase.
Synchronous Phase
POST /{user_id}/user/close-account
│
▼
User status is BANNED? ──Yes──► Return 200 (no-op)
│ No
▼
Has active float?
├── Yes → skip cleanup (debit card + Plaid items left in place)
└── No → proceed with full cleanup
│
▼
Write membership record (status: CANCELLED, event: CLOSEACCOUNT)
│
▼
Update user status → PAUSED
│
▼
If cleanup enabled:
├── Send user_id to cleanup SQS queue
└── Delete debit card (Payment Service) — failure is logged, not fatal
│
▼
Send cancellation notification (Segment)
│
▼
Return 200 OK
| When a user has an active float at cancellation, the debit card and Plaid items are intentionally left in place so the float can still be collected. Cleanup is skipped for these users. |
Asynchronous Cleanup Phase
The prod-user-service-cleanup Lambda consumes messages from the prod-user-service-cleanup SQS queue. Each message contains a user_id. The three cleanup steps run sequentially; all errors are collected but processing continues regardless of individual failures.
SQS message received (user_id)
│
▼
1. Remove Plaid items
│ List all active items (Transactions Service)
│ For each item: RemoveItem
│ └── If 412 (user has active float): skip silently
│
▼
2. Block Auth0 account
│ Prevents user from logging in
│
▼
3. Schedule Array cleanup (Entitlements Service)
│ Expects 201 Created or 404 Not Found
Cancellation Source Tracking
The membership_event_source field on the resulting membership record is set based on the caller identity:
| Caller | Event Source |
|---|---|
Admin API |
|
Ollie (internal tool) |
|
User Service (self) |
|
Subscription Service |
(empty — inherits from previous record) |
In-app (direct user action) |
|
Unknown |
|
Related Pages
-
Users & Data Model — User status values and DynamoDB schema
-
Reactivation — How users return to active status
-
Membership Upgrades — Upgrading tier mid-cycle
-
Event-Driven Flows — Chargeback detection (payment worker) and the outbound Kinesis events produced by status changes