Threads
Threads are the comment pins placed on a markup. Use these endpoints to list, read, create, resolve, reopen, and delete threads, reposition a thread’s pin, and manage the screenshot attached to a thread. Authenticate with an exchange token (embedded feedback) or a workspace- or organization-scoped API key (server-to-server) — see the Authentication overview.
List threads
GET /api/v2/threads
List the threads on a markup. Filter and paginate with the query parameters.
curl "https://api.markup.io/api/v2/threads?markupId=b3a33209-5d20-4560-b876-8e14993a01ef" \
-X GET \
-H "Authorization: Bearer <API-KEY-SECRET>" \
-H "Markup-API-Version: 2023-02-22" \
-H "Content-Type: application/json" {
"data": {
"threads": [
{}
],
"query": {
"resolved": true
},
"offset": 123,
"total": 123,
"cursorId": "some cursorId"
}
}Response Body ProjectThreadsResponsePublic
| Option | Type | |
|---|---|---|
| threads | ||
| query | ||
| offset | number | |
| total | number | |
| cursorId | string |
Request Query Params ListThreadsQuery
| Option | Type | |
|---|---|---|
| markupId | string | |
| cursorId optional | string | |
| limit optional | number | |
| withThreadOrMessageId optional | string | |
| resolved optional | boolean | |
| order optional | ||
| viewMode optional | ||
| q optional | string | |
| offset optional | number |
Get thread
GET /api/v2/threads/:id
Get a single thread by ID, including its messages.
curl "https://api.markup.io/api/v2/threads/9b83ce6d-8dc4-4464-8b61-e5e770442e61" \
-X GET \
-H "Authorization: Bearer <API-KEY-SECRET>" \
-H "Markup-API-Version: 2023-02-22" \
-H "Content-Type: application/json" {
"data": {
"id": "some id",
"createdAt": {},
"modifiedAt": {},
"scopes": [
"some string"
],
"assignedRoleSlugs": [
"some string"
],
"user": {
"id": "some id",
"createdAt": {},
"modifiedAt": {},
"name": "some name",
"guest": true
},
"number": 123,
"resolved": true,
"messages": [
{
"id": "some id",
"createdAt": {},
"modifiedAt": {},
"scopes": [
"some string"
],
"assignedRoleSlugs": [
"some string"
],
"threadId": "some threadId",
"message": "some message",
"messageJson": {},
"user": {
"id": "some id",
"createdAt": {},
"modifiedAt": {},
"name": "some name",
"guest": true
},
"userMetaData": {},
"attachments": [
{
"id": "some id",
"createdAt": {},
"modifiedAt": {},
"messageId": "some messageId",
"url": "some url",
"etag": "some etag",
"mimeType": "some mimeType",
"originalFilename": "some originalFilename",
"filesize": 123
}
],
"loomVideos": [
{
"id": "some id",
"createdAt": {},
"modifiedAt": {},
"messageId": "some messageId",
"title": "some title",
"sharedUrl": "some sharedUrl",
"width": 123,
"height": 123
}
],
"read": true
}
],
"userMetaData": {},
"projectId": "some projectId",
"proxyUrl": "some proxyUrl",
"originalUrl": "some originalUrl",
"canonicalUrl": "some canonicalUrl",
"elements": [
{
"id": "some id",
"createdAt": {},
"modifiedAt": {},
"threadId": "some threadId",
"path": "some path",
"offsetXPercentage": 123,
"offsetYPercentage": 123
}
],
"parents": [
{
"id": "some id",
"createdAt": {},
"modifiedAt": {},
"path": "some path",
"depth": 123,
"proxyUrl": "some proxyUrl",
"originalUrl": "some originalUrl",
"canonicalUrl": "some canonicalUrl"
}
],
"viewModeId": "some viewModeId",
"read": true,
"unreadMessageCount": 123
}
}Response Body - ThreadResponsePublic
Request Path Params IdRequestParam
| Option | Type | |
|---|---|---|
| id | string |
Create thread
POST /api/v2/threads
Create a new thread (comment pin) on a markup.
curl "https://api.markup.io/api/v2/threads" \
-X POST \
-H "Authorization: Bearer <API-KEY-SECRET>" \
-H "Markup-API-Version: 2023-02-22" \
-H "Content-Type: application/json" \
--data '{
"isImageThread": true,
"url": "some url",
"canonicalUrl": "some canonicalUrl",
"elements": [
{
"elementPath": "some elementPath",
"offsetXPercentage": 123,
"offsetYPercentage": 123
}
],
"offsetXPercentage": 123,
"offsetYPercentage": 123,
"message": {},
"browserContext": {},
"elementParents": [
{
"url": "some url",
"canonicalUrl": "some canonicalUrl",
"path": "some path",
"depth": 123
}
],
"viewModeId": "e7a0bcc0-2882-470d-93ca-20ee9950e588"
}'{
"data": {
"id": "some id",
"createdAt": {},
"modifiedAt": {},
"scopes": [
"some string"
],
"assignedRoleSlugs": [
"some string"
],
"user": {
"id": "some id",
"createdAt": {},
"modifiedAt": {},
"name": "some name",
"guest": true
},
"number": 123,
"resolved": true,
"messages": [
{
"id": "some id",
"createdAt": {},
"modifiedAt": {},
"scopes": [
"some string"
],
"assignedRoleSlugs": [
"some string"
],
"threadId": "some threadId",
"message": "some message",
"messageJson": {},
"user": {
"id": "some id",
"createdAt": {},
"modifiedAt": {},
"name": "some name",
"guest": true
},
"userMetaData": {},
"attachments": [
{
"id": "some id",
"createdAt": {},
"modifiedAt": {},
"messageId": "some messageId",
"url": "some url",
"etag": "some etag",
"mimeType": "some mimeType",
"originalFilename": "some originalFilename",
"filesize": 123
}
],
"loomVideos": [
{
"id": "some id",
"createdAt": {},
"modifiedAt": {},
"messageId": "some messageId",
"title": "some title",
"sharedUrl": "some sharedUrl",
"width": 123,
"height": 123
}
],
"read": true
}
],
"userMetaData": {},
"projectId": "some projectId",
"proxyUrl": "some proxyUrl",
"originalUrl": "some originalUrl",
"canonicalUrl": "some canonicalUrl",
"elements": [
{
"id": "some id",
"createdAt": {},
"modifiedAt": {},
"threadId": "some threadId",
"path": "some path",
"offsetXPercentage": 123,
"offsetYPercentage": 123
}
],
"parents": [
{
"id": "some id",
"createdAt": {},
"modifiedAt": {},
"path": "some path",
"depth": 123,
"proxyUrl": "some proxyUrl",
"originalUrl": "some originalUrl",
"canonicalUrl": "some canonicalUrl"
}
],
"viewModeId": "some viewModeId",
"read": true,
"unreadMessageCount": 123
}
}Response Body - ThreadResponsePublic
Request Body CreateThreadRequest
| Option | Type | |
|---|---|---|
| isImageThread | boolean | |
| projectId optional | string | |
| projectImageId optional | string | |
| url | string | |
| canonicalUrl | string | |
| elements | CreateThreadRequestElement[] | |
| offsetXPercentage | number | |
| offsetYPercentage | number | |
| startTime optional | number | |
| endTime optional | number | |
| message | Delta | |
| browserContext | ||
| elementParents | ThreadElementParentRequest[] | |
| viewModeId | string | |
| attachments optional | MessageRequestAttachment[] | |
| loomVideos optional | LoomVideoRequest[] | |
| width optional | number | |
| height optional | number | |
| annotations optional | ||
| textHighlight optional | ||
| pageNumber optional | number | |
| requireDimensionsOverride optional | boolean | |
| priority optional | number |
Resolve thread
POST /api/v2/threads/:id/resolve
Mark a thread as resolved.
curl "https://api.markup.io/api/v2/threads/7e2e7df9-1b16-4ceb-8219-ff204fb57708/resolve" \
-X POST \
-H "Authorization: Bearer <API-KEY-SECRET>" \
-H "Markup-API-Version: 2023-02-22" \
-H "Content-Type: application/json" {
"data": {
"threadId": "some threadId",
"modifiedAt": 123
}
}Response Body ThreadResolvedResponse
| Option | Type | |
|---|---|---|
| threadId | string | |
| modifiedAt | number |
Request Path Params IdRequestParam
| Option | Type | |
|---|---|---|
| id | string |
Unresolve thread
POST /api/v2/threads/:id/unresolve
Reopen a previously resolved thread.
curl "https://api.markup.io/api/v2/threads/9f55f067-02a1-4740-8b88-1822b1667188/unresolve" \
-X POST \
-H "Authorization: Bearer <API-KEY-SECRET>" \
-H "Markup-API-Version: 2023-02-22" \
-H "Content-Type: application/json" {
"data": {
"threadId": "some threadId"
}
}Response Body ThreadUnresolvedResponse
| Option | Type | |
|---|---|---|
| threadId | string |
Request Path Params IdRequestParam
| Option | Type | |
|---|---|---|
| id | string |
Mark thread as read
POST /api/v2/threads/:id/read
Mark a thread, and every message inside it, as read for the authenticated user. Read state is per user: with an exchange token it is recorded against the end user the token identifies, and with an API key it is recorded against the key’s API user. Returns 204 with no body.
curl "https://api.markup.io/api/v2/threads/b5d69388-a63a-4e47-8b30-1d401b05c270/read" \
-X POST \
-H "Authorization: Bearer <API-KEY-SECRET>" \
-H "Markup-API-Version: 2023-02-22" \
-H "Content-Type: application/json" 204 No ContentRequest Path Params IdRequestParam
| Option | Type | |
|---|---|---|
| id | string |
Delete thread
DELETE /api/v2/threads/:id
Delete a thread and all of its messages.
curl "https://api.markup.io/api/v2/threads/b223a6e3-4656-49ad-a3c0-873c65194b0e" \
-X DELETE \
-H "Authorization: Bearer <API-KEY-SECRET>" \
-H "Markup-API-Version: 2023-02-22" \
-H "Content-Type: application/json" 204 No ContentRequest Path Params IdRequestParam
| Option | Type | |
|---|---|---|
| id | string |
Move thread pin
PATCH /api/v2/threads/:id/move-pin
Reposition a thread’s pin on the markup.
curl "https://api.markup.io/api/v2/threads/39f3a550-9efc-4d4f-9fc4-e82dbc47bb61/move-pin" \
-X PATCH \
-H "Authorization: Bearer <API-KEY-SECRET>" \
-H "Markup-API-Version: 2023-02-22" \
-H "Content-Type: application/json" \
--data '{
"url": "some url",
"canonicalUrl": "some canonicalUrl",
"elements": [
{
"elementPath": "some elementPath",
"offsetXPercentage": 123,
"offsetYPercentage": 123
}
],
"browserContext": {},
"elementParents": [
{
"url": "some url",
"canonicalUrl": "some canonicalUrl",
"path": "some path",
"depth": 123
}
],
"viewportXPercentage": 123,
"viewportYPercentage": 123
}'{
"data": {
"id": "some id",
"userId": "some userId",
"projectId": "some projectId",
"number": 123,
"resolved": true,
"proxyHostId": "some proxyHostId",
"urlPath": "some urlPath",
"canonicalUrl": "some canonicalUrl",
"viewModeId": "some viewModeId"
}
}Response Body - Thread
Request Path Params IdRequestParam
| Option | Type | |
|---|---|---|
| id | string |
Request Body MoveThreadPinBody
| Option | Type | |
|---|---|---|
| url | string | |
| canonicalUrl | string | |
| elements | CreateThreadRequestElement[] | |
| browserContext | ||
| elementParents | ThreadElementParentRequest[] | |
| viewportXPercentage | number | |
| viewportYPercentage | number |
Get screenshot upload policy
GET /api/v2/threads/:id/screenshot-upload-policy
Get a pre-signed S3 upload policy for attaching a screenshot to a thread. Upload the file to S3 with the returned policy, then call PUT /threads/:id/screenshot to attach it.
curl "https://api.markup.io/api/v2/threads/47e0fcc1-0f66-40a4-9d22-65f096a07728/screenshot-upload-policy?fileId=f530691f-b840-40dd-8f6b-033fdf30db81&filesize=123&filename=some+filename&mimeType=some+mimeType" \
-X GET \
-H "Authorization: Bearer <API-KEY-SECRET>" \
-H "Markup-API-Version: 2023-02-22" \
-H "Content-Type: application/json" {
"data": {
"key": "some key",
"policy": {},
"bucket": "some bucket"
}
}Response Body S3UploadPolicyResponse
| Option | Type | |
|---|---|---|
| key | string | |
| policy | PresignedPost | |
| bucket | string | |
| metadata | Record |
Request Path Params IdRequestParam
| Option | Type | |
|---|---|---|
| id | string |
Request Query Params ScreenshotUploadPolicyRequestQuery
| Option | Type | |
|---|---|---|
| fileId | string | |
| filesize | number | |
| filename | string | |
| mimeType | string |
Set thread screenshot
PUT /api/v2/threads/:id/screenshot
Attach (or replace) the screenshot for a thread after uploading the file with the upload policy.
curl "https://api.markup.io/api/v2/threads/4aa3b253-8e49-45de-a6db-14b3741a823b/screenshot" \
-X PUT \
-H "Authorization: Bearer <API-KEY-SECRET>" \
-H "Markup-API-Version: 2023-02-22" \
-H "Content-Type: application/json" \
--data '{
"key": "some key",
"etag": "some etag",
"mimeType": "some mimeType",
"width": 123,
"height": 123,
"filesize": 123,
"fileId": "58dea1ba-c086-4651-8ce5-93541fb7fcc6"
}'{
"data": {
"id": "some id",
"createdAt": {},
"modifiedAt": {},
"mimeType": "some mimeType",
"width": 123,
"height": 123,
"filesize": 123,
"type": "extension",
"isProcessing": true
}
}Response Body - ScreenshotResponse
Request Path Params IdRequestParam
| Option | Type | |
|---|---|---|
| id | string |
Request Body CreateScreenshotRequest
| Option | Type | |
|---|---|---|
| key | string | |
| etag | string | |
| mimeType | string | |
| width | number | |
| height | number | |
| filesize | number | |
| originalFilename optional | string | |
| fileId | string |
Get thread screenshot
GET /api/v2/threads/:id/screenshot
Get the screenshot attached to a thread.
curl "https://api.markup.io/api/v2/threads/91414aae-3365-4b46-9613-55d7d59b5397/screenshot" \
-X GET \
-H "Authorization: Bearer <API-KEY-SECRET>" \
-H "Markup-API-Version: 2023-02-22" \
-H "Content-Type: application/json" {
"data": {
"id": "some id",
"createdAt": {},
"modifiedAt": {},
"mimeType": "some mimeType",
"width": 123,
"height": 123,
"filesize": 123,
"type": "extension",
"isProcessing": true
}
}Response Body - ScreenshotResponse
Request Path Params IdRequestParam
| Option | Type | |
|---|---|---|
| id | string |
Related types
ScreenshotResponse
| Option | Type | |
|---|---|---|
| id | string | |
| createdAt | Unix_Timestamp_Utc_Milliseconds | |
| modifiedAt | Unix_Timestamp_Utc_Milliseconds | |
| mimeType | string | |
| width | number | |
| height | number | |
| filesize | number | |
| type | ||
| isProcessing | boolean |
ThreadOrder
| Option | Type | |
|---|---|---|
| ThreadOrder | `page` | `pin_asc` | `pin_desc` | `activity` | `timestamp` | `priority_asc` | `priority_desc` |
ViewModeCategory
| Option | Type | |
|---|---|---|
| ViewModeCategory | `desktop` | `tablet` | `mobile` |
ThreadElementParentRequest
| Option | Type | |
|---|---|---|
| url | string | |
| canonicalUrl | string | |
| path | string | |
| depth | number |
BrowserContextRequest
| Option | Type | |
|---|---|---|
| browserName | string | |
| browserVersion | string | |
| os | string | |
| osVersion | string | |
| osVersionName | string | |
| platform | string | |
| screenWidth | number | |
| screenHeight | number | |
| viewportWidth | number | |
| viewportHeight | number |
CanvasThreadAnnotationRequest
| Option | Type | |
|---|---|---|
| version | string | |
| scale | number | |
| originalWidth | number | |
| originalHeight | number |
ThreadTextHighlightRequest
| Option | Type | |
|---|---|---|
| startKey | string | |
| endKey | string | |
| startOffset | number | |
| startIndex | number | |
| endOffset | number | |
| endIndex | number | |
| text | string |
IProjectThreadQuery
| Option | Type | |
|---|---|---|
| resolved | boolean |
ThreadResponsePublic
| Option | Type | |
|---|---|---|
| ThreadResponsePublic |
WebpageThreadResponsePublic
| Option | Type | |
|---|---|---|
| id | string | |
| createdAt | Unix_Timestamp_Utc_Milliseconds | |
| modifiedAt | Unix_Timestamp_Utc_Milliseconds | |
| scopes | string[] | |
| assignedRoleSlugs | string[] | |
| user | AuthorUserResponse | |
| number | number | |
| resolved | boolean | |
| messages | MessageResponseWithReadState[] | The thread's messages, each carrying its own |
| userMetaData | IUserResourceMetaData | |
| lastActivityAt | number | |
| annotations | AnnotationsResponse | |
| projectId | string | |
| proxyUrl | string | |
| originalUrl | string | |
| canonicalUrl | string | |
| elements | ThreadElementResponse[] | |
| parents | ThreadElementParentResponse[] | |
| browserContext | IBrowserContext | |
| viewModeId | string | |
| read | boolean | True when the thread and every message in it have been seen by the caller. Mark it read with |
| unreadMessageCount | number | Number of messages in the thread the caller has not seen since they last changed. |
ImageThreadResponsePublic
| Option | Type | |
|---|---|---|
| id | string | |
| createdAt | Unix_Timestamp_Utc_Milliseconds | |
| modifiedAt | Unix_Timestamp_Utc_Milliseconds | |
| scopes | string[] | |
| assignedRoleSlugs | string[] | |
| user | AuthorUserResponse | |
| number | number | |
| resolved | boolean | |
| messages | MessageResponseWithReadState[] | The thread's messages, each carrying its own |
| userMetaData | IUserResourceMetaData | |
| lastActivityAt | number | |
| annotations | AnnotationsResponse | |
| projectImageId | string | |
| offsetXPercentage | number | |
| offsetYPercentage | number | |
| startTime | number | |
| endTime | number | |
| read | boolean | True when the thread and every message in it have been seen by the caller. Mark it read with |
| unreadMessageCount | number | Number of messages in the thread the caller has not seen since they last changed. |
PdfThreadResponsePublic
| Option | Type | |
|---|---|---|
| id | string | |
| createdAt | Unix_Timestamp_Utc_Milliseconds | |
| modifiedAt | Unix_Timestamp_Utc_Milliseconds | |
| scopes | string[] | |
| assignedRoleSlugs | string[] | |
| user | AuthorUserResponse | |
| number | number | |
| resolved | boolean | |
| messages | MessageResponseWithReadState[] | The thread's messages, each carrying its own |
| userMetaData | IUserResourceMetaData | |
| lastActivityAt | number | |
| annotations | AnnotationsResponse | |
| projectImageId | string | |
| offsetXPercentage | number | |
| offsetYPercentage | number | |
| pageNumber | number | |
| textHighlight | TextHighlightResponse | |
| read | boolean | True when the thread and every message in it have been seen by the caller. Mark it read with |
| unreadMessageCount | number | Number of messages in the thread the caller has not seen since they last changed. |
Thread
| Option | Type | |
|---|---|---|
| Thread |
WebpageThread
| Option | Type | |
|---|---|---|
| id | string | |
| userId | string | |
| projectId | string | |
| number | number | |
| resolved | boolean | |
| proxyHostId | string | |
| urlPath | string | |
| canonicalUrl | string | |
| viewModeId | string |
ImageThread
| Option | Type | |
|---|---|---|
| id | string | |
| userId | string | |
| projectId | string | |
| number | number | |
| resolved | boolean | |
| projectImageId | string | |
| offsetXPercentage | number | |
| offsetYPercentage | number | |
| startTime | number | |
| endTime | number |
PdfThread
| Option | Type | |
|---|---|---|
| id | string | |
| userId | string | |
| projectId | string | |
| number | number | |
| resolved | boolean | |
| pageNumber | number | |
| pageRelativeX | number | |
| pageRelativeY | number |
ScreenshotType
| Option | Type | |
|---|---|---|
| ScreenshotType | `extension` | `native` | `static` |