Skip to content
VoteFirst Docs
Dashboard

Embed

Headless client

Every route as a promise, for a page that renders its own interface and wants the script only for the data and the identity handling. Nothing is drawn and no stylesheet is needed.

VoteFirst.client is the same object the widgets use, so a page can render its own list and still open a suggestion popup beside it, and both will agree about who the visitor is.

Reading a board without rendering one
VoteFirst.init({ project: 'your-project-slug', key: 'your-embed-key' });

const board = await VoteFirst.client.board();
const page = await VoteFirst.client.features({ sort: 'trending', per_page: 5 });

Reading

The parameters are the API's own, so anything in the reference works here without translation. A list resolves to { items, meta }, where meta carries page, per_page, total, total_pages and has_more. Everything else resolves to the object itself.

MethodWhat it returns
board(options)The project with its boards, columns, display settings and theme. The answer is held for the life of the page, because every widget needs it; pass { refresh: true } to ask again.
features(params)One page of features. Takes board, column, scope, status, tag, q, sort, page and per_page.
feature(id)One feature in full.
comments(featureID, params)One page of comments on a feature.
changelog(params)One page of published releases, most recent first.
release(id)One release with its body and the features in it.

Writing

Every write needs a voter identity, and each of these mints one first if this browser does not already hold one. That is the whole of it: there is nothing to call before the first vote.

MethodWhat it does
vote(featureID), unvote(featureID)Casts and withdraws this visitor's vote. Voting twice rejects with already_exists.
suggest(body)Files a suggestion. The body is { heading, description }.
comment(featureID, body)Adds a comment. The body is { content }, and parent_id makes it a reply.
likeComment(id)Likes a comment, or takes the like back.
deleteComment(id)Removes a comment this visitor left.

Identity

MethodWhat it does
identity()The visitor as the board currently knows them: voter_id, kind, display_name and the voted_feature_ids you need to draw your own vote controls in the right state. Mints an anonymous identity if there is none, and only ever once however many callers ask at the same moment.
signIn(token)Carries your own sign in across. Every widget on the page reloads. A refused token leaves the visitor anonymous rather than stranded, and rejects with identity_invalid.
signOut()Drops the sign in. The anonymous identity this browser had before it is still there, and every widget reloads.
forget()Clears the anonymous identity from this browser as well. Votes already cast stay on the board under the identity that cast them.

The sign in is held in memory rather than stored, so your page calls signIn on every load. How visitors are known is what happens for everybody who never signs in.

The VoteFirst object

MemberWhat it is
versionThe version of the script that is running, as a string.
config()What init settled on: project, apiBase, version, and persistent for whether this browser let the identity be stored at all.
on(name, fn), off(name, fn)Events from every widget at once.
ready(fn)Runs the function with the API as its argument. Once this object exists the script has loaded, so this is a shape rather than a wait.
clientEverything on this page.

persistent is worth reading on a page that offers to remember a visitor. A hardened browser profile refuses local storage outright, and there the identity lives in memory for one page session: the visitor can vote, and their votes will not be theirs again tomorrow.

Running before the script has loaded

The script tag is best placed where your page loads its other scripts, which is usually not before the code that wants to call it. Two things make the order stop mattering.

window.vfq is a queue. Push a function onto it and it runs with the API as its argument, whether the script has arrived yet or not.

Anywhere on the page, in any order
window.vfq = window.vfq || [];
window.vfq.push(function (VoteFirst) {
  VoteFirst.init({ project: 'your-project-slug', key: 'your-embed-key' });
  VoteFirst.roadmap('#roadmap');
});

And the document is given a votefirst:ready event the moment the script has finished loading, carrying the API as its detail, for code that would rather listen than queue.

document.addEventListener('votefirst:ready', function (event) {
  event.detail.init({ project: 'your-project-slug', key: 'your-embed-key' });
});

Reach for the queue. It behaves the same whether the script has arrived or not, and it leaves no listener to unbind later.