A financial host (or issuer processor) is the system that sits between a card network and a cardholder's funds. It receives real-time authorization requests, manages hold accounts that ring-fence funds during the authorization-to-settlement lifecycle, and ultimately settles against scheme liability accounts. This example models the full card issuing flow in a single Formance ledger schema — authorizations, presentments, refunds, chargebacks, and stand-in processing.
This is an illustrative example. Adapt the schema to your specific card program, BIN sponsor requirements, and scheme rules.
The Complete Schema#
This is the full ledger schema for a card issuing financial host. The sections below explain each part.
System Actors#
A card issuing financial host involves five distinct actors, each representing a different side of every card transaction. Understanding their roles — and how their account balances behave — is essential before reading the ledger schema.
- Cardholders: The end users whose funds you manage. Their accounts are normal credit accounts (liabilities) — the balance represents funds you owe them.
- Banks: Your banking partners. Normal debit accounts — represent actual currency reserves held at partner institutions.
- Schemes: Card networks (Visa, Mastercard, etc.). Normal credit accounts — track settlement liabilities between you and the network.
- Program Manager: BIN sponsors or third-party card program managers. Tracks reverse liability obligations.
- Platform: Your operational accounts for fees, revenue, and dispute costs.
The Chart of Accounts below shows how these actors map to the ledger's account hierarchy.
Chart of Accounts#
The chart section defines five account groups that mirror the participants in a card transaction.
Cardholders#
Each cardholder has a main account (their spendable balance), plus dynamic sub-accounts created per authorization hold and per pending refund. The main balance minus total holds gives the available balance the cardholder can spend.
Schemes#
Scheme accounts track what the network owes you (or you owe the network) across the settlement cycle. The chargeback sub-account isolates dispute flows from normal settlement.
Banks, Program Manager, Platform#
banks:$bank_id:main— Normal debit accounts. Nostro accounts representing actual funds at partner banks.program_manager:$pm_id:liability— Normal credit accounts. Tracks reverse liability obligations to BIN sponsors or program managers.platform:$platform_name— Normal debit accounts. Operational accounts for fees, revenue, and chargeback fees.
Transaction Patterns#
Authorization Lifecycle#
The authorization flow manages the time between a card swipe and final settlement.
Note: A declined authorization creates no ledger posting. The ledger only records approved transactions. Declined attempts should be logged separately for fraud monitoring and analytics.
CARD_AUTHORIZATION_APPROVED moves funds from the cardholder's main account to a hold account keyed by authorization ID. An optional overdraft limit can be specified per cardholder.
vars {
asset $asset
number $amount
number $overdraft
account $account_id
string $authorization_id
string $pii_id
string $trx_details
}
send [$asset $amount] (
source = @cardholder:$account_id:main allowing overdraft up to [$asset $overdraft]
destination = @cardholder:$account_id:hold:$authorization_id
)
set_tx_meta("authorization_id", $authorization_id)
set_tx_meta("pii_id", $pii_id)
set_tx_meta("trx_details", $trx_details)CARD_AUTHORIZATION_PARTIAL uses a wildcard amount ([$asset *]) with a max clause — it approves only what is available up to the requested amount. This is common at fuel dispensers or split-tender scenarios.
vars {
asset $asset
number $amount
number $overdraft
account $account_id
string $authorization_id
string $pii_id
string $trx_details
}
send [$asset *] (
source = max [$asset $amount] from @cardholder:$account_id:main allowing overdraft up to [$asset $overdraft]
destination = @cardholder:$account_id:hold:$authorization_id
)
set_tx_meta("authorization_id", $authorization_id)
set_tx_meta("pii_id", $pii_id)
set_tx_meta("trx_details", $trx_details)CARD_AUTHORIZATION_INCREMENTAL adds funds to an existing hold account. Used for hotels, car rentals, and other scenarios where the final amount is not known upfront.
AUTHORIZATION_REVERSAL releases a specific amount from a hold back to main. Supports both full and partial reversals.
vars {
asset $asset
number $amount
account $account_id
string $authorization_id
string $reversal_id
string $pii_id
string $trx_details
}
send [$asset $amount] (
source = @cardholder:$account_id:hold:$authorization_id
destination = @cardholder:$account_id:main
)
set_tx_meta("authorization_id", $authorization_id)
set_tx_meta("reversal_id", $reversal_id)
set_tx_meta("pii_id", $pii_id)
set_tx_meta("trx_details", $trx_details)
set_tx_meta("transaction_type", "authorization_reversal")HOLD_REVERSAL_WILDCARD releases whatever balance remains in a hold account using a wildcard amount. Used after partial presentments, expired authorizations, or cancellations.
vars {
asset $asset
account $account_id
string $authorization_id
string $reversal_id
string $pii_id
string $trx_details
}
// Release all remaining funds from hold account back to main account
send [$asset *] (
source = @cardholder:$account_id:hold:$authorization_id
destination = @cardholder:$account_id:main
)
set_tx_meta("authorization_id", $authorization_id)
set_tx_meta("reversal_id", $reversal_id)
set_tx_meta("pii_id", $pii_id)
set_tx_meta("trx_details", $trx_details)
set_tx_meta("transaction_type", "hold_reversal")Authorization requests are latency-critical — card networks typically require responses within 100-200ms. Design your integration to minimize round-trips to the ledger.
Presentment (Settlement)#
Presentment transactions settle authorized (or offline) transactions against the scheme. They typically arrive in batch files from the network, hours or days after the original authorization.
PRESENTMENT moves funds from the hold account to the scheme's main liability account. The presentment amount may differ from the authorization amount (e.g., due to currency conversion or partial fulfillment). After presentment, any remaining hold balance should be released via HOLD_REVERSAL_WILDCARD.
vars {
asset $asset
number $amount
account $account_id
string $authorization_id
string $presentment_id
account $scheme_id
string $pii_id
string $trx_details
}
send [$asset $amount] (
source = @cardholder:$account_id:hold:$authorization_id
destination = @schemes:$scheme_id:main
)
set_tx_meta("authorization_id", $authorization_id)
set_tx_meta("presentment_id", $presentment_id)
set_tx_meta("pii_id", $pii_id)
set_tx_meta("trx_details", $trx_details)
set_tx_meta("transaction_type", "presentment")PRESENTMENT_WITH_TIP handles cases where the settled amount exceeds the authorization (e.g., restaurant tips). It drains the hold for the authorized amount and debits the cardholder's main account for the additional amount — two sends in a single atomic transaction.
vars {
asset $asset
number $auth_amount
number $additional_amount
account $account_id
string $authorization_id
string $presentment_id
account $scheme_id
string $pii_id
string $trx_details
}
// Move the authorized amount from hold to scheme
send [$asset $auth_amount] (
source = @cardholder:$account_id:hold:$authorization_id
destination = @schemes:$scheme_id:main
)
// Debit the additional amount (tip) directly from main account
send [$asset $additional_amount] (
source = @cardholder:$account_id:main
destination = @schemes:$scheme_id:main
)
set_tx_meta("authorization_id", $authorization_id)
set_tx_meta("presentment_id", $presentment_id)
set_tx_meta("pii_id", $pii_id)
set_tx_meta("trx_details", $trx_details)
set_tx_meta("transaction_type", "presentment_with_tip")OFFLINE_PRESENTMENT processes transactions where the card's chip approved the transaction without an online authorization (e.g., transit, in-flight purchases). Uses allowing unbounded overdraft because the issuer is obligated to honor the chip's decision.
vars {
asset $asset
number $amount
account $account_id
string $presentment_id
account $scheme_id
string $pii_id
string $trx_details
}
// Mandatory debit from main account - no prior authorization exists
send [$asset $amount] (
source = @cardholder:$account_id:main allowing unbounded overdraft
destination = @schemes:$scheme_id:main
)
set_tx_meta("presentment_id", $presentment_id)
set_tx_meta("pii_id", $pii_id)
set_tx_meta("trx_details", $trx_details)
set_tx_meta("transaction_type", "offline_presentment")
set_tx_meta("authorization_mode", "offline")Offline presentments arrive without a prior authorization. They should be flagged for risk review since the cardholder's balance was not checked at the time of purchase.
Refunds#
Refunds follow a two-step authorization-then-posting pattern, mirroring how refund messages flow through card networks.
Authorize
REFUND_AUTHORIZATION moves funds from the scheme to a pending refund account. The cardholder cannot spend these funds yet.
vars {
asset $asset
number $amount
account $account_id
string $refund_auth_id
account $scheme_id
string $pii_id
string $trx_details
}
// Move from scheme to pending refund account (not yet available to cardholder)
send [$asset $amount] (
source = @schemes:$scheme_id:main allowing unbounded overdraft
destination = @cardholder:$account_id:refund:pending:$refund_auth_id
)
set_tx_meta("refund_auth_id", $refund_auth_id)
set_tx_meta("pii_id", $pii_id)
set_tx_meta("trx_details", $trx_details)
set_tx_meta("transaction_type", "refund_authorization")
set_tx_meta("refund_status", "pending")Post
REFUND_POSTING releases the pending funds to the cardholder's main account, making them available.
vars {
asset $asset
number $amount
account $account_id
string $refund_auth_id
string $refund_posting_id
string $trx_details
}
// Release funds from pending to main account (now available to cardholder)
send [$asset $amount] (
source = @cardholder:$account_id:refund:pending:$refund_auth_id
destination = @cardholder:$account_id:main
)
set_tx_meta("refund_auth_id", $refund_auth_id)
set_tx_meta("refund_posting_id", $refund_posting_id)
set_tx_meta("trx_details", $trx_details)
set_tx_meta("transaction_type", "refund_posting")
set_tx_meta("refund_status", "completed")Chargebacks#
Chargebacks involve a multi-step dispute process between cardholder, issuer, and merchant. The schema uses a separate chargeback sub-account on schemes to keep dispute flows isolated from normal settlement.
Accept
CHARGEBACK_ACCEPTANCE credits the cardholder from the scheme's dedicated chargeback account (with unbounded overdraft, since the scheme owes you). This is the provisional credit to the cardholder while the dispute is in progress.
vars {
asset $asset
number $amount
account $account_id
string $chargeback_id
account $scheme_id
string $original_presentment_id
string $pii_id
string $trx_details
}
// Credit cardholder from scheme's chargeback account
send [$asset $amount] (
source = @schemes:$scheme_id:chargeback allowing unbounded overdraft
destination = @cardholder:$account_id:main
)
set_tx_meta("chargeback_id", $chargeback_id)
set_tx_meta("original_presentment_id", $original_presentment_id)
set_tx_meta("pii_id", $pii_id)
set_tx_meta("trx_details", $trx_details)
set_tx_meta("transaction_type", "chargeback_acceptance")
set_tx_meta("chargeback_status", "accepted")Confirm
CHARGEBACK_CONFIRMATION moves funds from the scheme's main settlement account to cover the chargeback account, aligning with the actual settlement deduction from the network.
vars {
asset $asset
number $amount
string $chargeback_id
account $scheme_id
string $settlement_ref
string $trx_details
}
// Cover the chargeback account from scheme's main settlement account
send [$asset $amount] (
source = @schemes:$scheme_id:main allowing unbounded overdraft
destination = @schemes:$scheme_id:chargeback
)
set_tx_meta("chargeback_id", $chargeback_id)
set_tx_meta("settlement_ref", $settlement_ref)
set_tx_meta("trx_details", $trx_details)
set_tx_meta("transaction_type", "chargeback_confirmation")
set_tx_meta("chargeback_status", "confirmed")If the merchant wins the dispute, SECOND_PRESENTMENT reverses the chargeback — debits the cardholder and re-establishes the scheme liability. Uses unbounded overdraft because the reversal is mandatory regardless of the cardholder's current balance.
vars {
asset $asset
number $amount
account $account_id
string $chargeback_id
string $second_presentment_id
account $scheme_id
string $pii_id
string $trx_details
}
// Debit cardholder (reverses the chargeback credit) and credit scheme liability
send [$asset $amount] (
source = @cardholder:$account_id:main allowing unbounded overdraft
destination = @schemes:$scheme_id:main
)
set_tx_meta("chargeback_id", $chargeback_id)
set_tx_meta("second_presentment_id", $second_presentment_id)
set_tx_meta("pii_id", $pii_id)
set_tx_meta("trx_details", $trx_details)
set_tx_meta("transaction_type", "second_presentment")
set_tx_meta("chargeback_status", "reversed")Stand-In Processing (STIP)#
STIP_ADVICE handles transactions that were approved by the network's stand-in processor when the issuer was unavailable. These are mandatory debits with unbounded overdraft — the transaction was already approved and the cardholder has already received the goods or services.
vars {
asset $asset
number $amount
account $account_id
string $stip_advice_id
account $scheme_id
string $pii_id
string $trx_details
}
// Mandatory debit from main account - transaction was already approved by stand-in processor
send [$asset $amount] (
source = @cardholder:$account_id:main allowing unbounded overdraft
destination = @schemes:$scheme_id:main
)
set_tx_meta("stip_advice_id", $stip_advice_id)
set_tx_meta("pii_id", $pii_id)
set_tx_meta("trx_details", $trx_details)
set_tx_meta("transaction_type", "stip_advice")
set_tx_meta("authorization_mode", "stand_in")STIP transactions bypass your authorization logic entirely. Monitor these closely and ensure your system can process the resulting advice messages promptly to keep balances accurate.
Available Balance vs. Actual Balance#
A cardholder's actual balance is the balance on their main account. Their available balance is the actual balance minus the sum of all active holds. When deciding whether to approve an authorization, use available balance — not actual balance.
The schema achieves this naturally: approving an authorization moves funds out of main into a hold sub-account, so the main balance always reflects what is truly available.
Overdraft Policies#
The schema supports three overdraft modes depending on the transaction type:
- Bounded overdraft (
allowing overdraft up to [$asset $overdraft]) — Used for standard authorizations where you control the credit limit per cardholder. - Unbounded overdraft (
allowing unbounded overdraft) — Used for mandatory debits like offline presentments, STIP advice, and second presentments where the transaction has already been approved. - No overdraft — The default. Presentments from hold accounts and refund postings should never need overdraft since funds are already ring-fenced.
Queries#
The schema defines reusable queries for operational monitoring and reconciliation. These leverage the hierarchical account structure and metadata to filter across the ledger.
Authorization monitoring:
ALL_HOLDS/CARDHOLDER_HOLDS/SPECIFIC_HOLD— Inspect authorization holds at various levels of granularity.ACTIVE_HOLDS_FOR_CARD— Find active holds for a specific payment instrument (card), filtered by metadata and positive balance.
Settlement and reconciliation:
SCHEME_LIABILITY/ALL_SCHEME_LIABILITIES— Track what each network owes you (or vice versa) for settlement reconciliation.ALL_CARDHOLDER_BALANCES— Overview of all cardholder positions.
Disputes and refunds:
PENDING_REFUNDS— Find all authorized but unposted refunds across the system.SCHEME_CHARGEBACKS/ALL_CHARGEBACKS— Monitor dispute-related liabilities.
Risk and audit:
OFFLINE_TRANSACTIONS— Audit trail of all offline presentments, useful for risk monitoring.