API overview
Sign-in and tokens, pagination, retries, edit conflicts and rate limits.
Base URL
https://commentkit.dev/api/v1The widget, the dashboard, the MCP server and the CLI all use this API; there is no more powerful private API. Every operation is listed in the API reference. Requests and responses are JSON.
Authentication
There are three kinds of callers. The project key (pk_live_…) only identifies a project; it never authorises anything.
- Widget session (browser). The widget signs people in on the CommentKit domain, in a popup or a same-tab redirect, with PKCE.
POST /oauth/tokenreturns a 15-minute access token and a refresh token that rotates on every use; replaying an old one ends the whole session. The token is bound to one project and one website origin, and only works from that website. Send it asAuthorization: Bearer …. - Agent token (server side only). A project token for the CLI, CI and the local MCP server, created in Project → Connect or with
commentkit token create. It reads by default; thereplyandstatuspermissions are opt-in. Tokens expire after 30 days (commentkit token create --daysallows up to 90). Requests that carry a browserOriginare refused withbrowser_not_allowed. Agent endpoints live under/agent/projects/{project}. - OAuth for MCP. The hosted MCP server at
https://commentkit.dev/mcpuses OAuth 2.1: discovery at/.well-known/oauth-protected-resource/mcpand/.well-known/oauth-authorization-server, dynamic client registration, the authorization code flow with PKCE (S256), and a device flow (used bycommentkit login). The person chooses the project and the permissions (feedback:read,feedback:reply,feedback:status) on a consent screen. Access tokens last an hour and refresh tokens rotate for 30 days. Most MCP apps do all of this for you; see MCP.
Pagination
Lists use cursors. Pass limit (1 to 100) and, for the next page, the next_cursor from the previous response as cursor. When next_cursor is null, you have everything.
GET /api/v1/threads?limit=50
GET /api/v1/threads?limit=50&cursor=<next_cursor>Safe retries
Every write (POST), such as creating feedback, replying, uploading and every agent write, accepts an Idempotency-Key header: any unique string of 8 to 100 characters from A-Z a-z 0-9 _ - : .. If the connection drops, send the same request with the same key again. For 24 hours you get the first answer back, with Idempotent-Replayed: true, and nothing happens twice.
- Using the same key for a different request is refused with
422and the codeidempotency_key_reused. Use a new key for every new action. - Retrying while the first request is still running answers
409(idempotency_in_progress). Wait forRetry-Afterand try again. - Only successful answers are kept, so a request that failed can be retried with the same key.
Edit conflicts and caching
Reading or changing a thread returns an ETag, and every message carries an etag. Send it back as If-Match on PATCH /threads/{thread} or PATCH /messages/{message}. If someone changed it in the meantime, the change is refused with 412 and the code edit_conflict: fetch it again and reapply your change.
Lists (GET /threads and GET /threads/{thread}/messages) also send an ETag. Send it as If-None-Match and you get an empty 304 when nothing changed.
Rate limits
- Reading: 600 requests a minute.
- Writing: 60 requests a minute and 2,000 a day.
- Signing in (
/oauth/token,/oauth/revoke): 10 a minute and 60 an hour per IP address.
Limits count per signed-in person, or per IP address before sign-in. Responses carry RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset. Over the limit you get 429 with Retry-After (seconds) and the code rate_limited.
Errors
Errors are RFC 9457 problems (application/problem+json) with a stable code and a plain-language detail. See error codes for every code.
Changes
Changes within v1 are additive only: new fields and endpoints may appear at any time, so ignore fields you do not know. Breaking changes need a new major version, with 12 months’ notice. See versions and changes.