VRPlatformVRPlatform
Build a Product UI

Banking

Build accounts, bank records, reconciliation, and bank-rule workflows

Mental Model

Accounts define where amounts post, and bank records represent imported financial activity. Reconciliation links those records to transactions; bank rules turn matching records into transaction proposals or automatic actions.

Resources and Lifecycle

Accounts and Assignments

Account types are ledger, bank, recurring fee, and non-posting. Categories, system assignments, active status, and banking configuration add behavior.

Bank accounts distinguish trust, operating, and external accounts plus deposit and credit-card behavior. Bank records can be transaction or balance records, active or inactive, reconciled, or matched by a rule.

Plaid credit feeds default to credit-card behavior. Relay Financial accounts whose names end in Credit Account receive the same classification even when Plaid reports them as checking/depository accounts.

Bank Records and Matching

Imported records retain source and account identity. Reconciliation and rule matching are relationships with their own state and recovery actions.

Read Model

Render returned account category and assignments, banking capability issues, bank-record source and reconciliation state, delete locks, matching candidates, and rule preview results. Historical postings retain inactive account identity.

Decision Table

Accounts and Feeds

DecisionRule
Immutable categoryDisable category selector
Feed connect conflictonConflict=error (default), replace, or replace-and-detach-history
Feed disconnectmode=preserve-history (default) or detach-history

Accounts return categoryLock with status: locked and reason: immutable; respect it and disable the category selector instead of submitting a classification change. A feed connect conflict occurs when the target accounting account already has a connected bank feed: replace deactivates the old feed, and replace-and-detach-history also detaches its unreconciled bank-record history. On feed disconnect, preserve-history keeps imported records attached while detach-history detaches the unreconciled ones.

Bank Rules

DecisionRule
Internal transfer accountInternal bank account; never external
Rule conditionDescription or amount comparison
Multiple conditions`matchMode=all
AllocationFlat, percentage, or one remainder line
Automatic modeCreate and match only after valid preview/configuration

Flat and percentage rule lines cannot be mixed, and a percentage rule must satisfy the full percentage allocation without a remainder line. Every line needs an account or supported assignment.

Editability

Bank-record lock.delete.reasons[] can include notCsvSource, plaidSource, reconciled, balanceRecord, or bankRuleMatched. Disable deletion and show the corresponding recovery instead of trying the request repeatedly.

Reconciliation constrains transaction payment edits independently from line locks. Account assignment and category changes can affect future journal construction and party rules.

Preview and Preflight

Use Preview a bank rule (POST /bank-rules/preview) to show matching records and allocations. Run a saved bank rule (POST /bank-rules/{id}/run) supports dry run. Rule configuration has schema validation but no generic mutation dry run.

Database-backed bank account and record mutations expose dry run where declared. Feed connections, Plaid, and provider actions have external side effects and do not.

Mutation Recipe

  1. Load account, category, assignment, capability, and current issue data.
  2. Validate bank classification and internal/external eligibility.
  3. Load bank-record locks and reconciliation candidates.
  4. Validate rule conditions and allocation shape locally.
  5. Preview the rule or matching proposal.
  6. Dry-run the final supported database mutation or saved rule run.
  7. Confirm and apply.
  8. Re-read account, record, transaction payment, and sync state.

Failure and Recovery

Use account issues[] for setup repair, such as a last-four mismatch or an operating bank account on a team where the Operations Accounting feature is not enabled (see the issue catalog). For delete locks, direct the user to unreconcile, preserve imported history, or remove the matching relationship as the reason requires. Provider failures belong in connection health, not a local database rollback flow.

Common Recipes

Match an imported expense

Load candidates, select the transaction, verify record totals, dry-run the supported match/update, apply, and re-read both the record and payment lock.

PUT /transactions/{transactionId}?dryRun=true
Content-Type: application/json

{
  "payment": {
    "status": "paid",
    "date": "2026-07-18",
    "bankRecordIds": ["55555555-5555-4555-8555-555555555555"]
  }
}

Create a percentage bank rule

Choose conditions and source accounts, use only percentage allocation lines, validate the full allocation, preview matches, then create the rule (POST /bank-rules) and test it.

Send this payload to /bank-rules/preview first, then to /bank-rules after the proposed matches and allocations are confirmed:

POST /bank-rules/preview
Content-Type: application/json

{
  "name": "Split cleaning charges",
  "transactionType": "expense",
  "mode": "suggest",
  "matchMode": "all",
  "conditions": [
    {
      "type": "description",
      "operator": "contains",
      "value": "cleaning"
    }
  ],
  "vendorContactId": "33333333-3333-4333-8333-333333333333",
  "accountIds": ["22222222-2222-4222-8222-222222222222"],
  "lines": [
    {
      "type": "percentage",
      "amount": 60000,
      "accountId": "44444444-4444-4444-8444-444444444444"
    },
    {
      "type": "percentage",
      "amount": 40000,
      "accountId": "66666666-6666-4666-8666-666666666666"
    }
  ]
}

API Reference

On this page