Publishes bank.bankAccount.*; null publishes nothing (scripts, tests).
Protected ReadonlydbStaticinstanceThe process default: built from each dependency's own instance().
Register an account.
Two checks run before the insert, and both exist so the operator learns about a problem while they can still act on it rather than by noticing a record that never appears in the view:
MANUAL_BANK_ACCOUNT_ALREADY_EXISTS surfaces the partial unique index as
a functional error. The number is already registered; that is a form
outcome, not a constraint-violation throw.ACCOUNT_REGISTERED_BY_PROVIDER catches an account a virtual_account
already owns. That table outranks a manual registration (§ 4.7), so the
MANUAL arm would anti-join the row straight back out. A Victor account
is itself a register row (provider = 'VICTOR'), so the key-conflict
check above already names it.Soft delete. Stamps status and deletedAt together — deletedAt is what
the view's anti-joins read, status is the surfaced projection, and one
without the other is a bug.
The account is not lost: retiring the registration hands it back to the
OBSERVED backstop, and frees its key for a fresh registration.
Clears the row's RouteFusion beneficiary under routeFusionEntityId only
if it is still beneficiaryId, and answers false when it was not. A hard
delete, because the table holds only the pair's current beneficiary.
Clears the row's counterparty only if it is still victorCounterpartyId.
The RouteFusion beneficiary recorded for the row under routeFusionEntityId
while created from payee, with any fields RouteFusion asked changes to;
null when none is, or another payee's is.
Marks the row's beneficiary under routeFusionEntityId as one RouteFusion
asked changes to, only while it is still beneficiaryId, and says whether
it did. A beneficiary recorded for another payee replaces it and the mark.
Records the row's RouteFusion beneficiary under routeFusionEntityId, only
while the row still holds the payee's numbers; false when they moved.
Replaces a beneficiary recorded for another payee, clearing any change
request on it, and keeps one already recorded for this one, so two runs
that each created one keep the first.
Records Victor's counterparty for the row, only while the row still holds the numbers it was created for; false when they moved. Provider rows too.
Register an account a provider integration owns (a RouteFusion wallet, an
Anchorage vault, a Zenus FBO). Idempotent on (provider, providerAccountId),
so a retry returns the live row. Takes internal ids and skips the number
checks because the row has no numbers.
PROVIDER_ACCOUNT_OWNED_ELSEWHERE: the live row disagrees on owner or
ledger account, the same rule migration 20260929200600 enforces, so one
register row never silently serves two owners.The program the account sits in (a RouteFusion wallet's); null for none.
OptionalaccountNumbers?: string[]OptionalbankIds?: string[]OptionalcustomerIds?: string[]OptionalheldByInternalCustomer?: booleanRows a live virtual account names for an internal customer: CFX's own RouteFusion wallets, the only kind a bank transfer pays out from.
Optionalids?: string[]OptionalincludeDeleted?: booleanOptionalorderBy?: DbOrderByCriterion<Optionalpage?: number1-based page; defaults to 1.
OptionalpageSize?: numberOptionalproviders?: string[]Optionalq?: stringFree-text over name, account number, routing number and bank name.
OptionalroutingNumbers?: string[]Optionalstatuses?: ("ACTIVE" | "CLOSED" | "DELETED")[]Optionaltypes?: BankAccountType[]Update mutable fields. Pass null to clear a nullable field; omitting one
leaves it untouched.
discoveredFrom is deliberately absent: how a row first arrived is a
historical fact about the operator loop, not a field to rewrite. status
excludes DELETED at the type level — that transition belongs to
delete, which stamps deletedAt alongside it.
Writes to the operator-maintained bank-account registry behind
b2b.bank_account_view(BANK_ACCOUNT_VIEW.md § 4.7).Deliberately uncached, unlike BankAdminService. Banks are read on request paths and earn a
ServiceCache; this table is admin-only and low-traffic, so a cache would buy nothing and add an invalidation surface that can go stale. Add one when a read path actually demands it.Two of the create-time checks exist to keep the operator's mental model and the view's contents in agreement — see BankAccountAdminService.create.