Bank Accounts and Connections
Connect Plaid, map feeds to GL accounts, and recover configuration
Last Updated: 2026-09-03
Version: 1.2
A bank Account is the GL posting target. An AccountConnection is an optional external identity discovered through Plaid or another provider. Connecting a feed never creates a second accounting account.
Account And Feed Model
account.banking.connections[] contains the linked AccountConnections. The
outer item id is the accountConnectionId; its nested connection.id is the
provider Connection ID.
Bank Accounts distinguish trust, operating, and external categories plus
deposit and credit-card behavior. Respect the category, assignments,
categoryLock, capabilities, and issues[] returned by the API.
Bank Fees and Interest Income are ordinary ledger accounts. Add them to the published global or partner ledger template when they should be present in a new team's chart. Use a BankRule when one team wants a repeatable interest mapping. The rule must explicitly select the intended account and party; VRPlatform has no global beneficiary default because the legal recipient depends on the team and jurisdiction.
Connect Plaid
Plaid configuration is one three-step session:
| Step | Request | Result |
|---|---|---|
| Start | POST /plaid/connect | connectId and Link token |
| Complete Link | POST /plaid/connect/complete | Complete configuration read model |
| Confirm mapping | POST /plaid/connect/configure | Account decisions and one initial sync |
The connectId owns idempotency, selected environment, recovery, and final
configuration. Do not create a second session while completion is still
retryable.
An embedded product can receive embed:banking:v1. That bundle grants only
the three Plaid connect requests above. It does not grant general Account,
BankRecord, Transaction, report, or team-administration access. The embedded
session remains bound to its team, partner, allowed origin, expiry, and
revocation state. Use /banking as the embedded view path.
Plaid credentials, public and access tokens, Item IDs, Link session IDs, and provider payloads never appear in public audit events. Reviewed connection, account-connection, and account configuration changes can appear in the Activity Log.
Choose The Session Mode
| Mode | Use | Connection ID |
|---|---|---|
create | Add a new Plaid Item | Omit |
update | Repair an existing Item with reusable credentials | Require |
replace | Replace credentials with a new Item | Require |
The optional environment is sandbox or production and defaults to
production. Completion and configuration reuse the session's environment and
do not accept it again.
New and replacement Items request Plaid Transactions only. Plaid Auth is not part of reconciliation and would exclude credit-card accounts. Update mode retains the existing Item's products.
Hostaway-region sessions use the hostaway Plaid customization, United States
country scope, and the Hostaway client name for create, update, and replace.
Render Completion Directly
The completion response is the configuration screen model:
accountscontains eligible existing GL bank accounts;bankAccountCategoriescontains available classifications; andbankAccounts[]contains status, suggestions, capabilities, and typed issues for every discovered feed.
Do not fetch /accounts again or poll AccountConnections for discovery. Submit
one connect or archive decision for every row whose configuration status is
required. Optional opening balances use { date, centAmount }.
The final configuration executes all decisions sequentially in one transaction. A conflict rolls back every mapping. After commit, the API creates one durable initial sync.
Identity And Replacement Safety
A four-digit banking suffix is unique among a team's GL bank accounts. A feed and its linked Account may share the suffix because they represent the same bank account.
Plaid sandbox institutions reuse test masks. Sandbox sessions therefore do not use last four digits for suggestions or duplicate checks and do not copy the mask onto a GL Account. Provider identity checks still apply.
When replacement identity matches several historical feeds, the API does not guess. It returns the feed as unlinked and keeps eligible GL Accounts available for an explicit decision. A confirmed replacement can archive inactive predecessors but cannot displace an active feed. Historical BankRecords stay on the same GL Account.
Plaid credit feeds default to credit-card behavior. Relay Financial names that
end in Credit Account receive the same classification when Plaid reports a
checking or depository type.
Import Bank Activity
Plaid and provider feeds retain their source and AccountConnection identity.
CSV imports use the canonical accountId; their accountConnectionId is
null. See Import Data from CSV.
Records earlier than a feed's configured start date are stored as inactive history. This is an expected exclusion, not a failed sync or reconnect signal.
When a feed resumes after a CSV stopgap import, the API warns if a new record has the same account, date, and exact amount as an active record from the other source. Those fields are not a unique identity, so neither record is merged or skipped. Let a manager review the warning.
Account And Feed Decisions
| Decision | Behavior |
|---|---|
| Immutable account category | Disable the selector |
| Initialize banking | Write account state, opening Transaction, and feed checkpoint atomically |
| Credit-card opening balance | Negative feed checkpoint and positive opening Transaction line |
| Delete a protected Account | Respect the lock or use onLocked=archive when offered |
| Attach over an active feed | onConflict=error, replace, or replace-and-detach-history |
| Detach a feed | Preserve history by default; detach unreconciled history only when requested |
| Unselected Plaid feed | Submit action=archive |
An Account referenced by a BankRule selector or posting line cannot be hard
deleted; use onLocked=archive to preserve the rule. An active recurring fee
that uses an Account as a direct posting account, tax posting account, or
formula input blocks deletion and archive. Repoint or deactivate the fee first.
Banking initialization can create its opening-balance Transaction before the
team has selected statementStartAt. Ordinary Transactions remain unavailable
until accounting start is configured.
ACH Readiness
Account and AccountConnection reads expose
capabilities.achPayments = { enabled, disabledReasons }. Use only an
AccountConnection whose capability is enabled; a Ramp Connection does not make
every embedded account eligible.
Ramp funding requires an active deposit AccountConnection with its provider
account ID in uniqueRef. The replacement-stable persistentRef is not a
funding requirement. Payment execution also checks that the linked source has
one Ramp entity and bank-account ID and that the provider account matches the
AccountConnection.
Contact reads expose only masked ACH state: provider, connection, last four,
status, failure reason, and hasInFlightPayment. Full routing and account
numbers are write-only through POST /contacts/{id}/payment-methods/ach and
are never stored by VRPlatform.
Do not send a client-selected connectionId for ACH setup. The API resolves
the single eligible Ramp flow. The first setup activates an unconfigured flow;
an explicitly inactive, missing, or ambiguous setup remains blocked.
Ramp vendor creation requires contact email and country, plus state for a
United States vendor. Phone and address are optional. When the contact has no
location, the integration uses the connected Ramp business billing address.
The pushRampVendors flow must select an active Ramp user as Vendor owner.
ACH setup returns HTTP 422 before it queues verification when that setting is
missing or no longer resolves to an active user on the same Ramp connection.
The error code is RAMP_VENDOR_OWNER_REQUIRED; its context returns the flow,
connection, and vendorOwner setting key so clients can open the existing
connection setting.
An existing ownerless vendor cannot inherit a different vendor's owner or use
the connection default retroactively. Contact ACH state returns
status=failed with instructions in failureReason; send the user to that
vendor in Ramp to assign an owner before ACH becomes ready.
Statement payout preview treats an unconfigured transfersToBills flow as
eligible without activating it. The first otherwise-valid payout activates the
flow. A dry run rolls that activation back, and an explicitly inactive flow
remains unavailable.
An in-flight payout stays on the bank account selected when it was scheduled. Keep bank editing available, but explain that the change affects future payments only.
Plaid Recovery
| Error | Client action |
|---|---|
PLAID_PROVIDER_UNAVAILABLE | Retry the same action and connectId |
PLAID_CONNECT_IN_PROGRESS | Retry the same completion; do not start another session |
CONNECTION_RECONNECT_REQUIRED | Run the existing Item's reconnect flow |
PLAID_CONNECT_REQUIRES_NEW_LINK | Follow context.restartMode and its Connection ID exactly |
PLAID_CONNECT_CONFIGURATION_CHANGED | Replace the screen with context.result and reconfirm |
Plaid Link error INVALID_UPDATED_USERNAME means update mode cannot repair the
Item. Start a new replace session for the same Connection.
Do not infer create or replace mode from local state. Use the server-owned recovery directive. Display the stable top-level error and use allowlisted provider fields only for diagnostics.
API Reference
- Initialize banking:
POST /accounts/{id}/banking-init - Attach a feed:
POST /accounts/{id}/connections - Detach a feed:
DELETE /accounts/{id}/connections/{accountConnectionId} - Start Plaid Link:
POST /plaid/connect - Complete Plaid Link:
POST /plaid/connect/complete - Configure Plaid accounts:
POST /plaid/connect/configure
