Skip to main content

Annotations API

The Annotations API implements the W3C Web Annotation Protocol for creating, reading, updating, and deleting annotations.

W3C Compliance

All annotation responses conform to:

Responses include the W3C JSON-LD context and Cluster's research extensions.

Content Negotiation

Request W3C JSON-LD format:

Accept: application/ld+json; profile="http://www.w3.org/ns/anno.jsonld"

Or standard JSON (internally converted):

Accept: application/json

Endpoints

List Annotations

GET /api/annotations

Query Parameters:

ParameterTypeDescription
studyIduuidFilter by study
fileIduuidFilter by file reference
motivationstringFilter by motivation (highlighting, tagging, etc.)
tagIduuidFilter by tag
creatorIduuidFilter by creator
participantIdstringFilter by participant ID
offsetintegerPagination offset
limitintegerResults per page (max 100)

Response:

{
"@context": [
"http://www.w3.org/ns/anno.jsonld",
"https://cluster.research/ns/research.jsonld"
],
"type": "AnnotationCollection",
"total": 42,
"first": "/api/annotations?offset=0&limit=20",
"items": [
{
"id": "urn:uuid:a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"type": "Annotation",
"motivation": "highlighting",
"created": "2025-01-03T14:30:00Z",
"creator": {
"id": "urn:uuid:user-id",
"type": "Person",
"name": "Jane Smith"
},
"target": {
"source": "https://company.sharepoint.com/sites/research/interviews/p05.mp4",
"selector": {
"type": "FragmentSelector",
"conformsTo": "http://www.w3.org/TR/media-frags/",
"value": "t=324.5,358.2"
}
}
}
]
}

Get Annotation

GET /api/annotations/:id

Response:

{
"@context": [
"http://www.w3.org/ns/anno.jsonld",
"https://cluster.research/ns/research.jsonld"
],
"id": "urn:uuid:a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"type": "Annotation",
"motivation": ["highlighting", "tagging"],
"created": "2025-01-03T14:30:00Z",
"modified": "2025-01-03T15:00:00Z",
"creator": {
"id": "urn:uuid:550e8400-e29b-41d4-a716-446655440000",
"type": "Person",
"name": "Jane Smith"
},
"body": [
{
"type": "SpecificResource",
"source": "urn:uuid:tag-pain-point",
"purpose": "tagging"
}
],
"target": {
"source": "https://company.sharepoint.com/sites/research/interviews/p05-transcript.txt",
"selector": [
{
"type": "TextQuoteSelector",
"exact": "I had no idea what to do after I created my account.",
"prefix": "the signup was fine but ",
"suffix": " I ended up just closing"
},
{
"type": "TextPositionSelector",
"start": 2847,
"end": 2934
}
]
},
"research:study": "urn:uuid:study-id",
"research:participant": "P05"
}

Create Annotation

POST /api/annotations
Content-Type: application/ld+json; profile="http://www.w3.org/ns/anno.jsonld"

Request Body:

{
"@context": [
"http://www.w3.org/ns/anno.jsonld",
"https://cluster.research/ns/research.jsonld"
],
"type": "Annotation",
"motivation": "highlighting",
"target": {
"source": "https://company.sharepoint.com/sites/research/interviews/p05.mp4",
"selector": {
"type": "FragmentSelector",
"conformsTo": "http://www.w3.org/TR/media-frags/",
"value": "t=120,145"
}
},
"research:study": "urn:uuid:study-id",
"research:participant": "P05"
}

Response:

HTTP/1.1 201 Created
Location: /api/annotations/a1b2c3d4-e5f6-7890-abcd-ef1234567890
{
"@context": [
"http://www.w3.org/ns/anno.jsonld",
"https://cluster.research/ns/research.jsonld"
],
"id": "urn:uuid:a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"type": "Annotation",
"motivation": "highlighting",
"created": "2025-01-03T14:30:00Z",
"creator": {
"id": "urn:uuid:user-id",
"type": "Person",
"name": "Jane Smith"
},
"target": {
"source": "https://company.sharepoint.com/sites/research/interviews/p05.mp4",
"selector": {
"type": "FragmentSelector",
"conformsTo": "http://www.w3.org/TR/media-frags/",
"value": "t=120,145"
}
},
"research:study": "urn:uuid:study-id",
"research:participant": "P05"
}

Update Annotation

PUT /api/annotations/:id
Content-Type: application/ld+json; profile="http://www.w3.org/ns/anno.jsonld"

Request Body:

Full annotation object (replaces existing):

{
"@context": [
"http://www.w3.org/ns/anno.jsonld",
"https://cluster.research/ns/research.jsonld"
],
"type": "Annotation",
"motivation": ["highlighting", "tagging"],
"body": [
{
"type": "SpecificResource",
"source": "urn:uuid:tag-id",
"purpose": "tagging"
}
],
"target": {
"source": "https://company.sharepoint.com/sites/research/interviews/p05.mp4",
"selector": {
"type": "FragmentSelector",
"conformsTo": "http://www.w3.org/TR/media-frags/",
"value": "t=120,145"
}
}
}

Response:

HTTP/1.1 200 OK

Returns updated annotation.

Delete Annotation

DELETE /api/annotations/:id

Response:

HTTP/1.1 204 No Content

Selector Types

TextQuoteSelector

For selecting text in documents/transcripts:

{
"type": "TextQuoteSelector",
"exact": "the exact selected text",
"prefix": "50 chars before ",
"suffix": " 50 chars after"
}

TextPositionSelector

Fallback for text selection:

{
"type": "TextPositionSelector",
"start": 100,
"end": 150
}

FragmentSelector

For video/audio time ranges:

{
"type": "FragmentSelector",
"conformsTo": "http://www.w3.org/TR/media-frags/",
"value": "t=30.5,45.2"
}

Time format: t=start,end (seconds with decimals)

Motivations

Supported W3C motivations:

MotivationDescription
highlightingMark text/video segment
taggingApply tag to target
commentingAdd text comment
describingDescribe the target
linkingLink multiple targets
classifyingClassify into category
questioningPose a question
bookmarkingBookmark for later

Research Extensions

Cluster extends W3C annotations with research metadata:

{
"research:study": "urn:uuid:study-id",
"research:participant": "P05",
"research:session": "2025-01-03-morning"
}

Tagging

To add tags to an annotation, include them in the body:

{
"type": "Annotation",
"motivation": ["highlighting", "tagging"],
"body": [
{
"type": "SpecificResource",
"source": "urn:uuid:tag-id-1",
"purpose": "tagging"
},
{
"type": "SpecificResource",
"source": "urn:uuid:tag-id-2",
"purpose": "tagging"
}
],
"target": { ... }
}

Comments

To add a comment:

{
"type": "Annotation",
"motivation": ["highlighting", "commenting"],
"body": {
"type": "TextualBody",
"value": "This is a key moment in the interview",
"format": "text/plain",
"purpose": "commenting"
},
"target": { ... }
}

To link annotations across files:

{
"type": "Annotation",
"motivation": "linking",
"body": {
"type": "TextualBody",
"value": "Same pattern observed in both interviews",
"purpose": "describing"
},
"target": [
{
"source": "https://company.sharepoint.com/sites/research/interviews/p05.txt",
"selector": {
"type": "TextQuoteSelector",
"exact": "I didn't know what to do"
}
},
{
"source": "https://company.sharepoint.com/sites/research/interviews/p03.txt",
"selector": {
"type": "TextQuoteSelector",
"exact": "the next step wasn't clear"
}
}
]
}

Batch Operations

Create Multiple Annotations

POST /api/annotations/batch
Content-Type: application/json
{
"annotations": [
{ ... },
{ ... }
]
}

Response:

{
"created": 2,
"annotations": [
{ "id": "urn:uuid:...", ... },
{ "id": "urn:uuid:...", ... }
]
}

Delete Multiple Annotations

DELETE /api/annotations/batch
Content-Type: application/json
{
"ids": [
"a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"b2c3d4e5-f6a7-8901-bcde-f23456789012"
]
}
GET /api/annotations/search?q=confusion+onboarding

Response:

{
"total": 5,
"items": [
{
"id": "urn:uuid:...",
"score": 0.95,
"highlight": "...I was <mark>confused</mark> during <mark>onboarding</mark>..."
}
]
}
POST /api/annotations/search
Content-Type: application/json
{
"query": "confusion",
"filters": {
"studyId": "uuid",
"motivation": ["highlighting", "commenting"],
"tags": ["pain-point"],
"dateRange": {
"from": "2025-01-01",
"to": "2025-01-31"
}
},
"sort": {
"field": "created",
"order": "desc"
}
}

Next Steps