diff --git a/docs/content/docs/transactions/chaining.mdx b/docs/content/docs/transactions/chaining.mdx
new file mode 100644
index 00000000..37ec37a9
--- /dev/null
+++ b/docs/content/docs/transactions/chaining.mdx
@@ -0,0 +1,177 @@
+---
+title: "Transaction Chaining"
+description: "Build multiple dependent transactions without waiting for on-chain confirmation between them"
+---
+
+# Transaction Chaining
+
+Build a sequence of dependent transactions up-front, then submit them in order — no waiting for blocks between steps.
+
+## The Problem
+
+Each Cardano transaction spends UTxOs and creates new ones. Normally you can't build the second transaction until the first is confirmed on-chain, because the new UTxOs it creates don't exist yet from the provider's perspective.
+
+This 10–30 second wait between steps is painful for multi-step workflows: batch payouts, batch minting, or any sequence of operations that logically belong together.
+
+## How It Works
+
+After `.build()` completes, the resulting `SignBuilder` exposes a `.chainResult()` method that returns:
+
+```
+ChainResult
+├── consumed — UTxOs coin selection spent from the available set
+├── available — remaining unspent UTxOs + newly created outputs (with pre-computed txHash)
+└── txHash — pre-computed hash of this transaction (blake2b-256 of the body)
+```
+
+The `available` array is the key. It contains the UTxOs your wallet still holds *plus* any outputs this transaction creates — already tagged with the correct `txHash` so they're valid as inputs to the next build. Pass it as `availableUtxos` in the next `.build()` call.
+
+```
+tx1.build({ availableUtxos: walletUtxos })
+ └── tx1.chainResult().available ← remaining UTxOs + tx1's new outputs
+ │
+ ▼
+tx2.build({ availableUtxos: tx1.chainResult().available })
+ └── tx2.chainResult().available ← remaining + tx2's new outputs
+ │
+ ▼
+tx3.build({ availableUtxos: tx2.chainResult().available })
+```
+
+Transactions must be **submitted in order**. Each transaction spends outputs created by the previous one, so the node will reject tx2 if tx1 hasn't been submitted yet.
+
+## Usage
+
+### Two sequential payments
+
+The simplest case: two payments built back-to-back, submitted in order.
+
+```typescript twoslash
+import { Address, Assets, createClient } from "@evolution-sdk/evolution"
+
+const client = createClient({
+ network: "preprod",
+ provider: { type: "blockfrost", baseUrl: "https://cardano-preprod.blockfrost.io/api/v0", projectId: process.env.BLOCKFROST_API_KEY! },
+ wallet: { type: "seed", mnemonic: process.env.WALLET_MNEMONIC!, accountIndex: 0 }
+})
+
+const alice = Address.fromBech32("addr_test1vrm9x2dgvdau8vckj4duc89m638t8djmluqw5pdrFollw8qd9k63")
+const bob = Address.fromBech32("addr_test1qz2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer3n0d3vllmyqwsx5wktcd8cc3sq835lu7drv2xwl2wywfgs68faae")
+
+// Build first transaction — auto-fetches wallet UTxOs
+const tx1 = await client
+ .newTx()
+ .payToAddress({ address: alice, assets: Assets.fromLovelace(2_000_000n) })
+ .build()
+
+// Build second transaction immediately — no waiting for tx1 to confirm
+const tx2 = await client
+ .newTx()
+ .payToAddress({ address: bob, assets: Assets.fromLovelace(2_000_000n) })
+ .build({ availableUtxos: tx1.chainResult().available })
+
+// Submit in order — tx1 must reach the node before tx2
+const signed1 = await tx1.sign()
+await signed1.submit()
+
+const signed2 = await tx2.sign()
+await signed2.submit()
+```
+
+### Spending an output from the previous transaction
+
+Use `tx1.chainResult().available` to find the output you want to spend in tx2.
+
+```typescript twoslash
+import { Address, Assets, createClient } from "@evolution-sdk/evolution"
+
+const client = createClient({
+ network: "preprod",
+ provider: { type: "blockfrost", baseUrl: "https://cardano-preprod.blockfrost.io/api/v0", projectId: process.env.BLOCKFROST_API_KEY! },
+ wallet: { type: "seed", mnemonic: process.env.WALLET_MNEMONIC!, accountIndex: 0 }
+})
+
+const alice = Address.fromBech32("addr_test1vrm9x2dgvdau8vckj4duc89m638t8djmluqw5pdrFollw8qd9k63")
+const bob = Address.fromBech32("addr_test1qz2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer3n0d3vllmyqwsx5wktcd8cc3sq835lu7drv2xwl2wywfgs68faae")
+
+// tx1 sends 5 ADA to Alice
+const tx1 = await client
+ .newTx()
+ .payToAddress({ address: alice, assets: Assets.fromLovelace(5_000_000n) })
+ .build()
+
+const chain1 = tx1.chainResult()
+
+// Find Alice's output in the chain result — it has a pre-computed txHash
+const aliceAddress = Address.toBech32(alice)
+const aliceOutput = chain1.available.find(
+ utxo => Address.toBech32(utxo.address) === aliceAddress
+)!
+
+// tx2 immediately spends Alice's output, forwarding to Bob
+const tx2 = await client
+ .newTx()
+ .collectFrom({ inputs: [aliceOutput] })
+ .payToAddress({ address: bob, assets: Assets.fromLovelace(4_500_000n) })
+ .build({ availableUtxos: chain1.available })
+
+// Sign and submit in order
+await (await tx1.sign()).submit()
+await (await tx2.sign()).submit()
+```
+
+### Three-step batch
+
+Chain three builds together up-front, then submit all three.
+
+```typescript twoslash
+import { Address, Assets, createClient } from "@evolution-sdk/evolution"
+
+const client = createClient({
+ network: "preprod",
+ provider: { type: "blockfrost", baseUrl: "https://cardano-preprod.blockfrost.io/api/v0", projectId: process.env.BLOCKFROST_API_KEY! },
+ wallet: { type: "seed", mnemonic: process.env.WALLET_MNEMONIC!, accountIndex: 0 }
+})
+
+const recipients = [
+ Address.fromBech32("addr_test1vrm9x2dgvdau8vckj4duc89m638t8djmluqw5pdrFollw8qd9k63"),
+ Address.fromBech32("addr_test1qz2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer3n0d3vllmyqwsx5wktcd8cc3sq835lu7drv2xwl2wywfgs68faae"),
+ Address.fromBech32("addr_test1qpq6xvp5y4fw0wfgxfqmn78qqagkpv4q7qpqyz8s8x3snp5n0d3vllmyqwsx5wktcd8cc3sq835lu7drv2xwl2wywfgsc3z7t3"),
+]
+
+const tx1 = await client
+ .newTx()
+ .payToAddress({ address: recipients[0], assets: Assets.fromLovelace(5_000_000n) })
+ .build()
+
+const tx2 = await client
+ .newTx()
+ .payToAddress({ address: recipients[1], assets: Assets.fromLovelace(5_000_000n) })
+ .build({ availableUtxos: tx1.chainResult().available })
+
+const tx3 = await client
+ .newTx()
+ .payToAddress({ address: recipients[2], assets: Assets.fromLovelace(5_000_000n) })
+ .build({ availableUtxos: tx2.chainResult().available })
+
+// All three built — now submit in order
+for (const tx of [tx1, tx2, tx3]) {
+ const signed = await tx.sign()
+ await signed.submit()
+}
+```
+
+## Gotchas
+
+- **Submit in order.** Each transaction in the chain depends on outputs from the previous one. Submitting tx2 before tx1 means the node sees inputs that don't exist yet and rejects it.
+- **Not retry-safe by default.** The chain is built from a single snapshot of chain state. If tx1 fails after you've built tx2 (e.g. a network error mid-submit), you cannot safely retry just tx2 — you need to rebuild the whole chain. See [Retry-Safe Transactions](/docs/transactions/retry-safe) for how to structure resilient pipelines.
+- **`chainResult()` is memoized.** It's computed once from the build result and cached. Calling it multiple times is free but you always get the same snapshot.
+- **The outputs in `available` are not yet on-chain.** They exist only as pre-computed UTxOs. Don't pass them to any provider call (e.g. `getUtxos`) — they won't be there yet.
+
+## Next Steps
+
+
+
+
+
+
diff --git a/docs/content/docs/transactions/index.mdx b/docs/content/docs/transactions/index.mdx
index 0c5bee8b..347816d9 100644
--- a/docs/content/docs/transactions/index.mdx
+++ b/docs/content/docs/transactions/index.mdx
@@ -8,6 +8,8 @@ import { Card, Cards } from 'fumadocs-ui/components/card'
-
+
+
+
diff --git a/docs/content/docs/transactions/meta.json b/docs/content/docs/transactions/meta.json
index 39a88f12..fa6385d4 100644
--- a/docs/content/docs/transactions/meta.json
+++ b/docs/content/docs/transactions/meta.json
@@ -5,6 +5,7 @@
"first-transaction",
"simple-payment",
"multi-output",
+ "chaining",
"retry-safe"
]
}