Skip to Content
REST APIEndpoints

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

MethodPathToolWho can call it
GET/api/v1/workspaceslist_workspacesAny paid seat
GET/api/v1/projectslist_projectsAny paid seat
GET/api/v1/memberslist_membersAny paid seat
GET/api/v1/itemslist_items, search_itemsAny paid seat
GET/api/v1/items/{itemId}get_itemAny paid seat
POST/api/v1/items/updatepost_updateAny paid seat
POST/api/v1/items/createcreate_itemAny paid seat
POST/api/v1/items/statusset_statusAny paid seat
POST/api/v1/items/assignassignAny paid seat
POST/api/v1/items/mergemerge_itemsAny paid seat
POST/api/v1/rulessave_ruleWorkspace owners and admins
GET/api/v1/whats-newwhats_newAny paid seat
GET/api/v1/reviewproject_reviewAny paid seat
GET/api/v1/decisionsdecision_logAny paid seat
GET/api/v1/sourcefind_by_sourceAny paid seat
POST/api/v1/similarfind_similarAny paid seat

Discovery

/api/v1/workspacesGETlist_workspacesAny paid seat

List 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 seat

List projects

Returns the projects you can reach, most recent activity first, across every workspace unless you name one. Archived projects aren’t included.

Query parameterTypeDescription
workspaceIdstringOptional. 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 seat

List members

Returns the people in a workspace, so you can name an assignee or a mention.

Query parameterTypeDescription
projectIdstringA project in the workspace.
workspaceIdstringOr 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 seat

List items

Returns a summary of the project, then one row per thread, newest first.

Query parameterTypeDescription
projectIdstringThe project to read. Optional when your token reaches one workspace with a single project. Otherwise the error lists the projects and their IDs.
statusstringopen, deferred or resolved. Defaults to all.
typestringA type key, such as bug, idea or feedback.
pagestringOnly threads on one page, such as /checkout.
assigneestringA user id, me for yourself, or none for unassigned threads.
qstringOnly threads whose first message or replies contain this text.
limitnumberRows 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
    }
  ]
}
  • summary always covers the whole project, before your filters.
  • matched is the number of threads that passed the filters. truncated is true when limit cut the list short. This endpoint doesn’t page, so narrow the filters instead.
  • capped is true when the project has more than scanLimit threads. Only the most recent threads were read, and the counts are a minimum.
  • excerpt is the thread’s one-line summary when it has one (excerptSource: "summary"). Otherwise it is the start of the first message.
  • reportCount counts duplicates merged into the thread. Merged duplicates do not appear as rows of their own.
  • agentTask is what someone asked an agent to do with the thread (investigate, plan, fix or build), or null.
/api/v1/items/{itemId}GETget_itemAny paid seat

Get 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
}
  • anchor identifies the element: a CSS selector, its visible text, any text the person selected, and the click position as percentages of the element. skyId and skyKey are present when the site was built with Skyelight Build.
  • source is the file, line, commit and branch that rendered the element, from Skyelight Build’s stamps, or null on sites built without it. sourcePath lists the stamped files around the element.
  • sourceUrl links to that line at that commit when the project has a repository linked, otherwise null.
  • movedAt is set when someone moved the pin to another element after the page was captured. source describes the new element. The screenshot and page context still describe the original element.
  • evidence.screenshotUrl is null when no screenshot was taken. domContext is the page context: the redacted markup around the element. When the workspace’s retention period has passed, expired is true and both are null.
  • attachments lists images people attached to the thread or its replies, each with a url, author and where (thread or reply).
  • linear lists any Linear issue the thread was sent to.
  • duplicateOf is 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/updatePOSTpost_updateAny paid seat

Reply to an item

Posts a reply on the thread, where the person who created the thread can read it.

Body fieldTypeRequiredDescription
itemIdstringyesThe thread to reply to.
bodystringyesThe reply, up to 4,000 characters.
mentionsstring[]noUp to 10 people to notify, each as a user id, email, name or me. Write @Name in the body where you mention them.
linksobject[]noUp to 5 links, each { "url": "...", "label": "..." }. Added to the end of the reply. label is optional.
curl -X POST https://app.skyelight.ai/api/v1/items/update \
  -H "Authorization: Bearer $SKYELIGHT_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "itemId": "ITEM_ID",
    "body": "Fixed: the button was disabled after the first submit. @Sam can you check the preview?",
    "mentions": ["Sam"],
    "links": [{ "label": "PR", "url": "https://github.com/acme/web/pull/412" }]
  }'
{ "pinId": "r7...", "postedAs": "Dana Reyes", "mentioned": ["Sam Lee"] }

A name that matches more than one person is refused with the candidates, and nothing is posted. You can’t reply to a reply.

/api/v1/items/createPOSTcreate_itemAny paid seat

Create an item

Creates a new thread on a page.

Body fieldTypeRequiredDescription
urlstringyesFull URL of the page it is about.
bodystringyesThe message, up to 4,000 characters.
projectIdstringwhen your token reaches several projectsThe project to create it in.
selectorstringnoCSS selector of the element, if there is one.
pageTitlestringnoThe page’s title.
mentionsstring[]noPeople to notify, as for replies.
assigneestringnoWho 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 seat

Change status

Body fieldTypeRequiredDescription
itemIdstringyesThe thread.
statusstringyesopen, 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 seat

Assign an item

Body fieldTypeRequiredDescription
itemIdstringyesThe thread.
assigneestring or nullyesA 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 seat

Merge 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 fieldTypeRequiredDescription
itemIdstringyesThe thread to keep.
duplicateIdsstring[]yesThreads 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 admins

Save 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 fieldTypeRequiredDescription
rulestringyesThe rule, in one sentence, under 500 characters.
projectIdstringwhen your token reaches several projectsThe project.
sourceItemIdsstring[]noThreads 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 seat

What'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 parameterTypeDescription
projectIdstringThe project.
sincestringOptional. 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 seat

Project 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 parameterTypeDescription
projectIdstringThe project.
sectionstringOmit for the overview. resolved, deferred, open or discussions to read one section in full.
offsetnumberWith 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&section=resolved&offset=100" \
  -H "Authorization: Bearer $SKYELIGHT_API_TOKEN"
/api/v1/decisionsGETdecision_logAny paid seat

Decision 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 parameterTypeDescription
projectIdstringThe project.
sincestringOptional. Only decisions after this date.
limitnumberDecisions per page. Default 15, at most 30.
offsetnumberWhere 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 seat

Find 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 parameterTypeDescription
projectIdstringThe project.
filestringA path or file name, such as components/PricingTable.tsx or PricingTable.tsx. Matched on whole path segments.
linenumberOptional. Threads nearest this line come first.
componentstringA component name, such as PricingTable.
statusstringopen, deferred or resolved.
limitnumberRows 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 seat

Find 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 fieldTypeRequiredDescription
textstringtext or itemIdWhat you are about to report.
itemIdstringtext or itemIdFind duplicates of this thread instead.
projectIdstringnoOptional when itemId is given.
pagestringnoA page path or URL, such as /checkout.
selectorstringnoCSS selector of the element.
includeResolvedbooleannoDefault true, since a resolved match may mean the problem came back.
limitnumbernoMatches 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.

Last updated on