Skip to main content

Clusters API

Clusters (affinity groups) are collections of annotations organized thematically. They support the synthesis process by allowing researchers to group related findings.

W3C Compliance

Clusters are implemented as W3C Annotation Collections, making them fully portable:

{
"@context": [
"http://www.w3.org/ns/anno.jsonld",
"https://cluster.research/ns/research.jsonld"
],
"type": ["AnnotationCollection", "research:AffinityGroup"],
"label": "Onboarding Confusion"
}

Endpoints

List Clusters

GET /api/clusters

Query Parameters:

ParameterTypeDescription
studyIduuidFilter by study
boardIduuidFilter by synthesis board

Response:

{
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Onboarding Confusion",
"color": "#e74c3c",
"studyId": "study-uuid",
"boardId": "board-uuid",
"position": {
"x": 100,
"y": 200
},
"size": {
"width": 300,
"height": 400
},
"annotationCount": 8,
"createdBy": {
"id": "user-uuid",
"name": "Jane Smith"
},
"createdAt": "2025-01-03T14:00:00Z",
"updatedAt": "2025-01-03T15:30:00Z"
}
]
}

Get Cluster

GET /api/clusters/:id

Response:

{
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Onboarding Confusion",
"color": "#e74c3c",
"studyId": "study-uuid",
"position": {
"x": 100,
"y": 200
},
"annotations": [
{
"id": "annotation-uuid-1",
"position": { "x": 10, "y": 20 },
"preview": "I had no idea what to do...",
"participant": "P01"
},
{
"id": "annotation-uuid-2",
"position": { "x": 10, "y": 80 },
"preview": "The next step wasn't clear...",
"participant": "P03"
}
]
}
}

Create Cluster

POST /api/clusters
Content-Type: application/json

Request:

{
"name": "Navigation Issues",
"color": "#3498db",
"studyId": "study-uuid",
"boardId": "board-uuid",
"position": {
"x": 400,
"y": 100
}
}

Response:

HTTP/1.1 201 Created
Location: /api/clusters/660e8400-e29b-41d4-a716-446655440000
{
"data": {
"id": "660e8400-e29b-41d4-a716-446655440000",
"name": "Navigation Issues",
"color": "#3498db",
"annotationCount": 0
}
}

Update Cluster

PATCH /api/clusters/:id
Content-Type: application/json

Request:

{
"name": "Navigation & Wayfinding Issues",
"color": "#2980b9",
"position": {
"x": 450,
"y": 150
},
"size": {
"width": 350,
"height": 500
}
}

Delete Cluster

DELETE /api/clusters/:id

Annotations in the cluster are removed from the cluster but not deleted.

Response:

HTTP/1.1 204 No Content

Cluster Annotations

Add Annotation to Cluster

POST /api/clusters/:id/annotations
Content-Type: application/json

Request:

{
"annotationId": "annotation-uuid",
"position": {
"x": 20,
"y": 100
}
}

Response:

{
"data": {
"clusterId": "cluster-uuid",
"annotationId": "annotation-uuid",
"position": { "x": 20, "y": 100 },
"sortOrder": 3
}
}

Remove Annotation from Cluster

DELETE /api/clusters/:id/annotations/:annotationId

Response:

HTTP/1.1 204 No Content

Reorder Annotations

PUT /api/clusters/:id/annotations/order
Content-Type: application/json

Request:

{
"order": [
{ "annotationId": "uuid-1", "sortOrder": 0 },
{ "annotationId": "uuid-2", "sortOrder": 1 },
{ "annotationId": "uuid-3", "sortOrder": 2 }
]
}

Move Annotation Position

PATCH /api/clusters/:id/annotations/:annotationId
Content-Type: application/json

Request:

{
"position": {
"x": 50,
"y": 120
}
}

Synthesis Boards

Boards are canvases that contain clusters for visual organization.

List Boards

GET /api/studies/:studyId/boards

Response:

{
"data": [
{
"id": "board-uuid",
"name": "Main Synthesis",
"studyId": "study-uuid",
"clusterCount": 5,
"createdAt": "2025-01-03T10:00:00Z"
}
]
}

Create Board

POST /api/studies/:studyId/boards
Content-Type: application/json

Request:

{
"name": "Thematic Analysis"
}

Get Board with Clusters

GET /api/boards/:id

Response:

{
"data": {
"id": "board-uuid",
"name": "Main Synthesis",
"clusters": [
{
"id": "cluster-1",
"name": "Onboarding Confusion",
"color": "#e74c3c",
"position": { "x": 100, "y": 200 },
"size": { "width": 300, "height": 400 },
"annotationCount": 8
},
{
"id": "cluster-2",
"name": "Navigation Issues",
"color": "#3498db",
"position": { "x": 450, "y": 150 },
"size": { "width": 280, "height": 350 },
"annotationCount": 5
}
]
}
}

Batch Operations

Move Multiple Annotations

Move annotations between clusters:

POST /api/clusters/batch/move
Content-Type: application/json

Request:

{
"annotationIds": ["uuid-1", "uuid-2", "uuid-3"],
"fromClusterId": "cluster-a",
"toClusterId": "cluster-b"
}

Merge Clusters

Combine two clusters into one:

POST /api/clusters/:id/merge
Content-Type: application/json

Request:

{
"sourceClusterId": "cluster-to-merge",
"keepPositions": false
}

The source cluster is deleted after merge.

Split Cluster

Create a new cluster with selected annotations:

POST /api/clusters/:id/split
Content-Type: application/json

Request:

{
"name": "New Cluster",
"color": "#9b59b6",
"annotationIds": ["uuid-1", "uuid-2"]
}

Response:

{
"data": {
"newCluster": {
"id": "new-cluster-uuid",
"name": "New Cluster",
"annotationCount": 2
},
"originalCluster": {
"id": "original-uuid",
"annotationCount": 6
}
}
}

W3C Export

Export Cluster as Collection

GET /api/clusters/:id/export
Accept: application/ld+json; profile="http://www.w3.org/ns/anno.jsonld"

Response:

{
"@context": [
"http://www.w3.org/ns/anno.jsonld",
"https://cluster.research/ns/research.jsonld"
],
"id": "urn:uuid:cluster-id",
"type": ["AnnotationCollection", "research:AffinityGroup"],
"label": "Onboarding Confusion",
"total": 8,
"first": {
"type": "AnnotationPage",
"items": [
{
"id": "urn:uuid:annotation-1",
"type": "Annotation",
"motivation": "highlighting",
"target": {
"source": "https://company.sharepoint.com/...",
"selector": {
"type": "TextQuoteSelector",
"exact": "I had no idea what to do..."
}
}
}
]
}
}

Real-time Updates

Cluster changes can be synced in real-time for collaborative synthesis.

WebSocket Connection

const ws = new WebSocket('wss://cluster.yourcompany.com/api/boards/:id/ws');

ws.onmessage = (event) => {
const update = JSON.parse(event.data);
// { type: 'cluster.moved', clusterId: '...', position: {...} }
// { type: 'annotation.added', clusterId: '...', annotationId: '...' }
};

Event Types

EventDescription
cluster.createdNew cluster added
cluster.updatedCluster properties changed
cluster.deletedCluster removed
cluster.movedCluster position changed
annotation.addedAnnotation added to cluster
annotation.removedAnnotation removed from cluster
annotation.movedAnnotation position changed

Auto-Clustering (Future)

AI-assisted clustering (planned feature):

POST /api/studies/:id/auto-cluster
Content-Type: application/json

Request:

{
"method": "semantic",
"minClusterSize": 3,
"maxClusters": 10
}

Response:

{
"data": {
"suggestedClusters": [
{
"name": "Suggested: Onboarding Issues",
"annotations": ["uuid-1", "uuid-2", "uuid-3"],
"confidence": 0.85
}
]
}
}

Next Steps