---
title: "Inject content REST API"
description: "Manage your Collaboration documents with JSON updates using the Inject Content API. Learn more in our docs!"
canonical_url: "https://tiptap.dev/docs/collaboration/documents/content-injection"
---

# Inject content REST API

Manage your Collaboration documents with JSON updates using the Inject Content API. Learn more in our docs!

To inject content into documents server-side, use the PATCH endpoint described in this document. This feature supports version history, tracking changes as well as content added through this endpoint.

- **1. Activate trial or subscribe**

  Start a [free trial](https://cloud.tiptap.dev/v2?trial=true) or [subscribe to the Team plan](https://cloud.tiptap.dev/v2/billing) in your account.
- **2. Start Document server**

  [Add an Environment](https://cloud.tiptap.dev/v2/configuration/document-server) in your dashboard and configure your [Document server](https://cloud.tiptap.dev/v2/configuration/document-server).

The update document endpoint also allows JSON updates to modify documents on your Collaboration server, both On-Premises and Cloud:

- Add `json`, `binary`, or `base64` content to any document server-side.
- Inject content into specific nodes using the [UniqueID extension](https://tiptap.dev/docs/editor/extensions/functionality/uniqueid.md).
- Users can still collaborate in real-time as content is injected.
- Track user and injected content changes, fully compatible with [Snapshots](https://tiptap.dev/docs/collaboration/documents/snapshot.md).

### Use cases

The content injection REST API enables a couple of handy but sophisticated use cases:

- Live translation of document content.
- Programmatically tagging or manipulating document content server-side.
- Integrating server-side components, like executing SQL queries and displaying results.
- Version history integration and conflict-free merging of concurrent edits.

## Create a document

To seed a new document on the Tiptap Collab server, use the `POST` method with the following endpoint:

```bash
POST /api/documents/:identifier?format=:format
```

The server will return HTTP status `204` for successful creation, `409` if the document already exists (you must delete it first to overwrite), and `422` if the action failed.

The `format` parameter accepts the same values as the update endpoint (`binary`, `base64`, or `json`).

## Update a document

To update an existing document on the Collaboration server, you can use the `PATCH` method with the following API endpoint:

```bash
PATCH /api/documents/:identifier?format=:format
```

This endpoint accepts a Yjs update message and applies it to the specified document.

### Query parameters

| Parameter            | Type       | Default   | Description                                                                                                                                                                                                                                   |
| -------------------- | ---------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `format`             | `string`   | —         | The format of the update: `json`, `binary` (Yjs's `Y.encodeStateAsUpdate`), or `base64` (binary state encoded as Base64).                                                                                                                     |
| `mode`               | `string`   | `default` | The update strategy: `default` merges nodes, `append` appends only, `node` replaces matched nodes with the payload, `attrs` replaces attributes of matched nodes, `delete` deletes matched nodes (no payload needed). See [modes](#modes).    |
| `nodeAttributeName`  | `string`   | —         | Limit the update to nodes with an attribute of this name. Used together with `nodeAttributeValue`.                                                                                                                                            |
| `nodeAttributeValue` | `string`   | —         | Limit the update to nodes with an attribute of this value. Used together with `nodeAttributeName`. You can pass multiple values: `?nodeAttributeValue=a&nodeAttributeValue=b`.                                                                |
| `checksum`           | `string`   | —         | Only apply the update if the document's current state matches this checksum (returned as `x-FRAGMENTNAME-checksum` in the get document response). Returns `409 Checksum mismatch` otherwise.                                                  |
| `user`               | `string`   | —         | User ID to associate with the change.                                                                                                                                                                                                         |
| `skipVersioning`     | `1`        | —         | If passed, auto versioning won't be triggered by this change.                                                                                                                                                                                 |
| `fragment`           | `string`   | `default` | Yjs fragment to update. You can update multiple fragments with the same content (when `format=json`) by passing multiple values: `?fragment=a&fragment=b`.                                                                                    |
| `mergeAttributes`    | `0` \| `1` | `0`       | Only used with `mode=attrs`. When `1`, attributes not included in the request payload are preserved. When `0` (default), missing attributes are deleted.                                                                                      |
| `multiUpdates`       | `true`     | —         | Only used with `mode=attrs` and `format=json`. Enables updating multiple nodes with different attribute values in a single request. The request body must be a JSON array of `{ where, attrs }` objects. See [Multi updates](#multi-updates). |

Upon successful update, the server will return HTTP status `204`. If the document does not exist, it will return `404`, and if the payload is invalid or the update cannot be applied, it will return `422`.

**Example:** `curl` command to update a document

```bash
curl --location --request PATCH 'https://YOUR_APP_ID.collab.tiptap.cloud/api/documents/DOCUMENT_NAME' \\
--header 'Authorization: YOUR_SECRET_FROM_SETTINGS_AREA' \\
--data '@yjsUpdate.binary'
```

## Update via JSON

When updating via JSON, the server computes the difference between the current document state and the provided JSON, then internally calculates the required Yjs update to reach the target state.

To ensure precise updates, especially for node-specific changes, it is recommended to use the `nodeAttributeName` and `nodeAttributeValue` parameters. These can be generated by Tiptap's [UniqueID Extension](https://tiptap.dev/docs/editor/extensions/functionality/uniqueid.md) or a custom implementation.
Note that this only works for top level nodes when using mode=default.

Omitting these parameters may result in overwriting any updates made between fetching the document and issuing the update call. The `get document` call returns a header `x-${fragmentName}-checksum` which can be used to detect conflicts by passing
it to the update call as `?checksum=${checksum}`. If the document has been updated since the last fetch, the update will fail with a `409 Checksum mismatch.` status.

### Modes

The `mode` parameter controls how content is applied to the document:

#### Default mode

Merges nodes from the payload into the document. This is the default behavior when no mode is specified.

```bash
curl --location --request PATCH 'https://YOUR_APP_ID.collab.tiptap.cloud/api/documents/my-doc?format=json' \
--header 'Authorization: YOUR_SECRET' \
--header 'Content-Type: application/json' \
--data '{
  "type": "doc",
  "content": [
    {
      "type": "paragraph",
      "content": [{ "type": "text", "text": "Merged content" }]
    }
  ]
}'
```

#### Append mode

Appends nodes to the document's JSON representation without altering existing nodes.

```bash
curl --location --request PATCH 'https://YOUR_APP_ID.collab.tiptap.cloud/api/documents/my-doc?format=json&mode=append' \
--header 'Authorization: YOUR_SECRET' \
--header 'Content-Type: application/json' \
--data '{
  "type": "doc",
  "content": [
    {
      "type": "paragraph",
      "content": [{ "type": "text", "text": "This paragraph is appended at the end" }]
    }
  ]
}'
```

#### Node mode

Replaces the node matching `nodeAttributeName`/`nodeAttributeValue` with the node in the payload.

```bash
curl --location --request PATCH 'https://YOUR_APP_ID.collab.tiptap.cloud/api/documents/my-doc?format=json&mode=node&nodeAttributeName=id&nodeAttributeValue=abc-123' \
--header 'Authorization: YOUR_SECRET' \
--header 'Content-Type: application/json' \
--data '{
  "type": "paragraph",
  "attrs": { "id": "abc-123" },
  "content": [{ "type": "text", "text": "This replaces the entire matched node" }]
}'
```

#### Attrs mode

Replaces attributes of all matched nodes with the request payload (send just the attribute object). See [Update only node attrs](#update-only-node-attrs) for details on `mergeAttributes`.

```bash
curl --location --request PATCH 'https://YOUR_APP_ID.collab.tiptap.cloud/api/documents/my-doc?format=json&mode=attrs&nodeAttributeName=id&nodeAttributeValue=abc-123' \
--header 'Authorization: YOUR_SECRET' \
--header 'Content-Type: application/json' \
--data '{
  "textAlign": "center",
  "indent": 2
}'
```

#### Delete mode

Deletes nodes matching `nodeAttributeName`/`nodeAttributeValue`. No payload is needed.

```bash
curl --location --request PATCH 'https://YOUR_APP_ID.collab.tiptap.cloud/api/documents/my-doc?format=json&mode=delete&nodeAttributeName=id&nodeAttributeValue=abc-123' \
--header 'Authorization: YOUR_SECRET'
```

### Useful parameter combinations

#### Track who made a change

Associate a user ID with the injected content, useful for audit trails and version history.

```bash
curl --location --request PATCH 'https://YOUR_APP_ID.collab.tiptap.cloud/api/documents/my-doc?format=json&user=bot-user-1' \
--header 'Authorization: YOUR_SECRET' \
--header 'Content-Type: application/json' \
--data '{
  "type": "doc",
  "content": [
    {
      "type": "paragraph",
      "content": [{ "type": "text", "text": "Injected by bot" }]
    }
  ]
}'
```

#### Skip auto versioning

Inject content without creating a new version snapshot.

```bash
curl --location --request PATCH 'https://YOUR_APP_ID.collab.tiptap.cloud/api/documents/my-doc?format=json&skipVersioning=1' \
--header 'Authorization: YOUR_SECRET' \
--header 'Content-Type: application/json' \
--data '{
  "type": "doc",
  "content": [
    {
      "type": "paragraph",
      "content": [{ "type": "text", "text": "Silent update" }]
    }
  ]
}'
```

#### Update a specific fragment

Target a specific Yjs fragment instead of the default one, or update multiple fragments at once.

```bash
# Update a single fragment
curl --location --request PATCH 'https://YOUR_APP_ID.collab.tiptap.cloud/api/documents/my-doc?format=json&fragment=sidebar' \
--header 'Authorization: YOUR_SECRET' \
--header 'Content-Type: application/json' \
--data '{
  "type": "doc",
  "content": [
    {
      "type": "paragraph",
      "content": [{ "type": "text", "text": "Sidebar content" }]
    }
  ]
}'
```

```bash
# Update multiple fragments with the same content
curl --location --request PATCH 'https://YOUR_APP_ID.collab.tiptap.cloud/api/documents/my-doc?format=json&fragment=header&fragment=footer' \
--header 'Authorization: YOUR_SECRET' \
--header 'Content-Type: application/json' \
--data '{
  "type": "doc",
  "content": [
    {
      "type": "paragraph",
      "content": [{ "type": "text", "text": "Shared content" }]
    }
  ]
}'
```

#### Conflict-safe update with checksum

Only apply the update if the document hasn't changed since you last fetched it.

```bash
# 1. Fetch the document and note the checksum header (x-default-checksum). The header is present when fetching the document with ?format=json. If you want to get the checksum for a different fragment, send &fragment=customfragmentname
# 2. Use the checksum in the update call
curl --location --request PATCH 'https://YOUR_APP_ID.collab.tiptap.cloud/api/documents/my-doc?format=json&checksum=THE_CHECKSUM_VALUE' \
--header 'Authorization: YOUR_SECRET' \
--header 'Content-Type: application/json' \
--data '{
  "type": "doc",
  "content": [
    {
      "type": "paragraph",
      "content": [{ "type": "text", "text": "Only applied if document unchanged" }]
    }
  ]
}'
```

#### Merge attributes without deleting existing ones

Update specific attributes on matched nodes while preserving all other existing attributes.

```bash
curl --location --request PATCH 'https://YOUR_APP_ID.collab.tiptap.cloud/api/documents/my-doc?format=json&mode=attrs&nodeAttributeName=id&nodeAttributeValue=abc-123&mergeAttributes=1' \
--header 'Authorization: YOUR_SECRET' \
--header 'Content-Type: application/json' \
--data '{
  "textAlign": "center"
}'
```

### Full example

**Example:** Updating a document using JSON

```typescript
// Define the document name, secret, and application ID
const docName = '' // URI-encoded if necessary
const secret = ''
const appId = ''; // Your document server ID from the Cloud dashboard

// Construct the base URL
const url = `https://${appId}.collab.tiptap.cloud`

// Fetch the current document's JSON representation
const docJson = await axios.get(`${url}/api/documents/${docName}?format=json`, {
    headers: {
    Authorization: secret
  },
})

// Extract the document's JSON content
const tiptapJson = docJson.data
const nodes = tiptapJson.content

// Find and log specific nodes using their unique identifiers
const query = nodes.find(n => n.attrs?.identifier === 'fe5c0789-85d9-4877-a2c3-bccf5d874866').content[0].text
const resultTable = nodes.find(n => n.attrs?.identifier === '246368b6-0746-4ca1-a16f-8d964aff4041')

console.log(`Query: ${query}`)
console.log(JSON.stringify(resultTable.content))

// Append new content to the result table node
resultTable.content.push({
  // New table row content here
  {
    "type": "tableRow",
    "content": [
      {
        "type": "tableCell",
        "attrs": {
          "colspan": 1,
          "rowspan": 1
        },
        "content": [
          {
            "type": "paragraph",
            "attrs": {
              "textAlign": "left"
            },
            "content": [
              {
                "type": "text",
                "text": "Jan"
              }
            ]
          }
        ]
      },
      {
        "type": "tableCell",
        "attrs": {
          "colspan": 1,
          "rowspan": 1
        },
        "content": [
          {
            "type": "paragraph",
            "attrs": {
              "textAlign": "left"
            },
            "content": [
              {
                "type": "text",
                "text": "Thurau"
              }
            ]
          }
        ]
      },
      {
        "type": "tableCell",
        "attrs": {
          "colspan": 1,
          "rowspan": 1
        },
        "content": [
          {
            "type": "paragraph",
            "attrs": {
              "textAlign": "left"
            },
            "content": [
              {
                "type": "text",
                "text": "jan@janthurau.de"
              }
            ]
          }
        ]
      }
    ]
  }
})

// Send the updated JSON back to the server to apply the changes
await axios.patch(`${url}/api/documents/${docName}?format=json`, tiptapJson, {
  headers: {
    Authorization: secret
  }
})
```

### Update only node attrs

If you want to only update attributes of a node, you can use the `?mode=attrs` query parameter. This will only update the attributes of the node and not its content.
In this mode, the `nodeAttributeName` and `nodeAttributeValue` parameters work for any (not just top level) nodes.

By default, all attributes on matched nodes are replaced — attributes not included in the payload are deleted. To preserve existing attributes and only update those specified in the payload, pass `?mergeAttributes=1`.

Not specifying a node filter (`nodeAttributeName`, `nodeAttributeValue`) will result in all nodes being updated.

```
curl --location --request PATCH '/api/documents/:identifier?format=json&nodeAttributeName=id&nodeAttributeValue=12&mode=attrs' \
--data '{
  "indent": 12,
  "textAlign": "right"
}'
```

### Multi updates

When using `mode=attrs`, you can update multiple nodes with different attribute values in a single request by passing `multiUpdates=true`. Instead of targeting nodes with `nodeAttributeName`/`nodeAttributeValue` query parameters, you provide an array of update objects in the request body. Each object specifies a `where` clause to match nodes and the `attrs` to apply.

All conditions within a `where` clause are connected using AND logic — a node must match every specified attribute to be updated.

```bash
curl --location --request PATCH 'https://YOUR_APP_ID.collab.tiptap.cloud/api/documents/my-doc?format=json&mode=attrs&multiUpdates=true' \
--header 'Authorization: YOUR_SECRET' \
--header 'Content-Type: application/json' \
--data '[
  {
    "where": {
      "id": "id123"
    },
    "attrs": {
      "textAlign": "center"
    }
  },
  {
    "where": {
      "name": "customname"
    },
    "attrs": {
      "indent": 2
    }
  }
]'
```

You can combine `multiUpdates` with other parameters like `user`, `skipVersioning`, `fragment`, and `mergeAttributes`:

```bash
curl --location --request PATCH 'https://YOUR_APP_ID.collab.tiptap.cloud/api/documents/my-doc?format=json&mode=attrs&multiUpdates=true&user=bot-user-1&mergeAttributes=1' \
--header 'Authorization: YOUR_SECRET' \
--header 'Content-Type: application/json' \
--data '[
  {
    "where": {
      "id": "node-1",
      "type": "heading"
    },
    "attrs": {
      "level": 2
    }
  },
  {
    "where": {
      "id": "node-2"
    },
    "attrs": {
      "textAlign": "right"
    }
  }
]'
```

## Document fields

These endpoints allow you to read and update custom Yjs fields on a document. This is useful when you use custom Yjs types (e.g. `Y.getMap('configuration')`) and want to get or update values via the API.

### Get a document field

```bash
GET /api/documents/:identifier/fields?field=:field&type=:type
```

Returns the current value of a custom Yjs field.

#### Query parameters

| Parameter | Type     | Default | Description                                                                                                                                                                                                                                                                                          |
| --------- | -------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `field`   | `string` | —       | The name of the custom field.                                                                                                                                                                                                                                                                        |
| `type`    | `string` | —       | The Yjs type of the custom field. Supported values: `map`, `array`. Passing the wrong type will return incorrect results. Note that calling this endpoint will create the type on the document if it does not exist. Switching the type for the same field name will create unintended side effects. |

```bash
curl --location 'https://YOUR_APP_ID.collab.tiptap.cloud/api/documents/my-doc/fields?field=configuration&type=map' \
--header 'Authorization: YOUR_SECRET'
```

### Update a document field

```bash
PATCH /api/documents/:identifier/fields?field=:field&type=:type
PUT /api/documents/:identifier/fields?field=:field&type=:type
```

Updates a custom Yjs field on the document.

- **`PATCH`** merges the request body into the existing field values — only the keys you send are updated, existing keys are preserved.
- **`PUT`** replaces the field entirely — keys not present in the request body are removed.

#### Query parameters

| Parameter        | Type     | Default | Description                                                                          |
| ---------------- | -------- | ------- | ------------------------------------------------------------------------------------ |
| `field`          | `string` | —       | The name of the custom field.                                                        |
| `type`           | `string` | —       | The Yjs type of the custom field. Only `map` is supported for updates at the moment. |
| `user`           | `string` | —       | User ID to associate with the change.                                                |
| `skipVersioning` | `1`      | —       | If passed, auto versioning won't be triggered by this change.                        |

#### Example: Merge values with PATCH

```bash
curl --location --request PATCH 'https://YOUR_APP_ID.collab.tiptap.cloud/api/documents/my-doc/fields?field=configuration&type=map' \
--header 'Authorization: YOUR_SECRET' \
--header 'Content-Type: application/json' \
--data '{
  "key": "newValue",
  "array": [1, 2, 3],
  "object": {
    "id": 62,
    "position": ""
  },
  "arrayOfObjects": [
    { "id": 61 }
  ]
}'
```

#### Example: Replace all values with PUT

```bash
curl --location --request PUT 'https://YOUR_APP_ID.collab.tiptap.cloud/api/documents/my-doc/fields?field=configuration&type=map' \
--header 'Authorization: YOUR_SECRET' \
--header 'Content-Type: application/json' \
--data '{
  "key": "newValue"
}'
```

Any existing keys not included in the `PUT` body will be removed from the field.
