diff --git a/modules/chronik-client/.mocharc.js b/modules/chronik-client/.mocharc.js
--- a/modules/chronik-client/.mocharc.js
+++ b/modules/chronik-client/.mocharc.js
@@ -1,4 +1,4 @@
 'use strict';
 module.exports = {
-    timeout: '20s',
+    timeout: '60s',
 };
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
@@ -96,3 +96,4 @@
 0.18.0 - Add support for websocket connections to `ChronikClientNode`
 0.19.0 - Add support for token data in tx inputs and outputs to `ChronikClientNode`
 0.20.0 - Add support for calling script endpoints by address to `ChronikClientNode`
+0.21.0 - Add support for `tokenId` endpoints and token data in utxos to `ChronikClientNode`
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": "0.20.0",
+    "version": "0.21.0",
     "lockfileVersion": 2,
     "requires": true,
     "packages": {
         "": {
             "name": "chronik-client",
-            "version": "0.20.0",
+            "version": "0.21.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": "0.20.0",
+    "version": "0.21.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/ChronikClientNode.ts b/modules/chronik-client/src/ChronikClientNode.ts
--- a/modules/chronik-client/src/ChronikClientNode.ts
+++ b/modules/chronik-client/src/ChronikClientNode.ts
@@ -143,6 +143,11 @@
         return convertToRawTx(rawTx);
     }
 
+    /** Create object that allows fetching info about a given token */
+    public tokenId(tokenId: string): TokenIdEndpoint {
+        return new TokenIdEndpoint(this._proxyInterface, tokenId);
+    }
+
     /** Create object that allows fetching script history or UTXOs. */
     public script(
         scriptType: ScriptType_InNode,
@@ -228,6 +233,91 @@
     }
 }
 
+/** Allows fetching tokenId confirmedTxs, unconfirmedTxs, history, and UTXOs. */
+export class TokenIdEndpoint {
+    private _proxyInterface: FailoverProxy;
+    private _tokenId: string;
+
+    constructor(proxyInterface: FailoverProxy, tokenId: string) {
+        this._proxyInterface = proxyInterface;
+        this._tokenId = tokenId;
+    }
+
+    /**
+     * Fetches the tx history of this tokenId, in anti-chronological order.
+     * @param page Page index of the tx history.
+     * @param pageSize Number of txs per page.
+     */
+    public async history(
+        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_InNode> {
+        const data = await this._proxyInterface.get(
+            `/token-id/${this._tokenId}/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 tokenId, in anti-chronological order.
+     * @param page Page index of the tx history.
+     * @param pageSize Number of txs per page.
+     */
+    public async confirmedTxs(
+        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_InNode> {
+        const data = await this._proxyInterface.get(
+            `/token-id/${this._tokenId}/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 tokenId, in anti-chronological order.
+     * @param page Page index of the tx history.
+     * @param pageSize Number of txs per page.
+     */
+    public async unconfirmedTxs(
+        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_InNode> {
+        const data = await this._proxyInterface.get(
+            `/token-id/${this._tokenId}/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,
+        };
+    }
+
+    /**
+     * Fetches the current UTXO set for this tokenId.
+     */
+    public async utxos(): Promise<TokenIdUtxos> {
+        const data = await this._proxyInterface.get(
+            `/token-id/${this._tokenId}/utxos`,
+        );
+        const utxos = proto.Utxos.decode(data);
+        return {
+            tokenId: this._tokenId,
+            utxos: utxos.utxos.map(convertToUtxo),
+        };
+    }
+}
+
 /** Config for a WebSocket connection to Chronik. */
 export interface WsConfig_InNode {
     /** Fired when a message is sent from the WebSocket. */
@@ -587,7 +677,7 @@
     if (utxo.outpoint === undefined) {
         throw new Error('UTXO outpoint is undefined');
     }
-    return {
+    const utxoInNode: Utxo_InNode = {
         outpoint: {
             txid: toHexRev(utxo.outpoint.txid),
             outIdx: utxo.outpoint.outIdx,
@@ -597,6 +687,11 @@
         value: parseInt(utxo.value),
         isFinal: utxo.isFinal,
     };
+    if (typeof utxo.token !== 'undefined') {
+        // We only return a token key if we have token data for this input
+        utxoInNode.token = convertToTokenInNode(utxo.token);
+    }
+    return utxoInNode;
 }
 
 function convertToTokenEntry(tokenEntry: proto.TokenEntry): TokenEntry {
@@ -711,13 +806,19 @@
         );
     }
 
-    return {
+    const tokenInNode: Token_InNode = {
         tokenId: token.tokenId,
         tokenType: convertToTokenType(token.tokenType),
-        entryIdx: token.entryIdx,
         amount: token.amount,
         isMintBaton: token.isMintBaton,
     };
+
+    // We do not bother including entryIdx for utxos, where it is always -1
+    if (token.entryIdx !== -1) {
+        tokenInNode.entryIdx = token.entryIdx;
+    }
+
+    return tokenInNode;
 }
 
 function convertToBlockMsgType(msgType: proto.BlockMsgType): BlockMsgType {
@@ -1063,6 +1164,8 @@
     value: number;
     /** Is this utxo avalanche finalized */
     isFinal: boolean;
+    /** Token value attached to this utxo */
+    token?: Token_InNode;
 }
 
 /** Token coloring an input or output */
@@ -1071,8 +1174,12 @@
     tokenId: string;
     /** Token type of the token */
     tokenType: TokenType;
-    /** Index into `token_entries` for `Tx`. -1 for UTXOs */
-    entryIdx: number;
+    /**
+     * Index into `token_entries` for `Tx`
+     * chronik returns -1 for UTXOs, chronik-client
+     * passes no entryIdx key for UTXOS
+     */
+    entryIdx?: number;
     /** Base token amount of the input/output */
     amount: string;
     /** Whether the token is a mint baton */
@@ -1163,3 +1270,11 @@
     type: 'Error';
     msg: string;
 }
+
+/** List of UTXOs */
+export interface TokenIdUtxos {
+    /** TokenId used to fetch these utxos */
+    tokenId: string;
+    /** UTXOs */
+    utxos: Utxo_InNode[];
+}
diff --git a/modules/chronik-client/test/integration/token_alp.ts b/modules/chronik-client/test/integration/token_alp.ts
--- a/modules/chronik-client/test/integration/token_alp.ts
+++ b/modules/chronik-client/test/integration/token_alp.ts
@@ -6,7 +6,12 @@
 import chaiAsPromised from 'chai-as-promised';
 import { ChildProcess } from 'node:child_process';
 import { EventEmitter, once } from 'node:events';
-import { ChronikClientNode, Tx_InNode } from '../../index';
+import {
+    ChronikClientNode,
+    Token_InNode,
+    TxHistoryPage_InNode,
+    Tx_InNode,
+} from '../../index';
 import initializeTestRunner from '../setup/testRunner';
 
 const expect = chai.expect;
@@ -21,6 +26,8 @@
     let get_alp_genesis2_txid: Promise<string>;
     let get_alp_multi_txid: Promise<string>;
     let get_alp_mega_txid: Promise<string>;
+    let get_alp_mint_two_txid: Promise<string>;
+    let get_alp_send_two_txid: Promise<string>;
     const statusEvent = new EventEmitter();
 
     before(async () => {
@@ -69,6 +76,18 @@
                 });
             }
 
+            if (message && message.alp_mint_two_txid) {
+                get_alp_mint_two_txid = new Promise(resolve => {
+                    resolve(message.alp_mint_two_txid);
+                });
+            }
+
+            if (message && message.alp_send_two_txid) {
+                get_alp_send_two_txid = new Promise(resolve => {
+                    resolve(message.alp_send_two_txid);
+                });
+            }
+
             if (message && message.status) {
                 statusEvent.emit(message.status);
             }
@@ -126,6 +145,8 @@
     let alpNextGenesisTxid = '';
     let alpMultiTxid = '';
     let alpMegaTxid = '';
+    let alpMintTwoTxid = '';
+    let alpSendTwoTxid = '';
 
     let alpGenesis: Tx_InNode;
     let alpMint: Tx_InNode;
@@ -133,6 +154,10 @@
     let alpNextGenesis: Tx_InNode;
     let alpMulti: Tx_InNode;
     let alpMega: Tx_InNode;
+    let alpMintTwo: Tx_InNode;
+    let alpSendTwo: Tx_InNode;
+
+    let confirmedTxsForAlpGenesisTxid: TxHistoryPage_InNode;
 
     it('Gets an ALP genesis tx from the mempool', async () => {
         const chronikUrl = await chronik_url;
@@ -157,7 +182,7 @@
         ]);
 
         // We get expected outputs including expected Token data
-        expect(alpGenesis.outputs).to.deep.equal([
+        const expectedOutputs = [
             {
                 ...BASE_TX_OUTPUT,
                 value: 0,
@@ -210,7 +235,8 @@
                     isMintBaton: true,
                 },
             },
-        ]);
+        ];
+        expect(alpGenesis.outputs).to.deep.equal(expectedOutputs);
 
         // We get a Entries of expected shape, with tokenId the txid for a genesis tx
         expect(alpGenesis.tokenEntries).to.deep.equal([
@@ -231,6 +257,61 @@
 
         // Normal status
         expect(alpGenesis.tokenStatus).to.eql('TOKEN_STATUS_NORMAL');
+
+        // We can get the same token info from calling chronik.tokenId.utxos() on this genesis txid
+        const utxosByTokenId = await chronik.tokenId(alpGenesisTxid).utxos();
+
+        // We get the calling tokenId returned
+        expect(utxosByTokenId.tokenId).to.eql(alpGenesisTxid);
+
+        // Utxos returned by token id include Token object matching the outputs, except they have no entryIdx
+
+        // Get only the outputs that are token utxos for alpGenesisTxid
+        const outputsWithTokenKey = expectedOutputs.filter(
+            output => 'token' in output,
+        );
+
+        const utxoTokenKeysFromOutputs: Token_InNode[] = [];
+        for (const output of outputsWithTokenKey) {
+            if ('token' in output) {
+                const { token } = output;
+                // Remove the entryIdx key from these outputs, as we do not expect to see it in tokenId.utxos() output
+                delete (token as Token_InNode).entryIdx;
+                utxoTokenKeysFromOutputs.push(output.token as Token_InNode);
+            }
+        }
+
+        // We have as many utxosByTokenId as we do outputs with token key
+        expect(utxosByTokenId.utxos.length).to.eql(
+            utxoTokenKeysFromOutputs.length,
+        );
+
+        // They match and are in the same order
+        for (let i = 0; i < utxosByTokenId.utxos.length; i += 1) {
+            expect(utxosByTokenId.utxos[i].token).to.deep.equal(
+                utxoTokenKeysFromOutputs[i],
+            );
+        }
+
+        // We get the same tx info for this tx from calling chronik.tokenId().unconfirmedTxs()
+        const unconfirmedTxsForThisTokenId = await chronik
+            .tokenId(alpGenesisTxid)
+            .unconfirmedTxs();
+        expect(unconfirmedTxsForThisTokenId.txs.length).to.eql(1);
+        expect(unconfirmedTxsForThisTokenId.txs[0]).to.deep.equal(alpGenesis);
+
+        // We get nothing from confirmedTxs() as none are confirmed
+        const confirmedTxsForThisTokenId = await chronik
+            .tokenId(alpGenesisTxid)
+            .confirmedTxs();
+        expect(confirmedTxsForThisTokenId.txs.length).to.eql(0);
+
+        // History returns the output of confirmed + unconfirmed (in this case, just unconfirmed)
+        const historyForThisTokenId = await chronik
+            .tokenId(alpGenesisTxid)
+            .history();
+        expect(historyForThisTokenId.txs.length).to.eql(1);
+        expect(historyForThisTokenId.txs[0]).to.deep.equal(alpGenesis);
     });
     it('Gets an ALP mint tx from the mempool', async () => {
         const chronikUrl = await chronik_url;
@@ -607,6 +688,45 @@
 
         // Normal status
         expect(alpMulti.tokenStatus).to.eql('TOKEN_STATUS_NORMAL');
+
+        // Test order of tokenId.history()
+        const unconfirmedTxs = await chronik
+            .tokenId(alpGenesisTxid)
+            .unconfirmedTxs();
+
+        const alphabeticalBroadcastAlpGenesisMempoolTxs = [
+            alpGenesisTxid,
+            alpMintTxid,
+            alpSendTxid,
+            alpMultiTxid,
+        ].sort();
+
+        // Unconfirmed txs come in alphabetical order by txid
+        // Note that the genesis tx was broadcast first but is alphabetically 2nd
+        for (let i = 0; i < unconfirmedTxs.txs.length; i += 1) {
+            expect(unconfirmedTxs.txs[i].txid).to.eql(
+                alphabeticalBroadcastAlpGenesisMempoolTxs[i],
+            );
+        }
+
+        // Test order of tokenId.history()
+        const historyTxs = await chronik.tokenId(alpGenesisTxid).history();
+
+        // History txs are sorted by blockheight, then timeFirstSeen, then reverse alphabetical by txid
+        // These txs all have the same blockheight and timeFirstSeen, so we see them in reverse alphabetical order
+        const reverseAlphabeticalBroadcastAlpGenesisMempoolTxs = [
+            alpGenesisTxid,
+            alpMintTxid,
+            alpSendTxid,
+            alpMultiTxid,
+        ]
+            .sort()
+            .reverse();
+        for (let i = 0; i < historyTxs.txs.length; i += 1) {
+            expect(historyTxs.txs[i].txid).to.eql(
+                reverseAlphabeticalBroadcastAlpGenesisMempoolTxs[i],
+            );
+        }
     });
     it('Can get all of the above txs, and a wild mega-tx, from the blockTxs endpoint after they are mined in a block', async () => {
         const chronikUrl = await chronik_url;
@@ -904,7 +1024,7 @@
             alpMega,
         ].sort((a, b) => a.txid.localeCompare(b.txid));
 
-        // The token fields of Tx_InNode(s) from blockTxs match the Tx_InNode(s) from tx]
+        // The token fields of Tx_InNode(s) from blockTxs match the Tx_InNode(s) from tx
         // Note the txs are not expected to fully match bc now we have block key and spentBy,
         // expected after confirmation
         // This type of functionality is tested in blocktxs_and_tx_and_rawtx.ts
@@ -939,5 +1059,260 @@
 
         // Same tx count as blockTxs
         expect(history.numTxs).to.eql(7);
+
+        // Now we have no unconfirmed txs for the alpGenesisTxid
+        const unconfirmedTxsForThisTokenId = await chronik
+            .tokenId(alpGenesisTxid)
+            .unconfirmedTxs();
+        expect(unconfirmedTxsForThisTokenId.txs.length).to.eql(0);
+
+        // We can get all the confirmedTxs for alpGenesisTxid
+        // Note: they are in alphabetical order by txid (at least, in this block)
+        const broadcastAlpTxsOfAlpGenesisTokenId = [
+            alpGenesis,
+            alpMint,
+            alpSend,
+            alpMulti,
+            alpMega,
+        ].sort((a, b) => a.txid.localeCompare(b.txid));
+
+        confirmedTxsForAlpGenesisTxid = await chronik
+            .tokenId(alpGenesisTxid)
+            .confirmedTxs();
+
+        expect(confirmedTxsForAlpGenesisTxid.txs.length).to.eql(
+            broadcastAlpTxsOfAlpGenesisTokenId.length,
+        );
+
+        // They are sorted by blockheight, then alphabetical by txid
+        // In this case, they all have the same blockheight -- so we see alphabetical by txid
+        // Note that timeFirstSeen is not considered from this endpoint, as alpMega has timeFirstSeen of 0
+        // But it appears second here
+        for (let i = 0; i < confirmedTxsForAlpGenesisTxid.txs.length; i += 1) {
+            // in practice, everything matches except for the 'block' and 'output.spentBy' keys
+            // these are expected to have changed since we stored the txs when they were in the mempool
+            // now we are comparing result to confirmed txs
+            expect(confirmedTxsForAlpGenesisTxid.txs[i].txid).to.eql(
+                broadcastAlpTxsOfAlpGenesisTokenId[i].txid,
+            );
+            expect(confirmedTxsForAlpGenesisTxid.txs[i].inputs).to.deep.equal(
+                broadcastAlpTxsOfAlpGenesisTokenId[i].inputs,
+            );
+            expect(
+                confirmedTxsForAlpGenesisTxid.txs[i].tokenEntries,
+            ).to.deep.equal(broadcastAlpTxsOfAlpGenesisTokenId[i].tokenEntries);
+        }
+    });
+    it('Can get confirmed and unconfirmed txs from tokenId.history()', async () => {
+        const chronikUrl = await chronik_url;
+        const chronik = new ChronikClientNode(chronikUrl);
+
+        alpMintTwoTxid = await get_alp_mint_two_txid;
+        alpSendTwoTxid = await get_alp_send_two_txid;
+
+        // Can these from the tx endpoint
+        alpMintTwo = await chronik.tx(alpMintTwoTxid);
+        alpSendTwo = await chronik.tx(alpSendTwoTxid);
+
+        // alpSendTwoTxid eb227bec6fd4d270262e14efa128a970f0ea4e529c599b1eb28af5fc8eaf3452 (broadcast 1st, alphabetically 2nd)
+        // alpMintTwoTxid 665c70b6c01b8e9b4865136ee650bae344fb8b45def49eb7ec303b04d2520b4b (broadcast 2nd, alphabetically 1st)
+        const alphabeticalUnconfirmedAlpGenesisTxs = [
+            alpMintTwo,
+            alpSendTwo,
+        ].sort((a, b) => a.txid.localeCompare(b.txid));
+
+        // Can get these from unconfirmed txs
+        const unconfirmedTxs = await chronik
+            .tokenId(alpGenesisTxid)
+            .unconfirmedTxs();
+
+        // unconfirmedTxs returns them in alphabetical order
+        for (let i = 0; i < unconfirmedTxs.txs.length; i += 1) {
+            expect(unconfirmedTxs.txs[i]).to.deep.equal(
+                alphabeticalUnconfirmedAlpGenesisTxs[i],
+            );
+        }
+
+        // They are the only unconfirmed txs
+        expect(unconfirmedTxs.txs.length).to.eql(2);
+
+        // Calling chronik.tokenId.history() returns all confirmed and unconfirmed txs
+
+        const allTokenTxsForAlpGenesisTxid = await chronik
+            .tokenId(alpGenesisTxid)
+            .history();
+
+        // We get all expected txs, confirmed and unconfirmed
+        expect(allTokenTxsForAlpGenesisTxid.txs.length).to.eql(
+            confirmedTxsForAlpGenesisTxid.txs.length +
+                unconfirmedTxs.txs.length,
+        );
+
+        // Txs from history are sorted by blockheight, then timeFirstSeen, then txid
+        // In this case, timeFirstSeen is a mock time, so it is the same for each
+        // So we expect reverse alphabetical order by txid
+        const unconfirmedInHistory = allTokenTxsForAlpGenesisTxid.txs.splice(
+            0,
+            2,
+        );
+
+        const reverseAlphabeticalUnconfirmedAlpGenesisTxs = [
+            alpMintTwo,
+            alpSendTwo,
+        ].sort((b, a) => a.txid.localeCompare(b.txid));
+        expect(unconfirmedInHistory).to.deep.equal(
+            reverseAlphabeticalUnconfirmedAlpGenesisTxs,
+        );
+
+        // We get the rest of the txs in expected order
+        // Sorted by blockheight, then timeFirstSeen, then txid
+        // In this case, all txs have the same blockheight
+        // alpMegaTxid has timeFirstSeen of 0 since it was manually mined
+        // All other txs have the same timeFirstSeen so are sorted in reverse alphabetical order
+
+        // Confirm the order of txs from .history()
+        const confirmedAlpTxs = [
+            alpMegaTxid, // timeFirstSeen 0             | 72101f535470e0a6de7db9ba0ba115845566f738cc5124255b472347b5927565
+            alpSendTxid, // timeFirstSeen 1300000000    | e623ab8971c93fa1a831a4310da65554c8dfd811c16cd5d41c6612268cb5dd5f
+            alpMultiTxid, // timeFirstSeen 1300000000   | e3c47f14d7ba3ab9a6f32a0fa8fcac41d06d3af595ebb5bab77ad03633a52eba
+            alpGenesisTxid, // timeFirstSeen 1300000000 | bb4d71aa6c0a92144f854402f2677975ad86d3a72cb7b0fb48d02473a88fc6e2
+            alpMintTxid, // timeFirstSeen 1300000000    | 0dab1008db30343a4f771983e9fd96cbc15f0c6efc73f5249c9bae311ef1e92f
+        ];
+
+        for (let i = 0; i < allTokenTxsForAlpGenesisTxid.txs.length; i += 1) {
+            expect(confirmedAlpTxs[i]).to.eql(
+                allTokenTxsForAlpGenesisTxid.txs[i].txid,
+            );
+        }
+
+        // Make sure other parts of returned tx objects are as expected
+        // Since one of the utxos from confirmedTxsForAlpGenesisTxid was spent to create
+        // the second mint tx, this spentBy key has changed
+
+        // We spent the mint baton
+        const newConfirmedMintTxIndex =
+            allTokenTxsForAlpGenesisTxid.txs.findIndex(
+                tx => tx.txid === alpMintTxid,
+            );
+
+        const newMintTx = allTokenTxsForAlpGenesisTxid.txs.splice(
+            newConfirmedMintTxIndex,
+            1,
+        )[0];
+        const confirmedMintTxIndex =
+            confirmedTxsForAlpGenesisTxid.txs.findIndex(
+                tx => tx.txid === alpMintTxid,
+            );
+        const oldMintTx = confirmedTxsForAlpGenesisTxid.txs.splice(
+            confirmedMintTxIndex,
+            1,
+        )[0];
+
+        // We have removed this tx from both histories
+        expect(allTokenTxsForAlpGenesisTxid.txs.length).to.eql(
+            confirmedTxsForAlpGenesisTxid.txs.length,
+        );
+
+        // They are the same except for outputs, as expected
+        expect(oldMintTx.inputs).to.deep.equal(newMintTx.inputs);
+        expect(oldMintTx.tokenEntries).to.deep.equal(newMintTx.tokenEntries);
+
+        // Since one of the utxos from confirmedTxsForAlpGenesisTxid was spent to create
+        // the second send tx, this spentBy key has changed
+        const newConfirmedSendTxIndex =
+            allTokenTxsForAlpGenesisTxid.txs.findIndex(
+                tx => tx.txid === alpSendTxid,
+            );
+
+        const newSendTx = allTokenTxsForAlpGenesisTxid.txs.splice(
+            newConfirmedSendTxIndex,
+            1,
+        )[0];
+        const confirmedSendTxIndex =
+            confirmedTxsForAlpGenesisTxid.txs.findIndex(
+                tx => tx.txid === alpSendTxid,
+            );
+        const oldSendTx = confirmedTxsForAlpGenesisTxid.txs.splice(
+            confirmedSendTxIndex,
+            1,
+        )[0];
+
+        // We have removed this tx from both histories
+        expect(allTokenTxsForAlpGenesisTxid.txs.length).to.eql(
+            confirmedTxsForAlpGenesisTxid.txs.length,
+        );
+
+        // They are the same except for outputs, as expected
+        expect(oldSendTx.inputs).to.deep.equal(newSendTx.inputs);
+        expect(oldSendTx.tokenEntries).to.deep.equal(newSendTx.tokenEntries);
+
+        // The other txs are the same, though the order is not the same
+        // allTokenTxsForAlpGenesisTxid is sorted by blockheight, then timeFirstSeen, then reverse alphabetical by txid
+        // confirmedTxsForAlpGenesisTxid is sorted by blockheight, then alphabetical by txid
+
+        // These respective orderings are already tested above, here we test they have the same content
+        expect(
+            allTokenTxsForAlpGenesisTxid.txs.sort((a, b) =>
+                a.txid.localeCompare(b.txid),
+            ),
+        ).to.deep.equal(
+            confirmedTxsForAlpGenesisTxid.txs.sort((a, b) =>
+                a.txid.localeCompare(b.txid),
+            ),
+        );
+    });
+    it('We get tx history in expected order from both tokenId().history() and tokenId.confirmedTxs()', async () => {
+        const chronikUrl = await chronik_url;
+        const chronik = new ChronikClientNode(chronikUrl);
+
+        // Can get all confirmed token txs for alpGenesisTxid
+        const confirmedTxs = await chronik
+            .tokenId(alpGenesisTxid)
+            .confirmedTxs();
+
+        // Confirmed txs are sorted by blockheight, then alphabetically by txid
+        // timeFirstSeen is not considered, demonstrated by alpMegaTxid not being first
+        const confirmedAlpTxids = [
+            alpMintTxid, // blockheight 102, timeFirstSeen 1300000000    | 0dab1008db30343a4f771983e9fd96cbc15f0c6efc73f5249c9bae311ef1e92f
+            alpMegaTxid, // blockheight 102, timeFirstSeen 0             | 72101f535470e0a6de7db9ba0ba115845566f738cc5124255b472347b5927565
+            alpGenesisTxid, // blockheight 102, timeFirstSeen 1300000000 | bb4d71aa6c0a92144f854402f2677975ad86d3a72cb7b0fb48d02473a88fc6e2
+            alpMultiTxid, // blockheight 102, timeFirstSeen 1300000000   | e3c47f14d7ba3ab9a6f32a0fa8fcac41d06d3af595ebb5bab77ad03633a52eba
+            alpSendTxid, //blockheight 102, timeFirstSeen 1300000000     | e623ab8971c93fa1a831a4310da65554c8dfd811c16cd5d41c6612268cb5dd5f
+            alpMintTwoTxid, // blockheight 103, timeFirstSeen 1300000000 | 665c70b6c01b8e9b4865136ee650bae344fb8b45def49eb7ec303b04d2520b4b
+            alpSendTwoTxid, // blockheight 103, timeFirstSeen 1300000000 | eb227bec6fd4d270262e14efa128a970f0ea4e529c599b1eb28af5fc8eaf3452
+        ];
+
+        // Same amount of txs in each
+        expect(confirmedTxs.txs.length).to.eql(confirmedAlpTxids.length);
+
+        // Txs are in expected order
+        for (let i = 0; i < confirmedTxs.txs.length; i += 1) {
+            expect(confirmedTxs.txs[i].txid).to.eql(confirmedAlpTxids[i]);
+        }
+
+        // Can get all confirmed token txs for alpGenesisTxid
+        const history = await chronik.tokenId(alpGenesisTxid).history();
+
+        // Txs from history are ordered by blockheight, then timeFirstSeen, then reverse alphabetical by txid
+        // In this case, alpSendTwoTxid and alpMintTwoTxid are from the highest blockheight.
+        // alpMegaTxid has timeFirstSeen of 0 because it had to be manually mined in block. So, it comes first in the next block
+        // The other txids in the alpMegaTxid block all have the same timeFirstSeen, so they are sorted reverse alphabetically
+        const historyAlpTxids = [
+            alpSendTwoTxid, // timeFirstSeen 1300000000, blockheight 103 | eb227bec6fd4d270262e14efa128a970f0ea4e529c599b1eb28af5fc8eaf3452
+            alpMintTwoTxid, // timeFirstSeen 1300000000, blockheight 103 | 665c70b6c01b8e9b4865136ee650bae344fb8b45def49eb7ec303b04d2520b4b
+            alpMegaTxid, // timeFirstSeen 0, blockheight 102             | 72101f535470e0a6de7db9ba0ba115845566f738cc5124255b472347b5927565
+            alpSendTxid, // timeFirstSeen 1300000000, blockheight 102    | e623ab8971c93fa1a831a4310da65554c8dfd811c16cd5d41c6612268cb5dd5f
+            alpMultiTxid, // timeFirstSeen 1300000000, blockheight 102   | e3c47f14d7ba3ab9a6f32a0fa8fcac41d06d3af595ebb5bab77ad03633a52eba
+            alpGenesisTxid, // timeFirstSeen 1300000000, blockheight 102 | bb4d71aa6c0a92144f854402f2677975ad86d3a72cb7b0fb48d02473a88fc6e2
+            alpMintTxid, // timeFirstSeen 1300000000, blockheight 102    | 0dab1008db30343a4f771983e9fd96cbc15f0c6efc73f5249c9bae311ef1e92f
+        ];
+
+        // Same amount of txs in each
+        expect(history.txs.length).to.eql(historyAlpTxids.length);
+
+        // Txs in expected order
+        for (let i = 0; i < confirmedTxs.txs.length; i += 1) {
+            expect(history.txs[i].txid).to.eql(historyAlpTxids[i]);
+        }
     });
 });
diff --git a/test/functional/setup_scripts/chronik-client_token_alp.py b/test/functional/setup_scripts/chronik-client_token_alp.py
--- a/test/functional/setup_scripts/chronik-client_token_alp.py
+++ b/test/functional/setup_scripts/chronik-client_token_alp.py
@@ -308,8 +308,70 @@
         assert_equal(node.getblockcount(), 102)
         yield True
 
-        self.log.info("Step 7: Send wild oversized ALP tx")
-        assert_equal(node.getblockcount(), 102)
+        self.log.info("Step 7: Send another ALP tx in the mempool")
+        # Another ALP Send tx
+        alp_send_two_tx = CTransaction()
+        alp_send_two_tx.vin = [
+            CTxIn(
+                COutPoint(int(another_alp_genesis_tx_txid, 16), 4),
+                SCRIPTSIG_OP_TRUE,
+            ),
+            CTxIn(
+                COutPoint(int(alp_send_tx_txid, 16), 2),
+                SCRIPTSIG_OP_TRUE,
+            ),
+        ]
+        alp_send_two_tx.vout = [
+            alp_opreturn(
+                alp_send(
+                    token_id=alp_genesis_tx_txid,
+                    output_amounts=[10, 1],
+                ),
+            ),
+            CTxOut(coinvalue - 300000, P2SH_OP_TRUE),
+            CTxOut(546, P2SH_OP_TRUE),
+        ]
+        alp_send_two_tx_txid = node.sendrawtransaction(
+            alp_send_two_tx.serialize().hex()
+        )
+        self.log.info(f"alp_send_two_tx_txid: {alp_send_two_tx_txid}")
+        send_ipc_message({"alp_send_two_txid": alp_send_two_tx_txid})
+
+        # Another ALP Mint tx
+        alp_mint_two_tx = CTransaction()
+        alp_mint_two_tx.vin = [
+            CTxIn(
+                # COutPoint(int(another_alp_genesis_tx_txid, 16), 4),
+                COutPoint(int(alp_send_two_tx_txid, 16), 1),
+                SCRIPTSIG_OP_TRUE,
+            ),
+            CTxIn(
+                COutPoint(int(alp_mint_tx_txid, 16), 3),
+                SCRIPTSIG_OP_TRUE,
+            ),
+        ]
+        alp_mint_two_tx.vout = [
+            alp_opreturn(
+                alp_mint(
+                    token_id=alp_genesis_tx_txid,
+                    mint_amounts=[5, 0],
+                    num_batons=1,
+                ),
+            ),
+            CTxOut(546, P2SH_OP_TRUE),
+            CTxOut(546, P2SH_OP_TRUE),
+            CTxOut(546, P2SH_OP_TRUE),
+            CTxOut(coinvalue - 400000, P2SH_OP_TRUE),
+        ]
+        alp_mint_two_tx_txid = node.sendrawtransaction(
+            alp_mint_two_tx.serialize().hex()
+        )
+        send_ipc_message({"alp_mint_two_txid": alp_mint_two_tx_txid})
+        yield True
+
+        self.log.info("Step 8: Confirm your second ALP tx")
+        self.generate(node, 1)
+        assert_equal(node.getblockcount(), 103)
         yield True