Callback handler

The document editing service informs the document storage service about the status of the document editing using the callbackUrl from JavaScript API. The document editing service use the POST request with the information in body.

Parameters and their description:

Parameter Description Type Presence
Defines the object received when the user takes an action with the document. The type field value can have the following values:
  • 0 - the user disconnects from the document co-editing,
  • 1 - the new user connects to the document co-editing,
  • 2 - the user clicks the forcesave button.
The userid field value is the user identifier.
array of object optional
Defines the array of objects with the document changes history. The object is present when the status value is equal to 2 or 3 only. Must be sent as a property changes of the object sent as the argument to the refreshHistory method. Removed since version 4.2, please use history instead. array of object optional
Defines the link to the file with the document editing data used to track and display the document changes history. The link is present when the status value is equal to 2 or 3 only. The file must be saved and its address must be sent as changesUrl parameter using the setHistoryData method to show the changes corresponding to the specific document version. string optional
Defines an extension of the document that is downloaded from the link specified with the url parameter. The file type is OOXML by default but if the assemblyFormatAsOrigin server setting is enabled, the file will be saved in its original format. string optional
Defines the type of initiator when the force saving request is performed. Can have the following values:
  • 0 - the force saving request is performed to the command service,
  • 1 - the force saving request is performed each time the saving is done (e.g. the Save button is clicked), which is only available when the forcesave option is set to true.
  • 2 - the force saving request is performed by timer with the settings from the server config.
The type is present when the status value is equal to 6 or 7 only.
integer optional
Defines the object with the document changes history. The object is present when the status value is equal to 2 or 3 only. It contains the object changes and serverVersion, which must be sent as properties changes and serverVersion of the object sent as the argument to the refreshHistory method. object optional
Defines the edited document identifier. string required
Defines the status of the document. Can have the following values:
  • 1 - document is being edited,
  • 2 - document is ready for saving,
  • 3 - document saving error has occurred,
  • 4 - document is closed with no changes,
  • 6 - document is being edited, but the current document state is saved,
  • 7 - error has occurred while force saving the document.
integer required
Defines the link to the edited document to be saved with the document storage service. The link is present when the status value is equal to 2 or 3 only. string optional
Defines the custom information sent to the command service in case it was present in the request. string optional
Defines the list of the identifiers of the users who opened the document for editing; when the document has been changed the users will return the identifier of the user who was the last to edit the document (for status 2 and status 6 replies). array of string optional

Since version 7.0, callbackUrl is used from the last tab of the same user. Prior to version 7.0, callbackUrl from the first user tab was used.

Status Description

It is received every user connection to or disconnection from document co-editing. Their callbackUrl is used.

Please note that the status 1 can be also received when the user is returned to the document with no changes after the Internet problems. This situation can be described as follows:

  • When the user opens a document, the status 1 is sent.
  • If the Internet connection is lost and the user has not made any changes to the document, the status 4 is sent. An error is displayed on the screen and the document is opened in the viewer.
  • Within 100 seconds, the Internet connection is restored, the user is reconnected to the document and the status 1 is sent again.
  • Now the user can continue to edit the document. The status 2 or 4 will be received depending on whether the user made any changes to the document or not.

It is received 10 seconds after the document is closed for editing with the identifier of the user who was the last to send the changes to the document editing service. The callbackUrl from the user who made the last changes to the file is used.

It is received after the document is closed for editing with no changes by the last user. Their callbackUrl is used.

It is received when the force saving request is performed.

The callbackUrl depends on forcesavetype parameter:

  • If forcesavetype parameter is set to 1, the callbackUrl from the user who clicked the Save button is used.
  • If forcesavetype parameter is set to 0 or 2, the callbackUrl from the user who made the last changes to the file is used.

Starting from version 5.5 to version 6.1, the callbackUrl from the user who made the last changes to the file is always used.

{
    "actions": [{"type": 1, "userid": "78e1e841"}],
    "key": "Khirz6zTPdfd7",
    "status": 1,
    "users": ["6d5a81d0", "78e1e841"]
}
{
    "actions": [{"type": 0, "userid": "78e1e841"}],
    "changesurl": "https://documentserver/url-to-changes.zip",
    "history": {
        "changes": changes,
        "serverVersion": serverVersion
    },
    "filetype": "docx",
    "key": "Khirz6zTPdfd7",
    "status": 2,
    "url": "https://documentserver/url-to-edited-document.docx",
    "users": ["6d5a81d0"]
}
{
    "key": "Khirz6zTPdfd7",
    "status": 4
}
{
    "changesurl": "https://documentserver/url-to-changes.zip",
    "forcesavetype": 0,
    "history": {
        "changes": changes,
        "serverVersion": serverVersion
    },
    "filetype": "docx",
    "key": "Khirz6zTPdfd7",
    "status": 6,
    "url": "https://documentserver/url-to-edited-document.docx",
    "users": ["6d5a81d0"],
    "userdata": "sample userdata"
}

The document storage service must return the following response, otherwise the document editor will display an error message:

{
    "error": 0
}

The document manager and document storage service are either included to Community Server or must be implemented by the software integrators who use ONLYOFFICE Document Server on their own server.

public class WebEditor : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
        string body;
        using (var reader = new StreamReader(context.Request.InputStream))
            body = reader.ReadToEnd();

        var fileData = new JavaScriptSerializer().Deserialize<Dictionary<string, object>>(body);
        if ((int) fileData["status"] == 2)
        {
            var req = WebRequest.Create((string) fileData["url"]);

            using (var stream = req.GetResponse().GetResponseStream())
            using (var fs = File.Open(PATH_FOR_SAVE, FileMode.Create))
            {
                var buffer = new byte[4096];
                int readed;
                while ((readed = stream.Read(buffer, 0, 4096)) != 0)
                    fs.Write(buffer, 0, readed);
            }
        }
        context.Response.Write("{\"error\":0}");
    }
}
PATH_FOR_SAVE is the absolute path to your computer folder where the file will be saved including the file name.
public class IndexServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        PrintWriter writer = response.getWriter();

        Scanner scanner = new Scanner(request.getInputStream()).useDelimiter("\\A");
        String body = scanner.hasNext() ? scanner.next() : "";

        JSONObject jsonObj = (JSONObject) new JSONParser().parse(body);

        if((long) jsonObj.get("status") == 2)
        {
            String downloadUri = (String) jsonObj.get("url");

            URL url = new URL(downloadUri);
            java.net.HttpURLConnection connection = (java.net.HttpURLConnection) url.openConnection();
            InputStream stream = connection.getInputStream();

            File savedFile = new File(pathForSave);
            try (FileOutputStream out = new FileOutputStream(savedFile)) {
                int read;
                final byte[] bytes = new byte[1024];
                while ((read = stream.read(bytes)) != -1) {
                    out.write(bytes, 0, read);
                }

                out.flush();
            }

            connection.disconnect();
        }
        writer.write("{\"error\":0}");
    }
}
pathForSave is the absolute path to your computer folder where the file will be saved including the file name.
var fs = require("fs");
var syncRequest = require("sync-request");

app.post("/track", function (req, res) {

    var updateFile = function (response, body, path) {
        if (body.status == 2)
        {
            var file = syncRequest("GET", body.url);
            fs.writeFileSync(path, file.getBody());
        }

        response.write("{\"error\":0}");
        response.end();
    }

    var readbody = function (request, response, path) {
        var content = "";
        request.on("data", function (data) {
            content += data;
        });
        request.on("end", function () {
            var body = JSON.parse(content);
            updateFile(response, body, path);
        });
    }

    if (req.body.hasOwnProperty("status")) {
        updateFile(res, req.body, pathForSave);
    } else {
        readbody(req, res, pathForSave)
    }
});
pathForSave is the absolute path to your computer folder where the file will be saved including the file name.
<?php

if (($body_stream = file_get_contents("php://input"))===FALSE){
    echo "Bad Request";
}

$data = json_decode($body_stream, TRUE);

if ($data["status"] == 2){
    $downloadUri = $data["url"];
        
    if (($new_data = file_get_contents($downloadUri))===FALSE){
        echo "Bad Response";
    } else {
        file_put_contents($path_for_save, $new_data, LOCK_EX);
    }
}
echo "{\"error\":0}";

?>
$path_for_save is the absolute path to your computer folder where the file will be saved including the file name.
class ApplicationController < ActionController::Base
    def index
        body = request.body.read

        file_data = JSON.parse(body)
        status = file_data["status"].to_i

        if status == 2
            download_uri = file_data["url"]
            uri = URI.parse(download_uri)
            http = Net::HTTP.new(uri.host, uri.port)

            if download_uri.start_with?("https")
                http.use_ssl = true
                http.verify_mode = OpenSSL::SSL::VERIFY_NONE
            end

            req = Net::HTTP::Get.new(uri.request_uri)
            res = http.request(req)
            data = res.body

            File.open(path_for_save, "wb") do |file|
                file.write(data)
            end
        end
        render :text => "{\"error\":0}"
    end
end
path_for_save is the absolute path to your computer folder where the file will be saved including the file name.