Roadmap spaces

Embed
Preview of the card
<a href="https://forumulate.com/guides/roadmap-spaces"><img src="https://forumulate.com/guides/_api/public/orgs/046a9bc0-d530-43ae-9bae-af7945008cda/spaces/36aa4df0-1e95-4cef-8ae9-795169441345/pages/6b1e9b50-6f40-468b-a89e-32c04b919910/social-card.svg?v=3e7ae33eec8b" alt="Roadmap spaces" width="600" height="315"></a>

A Roadmap space is a voting board: members suggest items, readers vote, and the board arranges the items by status. This guide covers everything a board can look like and do: its looks and how to mix them, the pages around the board (tags, statuses, the submit page, feeds, the widget), one item's own overrides, the theme-component outlets, and the behaviour settings with their defaults. The basics of lanes, voting and anonymous votes are summarised in Roadmap and Status spaces.

Everything under theme.roadmap lives in the space theme you already set with PUT or PATCH /orgs/{orgId}/spaces/{spaceId}/theme. Every key is optional, and like every theme object it is strict: a misspelt key is a 422 that names it, never a silent default.

Layers: theme, space, item

What a reader sees is built in this order, and every key you set wins over the layer under it:

  1. the default layer of the look you picked (never stored);

  2. the site theme, when the board is part of a site;

  3. the space theme (theme.roadmap);

  4. one item's own customization.theme.roadmap (its page only).

GET …/theme answers with what you stored, never the resolved theme, so you can send it back whole without freezing the defaults into it.

Looks are patches

theme.roadmap.look picks a starting point. A look is not a mode: it is a handful of independent keys laid under your own, so any key you set still wins, and any key a look sets can be set alone under another look.

Look

What it is

The keys it sets

default (unset)

Panels in the site's reading column

The default layer below

paper

The plain board, exactly as it drew before the type had a look

Nothing: no layer at all. Note that width then falls back to board (72rem)

kanban

GitHub's or Linear's project board: every lane side by side, scrolling sideways

width: max; lanes: {style: tinted-header, overflow: scroll, minWidth: 280, empty: show}; card: {frame: {surface: raised}, meta: [comments, target, tags], pinMark: pin, titleLines: 2}; vote.position: top-end

minimal

Canny's quiet list of requests

lanes: {style: plain, headerRule: true, countStyle: text, empty: hide}; card: {frame: {surface: flat}, density: compact, meta: [comments], excerptLines: 1, progress: text}; vote: {style: inline, glyph: arrow, position: end}

portal

Productboard's portal: a hero over the board

header: {layout: hero, hero: {align: center, size: lg}, subtitlePlacement: under-title}; filters.style: segmented; card: {cover: top, pinMark: label, eyebrow: theme}; vote.style: ghost

The default layer (what default is, and what every other look but paper patches over):

  • width: column, so the board's title lines up with every other page on the site;

  • lanes: style: panel, glyph: dot, overflow: fit, empty: collapse, countStyle: pill, phone: tabs, and tones (Submitted and Declined neutral, Planned info, In progress warning, Shipped success);

  • card: meta: [comments, target], progress: bar, cover: top;

  • vote: style: pill, glyph: chevron, position: start;

  • header: subtitlePlacement: under-title, subscribe: true;

  • submit: sections: [problem, proposal], rich, similar, tags and theme all on;

  • filters: sort: [top, new, trending], tags: true, mine: true;

  • item: a side rail (vote, voters, follow, facts, share, actions), voters: stack, and activity, response, shippedIn, commentSortControl, reactions, replies, staffBadge and mergedRequests on, with merged: redirect.

Each of those is one key, so an owner who wants one of them back sets that key alone.

Combinations no look gives you

The minimal list with the portal's hero over it:

curl -X PATCH "https://forumulate.com/api/orgs/$ORG/spaces/$SPACE/theme" \
  -H "Authorization: Bearer $FORUMULATE_TOKEN" -H 'content-type: application/json' \
  -d '{ "roadmap": {
        "look": "minimal",
        "header": { "layout": "hero", "hero": { "eyebrow": "Product roadmap", "align": "center" } }
      } }'

The kanban board in the reading column, with votes counted but not shown:

{ "roadmap": { "look": "kanban", "width": "column", "lanes": { "overflow": "fit" }, "vote": { "count": "hide" } } }

The portal look on dark cards, numbered like issues:

{
  "roadmap": {
    "look": "portal",
    "card": { "eyebrow": "number", "numberPrefix": "RFC" },
    "colors": {
      "card": { "color": "#111827", "colorDark": "#0b0f19" },
      "cardTitle": { "color": "#f9fafb" }
    }
  }
}

Presets: board, list, timeline

The preset is the board's arrangement, set with PUT /orgs/{orgId}/spaces/{spaceId}/preset ({"preset": "timeline"}) or in the studio's Look fold.

Preset

What it draws

board (recommended)

Columns by status, cards within them

list

One column grouped by status, read top to bottom. Best for long boards

timeline

Items along their horizons (Now, Next, Later, or Q1, Q2 …), left to right: when, not what state. Its rail and dots take colors.timeline

A timeline reads best on a board whose lanes are horizons: set groupBy: "target" and horizons on the settings door (below).

The board keys

lanes

Key

Default

What it does

style

panel

plain (loose text), panel (a bordered panel), tinted-header

headerRule

off

A hairline under each lane's head

gap

Space between lanes, 0–48 px

minWidth

A lane's narrowest width, 160–480 px (fit and scroll)

overflow

fit

wrap (the old fixed grid), fit (columns sized to the lanes shown), scroll (sideways)

empty

collapse

An empty lane is shown with its line, folded to a thin head, or hiden

emptyText

"Nothing here yet"

What an empty lane says, in your words

countStyle

pill

text or pill

phone

tabs

Under 768px: stack, tabs (one lane at a time), swipe

glyph

dot

Every lane's mark: none, dot, ring, half, check

tones

see above

Each stock state's colour: {tone} (a palette slot, so a repainted palette.success repaints Shipped) or {color, colorDark}. Keys: submitted, planned, inProgress, shipped, declined

statuses

Each stock state's own glyph and one-line description ("Actively being built")

A status the board added carries its colour, mark and description on the status itself (the settings door's statuses), because a theme cannot know a key a board invented.

card

Key

Default

What it does

frame

The shared card surface (surface, radius, padding, shadow)

density

compact, cozy, comfortable

eyebrow

none

The line above the title: tag, theme, board (on a feed of several boards), number ("#24")

meta

[comments, target]

The footer, in order: comments, target, effort, tags, author, number

fields

Up to 6 of the space's own public fields by key, after meta. An internal field is never sent to readers, so naming one shows nothing

titleLines

never clipped

1–4

excerptLines

2

0–4; 0 hides it

progress

bar

The item's progress field as a bar, as text, or none

cover / coverAspect

top / wide

The item's cover across the card; wide, banner, square. The box is reserved, so nothing shifts

numberPrefix

"roadmap" → "roadmap #24"

pinMark

none

How a pinned item says so: pin glyph or label

vote

Key

Default

What it does

style

pill

box, pill, inline, ghost

position

start

start, end, top-end (a corner pill), footer

glyph

chevron

chevron, arrow, thumb, triangle

votedFill

brand

What a cast vote fills with: brand or ink

count

show

hide keeps the button and drops the number, so the loudest request does not read as the plan

budget

off

"3 votes left" beside the control, on a board with a vote budget

header, filters and colors

Key

Default

What it does

header.layout

line

hero draws header.hero in place of the title row

header.hero

eyebrow, headline (the <h1>; unset = the board's name), align, size (sm/md/lg), submit (the button, on), submitLabel ("Suggest an idea"), layers/layersDark (the title band's background vocabulary), ink/inkDark

header.subtitle

the stock line

The line under the title. It is also the board's meta description and its feeds' description

header.subtitlePlacement

under-title

under-title, under-submit (falls back under the title when there is no submit band), none

header.subscribe

on

A Subscribe control beside the title: the board's moves by email, its feed, and Follow for members

filters.sort

[top, new, trending]

The sort row: top, new, trending, updated, oldest, comments, manual

filters.defaultSort

top

The order the board opens in. manual is your team's priority order

filters.tags / status / mine

tags and mine on

Rows of tags, of lanes, and "My votes"

filters.style

pills

pills, tabs, segmented

width

column

column (56rem), board (72rem), max (the page shell), full (the window)

colors.*

One colour per element: card, cardBorder, cardTitle, lane, laneHead, vote, voted (wins over votedFill), pin, timeline. Each is a palette tone or a color with its colorDark twin

Filtering is done on the server through the address (?sort=&tag=&status=&mine=1&q=), so a filtered board is a link a reader can share.

submit

The public "suggest" form. sections names the shape's slots it asks for, in order (problem, proposal; a key the shape does not have is skipped); rich offers the rich editor behind a "Formatting" switch; similar lists requests that look like the one being typed, so the duplicate gets a vote instead of a twin; tags and theme let the author pick from the board's own.

The item page

theme.roadmap.item shapes one item's page.

Key

Default

What it does

rail

side rail

layout (side or stacked), items (vote, voters, follow, facts, share, actions and the other shared rail items), sticky (on), align (end)

actions

every allowed one

items from copyLink, copyMarkdown, viewMarkdown, print, share

voters

stack

none, stack (faces and a count), list (faces with names). A member who hid their profile is counted, never shown

activity

on

The item's history: every lane it moved through, and when

response

on

The team's latest answer in a panel under the title

shippedIn

on

"Shipped in v2.4" for an item a published changelog release cites

commentSort / commentSortControl

oldest / on

The comment order, and a Newest/Oldest switch

reactions, replies, staffBadge

on

Reactions, one-level replies, a "Team" chip on staff comments

merged

redirect

A merged request either sends its address on to the survivor (308) or keeps its page with a "Merged into" notice

mergedRequests

on

The survivor lists what was merged into it, with their votes

width

text

text, wide, board, full

titleSize

lg

sm, md, lg, xl

meta

[author, comments]

The byline: author, date, updated, comments, votes, views. [] hides it

cover

none

above-title, below-title, banner

statusChip

auto

auto, themed (the lane's colour and mark), plain, none

back

on

The "Back to roadmap" link

tags

off

The item's tags as chips, each linking to its tag page

related

none

by (tags, status, tags-or-status), count (1–12, 4), layout (list, cards)

languages

above-title

Where the languages row sits: above-title, below-title, none

colors

title, meta, surface, border, response, tag, rail, each a tone with a dark twin

print

all off

What a printed item keeps beyond its head and body: comments, activity, rail

phone

rail (last, first, hidden) and titleSize under 640px

One item's own overrides

A moderator can give one item its own look: anything a piece may carry (CSS, token keys, a share image, og title and description, JSON-LD) plus theme.roadmap, which a roadmap item's page reads over the space's. Send it under customization on POST …/roadmap or PATCH …/roadmap/{topicId}; keys merge and null clears. It needs forum.moderate (a 403 naming the key from anyone else). Another type's namespace (theme.post, theme.faq, …) is refused with a 422 that names it, because no roadmap page would draw it.

curl -X PATCH "https://forumulate.com/api/orgs/$ORG/spaces/$SPACE/roadmap/$TOPIC" \
  -H "Authorization: Bearer $FORUMULATE_TOKEN" -H 'content-type: application/json' \
  -d '{ "customization": { "theme": { "roadmap": {
        "item": { "cover": "banner", "titleSize": "xl", "rail": { "layout": "stacked" } }
      } } } }'

In the studio it is Page look & sharing on the item. The general rules are in Theme your site → Per-page customization.

Pages around the board

Address (under the board's mount)

What it is

/<slug>

An item. /<slug>.md is its markdown twin

/new

The submit form on its own page, the address to link a "Request a feature" menu item to

/tag and /tag/<tag>

Every tag with its count, and every request with one tag

/status and /status/<status>

Every lane with its count, and every request in one lane. The slug is the status key in lower case with - (/status/in-progress). A lane the board keeps private is a 404 here as on the board

/me

The reader's own activity on the board

rss.xml, atom.xml, feed.json

The board's feeds

widget.js, widget.json

The feedback widget (below)

tag, status and new are therefore reserved: no item can take those slugs. The hub pages and every public lane are in the sitemap, and an item with published translations is served at /<lang>/<slug> with hreflang naming each language (see Translations everywhere). Any board or item address with ?embed=1 renders bare, for an iframe.

Feeds

theme.roadmap.feed sets the feeds' words: title (unset: the board's name), description (unset: header.subtitle, then the name) and statusPrefix ("[Planned] …" before each entry, on). Channel text is in the board's language, with <language> set.

The feedback widget

A floating "Feedback" launcher a customer puts in their own product. It lists the most-wanted requests, what shipped lately, or both, and a "Suggest an idea" link to /new:

<script src="https://acme.com/roadmap/widget.js" async></script>

Its defaults come from theme.roadmap.widget:

Key

Default

What it does

position

bottom-right

bottom-right, bottom-left, top-right, top-left

show

both

top, shipped, both

limit

5

Items listed, 1–10

label

"Feedback"

The launcher's words

accent

A tone with a dark twin

radius

12

0–24 px

badge

on

A count of what shipped since the reader last opened it

submit

on

"Suggest an idea" in the panel

scheme

auto

auto, light, dark

The script tag can override any of them per page with data-position, data-show, data-accent, data-scheme, data-locale and data-label; data-trigger="#my-button" opens the panel from your own button instead of drawing a launcher.

Outlets

Theme components (Theme components) can be placed in these roadmap outlets:

Outlet

Page

Context

What it is

roadmap-above-board

board

Above the board

roadmap-below-board

board

Below the board

roadmap-lane-head

board (repeats)

lane.name, lane.status

Under the heading of every lane

roadmap-card-footer

board (repeats)

item.title, item.slug, item.status

The foot of every card

roadmap-item-above-body

item

item.title, item.slug, item.status

Between the title and facts and the description

roadmap-item-aside

item

same

Under the vote and status card

roadmap-item-after-body

item

same

After the description, before the discussion

None of them wraps reader content: each adds beside it.

Behaviour settings

Board behaviour lives on GET/PUT /orgs/{orgId}/spaces/{spaceId}/roadmap-settings. Every field resolves the same way: this board's value, then the organization's default where one exists, then today's behaviour.

Field

Default

What it does

anonymousVotes

off (org default roadmap.anonymous_votes)

Logged-out readers may vote. See how an anonymous vote is counted

statusLabels

the stock words (org default roadmap.statuses)

This board's words for the five stock lanes

statuses

none

Up to 10 lanes of your own: {key, label, description?, tone?, glyph?}. key is UPPER_SNAKE_CASE. Removing one that still holds items is a 409; a stock key is a 422

statusOrder

stock order

The order lanes render in. An ordering, not a filter: lanes left out are appended

hiddenStatuses

none

Lanes not drawn. Their items 404 to readers unless the lane is also in publicStatuses. One lane must stay

publicStatuses

none

Hidden lanes whose items still open by their own link

closedStatuses

none

Lanes whose items take no more votes

groupBy

status

What a lane is: status, the item's target (a horizon), or its theme

horizons

free text

Up to 12 horizons in order ("Now", "Next", "Later"), written as the target field's options. [] makes target free text again

voteMode

binary

importance: a voter picks nice to have, important or critical, and items rank by the sum

voteBudget

0 (unlimited)

How many items one voter may back at once, 0–100

voteWeights

equal

Five numbers, one per trust level 0–4, each 0–10. Anonymous votes always count one

downvotes

off

Members may vote against an item. Never anonymous readers: an anonymous vote is demand, not a veto

moderateSubmissions

off

Hold every public submission, signed in or not, for review

Who may post is a community setting, on PATCH …/forum/settings (whoCanPost), with the organization default community.who_can_post. SHIPPED is reserved: a release that ships an item moves it there, whatever the lane is called.

curl -X PUT "https://forumulate.com/api/orgs/$ORG/spaces/$SPACE/roadmap-settings" \
  -H "Authorization: Bearer $FORUMULATE_TOKEN" -H 'content-type: application/json' \
  -d '{ "groupBy": "target", "horizons": ["Now", "Next", "Later"],
        "statuses": [{ "key": "UNDER_REVIEW", "label": "Under review", "tone": { "tone": "info" }, "glyph": "ring" }],
        "closedStatuses": ["SHIPPED", "DECLINED"], "voteBudget": 10 }'

The team's priority order

filters.defaultSort: "manual" (or ?sort=manual) orders each lane the way your team placed it, with anything never placed following by votes. Set a lane's order with PUT /orgs/{orgId}/spaces/{spaceId}/roadmap/lanes/{status}/order and {"ids": [...]} (needs forum.moderate), or move cards up and down on the studio's board.

Front-door switches

On PUT …/preset under frontDoor: showVotes, showLaneCounts and showEmptyLanes (all on), badgeStyle (the lane heads and status chip, pill by default), and subscribeStyle (menu).

In the studio

  • Look fold: the preset, the Board look picker, the front-door switches, and every theme.roadmap key, which also opens by double-clicking its part on the page.

  • Pages fold → Board: the items, their moves between lanes, and their order within a lane.

  • Settings fold → Lanes and voting: lane names, your own statuses with their colours and marks, hidden, public and closed lanes, grouping and horizons, vote mode, budget, weights, downvotes and review.

  • Settings fold → Network access: the IP allow-list (below).

  • An item → Page look & sharing: its own overrides.

Restricting a board to your network

settings.networkAccess.ipAllowList limits every public read of the board (pages, feeds, markdown, JSON and the widget) to a list of IP addresses and CIDR blocks; readers elsewhere see "Not available from this network". An empty list means everyone. See Network allow-list.

Importing from Canny or Featurebase

POST /orgs/{orgId}/imports/roadmap with {"source": "canny" | "featurebase", "data": …} turns an export (CSV text or JSON) into a board: statuses mapped onto the five lanes, ETA as target, category as theme, named voters as votes and the rest as anonymous votes. It is re-runnable by slug. Details in Importers.

Templates

Six official roadmap templates start a board with a look and sample items: Lanyard (bordered lane panels, square vote box), Fernway (four open columns with marks and descriptions), Gridlane (a dense sideways-scrolling project board), Horizon (Now / Next / Later), Slate (a dark, dense list) and Milepost (a timeline in the team's own priority order, minimal look, pin marks). Each is only keys and settings, so every one of them can be changed afterwards like any board.

What you need to set up

Nothing on a roadmap needs an account or a credential. A few parts stay empty until their data exists:

  • "Shipped in" appears only once a published changelog release cites the item.

  • Cover and progress on cards draw only for items with coverUrl or progress set.

  • The vote budget chip (vote.budget) shows only on a board with a voteBudget.

  • Item languages appear only once the item has a published translation in one of the space's languages.

  • Network access is open to everyone until you add an address.

In the API

Every operation below is also reachable from the other surfaces without a dedicated command. forumulate api <Operation> calls it from the CLI, the JavaScript SDK has a typed method for each one (the last column names it; the Python and Go clients are generated from the same spec), and an agent reaches the organization-scoped ones through the MCP call_api tool. Where a CLI command or MCP tool exists too, the last column names it. Operation names link to the API reference.

Roadmap

Operation

Request

What it does

Shortcuts

Roadmap_setLaneOrder

PUT /orgs/{orgId}/spaces/{spaceId}/roadmap/lanes/{status}/order

Replace the order in a space

client.roadmap.setLaneOrder()

Roadmap_board

GET /orgs/{orgId}/spaces/{spaceId}/roadmap

Get the roadmap in a space

forumulate roadmap board<br>client.roadmap.board()

Roadmap_submit

POST /orgs/{orgId}/spaces/{spaceId}/roadmap

Create the roadmap in a space

forumulate roadmap submit<br>client.roadmap.submit()

Roadmap_get

GET /orgs/{orgId}/spaces/{spaceId}/roadmap/{topicId}

Get the roadmap

forumulate roadmap get<br>client.roadmap.get()

Roadmap_update

PATCH /orgs/{orgId}/spaces/{spaceId}/roadmap/{topicId}

Update the roadmap

forumulate roadmap update<br>client.roadmap.update()

Roadmap_vote

POST /orgs/{orgId}/spaces/{spaceId}/roadmap/{topicId}/vote

Create the vote in a space (roadmap)

forumulate roadmap vote<br>client.roadmap.vote()

Roadmap_comment

POST /orgs/{orgId}/spaces/{spaceId}/roadmap/{topicId}/comments

Create a comment in a space (roadmap)

forumulate roadmap comment<br>client.roadmap.comment()

Roadmap_proxyVotes

GET /orgs/{orgId}/spaces/{spaceId}/roadmap/{topicId}/proxy-votes

List proxy votes in a space

forumulate roadmap proxy-votes list<br>client.roadmap.proxyVotes()

Roadmap_recordProxyVote

POST /orgs/{orgId}/spaces/{spaceId}/roadmap/{topicId}/proxy-votes

Create a proxy vote in a space

forumulate roadmap proxy-votes add<br>client.roadmap.recordProxyVote()

Roadmap_removeProxyVote

DELETE /orgs/{orgId}/spaces/{spaceId}/roadmap/{topicId}/proxy-votes/{voteId}

Delete the proxy vote

forumulate roadmap proxy-votes remove<br>client.roadmap.removeProxyVote()

Roadmap_merge

POST /orgs/{orgId}/spaces/{spaceId}/roadmap/{topicId}/merge

Merge the roadmap

forumulate roadmap merge<br>client.roadmap.merge()

Roadmap_setStatus

POST /orgs/{orgId}/spaces/{spaceId}/roadmap/{topicId}/status

Create the status in a space (roadmap)

forumulate roadmap status<br>client.roadmap.setStatus()<br>MCP set_roadmap_status

The Forumulate teamUpdated