# VoteShip — Installation Guide

> VoteShip is a feature request management platform with voting boards, public roadmaps, changelogs, and AI insights. This guide walks you through complete setup — from account creation to widget installation, MCP server setup, and API usage.

## Quick Start

3-step summary: signup, get keys, add script tag.

1. Create an account at `https://app.voteship.app/signup`
2. Copy your public key from `https://app.voteship.app/dashboard/share`
3. Add the widget script tag before `</body>` in your HTML

## Step 1: Create an Account

Navigate to `https://app.voteship.app/signup`. The signup form has:

- **Name** — text input
- **Email** — text input
- **Password** — text input (minimum 8 characters)
- **"Create Account"** — submit button

After signup you will be redirected to `/setup` where you enter a workspace name. This creates your first project and redirects you to the dashboard.

If you already have an account, sign in at `https://app.voteship.app/login` instead.

## Step 2: Get Your API Keys

Navigate to `https://app.voteship.app/dashboard/share`. In the "API Keys" section you will find:

- **Public Key** (starts with `pk_`) — safe for client-side use, required for the widget script
- **Secret Key** (starts with `sk_`) — server-side only, used for the REST API and MCP server

Copy both keys and store them securely. The public key goes into your widget script tag. The secret key is used for server-to-server API calls and must never be exposed in client-side code.

## Step 3: Install the Widget

### Floating Feedback Button (Recommended)

Add this before `</body>`:

```html
<script
  src="https://app.voteship.app/widget/widget.js"
  data-project="YOUR_PUBLIC_KEY"
  data-position="bottom-right"
  defer
></script>
```

When `data-container` is not set, the widget automatically renders as a floating feedback button. Available positions: `bottom-right`, `bottom-left`, `top-right`, `top-left`.

### Inline Voting Board

When `data-container` is set, the widget renders as an inline voting board inside the specified element:

```html
<div id="voteship-board"></div>
<script
  src="https://app.voteship.app/widget/widget.js"
  data-project="YOUR_PUBLIC_KEY"
  data-container="voteship-board"
  defer
></script>
```

### Inline Roadmap

```html
<div id="voteship-roadmap"></div>
<script
  src="https://app.voteship.app/widget/widget.js"
  data-project="YOUR_PUBLIC_KEY"
  data-container="voteship-roadmap"
  defer
></script>
<script>
  window.addEventListener('voteship:ready', () => {
    window.VoteShip.loadRoadmap('voteship-roadmap');
  });
</script>
```

### Inline Changelog

```html
<div id="voteship-changelog"></div>
<script
  src="https://app.voteship.app/widget/widget.js"
  data-project="YOUR_PUBLIC_KEY"
  data-container="voteship-changelog"
  defer
></script>
<script>
  window.addEventListener('voteship:ready', () => {
    window.VoteShip.loadChangelog('voteship-changelog');
  });
</script>
```

## Framework-Specific Installation

### React / Next.js

```tsx
// In your layout or app component
'use client';
import { useEffect } from 'react';

export function FeedbackWidget() {
  useEffect(() => {
    const script = document.createElement('script');
    script.src = 'https://app.voteship.app/widget/widget.js';
    script.defer = true;
    script.setAttribute('data-project', 'YOUR_PUBLIC_KEY');
    script.setAttribute('data-position', 'bottom-right');
    document.body.appendChild(script);
    return () => { script.remove(); };
  }, []);
  return null;
}
```

Then render `<FeedbackWidget />` in your root layout or any page.

### Vue / Nuxt

```vue
<script setup>
import { onMounted, onUnmounted } from 'vue';

let script;
onMounted(() => {
  script = document.createElement('script');
  script.src = 'https://app.voteship.app/widget/widget.js';
  script.defer = true;
  script.setAttribute('data-project', 'YOUR_PUBLIC_KEY');
  script.setAttribute('data-position', 'bottom-right');
  document.body.appendChild(script);
});
onUnmounted(() => script?.remove());
</script>
```

### Svelte / SvelteKit

```svelte
<script>
  import { onMount, onDestroy } from 'svelte';
  let script;
  onMount(() => {
    script = document.createElement('script');
    script.src = 'https://app.voteship.app/widget/widget.js';
    script.defer = true;
    script.setAttribute('data-project', 'YOUR_PUBLIC_KEY');
    script.setAttribute('data-position', 'bottom-right');
    document.body.appendChild(script);
  });
  onDestroy(() => script?.remove());
</script>
```

### Angular

Add to `src/index.html` before `</body>`:

```html
<script
  src="https://app.voteship.app/widget/widget.js"
  data-project="YOUR_PUBLIC_KEY"
  data-position="bottom-right"
  defer
></script>
```

### Plain HTML

Add before `</body>`:

```html
<script
  src="https://app.voteship.app/widget/widget.js"
  data-project="YOUR_PUBLIC_KEY"
  data-position="bottom-right"
  defer
></script>
```

## Step 4: Identify Users (Optional)

If your app has authenticated users, identify them so votes are tied to real accounts instead of anonymous fingerprints.

### Option A: JavaScript API

```js
window.VoteShip.identify({
  id: 'user_123',             // Your app's user ID (required)
  email: 'user@example.com',  // Optional
  name: 'Jane Doe',           // Optional
  userSpend: 99.99,           // Optional — monthly revenue for prioritization
});
```

Call `window.VoteShip.reset()` on logout to clear the identified user.

### Option B: Declarative (data attributes)

Pass user identity directly on the script tag for automatic identification on load:

```html
<script
  src="https://app.voteship.app/widget/widget.js"
  data-project="YOUR_PUBLIC_KEY"
  data-position="bottom-right"
  data-user-id="user_123"
  data-user-email="user@example.com"
  data-user-name="Jane Doe"
  defer
></script>
```

## Step 5: MCP Server for AI Agents (Optional)

VoteShip has an official MCP server that lets AI agents (Claude, Cursor, Windsurf, etc.) manage feature requests, votes, roadmaps, and changelogs directly. **No API key is required** — public tools work out of the box.

### Public Mode (No API Key)

Any agent can submit feedback, browse boards, vote, and comment without credentials:

```json
{
  "mcpServers": {
    "voteship": {
      "command": "npx",
      "args": ["-y", "@voteship/mcp-server"],
      "env": {
        "VOTESHIP_PROJECT_SLUG": "your-project-slug"
      }
    }
  }
}
```

This provides **4 public tools**: `submit_feature_request`, `browse_board`, `upvote_post`, `add_public_comment`, plus a feedback prompt.

### Full Mode (With API Key)

For admin access, add your secret key:

```json
{
  "mcpServers": {
    "voteship": {
      "command": "npx",
      "args": ["-y", "@voteship/mcp-server"],
      "env": {
        "VOTESHIP_API_KEY": "YOUR_SECRET_KEY"
      }
    }
  }
}
```

### What the MCP server provides

- **28 tools** (24 admin + 4 public) — create/update/delete posts, vote, comment, manage tags, users, releases, roadmap, AI triage, sprint planning, webhooks, Stripe revenue sync, and more
- **5 resources** — project config, roadmap state, changelog, analytics summary, and full board state
- **5 prompts** — triage inbox, plan sprint, draft changelog, summarize feedback, submit feedback

### Example agent workflows

- "Submit a feature request to the acme-app board: Add dark mode support"
- "Triage all pending feature requests and categorize them"
- "What are the top 10 most-voted features?"
- "Draft a changelog from all features completed this month"
- "Create a sprint plan from the highest-priority requests"

npm package: [`@voteship/mcp-server`](https://www.npmjs.com/package/@voteship/mcp-server)

## Step 6: Configure Integrations via API (Optional)

Use the REST API with your secret key to configure integrations programmatically.

- **Base URL:** `https://app.voteship.app/api/v1`
- **Auth header:** `Authorization: Bearer YOUR_SECRET_KEY`

### Slack — Send notifications to a channel

```bash
curl -X PUT https://app.voteship.app/api/v1/integrations/slack \
  -H "Authorization: Bearer YOUR_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{"webhookUrl": "https://hooks.slack.com/services/...", "enabled": true}'
```

### Discord — Send notifications to a channel

```bash
curl -X PUT https://app.voteship.app/api/v1/integrations/discord \
  -H "Authorization: Bearer YOUR_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{"webhookUrl": "https://discord.com/api/webhooks/...", "enabled": true}'
```

### GitHub — Auto-create issues from feedback

```bash
curl -X PUT https://app.voteship.app/api/v1/integrations/github \
  -H "Authorization: Bearer YOUR_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{"repoOwner": "your-org", "repoName": "your-repo", "accessToken": "ghp_...", "enabled": true, "autoCreateIssues": true, "syncLabels": false}'
```

### Linear — Auto-create issues from feedback

```bash
curl -X PUT https://app.voteship.app/api/v1/integrations/linear \
  -H "Authorization: Bearer YOUR_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{"apiKey": "lin_api_...", "teamId": "TEAM_ID", "enabled": true, "autoCreateIssues": true}'
```

### Webhooks — Get notified of all events

```bash
curl -X POST https://app.voteship.app/api/v1/integrations/webhooks \
  -H "Authorization: Bearer YOUR_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://your-site.com/webhooks/voteship", "events": ["post.created", "vote.created", "comment.created", "post.status_changed"]}'
```

## Step 7: Verify Installation

### Option A: Visual verification

Visit your site. A feedback button should appear in the bottom-right corner (or whichever position you configured). Click it to open the feedback form and confirm it loads correctly.

### Option B: API verification

```bash
curl https://app.voteship.app/api/public/verify?key=YOUR_PUBLIC_KEY
```

Returns `{"valid": true, "projectName": "...", "slug": "..."}` if the key is valid.

## Public URLs

After setup, your project has these public pages (replace `YOUR_SLUG` with your project's slug):

- **Voting board:** `https://YOUR_SLUG.voteship.app/board`
- **Roadmap:** `https://YOUR_SLUG.voteship.app/roadmap`
- **Changelog:** `https://YOUR_SLUG.voteship.app/releases`

## JavaScript API Reference

The widget exposes a global `window.VoteShip` object with these methods:

| Method | Description |
|--------|-------------|
| `init(config?)` | Initialize the widget (auto-called when `data-project` is set) |
| `identify({ id, email?, name?, userSpend? })` | Associate the visitor with a known user |
| `reset()` | Clear identified user and return to anonymous mode |
| `openPopup()` | Programmatically open the floating popup |
| `closePopup()` | Programmatically close the floating popup |
| `loadVotingBoard(containerId?)` | Mount an inline voting board |
| `loadRoadmap(containerId?)` | Mount an inline roadmap |
| `loadChangelog(containerId?)` | Mount an inline changelog |
| `loadFeedbackPopup()` | Mount/remount the floating feedback popup |

### Events

| Event | Description |
|-------|-------------|
| `voteship:ready` | Fired on `window` after the widget initializes |
| `voteship:open` | Listened by popup to open programmatically |
| `voteship:close` | Listened by popup to close programmatically |

## REST API Reference

Full REST API documentation: `https://app.voteship.app/docs`

All API requests require the `Authorization: Bearer YOUR_SECRET_KEY` header.

### Posts

| Method | Endpoint | Description |
|--------|----------|-------------|
| `GET` | `/api/v1/posts` | List feature requests (paginated) |
| `POST` | `/api/v1/posts` | Create a feature request |
| `GET` | `/api/v1/posts/:id` | Get a single post |
| `PATCH` | `/api/v1/posts/:id` | Update a post |
| `DELETE` | `/api/v1/posts/:id` | Delete a post |
| `GET` | `/api/v1/posts/:id/votes` | List votes on a post |
| `POST` | `/api/v1/posts/:id/votes` | Add a vote |
| `GET` | `/api/v1/posts/:id/comments` | List comments on a post |
| `POST` | `/api/v1/posts/:id/comments` | Add a comment |
| `POST` | `/api/v1/posts/from-text` | Extract feature request from raw text (AI) |
| `GET` | `/api/v1/posts/similar` | Find semantically similar posts (AI) |

### Tags

| Method | Endpoint | Description |
|--------|----------|-------------|
| `GET` | `/api/v1/tags` | List tags |
| `POST` | `/api/v1/tags` | Create a tag |
| `PATCH` | `/api/v1/tags/:id` | Update a tag |
| `DELETE` | `/api/v1/tags/:id` | Delete a tag |

### Releases (Changelog)

| Method | Endpoint | Description |
|--------|----------|-------------|
| `GET` | `/api/v1/releases` | List releases |
| `POST` | `/api/v1/releases` | Create a release |
| `GET` | `/api/v1/releases/:id` | Get a single release |
| `PATCH` | `/api/v1/releases/:id` | Update a release |
| `DELETE` | `/api/v1/releases/:id` | Delete a release |

### Other

| Method | Endpoint | Description |
|--------|----------|-------------|
| `GET` | `/api/v1/users` | List board users |
| `GET` | `/api/v1/activity` | Activity log |
| `GET` | `/api/v1/roadmap` | Roadmap with posts by status |
| `GET` | `/api/v1/analytics` | Analytics summary |
| `POST` | `/api/v1/import` | CSV import |
| `POST` | `/api/v1/import/nolt` | Import from Nolt |
| `POST` | `/api/v1/import/uservoice` | Import from UserVoice |

### AI

| Method | Endpoint | Description |
|--------|----------|-------------|
| `POST` | `/api/v1/ai/triage` | Auto-triage pending posts |
| `POST` | `/api/v1/ai/summary` | Summarize feedback trends |
| `POST` | `/api/v1/ai/sprint-plan` | Generate a sprint plan from top posts |

### Integrations

| Method | Endpoint | Description |
|--------|----------|-------------|
| `GET/PUT/DELETE` | `/api/v1/integrations/slack` | Slack webhook |
| `GET/PUT/DELETE` | `/api/v1/integrations/discord` | Discord webhook |
| `GET/PUT/DELETE` | `/api/v1/integrations/github` | GitHub issue sync |
| `GET/PUT/DELETE` | `/api/v1/integrations/linear` | Linear issue sync |
| `GET/POST` | `/api/v1/integrations/webhooks` | Webhook endpoints |
| `GET/PATCH/DELETE` | `/api/v1/integrations/webhooks/:id` | Manage a webhook |

## Configuration Options

| Attribute | Values | Default | Description |
|-----------|--------|---------|-------------|
| `data-project` | Your public key | (required) | Project identifier |
| `data-position` | `bottom-right`, `bottom-left`, `top-right`, `top-left` | `bottom-right` | Button position (floating mode) |
| `data-container` | Element ID | — | Target element ID; when set, renders inline instead of floating |
| `data-theme` | `auto`, `light`, `dark` | `auto` | Color theme (`auto` detects from host page and system preference) |
| `data-api-base` | URL | Script origin | Custom API base URL (advanced) |
| `data-locale` | Locale string | — | Locale hint for i18n (e.g., `en`, `fr`) |
| `data-user-id` | User ID string | — | Auto-identify user on load |
| `data-user-email` | Email string | — | User email for auto-identification |
| `data-user-name` | Name string | — | User display name for auto-identification |

## Important Notes

- No npm packages needed — the widget is a single script tag.
- The script loads asynchronously with `defer` and will not block your page.
- The floating popup uses `all: initial` CSS reset to prevent host page styles from leaking in. Inline components render directly into your container.
- All data is accessible via the REST API with your secret key.
- AI agents can manage everything via the MCP server (`@voteship/mcp-server`). No API key needed for public tools.
- Free tier includes 1 project, unlimited feedback, and a public board plus roadmap.
