From feedback to roadmap without a second tool

Product

Most teams run these as two systems. A feedback board where users post ideas and vote, and a public roadmap where the team shows what is coming. Two tools, two databases, and a manual copy-paste step in between that stops happening around week six.

Then the roadmap goes stale, users notice, and it quietly disappears from the nav.

They are the same records

A roadmap item is a feedback post that got accepted. That is the entire relationship, and once you model it that way the copy-paste step disappears — because there is nothing to copy.

In Kookee, feedback posts live on a board with columns your team defines — Open, Planned, In progress, Shipped, whatever fits. The feedback board shows every column. The roadmap shows the same posts, grouped by the columns you choose to make public:

TypeScript
const columns = (await kookee.feedback.getColumns()).filter((column) => column.isVisible);
const { data: posts } = await kookee.feedback.list({ sort: 'top', limit: 100 });

const roadmap = columns.map((column) => ({
  ...column,
  posts: posts.filter((post) => post.columnId === column.id),
}));

There is no roadmap API, because there does not need to be one. Moving a card is dragging it to another column, and the person who does it is the person triaging feedback anyway. In React, Roadmap from @kookee/react/roadmap renders the same grouping as a read-only board — see Show a read-only roadmap with the React components.

Voting is prioritization input, not the decision

Posts have vote counts and comment counts, and sort: 'top' and sort: 'trending' surface what people care about now:

TypeScript
const top = await kookee.feedback.list({ sort: 'top', category: 'feature', limit: 20 });

Worth being clear about what this data is. Vote counts tell you what your loudest, most engaged users want — which is a real signal and not the same thing as what your whole user base needs. Use it to find the things you did not know about, not to outsource the roadmap. A closed "Declined" column exists precisely so you can say no in public with a reason attached.

Collection happens where moderation lives

Posting, voting and commenting happen on your hosted portal, where visitors sign in. That is deliberate: one person, one vote, and your team's moderation in one place — no spam from a leaked key, no identity you have to vouch for from your own backend.

Your application reads the board through the API and renders it however you like — a roadmap page, a "most requested" widget in your pricing page, a link from a feature's empty state to the discussion about it. The write side stays on the portal; the read side goes anywhere.

Close the loop, visibly

Two details do most of the work in making a board feel alive.

Comments by project owners and admins are flagged with isOfficial, so an answer from your team renders differently from a reply by another user. And getTopContributors() returns the people whose posts drew the most votes — the handful of people who consistently tell you the truth about your product, which is a list worth having for reasons well beyond a leaderboard.

The loop that keeps a board healthy is short: someone posts, the team responds in public, the card moves, and it appears on the roadmap. Then it ships and lands in the changelog — which is one more kookee.changelog entry, in the same project, behind the same API key.