HollahoopHollahoop
API reference

Build against your feedback loop.

The Hollahoop API is exposed through a hosted MCP JSON-RPC endpoint. Use one org-scoped token to let Cursor, Claude, Codex, internal scripts, or backend jobs read feedback, move roadmap work, publish Updates, and maintain docs.

Token auth

Send Authorization: Bearer hh_live_… with every request. Tokens are created from your project workspace and scoped to that organisation.

MCP-native

Use the same endpoint from agents and code. Tool schemas are discoverable through tools/list, and execution happens through tools/call.

Read / write scopes

Each tool declares the permission it needs. Revoked tokens stop working immediately, and REST calls include rate-limit headers.

Endpoint

POST JSON-RPC 2.0 requests to the MCP endpoint. Browser GET requests redirect to the public MCP overview, while API clients should send JSON.

Base URL
https://hollahoop.app/mcp
Auth header
Authorization: Bearer hh_live_…
Content type
application/json
curl https://hollahoop.app/mcp \
  -H "Authorization: Bearer hh_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/list",
    "params": {}
  }'

REST mirror

Prefer plain HTTP? The same Bearer token works against the REST endpoints below. Bodies and responses match the MCP tool schemas one-to-one, so you can pick whichever surface fits your stack.

Rate limits are enforced per API key and per client IP (120 read / 30 write requests per minute; Pro orgs with higher_api_limit get 5×). Responses include x-ratelimit-limit, x-ratelimit-remaining, and x-ratelimit-reset; 429 responses also include retry-after. Repeated 429s trigger a short storm cooldown. Operator notes: docs/MCP_SECURITY.md in the repo.

Generating a client? The full machine-readable contract lives at /api/v1/openapi.json (OpenAPI 3.1). Point Swagger UI, Redoc, or your codegen of choice at it.

Prefer a tiny official client? Install @hollahoop/sdk from the repo at packages/js (see README for listPosts, createPost, commentOnPost, listChangelog, publishChangelog, and listStatuses). No runtime dependencies beyond fetch.

GET/api/v1/healthNo auth

Liveness probe used by /status and external monitors.

GET/api/v1/readyNo auth

Readiness probe: checks PostgREST and Redis. 503 when degraded.

GET/api/v1/projectsBearer (read)

List projects under this token's organisation. Mirrors list_projects.

POST/api/v1/projectsBearer (write)

Create a project (boards, statuses, tags auto-seeded). Mirrors create_project.

GET/api/v1/projects/:project/postsBearer (read)

List feedback posts with board/status/sort pagination. Mirrors list_posts.

POST/api/v1/projects/:project/postsBearer (write)

Create a feedback post on a board. Mirrors create_post.

GET/api/v1/projects/:project/posts/:postIdBearer (read)

Fetch a single post with its comments. Mirrors get_post.

PATCH/api/v1/projects/:project/posts/:postIdBearer (write)

Update post status, pin/lock, and/or release estimate. Mirrors update_post_status.

GET/api/v1/projects/:project/posts/:postId/commentsBearer (read)

List comments on a post (limit/offset pagination). Mirrors list_comments.

POST/api/v1/projects/:project/posts/:postId/commentsBearer (write)

Add a comment on a post. Mirrors comment_on_post.

POST/api/v1/projects/:project/posts/:postId/votesBearer (write)

Cast a vote on a post (idempotent). Mirrors add_vote.

GET/api/v1/projects/:project/boardsBearer (read)

List feedback boards for a project. Mirrors list_boards.

GET/api/v1/projects/:project/statusesBearer (read)

List the roadmap status workflow for a project. Mirrors list_statuses.

GET/api/v1/projects/:project/tagsBearer (read)

List feedback tags for a project. Mirrors list_tags.

POST/api/v1/projects/:project/tagsBearer (write)

Create a feedback tag. Mirrors create_tag.

DELETE/api/v1/projects/:project/tags/:tagIdBearer (write)

Delete a feedback tag. Mirrors delete_tag.

PUT/api/v1/projects/:project/posts/:postId/tagsBearer (write)

Replace tags on a post. Mirrors set_post_tags.

GET/api/v1/projects/:project/surveysBearer (read)

List survey campaigns (NPS / CSAT) with response counts and summary scores. Mirrors list_surveys.

POST/api/v1/projects/:project/surveysBearer (write)

Create an NPS or CSAT survey campaign. Mirrors create_survey.

GET/api/v1/projects/:project/surveys/:surveyIdBearer (read)

Fetch one survey with summary and recent responses. Mirrors get_survey.

PATCH/api/v1/projects/:project/surveys/:surveyIdBearer (write)

Activate/deactivate or edit a survey. Mirrors update_survey.

DELETE/api/v1/projects/:project/surveys/:surveyIdBearer (write)

Permanently delete a survey campaign and its responses. Mirrors delete_survey.

GET/api/v1/projects/:project/ticketsBearer (read)

List support inbox tickets (optional status, priority, unassigned, limit). Mirrors list_tickets.

POST/api/v1/projects/:project/ticketsBearer (write)

Create a support ticket with an opening message. Mirrors create_ticket.

GET/api/v1/projects/:project/tickets/:ticketIdBearer (read)

Fetch one support ticket with its messages. Mirrors get_ticket.

PATCH/api/v1/projects/:project/tickets/:ticketIdBearer (write)

Update ticket status, priority, or assignee. Mirrors update_ticket.

POST/api/v1/projects/:project/tickets/:ticketId/repliesBearer (write)

Add a message to a support ticket. Mirrors reply_to_ticket.

GET/api/v1/projects/:project/searchBearer (read)

Full-text search across posts, Updates, and docs. Mirrors search (?q=, optional kind & limit).

GET/api/v1/projects/:project/statusBearer (read)

Fetch the public status page snapshot. Mirrors get_status_page.

GET/api/v1/projects/:project/status/subscribersBearer (read)

List status-page email subscribers (limit/offset). Mirrors list_status_subscribers.

DELETE/api/v1/projects/:project/status/subscribers/:subscriberIdBearer (write)

Remove a status-page email subscriber. Mirrors delete_status_subscriber.

PATCH/api/v1/projects/:project/statusBearer (write)

Enable or disable the public status page. Mirrors set_status_page_enabled.

POST/api/v1/projects/:project/status/incidentsBearer (write)

Open a status-page incident. Mirrors create_status_incident.

POST/api/v1/projects/:project/status/incidents/:incidentId/updatesBearer (write)

Post an incident lifecycle update. Mirrors add_status_incident_update.

POST/api/v1/projects/:project/status/componentsBearer (write)

Add a status-page component. Mirrors create_status_component.

PATCH/api/v1/projects/:project/status/components/:componentIdBearer (write)

Set a component current_status. Mirrors update_status_component.

GET/api/v1/projects/:project/changelogBearer (read)

List Updates/changelog entries, including drafts unless filtered. Mirrors list_changelog.

GET/api/v1/projects/:project/changelog/:entryIdBearer (read)

Fetch one changelog entry by UUID or slug. Mirrors get_changelog.

POST/api/v1/projects/:project/changelogBearer (write)

Publish a changelog entry. Mirrors publish_changelog.

GET/api/v1/projects/:project/docsBearer (read)

List doc pages (public, internal, or all). Mirrors list_docs.

GET/api/v1/projects/:project/docs/:pageIdBearer (read)

Fetch one doc page by UUID or slug (optional locale and version query params). Mirrors get_doc.

PUT/api/v1/projects/:project/docsBearer (write)

Create or update a doc page. Mirrors upsert_doc_page.

GET/api/v1/projects/:project/domainsBearer (read)

List custom domains for a project. Mirrors list_domains.

POST/api/v1/projects/:project/domainsBearer (write)

Add a custom domain (returns DNS verification hints). Mirrors add_domain.

POST/api/v1/projects/:project/domains/:domainId/verifyBearer (write)

Re-check domain DNS verification. Mirrors verify_domain.

POST/api/v1/projects/:project/ciBearer (write)

Record a CI run (GitHub Actions, GitLab, etc.). Mirrors report_ci_status.

Create a project via REST
curl -X POST https://hollahoop.app/api/v1/projects \
  -H "Authorization: Bearer hh_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "slug": "acme-feedback",
    "name": "Acme Feedback",
    "tagline": "What customers want next",
    "is_public": true
  }'
List roadmap posts via REST
curl "https://hollahoop.app/api/v1/projects/hollahoop/posts?status=planned&sort=top&limit=10" \
  -H "Authorization: Bearer hh_live_..."
List published Updates via REST
curl "https://hollahoop.app/api/v1/projects/hollahoop/changelog?status=published&limit=10" \
  -H "Authorization: Bearer hh_live_..."
Search a project via REST
curl "https://hollahoop.app/api/v1/projects/hollahoop/search?q=webhooks&kind=posts&limit=10" \
  -H "Authorization: Bearer hh_live_..."
Report CI status via REST
curl -X POST https://hollahoop.app/api/v1/projects/hollahoop/ci \
  -H "Authorization: Bearer hh_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "provider": "github",
    "repo": "acme/hollahoop",
    "sha": "abc1234567890",
    "status": "success",
    "url": "https://github.com/acme/hollahoop/actions/runs/42"
  }'

Errors

Every non-2xx REST response uses the same JSON envelope, so you can handle failures with one code path. The code is stable and machine-readable; the message is for your logs, not end users.

Error envelope
{
  "error": {
    "code": "not_found",
    "message": "No project 'acme-feedback' in org 'acme'"
  }
}
Rate-limited response (HTTP 429)
HTTP/1.1 429 Too Many Requests
x-ratelimit-limit: 120
x-ratelimit-remaining: 0
x-ratelimit-reset: 1718323200
retry-after: 42

{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests for this API key. Please retry after the reset window."
  }
}
HTTPcodeWhen it happens
400invalid_jsonRequest body was not valid JSON.
401missing_token / invalid_token / revoked_tokenNo usable Bearer token was supplied.
403forbidden_scope / forbiddenThe key lacks the read or write scope this endpoint needs.
404not_foundThe project or resource is not in this organisation.
409slug_takenA project with that slug already exists.
422invalid_input / invalid_slugBody or query parameters failed validation.
429rate_limitedPer-key rate limit exceeded. See retry-after.
500internal_errorUnexpected server error. Safe to retry with backoff.

Tool calls

Call a tool by name with a JSON object that matches its schema. Agents do this automatically; scripts can call the same endpoint directly.

List projects
{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "list_projects",
    "arguments": {}
  }
}
Publish an Update
{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "tools/call",
  "params": {
    "name": "publish_changelog",
    "arguments": {
      "project": "hollahoop",
      "title": "New API docs",
      "body_md": "We added a public API reference.",
      "tag": "announcement"
    }
  }
}

Available tools

45 tools are currently exposed. Read tools can inspect data; write tools can create or mutate feedback, roadmap, Updates, and docs.

See MCP setup details →

Read

list_projects

List every project the API key can see (scoped to a single Hollahoop organisation).

list_boards

List feedback boards on a project (e.g. feature-requests, bugs, general).

list_statuses

List the post-status workflow for a project (Open / Planned / In progress / Shipped / Declined and any custom ones).

list_tags

List feedback tags for a project (slug, label, color).

list_surveys

List survey campaigns on a project (NPS / CSAT), including response_count and summary_score.

get_survey

Get one survey campaign with summary_score, response_count, and recent responses.

list_tickets

List support inbox tickets for a project. Spam is hidden unless status=spam. Most recently active first.

get_ticket

Get one support ticket and its messages (oldest first), scoped to a project.

list_posts

List feedback posts on a project, optionally filtered by board and/or status. Default sort is by vote count.

get_post

Fetch a single post by id, including its comments.

list_comments

List comments on a feedback post, oldest first. Use limit and offset to paginate; get_post also returns comments inline.

list_changelog

List changelog / Updates entries for a project (drafts and published).

get_changelog

Fetch a single changelog / Updates entry by id or slug.

list_docs

List documentation pages for a project, grouped by section. Use the `visibility` filter to scope to public or internal pages.

get_doc

Fetch a single documentation page by id or slug, including Markdown body. When using a slug, scope with locale and version to match list_docs / upsert_doc_page.

search

Free-text search across posts, changelog entries, and docs for a project. Uses PostgreSQL full-text search (ts_rank) when available, with ILIKE fallback. Semantic pgvector search is on the roadmap.

get_status_page

Read the public status page payload for a project (components + recent incidents). Requires the project status page to be enabled. Useful for embedding status in dashboards.

list_status_subscribers

List email subscribers who opted in on the public status page (newest first). Project members only; unsubscribe tokens are never returned.

list_domains

List custom hostnames for a project, including TXT challenge values for pending rows.

Write

create_project

Create a new project under this API key’s organisation. Returns the project plus the URL of its public board. Default boards, statuses, changelog tags, and docs section are auto-seeded.

create_tag

Create a feedback tag on a project.

delete_tag

Delete a feedback tag from a project. Removes the tag from all posts that used it.

set_post_tags

Replace the tags on a feedback post (full set). Pass an empty tag_ids array to clear.

create_survey

Create an NPS or CSAT survey campaign on a project. Returns the campaign id and public response URL.

update_survey

Update a survey campaign (activate/deactivate and/or edit title/question). At least one field required.

delete_survey

Permanently delete a survey campaign and all of its responses from a project.

create_ticket

Create a support ticket with an opening inbound message. Defaults to status=open, priority=normal.

update_ticket

Update a support ticket status, priority, and/or assignee. At least one field required.

reply_to_ticket

Add a message to a support ticket. Outbound replies move new/pending tickets to open.

create_post

Create a new feedback / roadmap post. Defaults to the feature-requests board and the project default status (Open).

update_post_status

Update a post: roadmap status, pin/lock flags, and/or release estimate. Status moves accept feedback (open → planned), start work, or ship.

comment_on_post

Add a comment (or reply, via parent_id) to a post. The author is the user who created this API key.

create_comment

Alias of comment_on_post. Add a comment (or reply, via parent_id) to a post. The author is the user who created this API key.

add_vote

Cast a vote on a post as the API key owner. Idempotent: if the user already voted, returns ok without double-counting. Authors cannot vote on their own posts.

publish_changelog

Create a changelog / Updates entry. Set publish=false to leave it as a draft. Auto-derives a slug from the title if none is given. The optional `tag` becomes the pill on the public Updates feed.

upsert_doc_page

Create or update a documentation page (uniquely identified by project + slug + locale). Body is Markdown. Pass visibility=internal to keep the page hidden from the public site.

delete_status_subscriber

Remove a status-page email subscriber (same effect as the public unsubscribe link). Pending incident emails for that subscriber are dropped via cascade.

set_status_page_enabled

Enable or disable the public status page for a project (same as Settings → Status page).

create_status_incident

Open a status-page incident (starts in investigating). Does not require the public status page to be enabled.

add_status_incident_update

Post an update on a status-page incident and advance its lifecycle (investigating → identified → monitoring → resolved).

create_status_component

Add a component to the project status page.

update_status_component

Set the current operational status of a status-page component (for automation / CI hooks).

report_ci_status

Record a CI run (GitHub Actions, GitLab CI, or generic) against a project. Same payload as POST /api/integrations/ci/webhook.

add_domain

Add a custom hostname to a project. Returns the TXT name and value to publish, then call verify_domain.

verify_domain

Check the TXT challenge for a pending custom hostname and mark it verified when the record matches.