Skip to content

Reports API ​

Reports are feedback items submitted by users or external systems. Reports can be promoted to issues, dismissed, or reordered on the board. External systems use project tokens to submit reports via the API.

Requires the Reports feature

Report creation is only available when the Reports feature is enabled for your workspace. If it is disabled, requests return 403 with "feature_restricted": true.

Submitting reports from an external system?

The Sending Reports guide walks through minting a token, posting a report, and triaging it on the board.

Permissions ​

ActionRequired PermissionScope
ListReports: ReadReports within accessible projects
CreateReports: CreateWithin accessible projects (or via project token)
PromoteReports: UpdateReports within accessible projects
DismissReports: UpdateReports within accessible projects
ReorderReports: UpdateReports within accessible projects
DeleteReports: DeleteReports within accessible projects
Upload attachmentAttachments: Create + (Reports: Read, the report's own creator, or a project token linked to the project)Attachments on reports within accessible projects

Admins and project owners bypass all permission checks for project-scoped resources.

Endpoints ​

MethodEndpointDescription
GET/api/projects/{projectId}/reportsList reports
GET/api/projects/{projectId}/reports/{reportId}Get a single report
POST/api/projects/{projectId}/reportsCreate a report
POST/api/projects/{projectId}/reports/promotePromote reports to an issue
POST/api/projects/{projectId}/reports/{reportId}/dismissDismiss a report
POST/api/projects/{projectId}/reports/reorderReorder reports
DELETE/api/projects/{projectId}/reports/{reportId}Delete a report
GET/api/projects/{projectId}/reports/{reportId}/attachmentsList attachments for a report
POST/api/projects/{projectId}/reports/{reportId}/attachmentsUpload an attachment to a report
GET/api/projects/{projectId}/reports/{reportId}/attachments/{attachmentId}/downloadDownload a report attachment

List Reports ​

GET /api/projects/{projectId}/reports

Returns all reports for a project in triage-queue order. Each report carries a rank: an opaque string that sorts the queue (ascending, ties broken by id). Compare ranks as plain strings and never build one yourself.

bash
curl https://{tenant}.kendo.dev/api/projects/1/reports \
  -H "Authorization: Bearer your-token"
json
[
  {
    "id": 1,
    "title": "Login button not working on mobile",
    "description": "The login button does not respond to taps on iOS Safari.",
    "source": 1,
    "author_name": "External CI",
    "project_id": 1,
    "created_by": null,
    "promoted_issue_id": null,
    "promoted_at": null,
    "dismissed_at": null,
    "dismiss_reason": null,
    "dismiss_reason_note": null,
    "rank": "i",
    "attachments_count": 0,
    "created_at": "2026-03-20T10:00:00.000000Z",
    "updated_at": "2026-03-20T10:00:00.000000Z"
  },
  {
    "id": 2,
    "title": "Typo in onboarding flow",
    "description": "Step 3 says 'conifgure' instead of 'configure'.",
    "source": 0,
    "author_name": null,
    "project_id": 1,
    "created_by": 4,
    "promoted_issue_id": null,
    "promoted_at": null,
    "dismissed_at": null,
    "dismiss_reason": null,
    "dismiss_reason_note": null,
    "rank": "q",
    "attachments_count": 0,
    "created_at": "2026-03-21T08:30:00.000000Z",
    "updated_at": "2026-03-21T08:30:00.000000Z"
  }
]

Get Report ​

GET /api/projects/{projectId}/reports/{reportId}

Returns a single report. Prefer this over filtering the list when you already know the id — a mature project holds hundreds of reports, each carrying its full description.

The report must belong to the project in the path; a report id from another project returns 404.

bash
curl https://{tenant}.kendo.dev/api/projects/1/reports/1 \
  -H "Authorization: Bearer your-token"
json
{
  "id": 1,
  "title": "Login button not working on mobile",
  "description": "The login button does not respond to taps on iOS Safari.",
  "source": 1,
  "author_name": "External CI",
  "project_id": 1,
  "created_by": null,
  "promoted_issue_id": null,
  "promoted_at": null,
  "dismissed_at": null,
  "dismiss_reason": null,
  "dismiss_reason_note": null,
  "rank": "i",
  "attachments_count": 2,
  "created_at": "2026-03-20T10:00:00.000000Z",
  "updated_at": "2026-03-20T10:00:00.000000Z"
}

The payload carries attachments_count, not the attachments themselves — fetch those from List Attachments when you need them.

Create Report ​

POST /api/projects/{projectId}/reports

Creates a new report. For external submission over the API, authenticate with a Read & write project token. Reports created this way are tagged source: 1 (Api) with created_by: null, and the token may attach files to them.

Reports filed by a signed-in user inside the kendo app, or with a personal access token, are tagged source: 0 (Manual), with created_by set to that user.

Request Fields ​

FieldTypeRequiredDescription
titlestringYesReport title, max 255 characters
descriptionstringYesReport description, max 65,535 characters
author_namestringNoName of the external reporter
filesfile[]NoUp to 5 image attachments (jpg, jpeg, png, gif, webp, bmp, tiff; max 3 MB each)
bash
curl -X POST https://{tenant}.kendo.dev/api/projects/1/reports \
  -H "Authorization: Bearer your-project-token" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Login button not working on mobile",
    "description": "The login button does not respond to taps on iOS Safari.",
    "author_name": "External CI"
  }'
json
{
  "id": 1,
  "title": "Login button not working on mobile",
  "description": "The login button does not respond to taps on iOS Safari.",
  "source": 1,
  "author_name": "External CI",
  "project_id": 1,
  "created_by": null,
  "promoted_issue_id": null,
  "promoted_at": null,
  "dismissed_at": null,
  "dismiss_reason": null,
  "dismiss_reason_note": null,
  "rank": "i",
  "attachments_count": 0,
  "created_at": "2026-03-20T10:00:00.000000Z",
  "updated_at": "2026-03-20T10:00:00.000000Z"
}

With File Attachments ​

When uploading files, use multipart/form-data instead of JSON:

bash
curl -X POST https://{tenant}.kendo.dev/api/projects/1/reports \
  -H "Authorization: Bearer your-token" \
  -F "title=Screenshot of the bug" \
  -F "description=See attached screenshot for details." \
  -F "author_name=External CI" \
  -F "files[0][email protected]" \
  -F "files[1][email protected]"

The response includes attachments_count reflecting the number of files stored with the report.

Promote Reports ​

POST /api/projects/{projectId}/reports/promote

Promotes one or more reports into a single issue. The reports are marked as promoted and linked to the newly created issue. Returns the updated reports.

Request Fields ​

FieldTypeRequiredDescription
report_idsint[]YesIDs of reports to promote (min 1)
titlestringYesIssue title, max 255 characters
descriptionstringYesIssue description, max 65,535 characters
lane_idintNoTarget lane for the new issue
priorityintNoPriority: 0=Highest, 1=High, 2=Medium, 3=Low, 4=Lowest
typeintNoType: 0=Feature, 1=Bug
assignee_idintNoUser ID to assign the issue to
sprint_idintNoSprint ID to add the issue to
epic_idintNoEpic ID to link the issue to
estimated_minutesintNoTime estimate in minutes
blocked_by_idsint[]NoIDs of issues that block this issue
blocks_idsint[]NoIDs of issues that this issue blocks
label_idsint[]NoLabel IDs to attach to the created issue (must belong to the project)
bash
curl -X POST https://{tenant}.kendo.dev/api/projects/1/reports/promote \
  -H "Authorization: Bearer your-token" \
  -H "Content-Type: application/json" \
  -d '{
    "report_ids": [1, 2],
    "title": "Mobile login and onboarding fixes",
    "description": "Combined report: login button broken on iOS + typo in onboarding.",
    "priority": 1,
    "type": 1
  }'
json
[
  {
    "id": 1,
    "title": "Login button not working on mobile",
    "description": "The login button does not respond to taps on iOS Safari.",
    "source": 1,
    "author_name": "External CI",
    "project_id": 1,
    "created_by": null,
    "promoted_issue_id": 42,
    "promoted_at": "2026-03-22T09:00:00.000000Z",
    "dismissed_at": null,
    "dismiss_reason": null,
    "dismiss_reason_note": null,
    "rank": "i",
    "attachments_count": 0,
    "created_at": "2026-03-20T10:00:00.000000Z",
    "updated_at": "2026-03-22T09:00:00.000000Z"
  },
  {
    "id": 2,
    "title": "Typo in onboarding flow",
    "description": "Step 3 says 'conifgure' instead of 'configure'.",
    "source": 0,
    "author_name": null,
    "project_id": 1,
    "created_by": 4,
    "promoted_issue_id": 42,
    "promoted_at": "2026-03-22T09:00:00.000000Z",
    "dismissed_at": null,
    "dismiss_reason": null,
    "dismiss_reason_note": null,
    "rank": "q",
    "attachments_count": 0,
    "created_at": "2026-03-21T08:30:00.000000Z",
    "updated_at": "2026-03-22T09:00:00.000000Z"
  }
]

Dismiss Report ​

POST /api/projects/{projectId}/reports/{reportId}/dismiss

Marks a report as dismissed, recording a reason category and an optional free-text note. Dismissed reports are not deleted but are excluded from the active reports view. Dismissing an already-dismissed or already-promoted report returns a 422 and leaves the recorded reason unchanged — a second dismiss call never overwrites the first, even when it sends the same category.

Request Fields ​

FieldTypeRequiredDescription
categorystringYesOne of the Dismiss Reason values
notestringNoFree-text note, max 10,000 characters
bash
curl -X POST https://{tenant}.kendo.dev/api/projects/1/reports/1/dismiss \
  -H "Authorization: Bearer your-token" \
  -H "Content-Type: application/json" \
  -d '{
    "category": "not-planned",
    "note": "Not a fit for the roadmap this quarter."
  }'
json
{
  "id": 1,
  "title": "Login button not working on mobile",
  "description": "The login button does not respond to taps on iOS Safari.",
  "source": 1,
  "author_name": "External CI",
  "project_id": 1,
  "created_by": null,
  "promoted_issue_id": null,
  "promoted_at": null,
  "dismissed_at": "2026-03-22T10:00:00.000000Z",
  "dismiss_reason": "not-planned",
  "dismiss_reason_note": "Not a fit for the roadmap this quarter.",
  "rank": "i",
  "attachments_count": 0,
  "created_at": "2026-03-20T10:00:00.000000Z",
  "updated_at": "2026-03-22T10:00:00.000000Z"
}

Reports dismissed before this field existed

dismiss_reason and dismiss_reason_note are null on reports that were dismissed before this field was introduced.

Reorder Reports ​

POST /api/projects/{projectId}/reports/reorder

Moves one report to a new position in the triage queue, between two neighbouring reports. Only the moved report's rank changes. Returns every report in the project in its new order.

Request Fields ​

FieldTypeRequiredDescription
report_idintYesThe report to move
rank_before_idint | nullNoThe report that should come directly before it. Omit or send null to move it to the top
rank_after_idint | nullNoThe report that should come directly after it. Omit or send null to move it to the bottom

All three ids must belong to the project. If the moved report or a named neighbour is deleted before the move runs, the request returns 404; fetch the list again and retry.

bash
curl -X POST https://{tenant}.kendo.dev/api/projects/1/reports/reorder \
  -H "Authorization: Bearer your-token" \
  -H "Content-Type: application/json" \
  -d '{
    "report_id": 2,
    "rank_before_id": null,
    "rank_after_id": 1
  }'
json
[
  {
    "id": 2,
    "title": "Typo in onboarding flow",
    "description": "Step 3 says 'conifgure' instead of 'configure'.",
    "source": 0,
    "author_name": null,
    "project_id": 1,
    "created_by": 4,
    "promoted_issue_id": null,
    "promoted_at": null,
    "dismissed_at": null,
    "dismiss_reason": null,
    "dismiss_reason_note": null,
    "rank": "d",
    "attachments_count": 0,
    "created_at": "2026-03-21T08:30:00.000000Z",
    "updated_at": "2026-03-22T11:00:00.000000Z"
  },
  {
    "id": 1,
    "title": "Login button not working on mobile",
    "description": "The login button does not respond to taps on iOS Safari.",
    "source": 1,
    "author_name": "External CI",
    "project_id": 1,
    "created_by": null,
    "promoted_issue_id": null,
    "promoted_at": null,
    "dismissed_at": null,
    "dismiss_reason": null,
    "dismiss_reason_note": null,
    "rank": "i",
    "attachments_count": 0,
    "created_at": "2026-03-20T10:00:00.000000Z",
    "updated_at": "2026-03-20T10:00:00.000000Z"
  }
]

Delete Report ​

DELETE /api/projects/{projectId}/reports/{reportId}

Returns 204 No Content on success.

bash
curl -X DELETE https://{tenant}.kendo.dev/api/projects/1/reports/1 \
  -H "Authorization: Bearer your-token"

Attachments ​

Reports support file attachments (images, documents, and other allowed file types). Use these endpoints to manage files after a report has been created — for uploading files at report-creation time, include them in the Create Report files[] field instead.

Upload Attachment ​

POST /api/projects/{projectId}/reports/{reportId}/attachments

Uploads a single file and links it to the report as a polymorphic attachment. Requires multipart/form-data with a file form field.

FieldTypeRequiredDescription
filefileYesMax 20 MB. Allowed: pdf, doc(x), xls(x), ppt(x), odt/ods/odp, txt, csv, json, xml, md, jpg/jpeg, png, gif, webp, bmp, tiff, mp4, mp3, wav, ogg

Images are automatically converted to WebP server-side for efficient storage; the returned filename reflects the stored file name.

bash
curl -X POST https://{tenant}.kendo.dev/api/projects/1/reports/1/attachments \
  -H "Authorization: Bearer your-token" \
  -F "[email protected]"
json
{
  "id": 42,
  "filename": "screenshot.webp",
  "mime_type": "image/webp",
  "size": 28417,
  "user_id": 4,
  "created_at": "2026-04-22T09:15:00.000000Z"
}

List Attachments ​

GET /api/projects/{projectId}/reports/{reportId}/attachments

Returns all attachments linked to a report, ordered by creation time (oldest first).

bash
curl https://{tenant}.kendo.dev/api/projects/1/reports/1/attachments \
  -H "Authorization: Bearer your-token"
json
[
  {
    "id": 42,
    "filename": "screenshot.webp",
    "mime_type": "image/webp",
    "size": 28417,
    "user_id": 4,
    "created_at": "2026-04-22T09:15:00.000000Z"
  }
]

Download Attachment ​

GET /api/projects/{projectId}/reports/{reportId}/attachments/{attachmentId}/download

Streams the attachment file bytes with a Content-Disposition: attachment header.

bash
curl https://{tenant}.kendo.dev/api/projects/1/reports/1/attachments/42/download \
  -H "Authorization: Bearer your-token" \
  -o screenshot.webp

Common Errors ​

StatusCause
401Token is missing, invalid, revoked, or expired
403Token lacks the required permission or access level, a project token is not linked to (or is inactive for) the target project, or the Reports feature is disabled ("feature_restricted": true)
422Request validation failed
429Rate limit exceeded — most report endpoints use the default API limit of 120 requests per minute; dismiss has its own limit of 30 requests per minute

Enums ​

Source ​

ValueNameDescription
0ManualSubmitted by a signed-in user inside the kendo app
1ApiSubmitted by an external system using a project token

Dismiss Reason ​

ValueDescription
not-plannedA reasonable request that is not currently planned. It does not fit the current product direction, even though the request itself is valid
duplicateThe same underlying problem or request is already tracked by an existing issue or another report, so this one is folded into that existing work instead of tracked separately
already-shippedThe described bug already has a solution shipped, or the requested capability already exists. The report predates the fix, or the reporter had not seen the shipped change yet
invalidNot a genuine report, such as spam, test data, or a support question rather than a product issue

See Also ​