{
 "number": 32861,
 "repo": "bitcoin/bitcoin",
 "url": "https://github.com/bitcoin/bitcoin/pull/32861",
 "title": "Have createwalletdescriptor auto-detect an unused(KEY)",
 "author": "Sjors",
 "author_association": "MEMBER",
 "created_at": "2025-07-03T08:55:40Z",
 "updated_at": "2026-09-17T10:58:29Z",
 "age_days": 441,
 "draft": false,
 "labels": [],
 "milestone": null,
 "base": "master",
 "head_sha": "bf0af8ae296afec325de542fd5d84a41bba028f6",
 "head_ref": "2025/07/smart-createwalletdescriptor",
 "head_repo": "Sjors/bitcoin",
 "head_history": [
  {
   "t": "2025-07-25T07:06:27Z",
   "sha": "604955304dfca14b1c0bb5ed1044141a5d1b2d79"
  },
  {
   "t": "2026-01-05T03:21:13Z",
   "sha": "f2752efd5aa6eb1dadf2012452b31ae362809ba1"
  },
  {
   "t": "2026-02-02T15:53:27Z",
   "sha": "c8b6d4ee2abbd1271e44aa66da6079cee6323529"
  },
  {
   "t": "2026-05-14T12:35:24Z",
   "sha": "fb57795e14400b3d3a969b88646bc0bab310acee"
  },
  {
   "t": "2026-05-29T08:49:30Z",
   "sha": "13d807e697572aaa4a966756467325520e53e56e"
  },
  {
   "t": "2026-07-03T18:17:30Z",
   "sha": "c4249c01d97b236321bbe4c0f65ca2748597772b"
  },
  {
   "t": "2026-08-06T12:39:13Z",
   "sha": "fe1a296ccfdc4ffb65481626d591e10249146520"
  },
  {
   "t": "2026-08-17T12:25:31Z",
   "sha": "d46c58d7602428ca263c7c3db0a429df4f31772c"
  },
  {
   "t": "2026-08-20T09:04:20Z",
   "sha": "9d8e530f52db5ec13f7d2ca846ebfbb158ab6062"
  },
  {
   "t": "2026-08-25T08:12:45Z",
   "sha": "9d4eb1f50d59a258592d4faab5e7c6daa2616a53"
  },
  {
   "t": "2026-09-14T14:08:30Z",
   "sha": "bf0af8ae296afec325de542fd5d84a41bba028f6"
  }
 ],
 "additions": 74,
 "deletions": 9,
 "changed_files": 7,
 "commit_count": 5,
 "size_bucket": "S",
 "mergeable_state": "clean",
 "bot": {
  "drahtbot": {
   "present": true,
   "reviews": {
    "concept_ack": [
     {
      "login": "w0xlt",
      "url": "https://github.com/bitcoin/bitcoin/pull/32861#issuecomment-4653599445"
     },
     {
      "login": "jeanpablojp",
      "url": "https://github.com/bitcoin/bitcoin/pull/32861#pullrequestreview-5013245082"
     }
    ],
    "approach_ack": [
     {
      "login": "adyshimony",
      "url": "https://github.com/bitcoin/bitcoin/pull/32861#issuecomment-3939650879"
     }
    ]
   },
   "conflicts": [
    {
     "number": 36133,
     "title": "wallet: store multipath descriptor",
     "author": "Sjors"
    }
   ]
  }
 },
 "acks_parsed": {
  "w0xlt": {
   "kind": "concept_ack",
   "hash": null,
   "t": "2026-06-08T21:12:55Z",
   "stale": false
  },
  "jeanpablojp": {
   "kind": "concept_ack",
   "hash": null,
   "t": "2026-08-24T22:38:13Z",
   "stale": false
  }
 },
 "acks_tally": {
  "ack": 0,
  "stale_ack": 0,
  "concept_ack": 2,
  "approach_ack": 0,
  "nack": 0,
  "concept_nack": 0,
  "approach_nack": 0
 },
 "reviews": {
  "approved": 0,
  "changes_requested": 0,
  "distinct_reviewers": [
   "adyshimony",
   "jeanpablojp",
   "rkrux",
   "w0xlt"
  ]
 },
 "signals": {
  "needs_rebase": false,
  "ci_failed": false,
  "mergeable_state": "clean",
  "last_author_activity": "2026-09-14T14:08:34Z",
  "last_reviewer_activity": "2026-08-24T22:38:13Z",
  "last_reviewer": "jeanpablojp",
  "author_silent_days": 3,
  "waiting_on_author_days": 0,
  "days_since_update": 0
 },
 "refs": {
  "mentioned": [
   29130,
   29136,
   32784,
   35436
  ],
  "depends_on": [],
  "fixes": [],
  "linked_issues": [],
  "references": [
   {
    "number": 29130,
    "type": "pull",
    "state": "closed",
    "merged": true,
    "merged_at": "2024-03-29",
    "title": "wallet: Add `createwalletdescriptor` and `gethdkeys` RPCs for adding new automatically generated descriptors"
   },
   {
    "number": 29136,
    "type": "pull",
    "state": "closed",
    "merged": true,
    "merged_at": "2026-05-14",
    "title": "wallet: `addhdkey` RPC to add just keys to wallets via new `unused(KEY)` descriptor"
   },
   {
    "number": 32784,
    "type": "pull",
    "state": "closed",
    "merged": true,
    "merged_at": "2026-08-15",
    "title": "wallet: derivehdkey RPC to get xpub at arbitrary path"
   },
   {
    "number": 35436,
    "type": "pull",
    "state": "closed",
    "merged": true,
    "merged_at": "2026-09-14",
    "title": "wallet: Add addHDkey interface"
   }
  ],
  "conflicts": [
   36133
  ]
 },
 "stack": {
  "shares_commits_with": [],
  "based_on": [],
  "base_for": []
 },
 "review_paths": [
  "src/test/descriptor_tests.cpp",
  "src/wallet/rpc/wallet.cpp",
  "test/functional/wallet_hd.py"
 ],
 "body": "The `createwalletdescriptor` was introduced in #29130 to let users add a `tr()` descriptor to an existing SegWit wallet. The new `addhdkey` method from #29136 introduces a new potential workflow: start from a blank wallet, generate an HD and then add only the descriptors you need, e.g.:\n\n```sh\nbitcoin rpc createwallet TaprootMaxi blank=true\nbitcoin rpc addhdkey\nbitcoin rpc createwalletdescriptor bech32m\n```\n\nBefore this PR the last line would fail, requiring the user to call `gethdkeys` and copy-paste the xpub.\n\nThis PR makes `createwalletdescriptor` a bit smarter so it just finds the `unused(KEY)` generated by `hdkey` and uses that.\n\nIf multiple `unused(KEY)` descriptors are present the user still has to pick one.\n\nA potential followup is to make our [multisig tutorial](https://github.com/bitcoin/bitcoin/blob/master/doc/multisig-tutorial.md) slightly safer to use. Rather than creating a full wallet, the instruction could be changed to start with a blank wallet and only generate the default legacy descriptor. This avoids accidental use of single sig `p2sh-segwit` and `bech32` addresses.\n\n--\n\nThe first two commits are quick followups for #29136.",
 "commits": [
  {
   "sha": "5d0e71dc7f87eec111e9314c0cb0f938f7f719d8",
   "date": "2026-09-14T14:02:57Z",
   "message": "test: check unused xprv descriptor pubkeys\n\nCo-authored-by: adyshimony <6388409+adyshimony@users.noreply.github.com>"
  },
  {
   "sha": "6d668f0ba22b8b0f97efcb5e9e3975219bbb03db",
   "date": "2026-09-14T14:05:43Z",
   "message": "wallet: reject duplicate addhdkey xprvs\n\nCo-authored-by: adyshimony <6388409+adyshimony@users.noreply.github.com>"
  },
  {
   "sha": "ef71fb55b4dbef4f83070f020a65746fb7da18fa",
   "date": "2026-09-14T14:05:43Z",
   "message": "rpc: make createwalletdescriptor smarter\n\nWhen a wallet contains only an unused(KEY) descriptor, use it. Previously the user would have to call listdescriptors and manually specify it."
  },
  {
   "sha": "2a567f8b55505c54e94945b57167ffb70e8eeab9",
   "date": "2026-09-14T14:05:43Z",
   "message": "doc: use a blank wallet in the multisig tutorial\n\nThe participant wallets are only used to derive an xpub and sign PSBTs\nfor the multisig wallet. Creating them blank and adding just the legacy\ndescriptor avoids handing the reader singlesig addresses they are not\nsupposed to use.\n\nThe legacy descriptor can't be dropped entirely because the signing code\nonly considers descriptors whose derivation paths cover the input.\n\nTo further discourage use of the singlesig descriptors, the PSBT example\nnow pays a separate recipient wallet rather than participant_1."
  },
  {
   "sha": "bf0af8ae296afec325de542fd5d84a41bba028f6",
   "date": "2026-09-14T14:05:43Z",
   "message": "doc: add release note for #32861"
  }
 ],
 "timeline": [
  {
   "t": "2025-07-18T09:38:11Z",
   "kind": "comment",
   "who": "rkrux",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nOnce the unused(KEY) descriptor is used, doesn't it become used? Keeping it as an unused descriptor later might come across as confusing."
  },
  {
   "t": "2025-07-18T09:44:14Z",
   "kind": "review_comment",
   "who": "rkrux",
   "assoc": "MEMBER",
   "path": "src/wallet/rpc/wallet.cpp",
   "commit": "604955304dfca14b1c0bb5ed1044141a5d1b2d79",
   "in_reply_to": null,
   "text": "In e8b1440aa3ed7efe3a9c2d10afb05e7247fff1f6 \"rpc: make createwalletdescriptor smarter\"\n\nThough this check here seems more thorough, but the presence of more than one `spkm` also seems sufficient to throw this error?"
  },
  {
   "t": "2025-07-18T09:50:28Z",
   "kind": "comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nI agree, and it was also brought up here: https://github.com/bitcoin/bitcoin/pull/29136#issuecomment-1868202696. It's orthogonal to this PR."
  },
  {
   "t": "2025-07-18T10:19:13Z",
   "kind": "comment",
   "who": "rkrux",
   "assoc": "MEMBER",
   "text": "Thanks, I recall reading this comment earlier but forgot about it later. If I am not missing anything, I don't suppose this point is orthogonal to this PR?\n\nThe linked comment also states that any RPC using the unused descriptor should delete it immediately afterwords.\n[quoted text omitted]\n\nI don't believe `generatewalletdescriptor` is present as an RPC in the codebase, and `createwalletdescriptor` stores the newly created descriptor in the database as I verified from the below patch. I can see the unused descriptor present after this RPC as well. Shouldn't we add the deletion of the unused descriptor in this PR itself?\n\n```diff\ndiff --git a/test/functional/wallet_createwalletdescriptor.py b/test/functional/wallet_createwalletdescriptor.py\nindex 6de0ca4782..a7086c5b9e 100755\n--- a/test/functional/wallet_createwalletdescriptor.py\n+++ b/test/functional/wallet_createwalletdescriptor.py\n@@ -125,7 +125,11 @@ class WalletCreateDescriptorTest(BitcoinTestFramework):\n\n         # Create unused(KEY) descriptor and try again\n         w1.addhdkey()\n+        print(\"listdescriptors: \", w1.listdescriptors())\n+        print(\"gethdkeys: \", w1.gethdkeys())\n         w1.createwalletdescriptor(type=\"bech32\")\n+        print(\"listdescriptors: \", w1.listdescriptors())\n+        print(\"gethdkeys: \", w1.gethdkeys())\n\n         self.nodes[0].createwallet(\"w2\", blank=True)\n         w2 = self.nodes[0].get_wallet_rpc(\"w2\")\n(END)\n```"
  },
  {
   "t": "2025-07-25T07:06:27Z",
   "kind": "force_push",
   "who": "Sjors",
   "commit": "604955304dfca14b1c0bb5ed1044141a5d1b2d79"
  },
  {
   "t": "2025-07-25T07:12:28Z",
   "kind": "comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nThat should be done in #29136 which introduces `createwalletdescriptor `, or in a followup, but not in this PR (which consists of only the last two commits)."
  },
  {
   "t": "2026-01-05T03:21:13Z",
   "kind": "force_push",
   "who": "Sjors",
   "commit": "f2752efd5aa6eb1dadf2012452b31ae362809ba1"
  },
  {
   "t": "2026-02-02T15:53:27Z",
   "kind": "force_push",
   "who": "Sjors",
   "commit": "c8b6d4ee2abbd1271e44aa66da6079cee6323529"
  },
  {
   "t": "2026-02-21T22:29:44Z",
   "kind": "review_comment",
   "who": "adyshimony",
   "assoc": "NONE",
   "path": "src/test/descriptor_tests.cpp",
   "commit": "c8b6d4ee2abbd1271e44aa66da6079cee6323529",
   "in_reply_to": null,
   "text": "Should this call use parse_priv instead of parse_pub?"
  },
  {
   "t": "2026-02-21T22:44:38Z",
   "kind": "review_comment",
   "who": "adyshimony",
   "assoc": "NONE",
   "path": "src/wallet/rpc/wallet.cpp",
   "commit": "9d4eb1f50d59a258592d4faab5e7c6daa2616a53",
   "in_reply_to": null,
   "text": "I think a duplicate check is needed. What if the wallet already has this xprv through an active descriptor and user calls addhdkey with the same xprv?  The descriptor check would not catch that, because unused(xprv) is a different descriptor string. A check is needed here to prevent adding the same private key twice.\n\n```\nconst CExtPubKey hd_xpub{hdkey.Nexuter()};\nif (wallet->GetKey(hd_xpub.pubkey.GetID())) {\n    throw JSONRPCError(RPC_WALLET_ERROR, \"HD key already exists\");\n}\n```"
  },
  {
   "t": "2026-02-21T22:46:58Z",
   "kind": "review_comment",
   "who": "adyshimony",
   "assoc": "NONE",
   "path": "test/functional/wallet_hd.py",
   "commit": "bf0af8ae296afec325de542fd5d84a41bba028f6",
   "in_reply_to": null,
   "text": "Cover the case where addhdkey is called with existing xprv. Expected behavior is to reject it as \"HD key already exists\"\n\n```\nexisting_wallet_xprv = wallet.gethdkeys(private=True)[0][\"xprv\"]\nassert_raises_rpc_error(-4, \"HD key already exists\", wallet.addhdkey, existing_wallet_xprv)\n```"
  },
  {
   "t": "2026-02-21T22:57:40Z",
   "kind": "comment",
   "who": "adyshimony",
   "assoc": "NONE",
   "text": "Review and tested c8b6d4ee2abbd1271e44aa66da6079cee6323529\n\nConcept ACK, Approach ACK.\n\nI think I have found a small issue where a xprv already in wallet via active descriptors, and addhdkey(xprv) is called from RPC with the same xprv.\n\nSuggested a fix in the review comments."
  },
  {
   "t": "2026-05-14T12:28:29Z",
   "kind": "review_comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "path": "src/test/descriptor_tests.cpp",
   "commit": "c8b6d4ee2abbd1271e44aa66da6079cee6323529",
   "in_reply_to": 2836745917,
   "text": "Thanks, fixed in a new commit (even though it pertains to #29136)."
  },
  {
   "t": "2026-05-14T12:35:24Z",
   "kind": "force_push",
   "who": "Sjors",
   "commit": "fb57795e14400b3d3a969b88646bc0bab310acee"
  },
  {
   "t": "2026-05-14T12:35:28Z",
   "kind": "comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "text": "Rebased after #29136 landed and included suggestions from @adyshimony."
  },
  {
   "t": "2026-05-29T08:49:30Z",
   "kind": "force_push",
   "who": "Sjors",
   "commit": "13d807e697572aaa4a966756467325520e53e56e"
  },
  {
   "t": "2026-05-29T08:49:44Z",
   "kind": "comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "text": "Rebased after https://github.com/bitcoin/bitcoin/pull/28333."
  },
  {
   "t": "2026-06-08T21:12:55Z",
   "kind": "comment",
   "who": "w0xlt",
   "assoc": "CONTRIBUTOR",
   "text": "Concept ACK"
  },
  {
   "t": "2026-07-03T18:17:30Z",
   "kind": "force_push",
   "who": "Sjors",
   "commit": "c4249c01d97b236321bbe4c0f65ca2748597772b"
  },
  {
   "t": "2026-08-06T12:39:13Z",
   "kind": "force_push",
   "who": "Sjors",
   "commit": "fe1a296ccfdc4ffb65481626d591e10249146520"
  },
  {
   "t": "2026-08-17T12:25:30Z",
   "kind": "comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "text": "Rebased after #32784."
  },
  {
   "t": "2026-08-17T12:25:31Z",
   "kind": "force_push",
   "who": "Sjors",
   "commit": "d46c58d7602428ca263c7c3db0a429df4f31772c"
  },
  {
   "t": "2026-08-20T09:04:20Z",
   "kind": "force_push",
   "who": "Sjors",
   "commit": "9d8e530f52db5ec13f7d2ca846ebfbb158ab6062"
  },
  {
   "t": "2026-08-24T22:38:13Z",
   "kind": "review",
   "who": "jeanpablojp",
   "assoc": "CONTRIBUTOR",
   "state": "COMMENTED",
   "commit": "9d8e530f52db5ec13f7d2ca846ebfbb158ab6062",
   "text": "Concept ACK\n\nI noticed that on a wallet with two active keys plus one from `addhdkey`, `derivehdkey` uses the unused one while `createwalletdescriptor` errors with \"Unable to determine which HD key to use from active descriptors\". Master does the same. But now that this RPC knows about unused(KEY), shouldn't that lookup also apply when there's more than one active key?\n\nThe `hdkey` hint is stale now too. It still says \"The HD key used by all other active descriptors\", where `derivehdkey`'s mentions both cases."
  },
  {
   "t": "2026-08-24T22:38:13Z",
   "kind": "review_comment",
   "who": "jeanpablojp",
   "assoc": "CONTRIBUTOR",
   "path": "src/wallet/rpc/wallet.cpp",
   "commit": "9d8e530f52db5ec13f7d2ca846ebfbb158ab6062",
   "in_reply_to": null,
   "text": "`GetHDPubKeys(HDKeyFilter::UnusedKey)` already returns this set, and `derivehdkey` uses it further down in this same file. I swapped this block for a call to it, the wallet tests still pass and `GetScriptlessSPKMs()` goes away entirely. Is there a reason for the separate helper that I'm missing?"
  },
  {
   "t": "2026-08-25T08:12:45Z",
   "kind": "force_push",
   "who": "Sjors",
   "commit": "9d4eb1f50d59a258592d4faab5e7c6daa2616a53"
  },
  {
   "t": "2026-08-25T08:12:47Z",
   "kind": "comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "text": "The rule and help text is now:\n\n[quoted text omitted]\nIn the ambiguous case @jeanpablojp describes, it's better to err on the side of failing, because descriptors can't be deleted. In that sense it's different from `derivehdkey`.\n\n[quoted text omitted]\nFixed\n\nI also added a commit to use improve the multisig tutorial."
  },
  {
   "t": "2026-08-25T08:12:49Z",
   "kind": "review_comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "path": "src/wallet/rpc/wallet.cpp",
   "commit": "9d8e530f52db5ec13f7d2ca846ebfbb158ab6062",
   "in_reply_to": 3848043069,
   "text": "I dropped f336199336f619f6c361e56da06e36aa46f776c8, as it's indeed superseded by `GetHDPubKeys(HDKeyFilter::UnusedKey)` from #32784."
  },
  {
   "t": "2026-09-14T14:08:30Z",
   "kind": "force_push",
   "who": "Sjors",
   "commit": "bf0af8ae296afec325de542fd5d84a41bba028f6"
  },
  {
   "t": "2026-09-14T14:08:34Z",
   "kind": "comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "text": "Rebased after #35436."
  }
 ],
 "labels_log": [
  {
   "t": "2026-01-03T00:23:03Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-01-05T03:34:09Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-05-14T11:02:47Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-05-14T13:26:19Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-05-28T20:20:14Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-05-29T09:22:50Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-05-29T10:21:12Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-06-02T10:28:23Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-08-15T02:50:24Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-08-17T16:39:19Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-09-14T13:40:39Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-09-14T15:54:16Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  }
 ],
 "state_log": [
  {
   "t": "2026-05-14T12:35:30Z",
   "kind": "ready_for_review",
   "who": "Sjors"
  }
 ],
 "text_chars": 7007,
 "text_tokens_estimate": 1751,
 "changed_paths": [
  "doc/multisig-tutorial.md",
  "doc/release-notes-32861.md",
  "src/test/descriptor_tests.cpp",
  "src/wallet/rpc/wallet.cpp",
  "src/wallet/wallet.cpp",
  "test/functional/wallet_createwalletdescriptor.py",
  "test/functional/wallet_hd.py"
 ],
 "files": [
  {
   "path": "doc/multisig-tutorial.md",
   "add": 17,
   "del": 4
  },
  {
   "path": "doc/release-notes-32861.md",
   "add": 9,
   "del": 0
  },
  {
   "path": "src/test/descriptor_tests.cpp",
   "add": 1,
   "del": 1
  },
  {
   "path": "src/wallet/rpc/wallet.cpp",
   "add": 16,
   "del": 3
  },
  {
   "path": "src/wallet/wallet.cpp",
   "add": 7,
   "del": 0
  },
  {
   "path": "test/functional/wallet_createwalletdescriptor.py",
   "add": 22,
   "del": 1
  },
  {
   "path": "test/functional/wallet_hd.py",
   "add": 2,
   "del": 0
  }
 ],
 "test_lines": 27,
 "git": {
  "head": "bf0af8ae296afec325de542fd5d84a41bba028f6",
  "head_matches_backup": true,
  "base": "76343a09f8025f20ac6ec8dad073132e70ef2b0f",
  "commits": [
   {
    "sha": "5d0e71dc7f",
    "subject": "test: check unused xprv descriptor pubkeys",
    "files": 1,
    "add": 1,
    "del": 1
   },
   {
    "sha": "6d668f0ba2",
    "subject": "wallet: reject duplicate addhdkey xprvs",
    "files": 2,
    "add": 9,
    "del": 0
   },
   {
    "sha": "ef71fb55b4",
    "subject": "rpc: make createwalletdescriptor smarter",
    "files": 2,
    "add": 38,
    "del": 4
   },
   {
    "sha": "2a567f8b55",
    "subject": "doc: use a blank wallet in the multisig tutorial",
    "files": 1,
    "add": 17,
    "del": 4
   },
   {
    "sha": "bf0af8ae29",
    "subject": "doc: add release note for #32861",
    "files": 1,
    "add": 9,
    "del": 0
   }
  ],
  "patch_truncated": false
 },
 "input_hash": "b413aff1c7e9aba0",
 "extracted_at": "2026-09-17T16:15:31+00:00"
}