Skip to content

REST API

Patchrooms has a small REST API for reading and updating the data behind a single project — projects, feedback reports, and channels. It is intentionally minimal: most teams pull feedback through the MCP endpoint or the dashboard, and use this API only for custom integrations.

https://room.patchrooms.com/api/v1

Every route lives under a project and is authenticated with a secret API key (prefix pr_sk_) created in the dashboard, sent as a Bearer token bound to that project:

Authorization: Bearer pr_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxx

Each endpoint requires a specific scope (listed below). A missing or revoked key returns 401; a key without the required scope returns 403.

In the paths below, :projectId is the project’s internal id (a MongoDB ObjectId), not the public pr_ project key.

GET /api/v1/projects/:projectId

Scope: project:read. Returns the project’s name, public key, URL patterns, default channel, and whether an identity secret is set.

{
"id": "665e...",
"name": "Acme App",
"projectKey": "pr_xxx",
"urlPatterns": ["https://app.acme.com/*"],
"defaultChannelKey": "bug",
"identitySecretSet": false,
"createdAt": "2026-05-01T12:00:00.000Z"
}
GET /api/v1/projects/:projectId/feedback

Scope: feedback:read. Returns reports newest first with cursor pagination.

Query paramTypeDescription
limitnumberPage size. Defaults to 20, capped at 100.
channelKeystringFilter by channel key.
statusstringFilter by report status.
beforestringCursor — a report id; returns reports older than it.
{
"items": [
{
"id": "665f...",
"channelKey": "bug",
"status": "open",
"blocks": [ /* Block[] */ ],
"context": { /* ... */ },
"identity": null,
"createdAt": "2026-06-03T09:14:22.000Z"
}
],
"nextCursor": "665e..."
}

Pass nextCursor back as the before query param to fetch the next page. nextCursor is null on the last page.

GET /api/v1/projects/:projectId/feedback/:reportId

Scope: feedback:read. Returns one report with its full blocks, context, and identity. Returns 404 if the report is not found in this project.

PATCH /api/v1/projects/:projectId/feedback/:reportId

Scope: feedback:write. Body: { "status": "<status>" }. An invalid status returns 400.

{ "id": "665f...", "status": "resolved" }
GET /api/v1/projects/:projectId/channels

Scope: channel:read. Returns the project’s channels. Pass ?archived=true to include archived channels.

{
"items": [
{
"id": "665d...",
"key": "bug",
"name": "Bug",
"color": "#c0392b",
"mascot": "fox",
"baseWeight": 1,
"archived": false
}
]
}
POST /api/v1/projects/:projectId/channels

Scope: channel:write. Creates a channel from the request body. A duplicate key in the same project returns 409; invalid input returns 400.

PATCH /api/v1/projects/:projectId/channels/:channelKey

Scope: channel:write. Updates the matching channel. Returns 404 if no channel with that key exists in the project.

POST /api/v1/projects/:projectId/hints

Scope: channel:write. Records a short-lived hint that biases channel matching toward a channel (it expires after 5 minutes).

Body fieldTypeDescription
channelKeystringChannel to bias toward. Required.
weight'suggest' | 'max' | numberStrength of the hint. Required.
userIdstringOptional user this hint applies to.
{ "ok": true }

Errors are returned as JSON with an error message and a code:

{ "error": "Not found", "code": "NOT_FOUND" }
HTTPcodeMeaning
400BAD_REQUESTInvalid input.
401UNAUTHORIZEDMissing, invalid, or revoked API key.
403FORBIDDENKey lacks the required scope.
404NOT_FOUNDResource not found in this project.
409CONFLICTChannel key already exists.