What the API does
The Huddlekit REST API lets your own code and tools read a workspace's projects and comments, create comments, change a comment's status, and subscribe to webhooks. It can't edit a comment's text, delete comments, post replies, or set priority, tags, or assignees.
The API is on the Team plan, and only workspace owners and admins can create keys. For an overview, see Webhooks & API. The full reference, with every field and response, is the OpenAPI description at huddlekit.com/openapi.json.
Create an API key
- Open Integrations in your workspace sidebar.
- In the API keys card, click Create key.
- Give the key a Name, such as the tool that will use it.
- Under Access, choose Read only or Read and write.
- Click Create key.
- Copy the key from Save your API key, then click I've saved it.
The key starts with hk_live_. It is shown once and can't be retrieved later, because Huddlekit stores only a hash of it. A key covers every project in the workspace.
Read only keys can list projects and comments. Read and write keys can also create comments, change their status, and manage webhook subscriptions.
To stop a key working, choose Revoke key from its menu. There's no confirmation step: calls with it are refused straight away, and any webhook subscriptions it created stop receiving events. They show Key revoked in the Webhooks card and can only be removed.
Make a request
Send requests to:
https://app.huddlekit.com/api/v1
Pass the key in the Authorization header:
curl https://app.huddlekit.com/api/v1/projects \
-H "Authorization: Bearer hk_live_…"
Request and response bodies are JSON.
Endpoints
| Method | Path | What it does | Scope |
|---|---|---|---|
GET | /me | The key's workspace, scopes, and source value | read |
GET | /projects | Every website, web app, and media project in the workspace | read |
GET | /comments | The newest comments on one project | read |
POST | /comments | Create a comment | write |
PATCH | /comments/{id} | Change a comment's status | write |
GET | /hooks | Webhook subscriptions this key created | read |
POST | /hooks | Subscribe a URL to webhook events | write |
DELETE | /hooks/{id} | Remove a subscription | write |
GET | /events/recent | Sample webhook bodies built from recent comments | read |
Projects and surfaces
Each project from GET /projects has a surface: website, webapp, or document for a media project. Pass the project's id as parent_id, and its surface, when you read or write its comments. surface defaults to website.
Listing comments
GET /comments?parent_id=…&surface=… returns the newest comments first. Add limit to choose how many, a whole number from 1 to 200; the default is 50. There is no paging past the newest 200.
Creating a comment
curl -X POST https://app.huddlekit.com/api/v1/comments \
-H "Authorization: Bearer hk_live_…" \
-H "Content-Type: application/json" \
-d '{
"parent_id": "b6d767d2-f8e3-4c1a-9b2e-5a7c3d1f0e84",
"surface": "website",
"text": "The footer links on the pricing page go nowhere",
"path": "/pricing"
}'
textis required, up to 10,000 characters.statusis optional:open(the default),in-review,in-progress, orresolved.- Where the comment sits depends on the surface:
pathfor a website;url,page_title, andpathfor a web app;page_number, andvideo_timestampin seconds on a video, for a media project. A field that belongs to another surface is refused, exceptpathon a media project, which is ignored.
The comment gets its number like any other, and it appears in Huddlekit from a guest named API. The API has no position to give it, so it's placed at the top-left of the page, and it has no screenshot or browser details.
Changing a status
curl -X PATCH https://app.huddlekit.com/api/v1/comments/COMMENT_ID \
-H "Authorization: Bearer hk_live_…" \
-H "Content-Type: application/json" \
-d '{ "status": "resolved", "surface": "website" }'
status is the only field you can change. Send the comment's surface too, or a web app or media comment returns 404.
Webhook subscriptions
POST /hooks with a targetUrl (https:// only, and not a Huddlekit address) subscribes that URL. Add event_types to receive only some events; leave it out to receive all four. The response includes the signing secret, once.
Subscribing the same URL again with the same key turns it back on with the new event list and doesn't return a new secret. GET /hooks and DELETE /hooks/{id} only see subscriptions made with the key you're using. Deleting one the key can't see, or an id that isn't valid, returns 200 with "deleted": false. They also show in the app's Webhooks card, where you can check their delivery log.
Deliveries are signed and retried the same way as endpoints added in the app. See Setting up webhooks.
Rate limits
Each key can make up to 200 read requests and 30 write requests a minute on each endpoint. Reads and writes are counted separately, so reading comments never uses up the budget for creating them. POST /hooks and DELETE /hooks/{id} share one write limit, and POST /comments and PATCH /comments/{id} have one each. Refused calls count too.
Going over a limit returns 429 with a Retry-After header giving the number of seconds to wait.
Errors
A failed call returns a JSON body with error and sometimes detail:
{ "error": "Unauthorized", "detail": "Invalid or revoked API key" }
| Status | Meaning |
|---|---|
400 | Something in the request is missing or invalid. error says what |
401 | The key is missing, malformed, unknown or revoked |
403 | The key lacks the scope, or the workspace has no active Team subscription (the body lists requiredPlans) |
404 | Not found. An id from another workspace, or one that isn't a valid id at all, also returns 404 (except DELETE /hooks/{id}, which answers 200 with "deleted": false) |
429 | Rate limit reached. Retry-After says how many seconds to wait |
500 | Something failed on our side. Try again |
503 | A temporary problem on our side, such as "Could not verify the API key. Please retry." Try again |
Good to know
- Plan — if the workspace leaves the Team plan, or a payment is past due, every call returns
403exceptDELETE /hooks/{id}, so you can still switch off your subscriptions. - Avoiding loops — subscriptions you make with
POST /hooksalready skip events your own key caused. For webhooks added in the app or with another key, filter onsource: events caused by your key carrysource: "connector:<key id>", andGET /mereturns that value. - Zapier connects with an API key. See Connecting Zapier.
- AI tools like Claude and Cursor connect over MCP instead, with no key. See Connecting AI tools with MCP.