diff --git a/modules/chronik-client/README.md b/modules/chronik-client/README.md
--- a/modules/chronik-client/README.md
+++ b/modules/chronik-client/README.md
@@ -110,3 +110,4 @@
 -   0.29.0 - Support for `plugins` endpoints: `utxos` and `groups` [D16605](https://reviews.bitcoinabc.org/D16605)
 -   1.0.0 - **(Breaking change)** Deprecate NNG chronik and rename all `InNode` classes and types [D16627](https://reviews.bitcoinabc.org/D16627). Users may no longer import `ChronikClientNode` class to run in-node chronik-client and must import `ChronikClient` (which is no longer the NNG class). [D16710](https://reviews.bitcoinabc.org/D16710)
 -   1.1.0 - Support websocket subscriptions to plugins [D16783](https://reviews.bitcoinabc.org/D16783)
+-   1.2.0 - Support `history`, `confirmedTxs`, and `unconfirmedTxs` methods for `plugins` endpoints [D16786](https://reviews.bitcoinabc.org/D16786)
diff --git a/modules/chronik-client/package-lock.json b/modules/chronik-client/package-lock.json
--- a/modules/chronik-client/package-lock.json
+++ b/modules/chronik-client/package-lock.json
@@ -1,12 +1,12 @@
 {
     "name": "chronik-client",
-    "version": "1.1.0",
+    "version": "1.2.0",
     "lockfileVersion": 2,
     "requires": true,
     "packages": {
         "": {
             "name": "chronik-client",
-            "version": "1.1.0",
+            "version": "1.2.0",
             "license": "MIT",
             "dependencies": {
                 "@types/ws": "^8.2.1",
diff --git a/modules/chronik-client/package.json b/modules/chronik-client/package.json
--- a/modules/chronik-client/package.json
+++ b/modules/chronik-client/package.json
@@ -1,6 +1,6 @@
 {
     "name": "chronik-client",
-    "version": "1.1.0",
+    "version": "1.2.0",
     "description": "A client for accessing the Chronik Indexer API",
     "main": "dist/index.js",
     "types": "dist/index.d.ts",
diff --git a/modules/chronik-client/src/ChronikClient.ts b/modules/chronik-client/src/ChronikClient.ts
--- a/modules/chronik-client/src/ChronikClient.ts
+++ b/modules/chronik-client/src/ChronikClient.ts
@@ -496,6 +496,72 @@
 
         return convertToPluginGroups(groups);
     }
+
+    /**
+     * Fetches the tx history of this groupHex for this plugin, in anti-chronological order.
+     * @param groupHex group as a lowercase hex string
+     * @param page Page index of the tx history.
+     * @param pageSize Number of txs per page.
+     */
+    public async history(
+        groupHex: string,
+        page = 0, // Get the first page if unspecified
+        pageSize = 25, // Must be less than 200, let server handle error as server setting could change
+    ): Promise<TxHistoryPage> {
+        const data = await this._proxyInterface.get(
+            `/plugin/${this._pluginName}/${groupHex}/history?page=${page}&page_size=${pageSize}`,
+        );
+        const historyPage = proto.TxHistoryPage.decode(data);
+        return {
+            txs: historyPage.txs.map(convertToTx),
+            numPages: historyPage.numPages,
+            numTxs: historyPage.numTxs,
+        };
+    }
+
+    /**
+     * Fetches the confirmed tx history of this groupHex for this plugin, in the order they appear on the blockchain.
+     * @param groupHex group as a lowercase hex string
+     * @param page Page index of the tx history.
+     * @param pageSize Number of txs per page.
+     */
+    public async confirmedTxs(
+        groupHex: string,
+        page = 0, // Get the first page if unspecified
+        pageSize = 25, // Must be less than 200, let server handle error as server setting could change
+    ): Promise<TxHistoryPage> {
+        const data = await this._proxyInterface.get(
+            `/plugin/${this._pluginName}/${groupHex}/confirmed-txs?page=${page}&page_size=${pageSize}`,
+        );
+        const historyPage = proto.TxHistoryPage.decode(data);
+        return {
+            txs: historyPage.txs.map(convertToTx),
+            numPages: historyPage.numPages,
+            numTxs: historyPage.numTxs,
+        };
+    }
+
+    /**
+     * Fetches the unconfirmed tx history of this groupHex for this plugin, in chronological order.
+     * @param groupHex group as a lowercase hex string
+     * @param page Page index of the tx history.
+     * @param pageSize Number of txs per page.
+     */
+    public async unconfirmedTxs(
+        groupHex: string,
+        page = 0, // Get the first page if unspecified
+        pageSize = 25, // Must be less than 200, let server handle error as server setting could change
+    ): Promise<TxHistoryPage> {
+        const data = await this._proxyInterface.get(
+            `/plugin/${this._pluginName}/${groupHex}/unconfirmed-txs?page=${page}&page_size=${pageSize}`,
+        );
+        const historyPage = proto.TxHistoryPage.decode(data);
+        return {
+            txs: historyPage.txs.map(convertToTx),
+            numPages: historyPage.numPages,
+            numTxs: historyPage.numTxs,
+        };
+    }
 }
 
 /** Config for a WebSocket connection to Chronik. */
diff --git a/modules/chronik-client/test/integration/plugins.ts b/modules/chronik-client/test/integration/plugins.ts
--- a/modules/chronik-client/test/integration/plugins.ts
+++ b/modules/chronik-client/test/integration/plugins.ts
@@ -7,7 +7,7 @@
 import { ChildProcess } from 'node:child_process';
 import { EventEmitter, once } from 'node:events';
 import path from 'path';
-import { ChronikClient, WsMsgClient, WsEndpoint } from '../../index';
+import { ChronikClient, WsMsgClient, WsEndpoint, Tx } from '../../index';
 import initializeTestRunner, {
     cleanupMochaRegtest,
     setMochaTimeout,
@@ -153,20 +153,81 @@
             pluginName: PLUGIN_NAME,
         });
 
+        // We get empty history if no txs exist for a plugin
+        expect(
+            await chronik.plugin(PLUGIN_NAME).history(BYTES_a),
+        ).to.deep.equal({
+            txs: [],
+            numPages: 0,
+            numTxs: 0,
+        });
+        expect(
+            await chronik.plugin(PLUGIN_NAME).unconfirmedTxs(BYTES_a),
+        ).to.deep.equal({
+            txs: [],
+            numPages: 0,
+            numTxs: 0,
+        });
+        expect(
+            await chronik.plugin(PLUGIN_NAME).confirmedTxs(BYTES_a),
+        ).to.deep.equal({
+            txs: [],
+            numPages: 0,
+            numTxs: 0,
+        });
+
         // We throw an error if the endpoint is called with plugin name that does not exist
+        const nonExistentPlugin = 'doesnotexist';
+        await expect(
+            chronik.plugin(nonExistentPlugin).utxos(BYTES_a),
+        ).to.be.rejectedWith(
+            Error,
+            `Failed getting /plugin/${nonExistentPlugin}/${BYTES_a}/utxos: 404: Plugin "${nonExistentPlugin}" not loaded`,
+        );
+        await expect(
+            chronik.plugin(nonExistentPlugin).history(BYTES_a),
+        ).to.be.rejectedWith(
+            Error,
+            `Failed getting /plugin/${nonExistentPlugin}/${BYTES_a}/history?page=0&page_size=25: 404: Plugin "${nonExistentPlugin}" not loaded`,
+        );
         await expect(
-            chronik.plugin('doesnotexist').utxos(BYTES_a),
+            chronik.plugin(nonExistentPlugin).confirmedTxs(BYTES_a),
         ).to.be.rejectedWith(
             Error,
-            `Failed getting /plugin/doesnotexist/${BYTES_a}/utxos: 404: Plugin "doesnotexist" not loaded`,
+            `Failed getting /plugin/${nonExistentPlugin}/${BYTES_a}/confirmed-txs?page=0&page_size=25: 404: Plugin "${nonExistentPlugin}" not loaded`,
+        );
+        await expect(
+            chronik.plugin(nonExistentPlugin).unconfirmedTxs(BYTES_a),
+        ).to.be.rejectedWith(
+            Error,
+            `Failed getting /plugin/${nonExistentPlugin}/${BYTES_a}/unconfirmed-txs?page=0&page_size=25: 404: Plugin "${nonExistentPlugin}" not loaded`,
         );
 
         // We throw an error if the endpoint is called with an invalid plugin group hex
+        const badPluginName = 'not a hex string';
+        await expect(
+            chronik.plugin(PLUGIN_NAME).utxos(badPluginName),
+        ).to.be.rejectedWith(
+            Error,
+            `Failed getting /plugin/${PLUGIN_NAME}/${badPluginName}/utxos: 400: Invalid hex: Invalid character 'n' at position 0`,
+        );
+        await expect(
+            chronik.plugin(PLUGIN_NAME).history(badPluginName),
+        ).to.be.rejectedWith(
+            Error,
+            `Failed getting /plugin/${PLUGIN_NAME}/${badPluginName}/history?page=0&page_size=25: 400: Invalid hex: Invalid character 'n' at position 0`,
+        );
         await expect(
-            chronik.plugin(PLUGIN_NAME).utxos('not a hex string'),
+            chronik.plugin(PLUGIN_NAME).confirmedTxs(badPluginName),
         ).to.be.rejectedWith(
             Error,
-            `Failed getting /plugin/${PLUGIN_NAME}/not a hex string/utxos: 400: Invalid hex: Invalid character 'n' at position 0`,
+            `Failed getting /plugin/${PLUGIN_NAME}/${badPluginName}/confirmed-txs?page=0&page_size=25: 400: Invalid hex: Invalid character 'n' at position 0`,
+        );
+        await expect(
+            chronik.plugin(PLUGIN_NAME).unconfirmedTxs(badPluginName),
+        ).to.be.rejectedWith(
+            Error,
+            `Failed getting /plugin/${PLUGIN_NAME}/${badPluginName}/unconfirmed-txs?page=0&page_size=25: 400: Invalid hex: Invalid character 'n' at position 0`,
         );
 
         // Connect to the websocket with a testable onMessage handler
@@ -311,6 +372,54 @@
                 },
             ],
         });
+
+        expect(
+            await chronik.plugin(PLUGIN_NAME).unconfirmedTxs(BYTES_a),
+        ).to.deep.equal({
+            txs: [firstTx],
+            numPages: 1,
+            numTxs: 1,
+        });
+
+        expect(
+            await chronik.plugin(PLUGIN_NAME).confirmedTxs(BYTES_a),
+        ).to.deep.equal({
+            txs: [],
+            numPages: 0,
+            numTxs: 0,
+        });
+
+        expect(
+            await chronik.plugin(PLUGIN_NAME).history(BYTES_a),
+        ).to.deep.equal({
+            txs: [firstTx],
+            numPages: 1,
+            numTxs: 1,
+        });
+
+        expect(
+            await chronik.plugin(PLUGIN_NAME).unconfirmedTxs(BYTES_b),
+        ).to.deep.equal({
+            txs: [],
+            numPages: 0,
+            numTxs: 0,
+        });
+
+        expect(
+            await chronik.plugin(PLUGIN_NAME).confirmedTxs(BYTES_b),
+        ).to.deep.equal({
+            txs: [],
+            numPages: 0,
+            numTxs: 0,
+        });
+
+        expect(
+            await chronik.plugin(PLUGIN_NAME).history(BYTES_b),
+        ).to.deep.equal({
+            txs: [],
+            numPages: 0,
+            numTxs: 0,
+        });
     });
     it('After broadcasting a tx with plugin utxos in group "b"', async () => {
         // Wait for expected msg at ws1
@@ -404,6 +513,60 @@
                 },
             ],
         });
+
+        // Update firstTx, as now it has a spent output
+        const firstTx = await chronik.tx(FIRST_PLUGIN_TXID);
+
+        // unconfirmed txs are sorted by timeFirstSeen
+        expect(
+            await chronik.plugin(PLUGIN_NAME).unconfirmedTxs(BYTES_a),
+        ).to.deep.equal({
+            txs: [secondTx, firstTx],
+            numPages: 1,
+            numTxs: 2,
+        });
+
+        expect(
+            await chronik.plugin(PLUGIN_NAME).confirmedTxs(BYTES_a),
+        ).to.deep.equal({
+            txs: [],
+            numPages: 0,
+            numTxs: 0,
+        });
+
+        // Note that the history endpoint keeps unconfirmed txs in reverse-chronological order
+        // Opposite order of unconfirmedTxs
+        expect(
+            await chronik.plugin(PLUGIN_NAME).history(BYTES_a),
+        ).to.deep.equal({
+            txs: [firstTx, secondTx],
+            numPages: 1,
+            numTxs: 2,
+        });
+
+        expect(
+            await chronik.plugin(PLUGIN_NAME).unconfirmedTxs(BYTES_b),
+        ).to.deep.equal({
+            txs: [secondTx],
+            numPages: 1,
+            numTxs: 1,
+        });
+
+        expect(
+            await chronik.plugin(PLUGIN_NAME).confirmedTxs(BYTES_b),
+        ).to.deep.equal({
+            txs: [],
+            numPages: 0,
+            numTxs: 0,
+        });
+
+        expect(
+            await chronik.plugin(PLUGIN_NAME).history(BYTES_b),
+        ).to.deep.equal({
+            txs: [secondTx],
+            numPages: 1,
+            numTxs: 1,
+        });
     });
     it('After mining a block with these first 2 txs', async () => {
         await expectWsMsgs(2, msgCollectorWs1);
@@ -448,6 +611,71 @@
                 data: [BYTES_abc],
             },
         });
+
+        // Update txs as they now have block keys
+        // Note that firstTx was already updated above
+        const secondTx = await chronik.tx(SECOND_PLUGIN_TXID);
+
+        // Sort alphabetical by txid, as this is how confirmed txs will be sorted
+        // aka lexicographic sorting
+        const txsSortedByTxid = [firstTx, secondTx].sort((a, b) =>
+            a.txid.localeCompare(b.txid),
+        );
+
+        // History sorting is more complicated
+        // Since timeFirstSeen here is constant, we end up getting "reverse-txid" order
+        // https://github.com/Bitcoin-ABC/bitcoin-abc/blob/a18387188c0d1235eca81791919458fec2433345/chronik/chronik-indexer/src/query/group_history.rs#L171
+        const txsSortedByTxidReverse = [firstTx, secondTx].sort((a, b) =>
+            b.txid.localeCompare(a.txid),
+        );
+
+        expect(
+            await chronik.plugin(PLUGIN_NAME).unconfirmedTxs(BYTES_a),
+        ).to.deep.equal({
+            txs: [],
+            numPages: 0,
+            numTxs: 0,
+        });
+
+        expect(
+            await chronik.plugin(PLUGIN_NAME).confirmedTxs(BYTES_a),
+        ).to.deep.equal({
+            txs: txsSortedByTxid,
+            numPages: 1,
+            numTxs: 2,
+        });
+
+        expect(
+            await chronik.plugin(PLUGIN_NAME).history(BYTES_a),
+        ).to.deep.equal({
+            txs: txsSortedByTxidReverse,
+            numPages: 1,
+            numTxs: 2,
+        });
+
+        expect(
+            await chronik.plugin(PLUGIN_NAME).unconfirmedTxs(BYTES_b),
+        ).to.deep.equal({
+            txs: [],
+            numPages: 0,
+            numTxs: 0,
+        });
+
+        expect(
+            await chronik.plugin(PLUGIN_NAME).confirmedTxs(BYTES_b),
+        ).to.deep.equal({
+            txs: [secondTx],
+            numPages: 1,
+            numTxs: 1,
+        });
+
+        expect(
+            await chronik.plugin(PLUGIN_NAME).history(BYTES_b),
+        ).to.deep.equal({
+            txs: [secondTx],
+            numPages: 1,
+            numTxs: 1,
+        });
     });
     it('After broadcasting a tx with plugin utxos in group "c"', async () => {
         // We get no websocket msgs at ws1
@@ -509,6 +737,44 @@
             pluginName: PLUGIN_NAME,
             utxos: [group_c_utxo],
         });
+
+        // Update secondTx as now an output is spent
+        const secondTx = await chronik.tx(SECOND_PLUGIN_TXID);
+
+        // Sort alphabetical by txid, as this is how confirmed txs will be sorted
+        // aka lexicographic sorting
+        const txsSortedByTxid = [secondTx, thirdTx].sort((a, b) =>
+            a.txid.localeCompare(b.txid),
+        );
+
+        // History sorting is more complicated
+        // Since timeFirstSeen here is constant, we end up getting "reverse-txid" order
+        // https://github.com/Bitcoin-ABC/bitcoin-abc/blob/a18387188c0d1235eca81791919458fec2433345/chronik/chronik-indexer/src/query/group_history.rs#L171
+        const txsSortedByTxidReverse = txsSortedByTxid.reverse();
+
+        expect(
+            await chronik.plugin(PLUGIN_NAME).unconfirmedTxs(BYTES_b),
+        ).to.deep.equal({
+            txs: [thirdTx],
+            numPages: 1,
+            numTxs: 1,
+        });
+
+        expect(
+            await chronik.plugin(PLUGIN_NAME).confirmedTxs(BYTES_b),
+        ).to.deep.equal({
+            txs: [secondTx],
+            numPages: 1,
+            numTxs: 1,
+        });
+
+        expect(
+            await chronik.plugin(PLUGIN_NAME).history(BYTES_b),
+        ).to.deep.equal({
+            txs: txsSortedByTxidReverse,
+            numPages: 1,
+            numTxs: 2,
+        });
     });
     it('After mining a block with this third tx', async () => {
         // We get expected ws confirmed msg
@@ -525,6 +791,50 @@
             pluginName: PLUGIN_NAME,
             utxos: [{ ...group_c_utxo, blockHeight: 103 }],
         });
+
+        // Get the second tx for this scope
+        const secondTx = await chronik.tx(SECOND_PLUGIN_TXID);
+        // Update third tx as it now has a block key
+        const thirdTx = await chronik.tx(THIRD_PLUGIN_TXID);
+
+        // Sort alphabetical by txid, as this is how confirmed txs will be sorted
+        // aka lexicographic sorting
+        const txsSortedByTxid = [secondTx, thirdTx].sort((a, b) =>
+            a.txid.localeCompare(b.txid),
+        );
+
+        // History sorting is more complicated
+        // Since timeFirstSeen here is constant, we end up getting "reverse-txid" order
+        // https://github.com/Bitcoin-ABC/bitcoin-abc/blob/a18387188c0d1235eca81791919458fec2433345/chronik/chronik-indexer/src/query/group_history.rs#L171
+        const txsSortedTxidReverse = [secondTx, thirdTx].sort(
+            (b, a) =>
+                a.timeFirstSeen - b.timeFirstSeen ||
+                a.txid.localeCompare(b.txid),
+        );
+
+        expect(
+            await chronik.plugin(PLUGIN_NAME).unconfirmedTxs(BYTES_b),
+        ).to.deep.equal({
+            txs: [],
+            numPages: 0,
+            numTxs: 0,
+        });
+
+        expect(
+            await chronik.plugin(PLUGIN_NAME).confirmedTxs(BYTES_b),
+        ).to.deep.equal({
+            txs: txsSortedByTxid,
+            numPages: 1,
+            numTxs: 2,
+        });
+
+        expect(
+            await chronik.plugin(PLUGIN_NAME).history(BYTES_b),
+        ).to.deep.equal({
+            txs: txsSortedTxidReverse,
+            numPages: 1,
+            numTxs: 2,
+        });
     });
     it('After invalidating the mined block with the third tx', async () => {
         await expectWsMsgs(1, msgCollectorWs2);
diff --git a/test/functional/setup_scripts/chronik-client_plugins.py b/test/functional/setup_scripts/chronik-client_plugins.py
--- a/test/functional/setup_scripts/chronik-client_plugins.py
+++ b/test/functional/setup_scripts/chronik-client_plugins.py
@@ -127,6 +127,8 @@
         chronik_sub_plugin(ws1, node, "my_plugin", b"a")
         chronik_sub_plugin(ws2, node, "my_plugin", b"b")
 
+        plugin = chronik.plugin("my_plugin")
+
         coinvalue = 5000000000
         tx1 = CTransaction()
         tx1.vin = [CTxIn(COutPoint(int(cointx, 16), 0), SCRIPTSIG_OP_TRUE)]
@@ -156,12 +158,19 @@
             [output.plugins for output in proto_tx1.outputs],
             tx1_plugin_outputs,
         )
-        proto_utxos1 = chronik.plugin("my_plugin").utxos(b"a").ok().utxos
+        proto_utxos1 = plugin.utxos(b"a").ok().utxos
         assert_equal(
             [utxo.plugins for utxo in proto_utxos1],
             tx1_plugin_outputs[1:],
         )
 
+        assert_equal(list(plugin.unconfirmed_txs(b"a").ok().txs), [proto_tx1])
+        assert_equal(list(plugin.confirmed_txs(b"a").ok().txs), [])
+        assert_equal(list(plugin.history(b"a").ok().txs), [proto_tx1])
+        assert_equal(list(plugin.unconfirmed_txs(b"b").ok().txs), [])
+        assert_equal(list(plugin.confirmed_txs(b"b").ok().txs), [])
+        assert_equal(list(plugin.history(b"b").ok().txs), [])
+
         yield True
         self.log.info("Step 3: Send a second tx to create plugin utxos in group 'b'")
 
@@ -192,12 +201,12 @@
             [output.plugins for output in proto_tx2.outputs],
             tx2_plugin_outputs,
         )
-        proto_utxos1 = chronik.plugin("my_plugin").utxos(b"a").ok().utxos
+        proto_utxos1 = plugin.utxos(b"a").ok().utxos
         assert_equal(
             [utxo.plugins for utxo in proto_utxos1],
             [tx1_plugin_outputs[1], tx1_plugin_outputs[2]],  # "abc" spent
         )
-        proto_utxos2 = chronik.plugin("my_plugin").utxos(b"b").ok().utxos
+        proto_utxos2 = plugin.utxos(b"b").ok().utxos
         assert_equal(
             [utxo.plugins for utxo in proto_utxos2],
             tx2_plugin_outputs[1:],
@@ -206,16 +215,61 @@
         assert_equal(ws1.recv(), ws_msg(tx2.hash, pb.TX_ADDED_TO_MEMPOOL))
         assert_equal(ws2.recv(), ws_msg(tx2.hash, pb.TX_ADDED_TO_MEMPOOL))
 
+        proto_tx1 = chronik.tx(tx1.hash).ok()
+        txs = sorted([proto_tx1, proto_tx2], key=lambda t: t.txid[::-1])
+        assert_equal(list(plugin.unconfirmed_txs(b"a").ok().txs), txs)
+        assert_equal(list(plugin.confirmed_txs(b"a").ok().txs), [])
+        assert_equal(list(plugin.history(b"a").ok().txs), txs[::-1])
+        assert_equal(list(plugin.unconfirmed_txs(b"b").ok().txs), [proto_tx2])
+        assert_equal(list(plugin.confirmed_txs(b"b").ok().txs), [])
+        assert_equal(list(plugin.history(b"b").ok().txs), [proto_tx2])
+
         yield True
         self.log.info("Step 4: Mine these first two transactions")
 
         # Mine tx1 and tx2
         block1 = self.generatetoaddress(node, 1, ADDRESS_ECREG_UNSPENDABLE)[-1]
+
         # Lexicographic order
         txids = sorted([tx1.hash, tx2.hash])
         assert_equal(ws1.recv(), ws_msg(txids[0], pb.TX_CONFIRMED))
         assert_equal(ws1.recv(), ws_msg(txids[1], pb.TX_CONFIRMED))
         assert_equal(ws2.recv(), ws_msg(tx2.hash, pb.TX_CONFIRMED))
+
+        proto_tx1 = chronik.tx(tx1.hash).ok()
+        assert_equal([inpt.plugins for inpt in proto_tx1.inputs], [{}])
+        assert_equal(
+            [output.plugins for output in proto_tx1.outputs],
+            tx1_plugin_outputs,
+        )
+
+        proto_tx2 = chronik.tx(tx2.hash).ok()
+        assert_equal(
+            [inpt.plugins for inpt in proto_tx2.inputs],
+            tx2_plugin_inputs,
+        )
+        assert_equal(
+            [output.plugins for output in proto_tx2.outputs],
+            tx2_plugin_outputs,
+        )
+        proto_utxos1 = chronik.plugin("my_plugin").utxos(b"a").ok().utxos
+        assert_equal(
+            [utxo.plugins for utxo in proto_utxos1],
+            [tx1_plugin_outputs[1], tx1_plugin_outputs[2]],  # "abc" spent
+        )
+        proto_utxos2 = chronik.plugin("my_plugin").utxos(b"b").ok().utxos
+        assert_equal(
+            [utxo.plugins for utxo in proto_utxos2],
+            tx2_plugin_outputs[1:],
+        )
+
+        txs = sorted([proto_tx1, proto_tx2], key=lambda t: t.txid[::-1])
+        assert_equal(list(plugin.unconfirmed_txs(b"a").ok().txs), [])
+        assert_equal(list(plugin.confirmed_txs(b"a").ok().txs), txs)
+        assert_equal(list(plugin.history(b"a").ok().txs), txs[::-1])
+        assert_equal(list(plugin.unconfirmed_txs(b"b").ok().txs), [])
+        assert_equal(list(plugin.confirmed_txs(b"b").ok().txs), [proto_tx2])
+        assert_equal(list(plugin.history(b"b").ok().txs), [proto_tx2])
         yield True
 
         self.log.info("Step 5: Send a third tx to create plugin utxos in group 'c'")
@@ -252,17 +306,23 @@
             [output.plugins for output in proto_tx3.outputs],
             tx3_plugin_outputs,
         )
-        proto_utxos2 = chronik.plugin("my_plugin").utxos(b"b").ok().utxos
+        proto_utxos2 = plugin.utxos(b"b").ok().utxos
         assert_equal(
             [utxo.plugins for utxo in proto_utxos2],
             [tx2_plugin_outputs[2]],  # only "borg" remaining
         )
-        proto_utxos3 = chronik.plugin("my_plugin").utxos(b"c").ok().utxos
+        proto_utxos3 = plugin.utxos(b"c").ok().utxos
         assert_equal(
             [utxo.plugins for utxo in proto_utxos3],
             tx3_plugin_outputs[1:],
         )
 
+        proto_tx2 = chronik.tx(tx2.hash).ok()
+        txs = sorted([proto_tx2, proto_tx3], key=lambda t: t.txid[::-1])
+        assert_equal(list(plugin.unconfirmed_txs(b"b").ok().txs), [proto_tx3])
+        assert_equal(list(plugin.confirmed_txs(b"b").ok().txs), [proto_tx2])
+        assert_equal(list(plugin.history(b"b").ok().txs), txs[::-1])
+
         yield True
 
         self.log.info("Step 6: Mine this tx")
@@ -271,6 +331,12 @@
         block2 = self.generatetoaddress(node, 1, ADDRESS_ECREG_UNSPENDABLE)[-1]
         assert_equal(ws2.recv(), ws_msg(tx3.hash, pb.TX_CONFIRMED))
 
+        proto_tx3 = chronik.tx(tx3.hash).ok()
+        txs = sorted([proto_tx2, proto_tx3], key=lambda t: t.txid[::-1])
+        assert_equal(list(plugin.unconfirmed_txs(b"b").ok().txs), [])
+        assert_equal(list(plugin.confirmed_txs(b"b").ok().txs), txs)
+        assert_equal(list(plugin.history(b"b").ok().txs), txs[::-1])
+
         yield True
 
         self.log.info("Step 7: Invalidate the block with the third tx")