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

user status
Status Trigger Description

PROCESSING

Account creation

Default status set when the Auth0 account is first created. User cannot take out floats and is not billed.

ACTIVE

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.

PAUSED

Cancellation or MX action

Subscription has ended. User can log in but is shown a reactivation screen. Not billed.

INVESTIGATE

MX action

User is flagged for review. Cannot take out floats. Not billed. MX can clear this status.

BANNED

MX action or chargeback

User is blocked from logging in. Triggered automatically when a CHARGED_BACK payment event is received. MX can unban, but user must still reactivate.

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.

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. It can be triggered explicitly via POST /{user_id}/user/activate or inline during the Plaid Layer signup flow.

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

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

MX

Ollie (internal tool)

Ollie

User Service (self)

typeform (legacy label)

Subscription Service

(empty — inherits from previous record)

In-app (direct user action)

in app

Unknown

unknown