Reconcile Bank Records
Explain suggestions, handle ambiguity, and apply confirmed matches
Last Updated: 2026-09-05
Version: 1.5
Reconciliation links imported BankRecords to balanced Transactions. The API can suggest an exact set of existing Transactions, suggest a saved BankRule, or apply an automatic BankRule that is explicitly configured for automatic execution.
Current Behavior At A Glance
| Question | Behavior |
|---|---|
| Why this set? | Combined amount, currency, dates, ambiguity, and member details |
| Why this BankRule? | Direction, account scope, match mode, and condition results |
| Which set is first? | Fewest Transactions, then the smallest total date difference |
| Which matching rule wins? | Highest priority wins for automatic execution |
| What if top automatic rules tie? | Nothing posts; the BankRecord remains unmatched |
| Does a manager always confirm? | Yes for suggest; no for one top automatic rule |
| Can one payout cover many bookings? | Yes, with one or several Transactions |
Read Running Balances
balance.current is the account balance after the BankRecord, in cents.
balance.correction is the adjustment needed to match a supplied balance
checkpoint. In chronological calculation order, the next closing balance equals
the previous closing balance plus the next record's amount and correction.
Inactive records contribute zero. Balance checkpoints precede ordinary records
with the same timestamp.
For amounts of 50, 75, and 25 cents, a final checkpoint of 150 cents gives
closing balances of 50, 125, and 150 cents. Records before the first
checkpoint are reconstructed from that checkpoint using the later amounts.
Load Suggestions
Request matching Transactions and saved BankRules with the BankRecord:
curl --get '/bank-records' \
--data-urlencode 'reconcileStatus=unpaid' \
--data-urlencode 'includeMatchingTransaction=true' \
--data-urlencode 'matchingTransactionDaysOffset=7' \
--data-urlencode 'includeMatchingRules=true'includeMatchingTransaction=true requests exact transaction-set suggestions.
matchingTransactionDaysOffset sets the maximum date difference and defaults
to seven days. Request includeMatchingRules=true when the response should
also include saved BankRule suggestions.
Each BankRecord can return:
{
"matchingTransactionSets": [],
"matchingRules": []
}Use sort=matched for records without a candidate first, or sort=-matched
for records with a candidate first. The API evaluates the complete filtered
set before pagination.
Explain A Transaction-Set Suggestion
Every matchingTransactionSets[] item is one exact reconciliation proposal.
It contains the combined result, ranking evidence, and one to five eligible
Transactions.
Read every suggested Transaction from the set's transactions[]. BankRecord
responses do not return a separate list of single-Transaction suggestions.
| Set field | Show the manager |
|---|---|
combinedAmount | The signed total of all member Transactions |
currency | The shared currency of the BankRecord and every member |
dateEvidence.totalDateOffset | Sum of the member date differences |
dateEvidence.maxDateOffset | Largest member date difference |
matchDetails.ambiguous | Another set has the same ranking evidence |
transactions[] | The accounting entries that would be reconciled together |
Set-level matchDetails.reasons[] explains why the proposal is eligible:
| Reason | Plain-language meaning |
|---|---|
exactAmount | The combined signed amount equals the BankRecord amount |
sameCurrency | The BankRecord and all members use the same currency |
allTransactionsEligible | Every member is available for reconciliation |
dateWithinWindow | Every member is inside the allowed date window |
Each member includes its Transaction summary plus matchDetails, listings[],
and reservations[].
| Member field | Show the manager |
|---|---|
amount | The signed bank-account impact contributed by this member |
matchDetails.matchingDate | The date used for the comparison |
dateOffset | Days between the BankRecord and matching date |
matchDetails.maxDateOffset | Maximum eligible difference |
account | Existing bank account, or null when matching assigns the account |
listings[] | Properties referenced by Transaction lines |
reservations[] | Booking references represented by Transaction lines |
matchDetails.ambiguous | Another candidate has the same date distance |
matchDetails.reasons[] is a stable list of satisfied criteria:
| Reason | Plain-language meaning |
|---|---|
exactAmount | The Transaction's bank-account amount equals the BankRecord amount |
sameBankAccount | The Transaction already uses this bank account |
bankAccountAssignedOnMatch | Reconciliation will assign this bank account |
dateWithinWindow | The matching date is inside the requested date window |
A useful summary is:
Exact total of 1,250.00 USD from two eligible Transactions, covering booking ABC-123 at Ocean House and booking XYZ-456 at Lake House.
Build that sentence from the typed fields. Do not parse descriptions or infer additional reasons.
Matching Date
For a paid expense, matchingDate is normally its payment date. Other eligible
Transactions normally use their Transaction date. This can make
matchingDate different from the displayed accounting date.
Set Order And Ties
Transaction sets are ordered by:
- one-member sets before multi-member sets;
- fewest members;
- lowest
dateEvidence.totalDateOffset; and - sorted Transaction IDs for deterministic output.
When two sets have the same member count and total date offset, each has
matchDetails.ambiguous=true. The ID tie-break only stabilizes the response;
it is not a recommendation.
The manager should compare:
- listing and reservation references;
matchingDateanddateOffset;- Transaction type, contact, description, and account; and
- the complete Transaction detail when the summaries still look identical.
One Payout Covering Multiple Bookings
A transaction set can group booking Transactions from different months when
their combined signed bank-account impact equals the BankRecord amount. Show
the member Transactions with their listing and reservation context, then send
all confirmed IDs to PUT /bank-records/{id} in
reconciledTransactionIds.
BankRecord: 1,250.00
July booking Transaction: 700.00
August booking Transaction: 550.00
Selected impact: 1,250.00Bounded, best-effort suggestion discovery evaluates the 20 closest eligible Transactions, returns at most 10 sets, and includes no more than five Transactions per set. Multi-Transaction sets use a maximum seven-day window. The reconciliation mutation validates the selected IDs and exact combined total again before it applies the match.
Bank Rules
A BankRule turns recurring bank descriptions or amounts into a proposed balanced Transaction. Rules support deposits and expenses.
What A Rule Matches
| Input | Supported behavior |
|---|---|
| Direction | Deposit or expense |
| Bank-account scope | Any bank account, or selected accountIds |
| Description | contains, equals, or startsWith |
| Amount | >, >=, =, <, or <= using integer cents |
| Multiple conditions | matchMode=all or matchMode=any |
matchingRules[].matchDetails explains the evaluation:
directionMatchesshows whether the BankRecord direction is correct;hasAccountScopeshows whether the rule is restricted to accounts;accountMatchesshows whether this BankRecord is inside that scope;matchModesays whether all or any conditions are required; andconditions[]returns every configured condition with itsmatchesresult.
A client can summarize the result as:
“Stripe payout” matched this deposit account and both configured conditions. It proposes a deposit with a merchant-fee line and a remainder line.
Use preview.transaction to show exactly what the rule will create.
Suggest Or Apply Automatically
mode | Authorization and behavior |
|---|---|
suggest | Show the proposal and require manager confirmation |
autoCreateAndMatch | The saved rule authorizes creation and reconciliation during ingest |
There is no separate global auto-match switch. Authorization belongs to each saved rule.
Priority And Ambiguity
Rule suggestions are ordered by:
- priority, highest first;
- creation time, newest first;
- rule ID for deterministic output.
Rules with the same mode and priority have
matchDetails.ambiguous=true. For suggestion rules, let the manager compare
the per-condition results and proposed Transactions.
During ingest, the API applies the single matching autoCreateAndMatch rule at
the highest priority. If several automatic rules share that priority:
- no rule is selected;
- no Transaction is created;
- the BankRecord remains unmatched; and
- the batch returns
CONFLICTwithpriorityandbankRuleIdsin context.
This is a deliberate manager-review state, not a request to select the first rule in the array.
Allocate The Transaction
| Line type | Use |
|---|---|
flat | A fixed cent amount |
percentage | A percentage in basis points |
remainder | The amount left after fixed lines |
Rules cannot mix flat and percentage lines. A fixed allocation requires one remainder line. A percentage allocation contains only percentage lines and must total 100%. Every line needs an account or supported assignment.
Use the deposit_merchantFee assignment for a merchant-fee percentage. The
API gives that allocation the fee direction while keeping the complete
Transaction balanced to the imported amount.
Expenses require vendorContactId; deposits do not accept it. Only expense
rules support usePriorMonthEnd. Expense rule lines can select an
applied-amount tax rate. See
Transaction type constraints.
Preview, Save, And Run
- Preview an unsaved definition with
POST /bank-rules/preview. - Show every matching BankRecord and its proposed Transaction.
- Save the confirmed rule with
POST /bank-rules. - Dry-run selected records with
POST /bank-rules/{id}/run?dryRun=true. - Apply the same record IDs without
dryRun.
{
"name": "Allocate processor payout",
"transactionType": "deposit",
"mode": "suggest",
"priority": 100,
"matchMode": "all",
"conditions": [
{
"type": "description",
"operator": "contains",
"value": "processor payout"
}
],
"accountIds": ["22222222-2222-4222-8222-222222222222"],
"lines": [
{
"type": "flat",
"amount": 250,
"assignment": "deposit_merchantFee"
},
{
"type": "remainder",
"accountId": "44444444-4444-4444-8444-444444444444"
}
]
}Saved-rule runs return one outcome per BankRecord: applied, wouldApply,
skipped, or failed. A rule version is applied at most once to the same
BankRecord.
Apply Or Remove A Match
To reconcile one BankRecord to one or several Transactions, update the
BankRecord's reconciledTransactionIds. To reconcile one normal Transaction to
one BankRecord, the Transaction can instead be saved with the ID in
payment.bankRecordIds. Normal Transactions cannot fan out to several
BankRecords; explicit bank transfers can link their source and destination
sides.
For PUT /bank-records/{id}, omit reconciledTransactionIds to leave the
match unchanged. Send null or [] to remove every reconciliation link.
Omit status or send null to leave status unchanged.
Dry-run the exact supported mutation before applying it. To remove the match, submit the remaining Transaction or BankRecord IDs rather than deleting accounting history.
Manual reconciliation has these cardinality rules:
- One BankRecord can reconcile to multiple Transactions when their exact total matches the BankRecord.
- A non-transfer Transaction cannot reconcile across multiple BankRecords.
- One explicit bank-to-bank transfer Transaction has a source and destination bank journal leg. It can reconcile to one BankRecord on each account side.
- Match each transfer side from its corresponding bank-account reconciliation screen. Matching one side does not match the other or remove the transfer from the other account's eligible Transactions.
- The API does not impose a five-Transaction selection limit. The five-member bound applies only to automatic suggestion discovery.
POST /bank-records/actions/batch applies one reversible action to at most 500
distinct BankRecords. It supports unmatch, exclude, and reactivate, with
the standard dryRun=true preflight. The request is atomic: every requested
row is reported in input order, and one blocked row prevents all changes. There
is no best-effort mode and no bulk-delete operation.
| Operation | Eligibility and locks | Balance and reconciliation | Dry run | Undo |
|---|---|---|---|---|
| Match | Active, supported source | Balance unchanged; links replaced | Yes | Unmatch |
| Unmatch | Existing record | Balance unchanged; links cleared | Yes | Match again |
| Exclude | Existing record | Removed from active balance; links cleared | Yes | Reactivate |
| Reactivate | Inactive record | Active balance restored; stays unmatched | Yes | Exclude |
| Delete | Unmatched CSV source; no delete lock reasons | Permanently removed | Yes | None |
Exclude only rows that do not represent real cash movement, such as a feed artifact or balance snapshot. If a bank error and its reversal both cleared the trust account, record both cash events as manager Transactions and reconcile them even when they net to zero. A typical pair is a Deposit for the error and an Expense or negative Deposit for the reversal.
Match has single-record dry run. Unmatch, exclude, and reactivate have both single-record and atomic batch dry run. Books-closed and statement locks do not block reconciliation changes. Unmatch leaves Transaction payment state unchanged.
Delete has individual dry run only. Its lock.delete.reasons[] may contain
notCsvSource, plaidSource, reconciled, balanceRecord, or
bankRuleMatched.
Reconciliation locks amount, Transaction date, bank account, and line-count changes until the match is removed. Descriptions and other documented field-level edits can remain available. See Transaction editability.
Eligibility Boundaries
Each existing-Transaction suggestion requires an active eligible Transaction, an available reconciliation slot, a date inside the allowed window, and an exact impact on the selected bank account. Set suggestions combine eligible Transactions only when their aggregate impact exactly matches the BankRecord.
Eligible types include deposits, paid or unpaid expenses, opening accounts payable, payouts, and transfers when their account side is valid. Unpaid expenses and opening accounts payable can receive the BankRecord's account during matching.
API Reference
- List BankRecords and suggestions —
GET /bank-records - Get one BankRecord —
GET /bank-records/{id} - Update reconciliation —
PUT /bank-records/{id} - Apply reversible actions in bulk
- Create a BankRule —
POST /bank-rules - Preview a BankRule —
POST /bank-rules/preview - Run a BankRule —
POST /bank-rules/{id}/run
