REST API

The Server AI Toolkit provides REST API endpoints for AI agent tools.

Backward compatibility

Previous API names such as schemaAwarenessData, getSchemaAwarenessData, /v3/ai/toolkit/tools, and /v3/ai/toolkit/schema-awareness-prompt are still available for backward compatibility, but they are deprecated. Use editorContext, getEditorContext, and /v3/ai/toolkit/tools for new integrations.

Postman collection

Browse the Postman collection for the Server AI Toolkit REST API.

Authentication

All requests must include:

  • Authorization: Bearer <JWT>
  • X-App-Id: <APP_ID>

Generate the JWT server-side with your AI secret. Get your App ID and secret key on the Server AI Toolkit settings page.

import jwt from 'jsonwebtoken'

const JWT_TOKEN = jwt.sign(
  {
    experimental_document_server_id: 'your-document-server-id',
    experimental_document_server_management_api_secret:
      'your-document-server-management-api-secret',
  },
  'your-ai-secret-key',
  { expiresIn: '1h' },
)

See the authorization guide for more information.

Document Server credentials

Include these JWT claims when you want the Server AI Toolkit to fetch and save Tiptap Cloud documents automatically:

  • experimental_document_server_id
  • experimental_document_server_management_api_secret

Base URL

The hosted base URL is:

https://api.tiptap.dev

In the examples below, requests are written as BASE_URL/v3/ai/....

How to provide the document

Tool endpoints accept exactly one document source:

  • document: Inline Tiptap JSON
  • experimental_documentOptions: A Tiptap Cloud document reference

experimental_documentOptions has the following shape:

  • documentId (string, required): The Tiptap Cloud document identifier.
  • userId? (string | null, optional): Identifier attributed to edits the AI performs.
  • field? (string | null, optional, default: "default"): Targets a specific collaborative field (Y.js XML fragment) inside the document when omitted or null. Use this when your document stores multiple editable fields — for example a separate title and body — under the same documentId.

Example with a Tiptap Cloud document:

{
  "experimental_documentOptions": {
    "documentId": "your-document-id",
    "userId": "ai-assistant"
  }
}

Example targeting a non-default collaborative field:

{
  "experimental_documentOptions": {
    "documentId": "your-document-id",
    "userId": "ai-assistant",
    "field": "title"
  }
}

Example with an inline document:

{
  "document": {
    "type": "doc",
    "content": []
  }
}

Threads require a cloud document

Thread and comment endpoints always require experimental_documentOptions.documentId.

Formats

Most endpoints accept a format field:

  • "json": Standard JSON representation
  • "shorthand": Tiptap Shorthand, a token-efficient format for prompts and model output

Tool endpoints

Get tool definitions

POST /v3/ai/toolkit/tools

Returns the available tool definitions for AI agents.

Request body:

  • editorContext (EditorContext, required). Editor context obtained from the getEditorContext function. See installation guide.
  • tools? (Record<string, boolean>, optional): Enable or disable individual tools. See the tools reference for the supported keys.
  • operationMeta? (string, optional, default: "")
  • format? ('json' | 'shorthand', optional, default: 'json')
  • schemaAwarenessData? (SchemaAwarenessData, optional, deprecated): Use editorContext instead. Supported for backward compatibility.

Response:

  • prompt (string): Add this to the system prompt for your AI request. It teaches the AI how the Tiptap document works, what elements it can contain, and how the document format works.
  • tools (array)
    • name (string)
    • description (string)
    • inputSchema (object): JSON schema for the AI-generated tool input.

Execute a tool

POST /v3/ai/toolkit/execute-tool

Executes one Server AI Toolkit tool such as tiptapRead, tiptapEdit, getThreads, or editThreads. See the tools reference for every supported tool and its type shapes.

Request body:

  • toolName (string, required)
  • input (required): Tool input generated by the AI model. The shape depends on toolName; see the tools reference.
  • editorContext (EditorContext, required unless schemaAwarenessData is provided)
  • schemaAwarenessData? (SchemaAwarenessData, optional, deprecated): Use editorContext instead. Supported for backward compatibility.
  • toolConfig? (optional): Tool-specific parameters provided by the developer, not by the AI model. See the tools reference.
  • format? ('json' | 'shorthand', optional, default: 'json')
  • reviewOptions? (ReviewOptions, optional, default: { mode: 'disabled' })
  • experimental_commentsOptions? ({ threadData?: Record<string, unknown>, commentData?: Record<string, unknown> }, optional): Metadata attached to new threads and comments created during thread-related tool execution.
  • exactly one of:
    • document
    • experimental_documentOptions

Response:

  • output: tool response that should be read by the AI model. See the tools reference.
  • toolResult: tool response in a format that is designed to be parsed by the developer. Should not be read by the AI. See the tools reference.
  • docChanged (boolean): whether the document changed as a result of the tool call
  • document (object | null): the document after the AI made changes to it

Stream a tool

POST /v3/ai/toolkit/stream-tool

Streams tool execution. The request shape matches POST /v3/ai/toolkit/execute-tool. The streaming response format is still being finalized.

Because this endpoint works on the document over a live connection, it can return connection and concurrency errors that the REST endpoints don't. They arrive as an error event in the response stream — see Streaming and session errors.

Legacy workflow endpoints

Workflow endpoints are deprecated

Workflow endpoints are preserved for existing integrations. They will be removed in a future release.

Get a workflow definition

Use one of these endpoints:

  • POST /v3/ai/toolkit/workflows/edit
  • POST /v3/ai/toolkit/workflows/insert-content
  • POST /v3/ai/toolkit/workflows/proofreader
  • POST /v3/ai/toolkit/workflows/threads

Request body:

  • format? ('json' | 'shorthand', optional, default: 'json'). The proofreader workflow only accepts 'shorthand'.

Response:

  • systemPrompt (string)
  • outputSchema (object)

Example:

curl --location 'BASE_URL/v3/ai/toolkit/workflows/edit' \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_JWT_TOKEN' \
  --header 'X-App-Id: YOUR_APP_ID' \
  --data '{
    "format": "shorthand"
  }'

Read document content for a workflow

POST /v3/ai/toolkit/read/read-document

Reads document content and returns workflow-ready content.

Request body:

  • range? ({ from: number, to: number }, optional, default: entire document)
  • editorContext (EditorContext, required unless schemaAwarenessData is provided)
  • schemaAwarenessData? (SchemaAwarenessData, optional, deprecated): Use editorContext instead. Supported for backward compatibility.
  • format? ('json' | 'shorthand', optional, default: 'json')
  • chunkSize? (number, optional, default: 32000)
  • reviewOptions? (ReviewOptions, optional, default: { mode: 'disabled' })
  • exactly one of:
    • document
    • experimental_documentOptions

Response:

  • output
    • success (boolean)
    • content? (unknown)
    • error? (string)
  • docChanged (boolean)
  • document (object | null)

Example:

curl --location 'BASE_URL/v3/ai/toolkit/read/read-document' \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_JWT_TOKEN' \
  --header 'X-App-Id: YOUR_APP_ID' \
  --data '{
    "editorContext": { /* editor context data */ },
    "format": "shorthand",
    "reviewOptions": {
      "mode": "disabled"
    },
    "experimental_documentOptions": {
      "documentId": "your-document-id",
      "userId": "ai-assistant"
    }
  }'

Read the current selection

POST /v3/ai/toolkit/read/read-selection

Reads the selection and returns either a selection payload or an explicit empty state.

Request body:

  • range ({ from: number, to: number }, required)
  • editorContext (EditorContext, required unless schemaAwarenessData is provided)
  • schemaAwarenessData? (SchemaAwarenessData, optional, deprecated): Use editorContext instead. Supported for backward compatibility.
  • format? ('json' | 'shorthand', optional, default: 'json')
  • chunkSize? (number, optional, default: 32000)
  • reviewOptions? (ReviewOptions, optional, default: { mode: 'disabled' })
  • exactly one of:
    • document
    • experimental_documentOptions

Response:

  • output
    • when the selection is empty:
      • isEmpty (true)
    • when the selection contains content:
      • isEmpty (false)
      • content (unknown)
      • nodeHashes (string[])
      • nodeRange ({ from: number, to: number })
      • prompt (string)
  • docChanged (boolean)
  • document (object | null)

Example:

curl --location 'BASE_URL/v3/ai/toolkit/read/read-selection' \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_JWT_TOKEN' \
  --header 'X-App-Id: YOUR_APP_ID' \
  --data '{
    "editorContext": { /* editor context data */ },
    "range": { "from": 10, "to": 42 },
    "format": "shorthand",
    "experimental_documentOptions": {
      "documentId": "your-document-id"
    }
  }'

Read threads

POST /v3/ai/toolkit/read/threads

Reads all threads and comments for a Tiptap Cloud document.

Request body:

  • editorContext (EditorContext, required unless schemaAwarenessData is provided)
  • schemaAwarenessData? (SchemaAwarenessData, optional, deprecated): Use editorContext instead. Supported for backward compatibility.
  • format ('json' | 'shorthand', required)
  • experimental_documentOptions ({ documentId: string, userId?: string, field?: string | null }, required)

Response:

  • output
    • threads? (unknown[])
    • error? (string)
  • docChanged (boolean)
  • document (object | null)

Example:

curl --location 'BASE_URL/v3/ai/toolkit/read/threads' \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_JWT_TOKEN' \
  --header 'X-App-Id: YOUR_APP_ID' \
  --data '{
    "editorContext": { /* editor context data */ },
    "format": "shorthand",
    "experimental_documentOptions": {
      "documentId": "your-document-id",
      "userId": "ai-assistant"
    }
  }'

Execute the insert-content workflow

POST /v3/ai/toolkit/execute-workflow/insert-content

Request body:

  • input (unknown, required): Generated insert-content payload
  • range? ({ from: number, to: number }, optional)
  • editorContext (EditorContext, required unless schemaAwarenessData is provided)
  • schemaAwarenessData? (SchemaAwarenessData, optional, deprecated): Use editorContext instead. Supported for backward compatibility.
  • format? ('json' | 'shorthand', optional, default: 'json')
  • chunkSize? (number, optional, default: 32000)
  • reviewOptions? (ReviewOptions, optional, default: { mode: 'disabled' })
  • exactly one of:
    • document
    • experimental_documentOptions

Response:

  • output
    • error? (string)
    • range? ({ from: number, to: number })
  • docChanged (boolean)
  • document (object | null)

Example:

curl --location 'BASE_URL/v3/ai/toolkit/execute-workflow/insert-content' \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_JWT_TOKEN' \
  --header 'X-App-Id: YOUR_APP_ID' \
  --data '{
    "editorContext": { /* editor context data */ },
    "input": "This is the replacement content.",
    "range": { "from": 10, "to": 42 },
    "format": "shorthand",
    "reviewOptions": {
      "mode": "disabled"
    },
    "experimental_documentOptions": {
      "documentId": "your-document-id",
      "userId": "ai-assistant"
    }
  }'

Execute the Tiptap Edit workflow

POST /v3/ai/toolkit/execute-workflow/tiptap-edit

Request body:

  • input (object, required): Edit workflow operations
  • editorContext (EditorContext, required unless schemaAwarenessData is provided)
  • schemaAwarenessData? (SchemaAwarenessData, optional, deprecated): Use editorContext instead. Supported for backward compatibility.
  • format? ('json' | 'shorthand', optional, default: 'json')
  • chunkSize? (number, optional, default: 32000)
  • reviewOptions? (ReviewOptions, optional, default: { mode: 'disabled' })
  • exactly one of:
    • document
    • experimental_documentOptions

Response:

  • output
    • operationResults? (array)
    • reason? ('validationError' | 'unexpectedError')
    • error? (string)
  • docChanged (boolean)
  • document (object | null)

Example:

curl --location 'BASE_URL/v3/ai/toolkit/execute-workflow/tiptap-edit' \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_JWT_TOKEN' \
  --header 'X-App-Id: YOUR_APP_ID' \
  --data '{
    "editorContext": { /* editor context data */ },
    "input": {
      "operations": [
        {
          "type": "replace",
          "target": "abc123",
          "content": "# Updated heading\n\nUpdated paragraph content."
        }
      ]
    },
    "format": "shorthand",
    "reviewOptions": {
      "mode": "disabled"
    },
    "experimental_documentOptions": {
      "documentId": "your-document-id",
      "userId": "ai-assistant"
    }
  }'

Execute the proofreader workflow

POST /v3/ai/toolkit/execute-workflow/proofreader

Request body:

  • input (object, required): Proofreader operations
  • editorContext (EditorContext, required unless schemaAwarenessData is provided)
  • schemaAwarenessData? (SchemaAwarenessData, optional, deprecated): Use editorContext instead. Supported for backward compatibility.
  • format ('shorthand', required)
  • chunkSize? (number, optional, default: 32000)
  • reviewOptions? (ReviewOptions, optional, default: { mode: 'disabled' })
  • exactly one of:
    • document
    • experimental_documentOptions

Response:

  • output
    • operationResults (array)
  • docChanged (boolean)
  • document (object | null)

Example:

curl --location 'BASE_URL/v3/ai/toolkit/execute-workflow/proofreader' \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_JWT_TOKEN' \
  --header 'X-App-Id: YOUR_APP_ID' \
  --data '{
    "editorContext": { /* editor context data */ },
    "input": {
      "operations": []
    },
    "format": "shorthand",
    "reviewOptions": {
      "mode": "trackedChanges",
      "trackedChangesOptions": {
        "userId": "ai-assistant"
      }
    },
    "experimental_documentOptions": {
      "documentId": "your-document-id"
    }
  }'

Execute the comments workflow

POST /v3/ai/toolkit/execute-workflow/threads

Request body:

  • input (object, required): Thread operations
  • editorContext (EditorContext, required unless schemaAwarenessData is provided)
  • schemaAwarenessData? (SchemaAwarenessData, optional, deprecated): Use editorContext instead. Supported for backward compatibility.
  • format? ('json' | 'shorthand', optional, default: 'json')
  • experimental_documentOptions ({ documentId: string, userId?: string, field?: string | null }, required)
  • experimental_commentsOptions? ({ threadData?: Record<string, unknown>, commentData?: Record<string, unknown> }, optional): Metadata attached to new threads and comments created by the workflow.

Response:

  • output
    • operations? (array)
    • error? (string)
  • docChanged (boolean)
  • document (object | null)

Example:

curl --location 'BASE_URL/v3/ai/toolkit/execute-workflow/threads' \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_JWT_TOKEN' \
  --header 'X-App-Id: YOUR_APP_ID' \
  --data '{
    "editorContext": { /* editor context data */ },
    "input": {
      "operations": [
        {
          "type": "createComment",
          "threadId": "thread-123",
          "content": "Please clarify this point."
        }
      ]
    },
    "format": "shorthand",
    "experimental_documentOptions": {
      "documentId": "your-document-id",
      "userId": "ai-assistant"
    },
    "experimental_commentsOptions": {
      "threadData": {
        "source": "ai"
      },
      "commentData": {
        "source": "ai"
      }
    }
  }'

Error handling

REST endpoints return errors with this shape:

{
  "error": {
    "message": "Human-readable description of what went wrong.",
    "status": 502,
    "code": "document_load_failed"
  }
}
  • message (string): Human-readable description. Don't match on it — the wording can change.
  • status (number): The HTTP status code, also set on the response.
  • code (string): A stable identifier you can branch on. Validation failures use code: "validation_failed" and add an issues array.

The API uses standard HTTP error codes:

  • 400 Bad Request: Invalid body, invalid format, missing document source, or missing Document Server credentials
  • 401 Unauthorized: Missing or invalid JWT or App ID
  • 404 Not Found: Unknown endpoint or tool
  • 422 Unprocessable Entity: Validation failed
  • 429 Too Many Requests: Duplicate request or rate-limit protection
  • 500 Internal Server Error: Unexpected server error
  • 502 Bad Gateway: Failed to load or save the Tiptap Cloud document

Streaming and session errors

POST /v3/ai/toolkit/stream-tool works on a Tiptap Cloud document over a live connection: it joins the document in real time so the AI edits the latest content alongside other collaborators. Errors that happen while establishing or using that connection are delivered in-band as an error event in the response stream, not as an HTTP status:

{ "type": "error", "code": "concurrent_edit_conflict", "status": 409, "message": "..." }

The status field mirrors the equivalent HTTP status. Possible code values:

codestatusWhen it happensHow to handle
concurrent_edit_conflict409The document changed (for example, a user edited it) while the tool was running, so the change couldn't be applied on top of stale content.Re-run the request. The AI reads the latest content on the next attempt. Safe to retry automatically. See Handle concurrent edits.
schema_mismatch409The editor schema in the request doesn't match the schema already in use for this document, or the stored document can't be parsed with it.Send a consistent editorContext schema for the same document. Don't retry with the same schema.
websocket_auth_failed401Tiptap Cloud rejected the document credentials when opening the connection.Refresh the Document Server JWT claims, then retry. Don't retry with the same token.
websocket_connection_failed502The connection to Tiptap Cloud closed before the initial sync completed.Retry with backoff. Check connectivity and Tiptap Cloud status.
websocket_sync_timeout504The document didn't finish its initial sync within the time limit.Retry with backoff. Large documents and degraded connectivity make this more likely.
session_limit_reached503The server is handling the maximum number of concurrent document sessions.Retry with backoff. Sessions are released as they go idle.
session_creation_cancelled503The session was torn down before it finished connecting, usually during a server restart.Retry.

Specific to Tiptap Cloud documents

These errors only occur on the live-connection path used by stream-tool. Requests that send an inline document never open a connection, so they don't return session or WebSocket errors.

Handle concurrent edits

concurrent_edit_conflict is expected in collaborative documents. While the AI reads a document, calls the model, and writes its changes, a user can edit the same document in between. Rather than overwrite that edit, the toolkit rejects the write so no work is lost.

Treat it as a safe, retriable signal: when you receive an error event with code: "concurrent_edit_conflict", run the same tool call again. The toolkit re-reads the document, so the AI works from the user's latest changes. Cap the number of retries to avoid long loops on a document that's being edited heavily.

// `runStream` sends the start/delta/end messages and resolves to the stream's
// final `error` event, or `null` when the stream completes successfully.
async function streamToolWithRetry(body: unknown, maxRetries = 3) {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    const error = await runStream(body)

    if (!error) {
      return // completed successfully
    }
    if (error.code !== 'concurrent_edit_conflict') {
      throw new Error(`stream-tool failed: ${error.code}`)
    }
    // Document changed mid-flight — retry with the latest content.
  }

  throw new Error('Tool call kept conflicting with concurrent edits.')
}