Split & Promote
Split and Promote are two inverse operations on composite transactions (TRANSFER, FX_CONVERSION, CASH_TRANSFER). Both are implemented in TransactionService as atomic operations within execute_batch().
Concepts
| Operation | Direction | Description |
|---|---|---|
| Split | Composite → Two independent | Break a linked pair into two standalone transactions |
| Promote | Two independent → Composite | Merge two standalone transactions into a linked pair |
Split
What It Does
A split takes a linked pair (two transactions sharing a link_uuid) and:
- Detaches both legs (removes
link_uuidfrom both) - Re-types each leg according to the deterministic Split Type Map:
| Source composite type | From-leg becomes | To-leg becomes |
|---|---|---|
CASH_TRANSFER |
WITHDRAWAL |
DEPOSIT |
TRANSFER |
ADJUSTMENT |
ADJUSTMENT |
FX_CONVERSION |
WITHDRAWAL |
DEPOSIT |
- Writes the two modified transactions back — in the same atomic batch
When to Use
- A broker CSV imported a cross-currency transfer as a single row; BRIM created it as
CASH_TRANSFERbut the two legs actually belong to different purposes - A
TRANSFERwas created incorrectly and needs to be corrected independently
API
The split is submitted as part of the standard batch endpoint:
Both id_a and id_b must be the two legs of the same linked pair (same link_uuid). Passing unlinked transactions raises a validation error.
Promote
What It Does
Promote merges two independent transactions (no link_uuid) into a new linked composite. The operation is atomic:
- Validates that the two transactions are compatible (types, currencies, brokers)
- Deletes the two originals
- Creates two new linked transactions (same data, new
link_uuid, updated types)
This delete-and-recreate approach (rather than update-in-place) ensures balance validation runs cleanly from scratch on the new pair.
Promotion Rules
The allowed promotion pairs are defined in TX_TYPE_METADATA.promote_from rules:
| From types | Target composite | Constraints |
|---|---|---|
DEPOSIT + WITHDRAWAL |
CASH_TRANSFER |
Distinct brokers, same currency |
DEPOSIT + WITHDRAWAL |
FX_CONVERSION |
Same or distinct brokers, different currencies |
DEPOSIT + WITHDRAWAL + asset fields |
TRANSFER |
Distinct brokers, requires asset_id + quantity |
Rule matching is handled by _find_promote_rule_match() and additional constraint checks in _check_promote_constraints().
Promote Suggest
Before promoting manually, clients can call the suggest endpoint to get candidate pairs:
promote_suggest_bulk() scans accessible transactions and returns ranked candidates compatible with the input transaction, including which composite type would result.
API
POST /transactions/commit
{
"promotes": [
{
"id_a": 201,
"id_b": 202,
"new_type": "CASH_TRANSFER"
}
]
}
For TRANSFER promotion, additionally supply asset_id, quantity, and optionally cost_basis_override.
Error Handling
Both operations participate in the standard collect-all-errors pipeline:
- Errors are reported as
TXValidationIssueitems in the response - A single error in any split or promote item rolls back the entire batch
- The frontend receives the full set of issues to highlight every problem at once
Common errors:
| Code | Cause |
|---|---|
pairTypeMismatch |
Split IDs do not belong to the same linked pair |
pairSameBroker |
TRANSFER/CASH_TRANSFER promote attempted with same broker on both sides |
promoteIncompatible |
The two transaction types cannot form any composite |
missingField |
TRANSFER promote missing asset_id or quantity |
Related
- 🏗️ Transaction Service — Batch pipeline and execution model
- ⚖️ WAC & Cost Basis — Cost basis impact of TRANSFER promotes
- 🔒 Balance Validation — Runs after every split/promote