Endpoints
This page lists every Skyelight REST API route, with a curl example and the
response shape. All paths are relative to https://app.skyelight.ai. Every
request needs an Authorization: Bearer header (see
Authentication), and every POST takes a JSON body
with Content-Type: application/json.
Each endpoint runs the MCP tool named beside it. The MCP tools reference describes the same behavior for agents.
The examples assume your token is in $SKYELIGHT_API_TOKEN:
export SKYELIGHT_API_TOKEN=sky_...If your token reaches one workspace, every call acts in that workspace. If it
reaches several, Skyelight finds the workspace from the projectId or item ID
you pass. A call that needs a workspace and passes neither gets a 400 that
asks for a projectId.
“Any paid seat” means an owner, admin or collaborator role. Reviewers can’t use the API. See Roles and permissions.
All endpoints
| Method | Path | Tool | Who can call it |
|---|---|---|---|
GET | /api/v1/workspaces | list_workspaces | Any paid seat |
GET | /api/v1/projects | list_projects | Any paid seat |
GET | /api/v1/members | list_members | Any paid seat |
GET | /api/v1/items | list_items, search_items | Any paid seat |
GET | /api/v1/items/{itemId} | get_item | Any paid seat |
POST | /api/v1/items/update | post_update | Any paid seat |
POST | /api/v1/items/create | create_item | Any paid seat |
POST | /api/v1/items/status | set_status | Any paid seat |
POST | /api/v1/items/assign | assign | Any paid seat |
POST | /api/v1/items/merge | merge_items | Any paid seat |
POST | /api/v1/rules | save_rule | Workspace owners and admins |
GET | /api/v1/whats-new | whats_new | Any paid seat |
GET | /api/v1/review | project_review | Any paid seat |
GET | /api/v1/decisions | decision_log | Any paid seat |
GET | /api/v1/source | find_by_source | Any paid seat |
POST | /api/v1/similar | find_similar | Any paid seat |
Discovery
/api/v1/workspacesGETlist_workspacesAny paid seatList workspaces
Returns the workspaces your token reaches, your role in each, and your own user ID and name.
curl https://app.skyelight.ai/api/v1/workspaces \
-H "Authorization: Bearer $SKYELIGHT_API_TOKEN"{
"workspaces": [
{
"id": "k17...",
"name": "Acme",
"slug": "acme",
"role": "collaborator",
"projectCount": 4,
"available": true,
"unavailableReason": null
}
],
"total": 1,
"you": { "id": "user_2ab...", "name": "Dana Reyes" }
}A workspace where agent tools aren’t available (you are a reviewer there, or
its plan doesn’t include them) is still listed, with available: false and
the reason in unavailableReason. you.id is your user ID, which the
assignee filter and fields accept.
/api/v1/projectsGETlist_projectsAny paid seatList projects
Returns the projects you can reach, most recent activity first, across every workspace unless you name one. Archived projects aren’t included.
| Query parameter | Type | Description |
|---|---|---|
workspaceId | string | Optional. Only projects in this workspace. |
curl https://app.skyelight.ai/api/v1/projects \
-H "Authorization: Bearer $SKYELIGHT_API_TOKEN"{
"projects": [
{
"id": "j57...",
"name": "Checkout rebuild",
"slug": "checkout-rebuild",
"workspace": { "id": "k17...", "name": "Acme" },
"repo": "https://github.com/acme/web",
"updatedAt": 1790000000000,
"lastActivityAt": 1790500000000
}
],
"total": 1
}repo is the repository linked to the project, or null.
/api/v1/membersGETlist_membersAny paid seatList members
Returns the people in a workspace, so you can name an assignee or a mention.
| Query parameter | Type | Description |
|---|---|---|
projectId | string | A project in the workspace. |
workspaceId | string | Or the workspace itself. |
curl "https://app.skyelight.ai/api/v1/members?projectId=PROJECT_ID" \
-H "Authorization: Bearer $SKYELIGHT_API_TOKEN"{
"workspace": { "id": "k17...", "name": "Acme" },
"members": [
{ "id": "user_2ab...", "name": "Dana Reyes", "email": "dana@acme.com", "role": "admin" }
]
}Items
/api/v1/itemsGETlist_items, search_itemsAny paid seatList items
Returns a summary of the project, then one row per thread, newest first.
| Query parameter | Type | Description |
|---|---|---|
projectId | string | The project to read. Optional when your token reaches one workspace with a single project. Otherwise the error lists the projects and their IDs. |
status | string | open, deferred or resolved. Defaults to all. |
type | string | A type key, such as bug, idea or feedback. |
page | string | Only threads on one page, such as /checkout. |
assignee | string | A user id, me for yourself, or none for unassigned threads. |
q | string | Only threads whose first message or replies contain this text. |
limit | number | Rows to return. Default 50, at most 200. |
curl "https://app.skyelight.ai/api/v1/items?projectId=PROJECT_ID&status=open&assignee=me" \
-H "Authorization: Bearer $SKYELIGHT_API_TOKEN"{
"project": { "id": "j57...", "name": "Checkout rebuild", "slug": "checkout-rebuild" },
"summary": {
"total": 21,
"open": 15,
"deferred": 3,
"resolved": 3,
"unassigned": 19,
"byType": { "bug": 7, "idea": 4, "feedback": 10 },
"byPage": { "/checkout": 8, "/settings": 4 }
},
"matched": 2,
"returned": 2,
"truncated": false,
"capped": false,
"scanLimit": 2000,
"items": [
{
"id": "i1...",
"page": "/checkout",
"type": "bug",
"status": "open",
"excerpt": "The pay button does nothing on the second click",
"excerptSource": "summary",
"reportCount": 5,
"author": "Sam Lee",
"agentTask": "fix",
"assignee": "Dana Reyes",
"assigneeId": "user_2ab...",
"replyCount": 2,
"createdAt": 1790400000000
}
]
}summaryalways covers the whole project, before your filters.matchedis the number of threads that passed the filters.truncatedistruewhenlimitcut the list short. This endpoint doesn’t page, so narrow the filters instead.cappedistruewhen the project has more thanscanLimitthreads. Only the most recent threads were read, and the counts are a minimum.excerptis the thread’s one-line summary when it has one (excerptSource: "summary"). Otherwise it is the start of the first message.reportCountcounts duplicates merged into the thread. Merged duplicates do not appear as rows of their own.agentTaskis what someone asked an agent to do with the thread (investigate,plan,fixorbuild), ornull.
/api/v1/items/{itemId}GETget_itemAny paid seatGet an item
Returns everything needed to work on one thread: the messages in order, the page, the pinned element, its source location, and the screenshot and page context captured with it.
curl https://app.skyelight.ai/api/v1/items/ITEM_ID \
-H "Authorization: Bearer $SKYELIGHT_API_TOKEN"{
"id": "i1...",
"project": {
"id": "j57...",
"name": "Checkout rebuild",
"repo": { "url": "https://github.com/acme/web" }
},
"page": "/checkout",
"url": "https://acme-git-checkout.vercel.app/checkout?step=2",
"pageTitle": "Checkout",
"reportCount": 5,
"duplicateOf": null,
"linear": [],
"type": "bug",
"typeSource": "ai",
"status": "open",
"agentTask": "fix",
"assignee": { "id": "user_2ab...", "name": "Dana Reyes", "mode": null },
"thread": {
"root": {
"author": "Sam Lee",
"authorId": "user_9cd...",
"content": "The pay button does nothing on the second click.",
"createdAt": 1790400000000,
"editedAt": null
},
"replies": [
{
"author": "Dana Reyes",
"authorId": "user_2ab...",
"content": "Reproduced on Safari too.",
"createdAt": 1790410000000,
"editedAt": null
}
]
},
"anchor": {
"selector": "form > button.pay",
"elementText": "Pay now",
"selectedText": null,
"skyId": "9dbfcb76",
"skyKey": null,
"viewport": { "width": 1440, "height": 900, "x": 52.1, "y": 48.7 }
},
"source": {
"file": "components/checkout/PayButton.tsx",
"line": 88,
"build": "a1b2c3d",
"branch": "feat/checkout"
},
"sourcePath": [],
"movedAt": null,
"sourceUrl": "https://github.com/acme/web/blob/a1b2c3d/components/checkout/PayButton.tsx#L88",
"evidence": {
"screenshotUrl": "https://...",
"domContext": { },
"capturedAt": 1790400000000,
"expired": false
},
"attachments": [],
"reactions": [],
"createdAt": 1790400000000,
"resolvedAt": null
}anchoridentifies the element: a CSS selector, its visible text, any text the person selected, and the click position as percentages of the element.skyIdandskyKeyare present when the site was built with Skyelight Build.sourceis the file, line, commit and branch that rendered the element, from Skyelight Build’s stamps, ornullon sites built without it.sourcePathlists the stamped files around the element.sourceUrllinks to that line at that commit when the project has a repository linked, otherwisenull.movedAtis set when someone moved the pin to another element after the page was captured.sourcedescribes the new element. The screenshot and page context still describe the original element.evidence.screenshotUrlisnullwhen no screenshot was taken.domContextis the page context: the redacted markup around the element. When the workspace’s retention period has passed,expiredistrueand both arenull.attachmentslists images people attached to the thread or its replies, each with aurl,authorandwhere(threadorreply).linearlists any Linear issue the thread was sent to.duplicateOfis the ID of the thread this one was merged into. Work on that thread instead.
Requesting a reply’s ID returns a 400 with the thread’s ID in parentId.
Writing
Writes are recorded as you and send the same notifications as the same action in the web app.
/api/v1/items/createPOSTcreate_itemAny paid seatCreate an item
Creates a new thread on a page.
| Body field | Type | Required | Description |
|---|---|---|---|
url | string | yes | Full URL of the page it is about. |
body | string | yes | The message, up to 4,000 characters. |
projectId | string | when your token reaches several projects | The project to create it in. |
selector | string | no | CSS selector of the element, if there is one. |
pageTitle | string | no | The page’s title. |
mentions | string[] | no | People to notify, as for replies. |
assignee | string | no | Who should take it: a user id, email, name or me. |
curl -X POST https://app.skyelight.ai/api/v1/items/create \
-H "Authorization: Bearer $SKYELIGHT_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"projectId": "PROJECT_ID",
"url": "https://acme-git-checkout.vercel.app/checkout",
"selector": "form > button.pay",
"body": "Contrast on the disabled pay button is below 3:1.",
"assignee": "me"
}'{ "pinId": "i9...", "postedAs": "Dana Reyes", "assignedTo": "Dana Reyes", "mentioned": [] }To check for an existing thread first, use Find similar items.
/api/v1/items/statusPOSTset_statusAny paid seatChange status
| Body field | Type | Required | Description |
|---|---|---|---|
itemId | string | yes | The thread. |
status | string | yes | open, deferred or resolved. |
curl -X POST https://app.skyelight.ai/api/v1/items/status \
-H "Authorization: Bearer $SKYELIGHT_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "itemId": "ITEM_ID", "status": "resolved" }'{ "status": "resolved", "changed": true }changed is false when the thread already had that status. Before you
resolve a thread, post a reply that says what changed.
/api/v1/items/assignPOSTassignAny paid seatAssign an item
| Body field | Type | Required | Description |
|---|---|---|---|
itemId | string | yes | The thread. |
assignee | string or null | yes | A user id, email, name or me. null unassigns. |
curl -X POST https://app.skyelight.ai/api/v1/items/assign \
-H "Authorization: Bearer $SKYELIGHT_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "itemId": "ITEM_ID", "assignee": "dana@acme.com" }'{ "assignedTo": "Dana Reyes", "changed": true }The assignee gets the same notification as for an assignment in the app. A resolved thread must be reopened before you can assign it.
/api/v1/items/mergePOSTmerge_itemsAny paid seatMerge duplicates
Merges duplicate threads into one. Each duplicate is hidden from thread lists and counts as an extra report on the thread you keep. Nothing is deleted, and a merge can’t be undone.
| Body field | Type | Required | Description |
|---|---|---|---|
itemId | string | yes | The thread to keep. |
duplicateIds | string[] | yes | Threads reporting the same problem, in the same project. Up to 50. |
curl -X POST https://app.skyelight.ai/api/v1/items/merge \
-H "Authorization: Bearer $SKYELIGHT_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "itemId": "ITEM_ID", "duplicateIds": ["DUP_1", "DUP_2"] }'{ "merged": 2, "canonicalId": "i1..." }merged is the number of threads merged. canonicalId is the thread they
went to: the itemId you sent, or the thread it was already merged into.
Replies and threads from another project are refused. See
Duplicates.
/api/v1/rulesPOSTsave_ruleWorkspace owners and adminsSave a rule
Adds a rule the team agreed on to the project context, under “Rules learned”, with who added it and when. Skyelight reads the project context when it sets a thread’s type, so a saved rule affects future threads.
| Body field | Type | Required | Description |
|---|---|---|---|
rule | string | yes | The rule, in one sentence, under 500 characters. |
projectId | string | when your token reaches several projects | The project. |
sourceItemIds | string[] | no | Threads the rule came from. The first 5 are cited. |
curl -X POST https://app.skyelight.ai/api/v1/rules \
-H "Authorization: Bearer $SKYELIGHT_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"projectId": "PROJECT_ID",
"rule": "Buttons use sentence case.",
"sourceItemIds": ["ITEM_ID"]
}'{ "saved": true, "rule": "Buttons use sentence case.", "cited": ["ITEM_ID"] }A rule the context already contains returns { "saved": false, "duplicate": true }.
Project history
These endpoints read a whole project at once and quote the threads, with each
thread’s item ID, so you can trace each statement to its thread. They read up to
the 2,000 most recent threads and report capped: true when a project has
more.
/api/v1/whats-newGETwhats_newAny paid seatWhat's new
Returns what changed on a project since you last looked: new threads, new replies grouped by thread, threads resolved or deferred, and threads assigned to you or mentioning you.
| Query parameter | Type | Description |
|---|---|---|
projectId | string | The project. |
since | string | Optional. A date (2026-09-01), an ISO time, or milliseconds since 1970. |
Without since, the endpoint reads from your bookmark for that project and
moves the bookmark to now. Your first call looks back 7 days. With since, it
reads from that time and leaves the bookmark where it is.
curl "https://app.skyelight.ai/api/v1/whats-new?projectId=PROJECT_ID" \
-H "Authorization: Bearer $SKYELIGHT_API_TOKEN"The response has since, until, firstBriefing, counts, and the lists
newThreads, newReplies, resolved, deferred, assignedToYou and
mentionsYou.
/api/v1/reviewGETproject_reviewAny paid seatProject review
Returns what you need to write a project review or hand-off: counts, breakdowns by type and page, who took part, resolved threads and their last message, deferred threads and the reason given, open threads, and the longest conversations quoted.
| Query parameter | Type | Description |
|---|---|---|
projectId | string | The project. |
section | string | Omit for the overview. resolved, deferred, open or discussions to read one section in full. |
offset | number | With section: where to start. |
The overview returns every count and the first 25 rows of each section. With
section, you get that section 100 rows (or 5 conversations) at a time.
pages reports each section’s total and nextOffset, which is null on
the last page.
curl "https://app.skyelight.ai/api/v1/review?projectId=PROJECT_ID§ion=resolved&offset=100" \
-H "Authorization: Bearer $SKYELIGHT_API_TOKEN"/api/v1/decisionsGETdecision_logAny paid seatDecision log
Returns threads that were resolved or deferred after a discussion, each with its question, outcome, who decided and the last messages of the conversation. It also returns types people corrected, outcomes that recur, and the project’s existing rules.
| Query parameter | Type | Description |
|---|---|---|
projectId | string | The project. |
since | string | Optional. Only decisions after this date. |
limit | number | Decisions per page. Default 15, at most 30. |
offset | number | Where to start, from the previous response’s nextOffset. |
curl "https://app.skyelight.ai/api/v1/decisions?projectId=PROJECT_ID&since=2026-09-01" \
-H "Authorization: Bearer $SKYELIGHT_API_TOKEN"The response has decided (the total), offset, nextOffset, decisions,
corrections, recurring and rules.
Code and duplicates
/api/v1/sourceGETfind_by_sourceAny paid seatFind by source
Returns threads by the code that rendered the pinned element, from the file and line that Skyelight Build stamps on each element. Look up a file or a component before you edit it.
| Query parameter | Type | Description |
|---|---|---|
projectId | string | The project. |
file | string | A path or file name, such as components/PricingTable.tsx or PricingTable.tsx. Matched on whole path segments. |
line | number | Optional. Threads nearest this line come first. |
component | string | A component name, such as PricingTable. |
status | string | open, deferred or resolved. |
limit | number | Rows to return. Default 50, at most 200. |
curl "https://app.skyelight.ai/api/v1/source?projectId=PROJECT_ID&file=PricingTable.tsx&status=open" \
-H "Authorization: Bearer $SKYELIGHT_API_TOKEN"With a file or component, the response has mode: "matches", a summary
of open, deferred and resolved counts, and the matching items. With neither,
it returns mode: "hotspots" and files: the stamped files ranked by open
and deferred threads.
/api/v1/similarPOSTfind_similarAny paid seatFind similar items
Returns existing threads that may report the same thing. Pass the text you are about to report, or an existing item to find what it duplicates. Matching compares shared words across first messages, summaries and replies, plus the same page, element and source file. It doesn’t use an AI model.
| Body field | Type | Required | Description |
|---|---|---|---|
text | string | text or itemId | What you are about to report. |
itemId | string | text or itemId | Find duplicates of this thread instead. |
projectId | string | no | Optional when itemId is given. |
page | string | no | A page path or URL, such as /checkout. |
selector | string | no | CSS selector of the element. |
includeResolved | boolean | no | Default true, since a resolved match may mean the problem came back. |
limit | number | no | Matches to return. Default 5, at most 20. |
curl -X POST https://app.skyelight.ai/api/v1/similar \
-H "Authorization: Bearer $SKYELIGHT_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"projectId": "PROJECT_ID",
"text": "Pay button stops responding after one click",
"page": "/checkout"
}'The response lists candidates, each with its score from 0 to 1 and the
reasons it matched. If two threads are the same problem,
merge them.