product-roadmap
The one plugin in the Zeitlines repository. It turns a timeline into a product roadmap that carries its pricing model: tiers, features, a matrix cell for every pair, highlight cards, and an ordered list of versions that cells can refer to.
What it contributes
- A pricing view: the tier × feature matrix, editable cell by cell
- A card view of the same model, for embedding on a public pricing page
- Version, Tier and Features item fields, derived from the pricing model
- A public JSON endpoint, GET /api/pricing/<id>, that needs no login
The model
The pricing model is stored normalised rather than as one document: features, tiers, the matrix cell by cell, the highlight cards, and the version list in the plugin's own configuration. The server assembles those into one shape, and the viewer, the Markdown export and the public endpoint all read that same shape.
| Piece | What it is |
|---|---|
| Feature | A row of the matrix — one capability of the product |
| Tier | A column — one purchasable plan |
| Cell | The value of a feature in a tier, optionally with the version it becomes available from |
| Highlight | A card tile, for the model rendered as cards rather than as a grid |
| Versions | The ordered list of version labels a cell can refer to |
The version reference is what makes this a roadmap rather than a price list: a cell can say a tier gains a feature from a given version, so the matrix states the plan over time rather than only the state today.
Writing to it
Each part of the model has its own endpoint, and a change to one of them touches one row. An update carries the row version it was based on and comes back as a conflict if something else changed it in the meantime.
Replacing the whole model still exists, but only as an explicit seeding operation. That split is the fix for a specific failure: whole-model replacement on every save is what used to drop one person's edit when two people worked on the matrix at once.
The item fields it contributes
| Field | Where its options come from |
|---|---|
| Version | The ordered version list in the plugin's configuration |
| Tier | The tiers in the pricing model |
| Features | The features in the pricing model |
They appear in the item form under the plugin's own heading and behave like any built-in field: group the timeline by tier, filter it to one version, set them from the context menu. None of that is special-cased anywhere in the core, which is the property the plugin exists to demonstrate.
Reading it from outside
GET /api/pricing/<timeline-id> → 200 application/json
No session, no token, no CORS gymnastics — and no second copy of your prices
to keep in sync. A marketing page fetches it at build time and renders it
however it likes. If you would rather have the model as a document,
npm run export:pricing writes a Markdown mirror.
Related
- Running a product roadmap on Zeitlines
- Building a plugin of your own
- The pricing chapter in the documentation
- The generated OpenAPI document
Frequently asked questions
- What does the product-roadmap plugin add?
- A pricing view (the tier × feature matrix), a card view of the same model, and three item fields derived from it: Version, Tier and Features. The assembled model is also readable through a public endpoint that needs no login.
- How is the plugin enabled on a timeline?
- As data: a row saying this timeline carries this plugin, plus a configuration bag that holds the ordered list of version labels. No schema change, no rebuild, no flag. Writing a populated pricing model to a timeline enables it automatically.
- Does a deployment without the plugin download its code?
- No. The plugin is a separate build chunk that imports its own stylesheet, so a build whose timelines do not use it ships neither. A check in the build asserts the pricing markers are absent from the entry chunk and present in a lazy one, so the claim is tested in both directions.
- Can an external pricing page read the model?
- Yes.
GET /api/pricing/<id>returns the assembled model as JSON without any authentication, with the internal row versions stripped. That endpoint is documented in the generated OpenAPI file, and it is marked there as the one operation that carries no security requirement.