[{"data":1,"prerenderedAt":67},["ShallowReactive",2],{"doc-feedback":3},{"html":4,"headings":5},"\u003Cp>A public feedback page per project where your users post ideas and bugs, vote on what\nmatters, comment, and follow progress through a roadmap and a changelog.\nHelpBites knows nothing about your app: statuses, post types, platforms and\ntags are configured per project, and identity is the same host-signed token the chat\nuses.\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cstrong>Plug and play\u003C\u002Fstrong> — one script tag renders the whole feedback page (posts,\nroadmap, changelog, post pages, composer) inside your page.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Your own UI\u003C\u002Fstrong> — everything the embed does is on \u003Ccode>\u002Fv1\u002Ffeedback\u003C\u002Fcode>.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Operators\u003C\u002Fstrong> — Project → Feedback in the panel: list or kanban, edit and\nmoderate posts, reply as the team, configure the page.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Planning &amp; roadmap\u003C\u002Fstrong> — Project → Planning: Linear-style items linked to\nposts and conversations; the ones you pick make the public roadmap (see\n\u003Ca href=\"#planning--roadmap\">Planning &amp; roadmap\u003C\u002Fa>).\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>Three things used to share the word \u003Cem>board\u003C\u002Fem>. They are called \u003Cstrong>Feedback\u003C\u002Fstrong> (the\npublic page where users post and vote), \u003Cstrong>Planning\u003C\u002Fstrong> (your private kanban of\nwork items) and the \u003Cstrong>Roadmap\u003C\u002Fstrong> (the public view of the Planning items you\npublish). The API and the embed keep their names: \u003Ccode>view=board\u003C\u002Fcode> is the feedback\npage's column layout (\u003Cem>Columns\u003C\u002Fem> in the panel), and \u003Ccode>board\u003C\u002Fcode> and \u003Ccode>board-items\u003C\u002Fcode> are\nPlanning.\u003C\u002Fp>\n\u003Ch2 id=\"1-turn-it-on\">1. Turn it on\u003Ca class=\"heading-anchor\" href=\"#1-turn-it-on\" aria-label=\"Link to this section\">#\u003C\u002Fa>\u003C\u002Fh2>\n\u003Cp>Panel → your project → \u003Cstrong>Feedback\u003C\u002Fstrong> → \u003Cem>Turn on feedback\u003C\u002Fem>. Then open\n\u003Cstrong>Settings\u003C\u002Fstrong> to shape it:\u003C\u002Fp>\n\u003Cdiv class=\"table-wrap\">\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Setting\u003C\u002Fth>\n\u003Cth>What it does\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>Statuses\u003C\u002Ftd>\n\u003Ctd>Your own keys and labels. Each has a \u003Cstrong>category\u003C\u002Fstrong>: \u003Ccode>open\u003C\u002Fcode>, \u003Ccode>planned\u003C\u002Fcode>, \u003Ccode>active\u003C\u002Fcode>, \u003Ccode>done\u003C\u002Fcode> or \u003Ccode>closed\u003C\u002Fcode>. Categories carry the meaning: open\u002Fplanned\u002Factive show by default, planned\u002Factive\u002Fdone make the roadmap, and done\u002Fclosed close voting. A done post can feed a \u003Ca href=\"#changelog\">changelog entry\u003C\u002Fa>, but is not one itself. A bell marks statuses that email the post's followers when it moves there.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Post types\u003C\u002Ftd>\n\u003Ctd>Keys, labels and an icon — an Iconify name (\u003Ccode>lucide:bug\u003C\u002Fcode>, \u003Ccode>octicon:rocket-16\u003C\u002Fcode>) or an emoji.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Platforms\u003C\u002Ftd>\n\u003Ctd>Optional (\u003Ccode>web\u003C\u002Fcode>, \u003Ccode>ios\u003C\u002Fcode>, …), one per post. Empty hides the picker.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Tags\u003C\u002Ftd>\n\u003Ctd>Optional labels with a colour (\u003Ccode>editor\u003C\u002Fcode>, \u003Ccode>api\u003C\u002Fcode>, …); a post carries up to 4. Empty hides the picker.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Post URL\u003C\u002Ftd>\n\u003Ctd>Where a post lives on your site. \u003Ccode>{id}\u003C\u002Fcode>, \u003Ccode>{number}\u003C\u002Fcode> and \u003Ccode>{slug}\u003C\u002Fcode> are replaced: \u003Ccode>https:\u002F\u002Fapp.example.com\u002Ffeedback\u002F{number}-{slug}\u003C\u002Fcode>. Used by emails and RSS.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Options\u003C\u002Ftd>\n\u003Ctd>Comments, authors editing their own posts, showing author names publicly, email notifications, roadmap and changelog views.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\u003C\u002Fdiv>\n\u003Cp>The same settings are the \u003Ccode>feedback\u003C\u002Fcode> object on \u003Ccode>PATCH \u002Fv1\u002Fadmin\u002Fprojects\u002F:id\u003C\u002Fcode>\n(partial: send only what changes).\u003C\u002Fp>\n\u003Ch2 id=\"2a-plug-and-play\">2a. Plug and play\u003Ca class=\"heading-anchor\" href=\"#2a-plug-and-play\" aria-label=\"Link to this section\">#\u003C\u002Fa>\u003C\u002Fh2>\n\u003Cdiv class=\"code-block\">\u003Cbutton type=\"button\" class=\"copy-btn\" data-copy aria-label=\"Copy to clipboard\">Copy\u003C\u002Fbutton>\u003Cpre class=\"shj-lang-html\">\u003Ccode>\u003Cspan class=\"shj-syn-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-syn-var\">div\u003C\u002Fspan> \u003Cspan class=\"shj-syn-class\">id\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\"feedback\"\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">&gt;\u003C\u002Fspan>\u003Cspan class=\"shj-syn-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-syn-var\">div\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">&gt;\u003C\u002Fspan>\n\u003Cspan class=\"shj-syn-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-syn-var\">script\u003C\u002Fspan> \u003Cspan class=\"shj-syn-class\">src\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\"https:\u002F\u002FYOUR-SUPPORT-HOST\u002Ffeedback.js\"\u003C\u002Fspan> \u003Cspan class=\"shj-syn-class\">data-project\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\"pk_…\"\u003C\u002Fspan> \u003Cspan class=\"shj-syn-class\">data-target\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\"#feedback\"\u003C\u002Fspan> \u003Cspan class=\"shj-syn-class\">defer\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">&gt;\u003C\u002Fspan>\u003Cspan class=\"shj-syn-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-syn-var\">script\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">&gt;\u003C\u002Fspan>\n\u003Cspan class=\"shj-syn-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-syn-var\">script\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">&gt;\u003C\u002Fspan>\n  window\u003Cspan class=\"shj-syn-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-syn-class\">HelpBitesFeedback\u003C\u002Fspan> \u003Cspan class=\"shj-syn-oper\">=\u003C\u002Fspan> window\u003Cspan class=\"shj-syn-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-syn-class\">HelpBitesFeedback\u003C\u002Fspan> \u003Cspan class=\"shj-syn-oper\">||\u003C\u002Fspan> []\n  \u003Cspan class=\"shj-syn-class\">HelpBitesFeedback\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-syn-func\">push\u003C\u002Fspan>([\u003Cspan class=\"shj-syn-str\">'init'\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> {\n    \u003Cspan class=\"shj-syn-cmnt\">\u002F\u002F The chat's token endpoint works as is. Resolve to null when nobody is signed in.\n\u003C\u002Fspan>    getToken\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> () \u003Cspan class=\"shj-syn-kwd\">=&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-syn-func\">fetch\u003C\u002Fspan>(\u003Cspan class=\"shj-syn-str\">'\u002Fapi\u002Fsupport\u002Ftoken'\u003C\u002Fspan>)\u003Cspan class=\"shj-syn-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-syn-func\">then\u003C\u002Fspan>(r \u003Cspan class=\"shj-syn-kwd\">=&gt;\u003C\u002Fspan> r\u003Cspan class=\"shj-syn-oper\">.\u003C\u002Fspan>ok \u003Cspan class=\"shj-syn-oper\">?\u003C\u002Fspan> r\u003Cspan class=\"shj-syn-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-syn-func\">json\u003C\u002Fspan>() \u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-num\">null\u003C\u002Fspan>)\u003Cspan class=\"shj-syn-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-syn-func\">then\u003C\u002Fspan>(r \u003Cspan class=\"shj-syn-kwd\">=&gt;\u003C\u002Fspan> r \u003Cspan class=\"shj-syn-oper\">&#38;&#38;\u003C\u002Fspan> r\u003Cspan class=\"shj-syn-oper\">.\u003C\u002Fspan>token)\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan>\n    \u003Cspan class=\"shj-syn-cmnt\">\u002F\u002F Called when a signed-out visitor tries to post, vote or comment.\n\u003C\u002Fspan>    onSignIn\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> () \u003Cspan class=\"shj-syn-kwd\">=&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-syn-func\">openLoginModal\u003C\u002Fspan>()\n  }])\n\u003Cspan class=\"shj-syn-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-syn-var\">script\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">&gt;\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fdiv>\n\u003Cul>\n\u003Cli>\u003Cstrong>Inline\u003C\u002Fstrong> (with \u003Ccode>data-target\u003C\u002Fcode>): the board is part of your page and the frame\ngrows with its content. \u003Cstrong>Floating\u003C\u002Fstrong> (no target): a \u003Cem>Feedback\u003C\u002Fem> button opens it\nin a panel.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Reading is open to everyone\u003C\u002Fstrong> with the public key. Writing needs a token;\nwithout one the board shows a sign-in prompt and calls your \u003Ccode>onSignIn\u003C\u002Fcode>.\u003C\u002Fli>\n\u003Cli>After your user signs in or out: \u003Ccode>HelpBitesFeedback.push(['refresh'])\u003C\u002Fcode>.\u003C\u002Fli>\n\u003Cli>Other calls: \u003Ccode>['open']\u003C\u002Fcode>, \u003Ccode>['close']\u003C\u002Fcode>, \u003Ccode>['openPost', id]\u003C\u002Fcode>,\n\u003Ccode>['setView', 'board' | 'roadmap' | 'changelog']\u003C\u002Fcode>.\u003C\u002Fli>\n\u003Cli>Options (in \u003Ccode>init\u003C\u002Fcode> or as \u003Ccode>data-*\u003C\u002Fcode> on the script tag): \u003Ccode>target\u003C\u002Fcode>, \u003Ccode>view\u003C\u002Fcode>,\n\u003Ccode>only\u003C\u002Fcode> (\u003Ccode>roadmap\u003C\u002Fcode>: the roadmap alone, see below), \u003Ccode>layout\u003C\u002Fcode> (the roadmap's:\n\u003Ccode>columns\u003C\u002Fcode> · \u003Ccode>timeline\u003C\u002Fcode> · \u003Ccode>list\u003C\u002Fcode>; default the board's), \u003Ccode>theme\u003C\u002Fcode> (\u003Ccode>system\u003C\u002Fcode> ·\n\u003Ccode>light\u003C\u002Fcode> · \u003Ccode>dark\u003C\u002Fcode>; default follows a \u003Ccode>dark\u003C\u002Fcode>\u002F\u003Ccode>light\u003C\u002Fcode> class on your \u003Ccode>&lt;html&gt;\u003C\u002Fcode>),\n\u003Ccode>platform\u003C\u002Fcode> (preselected for new posts), \u003Ccode>position\u003C\u002Fcode> (\u003Ccode>right\u003C\u002Fcode> · \u003Ccode>left\u003C\u002Fcode>),\n\u003Ccode>label\u003C\u002Fcode>, \u003Ccode>accent\u003C\u002Fcode>, \u003Ccode>deepLinkParam\u003C\u002Fcode> (default \u003Ccode>feedback\u003C\u002Fcode>), \u003Ccode>syncUrl\u003C\u002Fcode>.\u003C\u002Fli>\n\u003Cli>Deep links: \u003Ccode>?feedback=&lt;postId&gt;\u003C\u002Fcode> on your page opens that post. Inline boards\nkeep that parameter in your URL as people open posts, so links are shareable.\u003C\u002Fli>\n\u003Cli>The colours, logo and name come from the project settings.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>Only the project's \u003Cstrong>allowed origins\u003C\u002Fstrong> can hand the board a token; any other\nsite still gets a read-only board.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>The changelog on its own page\u003C\u002Fstrong>: \u003Ccode>https:\u002F\u002FYOUR-SUPPORT-HOST\u002Fchangelog\u002Fpk_…\u003C\u002Fcode>\n(optional \u003Ccode>?theme=light|dark|system\u003C\u002Fcode>) shows the changelog, read-only. It needs only\n\u003Cem>Public changelog\u003C\u002Fem> on in Project → Changelog → Settings, not the board; the\n\u003Cem>Public changelog\u003C\u002Fem> button there copies the link. To put it in your page, add\n\u003Ccode>&lt;div id=&quot;changelog&quot;&gt;&lt;\u002Fdiv&gt;&lt;script src=&quot;https:\u002F\u002FYOUR-SUPPORT-HOST\u002Fchangelog.js&quot; data-project=&quot;pk_…&quot; data-target=&quot;#changelog&quot; defer&gt;&lt;\u002Fscript&gt;\u003C\u002Fcode>\n(\u003Ccode>data-theme\u003C\u002Fcode> optional). The same\nentries are at \u003Ccode>GET \u002Fv1\u002Fprojects\u002Fpk_…\u002Fchangelog.rss\u003C\u002Fcode> and \u003Ccode>GET \u002Fv1\u002Fchangelog?pk=pk_…\u003C\u002Fcode>;\nsee \u003Ca href=\"#changelog\">Changelog\u003C\u002Fa>.\u003C\u002Fp>\n\u003Ch2 id=\"2b-your-own-ui\">2b. Your own UI\u003Ca class=\"heading-anchor\" href=\"#2b-your-own-ui\" aria-label=\"Link to this section\">#\u003C\u002Fa>\u003C\u002Fh2>\n\u003Cp>Every route is in the OpenAPI spec (\u003Ccode>\u002F_docs\u002Fscalar\u003C\u002Fcode>, tag \u003Cem>Feedback\u003C\u002Fem>).\u003C\u002Fp>\n\u003Cdiv class=\"table-wrap\">\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>\u003C\u002Fth>\n\u003Cth>\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ccode>GET \u002Fv1\u002Ffeedback\u002Fconfig\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>Statuses, types, platforms, options, project name\u002Ftheme, the viewer\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>GET \u002Fv1\u002Ffeedback\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>Posts. Filters: \u003Ccode>view\u003C\u002Fcode> (\u003Ccode>board\u003C\u002Fcode>, \u003Ccode>roadmap\u003C\u002Fcode>, \u003Ccode>changelog\u003C\u002Fcode> — posts marked \u003Ccode>showOnChangelog\u003C\u002Fcode>; \u003Cstrong>deprecated\u003C\u002Fstrong>, read \u003Ccode>GET \u002Fv1\u002Fchangelog\u003C\u002Fcode>), \u003Ccode>status\u003C\u002Fcode>, \u003Ccode>category\u003C\u002Fcode>, \u003Ccode>type\u003C\u002Fcode>, \u003Ccode>platform\u003C\u002Fcode>, \u003Ccode>tag\u003C\u002Fcode>, \u003Ccode>featured\u003C\u002Fcode>, \u003Ccode>mine\u003C\u002Fcode>, \u003Ccode>subscribed\u003C\u002Fcode>, \u003Ccode>q\u003C\u002Fcode>; \u003Ccode>sort\u003C\u002Fcode> (\u003Ccode>top\u003C\u002Fcode>, \u003Ccode>new\u003C\u002Fcode>, \u003Ccode>activity\u003C\u002Fcode>, \u003Ccode>completed\u003C\u002Fcode>); \u003Ccode>limit\u003C\u002Fcode> ≤ 500, \u003Ccode>offset\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>POST \u002Fv1\u002Ffeedback\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>New post: \u003Ccode>{ title, body, type, platform, tags }\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>GET \u002Fv1\u002Ffeedback\u002F:id\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>One post with its comments, plus \u003Ccode>linked\u003C\u002Fcode> (the \u003Ccode>#N\u003C\u002Fcode> posts it and its comments mention) and \u003Ccode>backrefs\u003C\u002Fcode> (posts mentioning it). \u003Ccode>:id\u003C\u002Fcode> may be the id, the number (\u003Ccode>12\u003C\u002Fcode> or \u003Ccode>12-any-slug\u003C\u002Fcode>) or the \u003Ccode>externalId\u003C\u002Fcode> it was imported with — in every per-post route\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>PATCH \u002Fv1\u002Ffeedback\u002F:id\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>Edit\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>DELETE \u002Fv1\u002Ffeedback\u002F:id\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>Delete\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>POST \u002Fv1\u002Ffeedback\u002F:id\u002Fvote\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>{ &quot;voted&quot;: true }\u003C\u002Fcode> \u002F \u003Ccode>{ &quot;voted&quot;: false }\u003C\u002Fcode> (safe to retry), or \u003Ccode>{}\u003C\u002Fcode> to toggle\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>POST \u002Fv1\u002Ffeedback\u002F:id\u002Freactions\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>{ key }\u003C\u002Fcode> toggles a reaction (\u003Ccode>+1\u003C\u002Fcode>, \u003Ccode>-1\u003C\u002Fcode>, \u003Ccode>laugh\u003C\u002Fcode>, \u003Ccode>hooray\u003C\u002Fcode>, \u003Ccode>confused\u003C\u002Fcode>, \u003Ccode>heart\u003C\u002Fcode>, \u003Ccode>rocket\u003C\u002Fcode>, \u003Ccode>eyes\u003C\u002Fcode>); add \u003Ccode>reacted\u003C\u002Fcode> to set it\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>PUT \u002Fv1\u002Ffeedback\u002F:id\u002Fsubscription\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>{ subscribed?, muted?, read? }\u003C\u002Fcode>: follow, unfollow, mute, mark read\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>GET \u002Fv1\u002Ffeedback\u002F:id\u002Fparticipants?q=\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>Who \u003Ccode>@\u003C\u002Fcode> can complete in the thread\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>POST \u002Fv1\u002Ffeedback\u002F:id\u002Fmerge\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>Admins: \u003Ccode>{ into, status? }\u003C\u002Fcode> merges a duplicate; \u003Ccode>{ into: null }\u003C\u002Fcode> unlinks\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>GET\u003C\u002Fcode>\u002F\u003Ccode>POST \u002Fv1\u002Ffeedback\u002F:id\u002Fcomments\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>Comments: \u003Ccode>{ body, parentId?, replyToId?, visibility? }\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>PATCH\u003C\u002Fcode>\u002F\u003Ccode>DELETE \u002Fv1\u002Ffeedback\u002F:id\u002Fcomments\u002F:commentId\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>Edit (\u003Ccode>body\u003C\u002Fcode>; admins also \u003Ccode>visibility\u003C\u002Fcode>) or delete a comment\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>POST \u002Fv1\u002Ffeedback\u002F:id\u002Fcomments\u002F:commentId\u002Freactions\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>React to a comment\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>GET\u003C\u002Fcode>\u002F\u003Ccode>PUT \u002Fv1\u002Ffeedback\u002Fpreferences\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>{ emails }\u003C\u002Fcode>: the user's feedback email switch\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>GET \u002Fv1\u002Ffeedback\u002Fevents\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>Server-sent events for the board\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>GET \u002Fv1\u002Fchangelog\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>The public changelog: published entries under their published releases (\u003Ccode>?pk=\u003C\u002Fcode>)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>GET \u002Fv1\u002Fprojects\u002F:pk\u002Fchangelog.rss\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>The changelog as RSS\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\u003C\u002Fdiv>\n\u003Cp>\u003Cstrong>Who is asking\u003C\u002Fstrong> decides what they can do:\u003C\u002Fp>\n\u003Cdiv class=\"table-wrap\">\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Caller\u003C\u002Fth>\n\u003Cth>How\u003C\u002Fth>\n\u003Cth>Can\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>Visitor\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>?pk=pk_…\u003C\u002Fcode> (or \u003Ccode>X-Project-Key\u003C\u002Fcode>)\u003C\u002Ftd>\n\u003Ctd>Read the public board\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>User\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>Authorization: Bearer &lt;identity token&gt;\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>Post, vote, comment; edit and delete their own\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Your staff\u003C\u002Ftd>\n\u003Ctd>An identity token with \u003Ccode>&quot;role&quot;: &quot;admin&quot;\u003C\u002Fcode> in its claims\u003C\u002Ftd>\n\u003Ctd>Everything a user can, plus statuses, hiding, featuring, locking, pins, merges, team notes, changelog fields, any post or comment. Their comments are marked as the team.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Operator\u003C\u002Ftd>\n\u003Ctd>Panel session or \u003Ccode>ak_\u003C\u002Fcode> admin key, plus \u003Ccode>?project=&lt;id&gt;\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>The same as staff\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\u003C\u002Fdiv>\n\u003Cp>\u003Ccode>role: &quot;admin&quot;\u003C\u002Fcode> is your decision, made where you mint the token — it is signed\nwith the project secret, so a browser cannot add it:\u003C\u002Fp>\n\u003Cdiv class=\"code-block\">\u003Cbutton type=\"button\" class=\"copy-btn\" data-copy aria-label=\"Copy to clipboard\">Copy\u003C\u002Fbutton>\u003Cpre class=\"shj-lang-js\">\u003Ccode>\u003Cspan class=\"shj-syn-kwd\">const\u003C\u002Fspan> body \u003Cspan class=\"shj-syn-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-syn-func\">b64\u003C\u002Fspan>({ iss\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-num\">PUBLIC_KEY\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> sub\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> user\u003Cspan class=\"shj-syn-oper\">.\u003C\u002Fspan>id\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> name\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> user\u003Cspan class=\"shj-syn-oper\">.\u003C\u002Fspan>name\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> email\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> user\u003Cspan class=\"shj-syn-oper\">.\u003C\u002Fspan>email\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> username\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> user\u003Cspan class=\"shj-syn-oper\">.\u003C\u002Fspan>handle\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> avatar\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> user\u003Cspan class=\"shj-syn-oper\">.\u003C\u002Fspan>avatarUrl\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-syn-oper\">...\u003C\u002Fspan>(user\u003Cspan class=\"shj-syn-oper\">.\u003C\u002Fspan>isStaff \u003Cspan class=\"shj-syn-oper\">?\u003C\u002Fspan> { role\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">'admin'\u003C\u002Fspan> } \u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> {})\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> iat\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> now\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> exp\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> now \u003Cspan class=\"shj-syn-oper\">+\u003C\u002Fspan> \u003Cspan class=\"shj-syn-num\">3600\u003C\u002Fspan> })\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fdiv>\n\u003Cp>\u003Ccode>username\u003C\u002Fcode> is the handle people \u003Ccode>@mention\u003C\u002Fcode> (never put an email address there:\nhandles are public); \u003Ccode>avatar\u003C\u002Fcode> is an http(s) image URL. Both are optional.\u003C\u002Fp>\n\u003Cp>Things to know:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>Each post carries viewer fields — \u003Ccode>hasVoted\u003C\u002Fcode>, \u003Ccode>isMine\u003C\u002Fcode>, \u003Ccode>canEdit\u003C\u002Fcode>,\n\u003Ccode>canDelete\u003C\u002Fcode>, \u003Ccode>canComment\u003C\u002Fcode>, \u003Ccode>canVote\u003C\u002Fcode>, \u003Ccode>subscribed\u003C\u002Fcode>, \u003Ccode>muted\u003C\u002Fcode>, \u003Ccode>unread\u003C\u002Fcode> (and\n\u003Ccode>reacted\u003C\u002Fcode> per reaction) — so a UI never has to work out permissions itself.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Numbers.\u003C\u002Fstrong> Every post gets the board's next \u003Ccode>number\u003C\u002Fcode> (\u003Ccode>#12\u003C\u002Fcode>) and a \u003Ccode>slug\u003C\u002Fcode>\nfrom its title; \u003Ccode>\u002Ffeedback\u002F12-dark-mode\u003C\u002Fcode> style URLs resolve by number, so a\nrenamed post keeps its links. \u003Ccode>#12\u003C\u002Fcode> in a post or comment is recorded as a\nreference (\u003Ccode>refs\u003C\u002Fcode>), which is how \u003Ccode>backrefs\u003C\u002Fcode> are found.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Locked\u003C\u002Fstrong> posts take votes, comments and reactions from admins only.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Comments\u003C\u002Fstrong> thread one level deep (\u003Ccode>parentId\u003C\u002Fcode>; \u003Ccode>replyToId\u003C\u002Fcode> names the comment\nanswered). Admins can write \u003Ccode>internal\u003C\u002Fcode> notes and \u003Ccode>hidden\u003C\u002Fcode>-moderate comments:\nboth reach admins only, never count in \u003Ccode>commentCount\u003C\u002Fcode> and never notify. An\nadmin can pin one public top-level comment (\u003Ccode>pinnedCommentId\u003C\u002Fcode> on the post).\nAuthors edit their comments while the board allows user edits (\u003Ccode>editedAt\u003C\u002Fcode>).\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Merging\u003C\u002Fstrong> (\u003Ccode>\u002Fmerge\u003C\u002Fcode>) moves the duplicate's votes onto the target (each voter\nonce), copies its followers, links it as \u003Ccode>duplicateOf\u003C\u002Fcode> and locks it in a\nclosed status. Unlinking does not move votes back.\u003C\u002Fli>\n\u003Cli>\u003Ccode>adminNote\u003C\u002Fcode> is a private note on the post, returned to admins only.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Bodies are untrusted.\u003C\u002Fstrong> \u003Ccode>body\u003C\u002Fcode>, \u003Ccode>changelogBody\u003C\u002Fcode> and comments are stored as\nsent (Markdown or HTML). Sanitize before rendering (DOMPurify or similar).\u003C\u002Fli>\n\u003Cli>Author names are hidden from the public unless the board shows them;\nadmins also get \u003Ccode>author.email\u003C\u002Fcode> and \u003Ccode>author.externalId\u003C\u002Fcode>.\u003C\u002Fli>\n\u003Cli>Hidden posts are invisible to everyone but admins — their authors included —\nand only come back with \u003Ccode>includeHidden=1\u003C\u002Fcode>.\u003C\u002Fli>\n\u003Cli>Authors start with their own vote. Moving a post into a done status for the\nfirst time stamps \u003Ccode>completedAt\u003C\u002Fcode> (the changelog date); admins can set it.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Live events\u003C\u002Fstrong> (\u003Ccode>feedback.post.created|updated|deleted\u003C\u002Fcode>,\n\u003Ccode>feedback.comment.created|updated|deleted\u003C\u002Fcode>) carry posts and comments without\nviewer fields: merge them over your copy. Team notes and hidden comments only\nreach admins; a comment hidden after the fact arrives as \u003Ccode>comment.deleted\u003C\u002Fcode>.\nWith \u003Ccode>?pk=\u003C\u002Fcode> the stream works with a plain \u003Ccode>EventSource\u003C\u002Fcode>.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2 id=\"planning--roadmap\">Planning &amp; roadmap\u003Ca class=\"heading-anchor\" href=\"#planning--roadmap\" aria-label=\"Link to this section\">#\u003C\u002Fa>\u003C\u002Fh2>\n\u003Cp>Each project can also use \u003Cstrong>Planning\u003C\u002Fstrong>: Linear-style items on a kanban, for\nthe work behind the feedback and the support conversations. The same items\ndrive the \u003Cstrong>public roadmap\u003C\u002Fstrong>. Projects without Planning keep the post-based\nroadmap (planned, in-progress and done posts).\u003C\u002Fp>\n\u003Ch3 id=\"in-the-panel\">In the panel\u003Ca class=\"heading-anchor\" href=\"#in-the-panel\" aria-label=\"Link to this section\">#\u003C\u002Fa>\u003C\u002Fh3>\n\u003Cp>Project → \u003Cstrong>Planning\u003C\u002Fstrong> → \u003Cem>Turn on planning\u003C\u002Fem>.\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cstrong>Items\u003C\u002Fstrong> have a title, an internal Markdown description, a status, a\npriority, an assignee, labels, a target date and a \u003Ccode>#number\u003C\u002Fcode>. Drag a card\nacross columns to change its status, or within one to reorder it; the order\nis kept. \u003Ccode>?item=&lt;id&gt;\u003C\u002Fcode> opens an item, so links to it can be shared.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Links.\u003C\u002Fstrong> An item links up to 50 feedback posts and 50 conversations. In the\nitem, \u003Cem>Link post\u003C\u002Fem> takes a post's \u003Ccode>#N\u003C\u002Fcode>. In the inbox, a conversation's\n\u003Cstrong>Planning\u003C\u002Fstrong> section files a new item from it (prefilled with its subject and\nthe customer's last message) or adds it to an existing one; the link shows on\nthe conversation's timeline as an internal event the customer never sees.\nThe feedback post editor has the same \u003Cstrong>Planning\u003C\u002Fstrong> section.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Filed from an error.\u003C\u002Fstrong> The Errors page's \u003Cstrong>File on board\u003C\u002Fstrong> button makes an item from a\ntracker issue (see \u003Ca href=\"\u002Fdocs\u002Ferrors\">ERRORS.md\u003C\u002Fa>); its drawer lists the errors it came from, each with\nits link back to the tracker.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Commits and pull requests.\u003C\u002Fstrong> When the GitHub App's webhook is on, a commit pushed to the\ndefault branch or a pull request merged into it that says \u003Ccode>#12\u003C\u002Fcode> is listed on item 12 (or on\nfeedback post 12 when the board has no such item), with a \u003Cem>closes\u003C\u002Fem> marker for \u003Ccode>fixes #12\u003C\u002Fcode>.\nA closing keyword also moves the item to the board's first done status. See \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Felrumo\u002Fhelp-bites-oss\u002Fblob\u002Fmain\u002Fdocs\u002FGITHUB.md\" target=\"_blank\" rel=\"noopener\">GITHUB.md\u003C\u002Fa>.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Votes.\u003C\u002Fstrong> An item's count is the sum of its linked posts' votes, hidden posts\nleft out. People vote on the posts, from the feedback page or from the roadmap.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Settings\u003C\u002Fstrong> — statuses (with the same categories as Feedback),\nlabels, the roadmap's layout and options, and \u003Cstrong>Sync linked feedback\u003C\u002Fstrong>: when\nan item moves to another status category, each linked post moves to the\nFeedback's first status of that category, exactly as if an admin had\nmoved it — so followers get the usual status emails. Off by default.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>The settings are the \u003Ccode>board\u003C\u002Fcode> object on \u003Ccode>PATCH \u002Fv1\u002Fadmin\u002Fprojects\u002F:id\u003C\u002Fcode> (partial:\n\u003Ccode>roadmap\u003C\u002Fcode> merges key by key; \u003Ccode>statuses\u003C\u002Fcode> and \u003Ccode>labels\u003C\u002Fcode> replace their lists).\u003C\u002Fp>\n\u003Ch3 id=\"what-goes-public\">What goes public\u003Ca class=\"heading-anchor\" href=\"#what-goes-public\" aria-label=\"Link to this section\">#\u003C\u002Fa>\u003C\u002Fh3>\n\u003Cp>The roadmap is published through the feedback page's public pages and API, so\nFeedback must be on, with its Roadmap view.\u003C\u002Fp>\n\u003Cp>Switching Planning on does not empty the roadmap: it keeps listing\nfeedback posts until at least one planning item is public in a planned, active or\ndone status. From then on Planning's public items are the roadmap.\u003C\u002Fp>\n\u003Cul>\n\u003Cli>Items are \u003Cstrong>private by default\u003C\u002Fstrong>. Turn on \u003Cem>On public roadmap\u003C\u002Fem> per item.\u003C\u002Fli>\n\u003Cli>A public item shows \u003Cstrong>once its status is planned, in progress or done\u003C\u002Fstrong> (by\ncategory). Backlog and canceled items, and those statuses' names, never go\nout.\u003C\u002Fli>\n\u003Cli>The roadmap shows only the item's \u003Cstrong>public description\u003C\u002Fstrong> — never the\ninternal one, not even as a fallback — its title, status, completion date and\nthe linked posts that are not hidden (with their votes).\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Labels and target dates are opt-in\u003C\u002Fstrong> (\u003Cem>Show labels\u003C\u002Fem>, \u003Cem>Show dates\u003C\u002Fem>); the\ntimeline layout always sends dates. Numbers, priorities, assignees, ranks and\nconversations never leave the panel.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch3 id=\"showing-it\">Showing it\u003Ca class=\"heading-anchor\" href=\"#showing-it\" aria-label=\"Link to this section\">#\u003C\u002Fa>\u003C\u002Fh3>\n\u003Cul>\n\u003Cli>\n\u003Cp>\u003Cstrong>Its own page\u003C\u002Fstrong>: \u003Ccode>https:\u002F\u002FYOUR-SUPPORT-HOST\u002Froadmap\u002Fpk_…\u003C\u002Fcode>, with optional\n\u003Ccode>?layout=columns|timeline|list\u003C\u002Fcode> and \u003Ccode>?theme=light|dark|system\u003C\u002Fcode>. The \u003Cem>Public\nroadmap\u003C\u002Fem> button on the board copies the link.\u003C\u002Fp>\n\u003C\u002Fli>\n\u003Cli>\n\u003Cp>\u003Cstrong>In your site\u003C\u002Fstrong>, inline or floating, with the feedback loader:\u003C\u002Fp>\n\u003Cdiv class=\"code-block\">\u003Cbutton type=\"button\" class=\"copy-btn\" data-copy aria-label=\"Copy to clipboard\">Copy\u003C\u002Fbutton>\u003Cpre class=\"shj-lang-html\">\u003Ccode>\u003Cspan class=\"shj-syn-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-syn-var\">div\u003C\u002Fspan> \u003Cspan class=\"shj-syn-class\">id\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\"roadmap\"\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">&gt;\u003C\u002Fspan>\u003Cspan class=\"shj-syn-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-syn-var\">div\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">&gt;\u003C\u002Fspan>\n\u003Cspan class=\"shj-syn-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-syn-var\">script\u003C\u002Fspan> \u003Cspan class=\"shj-syn-class\">src\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\"https:\u002F\u002FYOUR-SUPPORT-HOST\u002Ffeedback.js\"\u003C\u002Fspan> \u003Cspan class=\"shj-syn-class\">data-project\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\"pk_…\"\u003C\u002Fspan> \u003Cspan class=\"shj-syn-class\">data-view\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\"roadmap\"\u003C\u002Fspan> \u003Cspan class=\"shj-syn-class\">data-only\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\"roadmap\"\u003C\u002Fspan> \u003Cspan class=\"shj-syn-class\">data-label\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\"Roadmap\"\u003C\u002Fspan> \u003Cspan class=\"shj-syn-class\">data-target\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\"#roadmap\"\u003C\u002Fspan> \u003Cspan class=\"shj-syn-class\">defer\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">&gt;\u003C\u002Fspan>\u003Cspan class=\"shj-syn-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-syn-var\">script\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">&gt;\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fdiv>\n\u003Cp>Leave out \u003Ccode>data-target\u003C\u002Fcode> for a floating \u003Cem>Roadmap\u003C\u002Fem> button. Hand it a token the\nsame way as the board (\u003Ccode>init\u003C\u002Fcode> with \u003Ccode>getToken\u003C\u002Fcode>) so signed-in users can vote.\nThe full board embed shows the board-driven roadmap in its Roadmap tab too.\u003C\u002Fp>\n\u003C\u002Fli>\n\u003Cli>\n\u003Cp>\u003Cstrong>Layouts\u003C\u002Fstrong>: \u003Ccode>columns\u003C\u002Fcode> (one per status), \u003Ccode>timeline\u003C\u002Fcode> (by target quarter, undated\nlast) and \u003Ccode>list\u003C\u002Fcode> (compact rows under status headings). Clicking an item with\none linked post opens that post (and its vote button); several posts list\nunder the card. Vote totals update live.\u003C\u002Fp>\n\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch3 id=\"the-api\">The API\u003Ca class=\"heading-anchor\" href=\"#the-api\" aria-label=\"Link to this section\">#\u003C\u002Fa>\u003C\u002Fh3>\n\u003Cul>\n\u003Cli>\u003Ccode>GET \u002Fv1\u002Ffeedback\u002Froadmap\u003C\u002Fcode> — \u003Ccode>?pk=\u003C\u002Fcode>, an identity token (adds \u003Ccode>hasVoted\u003C\u002Fcode> per\npost) or an operator with \u003Ccode>?project=\u003C\u002Fcode>. Returns \u003Ccode>{ options, statuses, labels?, items }\u003C\u002Fcode>; 404 unless the feedback board offers a roadmap and the project board\nis on. \u003Ccode>GET \u002Fv1\u002Ffeedback\u002Fconfig\u003C\u002Fcode> says which roadmap applies:\n\u003Ccode>roadmapSource: &quot;board&quot; | &quot;posts&quot;\u003C\u002Fcode> (\u003Ccode>board\u003C\u002Fcode> once the board has a public item\nin a roadmap status).\u003C\u002Fli>\n\u003Cli>\u003Ccode>GET|POST \u002Fv1\u002Fadmin\u002Fboard-items?project=&lt;id&gt;\u003C\u002Fcode> (list, or create with optional\n\u003Ccode>feedbackPostIds\u003C\u002Fcode> \u002F \u003Ccode>conversationIds\u003C\u002Fcode>), \u003Ccode>PATCH|DELETE \u002Fv1\u002Fadmin\u002Fboard-items\u002F:id\u003C\u002Fcode>, and \u003Ccode>POST \u002Fv1\u002Fadmin\u002Fboard-items\u002F:id\u002Flinks\u003C\u002Fcode> with\n\u003Ccode>{ kind: &quot;feedback&quot; | &quot;conversation&quot;, id, linked }\u003C\u002Fcode>. These take an operator\nsession or an \u003Ccode>ak_…\u003C\u002Fcode> admin key, so release scripts and other tools can file\nand move items. The list filters by \u003Ccode>&amp;conversation=\u003C\u002Fcode> or \u003Ccode>&amp;feedbackPost=\u003C\u002Fcode>.\nMoving a card is a \u003Ccode>PATCH\u003C\u002Fcode> with \u003Ccode>status\u003C\u002Fcode> and a \u003Ccode>rank\u003C\u002Fcode> between its new\nneighbours (lower ranks come first).\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>An item filed from an error carries \u003Ccode>errorRefs: [{ provider, issueId, linkId, title, url }]\u003C\u002Fcode>\n(empty otherwise). \u003Ccode>GET \u002Fv1\u002Fadmin\u002Fgithub-refs?project=&lt;id&gt;&amp;kind=board|feedback&amp;id=&lt;item or post&gt;\u003C\u002Fcode>\nlists the commits and pull requests that mention it:\u003C\u002Fp>\n\u003Cdiv class=\"code-block\">\u003Cbutton type=\"button\" class=\"copy-btn\" data-copy aria-label=\"Copy to clipboard\">Copy\u003C\u002Fbutton>\u003Cpre class=\"shj-lang-bash\">\u003Ccode>\u003Cspan class=\"shj-syn-func\">curl\u003C\u002Fspan>\u003Cspan class=\"shj-syn-kwd\"> -H\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">\"Authorization: Bearer ak_…\"\u003C\u002Fspan> \\\n  \u003Cspan class=\"shj-syn-str\">\"https:\u002F\u002FYOUR-SUPPORT-HOST\u002Fv1\u002Fadmin\u002Fgithub-refs?project=PROJECT_ID&#38;kind=board&#38;id=12\"\u003C\u002Fspan>\n\u003Cspan class=\"shj-syn-cmnt\"># { \"refs\": [{ \"kind\": \"pull_request\", \"ref\": \"34\", \"repo\": \"acme\u002Fweb\", \"title\": \"Dark mode\", \"url\": \"https:\u002F\u002Fgithub.com\u002F…\", \"closes\": true, \"at\": \"…\" }] }\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fdiv>\n\u003Cp>\u003Cstrong>Known limit:\u003C\u002Fstrong> a person who voted on two posts linked to the same item counts\ntwice in that item's total — the sum is per post, not per person.\u003C\u002Fp>\n\u003Ch2 id=\"changelog\">Changelog\u003Ca class=\"heading-anchor\" href=\"#changelog\" aria-label=\"Link to this section\">#\u003C\u002Fa>\u003C\u002Fh2>\n\u003Cp>What shipped, in your own words. The changelog is its own kind of item, not a\nview of feedback posts: an \u003Cstrong>entry\u003C\u002Fstrong> (title, Markdown text, a type — \u003Cem>New\u003C\u002Fem>,\n\u003Cem>Improved\u003C\u002Fem>, \u003Cem>Fixed\u003C\u002Fem>, \u003Cem>Removed\u003C\u002Fem> or \u003Cem>Breaking\u003C\u002Fem>) optionally belongs to a \u003Cstrong>release\u003C\u002Fstrong>\n(a version, with a title and summary). Project → \u003Cstrong>Changelog\u003C\u002Fstrong> is where you write\nthem; the public side reads the same data.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Nothing is public by default.\u003C\u002Fstrong> An entry is public only when it is\n\u003Cem>published\u003C\u002Fem> \u003Cstrong>and\u003C\u002Fstrong> its release, if it has one, is published too. Publishing a\nrelease publishes the draft entries in it (dated with the release); an entry you\nunpublish afterwards stays unpublished. A release with nothing published in it\ndoes not show.\u003C\u002Fp>\n\u003Ch3 id=\"made-from\">Made from\u003Ca class=\"heading-anchor\" href=\"#made-from\" aria-label=\"Link to this section\">#\u003C\u002Fa>\u003C\u002Fh3>\n\u003Cp>An entry can link the \u003Cstrong>feedback posts\u003C\u002Fstrong>, \u003Cstrong>board items\u003C\u002Fstrong>, and \u003Cstrong>conversations\u003C\u002Fstrong>\n(tickets and error issues) it answers — up to 50 of each. The panel shows each\nlink's status, so you can see what is done before you publish. Links are the\nonly connection: \u003Cstrong>nothing is copied from a source when you link it\u003C\u002Fstrong>, and the\npublic changelog never mentions them.\u003C\u002Fp>\n\u003Cp>\u003Cem>New entry\u003C\u002Fem> from a source (the Changelog section of a post, board item or\nconversation) starts you off with text that is already public:\u003C\u002Fp>\n\u003Cdiv class=\"table-wrap\">\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Source\u003C\u002Fth>\n\u003Cth>What the new entry starts with\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>Feedback post\u003C\u002Ftd>\n\u003Ctd>its changelog title and text, else its title and text; a type from the post's (\u003Ccode>bug\u003C\u002Fcode> → Fixed, \u003Ccode>feature\u003C\u002Fcode> → New, else Improved)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Board item\u003C\u002Ftd>\n\u003Ctd>its title and \u003Cstrong>public\u003C\u002Fstrong> description (never the internal one)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Ticket or error issue\u003C\u002Ftd>\n\u003Ctd>nothing but a type (errors → Fixed). Never the subject, messages or stack trace\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\u003C\u002Fdiv>\n\u003Cp>With \u003Cstrong>auto-draft\u003C\u002Fstrong> on (Project → Changelog → Settings), a feedback post or board\nitem reaching a done status drafts an entry for it, unless an entry already links\nit. Drafts are never published for you. Tickets and errors are always manual.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Write from commits (AI).\u003C\u002Fstrong> With GitHub repos connected to the project (\u003Ca href=\"https:\u002F\u002Fgithub.com\u002Felrumo\u002Fhelp-bites-oss\u002Fblob\u002Fmain\u002Fdocs\u002FGITHUB.md\" target=\"_blank\" rel=\"noopener\">GITHUB.md\u003C\u002Fa>),\n\u003Cem>Changelog → From commits\u003C\u002Fem> has the AI read the repos' commit messages (never the code) and draft\nentries from them: for all commits, or only the last N days, weeks or months. It reads the newest\n300 commits of each repo's default branch, ignores merge commits and any commit an entry already\ncites, groups related commits, and skips what users would not notice (refactors, CI, dependency\nbumps). The entries come back as \u003Cstrong>drafts\u003C\u002Fstrong> marked \u003Cem>AI\u003C\u002Fem>, waiting for review; nothing is public\nuntil you approve one. In the list, select entries to approve several at once or \u003Cstrong>merge\u003C\u002Fstrong>\nthem into one new draft (the originals are archived); open one to edit it. Settings has two more\nswitches: \u003Cstrong>Write entries from new commits\u003C\u002Fstrong> drafts entries on every push to a repo's default\nbranch (needs the GitHub webhook), and \u003Cstrong>Publish them without review\u003C\u002Fstrong> makes those pushes\npublish what they write with no human looking. That second one is off by default and only\napplies to pushes; the button never publishes. A project's pushes run at most 20 times an hour.\nUses the workspace's AI like every other AI feature (and counts toward its credits).\u003C\u002Fp>\n\u003Ch3 id=\"telling-the-people-it-answers\">Telling the people it answers\u003Ca class=\"heading-anchor\" href=\"#telling-the-people-it-answers\" aria-label=\"Link to this section\">#\u003C\u002Fa>\u003C\u002Fh3>\n\u003Cp>Two things happen when an entry goes public, and a third is yours to press.\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cstrong>Followers of the posts it answers are emailed, once.\u003C\u002Fstrong> The posts the entry links, and the\nposts of the board items it links, are looked up; everyone following one of them (and not\nmuted, not opted out of feedback mail, with an email) gets one &quot;Shipped: …&quot; mail with the\nentry's title and summary, a link to the public entry, and the posts they follow, listed.\nSomeone following several of them gets one mail, not several. It uses the feedback board's\nswitch (\u003Cem>Email notifications\u003C\u002Fem>) and the project's mailer, exactly like a status change. The\nevent \u003Ccode>changelog.entry.published\u003C\u002Fcode> (\u003Ca href=\"\u002Fdocs\u002Fevents\">EVENTS.md\u003C\u002Fa>) is what triggers it. An entry counts\nas becoming public when it is created published, edited to published, approved, or when its\ndraft release is published. Imports never mail. An entry published more than seven days\nago (a backdated one) is history and is not mailed. Delivery is at-most-once, like the\nsubscribers' mail: the entry is marked (\u003Ccode>followersNotifiedAt\u003C\u002Fcode>) before anything is sent, so\na retry or a second server never mails twice, and a failed send is logged, never retried.\nThis is separate from \u003Ccode>changelog.notify\u003C\u002Fcode>, which is about people who subscribed to the\nchangelog itself; a follower who is also a subscriber can get both.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>The same posts learn the version\u003C\u002Fstrong> (\u003Ccode>shippedIn\u003C\u002Fcode>), as before.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Reply to the linked conversations (a button, never automatic).\u003C\u002Fstrong> Once an entry is public\nand answers conversations (its own links, plus the conversations of its board items), the\neditor shows \u003Cstrong>Reply to N linked conversations\u003C\u002Fstrong>: a dialog with the list (untick any) and a\nmessage that starts as the entry's title and public address, yours to edit. Sending appends\nthat message as the team to each conversation, through the same path as an inbox reply, and\nemails the customer unless you turn \u003Cem>Email them too\u003C\u002Fem> off. Error issues are left out (there is\nno customer). Sending the same words to the same conversation twice is a no-op.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cdiv class=\"code-block\">\u003Cbutton type=\"button\" class=\"copy-btn\" data-copy aria-label=\"Copy to clipboard\">Copy\u003C\u002Fbutton>\u003Cpre class=\"shj-lang-bash\">\u003Ccode>\u003Cspan class=\"shj-syn-cmnt\"># Who would be told\u003C\u002Fspan>\n\u003Cspan class=\"shj-syn-func\">curl\u003C\u002Fspan>\u003Cspan class=\"shj-syn-kwd\"> -H\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">\"Authorization: Bearer ak_…\"\u003C\u002Fspan> \\\n  \u003Cspan class=\"shj-syn-str\">\"https:\u002F\u002FYOUR-SUPPORT-HOST\u002Fv1\u002Fadmin\u002Fchangelog\u002Fentries\u002FENTRY_ID\u002Flinked-conversations?project=PROJECT_ID\"\u003C\u002Fspan>\n\u003Cspan class=\"shj-syn-cmnt\"># { \"count\": 2, \"conversations\": [{ \"id\": \"…\", \"subject\": \"Dark mode?\", \"endUserName\": \"Ada\", \"status\": \"open\" }], \"defaultBody\": \"Good news: “Dark mode” has just shipped.\\n\\nSee what changed: https:\u002F\u002F…\" }\u003C\u002Fspan>\n\n\u003Cspan class=\"shj-syn-cmnt\"># Tell them (conversationIds optional: default is all of them)\u003C\u002Fspan>\n\u003Cspan class=\"shj-syn-func\">curl\u003C\u002Fspan>\u003Cspan class=\"shj-syn-kwd\"> -X\u003C\u002Fspan> POST\u003Cspan class=\"shj-syn-kwd\"> -H\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">\"Authorization: Bearer ak_…\"\u003C\u002Fspan>\u003Cspan class=\"shj-syn-kwd\"> -H\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">\"Content-Type: application\u002Fjson\"\u003C\u002Fspan> \\\n  \u003Cspan class=\"shj-syn-str\">\"https:\u002F\u002FYOUR-SUPPORT-HOST\u002Fv1\u002Fadmin\u002Fchangelog\u002Fentries\u002FENTRY_ID\u002Freply-conversations?project=PROJECT_ID\"\u003C\u002Fspan> \\\n \u003Cspan class=\"shj-syn-kwd\"> -d\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">'{ \"body\": \"Dark mode is live, thanks for asking!\", \"notifyByEmail\": true }'\u003C\u002Fspan>\n\u003Cspan class=\"shj-syn-cmnt\"># { \"sent\": 2, \"failed\": 0, \"results\": [{ \"conversationId\": \"…\", \"ok\": true, \"emailSent\": true, \"error\": \"\" }] }\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fdiv>\n\u003Cp>These are operator tools (an admin session or \u003Ccode>ak_\u003C\u002Fcode> key), not something a visitor's browser\ncalls, so there is no embed snippet for them; the public side of an entry is the changelog\npage and \u003Ccode>public\u002Fchangelog.js\u003C\u002Fcode> described below.\u003C\u002Fp>\n\u003Ch3 id=\"public\">Public\u003Ca class=\"heading-anchor\" href=\"#public\" aria-label=\"Link to this section\">#\u003C\u002Fa>\u003C\u002Fh3>\n\u003Cdiv class=\"table-wrap\">\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>\u003C\u002Fth>\n\u003Cth>\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>Page\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>https:\u002F\u002FYOUR-SUPPORT-HOST\u002Fchangelog\u002Fpk_…\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>JSON\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>GET \u002Fv1\u002Fchangelog?pk=pk_…\u003C\u002Fcode>: \u003Ccode>{ releases: [{ version, title, summary, publishedAt, entries }], entries }\u003C\u002Fcode> — \u003Ccode>entries\u003C\u002Fcode> are the published ones that belong to no release\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Flat list\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>GET \u002Fv1\u002Fchangelog?pk=pk_…&amp;flat=1\u003C\u002Fcode>, or any of \u003Ccode>limit\u003C\u002Fcode> (≤100), \u003Ccode>skip\u003C\u002Fcode>, \u003Ccode>tag\u003C\u002Fcode>, \u003Ccode>area\u003C\u002Fcode>, \u003Ccode>version\u003C\u002Fcode> (a release's), \u003Ccode>since\u003C\u002Fcode>: \u003Ccode>{ entries, total, hasMore }\u003C\u002Fcode>, newest first, each entry carrying its release's \u003Ccode>version\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>One entry\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>GET \u002Fv1\u002Fchangelog\u002F:ref?pk=pk_…\u003C\u002Fcode> by slug, \u003Ccode>#N\u003C\u002Fcode> number or id\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>RSS\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>GET \u002Fv1\u002Fprojects\u002Fpk_…\u002Fchangelog.rss\u003C\u002Fcode>, one item per published entry\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>In your page\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>changelog.js\u003C\u002Fcode> (a read-only embed that grows with its content; no board needed), the board embed's \u003Cem>Changelog\u003C\u002Fem> tab, or your own UI on the JSON above\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\u003C\u002Fdiv>\n\u003Cp>All of them need the changelog to be public: \u003Ccode>changelog.enabled\u003C\u002Fcode> (Project →\nChangelog → Settings, or \u003Ccode>PATCH \u002Fv1\u002Fadmin\u002Fprojects\u002F:id {&quot;changelog&quot;:{&quot;enabled&quot;:true}}\u003C\u002Fcode>).\nIt is its own switch: the feedback board's \u003Cem>Changelog\u003C\u002Fem> option only adds a tab to\nthe board, and only when this is on. (Projects that were public through the\nboard were switched on by \u003Ccode>scripts\u002Fmigrate-changelog-public.mjs\u003C\u002Fcode>.) A public entry carries \u003Ccode>slug\u003C\u002Fcode>, \u003Ccode>summary\u003C\u002Fcode>,\n\u003Ccode>bodyHtml\u003C\u002Fcode> (the text rendered and \u003Cstrong>sanitised once, when it was saved\u003C\u002Fstrong>: put it\nin your page as it is), \u003Ccode>excerpt\u003C\u002Fcode>, \u003Ccode>area\u003C\u002Fcode>, \u003Ccode>pinned\u003C\u002Fcode>, \u003Ccode>iconGlyph\u003C\u002Fcode>, \u003Ccode>accentColor\u003C\u002Fcode>,\n\u003Ccode>imageUrl\u003C\u002Fcode>, \u003Ccode>links\u003C\u002Fcode> and \u003Ccode>commentCount\u003C\u002Fcode> besides its title and tag; never what it\nwas made from, its commits or its review. \u003Ccode>body\u003C\u002Fcode> is still the source (Markdown, or\nHTML when \u003Ccode>bodyFormat\u003C\u002Fcode> is \u003Ccode>html\u003C\u002Fcode>). Anonymous responses may be cached for a minute;\nanything sent with a token is \u003Ccode>no-store\u003C\u002Fcode>.\u003C\u002Fp>\n\u003Ch3 id=\"api-writers\">API (writers)\u003Ca class=\"heading-anchor\" href=\"#api-writers\" aria-label=\"Link to this section\">#\u003C\u002Fa>\u003C\u002Fh3>\n\u003Cp>Who may change the changelog (\u003Ccode>?project=&lt;id&gt;\u003C\u002Fcode> names the project for an operator; the secret and an admin token act on their own):\u003C\u002Fp>\n\u003Cul>\n\u003Cli>an operator: an admin session or \u003Ccode>ak_\u003C\u002Fcode> key;\u003C\u002Fli>\n\u003Cli>your server: the project secret (\u003Ccode>sk_…\u003C\u002Fcode>), whose entries are marked \u003Ccode>source: &quot;internal&quot;\u003C\u002Fcode> and authored \u003Ccode>automated\u003C\u002Fcode>;\u003C\u002Fli>\n\u003Cli>your staff in their browser: an identity token with \u003Ccode>&quot;role&quot;: &quot;admin&quot;\u003C\u002Fcode> (the entry is theirs).\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>Every route is in the OpenAPI spec, tag \u003Cem>Changelog\u003C\u002Fem>.\u003C\u002Fp>\n\u003Cdiv class=\"table-wrap\">\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>\u003C\u002Fth>\n\u003Cth>\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ccode>GET\u003C\u002Fcode>\u002F\u003Ccode>POST \u002Fv1\u002Fadmin\u002Fchangelog\u002Fentries\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>List (filters: \u003Ccode>status\u003C\u002Fcode> — \u003Ccode>draft\u003C\u002Fcode>, \u003Ccode>published\u003C\u002Fcode> or \u003Ccode>archived\u003C\u002Fcode>; none lists all but the archived — \u003Ccode>release\u003C\u002Fcode> — an id or \u003Ccode>none\u003C\u002Fcode>, \u003Ccode>feedbackPost\u003C\u002Fcode>, \u003Ccode>boardItem\u003C\u002Fcode>, \u003Ccode>conversation\u003C\u002Fcode>) or create \u003Ccode>{ title, summary, body, bodyFormat, tag, area, slug, status, releaseId | releaseVersion, publishedAt, pinned, iconGlyph, accentColor, imageUrl, links, commitShas, feedbackPostIds, boardItemIds, conversationIds }\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>PATCH\u003C\u002Fcode>\u002F\u003Ccode>DELETE \u002Fv1\u002Fadmin\u002Fchangelog\u002Fentries\u002F:id\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>Edit (the id, the slug, or the \u003Ccode>#N\u003C\u002Fcode> number) or delete. Set \u003Ccode>status: &quot;archived&quot;\u003C\u002Fcode> to retire an entry instead: never public, but it keeps its slug and its commits\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>POST \u002Fv1\u002Fadmin\u002Fchangelog\u002Fentries\u002F:id\u002Freview\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>{ action: &quot;approve&quot; | &quot;reject&quot;, notes }\u003C\u002Fcode> on a draft: approve publishes it, reject keeps it a draft marked \u003Ccode>rejected\u003C\u002Fcode>. Records who and when\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>POST \u002Fv1\u002Fadmin\u002Fchangelog\u002Fentries\u002Fbatch\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>Up to 50 entries; one that cites a commit an entry (an archived one too) already cites is \u003Cstrong>skipped\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>POST \u002Fv1\u002Fadmin\u002Fchangelog\u002Fentries\u002Fimport\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>Secret or operator only. Up to 100 entries with your own \u003Ccode>externalId\u003C\u002Fcode>: created, or updated when it exists. Keeps the \u003Ccode>slug\u003C\u002Fcode> and \u003Ccode>publishedAt\u003C\u002Fcode> it is given and the review trail; never drafts, links or tells anyone\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>POST \u002Fv1\u002Fadmin\u002Fchangelog\u002Fentries\u002Fmerge\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>{ refs: [2–10 entries], entry }\u003C\u002Fcode>: creates the merged entry, which also cites the sources' commits, and archives the sources. Writing the merged text is yours\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>POST \u002Fv1\u002Fadmin\u002Fchangelog\u002Fentries\u002F:id\u002Flinks\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>{ kind: &quot;feedback&quot; | &quot;board&quot; | &quot;conversation&quot;, id, linked }\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>GET \u002Fv1\u002Fadmin\u002Fchangelog\u002Fentries\u002F:id\u002Flinked-conversations\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>Operator or \u003Ccode>ak_\u003C\u002Fcode> key. \u003Ccode>{ count, conversations: [{ id, subject, endUserName, status }], defaultBody }\u003C\u002Fcode>: the customer conversations the entry answers, its own and its board items'\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>POST \u002Fv1\u002Fadmin\u002Fchangelog\u002Fentries\u002F:id\u002Freply-conversations\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>Operator or \u003Ccode>ak_\u003C\u002Fcode> key. \u003Ccode>{ body, conversationIds?, notifyByEmail? }\u003C\u002Fcode> → \u003Ccode>{ sent, failed, results }\u003C\u002Fcode>: replies as the team to each chosen conversation (all linked ones when omitted; up to 100)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>GET\u003C\u002Fcode>\u002F\u003Ccode>POST \u002Fv1\u002Fadmin\u002Fchangelog\u002Freleases\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>List or create \u003Ccode>{ version, title, summary }\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>PATCH\u003C\u002Fcode>\u002F\u003Ccode>DELETE \u002Fv1\u002Fadmin\u002Fchangelog\u002Freleases\u002F:id\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>Edit, or delete (its entries stay, with no release)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>POST \u002Fv1\u002Fadmin\u002Fchangelog\u002Freleases\u002F:id\u002Fpublish\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>Publish the release and its draft entries\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\u003C\u002Fdiv>\n\u003Cp>A release script is two calls: create the release, then post each entry with\n\u003Ccode>releaseId\u003C\u002Fcode>, then publish the release. If you think in versions rather than release\nobjects, send \u003Ccode>releaseVersion\u003C\u002Fcode> instead: the release of that version is found, or\ncreated already published. \u003Ccode>slug\u003C\u002Fcode> is the entry's URL key (from the title unless you\nsend one, suffixed \u003Ccode>-2\u003C\u002Fcode>, \u003Ccode>-3\u003C\u002Fcode>… when taken, and never one the API or \u003Ccode>changelog.reservedSlugs\u003C\u002Fcode>\nowns); it does not move when the title is edited. \u003Ccode>changelog.areas\u003C\u002Fcode>, when it lists any,\nlimits \u003Ccode>area\u003C\u002Fcode> to those.\u003C\u002Fp>\n\u003Ch3 id=\"comments\">Comments\u003Ca class=\"heading-anchor\" href=\"#comments\" aria-label=\"Link to this section\">#\u003C\u002Fa>\u003C\u002Fh3>\n\u003Cp>Readers can comment on a published entry when the project turns them on\n(\u003Ccode>changelog.comments\u003C\u002Fcode>, off by default). A comment is written by a \u003Cstrong>signed-in\nuser\u003C\u002Fstrong>: an identity token, never the project secret, because a comment has an\nauthor (the token's \u003Ccode>sub\u003C\u002Fcode>, shown as \u003Ccode>authorId\u003C\u002Fcode>; the name and avatar come from the\nuser's current profile). Markdown only, up to 4,000 characters, rendered once with\na strict allowlist: raw HTML is escaped, and there are no images, headings or tables,\nat most 6 links (all marked \u003Ccode>nofollow ugc\u003C\u002Fcode>). Five comments per ten minutes per user,\nand the same text twice in a row on one entry is a 409.\u003C\u002Fp>\n\u003Cdiv class=\"table-wrap\">\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>\u003C\u002Fth>\n\u003Cth>\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ccode>GET \u002Fv1\u002Fchangelog\u002F:ref\u002Fcomments\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>A thread, oldest first, \u003Ccode>?limit\u003C\u002Fcode> ≤ 100 and \u003Ccode>?skip\u003C\u002Fcode>: \u003Ccode>{ comments, total, hasMore }\u003C\u002Fcode>. Public with \u003Ccode>?pk=\u003C\u002Fcode>; a token adds \u003Ccode>viewerIsAuthor\u003C\u002Fcode>; an admin token or operator also sees \u003Ccode>hidden\u003C\u002Fcode> ones\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>POST \u002Fv1\u002Fchangelog\u002F:ref\u002Fcomments\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>{ body }\u003C\u002Fcode> → \u003Ccode>201 { comment, commentCount }\u003C\u002Fcode>. Only on a public entry\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>PATCH \u002Fv1\u002Fchangelog\u002Fcomments\u002F:id\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>{ body }\u003C\u002Fcode> is the author editing their own words (a moderator cannot rewrite them; the comment then shows as edited); \u003Ccode>{ status: &quot;hidden&quot; | &quot;visible&quot; }\u003C\u002Fcode> is moderation, for an admin token or operator\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>DELETE \u002Fv1\u002Fchangelog\u002Fcomments\u002F:id\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>The author, an admin token or an operator. The words go and the comment stops being returned; the row stays\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>POST \u002Fv1\u002Fadmin\u002Fchangelog\u002Fcomments\u002Fimport\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>Secret or operator only. Up to 100 comments \u003Ccode>{ externalId, entry, body, status, authorId, authorName, authorAvatar, authorIsStaff, postedAt, editedAt }\u003C\u002Fcode>: created or updated by \u003Ccode>externalId\u003C\u002Fcode>, keeping author, date and status; no rate limit, no notification\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\u003C\u002Fdiv>\n\u003Cp>An entry's \u003Ccode>commentCount\u003C\u002Fcode> is recomputed from its visible comments on every\nchange, never incremented. \u003Ccode>changelog.commentNotify\u003C\u002Fcode> (up to 5 addresses) is told\nabout each new comment that is not the team's own, when the project can send mail.\nErasing an end user (\u003Ccode>DELETE \u002Fv1\u002Fend-users\u002F:externalId\u003C\u002Fcode>) deletes their changelog\ncomments too.\u003C\u002Fp>\n\u003Ch3 id=\"notifications\">Notifications\u003Ca class=\"heading-anchor\" href=\"#notifications\" aria-label=\"Link to this section\">#\u003C\u002Fa>\u003C\u002Fh3>\n\u003Cp>With \u003Ccode>changelog.notify\u003C\u002Fcode> on, people can ask to be emailed about the changelog: an\n\u003Cstrong>instant\u003C\u002Fstrong> mail once a burst of publishes has gone quiet, or a \u003Cstrong>weekly\u003C\u002Fstrong> digest on\nMonday morning. It is off by default, and also needs the server to have\n\u003Ccode>CHANGELOG_LINK_SECRET\u003C\u002Fcode> (32+ characters) or \u003Ccode>HELPBITES_SECRET\u003C\u002Fcode> to sign its links\n(and \u003Ccode>ENABLE_CHANGELOG_NOTIFY=true\u003C\u002Fcode> for the scheduler). A post's changelog title,\ntext and version (\u003Ccode>changelogTitle\u003C\u002Fcode>, \u003Ccode>changelogBody\u003C\u002Fcode>, \u003Ccode>shippedIn\u003C\u002Fcode>) stay blank for\neveryone but admins until the post is on the changelog.\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cstrong>Signed in\u003C\u002Fstrong>: send their identity token (it carries an email). They are subscribed\nat once at \u003Ccode>instant\u003C\u002Fcode> or \u003Ccode>weekly\u003C\u002Fcode>, or \u003Ccode>off\u003C\u002Fcode> unsubscribes them. \u003Ccode>GET \u002Fv1\u002Fchangelog\u002Fsubscription\u003C\u002Fcode>\nsays their cadence (\u003Ccode>null\u003C\u002Fcode> when none).\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Anonymous\u003C\u002Fstrong>: \u003Ccode>?pk=\u003C\u002Fcode> and an \u003Ccode>email\u003C\u002Fcode>. Double opt-in: the address gets a confirm link\n(your \u003Ccode>changelog-confirm\u003C\u002Fcode> email template) and nothing is active until it is clicked.\nThe answer is always \u003Ccode>{ ok: true }\u003C\u002Fcode>, whether or not the address is already subscribed;\na repeat inside a day sends no second mail; ten requests an hour per IP and thirty\nconfirm mails an hour per project.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Hosted pages\u003C\u002Fstrong> take the links by default: \u003Ccode>\u002Fu\u002Fchangelog\u002Fconfirm\u002F&lt;token&gt;\u003C\u002Fcode> and\n\u003Ccode>\u002Fu\u002Fchangelog\u002Funsubscribe\u002F&lt;token&gt;\u003C\u002Fcode> on this server (it needs \u003Ccode>APP_URL\u003C\u002Fcode>), in the project's accent\nand logo. Each shows one button, so a mail scanner opening the link changes nothing.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Or your own pages.\u003C\u002Fstrong> Set \u003Ccode>changelog.confirmUrl\u003C\u002Fcode> and \u003Ccode>changelog.unsubscribeUrl\u003C\u002Fcode> (an http(s)\nURL or \u003Ccode>{siteUrl}\u002F…\u003C\u002Fcode>, containing \u003Ccode>{token}\u003C\u002Fcode>; \u003Ccode>''\u003C\u002Fcode> goes back to the hosted pages) and the mails\npoint there instead. Your pages \u003Ccode>POST\u003C\u002Fcode> the token (from a click or button, never on load:\nmail scanners prefetch links) to \u003Ccode>\u002Fv1\u002Fchangelog\u002Fsubscribe\u002Fconfirm\u003C\u002Fcode> and\n\u003Ccode>\u002Fv1\u002Fchangelog\u002Fsubscribe\u002Funsubscribe\u003C\u002Fcode>. Every digest also carries an RFC 8058 \u003Ccode>List-Unsubscribe\u003C\u002Fcode>\nheader pointing at the unsubscribe route on this server, which a mail provider POSTs with no\npage and no script. Confirm links expire after seven days; unsubscribe links never do.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cdiv class=\"table-wrap\">\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>\u003C\u002Fth>\n\u003Cth>\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ccode>POST \u002Fv1\u002Fchangelog\u002Fsubscribe\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>{ frequency: &quot;instant&quot; | &quot;weekly&quot; | &quot;off&quot;, email }\u003C\u002Fcode> (see above)\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>GET \u002Fv1\u002Fchangelog\u002Fsubscription\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>The signed-in user's \u003Ccode>{ frequency }\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>POST \u002Fv1\u002Fchangelog\u002Fsubscribe\u002Fconfirm\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>{ token }\u003C\u002Fcode> → \u003Ccode>{ frequency }\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>POST \u002Fv1\u002Fchangelog\u002Fsubscribe\u002Funsubscribe\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>{ token }\u003C\u002Fcode> or \u003Ccode>?token=\u003C\u002Fcode> → \u003Ccode>{ ok: true }\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>GET \u002Fv1\u002Fadmin\u002Fchangelog\u002Fsubscriptions\u002Fsummary\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>Counts: active \u003Ccode>instant\u003C\u002Fcode>, \u003Ccode>weekly\u003C\u002Fcode>, \u003Ccode>pending\u003C\u002Fcode>, \u003Ccode>unsubscribed\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>POST \u002Fv1\u002Fadmin\u002Fchangelog\u002Fsubscriptions\u002Fimport\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>Secret or operator only: \u003Ccode>subscriptions\u003C\u002Fcode> (up to 500) and past \u003Ccode>runs\u003C\u002Fcode>; created, or updated by \u003Ccode>externalId\u003C\u002Fcode>\u002Femail. Someone who unsubscribed here is never reactivated by an import, so a stale copy can be re-run safely; nobody is emailed\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>POST \u002Fv1\u002Fadmin\u002Fchangelog\u002Fgenerate\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>{ days? }\u003C\u002Fcode>: have the AI draft entries from the project's GitHub commits (all, or the last \u003Ccode>days\u003C\u002Fcode>). Background; always drafts (\u003Ccode>aiGenerated: true\u003C\u002Fcode>). \u003Ccode>GET\u003C\u002Fcode> returns \u003Ccode>{ run }\u003C\u002Fcode>: \u003Ccode>running\u003C\u002Fcode> \u002F \u003Ccode>done\u003C\u002Fcode> \u002F \u003Ccode>failed\u003C\u002Fcode>, with \u003Ccode>created\u003C\u002Fcode> and \u003Ccode>skipped\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>POST \u002Fv1\u002Fadmin\u002Fchangelog\u002Fnotify\u002Frun\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>{ job: &quot;instant&quot; | &quot;weekly&quot;, dryRun, now }\u003C\u002Fcode>: run a beat by hand, or only report what it would do\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\u003C\u002Fdiv>\n\u003Cp>\u003Cstrong>What goes out and when.\u003C\u002Fstrong> A publish (an entry created published, a draft approved or\nmoved to published, a release published) stamps the entry; imports never do. The instant\nsweep mails the stamped entries once the newest has waited \u003Ccode>digest.quietMinutes\u003C\u002Fcode> (30) or the\noldest \u003Ccode>digest.maxWaitMinutes\u003C\u002Fcode> (60), as one email; entries published more than a week ago are\nhistory, not news. The weekly digest covers the previous Monday-to-Monday week in\n\u003Ccode>digest.timezone\u003C\u002Fcode>, from \u003Ccode>digest.weeklyHour\u003C\u002Fcode> on Monday, once (a ledger row stops a second send).\nAn entry in a draft release waits for the release. Delivery is at-most-once: a run that crashes\nmid-send is abandoned without resending, because a lost note beats the same note twice. Two\ntemplates carry the design, in your project's email templates: \u003Ccode>changelog\u003C\u002Fcode> (variables\n\u003Ccode>subjectLine\u003C\u002Fcode>, \u003Ccode>preheader\u003C\u002Fcode>, \u003Ccode>firstName\u003C\u002Fcode>, \u003Ccode>intro\u003C\u002Fcode>, \u003Ccode>entriesHtml\u003C\u002Fcode>, \u003Ccode>changelogUrl\u003C\u002Fcode>, \u003Ccode>footerNote\u003C\u002Fcode>,\n\u003Ccode>unsubscribeUrl\u003C\u002Fcode>, \u003Ccode>oneClickUnsubscribeUrl\u003C\u002Fcode>) and \u003Ccode>changelog-confirm\u003C\u002Fcode> (\u003Ccode>cadence\u003C\u002Fcode>, \u003Ccode>confirmUrl\u003C\u002Fcode>). The\nwords (\u003Ccode>changelog.siteName\u003C\u002Fcode>, \u003Ccode>changelog.copy\u003C\u002Fcode>) are yours to change; the defaults are neutral\n(&quot;We just shipped…&quot;). Erasing an end user deletes their subscriptions too: the ones tied to their\nuser id and the ones made with their address alone.\u003C\u002Fp>\n\u003Ch3 id=\"moving-a-changelog-over\">Moving a changelog over\u003Ca class=\"heading-anchor\" href=\"#moving-a-changelog-over\" aria-label=\"Link to this section\">#\u003C\u002Fa>\u003C\u002Fh3>\n\u003Cp>Before entries existed the changelog was posts with \u003Ccode>showOnChangelog\u003C\u002Fcode> (and\n\u003Ccode>changelogTitle\u003C\u002Fcode>, \u003Ccode>changelogBody\u003C\u002Fcode>, \u003Ccode>shippedIn\u003C\u002Fcode>). Those fields still exist and\n\u003Ccode>GET \u002Fv1\u002Ffeedback?view=changelog\u003C\u002Fcode> still answers, but nothing public reads them\nany more. Copy them into entries once, after deploying:\u003C\u002Fp>\n\u003Cdiv class=\"code-block\">\u003Cbutton type=\"button\" class=\"copy-btn\" data-copy aria-label=\"Copy to clipboard\">Copy\u003C\u002Fbutton>\u003Cpre class=\"shj-lang-bash\">\u003Ccode>\u003Cspan class=\"shj-syn-func\">bun\u003C\u002Fspan> scripts\u002Fbackfill-changelog.mjs           \u003Cspan class=\"shj-syn-cmnt\"># dry run\u003C\u002Fspan>\n\u003Cspan class=\"shj-syn-func\">bun\u003C\u002Fspan> scripts\u002Fbackfill-changelog.mjs --apply   \u003Cspan class=\"shj-syn-cmnt\"># write\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fdiv>\n\u003Cp>Every done, visible, marked post becomes a published entry linked to it, dated\nwith the post's \u003Ccode>completedAt\u003C\u002Fcode>; posts sharing a \u003Ccode>shippedIn\u003C\u002Fcode> get a published\nrelease of that version. It is idempotent and does not change the posts. Until\nyou run it, a changelog that lived on posts shows nothing.\u003C\u002Fp>\n\u003Ch2 id=\"notifications-1\">Notifications\u003Ca class=\"heading-anchor\" href=\"#notifications-1\" aria-label=\"Link to this section\">#\u003C\u002Fa>\u003C\u002Fh2>\n\u003Cp>With email notifications on (and Mailgun configured) HelpBites emails a post's\n\u003Cstrong>followers\u003C\u002Fstrong>. Its author follows it on posting, commenters on commenting, and\nanyone \u003Ccode>@mentioned\u003C\u002Fcode> on being mentioned; anyone can follow, unfollow or mute\nthrough \u003Ccode>\u002Fsubscription\u003C\u002Fcode>.\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\n\u003Cp>Followers, when it moves into a status with the bell on;\u003C\u002Fp>\n\u003C\u002Fli>\n\u003Cli>\n\u003Cp>followers, when someone posts a public comment;\u003C\u002Fp>\n\u003C\u002Fli>\n\u003Cli>\n\u003Cp>people \u003Ccode>@mentioned\u003C\u002Fcode> in a comment (only the thread's own participants can be\nmentioned) get a mention email instead of the comment email.\u003C\u002Fp>\n\u003C\u002Fli>\n\u003Cli>\n\u003Cp>followers, once, when a changelog entry that answers the post (or a board item that links it)\ngoes public: a &quot;Shipped: …&quot; mail with the entry's title and a link to it (see \u003Cem>Telling the\npeople it answers\u003C\u002Fem> under Changelog).\u003C\u002Fp>\n\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>Never the person who acted, never for team notes or hidden posts. Only people\nwhose identity token carried an \u003Ccode>email\u003C\u002Fcode> can be emailed, and\n\u003Ccode>PUT \u002Fv1\u002Ffeedback\u002Fpreferences { &quot;emails&quot;: false }\u003C\u002Fcode> turns all of it off for one\nuser. One person hears about one post at most every ten minutes per kind.\u003C\u002Fp>\n\u003Ch2 id=\"moving-an-existing-board-over\">Moving an existing board over\u003Ca class=\"heading-anchor\" href=\"#moving-an-existing-board-over\" aria-label=\"Link to this section\">#\u003C\u002Fa>\u003C\u002Fh2>\n\u003Cp>\u003Ccode>POST \u002Fv1\u002Ffeedback\u002Fimport\u003C\u002Fcode> with the \u003Cstrong>project secret\u003C\u002Fstrong>, up to 100 posts a call:\u003C\u002Fp>\n\u003Cdiv class=\"code-block\">\u003Cbutton type=\"button\" class=\"copy-btn\" data-copy aria-label=\"Copy to clipboard\">Copy\u003C\u002Fbutton>\u003Cpre class=\"shj-lang-js\">\u003Ccode>\u003Cspan class=\"shj-syn-kwd\">await\u003C\u002Fspan> \u003Cspan class=\"shj-syn-func\">fetch\u003C\u002Fspan>(\u003Cspan class=\"shj-syn-str\">`\u003C\u002Fspan>\u003Cspan class=\"shj-syn-kwd\">${\u003C\u002Fspan>\u003Cspan class=\"shj-syn-num\">HOST\u003C\u002Fspan>\u003Cspan class=\"shj-syn-kwd\">}\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\u002Fv1\u002Ffeedback\u002Fimport`\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> {\n  method\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">'POST'\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan>\n  headers\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> { authorization\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">`Bearer \u003C\u002Fspan>\u003Cspan class=\"shj-syn-kwd\">${\u003C\u002Fspan>process\u003Cspan class=\"shj-syn-oper\">.\u003C\u002Fspan>env\u003Cspan class=\"shj-syn-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-syn-num\">HELP_BITES_SECRET\u003C\u002Fspan>\u003Cspan class=\"shj-syn-kwd\">}\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">`\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> 'content-type'\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">'application\u002Fjson'\u003C\u002Fspan> }\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan>\n  body\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-num\">JSON\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-syn-func\">stringify\u003C\u002Fspan>({ posts\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> [{\n    externalId\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">'legacy:8Jd2kQ'\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan>          \u003Cspan class=\"shj-syn-cmnt\">\u002F\u002F upsert key: re-running converges\n\u003C\u002Fspan>    number\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-num\">12\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan>                            \u003Cspan class=\"shj-syn-cmnt\">\u002F\u002F keep old #12 links (optional)\n\u003C\u002Fspan>    title\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">'Export as CSV'\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> body\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">'&lt;p&gt;…&lt;\u002Fp&gt;'\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> type\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">'feature'\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> platform\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">'web'\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> tags\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> [\u003Cspan class=\"shj-syn-str\">'export'\u003C\u002Fspan>]\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> status\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">'planned'\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan>\n    author\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> { externalId\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">'user_42'\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> name\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">'Ada'\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> email\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">'ada@example.com'\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> username\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">'ada'\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> avatarUrl\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">'https:\u002F\u002F…'\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> emailOptOut\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-bool\">false\u003C\u002Fspan> }\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan>\n    createdAt\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">'2024-03-01T10:00:00Z'\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> editedAt\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-num\">null\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> completedAt\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-num\">null\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan>\n    hidden\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-bool\">false\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> featured\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-bool\">false\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> locked\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-bool\">false\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> showOnChangelog\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-bool\">false\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> shippedIn\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">''\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> adminNote\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">''\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan>\n    votes\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> [\u003Cspan class=\"shj-syn-str\">'user_42'\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">'user_7'\u003C\u002Fspan>]\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan>          \u003Cspan class=\"shj-syn-cmnt\">\u002F\u002F exactly these voters\n\u003C\u002Fspan>    reactions\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> { '+1'\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> [\u003Cspan class=\"shj-syn-str\">'user_7'\u003C\u002Fspan>] }\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan>       \u003Cspan class=\"shj-syn-cmnt\">\u002F\u002F exactly these reactions\n\u003C\u002Fspan>    subscriptions\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> [{ externalId\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">'user_42'\u003C\u002Fspan> }\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> { externalId\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">'user_7'\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> muted\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-bool\">true\u003C\u002Fspan> }]\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-syn-cmnt\">\u002F\u002F exactly these followers\n\u003C\u002Fspan>    pinnedCommentExternalId\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">'c1'\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan>\n    duplicateOfExternalId\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-num\">null\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan>           \u003Cspan class=\"shj-syn-cmnt\">\u002F\u002F the post this one duplicates; import it first\n\u003C\u002Fspan>    comments\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> [\n      { externalId\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">'c1'\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> body\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">'Yes please'\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> author\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> { externalId\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">'user_7'\u003C\u002Fspan> }\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> isTeam\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-bool\">false\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> createdAt\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">'2024-03-02T09:00:00Z'\u003C\u002Fspan> }\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan>\n      { externalId\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">'c2'\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> body\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">'Agreed'\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> author\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> { externalId\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">'user_9'\u003C\u002Fspan> }\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> parentExternalId\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">'c1'\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> replyToExternalId\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">'c1'\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> reactions\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> { heart\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> [\u003Cspan class=\"shj-syn-str\">'user_7'\u003C\u002Fspan>] } }\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan>\n      { externalId\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">'c3'\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> body\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">'Check the export queue first'\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> isTeam\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-bool\">true\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> visibility\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">'internal'\u003C\u002Fspan> }\n    ]\n  }] })\n})\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fdiv>\n\u003Cp>Authors and voters are your own user ids — the \u003Ccode>sub\u003C\u002Fcode> your tokens will carry —\nso people keep their posts and votes when they next sign in. Without\n\u003Ccode>subscriptions\u003C\u002Fcode> a post's author and public commenters follow it. Posts without\na \u003Ccode>number\u003C\u002Fcode> take the next free one, and the board's counter moves past every\nimported number. Imports never send notifications.\u003C\u002Fp>\n\u003Cp>The board must already define every \u003Ccode>status\u003C\u002Fcode>, \u003Ccode>type\u003C\u002Fcode>, \u003Ccode>platform\u003C\u002Fcode> and tag key the\nposts use (set them first with \u003Ccode>PATCH \u002Fv1\u002Fadmin\u002Fprojects\u002F:id\u003C\u002Fcode>); an unknown key\nfails that post. Check \u003Ccode>GET \u002Fv1\u002Ffeedback\u002Fconfig?pk=\u003C\u002Fcode> before a large import. Release scripts publish changelog entries with\n\u003Ccode>POST \u002Fv1\u002Fadmin\u002Fchangelog\u002Fentries\u003C\u002Fcode> (see \u003Ca href=\"#changelog\">Changelog\u003C\u002Fa>), not by importing posts.\u003C\u002Fp>\n\u003Ch2 id=\"account-deletion\">Account deletion\u003Ca class=\"heading-anchor\" href=\"#account-deletion\" aria-label=\"Link to this section\">#\u003C\u002Fa>\u003C\u002Fh2>\n\u003Cp>Email-only users (see \u003Ccode>docs\u002FCONNECTING.md\u003C\u002Fcode>) erase themselves with \u003Ccode>DELETE \u002Fv1\u002Fanonymous\u002Fme\u003C\u002Fcode>. For users your server signs in:\u003C\u002Fp>\n\u003Cp>\u003Ccode>DELETE \u002Fv1\u002Fend-users\u002F:externalId\u003C\u002Fcode> with the project secret removes everything\nthat user left in the project: their posts (with the votes, comments and\nfollowers on them), their comments, votes, reactions and follows elsewhere,\ntheir support conversations, and the end-user record. Call it from your own account-deletion flow.\u003C\u002Fp>\n\u003Ch2 id=\"upgrading-a-board-from-before-numbers-and-follows\">Upgrading a board from before numbers and follows\u003Ca class=\"heading-anchor\" href=\"#upgrading-a-board-from-before-numbers-and-follows\" aria-label=\"Link to this section\">#\u003C\u002Fa>\u003C\u002Fh2>\n\u003Cp>Boards created before post numbers and follows existed need one backfill after\nthe deploy (after \u003Ccode>bun run schema:apply\u003C\u002Fcode>):\u003C\u002Fp>\n\u003Cdiv class=\"code-block\">\u003Cbutton type=\"button\" class=\"copy-btn\" data-copy aria-label=\"Copy to clipboard\">Copy\u003C\u002Fbutton>\u003Cpre class=\"shj-lang-bash\">\u003Ccode>\u003Cspan class=\"shj-syn-func\">bun\u003C\u002Fspan> scripts\u002Fbackfill-feedback.mjs          \u003Cspan class=\"shj-syn-cmnt\"># dry run\u003C\u002Fspan>\n\u003Cspan class=\"shj-syn-func\">bun\u003C\u002Fspan> scripts\u002Fbackfill-feedback.mjs --apply  \u003Cspan class=\"shj-syn-cmnt\"># number old posts, add follows\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fdiv>\n\u003Cp>It numbers unnumbered posts oldest first and makes each post's author and\ncommenters follow it, so emails keep reaching the people they did before.\u003C\u002Fp>\n",[6,10,13,16,19,23,26,29,32,35,38,41,44,47,50,53,56,58,61,64],{"id":7,"text":8,"level":9},"1-turn-it-on","1. Turn it on",2,{"id":11,"text":12,"level":9},"2a-plug-and-play","2a. Plug and play",{"id":14,"text":15,"level":9},"2b-your-own-ui","2b. Your own UI",{"id":17,"text":18,"level":9},"planning--roadmap","Planning & roadmap",{"id":20,"text":21,"level":22},"in-the-panel","In the panel",3,{"id":24,"text":25,"level":22},"what-goes-public","What goes public",{"id":27,"text":28,"level":22},"showing-it","Showing it",{"id":30,"text":31,"level":22},"the-api","The API",{"id":33,"text":34,"level":9},"changelog","Changelog",{"id":36,"text":37,"level":22},"made-from","Made from",{"id":39,"text":40,"level":22},"telling-the-people-it-answers","Telling the people it answers",{"id":42,"text":43,"level":22},"public","Public",{"id":45,"text":46,"level":22},"api-writers","API (writers)",{"id":48,"text":49,"level":22},"comments","Comments",{"id":51,"text":52,"level":22},"notifications","Notifications",{"id":54,"text":55,"level":22},"moving-a-changelog-over","Moving a changelog over",{"id":57,"text":52,"level":9},"notifications-1",{"id":59,"text":60,"level":9},"moving-an-existing-board-over","Moving an existing board over",{"id":62,"text":63,"level":9},"account-deletion","Account deletion",{"id":65,"text":66,"level":9},"upgrading-a-board-from-before-numbers-and-follows","Upgrading a board from before numbers and follows",1792677543075]