Skip to main content

Run a Public Idea Board and Roadmap

Devotel Orbit hosts a feature-request portal for your workspace: your end users submit product ideas, upvote the ones they want, comment for context, and watch a public roadmap move each idea from planned to shipped. This is the Canny / Productboard class of surface — it captures and prioritizes product feedback, distinct from the peer-to-peer community board where visitors answer each other’s questions. Because every route is unauthenticated, signed-out visitors participate without an account. The endpoint paths below are relative. Send them against https://api.orbit.devotel.io/api/v1.

Ideas board vs community — pick the right surface

Orbit ships two anonymous public-feedback surfaces. They serve different jobs: Point “I wish the product could…” traffic at the ideas board and “how do I…” traffic at the community board. Both are described in the Public API reference.

1. Enable the board

The idea board is opt-in per workspace. Until you flip it on, the public endpoints answer nothing. Enable it by setting settings.ideas.enabled = true on your organization settings — the same workspace-level block that holds the parallel settings.community.enabled flag (dashboard Settings → Organization, or the organizations API). The board’s tables are created on first use per workspace, so there is nothing to migrate and nothing to clean up if you turn the feature off again.

2. Scope every request to your workspace

Every request carries your workspace slug in the ?org=<slug> query parameter — the same tenant-scoping contract the hosted help center and community board use. The value is your organization url slug (lowercase letters, digits, hyphens). On write calls (POST, vote, comment) the slug travels in the JSON body as "org": "<slug>"; on reads it travels as a query parameter.

3. Anonymous-visitor posture

The board answers only anonymous traffic; no API key or session is accepted. To keep that posture safe, the gate collapses two failure modes into one: an unknown organization slug and an organization that has not enabled the idea board both return the identical 404. A probe cannot enumerate which tenants exist or which of them turned the feature on. Anything other than 404 means the workspace resolved and the flag is set. Anonymous visitors can never move an idea through its lifecycle — the public write paths only create ideas (always landing in under_review), comments, and votes. Status changes are an operator action.

4. Browse, submit, vote, comment

All paths below sit under /api/v1/public/ideas.

List ideas (GET /public/ideas)

The top sort ranks by vote count — the queue your visitors most want. Add &q=<term> for substring search over titles and bodies, &status=<under_review|planned|in_progress|shipped|declined> to filter by lifecycle stage, and &limit= / &offset= (cap 50 per page) to paginate. Each item returns id, title, an excerpt of the body, author_name, category, status, an optional admin_response note, and the vote_count / comment_count / view_count counters.

Submit an idea (POST /public/ideas)

A new idea is visible to everyone immediately and lands in under_review (201 with the fi_… id). Validation runs at the boundary: a title under 8 characters or a body under 15 returns 400 with per-field issues in the error envelope. category and locale are optional.

Vote on an idea (POST /public/ideas/:id/vote)

One vote per visitor per idea: the voter’s IP is hashed per idea and de-duplicated through that hash, so a repeat call is a no-op that returns { voted: false, vote_count } with the current count.

Comment on an idea (POST /public/ideas/:id/comments)

Commenting on a hidden or never-created idea returns the same 404 as an unknown org — visitors cannot probe which ideas exist.

Read an idea (GET /public/ideas/:id)

The response returns the full idea body plus its published comments. Each read bumps the idea’s view_count.

5. The public roadmap (GET /public/ideas/roadmap)

The roadmap returns three columns — planned, in_progress, and shipped — each holding up to 50 ideas. The planned and in-progress columns rank by vote count; the shipped column reads newest-first, so it doubles as a “what we shipped” feed for your own change-log page. Each idea carries the operator’s admin_response note (for example “Shipping in v2.4”) when one is set.

6. Operator workflow

Visitors never touch an idea’s status — moving a submission along the lifecycle is an operator action on your side:
  1. Triage. Work the under_review queue first. New submissions land there; sort=top tells you which closed queue to work next by raw demand.
  2. Commit. Promote an agreed idea to planned, move it to in_progress when build starts, and close it as shipped — or answer declined with a reason. Set an admin_response when you do; the note shows publicly against the idea.
  3. Publish. The shipped column is your published changelog feed. Read GET /public/ideas/roadmap?org=<slug> from your own site and render the shipped column next to (or as) your change-log so voters see the follow-through; the shipped ordering is already newest-first.
  4. Hide abuse. Hidden ideas and comments never surface in browse, search, detail, or roadmap responses — the public surface reads only published rows.

7. Abuse posture

The public routes are unauthenticated, so the surface relies on server-side limits and pseudonymised tracking rather than writer identity:
  • Rate limits per IP — reads at 120/min, votes at 30/min, and writes (submit + comment) at 5/min. A 429 means your embed is degrading or one visitor is scripting the board.
  • HMAC-hashed identifiers — the author email and the visitor IP are stored only as irreversible hashes, computed with a server-held key. The hashes exist for abuse tracing and vote de-duplication; raw IPs and raw emails never persist.
  • Content length caps — titles cap at 200 characters, bodies and comments at 10,000, author names at 80. An oversized write returns 400 before it is stored.
  • Opt-in discovery — the 404-collapse described above denies tenant enumeration as well.
The idea board is one of three self-service surfaces you can wire into your CX stack; pick per intent: