About Bridges
What bridges are, how they synchronize external systems with the ledger, and when to use them
A bridge is a ledger record that registers an external service so the ledger can talk to it. The most common example is a banking-core integration: a service that debits or credits real bank accounts whenever value moves on the ledger. But bridges are not limited to payment rails. Any service that needs to react to ledger activity -- receiving webhooks for balance changes, resolving aliases to account numbers, or tracking intent status updates -- can be registered as a bridge.
Without bridges, every integration point would need its own inline configuration scattered across effects, aspects, and wallet definitions. Bridges centralize that configuration into a single, referenceable record. You define the base URL, authentication, and capabilities once. Every other record that needs to reach the external service simply references the bridge by handle. When a URL rotates or an OAuth secret changes, you update one record instead of hunting through dozens of definitions.
When you need a bridge
Not every ledger deployment requires bridges. A ledger that only tracks balances internally -- no connection to external bank accounts, no webhooks, no alias resolution -- works without any bridges at all. Bridges become necessary when the ledger needs to coordinate with the outside world.
The most common trigger is real money movement. If a wallet on the ledger represents an actual bank account, someone needs to instruct that bank to move funds when a transfer happens. The bridge is that someone: it translates ledger operations into API calls against the banking core and reports back whether the operation succeeded.
Other scenarios that require bridges include resolving phone numbers or emails to underlying account identifiers through an alias directory (the anchors trait) and reacting to ledger events — balance changes, intent completions, record creation — with custom business logic (the events trait through effects).
A bridge is always associated with one or more wallets. When you configure a wallet and set its bridge property, you are telling the ledger: "whenever this wallet is involved in a money movement, call this bridge to coordinate with the external system." Multiple wallets can reference the same bridge, which is typical when a single banking-core integration serves many accounts.
How a bridge participates in money movement
The ledger processes intents -- requests to move value -- through a two-phase commit (2PC) protocol. Bridges are the mechanism through which external systems participate in that protocol.
The model works in three steps:
- Prepare -- the ledger asks the bridge "can you do this?" The bridge validates the operation against its backend (checking balances, compliance rules, account status) and responds with acceptance or rejection. Nothing has moved yet.
- Commit -- once every participant has accepted the prepare, the ledger tells each bridge "go ahead." The bridge executes the real operation on its backend -- debiting or crediting the actual bank account.
- Abort -- if any participant rejects the prepare, or if the intent needs to be cancelled, the ledger tells each bridge "never mind." The bridge releases any holds or reservations it created during prepare.
This protocol guarantees atomicity across systems that have no shared database. Either every participant commits, or every participant aborts. The ledger orchestrates the process; the bridge implements each side of the conversation for its particular backend.
Consider a transfer from Bank A to Bank B. The ledger sends a debit-prepare to Bank A's bridge and a credit-prepare to Bank B's bridge simultaneously. If Bank A accepts but Bank B rejects (perhaps the destination account is frozen), the ledger sends a debit-abort to Bank A. No money moves. If both accept, the ledger sends commit to both and the transfer completes. The bridge on each side translates these abstract operations into whatever its backend requires -- a core banking API call, an internal ledger entry, a message to a payment switch.
A bridge can participate on the debit side (when value leaves a wallet backed by the bridge), the credit side (when value arrives at a wallet backed by the bridge), or both. Which side a bridge handles is controlled by its traits, described below.
For the full lifecycle including endpoint contracts, request payloads, and response codes, see About Orchestrating Systems and Build the Credit and Debit Interface.
Bridge traits
Traits declare which capabilities a bridge implements. They are defined as an array of strings (or objects, for filtered traits) in the bridge record's traits property. The ledger uses traits to decide which calls to make: if a bridge does not declare a trait, the ledger skips those calls entirely and applies only its own internal validations.
The available traits are:
- credits -- the bridge handles credit operations. The ledger calls it during 2PC when value is arriving at a wallet backed by this bridge.
- debits -- the bridge handles debit operations. The ledger calls it during 2PC when value is leaving a wallet backed by this bridge.
- anchors -- the bridge resolves anchors. When another system queries anchors for a wallet backed by this bridge, the ledger proxies the request to the bridge. This is central to the alias directory flow. See About Anchors.
- domains -- the bridge resolves wallet domains. Similar to anchors but for domain resolution. See About Wallets.
- events -- the bridge receives event signals from ledger effects, such as balance changes, intent status transitions, or record creation.
If the traits property is omitted entirely, all traits are enabled by default. This means a newly registered bridge is expected to handle everything. In practice, most bridges declare only the traits they actually implement.
For example, a bridge with ["debits"] will be called for debit prepare, commit, and abort -- but not for credits. Credit operations on wallets backed by this bridge will proceed with only the ledger's internal validations.
Filtering traits
Sometimes a bridge should participate in 2PC only for certain intents. Rather than passing a trait as a simple string, you can pass an object with method and filter properties to narrow when the ledger calls the bridge.
Filters use dot notation to target properties of the event payload and support MongoDB-style operators ($ne, $gte, $in, etc.). They are applied during the prepare phase for credits and debits, and on every call for anchors. The domains trait does not support filters (its endpoint has no request body), and events filtering is configured on the effect record itself.
{
"traits": [
"credits",
{
"method": "debits",
"filter": {
"intent.data.schema": { "$ne": "settle" }
}
}
]
}In this example the bridge participates in all credit operations, but only in debit operations where the intent schema is not settle.
Claim grouping
An intent can contain multiple claims. By default, the ledger makes a separate 2PC call to the bridge for each claim where the bridge is involved. For intents with many claims this can produce a burst of unordered notifications that are hard to correlate on the bridge side.
To simplify processing, a bridge can opt into claim grouping by setting debits.claims.groupBy or credits.claims.groupBy in its config. Two grouping strategies are available:
- address -- claims are grouped by the literal handle in the claim's source or target field. Two claims that both debit from
my-walletproduce a single grouped call, even if their targets differ. - wallet -- claims are grouped by the resolved wallet. Two claims that debit from
my-walletandother-walletare grouped into one call if both wallets resolve to the same parentparent-wallet.
{
"config": {
"server": "https://my-server.com/api",
"debits.claims.groupBy": "address",
"credits.claims.groupBy": "wallet"
}
}Grouping only affects how the ledger batches calls to the bridge. It does not change the intent's claim structure or the 2PC guarantees.
Registering a bridge
A bridge record has a handle (its unique identifier on the ledger) and several properties that tell the ledger how to communicate with the external service.
schema defines the communication protocol. Currently the supported value is rest, which means the bridge exposes a standard HTTP/JSON interface that the ledger calls with signed payloads.
config holds the base URL and any operational settings. The server property is the root URL that the ledger prepends to all endpoint paths when calling the bridge.
Include the /v2 path prefix in your server URL. This represents the current ledger protocol version and makes it straightforward to support future versions. The ledger does not prepend it automatically.
secure is an array of authentication rules applied to outgoing requests. Rules are processed in order and each one sets an HTTP header. Supported schemas include header (custom headers), oauth2 (client-credentials flow), and mtls (mutual TLS). For full details on configuring authentication, see Bridge Authentication.
access defines who can read or modify the bridge record itself through standard ledger access-control rules.
traits declares the bridge's capabilities, as described above.
Here is a minimal bridge registration that handles both debits and credits over REST:
{
"handle": "bank-core",
"schema": "rest",
"config": {
"server": "https://bank-integration.example.com/v2"
},
"traits": [
"debits",
"credits"
],
"secure": [
{
"schema": "oauth2",
"clientId": "ledger-client",
"clientSecret": "{{ secret.bankOAuthSecret }}",
"tokenUrl": "https://auth.bank.example.com/token"
}
],
"access": [
{
"action": "any",
"signer": {
"public": "<bridge-signer-public-key>"
}
}
]
}This registers a bridge with handle bank-core that the ledger reaches at https://bank-integration.example.com/v2. It uses OAuth2 client credentials to authenticate outgoing requests. The bridge declares that it implements debit and credit operations, so the ledger will call it during 2PC for both sides of a transfer. The access rule restricts who can modify this bridge record.
Once registered, you associate this bridge with one or more wallets by setting the wallet's bridge property to bank-core. From that point on, any intent involving those wallets will trigger 2PC calls to this bridge.
Next steps
Processing Intents
How intents flow through the processing pipeline — aspects, orchestration, and effects.
Orchestrating Systems
The 2PC protocol — prepare, commit, and abort flows with sequence diagrams.
Build the Credit and Debit Interface
The core endpoints for debit and credit operations with request payloads.
Get Notifications
Optional — intent status updates, effect signals, and proof submission.
Bridge Authentication
Configuring header-based auth, OAuth2 client credentials, mTLS, and combining multiple security rules.
Bridge Error Handling
Retry mechanics, backoff configuration, and the activate endpoint for catching up after downtime.