Skip to content
Merged
Changes from 7 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
b457fd6
Introduce Server-side editor API docs.
DawidKossowski Jun 5, 2025
abb91e1
Docs: language review. [short flow]
godai78 Jun 5, 2025
7b1b366
Improvements for server-side editor API docs
scofalik Jun 9, 2025
da96e7e
Docs: minor language fixes. [short flow]
godai78 Jun 10, 2025
9bbe5e7
Merge branch 'stable' into cc/server-side-editor-api
godai78 Jun 12, 2025
926051f
Docs: minor updates. [skip ci]
godai78 Jun 12, 2025
b6d6e1e
Docs: rewording. [short flow]
godai78 Jun 13, 2025
adc35e6
Merge branch 'stable' into cc/server-side-editor-api
godai78 Jun 18, 2025
af22608
Docs: restructuring. [short flow]
godai78 Jun 18, 2025
8aae451
Docs: a link. [short flow]
godai78 Jun 25, 2025
ea90812
Introduce new examples that reflect recent API improvements.
DawidKossowski Jun 30, 2025
f5d6320
Merge branch 'stable' into cc/server-side-editor-api
godai78 Jun 30, 2025
06680ab
Docs: minor fixes. [short flow]
godai78 Jun 30, 2025
bfc648b
Docs: aligning. [short flow]
godai78 Jun 30, 2025
05846bc
Rearrange the annotations snippets.
DawidKossowski Jun 30, 2025
03e75bb
Minor improvements for SSE API guide.
DawidKossowski Jun 30, 2025
62d0320
Minor improvements for SSE API docs.
DawidKossowski Jun 30, 2025
f464bff
Update docs/features/server-side-editor-api.md
godai78 Jul 1, 2025
56baa7c
Add error handling section.
DawidKossowski Jul 8, 2025
43ac267
Update docs/features/server-side-editor-api.md
godai78 Jul 9, 2025
3a87fb5
Merge branch 'stable' into cc/server-side-editor-api
godai78 Jul 9, 2025
c427e3f
Merge branch 'stable' into cc/server-side-editor-api
godai78 Jul 14, 2025
3c090c1
Reworded some descriptions, added comments to code, moved around some…
scofalik Jul 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
351 changes: 351 additions & 0 deletions docs/features/server-side-editor-api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,351 @@
---
category: cloud-services
order: 30
meta-title: Cloud Services Server-side Editor API | CKEditor 5 Documentation
meta-description: Learn how to use server-side API to manage content and collaboration data easily without running the editor.
modified_at: 2025-06-05
badges: [ premium ]
---

# Server-side editor API

Server-side Editor API enables deep and complex integration of your application with all document data, enabling you to manipulate content and manage collaborative data such as suggestions, comments, and revision history, and much more, directly from your server-side code.

The remote script REST API endpoint allows you to execute any JavaScript code that uses the CKEditor 5 API, that could be executed by a browser, but without a need to open the editor by a human user. Instead, the script is executed on the Cloud Services server.

## Why use server-side editor API?

While CKEditor 5 provides a rich client-side editing experience, there are many scenarios where server-side content processing is essential:

* **Automation**: Run content processing tasks as part of your backend workflows.
* **Scalability**: Process multiple documents simultaneously without client-side limitations.
* **Security**: Process sensitive content in a controlled environment without exposing it to client-side manipulation.
* **Performance**: Handle large-scale content operations without impacting the user's browser.
* **Consistency**: Ensure uniform content changes across multiple documents.
* **Integration**: Connect with other server-side systems and databases directly.

## Common use cases

* **Deep integration**: Build custom features that can manage document content and related document data straight from your application UI, without a need to open the editor.
* **Content migration**: Restructure and update references across multiple documents, perfect for website redesigns or content reorganization.
* **Shared content blocks**: Automatically update reusable content (like headers, footers, or common sections) across all documents that use it.
* **Automated review systems**: Build systems that automatically review and suggest content changes, like grammar checks or style improvements.
* **AI-powered editing**: Make automated suggestions while users are actively editing, helping improve content quality.
* **Automated publishing**: Prepare and process content for publication, including formatting, metadata updates, and resolving comments.

## Getting started with server-side editor API
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great guide, I really like it. I have one problem, imagining the total noob users, like me, I couldn't answer the question from where should I get the editor instance in all of those cases.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually know that bit from the demo that we had on All Hands :D But yeah – that's exactly the reason why we need a practical example.

Second part that's unknown: how to read (and that you can actually pass) params from the REST endpoint.

Copy link
Copy Markdown
Contributor

@scofalik scofalik Jun 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, editor is a globally available variable, I think it could be mentioned just before the very first snippet. And we should also mention that you can simply copy-paste these snippets as payload for the REST API endpoint.

But one more missing piece of data may be that you actually need to upload the bundle. Of course that will be in the CS guide, but would be good to have it here as well?

Thanks for comments, we are biased with having this knowledge.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You do realize this is not exactly a guide for total noobs? We need to assume the user reading this has already gone through some of our docs, and also, I think we should link some to not duplicate all the knowledge. The question is the extent of this.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think even mediocre and advanced noobs will have no idea where the magic variable in 5+ snippets comes from and how to get it on the server ;)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this one is true :D

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not fully agree with those comments. The idea and the requirements were to prepare scripts that would inspire people and function correctly when copied&pasted -- and this has been fulfilled. No one will be able to use the SS Editor API without referring to the CS documentation, where detailed information should be provided.

However, if you feel that this information should be included here as well, we will add it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only need a sentence or a callout (note) that editor is globally available, that's all :D.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was added this morning :)


This guide explains how to write scripts that can be executed through the Server-side Editor API endpoint. The following sections provide examples of such scripts, each demonstrating a specific use case that can be automated on the server side.

For information about setting up and using the endpoint itself, see the {TODO: link Cloud Services Server-side Editor API} documentation.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally, we should have a short example here, so all the necessary knowledge is in one place. At least from the API usage perspective – so assuming that someone uploaded a bundle and have credentials already. Something that will be sufficient for a person familiar with the Collab REST APIs already.

Alternatively/Optionally: This could be a screencast showing one of the usecases: E.g. adding a comment via SS Ed API, while it simultaneously shows up in the editor content in a browser.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: This screencast will be useful for the blog post as well (cc @wojtekidd)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For example, one completely unclear thing here is – how I choose which document will the SS Ed API apply? What do I need to do to set that up? What if there's a collab session already? What if not?

I suppose details of this will make sense in the CS guide, but some minor explanation here and practical example may help a lot.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could be useful questions to answer @godai78

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll get to it, I believe :)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, the tricky part will be how much to add to this guide to understand enough without the need to jump around.


## Working with content

<info-box info>
Please note you need to have a bundle uploaded first with proper credentials <!-- link here--> for the service to work properly.
</info-box>

### Getting editor data

The most basic action you can perform is getting the editor's data. We use {@link module:core/editor/editor~Editor `editor`} for that, as it is a make a globally available variable by the Cloud environment.

```js
// Get the editor data.
const data = editor.getData();

return data;
```

You can also retrieve the data with specific options and include additional information about the document:

```js
// Get the editor data with suggestion highlights visible.
const data = editor.getData( { showSuggestionHighlights: true } );

// Get additional document information.
const wordCount = editor.plugins.get( 'WordCount' ).getWords();

// Return both the content and metadata.
return {
content: data,
wordCount: wordCount
};
```

This approach allows you to not only retrieve the document content but also process it, extract metadata, or prepare it for specific use cases like exports or integrations with other systems.

### Using commands

Commands provide a high-level API to interact with the editor and change the document content. Most editor features provide a command that you can use to trigger some action on the editor.

Here is a simple example. Imagine you need to fix a typo in a company name that is spread across multiple documents. Instead of forcing the user to do it manually, you can do it with a single line of code:

```js
// Replace all instances of "Cksource" with "CKSource" in the document.
editor.execute( 'replaceAll', 'CKSource', 'Cksource' );
```

This command will find all instances of "Cksource" in your documents and change them to "CKSource". This is perfect for making bulk updates in multiple documents. Simply, execute this call for every document you would like to change.

To learn more about the commands architecture, visit the [Commands documentation](https://ckeditor.com/docs/ckeditor5/latest/framework/architecture/core-editor-architecture.html#commands) guide.

### Insert HTML content

When you have HTML content ready (for example, from another system or a template), you can insert it directly into the editor. This is often simpler than building the content piece by piece using the editor API.

```js
// The HTML content we want to add.
const html = '<h2>New section</h2><p>This is a <strong>new section</strong> inserted into the document using <u>server-side editor API</u>.</p>';

// Convert HTML to the editor's model.
const model = editor.data.parse( html );

// Get the root element and create an insertion position.
const root = editor.model.document.getRoot();
const insertPosition = editor.model.createPositionAt( root, 1 );

// Insert the content at the specified position.
editor.model.insertContent( model, insertPosition );
```

### Using editor model API

If you cannot find a command that would perform a specific action on the document, you can use the editor API to apply precise changes. This approach offers the greatest flexibility and should cover any needs you may have. It requires, however, a better understanding of CKEditor internals.

For example, consider a scenario where you need to update all links in your document from `/docs/` to `/documents/`. This is a common task when moving content between environments or updating your site structure.

```js
// Get the root element and create a range that covers all content.
const root = editor.model.document.getRoot();
const range = editor.model.createRangeIn( root );
const items = Array.from( range.getItems() );

editor.model.change( writer => {
for ( const item of items ) {
let href = item.getAttribute( 'linkHref' );

if ( item.is( 'textProxy' ) && href ) {
// Update the link URL.
href = href.replace( '/docs/', '/documents/' );
writer.setAttribute( 'linkHref', href, item );
}
}
} );
```

This approach is particularly useful when you have to modify the document data in some specific way, and the generic, high-level API cannot cover it.

To learn more about working with the editor engine, see the {@link framework/architecture/editing-engine Editing engine} guide.

## Working with track changes

You can leverage the {@link features/track-changes track changes} feature API to manage existing content suggestions, retrieve final document data with all suggestions accepted, or implement automated or AI-powered content reviews.

### Using commands

Track changes is integrated with most editor commands. If you wish to change the document using commands and track these changes, all you need to do is turn on track changes mode.

Below is an example that shows a basic text replacement:

```js
// Enable track changes to mark our edits as suggestions.
editor.execute( 'trackChanges' );

// Make a simple text replacement.
editor.execute( 'replaceAll', 'CKSource', 'Cksource' );
```

The `trackChanges` command ensures that all changes made by other commands are marked as suggestions.

### Content changes

Now, let's see how to suggest deleting a specified part of the document:

```js
// Enable track changes to mark our edits as suggestions.
editor.execute( 'trackChanges' );

// Get the section we want to remove and prepare for deletion.
const firstElement = editor.model.document.getRoot().getChild( 0 );
const deleteRange = editor.model.createRangeIn( firstElement );
const deleteSelection = editor.model.createSelection( deleteRange );

// Remove the content as a suggestion.
editor.model.deleteContent( deleteSelection );
```

This functionality is essential when building, for example, automated content review systems. You might use it to precisely mark any content that should be removed. All content inside `deleteSelection` will become a deletion suggestion.

You can also suggest adding new content:

```js
// Enable track changes for the new content.
editor.execute( 'trackChanges' );

// Prepare the new content we want to add.
const modelFragment = editor.data.parse( 'Hello <strong>world!</strong>' );

// Add the content as a suggestion at the beginning of the document.
const firstElement = editor.model.document.getRoot().getChild( 0 );
const insertPosition = editor.model.createPositionAt( firstElement, 0 );

editor.model.insertContent( modelFragment, insertPosition );
```

The `insertContent()` method can be used in the following scenarios:

* Automated suggestions based on external data.
* Creating templates that need review before finalization.
* Integrating with content management systems to propose changes.
* Building custom workflows for content creation and review.

### Working with suggestions

You can use the {@link module:track-changes/trackchangesdata~TrackChangesData track changes data plugin} to get the document data with all suggestions either accepted or discarded:

```js
// Get the track changes data plugin.
const trackChangesData = editor.plugins.get( 'TrackChangesData' );

// Get the document data with all suggestions rejected.
// You can also use `trackChangesData.getDataWithAcceptedSuggestions()` to get data with all suggestions accepted.
const data = trackChangesData.getDataWithDiscardedSuggestions();

return data;
```

This is particularly useful when you need to show or process the "original" or the "final" document data.

While the previous example could be used to get the data, you may also want to permanently accept or discard suggestions. You can do this for all suggestions at once using the following command:

```js
// Accept all suggestions in the document.
// Use `discardAllSuggestions` command to discard all suggestions instead.
editor.execute( 'acceptAllSuggestions' );
```

This command is especially helpful when finalizing documents or when working with applications where a document is split into multiple CKEditor document instances but is treated as one unit in the application. In such cases, you might, for example, want to offer a button to accept all suggestions across all document parts.

For more granular control, you can also manage individual suggestions:

```js
// Get the track changes editing plugin.
const trackChangesEditing = editor.plugins.get( 'TrackChangesEditing' );

// Get a specific suggestion by its ID.
const suggestion = trackChangesEditing.getSuggestion( 'suggestion-id' );

// Accept the suggestion.
suggestion.accept();
// Or discard it.
// suggestion.discard();
```

It allows to display and manage suggestions outside of the editor, for example in a separate application view where users can see all comments and suggestions and resolve them without going into the editor.

### Attribute modifications

If you wish to create attributes suggestions using the editor model API, you need to specifically tell the track changes features to record these changes. Let's look at how to correctly make a suggestion to update links URLs:

```js
// Get the track changes editing plugin for direct access to suggestion recording.
const trackChangesEditing = editor.plugins.get( 'TrackChangesEditing' );

// Get the root element and create a range that covers all content.
const root = editor.model.document.getRoot();
const range = editor.model.createRangeIn( root );
const items = Array.from( range.getItems() );

// Process each item in the document.
for ( const item of items ) {
editor.model.change( writer => {
// Use _recordAttributeChanges to ensure the change is properly recorded as a suggestion.
trackChangesEditing._recordAttributeChanges( () => {
let href = item.getAttribute( 'linkHref' );

// Only process text proxies (parts of text nodes) that have a `linkHref` attribute.
if ( item.is( 'textProxy' ) && href ) {
// Update the link URL, for example changing '/docs/' to '/documents/'.
href = href.replace( '/docs/', '/documents/' );

// Set the new attribute value, which will be recorded as a suggestion.
writer.setAttribute( 'linkHref', href, item );
}
} );
} );
}
```

## Resolving comments

The {@link features/comments comments} feature allows your users to have discussions on certain parts of your documents. You can use the comments feature API to implement interactions with comments with no need to open the editor itself.

For example, here's how to resolve all comment threads in a given document:

```js
// Get all comment threads from the document.
const threads = editor.plugins.get( 'CommentsRepository' ).getCommentThreads();

// Resolve all open comment threads.
for ( const thread of threads ) {
if ( !thread.isResolved ) {
thread.resolve();
}
}
```

This code is particularly useful when you need to clean up a document. You might use it to automatically resolve old discussions, prepare documents for publication, or maintain a clean comment history in your content management system.

## Working with revision history

Use the {@link features/revision-history revision history} feature API to build more functional integration between your application and the document revisions data.

### Saving revisions

You can use Revision history API to save a new revision directly from your application backend:

```js
// Save the current state as a new revision.
editor.plugins.get( 'RevisionTracker' ).saveRevision( { name: 'New revision' } );
```

This can be used on an unchanged document just to create a document snapshot, or after you performed some changes to save them as a new revision.

Revision history API can help you build an automated mechanism that will automatically create revisions in some time intervals, or based on other factors. It can be particularly useful when you need to create checkpoints for your documents to maintain an audit trail of content modifications.

### Working with revision data

In more complex scenarios, you might have a need to work with content coming from various revisions of your document:

```js
// Get the revision management tools.
const revisionHistory = editor.plugins.get( 'RevisionHistory' );
const revisionTracker = editor.plugins.get( 'RevisionTracker' );

// Get the latest revision from history.
const revision = revisionHistory.getRevisions()[ 0 ];

// Get the document content and document roots attributes.
const documentData = await revisionTracker.getRevisionDocumentData( revision );
const attributes = await revisionTracker.getRevisionRootsAttributes( revision );

return { documentData, attributes };
```

This is useful if you need particular revision data for further processing. It will allow you build custom backend features based on revisions, like previewing revisions data outside of editor, exporting a particular revision to PDF, or integrating revisions data with external systems.

## Custom plugins

Server-side editor API capabilities could be extended by creating custom plugins. Custom plugins may implement complex logic and maintain reusable functionality across multiple server-side operations. Through the editor instance, you can access custom plugin API in your server-side scripts. This approach will make your code more organized and maintainable. It is especially recommended for complex operations that would be cumbersome to implement directly in the server-side script.

To use custom plugins in server-side executed scripts, simply add them to the editor bundle that you upload to Cloud Services. Then you can access them through the editor instance:

```js
// Get your custom plugin instance.
const myPlugin = editor.plugins.get( 'MyCustomPlugin' );

// Use the plugin's API.
return myPlugin.doSomething();
```

For more information about creating custom plugins, see the {@link framework/architecture/plugins Plugins architecture} guide and the {@link tutorials/creating-simple-plugin-timestamp Creating a basic plugin} tutorial.