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.
/api/v1/healthNo authLiveness probe used by /status and external monitors.
/api/v1/readyNo authReadiness probe: checks PostgREST and Redis. 503 when degraded.
/api/v1/projectsBearer (read)List projects under this token's organisation. Mirrors list_projects.
/api/v1/projectsBearer (write)Create a project (boards, statuses, tags auto-seeded). Mirrors create_project.
/api/v1/projects/:project/postsBearer (read)List feedback posts with board/status/sort pagination. Mirrors list_posts.
/api/v1/projects/:project/postsBearer (write)Create a feedback post on a board. Mirrors create_post.
/api/v1/projects/:project/posts/:postIdBearer (read)Fetch a single post with its comments. Mirrors get_post.
/api/v1/projects/:project/posts/:postIdBearer (write)Update post status, pin/lock, and/or release estimate. Mirrors update_post_status.
/api/v1/projects/:project/posts/:postId/commentsBearer (read)List comments on a post (limit/offset pagination). Mirrors list_comments.
/api/v1/projects/:project/posts/:postId/commentsBearer (write)Add a comment on a post. Mirrors comment_on_post.
/api/v1/projects/:project/posts/:postId/votesBearer (write)Cast a vote on a post (idempotent). Mirrors add_vote.
/api/v1/projects/:project/boardsBearer (read)List feedback boards for a project. Mirrors list_boards.
/api/v1/projects/:project/statusesBearer (read)List the roadmap status workflow for a project. Mirrors list_statuses.
/api/v1/projects/:project/tagsBearer (read)List feedback tags for a project. Mirrors list_tags.
/api/v1/projects/:project/tagsBearer (write)Create a feedback tag. Mirrors create_tag.
/api/v1/projects/:project/tags/:tagIdBearer (write)Delete a feedback tag. Mirrors delete_tag.
/api/v1/projects/:project/posts/:postId/tagsBearer (write)Replace tags on a post. Mirrors set_post_tags.
/api/v1/projects/:project/surveysBearer (read)List survey campaigns (NPS / CSAT) with response counts and summary scores. Mirrors list_surveys.
/api/v1/projects/:project/surveysBearer (write)Create an NPS or CSAT survey campaign. Mirrors create_survey.
/api/v1/projects/:project/surveys/:surveyIdBearer (read)Fetch one survey with summary and recent responses. Mirrors get_survey.
/api/v1/projects/:project/surveys/:surveyIdBearer (write)Activate/deactivate or edit a survey. Mirrors update_survey.
/api/v1/projects/:project/surveys/:surveyIdBearer (write)Permanently delete a survey campaign and its responses. Mirrors delete_survey.
/api/v1/projects/:project/ticketsBearer (read)List support inbox tickets (optional status, priority, unassigned, limit). Mirrors list_tickets.
/api/v1/projects/:project/ticketsBearer (write)Create a support ticket with an opening message. Mirrors create_ticket.
/api/v1/projects/:project/tickets/:ticketIdBearer (read)Fetch one support ticket with its messages. Mirrors get_ticket.
/api/v1/projects/:project/tickets/:ticketIdBearer (write)Update ticket status, priority, or assignee. Mirrors update_ticket.
/api/v1/projects/:project/tickets/:ticketId/repliesBearer (write)Add a message to a support ticket. Mirrors reply_to_ticket.
/api/v1/projects/:project/searchBearer (read)Full-text search across posts, Updates, and docs. Mirrors search (?q=, optional kind & limit).
/api/v1/projects/:project/statusBearer (read)Fetch the public status page snapshot. Mirrors get_status_page.
/api/v1/projects/:project/status/subscribersBearer (read)List status-page email subscribers (limit/offset). Mirrors list_status_subscribers.
/api/v1/projects/:project/status/subscribers/:subscriberIdBearer (write)Remove a status-page email subscriber. Mirrors delete_status_subscriber.
/api/v1/projects/:project/statusBearer (write)Enable or disable the public status page. Mirrors set_status_page_enabled.
/api/v1/projects/:project/status/incidentsBearer (write)Open a status-page incident. Mirrors create_status_incident.
/api/v1/projects/:project/status/incidents/:incidentId/updatesBearer (write)Post an incident lifecycle update. Mirrors add_status_incident_update.
/api/v1/projects/:project/status/componentsBearer (write)Add a status-page component. Mirrors create_status_component.
/api/v1/projects/:project/status/components/:componentIdBearer (write)Set a component current_status. Mirrors update_status_component.
/api/v1/projects/:project/changelogBearer (read)List Updates/changelog entries, including drafts unless filtered. Mirrors list_changelog.
/api/v1/projects/:project/changelog/:entryIdBearer (read)Fetch one changelog entry by UUID or slug. Mirrors get_changelog.
/api/v1/projects/:project/changelogBearer (write)Publish a changelog entry. Mirrors publish_changelog.
/api/v1/projects/:project/docsBearer (read)List doc pages (public, internal, or all). Mirrors list_docs.
/api/v1/projects/:project/docs/:pageIdBearer (read)Fetch one doc page by UUID or slug (optional locale and version query params). Mirrors get_doc.
/api/v1/projects/:project/docsBearer (write)Create or update a doc page. Mirrors upsert_doc_page.
/api/v1/projects/:project/domainsBearer (read)List custom domains for a project. Mirrors list_domains.
/api/v1/projects/:project/domainsBearer (write)Add a custom domain (returns DNS verification hints). Mirrors add_domain.
/api/v1/projects/:project/domains/:domainId/verifyBearer (write)Re-check domain DNS verification. Mirrors verify_domain.
/api/v1/projects/:project/ciBearer (write)Record a CI run (GitHub Actions, GitLab, etc.). Mirrors report_ci_status.
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
}'curl "https://hollahoop.app/api/v1/projects/hollahoop/posts?status=planned&sort=top&limit=10" \
-H "Authorization: Bearer hh_live_..."curl "https://hollahoop.app/api/v1/projects/hollahoop/changelog?status=published&limit=10" \
-H "Authorization: Bearer hh_live_..."curl "https://hollahoop.app/api/v1/projects/hollahoop/search?q=webhooks&kind=posts&limit=10" \
-H "Authorization: Bearer hh_live_..."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": {
"code": "not_found",
"message": "No project 'acme-feedback' in org 'acme'"
}
}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."
}
}| HTTP | code | When it happens |
|---|---|---|
| 400 | invalid_json | Request body was not valid JSON. |
| 401 | missing_token / invalid_token / revoked_token | No usable Bearer token was supplied. |
| 403 | forbidden_scope / forbidden | The key lacks the read or write scope this endpoint needs. |
| 404 | not_found | The project or resource is not in this organisation. |
| 409 | slug_taken | A project with that slug already exists. |
| 422 | invalid_input / invalid_slug | Body or query parameters failed validation. |
| 429 | rate_limited | Per-key rate limit exceeded. See retry-after. |
| 500 | internal_error | Unexpected 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.
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "list_projects",
"arguments": {}
}
}{
"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.
Read
List every project the API key can see (scoped to a single Hollahoop organisation).
List feedback boards on a project (e.g. feature-requests, bugs, general).
List the post-status workflow for a project (Open / Planned / In progress / Shipped / Declined and any custom ones).
List feedback tags for a project (slug, label, color).
List survey campaigns on a project (NPS / CSAT), including response_count and summary_score.
Get one survey campaign with summary_score, response_count, and recent responses.
List support inbox tickets for a project. Spam is hidden unless status=spam. Most recently active first.
Get one support ticket and its messages (oldest first), scoped to a project.
List feedback posts on a project, optionally filtered by board and/or status. Default sort is by vote count.
Fetch a single post by id, including its comments.
List comments on a feedback post, oldest first. Use limit and offset to paginate; get_post also returns comments inline.
List changelog / Updates entries for a project (drafts and published).
Fetch a single changelog / Updates entry by id or slug.
List documentation pages for a project, grouped by section. Use the `visibility` filter to scope to public or internal pages.
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.
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.
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 email subscribers who opted in on the public status page (newest first). Project members only; unsubscribe tokens are never returned.
List custom hostnames for a project, including TXT challenge values for pending rows.
Write
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 a feedback tag on a project.
Delete a feedback tag from a project. Removes the tag from all posts that used it.
Replace the tags on a feedback post (full set). Pass an empty tag_ids array to clear.
Create an NPS or CSAT survey campaign on a project. Returns the campaign id and public response URL.
Update a survey campaign (activate/deactivate and/or edit title/question). At least one field required.
Permanently delete a survey campaign and all of its responses from a project.
Create a support ticket with an opening inbound message. Defaults to status=open, priority=normal.
Update a support ticket status, priority, and/or assignee. At least one field required.
Add a message to a support ticket. Outbound replies move new/pending tickets to open.
Create a new feedback / roadmap post. Defaults to the feature-requests board and the project default status (Open).
Update a post: roadmap status, pin/lock flags, and/or release estimate. Status moves accept feedback (open → planned), start work, or ship.
Add a comment (or reply, via parent_id) to a post. The author is the user who created this API key.
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.
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.
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.
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.
Remove a status-page email subscriber (same effect as the public unsubscribe link). Pending incident emails for that subscriber are dropped via cascade.
Enable or disable the public status page for a project (same as Settings → Status page).
Open a status-page incident (starts in investigating). Does not require the public status page to be enabled.
Post an update on a status-page incident and advance its lifecycle (investigating → identified → monitoring → resolved).
Add a component to the project status page.
Set the current operational status of a status-page component (for automation / CI hooks).
Record a CI run (GitHub Actions, GitLab CI, or generic) against a project. Same payload as POST /api/integrations/ci/webhook.
Add a custom hostname to a project. Returns the TXT name and value to publish, then call verify_domain.
Check the TXT challenge for a pending custom hostname and mark it verified when the record matches.