Skip to main content

Events and callbacks

The SDK reports what's happening inside the embedded frame through callbacks — there is no separate addEventListener()/removeEventListener() API. All events are attached through the events key of the frame config, either at initialization or later via setConfig().

TFrameEvents is the alphabetical type reference for every handler and its payload. This page groups the events by the embedding modes that fire them, and covers the timing and default-behavior details that a single field's description doesn't.

const docSpace = DocSpace.SDK.initManager({
frameId: "ds-frame",
src: "https://your-docspace.com",
events: {
onAppReady: function () {
console.log("Frame ready");
},
},
});
note

This page covers the vanilla JS SDK's events config, used the same way across every init*() method (initManager, initEditor, initViewer, selectors, initSystem, initUploader, initPublicRoom, initForms, initChat, initPersonal). The separate React component has its own convention for wiring up handlers — check that page if you're using @onlyoffice/docspace-react instead of the SDK directly.

Available events​

EventAvailable inDescription
onAppReadyAll modesThe frame finished initializing successfully.
onAppErrorAll modesThe frame failed to initialize.
onAuthSuccessAll modesFires only from the ONLYOFFICE Apps confirm-by-link authentication page (/confirm/Auth, reached when that page loads inside the iframe) — not for regular session sign-in, and never in OAuth mode.
onAuthErrorAll modesAn OAuth token problem (one of four error codes) — only fires with OAuth-based authentication, see below.
onContentReadyAll modesThe frame's content has loaded.
onCloseCallbackRoom selector, File selectorThe selector was closed or the selection was canceled.
onSelectCallbackRoom selector, File selectorA room or file was selected.
onFileManagerClickManager, Public room, Forms, PersonalA file was clicked in the file list — see the note below, this one changes default behavior too.
onEditorOpenManager, Public room, PersonalA document was opened in the editor — see the note below, this one changes default behavior too. Not fired in Forms mode, unlike onFileManagerClick.
onEditorCloseCallbackEditor, ViewerThe document editor was closed.
onDownloadManager, Public room, PersonalA download was requested (only fires when downloadToEvent: true is set). Not fired in Editor or Viewer mode — nothing in those modes calls the client's download helper with this signal.
onNoAccessManager, Public room, ChatThe target file/folder exists but isn't accessible to the current user (Manager, Public room); in Chat mode, fires when there's no signed-in, non-guest user — see Chat mode for the exact condition (it's not about AI being disabled on the portal, which hides the composer but fires no event of its own).
onNotFoundManager, Public roomThe target file/folder doesn't exist.
onSignOutManager, Editor, Viewer, Room selector, File selector, System, UploaderThe user signed out — either through the portal's own profile-menu "Log out" action or the logout() instance method. Not fired in Forms, Chat, Personal, or Public room mode — those don't have a sign-out UI element or a logout command handler wired up on the client side.
onCustomActionManager, Personal, FormsA custom context menu or create-menu action (registered via customActions or setCustomActions()) was clicked.
onNavigateForms, PersonalThe user navigated to a different section.
onFilterSearchPersonal, Public roomThe file list's search text changed — fires with an empty string when the search is reset.
onUploadSuccessUploader, Forms, PersonalA file upload completed. In Forms and Personal mode, only for an upload()-initiated transfer; in Uploader mode, only for a native upload through its own dialog.
onUploadErrorUploader, Forms, PersonalA file upload failed — same per-mode scope as onUploadSuccess.
onUploadProgressUploaderUpload progress update — only fires for a native upload through Uploader mode's own dialog, per chunk. Never fires for an upload()-initiated transfer (Uploader mode doesn't support upload(), and Forms/Personal don't send progress updates for their own upload paths).
onGetExternalDataAny modeThe frame asks the host to read a value from its own external storage (e.g. a "tour seen" flag).
onSetExternalDataAny modeThe frame asks the host to persist a value in external storage.

Full type reference: TFrameEvents.

note

onFileManagerClick and onEditorOpen don't just notify you that something happened — attaching a handler for either one suppresses its default behavior (normally, clicking a file opens it in a new browser tab, and opening a file for editing does the same — in Forms and Personal mode, openEditorInSameTab controls this instead: true navigates the host page to the editor, false forces a new tab, and leaving it unset falls back to the portal's own "Open in the same tab" setting). With a handler attached, your code decides what to do instead; remove the handler (see Subscribing and updating handlers below) and the default behavior comes back.

onAuthError only fires when the frame is configured for OAuth-based authentication — supplying a getToken (or accessToken) function in the config instead of relying on the session cookie. Its code identifies what went wrong: an unresolved or unavailable token, a failed refresh, or a portal 401 that survived a fresh one. login()/logout() also become unavailable in this mode — both reject instead of running. See OAuth authentication for the full list of codes and how to set this up.

onContentReady can fire more than once per frame instance — e.g. after signing out, the frame reloads to show the sign-in page, which triggers onContentReady again without a second onAppReady. onAppReady itself isn't strictly limited to firing once either: signing back in through that sign-in page triggers onAppReady a second time. Don't assume either event only fires once at startup.

The order of onContentReady relative to onAppReady isn't guaranteed either: with noLoader: true (the default everywhere except Manager and System, which force it to false), the SDK fires onContentReady on the iframe's own load event, which can happen before onAppReady; with noLoader: false, it fires on a setIsLoaded command from the portal instead, timed differently. Don't build logic that depends on one always following the other.

onAppError is scoped to genuine SDK/init-level failures (bad src, CSP rejection, missing required config) — passing a nonexistent id (room/file/folder) does not trigger it. In Manager mode and Public room mode, the frame still initializes normally (onAppReady fires) and surfaces the problem via onNoAccess/onNotFound instead. The frame itself can also fire onAppError directly, with its own error message, when one of its pages fails to load (e.g. Chat mode) — not just the SDK's own CSP/connection/timeout failures.

In Public room mode specifically, id isn't just optional room-scoping — it's required config. Omitting it renders the portal's "Invalid link" error page instead of the room; onAppReady never fires in that case, but neither does onAppError — initialization itself has no timeout of its own, so there's no automatic failure signal here. onAppError only fires in Public room mode the same way it does everywhere else: for a genuine SDK/init-level failure, or when a hosted instance method call times out.

note

Editor mode and Viewer mode have no equivalent fallback: passing a missing or nonexistent id there fires neither onAppError nor onAppReady nor any not-found signal — the frame just never finishes initializing from the host's point of view. Validate the file id before calling initEditor/initViewer if you need to handle this case; the SDK gives you nothing to react to otherwise.

Chat mode has a related but different behavior: when there's no signed-in, non-guest user, the frame shows a no-access state — a chat history control with no composer — and fires onNoAccess (with an empty payload) alongside onAppReady. AI being disabled on the portal also hides the composer, but doesn't fire onNoAccess itself — there's no dedicated signal for that specific case, only the visual state. Attach a handler for onNoAccess rather than trying to infer either no-access state from onAppReady alone.

onCustomAction fires in Manager, Personal, and Forms mode — see Manager mode's section for the full setCustomActions()/customActions reference (context menu, create menu, restriction fields). onNavigate fires in both Forms mode and Personal mode — see either page for navigateSection() usage examples.

note

What triggers onUploadSuccess/onUploadError depends on the mode:

  • Forms and Personal mode: only an upload the host itself started with instance.upload() — dragging a file into either mode's own upload area doesn't trigger these events at all.
  • Uploader mode: upload() isn't available in this mode — only a native upload through the mode's own dialog fires these events, and with a different, much larger payload than an upload()-initiated one (see below). onUploadProgress is exclusive to this path too.

onGetExternalData and onSetExternalData are driven by the embedded app's own needs, not by anything the host requests — for example, the frame may use them to persist a "tour seen" flag. Return the stored value (or a Promise of it) from onGetExternalData; if you don't provide a handler, the frame gets undefined and may fall back to showing something again on every load.

Subscribing and updating handlers​

Pass handlers in the events object when you initialize a frame:

const docSpace = DocSpace.SDK.initEditor({
frameId: "ds-frame",
src: "https://your-docspace.com",
id: "your-file-id",
events: {
onAppReady: function () {
console.log("Editor ready");
},
onEditorCloseCallback: function () {
window.location.href = "/documents";
},
},
});

To change handlers on a frame that's already running, call setConfig() on its instance:

const frame = DocSpace.SDK.frames["ds-frame"];
await frame.setConfig({
events: {
onAppReady: function () {
console.log("New handler attached");
},
},
});

Each event's type is null | ((e?) => void) — pass null for a given event key to detach a handler:

const frame = DocSpace.SDK.frames["ds-frame"];
await frame.setConfig({
events: {
onFileManagerClick: null,
},
});

Event payloads​

Most events are simple lifecycle signals with no data of their own. An event the iframe reports without data still reaches your handler as an empty object {}, not undefined — only the events the SDK emits itself (onContentReady, onAppError, onAuthError) can be called with no argument at all. A few events pass along real data about what happened:

EventPayload
onAppReady{ frameId } — the ID of the frame that just finished initializing.
onContentReadyNo payload (undefined). See the ordering note above for its timing relative to onAppReady.
onSelectCallbackDepends on the mode: in Room selector, an array of room objects ({ id, label, icon?, roomType?, shared?, requestTokens? }); in File selector, a single file object ({ id, title, fileExst?, fileType?, path?, inPublic?, documentType?, requestTokens? } — no label here).
onEditorOpenThe file that was opened — the full file object (see below).
onFileManagerClickThe file that was clicked — the full file object (see below).
onAppErrorAn error message string (e.g. "The current domain is not set in the Content Security Policy (CSP) settings." on a CSP rejection).
onAuthError{ code?, message } — one of four token-failure codes, see OAuth authentication. Only fires with OAuth-based authentication.
onDownloadThe download URL as a plain string (only fires with downloadToEvent: true).
onSignOut, onCloseCallback, onNoAccessAn empty object {} — a signal only, no eventData.data to report.
onCustomAction{ action, type, item?, items?, folderId? } — action is the key you registered; type is "file", "folder", "room", or "create". For a single clicked entity, both item and items (a one-element array) are set; for a same-type multi-selection, only items is set; for create, neither is set. folderId is the id of the folder or room the user was in.
onNavigate{ section } — the section the user navigated to (e.g. "completed-forms").
onFilterSearch{ search } — the current search text; empty string when the search was reset.
onUploadSuccessForms/Personal (upload()-initiated, see above): { fileName, fileSize, uploadId }. Uploader mode's native dialog: an array wrapping the created file's info — [{ response: { id, folderId, version, title, providerKey, uploaded, file: {...} }, count, status, statusCode }] — a different shape entirely, not just a bigger version of the Forms/Personal one.
onUploadErrorForms/Personal: { fileName, message, uploadId }. Uploader mode's native dialog: { error, rejectedFiles? } — again a different shape, not the Forms/Personal one scaled up.
onUploadProgress{ sessionId, fileName, uploadedChunks, totalChunks, percent }.
onGetExternalData{ key, callId } — return the value stored for key (sync or a Promise); the SDK correlates the response by callId.
onSetExternalData{ key, value } — persist value under key; fire-and-forget, no response expected.

onFileManagerClick and onEditorOpen both pass a large file object — close to the shape returned by the backend API, including id, title, fileExst, webUrl, viewUrl, security, createdBy, and dozens more properties. See Get file information for the full schema rather than guessing from a partial example. The two payloads aren't identical, though: onEditorOpen's also includes an action field (e.g. "edit", describing how the editor was opened) that onFileManagerClick's doesn't have, while dropping a few fields onFileManagerClick does have (contextOptions, isFolder, icon, href, among others).

note

Clicking a file in the list only selects it and fires onFileManagerClick — it does not open the editor. onEditorOpen fires separately, when the file is actually opened (e.g. via the "Edit" context menu action, a double-click, or a hotkey) — in Manager mode, also for a document created from the "Create" dialog. Public room and Personal mode don't fire it for a newly created document; the portal opens its own editor for those instead.

Method-call errors​

Unlike the lifecycle events above, a failing instance method call (setConfig(), upload(), navigateSection(), and so on) doesn't go through events at all — it rejects its own returned Promise with a structured SDKError: { code, message, recoverable }. code is one of a fixed set of string values — "TIMEOUT", "DISCONNECTED", "CSP_VIOLATION", "MODE_MISMATCH", "INVALID_CONFIG", "UPLOAD_FAILED", "PARSE_ERROR", "TOKEN_RESOLVE_FAILED", "API_ERROR" — branch on code, not on message, since message is a free-text description meant for logging, not for comparison.

const frame = DocSpace.SDK.frames["ds-frame"];
frame.setConfig({ theme: "Dark" }).catch(function (err) {
if (err.code === "TIMEOUT") {
console.warn("The frame didn't respond in time — retry?", err.recoverable);
} else {
console.error(`[${err.code}] ${err.message}`);
}
});
note

If you're using the <script>-tag integration (as opposed to the npm package), SDKError/SDKErrorCode aren't exposed on window.DocSpace — only window.DocSpace.SDK is. Compare err.code against the string literal (as shown above) rather than trying to import the enum.

A method call also fails if the iframe doesn't respond within methodTimeout milliseconds (default 30000) — that specific call's Promise rejects with { code: "TIMEOUT", ... }, and separately fires onAppError with the same message as a plain string.

The MODE_MISMATCH guards the SDK enforces itself — calling navigateSection(), setCustomActions(), or upload() from a mode that doesn't support it — all reject their returned Promise the same way; none of them throw synchronously.

When the current mode has no handler at all for a method you called, the rejection's message names both the method and the mode (e.g. "logout is not available in forms mode") — this applies generally, not just to the three mode-guarded methods above. The same code also guards login() and logout() in OAuth mode, where the host — not the portal's session cookie — owns authentication.

warning

Most method calls reject with code: "API_ERROR" (carrying the portal's HTTP status and a sanitized error data object) when the ONLYOFFICE Apps portal itself reports a failure — an invalid parameter, a permission error, and so on. This requires a portal that flags method errors this way (client 4.0+); on an older portal, or if you're unsure which version you're targeting, treat a resolved value as unverified rather than assuming success. login() and createRoom() are the two exceptions that keep an older contract: instead of rejecting, they resolve with a sanitized { status, message } object — .catch() alone won't catch a failure from either. For a method that returns an entity (like createRoom()), checking for result.id on the resolved value is a more reliable success check than assuming a resolved promise means success.

login() needs its own check beyond status/message, since the shape of the returned object differs across portal versions. The reliable check is result.url: result.url === "/" means success; result.url starting with /confirm/ means a second factor is still required (not a failure — see Two-factor authentication); anything else is a failure. Still avoid logging or forwarding the resolved result object in its entirety on a failed attempt as a matter of habit — a portal older than client 4.0 won't have sanitized it first.

Common patterns​

Reacting to a file selection​

const selector = DocSpace.SDK.initFileSelector({
frameId: "ds-selector",
src: "https://your-docspace.com",
events: {
onSelectCallback: function (file) {
console.log("Selected:", file.title, file.id);
attachFileToRecord(file.id, file.title);
selector.destroyFrame();
},
onCloseCallback: function () {
selector.destroyFrame();
},
},
});

Reacting to a room selection​

Room selector's payload is an array — index into it rather than treating it as a single object:

const selector = DocSpace.SDK.initRoomSelector({
frameId: "ds-selector",
src: "https://your-docspace.com",
events: {
onSelectCallback: function (rooms) {
const room = rooms[0];
console.log("Selected room:", room.label, room.id);
linkRoomToTask(room.id, room.label);
selector.destroyFrame();
},
onCloseCallback: function () {
selector.destroyFrame();
},
},
});

See also: File selector mode, Room selector mode.

Redirecting after the editor closes​

const docSpace = DocSpace.SDK.initEditor({
frameId: "ds-frame",
src: "https://your-docspace.com",
id: "your-file-id",
events: {
onEditorCloseCallback: function () {
window.location.href = "/documents";
},
},
});

See also: Editor mode.

Handling inaccessible or missing content​

const docSpace = DocSpace.SDK.initManager({
frameId: "ds-frame",
src: "https://your-docspace.com",
id: "your-room-id",
events: {
onNoAccess: function () {
document.getElementById("ds-frame").innerHTML =
"You do not have permission to access this room.";
},
onNotFound: function () {
document.getElementById("ds-frame").innerHTML =
"This room no longer exists.";
},
},
});

See also: Manager mode, Public room mode.