Summary
When a user's Verified Bank Account (VBA) is locked — either a workspace VBA (policy.achAccount.state === 'LOCKED') or a personal bank account (accountData.state === 'LOCKED') — there is currently no indication on the HomePage and no guided flow to resolve it in NewDot.
In OldDot, users see a "Fix" button on their locked bank account in settings, which opens the UnlockWithdrawalBankAccount modal. That modal calls the BankAccount_UnlockSend / InitiateBankAccountUnlock API to create a Concierge conversation escalated to Operations for manual resolution.
We should add a widget to the Time Sensitive section on the HomePage that alerts users when they have a locked bank account and provides a CTA to initiate the unlock flow (matching OldDot behavior).
Current Behavior
- The
SETTLEMENT_ACCOUNT_LOCKED report action posts a chat message with a link to workspace settings, but the workspace settings page treats a locked VBA as "no VBA" (hasVBA only returns true for OPEN state), leading to a dead end.
BankAccount.isLocked() helper exists in src/libs/models/BankAccount.ts but is unused.
CONST.BANK_ACCOUNT.STATE.LOCKED constant is defined but not checked anywhere in the Time Sensitive section.
- There is no equivalent of OldDot's
BankAccount_UnlockSend API integration in NewDot.
Expected Behavior
A new widget appears in the Time Sensitive section when any of the user's bank accounts are in the LOCKED state:
- Workspace VBAs: Show for workspace admins when
policy.achAccount.state === 'LOCKED'
- Personal bank accounts: Show when
bankAccountList[].accountData.state === 'LOCKED'
The widget should display:
- A lock icon with a danger-colored background
- Title: "Your business bank account has been locked" (workspace) / "Your bank account has been locked" (personal)
- Subtitle: workspace name (for workspace VBAs) or "Wallet" (for personal)
- CTA button: "Fix" (reuse existing
ctaFix translation, danger: true style)
- On press: Call
InitiateBankAccountUnlock API with the bankAccountID, then navigate to Concierge chat (matching OldDot behavior)
Data Availability
No new backend data is needed to detect locked VBAs:
policy.achAccount.state already comes down via ONYXKEYS.COLLECTION.POLICY and includes the LOCKED value
ONYXKEYS.BANK_ACCOUNT_LIST entries have accountData.state which can be LOCKED
- Both are populated by existing
OpenApp/ReconnectApp API responses
For the unlock CTA:
- The
InitiateBankAccountUnlock write command already exists on the backend (apiCommandTypes.php) — it needs to be integrated into the NewDot API layer
- It accepts a
bankAccountID parameter and creates a Concierge conversation escalated to Operations
Implementation Details
Architecture
Backend (OpenApp) → Onyx (COLLECTION.POLICY, BANK_ACCOUNT_LIST)
↓
TimeSensitiveSection detects locked state
↓
UnlockBankAccount widget renders
↓
CTA → InitiateBankAccountUnlock API → Concierge chat
Files to Change
-
src/pages/home/TimeSensitiveSection/items/UnlockBankAccount.tsx (new)
- Widget component using
BaseWidgetItem (same pattern as FixCompanyCardConnection.tsx)
- Props:
bankAccountID, policyName?, maskedAccountNumber?
-
src/pages/home/TimeSensitiveSection/index.tsx
- Add detection logic: iterate
adminPolicies checking policy.achAccount?.state === CONST.BANK_ACCOUNT.STATE.LOCKED
- Add
useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST) for personal locked accounts
- Add condition to
hasAnyTimeSensitiveContent guard
- Render widget at appropriate priority (suggest between broken card connections and discount offers)
-
src/libs/actions/BankAccounts.ts
- Add
initiateBankAccountUnlock(bankAccountID: number) function calling the InitiateBankAccountUnlock write command
-
src/libs/API/types.ts
- Add
InitiateBankAccountUnlock to write commands and parameter types
-
src/languages/en.ts, src/languages/pt-BR.ts, src/languages/zh-hans.ts
- Add translation strings under
homePage.timeSensitiveSection.unlockBankAccount
OldDot Reference
Key OldDot files for reference:
Web-Expensify/site/app/modals/settings/unlockWithdrawalBankAccount.jsx — unlock modal UI
Web-Expensify/site/app/settings/reimbursement/bankAccountView.jsx — locked state detection + "Fix" button
Web-Expensify/lib/BankAccountAPI.php — sendEmailToUnlock() backend logic
Web-Expensify/api.php — InitiateBankAccountUnlock command routing
Open Questions
- Should the masked account number be displayed in the widget? The
ACHAccount type has accountNumber (may be masked from backend) and personal BankAccount has description ("Account ending in XXXX"). Need to verify actual API response format.
- Priority order within Time Sensitive section: suggested after card fraud but before broken card connections, since a locked VBA blocks reimbursements.
EXFY-18795
Issue Owner
Current Issue Owner: @ZhenjaHorbach
Summary
When a user's Verified Bank Account (VBA) is locked — either a workspace VBA (
policy.achAccount.state === 'LOCKED') or a personal bank account (accountData.state === 'LOCKED') — there is currently no indication on the HomePage and no guided flow to resolve it in NewDot.In OldDot, users see a "Fix" button on their locked bank account in settings, which opens the
UnlockWithdrawalBankAccountmodal. That modal calls theBankAccount_UnlockSend/InitiateBankAccountUnlockAPI to create a Concierge conversation escalated to Operations for manual resolution.We should add a widget to the Time Sensitive section on the HomePage that alerts users when they have a locked bank account and provides a CTA to initiate the unlock flow (matching OldDot behavior).
Current Behavior
SETTLEMENT_ACCOUNT_LOCKEDreport action posts a chat message with a link to workspace settings, but the workspace settings page treats a locked VBA as "no VBA" (hasVBAonly returnstrueforOPENstate), leading to a dead end.BankAccount.isLocked()helper exists insrc/libs/models/BankAccount.tsbut is unused.CONST.BANK_ACCOUNT.STATE.LOCKEDconstant is defined but not checked anywhere in the Time Sensitive section.BankAccount_UnlockSendAPI integration in NewDot.Expected Behavior
A new widget appears in the Time Sensitive section when any of the user's bank accounts are in the
LOCKEDstate:policy.achAccount.state === 'LOCKED'bankAccountList[].accountData.state === 'LOCKED'The widget should display:
ctaFixtranslation,danger: truestyle)InitiateBankAccountUnlockAPI with thebankAccountID, then navigate to Concierge chat (matching OldDot behavior)Data Availability
No new backend data is needed to detect locked VBAs:
policy.achAccount.statealready comes down viaONYXKEYS.COLLECTION.POLICYand includes theLOCKEDvalueONYXKEYS.BANK_ACCOUNT_LISTentries haveaccountData.statewhich can beLOCKEDOpenApp/ReconnectAppAPI responsesFor the unlock CTA:
InitiateBankAccountUnlockwrite command already exists on the backend (apiCommandTypes.php) — it needs to be integrated into the NewDot API layerbankAccountIDparameter and creates a Concierge conversation escalated to OperationsImplementation Details
Architecture
Files to Change
src/pages/home/TimeSensitiveSection/items/UnlockBankAccount.tsx(new)BaseWidgetItem(same pattern asFixCompanyCardConnection.tsx)bankAccountID,policyName?,maskedAccountNumber?src/pages/home/TimeSensitiveSection/index.tsxadminPoliciescheckingpolicy.achAccount?.state === CONST.BANK_ACCOUNT.STATE.LOCKEDuseOnyx(ONYXKEYS.BANK_ACCOUNT_LIST)for personal locked accountshasAnyTimeSensitiveContentguardsrc/libs/actions/BankAccounts.tsinitiateBankAccountUnlock(bankAccountID: number)function calling theInitiateBankAccountUnlockwrite commandsrc/libs/API/types.tsInitiateBankAccountUnlockto write commands and parameter typessrc/languages/en.ts,src/languages/pt-BR.ts,src/languages/zh-hans.tshomePage.timeSensitiveSection.unlockBankAccountOldDot Reference
Key OldDot files for reference:
Web-Expensify/site/app/modals/settings/unlockWithdrawalBankAccount.jsx— unlock modal UIWeb-Expensify/site/app/settings/reimbursement/bankAccountView.jsx— locked state detection + "Fix" buttonWeb-Expensify/lib/BankAccountAPI.php—sendEmailToUnlock()backend logicWeb-Expensify/api.php—InitiateBankAccountUnlockcommand routingOpen Questions
ACHAccounttype hasaccountNumber(may be masked from backend) and personalBankAccounthasdescription("Account ending in XXXX"). Need to verify actual API response format.EXFY-18795
Issue Owner
Current Issue Owner: @ZhenjaHorbach