Comments

Build a workflow that creates and updates comment threads on a collaborative document.

See the source code on GitHub.

Comments workflows require Tiptap Cloud

The threads workflow reads and writes comments stored on the Tiptap Document Server, so you must use experimental_documentOptions.documentId.

Reuse the document read session for thread edits

read-document returns a sessionId. Send that same sessionId with the execute request so the server can reject thread operations that target content the AI read before a user changed it.

1. Read the document and existing threads from the AI Server

The comments workflow needs both the current document content and the existing thread list. Call:

  • POST /v3/ai/toolkit/read/read-document
  • POST /v3/ai/toolkit/read/threads
const documentResponse = await fetch(`${apiBaseUrl}/toolkit/read/read-document`, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    Authorization: `Bearer ${jwtToken}`,
    'X-App-Id': appId,
  },
  body: JSON.stringify({
    schemaAwarenessData,
    format: 'shorthand',
    sessionId,
    reviewOptions: {
      mode: 'disabled',
    },
    experimental_documentOptions: {
      documentId,
      userId: 'ai-assistant',
    },
  }),
})

const documentReadResult = await documentResponse.json()

const threadsResponse = await fetch(`${apiBaseUrl}/toolkit/read/threads`, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    Authorization: `Bearer ${jwtToken}`,
    'X-App-Id': appId,
  },
  body: JSON.stringify({
    schemaAwarenessData,
    format: 'shorthand',
    experimental_documentOptions: {
      documentId,
      userId: 'ai-assistant',
    },
  }),
})

const threadsReadResult = await threadsResponse.json()

2. Generate thread operations

Get the workflow definition from the AI Server and ask the model to return thread operations.

const workflowResponse = await fetch(`${apiBaseUrl}/toolkit/workflows/threads`, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    Authorization: `Bearer ${jwtToken}`,
    'X-App-Id': appId,
  },
  body: JSON.stringify({
    format: 'shorthand',
  }),
})

const workflow = await workflowResponse.json()

const schemaResponse = await fetch(`${apiBaseUrl}/toolkit/schema-awareness-prompt`, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    Authorization: `Bearer ${jwtToken}`,
    'X-App-Id': appId,
  },
  body: JSON.stringify({
    schemaAwarenessData,
  }),
})

const { prompt: schemaAwarenessPrompt } = await schemaResponse.json()

const result = streamText({
  model,
  system: `${workflow.systemPrompt}\n\n${schemaAwarenessPrompt}`,
  prompt: JSON.stringify({
    content: documentReadResult.output.content,
    threads: threadsReadResult.output.threads ?? [],
    task,
  }),
  output: Output.object({ schema: z.fromJSONSchema(workflow.outputSchema) }),
})

3. Execute the workflow

The threads workflow output can be sent directly to the execute endpoint. Then call:

  • POST /v3/ai/toolkit/execute-workflow/threads
const output = await result.output

const executeResponse = await fetch(`${apiBaseUrl}/toolkit/execute-workflow/threads`, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    Authorization: `Bearer ${jwtToken}`,
    'X-App-Id': appId,
  },
  body: JSON.stringify({
    schemaAwarenessData,
    format: 'shorthand',
    input: output,
    sessionId: documentReadResult.sessionId,
    experimental_documentOptions: {
      documentId,
      userId: 'ai-assistant',
    },
    experimental_commentsOptions: {
      threadData: { userName: 'Tiptap AI' },
      commentData: { userName: 'Tiptap AI' },
    },
  }),
})

const executeResult = await executeResponse.json()

4. Connect the workflow to the comments UI

The client sends a task, waits for the workflow response, and lets the comments extension render the updated thread state from the collaborative document.

const response = await fetch('/api/server-comments-workflow', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    documentId,
    schemaAwarenessData: getSchemaAwarenessData(editor),
    task,
    sessionId,
  }),
})

const result: { sessionId: string } = await response.json()
setSessionId(result.sessionId)

End result

The result is a server-side comments workflow that can create, update, resolve, and remove threads:

See the source code on GitHub.

Next steps